Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
2428 commits
Select commit Hold shift + click to select a range
da15c04
feat: integrate FieldNamespace context into useFormValue
jthrilly Mar 30, 2026
e37c71a
feat: use FieldNamespace in useProtocolForm instead of manual prefix
jthrilly Mar 30, 2026
cc53836
feat: enable field value persistence in wizard form store
jthrilly Mar 30, 2026
a18c40c
refactor: migrate BioParentsStep to use FieldNamespace
jthrilly Mar 30, 2026
437f4e8
fix: use Map.has() for dormant value check, remove unnecessary Fragment
jthrilly Mar 30, 2026
db19637
feat: pass getFieldValue to wizard skip functions for form-store-base…
jthrilly Mar 30, 2026
9f32630
refactor: change wizard skip to receive SkipContext object instead of…
jthrilly Mar 30, 2026
1b64608
feat: add AdditionalParentsStep wizard component with role selection
jthrilly Mar 30, 2026
c7c5f2d
feat: improve Anonymisation interface transitions and encrypted backg…
jthrilly Mar 31, 2026
6506e82
wip second attempt at quick add form
jthrilly Mar 31, 2026
858b25f
cell wizard refactor
jthrilly Apr 1, 2026
d9ee721
layout fixes
jthrilly Apr 1, 2026
e1b0a64
add ego details step to ego cell wizard
jthrilly Apr 1, 2026
d039775
build(deps-dev): bump prisma from 7.5.0 to 7.6.0
dependabot[bot] Apr 1, 2026
ddc0df9
chore: add @aws-sdk/client-s3 and s3-request-presigner dependencies
jthrilly Apr 1, 2026
332a18e
feat: add storage provider AppSetting enum values and schemas
jthrilly Apr 1, 2026
0633d5e
feat: create FileStorage, AssetStorage services and error types
jthrilly Apr 1, 2026
7b3fc86
feat: create UploadThing storage layers with getDownloadUrl and Asset…
jthrilly Apr 1, 2026
81a79b2
feat: create S3 storage layers (FileStorage + AssetStorage)
jthrilly Apr 1, 2026
b58f4f9
feat: create StorageLayer composition with provider switching
jthrilly Apr 1, 2026
ce1969b
refactor: remove old export layer files superseded by lib/storage/
jthrilly Apr 1, 2026
aba1bde
feat: add presigned upload URL endpoint
jthrilly Apr 1, 2026
14c3d62
feat: create useUploadAssets hook and migrate useProtocolImport
jthrilly Apr 1, 2026
986a396
feat: add server actions for S3 configuration and validation
jthrilly Apr 1, 2026
e8595b5
feat: use getDownloadUrl for export download URLs
jthrilly Apr 1, 2026
389b29e
chore: fix lint errors in StorageProviderSelector and handler test, a…
jthrilly Apr 1, 2026
2d770f5
feat: add local MinIO dev server for S3 storage testing
jthrilly Apr 1, 2026
25b0d15
fix: prevent crash when deleting node
buckhalt Apr 1, 2026
e61c586
fix: move nodeAttributes inside useCallback to fix exhaustive-deps li…
buckhalt Apr 1, 2026
42c140e
e2e(snapshots): capture stage screenshots before navigation via inter…
buckhalt Apr 1, 2026
bdc52fd
feat: replace MinIO with local-s3 for lightweight dev S3 server (18MB)
jthrilly Apr 2, 2026
a8c2b1b
feat: switch dev S3 server to MinIO for ARM64 support
jthrilly Apr 2, 2026
6ace042
refactor: use RichSelectGroup for storage provider selection in setup
jthrilly Apr 2, 2026
dda4c3b
feat: verify UploadThing token on save by calling listFiles
jthrilly Apr 2, 2026
4008b63
fix: return formErrors instead of generic error on storage setup failure
jthrilly Apr 2, 2026
fe5d591
fix: validate UploadThing token structure before API call to prevent …
jthrilly Apr 2, 2026
d72359c
fix: catch thrown errors in storage form handlers to show meaningful …
jthrilly Apr 2, 2026
592edc2
chore: add Prisma migration for storage provider settings
jthrilly Apr 2, 2026
1f1cef1
fix: verify UploadThing token via direct API call to avoid env var fa…
jthrilly Apr 2, 2026
58a430d
fix: verify UploadThing token holistically via presigned URL registra…
jthrilly Apr 2, 2026
7db862e
refactor: remove Configure Participation step from setup wizard
jthrilly Apr 2, 2026
9728225
fix: remove divider lines between storage settings fields
jthrilly Apr 2, 2026
c93e24a
revert: restore divideChildren on storage settings card
jthrilly Apr 2, 2026
ac03a9a
feat: add info box explaining storage provider cannot be changed afte…
jthrilly Apr 2, 2026
c332c82
refactor: merge App Version and Configuration into a single settings …
jthrilly Apr 2, 2026
5bd5c96
refactor: rename Configuration to App Details and move to top of sett…
jthrilly Apr 2, 2026
c2dc42c
fix: UI tweaks — sidebar spacing, form error alignment, control min-w…
jthrilly Apr 2, 2026
26b051b
feat: add optional MinIO service to production docker-compose
jthrilly Apr 2, 2026
679428c
docs: add S3 connection details to MinIO service comment
jthrilly Apr 2, 2026
2143b9b
feat: auto-create MinIO bucket with public-read via init service
jthrilly Apr 2, 2026
1993ebb
docs: add comment explaining minio-init service
jthrilly Apr 2, 2026
3d8d81c
Merge pull request #694 from complexdatacollective/dependabot/npm_and…
jthrilly Apr 2, 2026
5e22c15
Merge pull request #693 from complexdatacollective/dependabot/npm_and…
jthrilly Apr 2, 2026
4a1f059
Merge pull request #691 from complexdatacollective/dependabot/npm_and…
jthrilly Apr 2, 2026
3beecfe
build(deps): bump @prisma/adapter-pg from 7.5.0 to 7.6.0
dependabot[bot] Apr 2, 2026
6eaf656
build(deps): bump @prisma/client from 7.5.0 to 7.6.0
dependabot[bot] Apr 2, 2026
ef0be53
Merge pull request #692 from complexdatacollective/dependabot/npm_and…
jthrilly Apr 2, 2026
a531bfd
chore: remove unused @uploadthing/react dependency and OurFileRouter …
jthrilly Apr 2, 2026
45e19fd
Merge branch 'next' into dependabot/npm_and_yarn/next/prisma/client-7…
jthrilly Apr 2, 2026
aa1da83
Merge pull request #690 from complexdatacollective/dependabot/npm_and…
jthrilly Apr 2, 2026
3663f2e
Merge pull request #695 from complexdatacollective/feat/s3-storage-pr…
jthrilly Apr 2, 2026
4d057e6
fix: initialize NodeDrawer as collapsed when starting with 0 nodes
jthrilly Apr 2, 2026
0af50a0
fix: pedigree connector line rendering and remove biologicalSexVariable
jthrilly Apr 2, 2026
5020a0d
fix: pedigree horizontal scrolling and remove interface padding
jthrilly Apr 2, 2026
121207b
fix: remove About You wizard step and update interaction tests
jthrilly Apr 2, 2026
69629f7
fix: pedigree layout alignment across cross-family structures
jthrilly Apr 2, 2026
9bffc2b
fix: increase pedigree row gap to full node height
jthrilly Apr 2, 2026
c0b2877
fix: skip grandparent checklist items for donors and surrogates
jthrilly Apr 2, 2026
1faa274
chore: fix all lint, typecheck and knip errors
jthrilly Apr 2, 2026
ecacb16
feat: implement disease nomination step with click-to-toggle nodes
jthrilly Apr 2, 2026
af33f9d
docs: pedigree network integration design spec
jthrilly Apr 2, 2026
8ae40c5
docs: pedigree network integration implementation plan
jthrilly Apr 2, 2026
2735c14
feat: add 'adoptive' edge type to pedigree layout system
jthrilly Apr 2, 2026
ac7b12f
refactor: replace adoptionStatus with 'adoptive' edge type
jthrilly Apr 2, 2026
ee5080c
refactor: migrate pedigree store to NcNode/NcEdge format
jthrilly Apr 2, 2026
06b87bb
feat: implement finalization, nomination, and reset flows
jthrilly Apr 2, 2026
7cd693d
test: update all tests for NcNode/NcEdge format
jthrilly Apr 2, 2026
e77ac00
fix: improve grandparent checklist item wording
jthrilly Apr 2, 2026
0378951
feat: add instructional popup after ego cell wizard completion
jthrilly Apr 2, 2026
8f60c0d
update intro text
jthrilly Apr 2, 2026
29a939c
fix: default egg/sperm donor questions to No
jthrilly Apr 2, 2026
f76edfb
fix: show finalization dialog on Next, block if wizard incomplete
jthrilly Apr 2, 2026
f5819a4
fix: right-align single dialog footer button
jthrilly Apr 2, 2026
edd4cfa
fix: update layoutDimensions test to match new row gap multiplier
jthrilly Apr 2, 2026
c69e068
chore: fix knip, test, and lint errors
jthrilly Apr 2, 2026
fbdcf9b
test: update e2e visual snapshots
jthrilly Apr 2, 2026
411ec16
small edit to pedigree
jthrilly Apr 2, 2026
cf0b528
change chromatic project key
jthrilly Apr 2, 2026
b592313
remove reference to family tree
jthrilly Apr 2, 2026
07bb98b
wider dialogs
jthrilly Apr 2, 2026
7112b9a
fix: render adoptive parent edges as dashed lines
jthrilly Apr 2, 2026
4c0a7e7
fix: lint error in appSettings, update e2e snapshots for all browsers
jthrilly Apr 2, 2026
337d796
fix: wizard UX issues — partnership labels, donor defaults, scroll
jthrilly Apr 2, 2026
a7ce948
fix: update gender identity labels to Man/boy and Woman/girl
jthrilly Apr 2, 2026
0d8b975
fix(FieldLabel): support markdown bold formatting for interview form …
buckhalt Apr 2, 2026
bf1dd4b
lint
buckhalt Apr 2, 2026
986f4bc
fix(e2e-test-data): add node shape prop to testDataBuilder
buckhalt Apr 2, 2026
2289593
fix(e2e): update testid to reflect changed section name
buckhalt Apr 2, 2026
ab2259f
fix(e2e-preview-mode): update api response message to match handler
buckhalt Apr 2, 2026
6a2abd7
fix: disable drag cursor on pedigree nodes during scaffolding step
jthrilly Apr 3, 2026
f987139
fix: prevent adding parents to ego's partners
jthrilly Apr 3, 2026
ad6db05
fix(e2e): update snapshots
buckhalt Apr 3, 2026
f39d080
e2e: finish interview, update snapshots, update protocol
buckhalt Apr 3, 2026
31e135a
fix(e2e): add waitfor to goto fixture, update snapshots again
buckhalt Apr 3, 2026
00cafbe
fix(e2e): copy standalone to tmp to fix intermittent race conditions
buckhalt Apr 3, 2026
4dbc1be
fix(e2e): use execFileSync to avoid shell injection in cp commands
buckhalt Apr 3, 2026
15dcb03
fix(e2e): fix flaky tests by waiting for mapbox idle and use serial i…
buckhalt Apr 3, 2026
214f52f
build(deps): bump @aws-sdk/client-s3 from 3.1021.0 to 3.1024.0
dependabot[bot] Apr 6, 2026
87434d0
build(deps-dev): bump chromatic from 16.0.0 to 16.1.0
dependabot[bot] Apr 6, 2026
abc4aac
build(deps): bump fuse.js from 7.1.0 to 7.3.0
dependabot[bot] Apr 6, 2026
4969ae0
build(deps): bump posthog-js from 1.363.6 to 1.364.7
dependabot[bot] Apr 6, 2026
6c409e5
build(deps-dev): bump typescript-eslint from 8.57.2 to 8.58.0
dependabot[bot] Apr 6, 2026
f844139
feat(styles): replace Quicksand with Nunito and leverage wider weight…
jthrilly Apr 8, 2026
49732b9
feat(name-generator-roster): add DataCard, fix sort/filter, refactor …
jthrilly Apr 8, 2026
23b3dc5
perf(name-generator-roster): avoid parent re-renders during drag
jthrilly Apr 8, 2026
081a7b4
significant fixes to Collection (perf)
jthrilly Apr 8, 2026
6087c78
Potential fix for pull request finding 'Useless conditional'
jthrilly Apr 9, 2026
c4c9e1f
fix: reduced-motion hang in Spinner, flaky storybook tests, dialog cl…
jthrilly Apr 9, 2026
8782430
fix(markdown): render heading levels correctly in RenderMarkdown
jthrilly Apr 9, 2026
a1785b9
fix(interview-theme): use sea-green for --link so links render correctly
jthrilly Apr 9, 2026
5a389fb
chore(dev): scaffold TS dev-s3 script with local-s3 container lifecycle
jthrilly Apr 9, 2026
b5d578e
test(e2e): regenerate visual snapshots
jthrilly Apr 9, 2026
2a27953
chore(dev): harden dev-s3 shutdown race and remove-container tolerance
jthrilly Apr 9, 2026
2a0a2fe
feat(dev): create fresco-dev bucket via AWS SDK in dev-s3 script
jthrilly Apr 9, 2026
d6068ff
feat(dev): apply public-read bucket policy in dev-s3 script
jthrilly Apr 9, 2026
096a145
chore(dev): rename dev-s3 volume to avoid MinIO volume collision
jthrilly Apr 9, 2026
cffcfc0
fix(popover): silence Base UI render-prop warning in PopoverTrigger
jthrilly Apr 9, 2026
15f6699
fix(InputField): long single-token values no longer overflow container
jthrilly Apr 9, 2026
3c98dfe
fix(uploadthing): use SDK uploader instead of hand-rolled presigned PUT
jthrilly Apr 9, 2026
45fc949
refactor(auth): consolidate auth modules under lib/auth
jthrilly Apr 9, 2026
4a46d71
fix(uploadthing): proxy preview uploads via UTApi; validate token via…
jthrilly Apr 9, 2026
d172904
chore(uploadthing): remove dead hand-rolled presigning code
jthrilly Apr 9, 2026
ed4adae
fix(name-generator-roster): re-query listbox inside waitFor
jthrilly Apr 9, 2026
9281c70
make minio a required service in example docker config
jthrilly Apr 9, 2026
7fafafd
chore(uploadthing): inline parseUploadThingToken into its only caller
jthrilly Apr 9, 2026
66f7aa3
fix(styles): apply consistent focus ring to native and styled select …
jthrilly Apr 9, 2026
e5c7afe
refactor(participants): remove Delete All button from toolbar
jthrilly Apr 10, 2026
726edaf
feat(participants): make interview count column sortable
jthrilly Apr 10, 2026
2a38f22
feat(participants): use SelectAllHeader for select-all toggle
jthrilly Apr 10, 2026
00e3ce4
feat(participants): redesign import/export section as two-column layout
jthrilly Apr 10, 2026
efb2af6
refactor(participants): simplify import/export cards to stacked layout
jthrilly Apr 10, 2026
f125afa
fix(participants): button width and protocol select in export URLs
jthrilly Apr 10, 2026
d752761
refactor(dashboard): consolidate participants toolbar, improve filter…
jthrilly Apr 10, 2026
3ef1b4d
fix(synthetic-interviews): use correct config for FamilyPedigree stage
jthrilly Apr 10, 2026
1dead6c
refactor(interviews): improve network filter UX with clearer structur…
jthrilly Apr 10, 2026
581b2b0
fix(activity-feed): remove duplicate Data Exported event from updateE…
jthrilly Apr 10, 2026
f43152c
fix(test): increase FilterInteraction waitFor timeout to prevent flak…
jthrilly Apr 10, 2026
35c5ae9
fix: resolve knip, lint, and typecheck errors
jthrilly Apr 10, 2026
5b58cdc
fix(e2e): update tests to match current component behavior
jthrilly Apr 10, 2026
9a9a175
update snapshots
jthrilly Apr 10, 2026
a0b3a3f
Merge pull request #697 from complexdatacollective/dependabot/npm_and…
jthrilly Apr 13, 2026
4b8dc85
Merge pull request #698 from complexdatacollective/dependabot/npm_and…
jthrilly Apr 13, 2026
8c1cbe6
Merge pull request #700 from complexdatacollective/dependabot/npm_and…
jthrilly Apr 13, 2026
e7f2f77
Merge pull request #701 from complexdatacollective/dependabot/npm_and…
jthrilly Apr 13, 2026
60e333c
update stage 16 snapshot
jthrilly Apr 13, 2026
3e01fe3
Merge pull request #699 from complexdatacollective/dependabot/npm_and…
jthrilly Apr 13, 2026
87f4b63
wait for GeoJSON source load before geospatial snapshots
jthrilly Apr 13, 2026
031682d
regenerate snapahots
jthrilly Apr 13, 2026
a1096cb
fix(ci): replace duplicate PR comments with single updated report
jthrilly Apr 13, 2026
a57e8bb
use queryRenderedFeatures for GeoJSON load detection
jthrilly Apr 13, 2026
f28b178
fix(ci): replace PR comment with per-browser status checks
jthrilly Apr 13, 2026
9feb6a0
regenerate interview-webkit visual snapshots
jthrilly Apr 13, 2026
7c35dc5
remove goto options: stability should be handled in waitForStageLoad,…
jthrilly Apr 13, 2026
bd84cd6
build(deps-dev): bump @eslint/js from 9.39.3 to 9.39.4
dependabot[bot] Apr 13, 2026
69e7086
build(deps-dev): bump @chromatic-com/storybook from 5.1.1 to 5.1.2
dependabot[bot] Apr 13, 2026
193f7a0
fix(name-generator): fix external nodes in side panel
buckhalt Apr 13, 2026
da25eba
fix: hide UploadThing prompts when S3 storage is configured
buckhalt Apr 13, 2026
68fd3df
fix(field-hints): correctly render markdown
buckhalt Apr 13, 2026
8d429d3
fix(convex-hulls): support all categorical value formats in convex hulls
buckhalt Apr 13, 2026
84bf5c1
fix(collection): deduplicate keys from external roster to ensure item…
buckhalt Apr 13, 2026
9d8ef37
fix: skip AlterForm/AlterEdgeForm stages when no items exist
jthrilly Apr 13, 2026
69eca9c
refactor silos spec for logical navigation
jthrilly Apr 13, 2026
f631b43
further refactoring of silos spec
jthrilly Apr 13, 2026
e6ac01c
refactor(field-hints): simplify Hint markdown handling
jthrilly Apr 14, 2026
9220699
docs(field-hints): fold hint markdown demo into UsingMarkdown story
jthrilly Apr 14, 2026
17abe0c
handle external nodes in node panel using disconnected Node component
jthrilly Apr 14, 2026
d5c02e3
fix(collection): warn on duplicate keys when building nodes
jthrilly Apr 14, 2026
616e3c8
fix(network-query): bridge categorical scalar/array storage in predicate
jthrilly Apr 14, 2026
3b195cc
fix(sociogram): remove duplicate tab stop and wire keyboard activation
jthrilly Apr 14, 2026
b57bf36
remove spacing from node list inside of node panel
jthrilly Apr 14, 2026
147ef6f
lint
jthrilly Apr 14, 2026
01b7a78
test run passing with shared Page
jthrilly Apr 14, 2026
cb9fce9
e2e(geospatial): unify map readiness into single data-map-idle attribute
jthrilly Apr 14, 2026
578d73e
e2e(sociogram): mock force-simulation worker with deterministic grid
jthrilly Apr 14, 2026
860490f
update snapshots
jthrilly Apr 14, 2026
735332f
Merge pull request #703 from complexdatacollective/dependabot/npm_and…
jthrilly Apr 14, 2026
4dcd432
Merge pull request #702 from complexdatacollective/dependabot/npm_and…
jthrilly Apr 14, 2026
170e5b2
fix(preview-upload): add requiresAuth and bodyFormat to response
buckhalt Apr 14, 2026
f75d3c1
drop bodyFormat prop
buckhalt Apr 14, 2026
052a7b6
export and reuse PresignedUrlWithAssetId type
buckhalt Apr 14, 2026
b985764
replace requiresAuth prop with actual headers client must include
buckhalt Apr 14, 2026
56198e9
improve tests for preview endpoints
buckhalt Apr 14, 2026
17e2de3
fix(e2e): wait for map idle before final capture and simplify form fill
jthrilly Apr 14, 2026
5edf563
fix(e2e): restore correct baselines for stage-18/39 intro pages
jthrilly Apr 14, 2026
480ecc5
fix(e2e): bump actionTimeout to 10s for webkit stability
jthrilly Apr 14, 2026
b54fd3b
fix(e2e): await URL step change in interview.next() and disable modal…
jthrilly Apr 14, 2026
fb122f5
fix(e2e): allow 5% interview screenshot tolerance and drop fillField …
jthrilly Apr 14, 2026
08e131e
fix(modal): run enter animation with duration 0 under reduced motion
jthrilly Apr 14, 2026
1bd3aac
Revert "fix(e2e): allow 5% interview screenshot tolerance and drop fi…
jthrilly Apr 14, 2026
6f5eed8
Revert "fix(e2e): bump actionTimeout to 10s for webkit stability"
jthrilly Apr 14, 2026
a7d1f04
e2e: webkit stability — explicit per-test capture, geospatial paint c…
jthrilly Apr 15, 2026
e7b7f46
attempt to fix checkbox
jthrilly Apr 15, 2026
2d057f8
remove pause
jthrilly Apr 15, 2026
4eed1ba
update baselines
jthrilly Apr 15, 2026
e855961
make final screenshots scroll to bottom for stability
jthrilly Apr 15, 2026
6935db5
full suite pass locally
jthrilly Apr 15, 2026
207863b
update additional baselines
jthrilly Apr 15, 2026
6f5068e
fix: audio and video playback failures in safari
buckhalt Apr 15, 2026
af08b79
Merge pull request #705 from complexdatacollective/fix/preview-upload
buckhalt Apr 15, 2026
5310583
further baseline updates to remove focus stages
jthrilly Apr 15, 2026
31db322
all interview tests passing
jthrilly Apr 15, 2026
55a7bf7
correct comment about .mov and .ogg files
buckhalt Apr 15, 2026
3f2d53f
Update lib/interviewer/Interfaces/Information/Information.tsx
buckhalt Apr 15, 2026
06cf090
add maxDiffPixels: 100 for small rendering differences in CI
jthrilly Apr 16, 2026
e80de32
fix(e2e): webkit stability — fillField retry, one-shot style injectio…
jthrilly Apr 16, 2026
d05edd8
fix: useAnimate bypasses MotionConfig.skipAnimations, breaking WebKit…
jthrilly Apr 16, 2026
5ff7d13
Merge pull request #707 from complexdatacollective/fix/video-audio-we…
jthrilly Apr 16, 2026
549b888
further baseline upadates
jthrilly Apr 16, 2026
91d892b
returning to earlier state
jthrilly Apr 17, 2026
4fe2c9b
wip(e2e): narrow testMatch to silos-protocol only
jthrilly Apr 17, 2026
f591b15
refactor(e2e): simplify TestDatabase to testcontainer + migrations
jthrilly Apr 17, 2026
9a7ba86
refactor(e2e): simplify AppServer to single fixed-port instance
jthrilly Apr 17, 2026
9b45c49
refactor(e2e): add minimal AppSettings seed helper
jthrilly Apr 17, 2026
90c5799
refactor(e2e): single-instance global setup
jthrilly Apr 17, 2026
d815dcf
fix(e2e): drop unused no-var eslint-disable in global-setup
jthrilly Apr 17, 2026
03bc235
refactor(e2e): single-instance global teardown
jthrilly Apr 17, 2026
7fbd8d8
refactor(e2e): minimal playwright config — three projects, serial
jthrilly Apr 17, 2026
b784202
refactor(e2e): minimal base test — prisma worker fixture only
jthrilly Apr 17, 2026
9d8ae9c
refactor(e2e): interview-test reads E2E_ASSET_URL from env, owns VISU…
jthrilly Apr 17, 2026
2886701
refactor(e2e): drop database fixture usage from silos-protocol spec
jthrilly Apr 17, 2026
1447352
refactor(e2e): replace docker/local scripts with minimal run-e2e.sh
jthrilly Apr 17, 2026
8933d7c
refactor(e2e): drop test:e2e:local script
jthrilly Apr 17, 2026
87adf17
refactor(e2e): delete obsolete layer-1 infrastructure files
jthrilly Apr 17, 2026
a7ad18a
fix(e2e): use object type for empty fixtures generic
jthrilly Apr 17, 2026
0aacde4
refactor(e2e): delete out-of-scope specs and orphaned snapshot baselines
jthrilly Apr 17, 2026
bc27eb8
ci(e2e): simplified workflow — single run, gh-pages by branch, PR com…
jthrilly Apr 17, 2026
6c7dcfa
chore(e2e): delete existing visual baselines before regeneration
jthrilly Apr 17, 2026
92623da
fix(e2e): drop per-worker protocol.cleanup — leaves orphan Asset rows…
jthrilly Apr 17, 2026
7af243a
chore(e2e): firefox full baselines + webkit partial baselines through…
jthrilly Apr 17, 2026
6eecc4b
fix(dnd): pin activeDropTargetId from keyboard-nav index, not element…
jthrilly Apr 17, 2026
aefc01e
chore(e2e): webkit baselines for stages past DnD fix
jthrilly Apr 17, 2026
61baaf5
chore: resolve knip + lint cleanup
jthrilly Apr 17, 2026
68eb5d2
fix(ci): testcontainers host override + reclaim artifact ownership
jthrilly Apr 17, 2026
dc2c21b
ci: provide Postgres service for build-time queries
jthrilly Apr 17, 2026
1008358
fix(app): keep root-layout analytics dynamic so Docker image builds w…
jthrilly Apr 17, 2026
16ed1ed
test(e2e): add captureFinal() to female-ineligibility stages + baselines
jthrilly Apr 17, 2026
1eb10f7
test(e2e): mark Stage 8 Geospatial Interface as slow on webkit
jthrilly Apr 17, 2026
37ca4cf
add test.slow to all geospatial interfaces
jthrilly Apr 17, 2026
f81bff0
fix(e2e): bump interview.next() waitForURL to 20s for WebKit headroom
jthrilly Apr 17, 2026
13ecf1a
Merge pull request #710 from complexdatacollective/refactor/e2e-simplify
jthrilly Apr 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
335 changes: 335 additions & 0 deletions .claude/skills/generate-e2e-tests/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
---
name: generate-e2e-tests
description: Generate comprehensive Playwright e2e tests from a .netcanvas protocol file and an optional recording. Invoke with /generate-e2e-tests <protocol-path> [recording-path]
user-invocable: true
---

