diff --git a/AGENTS.md b/AGENTS.md index da121d43..3a08c6ef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -93,6 +93,7 @@ npm ci --legacy-peer-deps npm --prefix functions ci npm --prefix functions run lint npm --prefix functions run test:run -- --runInBand +CI=true npm test -- --watchAll=false --runInBand npm run test:rules CI=true DISABLE_ESLINT_PLUGIN=true npx --no-install react-scripts build ``` @@ -103,7 +104,8 @@ The following are **NOT AVAILABLE YET** on `main`: - `npm run emulators` and fail-closed Auth/Firestore/Functions isolation — owned by #99. - `npm run test:spa-navigation` — owned by #99. -- A trustworthy frontend Jest baseline/quality gate — owned by #103. + +The deterministic frontend Jest baseline is available through the command above. It is not yet a required CI quality gate; workflow enforcement remains **NOT AVAILABLE YET** under #105. Do not run Firebase-backed local development against the current production-configured client. Do not describe queued working-tree commands as merged or available. diff --git a/GITHUB_ISSUES.md b/GITHUB_ISSUES.md index 457b7ce4..b5e3b44c 100644 --- a/GITHUB_ISSUES.md +++ b/GITHUB_ISSUES.md @@ -121,12 +121,12 @@ Do not redesign routing or hosting in this issue; WEB-001 owns that. Do not incl ## CI-001 — Repair test gates and secure the deployment pipeline **Labels:** `priority:P0`, `type:security`, `type:testing`, `area:ci`, `size:L`, `needs-external-config` -**Status:** Published as tracker [#105](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/105); CI-001A [#103](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/103) is claimed and queued. The current workflow remains non-compliant. +**Status:** Published as tracker [#105](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/105); focused child [#103](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/103) owns the deterministic local frontend Jest baseline. The live issue records its merge state. Required workflow gating and protected deployment remain open under #105, so the current workflow is still non-compliant. **Depends on:** SAFETY-001 ### Problem -Frontend tests fail because the Jest environment lacks `TextEncoder`. CI runs a mutating `lint:fix` command and ignores failure with `|| true`. Deployment runs independently of CI, gives a full Firebase service-account JSON to the frontend build, installs `firebase-tools@latest`, deploys frontend before backend, and can succeed while silently skipping Firebase deployment. +Before #103, frontend tests failed because the Jest environment lacked `TextEncoder`. The focused child repairs that local baseline, but CI still runs a mutating `lint:fix` command, ignores failure with `|| true`, and does not require frontend Jest. Deployment runs independently of CI, gives a full Firebase service-account JSON to the frontend build, installs `firebase-tools@latest`, deploys frontend before backend, and can succeed while silently skipping Firebase deployment. ### Scope diff --git a/GITHUB_ISSUE_SLICES.md b/GITHUB_ISSUE_SLICES.md index aa9e8bf5..68afe276 100644 --- a/GITHUB_ISSUE_SLICES.md +++ b/GITHUB_ISSUE_SLICES.md @@ -16,7 +16,7 @@ An agent-ready child has one observable outcome, one trust boundary, explicit de | ID | Status/dependency | Bounded deliverable | Close evidence | | --- | --- | --- | --- | -| CI-001A | ready; SAFETY-001 | Add non-mutating fail-closed frontend lint and one deterministic local/CI quality gate for SPA, frontend, Functions, Rules, and build. | Deliberate lint/test failure blocks; clean-worktree check; all local suites pass. | +| CI-001A | published as #103; local baseline slice | Repair Jest browser globals and replace the stale starter test with a deterministic, no-provider-network MPRC app smoke test. Required workflow gating remains under #105. | Node 20 focused/full frontend tests, changed-file lint, clean diff, and diagnostic build pass; do not claim a required CI gate. | | CI-001B | owner_action; CI-001A | Protect backend-first staging/production deployment; pin tools/Actions; remove frontend service-account JSON; configure OIDC/WIF, approvals, missing-config failure, smoke and roll-forward. | PR gate and negative credential run; private two-owner IAM review; staging deployment evidence. | | SUPPLY-001A | ready; CI-001A | Prove `gpxparser` unused and remove its obsolete transitive chain. | `rg`/tree proof, before/after audits, root lockfile diff, full frontend checks. | | SUPPLY-001B | ready; CI-001A | Patch React Router within compatible 6.x and resolve/test future flags without route redesign. | Navigation/callback tests, audit delta, build. | diff --git a/OPERATIONS_RUNBOOK.md b/OPERATIONS_RUNBOOK.md index a634e098..6e45193d 100644 --- a/OPERATIONS_RUNBOOK.md +++ b/OPERATIONS_RUNBOOK.md @@ -174,9 +174,10 @@ Trigger or complete test-mode Checkout through the application when testing end- ```bash npm --prefix functions run lint npm --prefix functions run test:run -- --runInBand +CI=true npm test -- --watchAll=false --runInBand ``` -The SPA navigation command is **NOT AVAILABLE YET** and belongs to #99. A dependable frontend Jest baseline/quality gate is **NOT AVAILABLE YET** and belongs to [#103](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/103). +The SPA navigation command is **NOT AVAILABLE YET** and belongs to #99. The frontend Jest command is a dependable local baseline, but a required CI quality gate is **NOT AVAILABLE YET** and remains under [#105](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/105). ### Firestore Rules @@ -460,6 +461,8 @@ The earlier combined pre-extraction working tree reported 7 frontend tests, 4 SP Each isolated issue must publish its own exact base commit, commands, counts, and results. A documentation-only #104 branch starts from `main` with the merged #98 role-grant behavior; it must not claim the queued emulator, webhook, Rules, or frontend-test outcomes. -On the isolated #104 branch based at `ce22c110`, Functions lint and 17/17 Functions tests pass, 97/97 Firestore Rules tests pass with Java 17, and the diagnostic production build passes. Frontend Jest runs 15 Login tests but the App suite fails on the known missing `TextEncoder`; #103 owns that baseline repair. These are branch checks, not deployment evidence. +On the isolated #104 branch based at `ce22c110`, Functions lint and 17/17 Functions tests pass, 97/97 Firestore Rules tests pass with Java 17, and the diagnostic production build passes. At that point, frontend Jest ran 15 Login tests but the App suite failed on the known missing `TextEncoder`. These are historical branch checks, not deployment evidence. + +On the isolated #103 branch based at `0e03ac1`, Node 20 focused App tests pass 1/1 and the complete frontend suite on the branch passes 16/16. Changed-file lint, diff checks, and the diagnostic production build also pass. Service initialization and provider-network paths are prevented by test mocks; this was not a socket-monitored provider test, and the separately owned #99 Firebase/Sentry tests are not present on the branch. A separate read-only Node 20 compatibility run against the preserved #99 working tree passed those two suites 6/6, but that is not #103 branch or merge coverage. This proves a deterministic local baseline only. It does not prove a required CI gate, website publication, Firebase deployment, provider configuration, or production behavior. Provider configuration, production secrets, Netlify publication, Firebase deployment, and live behavior always require separate dated evidence. Local tests and a green GitHub summary do not prove them. diff --git a/README.md b/README.md index cc0bcd52..04527206 100644 --- a/README.md +++ b/README.md @@ -61,11 +61,12 @@ After #99 merges, the runbook and this section must be updated from its merged t ```bash npm --prefix functions run lint npm --prefix functions run test:run -- --runInBand +CI=true npm test -- --watchAll=false --runInBand npm run test:rules CI=true DISABLE_ESLINT_PLUGIN=true npx --no-install react-scripts build ``` -Rules tests require Java 17. The direct `react-scripts build` command is useful for a diagnostic compile because the normal `npm run build` runs the sitemap generator and may intentionally update `public/sitemap.xml`. The SPA navigation command belongs to queued #99, and a trustworthy frontend Jest baseline belongs to queued [#103](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/103); do not claim either from this documentation pull request. +Rules tests require Java 17. The direct `react-scripts build` command is useful for a diagnostic compile because the normal `npm run build` runs the sitemap generator and may intentionally update `public/sitemap.xml`. The SPA navigation command remains queued under #99. The frontend Jest command now provides a deterministic local baseline, but current CI does not run it as a required gate; [#105](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/105) owns that remaining workflow work. ## Working on the platform diff --git a/SECURITY.md b/SECURITY.md index 6238d568..42fc03d1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -82,7 +82,7 @@ The findings below describe the repository at the start of the 2026-07-12 assess | RISK-032 | `auditLog` arrays grow within primary documents and are editable by broad admins. | Move to append-oriented, immutable-by-client audit records with retention/export rules. | | RISK-033 | CSV export mitigates formula injection, but roster export has no explicit re-auth, download audit, row limit/streaming limit, or data-minimization profiles. | Add scoped exports, reason/re-auth, audit, minimum columns, safe filename IDs, and large-export controls. | | RISK-034 | Webhook error response includes the Stripe library's signature error detail. | Return generic client errors; keep sanitized structured diagnostics server-side. | -| RISK-035 | Frontend tests currently fail because `TextEncoder` is absent in Jest; CI's frontend lint step is allowed to fail with `|| true`; Functions have only seven unit tests and about 20% line coverage. | Repair the harness, make non-mutating lint/test gates mandatory, and add domain/integration coverage. | +| RISK-035 | The local frontend Jest baseline is deterministic, but CI does not run it as a required gate; CI's frontend lint step is still allowed to fail with `|| true`, and broader domain/integration coverage remains incomplete. | Make non-mutating lint/test gates mandatory and add domain/integration coverage. | | RISK-036 | Production and staging deployment topology is inconsistent in docs/workflows; backend deploy follows frontend and can silently skip when a secret is absent. | Define environment-protected deployments, deploy compatible backend first, fail required production stages closed, and add rollback evidence. | | RISK-037 | Account/registration deletion, export, retention, backup, and restore procedures are incomplete. | Approve retention matrix, automate minimization, support access/deletion requests, and test backup restoration. | | RISK-038 | Source-controlled secret scan is ad hoc; no continuous secret scanner, dependency update bot, SBOM, provenance, or branch protection is documented. | Add secret/dependency/code scanning, reviewed lockfile updates, protected environments/branches, and artifact provenance appropriate to project scale. | diff --git a/src/App.test.jsx b/src/App.test.jsx index 2a68616d..aa7ea58c 100644 --- a/src/App.test.jsx +++ b/src/App.test.jsx @@ -1,9 +1,41 @@ +/* eslint-env jest */ + import React from 'react'; import { render, screen } from '@testing-library/react'; import App from './App'; -test('renders learn react link', () => { +jest.mock('./services/ServiceLocatorProvider', () => function TestServiceLocatorProvider({ + children, +}) { + return children; +}); + +jest.mock('./services/ServiceLocatorContext', () => ({ + useServiceLocator: () => ({ services: null, isReady: false }), +})); + +jest.mock('./services/hooks/useAuth', () => ({ + useAuth: () => ({ + user: null, + isLoading: false, + isAuthenticated: false, + isMember: false, + isAdmin: false, + signIn: jest.fn(), + signOut: jest.fn(), + register: jest.fn(), + }), +})); + +test('renders the MPRC home route without contacting Firebase', () => { render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); + expect(screen.getByRole('heading', { + level: 1, + name: /mid-peninsula running club/i, + })).toBeInTheDocument(); + expect(screen.getByRole('link', { name: /skip to content/i })).toHaveAttribute( + 'href', + '#main-content', + ); + expect(screen.getByRole('main')).toHaveAttribute('id', 'main-content'); }); diff --git a/src/setupTests.js b/src/setupTests.js index 8f2609b7..71ccfbfc 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -1,5 +1,27 @@ +/* eslint-env jest */ + // jest-dom adds custom jest matchers for asserting on DOM nodes. // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom import '@testing-library/jest-dom'; + +// Firebase Functions' Node test bundle pulls in undici, which expects the Web +// Encoding globals that Jest's jsdom environment does not provide by default. +// Use Node's standards-compatible implementations in tests only. +const { TextDecoder, TextEncoder } = require('util'); +const { + ReadableStream, + TransformStream, + WritableStream, +} = require('stream/web'); + +global.TextDecoder = TextDecoder; +global.TextEncoder = TextEncoder; +global.ReadableStream = ReadableStream; +global.TransformStream = TransformStream; +global.WritableStream = WritableStream; + +// jsdom does not implement scrolling. App smoke tests need a harmless test-only +// shim because ScrollToTop invokes this API after route changes. +global.scrollTo = jest.fn();