Sprint 3 Test Plan
This page names the test type for each acceptance test (AT) of the Sprint 3 user stories
(#182). It also names the test
that checks the AT now, or the test that the team plans to add. The
Traceability Matrix records the results of these tests
(#214).
Status was updated on October 27, 2026. The scope is 14 stories and 50 ATs after US 3.0.4 was removed. See release checks for local browser evidence and open release checks.
Test types
| Type |
What it checks |
Tool |
Where |
| Unit |
One function or server action. Supabase, S3 and other services are mocks. |
Jest |
webapp/__tests__/lib/, webapp/__tests__/components/queries.* |
| Integration |
One page, component or API route with its children. The server actions are mocks. |
Jest and React Testing Library |
webapp/__tests__/pages/, webapp/__tests__/components/, webapp/__tests__/app/ |
| Database |
One SQL function on a local Supabase, with no mocks. |
Jest |
webapp/__tests__/db/ |
| E2E |
A full user flow in a browser against a local Supabase. |
Browser tooling; existing Cypress CI jobs |
Local release checks; webapp/cypress/e2e/ |
| Manual |
A flow that the tools cannot check, for example a device that goes offline. A team member does the steps and records the result in the PR. |
Browser |
The PR of the story |
Rules:
- Every AT has at least one automated test, unless the AT is marked Manual.
- An AT that refuses an action (for example "only the author can edit") has a test for the refusal,
not only for the success scenario.
- Put the story and AT number in the test name, for example
'(4.0.4 AT2) ...'. See
How to update this page.
- File paths are relative to
webapp/. A number after a colon is the line of the test.
How to read the Status column
| Status |
Meaning |
| Exists |
A test that checks the AT is on sprint-3. |
| Partial |
A test exists, but it checks only part of the AT. The note tells which part is missing. |
| Planned |
No test exists yet. The team adds the named test with the story's PR. |
| Manual |
The AT is checked by hand. The steps are in the note. |
1.0.3 Show history of Site Inspections on a Steward's Profile (Green)
| AT |
Type |
Test |
Status |
| AT1: the profile lists previous inspections with site and date |
Integration |
__tests__/components/ProfilePage.test.tsx:141 (site and date of each row), :161 (empty history), :256, :262, :282 (paging) |
Exists |
| AT2: a past inspection opens with its submitted information |
Integration |
__tests__/components/ProfilePage.test.tsx:108 |
Exists |
| AT3: hidden answers are not shown to other stewards |
Unit, Integration |
__tests__/components/queries.exportPaging.test.ts:182, :208; __tests__/app/api/site-images.test.ts:157, :193; __tests__/pages/SiteDetails.test.tsx:564 |
Exists |
1.0.4 Aggregate User Activity and Contributions on a user's profile (Green)
| AT |
Type |
Test |
Status |
| AT1: the aggregates match the user's data |
Unit, Integration |
__tests__/app/api/profile.test.ts (contribution statistics), __tests__/components/ProfilePage.test.tsx |
Passed locally after the shared-branch profile test fixes; required CI pending |
| AT2: a new report adds 1 to the SIR count |
Integration |
__tests__/components/ProfilePage.test.tsx:173 loads the profile, remounts it after a new report and checks the count from 3 to 4 |
Exists |
1.0.6 Select and Edit Skills and Focus Areas (Red)
| AT |
Type |
Test |
Status |
| AT1: dropdowns offer existing values and Other |
Integration |
__tests__/components/TagPicker.test.tsx:13,30, __tests__/components/ProfilePage.test.tsx:372 |
Exists |
| AT2: typing suggests similar values |
Unit, Integration |
__tests__/lib/profileTags.test.ts, __tests__/components/TagPicker.test.tsx:41 |
Exists |
| AT3: saved changes appear on the profile |
Integration |
__tests__/components/ProfilePage.test.tsx:372, __tests__/components/TagPicker.test.tsx:94 |
Exists |
2.0.1 View Sites and Past Visits on an Interactive Map (Red)
| AT |
Type |
Test |
Status |
| AT1: every active Site is drawn with its boundary |
Integration, E2E |
__tests__/components/SiteMap.test.tsx (boundary layer and error note, :324, :1160); cypress/e2e/map_filter.cy.ts |
Partial: add a (2.0.1 AT1) test that checks inactive sites are not drawn |
| AT2: a GPX track is drawn in a style different from the boundary |
Unit, Integration |
__tests__/lib/gpxTrack.test.ts:78; __tests__/components/SiteMap.test.tsx:1619 |
Exists |
| AT3: the Site page links to its location on the map |
Integration |
__tests__/pages/SiteDetails.test.tsx:300 |
Exists |
| AT4: only the newly selected Site is highlighted |
Integration |
__tests__/components/SiteMap.test.tsx:1541, :1550, :1565; __tests__/pages/Sites.test.tsx:960 |
Exists |
2.0.8 View a Site's Details From the Map (Blue)
| AT |
Type |
Test |
Status |
| AT1: selecting a Site opens its detail view |
Integration, E2E |
__tests__/components/SiteMap.test.tsx:681, :697; cypress/e2e/map_site_popup.cy.ts |
Exists |
| AT2: the detail view shows the same name, county and last inspection date as the Site page |
Unit, Integration, E2E |
__tests__/components/queries.boundaries.test.ts:150, :171, :191, :201; __tests__/lib/boundaries.test.ts:230, :239; __tests__/components/SiteMap.test.tsx:705, :751, :768; cypress/e2e/map_site_popup.cy.ts |
Exists |
| AT3: "View SIRs" goes to the past SIRs |
Integration, E2E |
__tests__/components/SiteMap.test.tsx:789, :805; cypress/e2e/map_site_popup.cy.ts |
Exists |
2.0.9 Upload a GPX Track of a Site Visit (Red)
| AT |
Type |
Test |
Status |
| AT1: a valid .gpx file is saved and linked to the Site and the account |
Unit, Integration |
__tests__/lib/gpx-track-actions.test.ts:72, :90; __tests__/components/GpxTrackUpload.test.tsx:68 |
Exists |
| AT2: a file without the .gpx extension shows "only GPX files" |
Unit, Integration |
__tests__/lib/gpx-track-actions.test.ts:122; __tests__/components/GpxTrackUpload.test.tsx:99 |
Exists |
| AT3: an invalid .gpx file or one with no track points is refused and nothing is saved |
Unit, Integration |
__tests__/lib/gpx-track-actions.test.ts:235, :246; __tests__/components/GpxTrackUpload.test.tsx:140 |
Exists |
| AT4: a stored track reads back with the same track points |
Unit, Integration |
__tests__/components/queries.gpxTracks.test.ts:109; __tests__/lib/gpxTrack.test.ts:33; __tests__/components/GpxTrackUpload.test.tsx:166 |
Exists |
| AT5: a file over the size limit is refused with a message that names the limit |
Unit, Integration |
__tests__/lib/gpx-track-actions.test.ts:257; __tests__/components/GpxTrackUpload.test.tsx:111 |
Exists |
| AT6: several files at once: each valid file is saved and each refused file is listed with its reason |
Unit, Integration |
__tests__/lib/gpx-track-actions.test.ts:289; __tests__/components/GpxTrackUpload.test.tsx:123, :140 |
Exists |
2.0.12 Score a Site for To-Visit Priority (Red)
| AT |
Type |
Test |
Status |
| AT1: An Admin enters the three scores and they are saved |
Unit, Integration |
__tests__/lib/sitePriority.test.ts:18, __tests__/lib/site-priority-actions.test.ts:44, __tests__/components/SitePriority.test.tsx:48 |
Exists |
| AT2: The Site page shows one to-visit priority calculated from the scores |
Unit, Integration |
__tests__/lib/sitePriority.test.ts:7, __tests__/lib/site-priority-actions.test.ts:44, __tests__/components/SitePriority.test.tsx:48 |
Exists |
| AT3: Two Sites with the same scores show the same priority |
Unit, Integration |
__tests__/lib/sitePriority.test.ts:12, __tests__/lib/site-priority-actions.test.ts:137 |
Exists |
| AT4: A Steward sees the priority and the three scores |
Unit, Integration |
__tests__/lib/site-priority-actions.test.ts:110, __tests__/components/SitePriority.test.tsx:21 |
Exists |
| AT5: The server refuses a Steward's change and the scores stay the same |
Unit, Integration |
__tests__/lib/site-priority-actions.test.ts:62, __tests__/components/SitePriority.test.tsx:73 |
Exists |
2.0.13 Flag Site Points That Are a Year Old (Red)
| AT |
Type |
Test |
Status |
| AT1: An Admin sees a reminder when the points are more than a year old |
Unit, Integration |
__tests__/lib/sitePriority.test.ts:38, __tests__/lib/site-priority-actions.test.ts:125, __tests__/components/SitePriority.test.tsx:100 |
Exists |
| AT2: No reminder when the points are less than a year old |
Unit, Integration |
__tests__/lib/sitePriority.test.ts:42, __tests__/lib/site-priority-actions.test.ts:131, __tests__/components/SitePriority.test.tsx:106 |
Exists |
| AT3: Saving new points removes the reminder and stores the new date |
Unit, Integration |
__tests__/lib/site-priority-actions.test.ts:44, __tests__/components/SitePriority.test.tsx:113 |
Exists |
| AT4: A Steward does not see the Admin reminder |
Unit, Integration |
__tests__/lib/site-priority-actions.test.ts:110, __tests__/components/SitePriority.test.tsx:106 |
Exists |
2.0.14 Color Map Sites by Type (Blue)
| AT |
Type |
Test |
Status |
| AT1: a boundary uses the saved site or type color |
Unit, Integration |
__tests__/components/SiteMap.test.tsx, __tests__/components/queries.boundaryColors.test.ts, __tests__/lib/boundaries.test.ts |
Exists |
3.0.3 Export All Digital Files (Red)
| AT |
Type |
Test |
Status |
| AT1: export produces a ZIP |
Unit, Integration |
__tests__/lib/processExportJob.test.ts (buildExportZip), __tests__/components/ExportAllFilesButton.test.tsx |
Exists |
| AT2: the archive includes all images and file types |
Unit, Integration |
__tests__/lib/processExportJob.test.ts (collectExportEntries, paging and missing objects), __tests__/app/api/export.test.ts |
Exists |
4.0.2 Offline and Online Map Layers (Red)
| AT |
Type |
Test |
Status |
| AT1: download a map area for offline use and see its download status |
Unit, Integration |
__tests__/components/OfflineSavePanel.test.tsx:218, :257, :269, :291; __tests__/lib/map/offline/tileCache.test.ts |
Exists |
| AT2: offline, downloaded tiles render and the map pans and zooms |
Unit, E2E |
__tests__/lib/map/offline/tileCache.test.ts, local browser run |
Exists; local browser passed |
| AT3: offline, missing tiles outside the saved area are indicated |
Integration, E2E |
__tests__/components/SiteMap.test.tsx (offline badge), local browser run |
Exists; local browser passed |
| AT4: online, the map uses live requests instead of the cache |
Integration, E2E |
__tests__/components/SiteMap.test.tsx (4.0.2 AT4), local reconnect check |
Exists; local browser passed |
4.0.4 Edit My Previous Inspections (Blue)
| AT |
Type |
Test |
Status |
| AT1: the author opens a past SIR and enters edit mode |
Integration, E2E |
__tests__/pages/UserEditSiteInspectionForm.test.tsx:616, :641; cypress/e2e/edit_own_report.cy.ts |
Exists |
| AT2: another steward cannot enter edit mode or change the SIR |
Unit, Integration, E2E |
__tests__/components/queries.report-writes.test.ts:157,167, __tests__/pages/UserEditSiteInspectionForm.test.tsx:623; local browser run; cypress/e2e/edit_own_report.cy.ts |
Exists |
| AT3: clearing a required answer blocks submit and lists missing questions |
Unit, Integration, E2E |
__tests__/components/queries.report-writes.test.ts:200,212, __tests__/pages/UserEditSiteInspectionForm.test.tsx:723; local browser run |
Exists |
| AT4: saved edits show everywhere the report is rendered |
Integration, E2E |
__tests__/pages/UserEditSiteInspectionForm.test.tsx:825, :842; cypress/e2e/edit_site_inspection_form.cy.ts |
Partial: add tests for the profile history and the PDF export (#210) |
5.0.2 Reset a Forgotten Password (Green)
| AT |
Type |
Test |
Status |
| AT1: "Forgot password" with an email sends an email |
Integration, E2E |
__tests__/app/password-reset-request-code.test.ts:44 to :135; __tests__/pages/ForgotPassword.test.tsx:33, :41, :55; __tests__/pages/Login.test.tsx:51; cypress/e2e/password_reset_wrong_code.cy.ts |
Exists |
| AT2: the user verifies a code before the reset |
Integration, E2E |
__tests__/app/password-reset-verify-code.test.ts:48, :55, :63; __tests__/pages/ForgotPassword.test.tsx:68, :87; __tests__/pages/ResetPassword.test.tsx:32; cypress/e2e/password_reset_wrong_code.cy.ts |
Exists |
| AT3: the new password works and the old one does not |
Integration, E2E |
__tests__/pages/ResetPassword.test.tsx; local SMTP and browser run |
Exists; real local Auth passed |
| AT4: no admin step is needed |
Integration |
__tests__/pages/ForgotPassword.test.tsx:41, :68; __tests__/pages/ResetPassword.test.tsx:61 |
Exists |
6.0.1 Store Each Site's Geometry From the Shared Source (Blue)
The tests in __tests__/db/ run the real SQL function on a local Supabase. npm test skips them. Run them with
npm run test:db -- --runInBand after supabase start.
| AT |
Type |
Test |
Status |
| AT1: a Site with a boundary in the KML is linked to it, and the stored boundary has the same longitude and latitude values as the KML |
Unit, Database |
__tests__/lib/kmlImport.test.ts:205, :214, :253; __tests__/lib/kml-import-actions.test.ts:144; __tests__/db/site-boundary-links.test.ts:102, :135 |
Exists |
| AT2: a Site read from the database has a boundary link or no link |
Database |
__tests__/db/site-boundary-links.test.ts:102, :114, :157 |
Exists |
| AT3: each Site is linked to a boundary or is on the list of Sites with no boundary for an Admin |
Unit, Integration, Database |
__tests__/lib/kml-import-actions.test.ts:325, :337; __tests__/components/KmlImportModal.test.tsx:41, :59; __tests__/db/site-boundary-links.test.ts:114, :125, :148 |
Exists |
| AT4: the Unlinked filter shows only the Sites with no boundary, and the Linked filter shows only the Sites with a boundary |
Unit, Integration |
__tests__/lib/boundaryFilters.test.ts:228; __tests__/pages/AdminSites.test.tsx:107; __tests__/components/MapAreaFilters.test.tsx:36 |
Exists |
| AT5: the server refuses a Steward who calls the Admin import, and returns no list |
Unit |
__tests__/lib/kml-import-actions.test.ts:116, :128, :137 |
Exists |
Summary
The traceability matrix has one row per AT. Coverage is now:
| Story |
Team |
ATs |
Covered |
Partial |
None |
| US 1.0.3 |
Green |
3 |
AT1, AT2, AT3 |
- |
- |
| US 1.0.4 |
Green |
2 |
AT1, AT2 |
- |
- |
| US 1.0.6 |
Red |
3 |
AT1, AT2, AT3 |
- |
- |
| US 2.0.1 |
Red |
4 |
AT2, AT3, AT4 |
AT1 |
- |
| US 2.0.8 |
Blue |
3 |
AT1, AT2, AT3 |
- |
- |
| US 2.0.9 |
Red |
6 |
AT1, AT2, AT3, AT4, AT5, AT6 |
- |
- |
| US 2.0.12 |
Red |
5 |
AT1, AT2, AT3, AT4, AT5 |
- |
- |
| US 2.0.13 |
Red |
4 |
AT1, AT2, AT3, AT4 |
- |
- |
| US 2.0.14 |
Blue |
1 |
AT1 |
- |
- |
| US 3.0.3 |
Red |
2 |
AT1, AT2 |
- |
- |
| US 4.0.2 |
Red |
4 |
AT1, AT2, AT3, AT4 |
- |
- |
| US 4.0.4 |
Blue |
4 |
AT1, AT2, AT3 |
AT4 |
- |
| US 5.0.2 |
Green |
4 |
AT1, AT2, AT3, AT4 |
- |
- |
| US 6.0.1 |
Blue |
5 |
AT1, AT2, AT3, AT4, AT5 |
- |
- |
| Total |
|
50 |
48 |
2 |
0 |
US 3.0.4 is removed. The two partial rows (2.0.1 AT1 and 4.0.4 AT4) still need evidence. 1.0.3 AT1
was counted as partial in this table only; its row above and in the matrix already listed its tests
(ProfilePage.test.tsx:141, :161), so it is covered. The combined local suite passes
1895 tests, with 36 skipped (131 passing suites; the four database suites skip without a local
Supabase). Required CI must pass before release.
Local run, October 27, 2026
GitHub Actions minutes ran out, so the checks were run on a local machine instead. The source was
main at 45f1cf99 in a clean worktree, with a local Supabase and the local seed
(scripts/e2e_seed.js). Production was not used. The tables below are this local run.
Cybera instance. The team also connected a Cybera instance and ran
the tests there, and the results were confirmed on that instance (reported by Saumya, October 27,
2026). The run link, the commit and the per-spec counts of that run are not recorded here; add them
when they are available.
| Check |
Result |
Jest (npm test) |
1900 passed, 36 skipped, 0 failed (131 suites passed, 4 skipped) |
Database suites (npm run test:db, real local Supabase) |
4 suites, 36 tests, all passed |
Type check (tsc --noEmit) |
Passed |
Lint (eslint .) |
Passed |
Production build (next build) |
Passed |
Cypress, the 10 specs of the CI list, Electron, against next start on port 3000 |
37 tests: 31 passed, 3 failed, 3 skipped. 7 of 10 specs passed. |
Cypress results by spec:
| Spec |
Result |
logout.cy.ts (5.0.4) |
5 passed |
authorized_check.cy.ts (3.0.8) |
1 passed |
add_site.cy.ts (2.0.2) |
6 passed |
archive_report.cy.ts (7.0.8) |
5 passed |
deactivate_site.cy.ts (2.0.3) |
6 passed |
edit_own_report.cy.ts (4.0.4 AT1, AT2) |
2 passed |
password_reset_wrong_code.cy.ts (5.0.2) |
3 passed |
profile.cy.ts (1.0.2) |
1 passed, 1 failed: AT5 looks for a label with the text "Skills", and the 1.0.6 dropdowns replaced it. The spec is out of date, not the app. |
map_site_popup.cy.ts (2.0.8) |
2 passed, 1 failed: AT2 expects "Jan 15, 2026" and the popup shows "Jan 14, 2026". The seed date is 2026-01-15, so the popup date moves back one day in a time zone behind UTC (this run was in MDT). Possible app bug to confirm. |
map_filter.cy.ts (2.0.6) |
1 failed, 3 skipped: the before each hook waits for "3 protected areas found", and the map page no longer shows that text. The spec is out of date since the old map was removed (#320). |
Limits of this run:
- Several specs set
const APP = 'http://localhost:3000' (for example map_site_popup.cy.ts,
password_reset_wrong_code.cy.ts, archive_report.cy.ts and deactivate_site.cy.ts), so the
server had to run on port 3000. The same constant makes these specs ignore CYPRESS_BASE_URL,
so they cannot run against the deployed site until it is replaced with Cypress.config('baseUrl').
- The three failures above are not yet fixed or re-run. The Cypress CI list still contains
profile.cy.ts and map_filter.cy.ts.
- This document records no Cypress run against the deployed site. The Cybera run above is
reported, not recorded with counts or a link.
How to run the tests
From webapp/:
- Jest (unit and integration):
npm test
- One file:
npx jest __tests__/components/SiteMap.test.tsx
- Local browser checks: follow Sprint 3 release checks. The October 26 run used a local Supabase, local SMTP and disposable accounts and made no Cypress run. The October 27 local run above did run Cypress.
- Existing Cypress CI jobs are in
.github/workflows/e2e.yaml. AGENTS.md says an agent never runs Cypress; the October 27 run was made at the user's explicit request, against the local Supabase only.