# Generate E2E Tests from Protocol

You are generating comprehensive Playwright e2e tests for a Fresco interview protocol.

## Inputs

- **Protocol path**: `$1` — path to a `.netcanvas` file (ZIP containing `protocol.json`)
- **Recording path** (optional): `$2` — path to a recording directory (contains `actions.jsonl`, `SESSION.md`, `screenshots/`)

If no recording is provided, generate a **synthetic happy path** using the data generation strategies in STAGE_TEST_REFERENCE.md (see "Synthetic Data Generation" section).

## Step 1: Read Reference Materials

Read these files to understand the testing infrastructure and patterns:

1. `tests/e2e/docs/STAGE_TEST_REFERENCE.md` — what to test for each stage type, fixture availability, validation testing patterns
2. `tests/e2e/fixtures/stage-fixture.ts` — available fixture methods and their signatures
3. `tests/e2e/fixtures/interview-fixture.ts` — interview navigation fixture
4. `tests/e2e/fixtures/protocol-fixture.ts` — protocol installation and network state inspection
5. `tests/e2e/CLAUDE.md` — full e2e testing architecture guide
6. `tests/e2e/specs/interview/silos-protocol.spec.ts` — reference test implementation to match style/structure
7. `CLAUDE.md` — project coding conventions (path aliases, TypeScript, etc.)

## Step 2: Extract and Analyze Protocol

Extract the protocol JSON:

```bash
unzip -p "$1" protocol.json
```

From the extracted JSON, build a **stage map** — for each stage (by index), extract:

- `type` — stage type (e.g., `NameGeneratorQuickAdd`, `EgoForm`, `Sociogram`)
- `label` — display label
- `subject` — `{ entity, type }` pointing to codebook entry (null for Information/Anonymisation)
- `introductionPanel` — title and text (if present)
- `form.fields[]` — array of `{ variable }` referencing codebook variables
- `prompts[]` — array of prompt objects (with `createEdge`, `variable`, `highlight`, etc.)
- `panels[]` — side panel configuration
- `behaviours` — `maxNodes`, `minNodes`, `freeDraw`, etc.

For each form field, resolve the variable UUID against the codebook:

- Look up `codebook.[entity].[type].variables.[variableId]`
- Extract: `name`, `type`, `component`, `validation`, `options`

This gives you the field name (UUID), display name, input component type, validation rules, and available options for each form field.

## Step 3: Analyze Recording (if provided)

If `$2` is provided, read `$2/actions.jsonl` (one JSON object per line).

Group actions by stage — track URL changes via the `step=N` query parameter. For each stage visited:

- Extract the sequence of user actions (click, fill, press, select)
- Note filled values and selected options
- Note which nodes were created (names entered in quick-add or name generator forms)
- Note edge-creating interactions (sociogram clicks, dyad census selections)

The recording represents the **happy path** — the exact user journey to replay.

### If no recording

Generate a synthetic happy path from the protocol alone:

1. Walk through all stages in order (index 0 to N)
2. For each stage, use the **Synthetic Data Generation** section of STAGE_TEST_REFERENCE.md to determine what values to fill, how many nodes to create, etc.
3. For conditional/skip logic, choose the path that visits the **most stages**
4. Track synthetic state as you go — node names created on earlier stages are needed for bin/census/sociogram stages later

## Step 4: Generate Test File

Create `tests/e2e/specs/interview/<protocol-name>.spec.ts` where `<protocol-name>` is derived from the protocol name (kebab-case, lowercase).

### File Structure

Follow this exact pattern (from the reference implementation):

```typescript
/**
* <Protocol Name> Tests
*
* Tests interview stage navigation using a real .netcanvas protocol file.
*/

import path from 'node:path';
import { expect, test } from '~/tests/e2e/fixtures/interview-test.js';
import { expectURL } from '~/tests/e2e/helpers/expectations.js';

const PROTOCOL_PATH = path.resolve(
import.meta.dirname,
'../../data/<filename>.netcanvas',
);

let sharedProtocolId: string;

test.describe('<Protocol Name>', () => {
test.beforeAll(async ({ database, protocol }) => {
await database.restoreSnapshot();
const { protocolId } = await protocol.install(PROTOCOL_PATH);
sharedProtocolId = protocolId;
});

test.describe('Happy Path', () => {
test.describe.configure({ mode: 'serial' });

let interviewId: string;

test.beforeAll(async ({ protocol }) => {
interviewId = await protocol.createInterview(sharedProtocolId);
});

test.beforeEach(({ interview }) => {
interview.interviewId = interviewId;
});

test.afterEach(async ({ page, interview }) => {
const stepMatch = /step=(\d+)/.exec(page.url());
if (stepMatch?.[1]) {
const step = stepMatch[1];
// List stage indices with non-deterministic rendering
const highToleranceStages: string[] = [/* sociogram indices */];

await interview.capture(`stage-${step}-final`, {
maxDiffPixelRatio: highToleranceStages.includes(step)
? 0.1
: undefined,
});
}
});

// One test() per stage...
});
});
```

### Per-Stage Test Generation

For each stage in the protocol, generate a `test()` block. Use the STAGE_TEST_REFERENCE.md to determine what to test.

#### Mapping Recording Actions to Fixture Calls

Translate recording actions to fixture method calls using these mappings:

| Recording Pattern | Fixture Call |
|---|---|
| Navigate to URL with `step=N` | `interview.goto(N)` |
| Click element matching next/forward button | `interview.nextButton.click()` |
| Fill input within `[data-field-name="UUID"]` | `stage.form.fillText(UUID, value)` or `fillNumber`/`fillDate` based on codebook component |
| Click radio within `[data-field-name="UUID"]` | `stage.form.selectRadio(UUID, optionLabel)` |
| Click checkbox within `[data-field-name="UUID"]` | `stage.form.selectCheckbox(UUID, optionLabel)` |
| Click toggle button within `[data-field-name="UUID"]` | `stage.form.selectToggleButton(UUID, optionLabel)` |
| Fill quick-add input + press Enter | `stage.quickAdd.addNode(value)` |
| Click "Add a person" button | `stage.nameGenerator.openAddForm()` |
| Click "Finished" button in dialog | `stage.nameGenerator.submitForm()` |
| Drag node from panel | `stage.nodePanel.dragNodeToMainList(label)` |
| Click node on sociogram (connecting) | `stage.sociogram.connectNodes(from, to)` |
| Drag node to ordinal bin | `stage.ordinalBin.dragNodeToBin(node, bin)` |
| Drag node to categorical bin | `stage.categoricalBin.dragNodeToBin(node, bin)` |

#### Determine Form Method from Codebook

Use the codebook variable's `component` (or `type` if no component) to pick the right form fixture method:

| Component | Method |
|---|---|
| `Text`, `TextArea` | `fillText` |
| `Number` | `fillNumber` |
| `DatePicker` | `fillDate` |
| `RadioGroup` | `selectRadio` |
| `LikertScale` | `selectLikert` |
| `CheckboxGroup` | `selectCheckbox` |
| `ToggleButtonGroup` | `selectToggleButton` |
| `Boolean` | `selectRadio` (options are "Yes"/"No" or custom labels from codebook) |

#### Comments

Add a comment above each form field interaction with the field's display name and component type:

```typescript
// 1. Date of birth (DatePicker)
await stage.form.fillDate('596c2ac2-...', '2000-06-15');

// 2. Gender identity (RadioGroup)
await stage.form.selectRadio('a06f06f5-...', 'Cisgender Male');
```

### Validation Tests

For each form stage (EgoForm, AlterForm, AlterEdgeForm), examine the codebook variables for targeted validation rules. Generate validation test assertions **within the happy path test** for that stage:

1. **Before filling fields**: Try to advance, verify validation blocks:
```typescript
// Verify validation blocks advancement
await interview.nextButton.click();
await expectURL(page, /step=N/); // Still on same stage

// Verify required field errors
await expect(
stage.form.getFieldError('field-uuid'),
).toBeVisible();
```

2. **Then fill fields normally** from the recording data.

Only test these validations (skip others):
- `required: true` — always test
- `minValue` / `maxValue` — test if present
- `minLength` / `maxLength` — test if present
- `pattern` — test if present
- `unique` — test if applicable (needs duplicate value scenario)
- `sameAs` / `differentFrom` — test if present

### Network State Verification

The sync middleware uses a 3-second debounce with leading+trailing edges. Each `interview.goto()` destroys the current page, killing any pending trailing-edge syncs. Stages that set data used by downstream skip logic or filtering must explicitly wait for that data to persist.

#### Form stages (EgoForm, AlterForm) must click Next to submit

Form data lives in React Hook Form's local state until the form is submitted. **You must click `interview.nextButton` at the end of every form stage** to flush the data to Redux. Without this, the sync middleware never sees the data.

For **EgoForm** stages, click Next as the last interaction (replaces the `toBeEnabled` assertion):

```typescript
// Submit form to flush data to Redux
await interview.nextButton.click();
```

For **AlterForm** stages with slides, click Next after filling the **last slide** (the earlier slides already submit when you click Next to advance):

```typescript
// Submit last slide to flush form data to Redux
await interview.nextButton.click();
```

Note: clicking Next navigates to the next stage, so the `afterEach` screenshot will capture the next stage's initial state rather than the current stage's final state.

#### Persistence waits for skip logic

After stages that set attributes consumed by downstream skip logic or filtering, add explicit waits using the protocol fixture. Available methods:

- `protocol.waitForNodes(interviewId, expectedCount)` — after node creation stages
- `protocol.waitForNode(interviewId, nodeName)` — when count alone is ambiguous
- `protocol.waitForNodeAttribute(interviewId, nodeName, attributeId)` — after CategoricalBin, OrdinalBin, or AlterForm stages (checks for non-null value)
- `protocol.waitForEgoAttribute(interviewId, attributeId, expectedValue)` — after EgoForm stages

Example for a CategoricalBin stage with downstream skip logic:

```typescript
test('Stage N: CategoricalBin', async ({ interview, stage, protocol }) => {
await interview.goto(N);

await stage.categoricalBin.dragNodeToBin('Dan', 'Yes');
await stage.categoricalBin.dragNodeToBin('Alice', 'No');

await expect(interview.nextButton).toBeEnabled();

// Wait for the LAST categorized node's attribute to persist
await protocol.waitForNodeAttribute(
interview.interviewId,
'Alice',
'variable-uuid',
);
});
```

**Always add `protocol` to the test's destructured fixtures** when using persistence waits.

### Stages With Placeholder Fixtures

Check the Fixture Availability Summary in STAGE_TEST_REFERENCE.md. If a stage type's fixture is marked **Placeholder**, generate a minimal test with a TODO referencing the placeholder:

```typescript
test('Stage N: Stage Label', async ({ page, interview }) => {
await interview.goto(N);

// TODO: stage.dyadCensus is a placeholder fixture — implement its
// interaction methods before writing full test assertions.
// See DyadCensusFixture JSDoc in stage-fixture.ts for the methods needed.
//
// Expected behavior from recording:
// - Dismiss intro panel
// - Select Yes/No for each node pair
// - Auto-advances after 350ms
});
```

Always reference the `stage.<fixtureName>` property (e.g., `stage.dyadCensus`, `stage.narrative`) so the test structure is ready — it just needs the fixture methods implemented. Never use raw Playwright selectors as a fallback.

### Skipped Stages

If the recording skips certain stage indices (e.g., conditional stages), add a comment:

```typescript
// Stages N-M are skipped (conditional on <condition from codebook>)
```

### Browser-Specific Skips

Add `test.skip()` for known browser limitations:

```typescript
// Skip geospatial on Firefox (no WebGL in Playwright's Firefox)
test.skip(browserName === 'firefox', 'Firefox lacks WebGL support in Playwright');
```

## Step 5: Verify Protocol File Location

Check if the `.netcanvas` file is already in `tests/e2e/data/`. If not, suggest copying it there and update the path constant accordingly.

## Step 6: Output Summary

After generating the test file, output:
1. Path to the generated test file
2. Number of stages covered
3. Number of validation tests included
4. List of stages with TODO placeholders (missing fixtures)
5. Suggested next steps (copy protocol to test data, run tests, etc.)

## Important Rules

- **Always use path aliases** (`~/tests/e2e/...`) for imports, never relative paths
- **Use `.js` extensions** in import paths (TypeScript with ESM)
- **Field names are UUIDs** — always use the variable UUID from the codebook, not the display name
- **Serial mode** — interview tests MUST use `test.describe.configure({ mode: 'serial' })`
- **Soft assertions for screenshots** — the `afterEach` capture pattern handles this via `interview.capture()`
- **No `console.log`** — project ESLint rule forbids it
- **Follow existing patterns** — match the style, structure, and conventions of `silos-protocol.spec.ts` exactly
20 changes: 7 additions & 13 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
# -------------------
# Required environment variables
# -------------------
DATABASE_URL="postgres://user:password@host:5432/database?schema=public" # A pooled connection URL for Prisma.
DATABASE_URL_UNPOOLED="postgres://user:password@host:5432/database?schema=public" # A non-pooling connection URL for Prisma

# -------------------
# Optional environment variables - uncomment to use
# -------------------

#DISABLE_ANALYTICS # true or false - If true, the app will not send anonymous analytics and error data. Defaults to false.
#SANDBOX_MODE=false # true or false - if true, the app will use the sandbox mode, which disables resetting the database and other features
#PUBLIC_URL="http://yourdomain.com" # When using advanced deployment, this is required. Set to the domain name of your app
#INSTALLATION_ID="your-app-name" # A unique identifier for your app, used for analytics. Generated automatically if not set.
#USE_NEON_POSTGRES_ADAPTER=false # true or false - If true, uses Neon serverless PostgreSQL adapter instead of standard pg adapter. Required for Vercel/Netlify deployments with Neon. Defaults to false.

# -------------------
# Required environment variables
# -------------------

POSTGRES_USER="postgres" # Your PostgreSQL username
POSTGRES_PASSWORD="postgres" # Your PostgreSQL password
POSTGRES_DATABASE="postgres" # Your PostgreSQL database name
POSTGRES_HOST="postgres" # Your PostgreSQL host
DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/${POSTGRES_DATABASE}?schema=public" # A pooled connection URL for Prisma.
DATABASE_URL_UNPOOLED="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/${POSTGRES_DATABASE}?schema=public" # A non-pooling connection URL for Prisma
#USE_NEON_POSTGRES_ADAPTER=false # true or false - If true, uses Neon serverless PostgreSQL adapter instead of standard pg adapter. Required for Vercel/Netlify deployments with Neon. Defaults to false.
Loading
Loading