Testing

Current state

Two layers:

npm test              # Vitest single-run (unit tests with mocks)
npm run test:watch    # Vitest watch mode
npm run test:ci       # Vitest single-run + real-engine contract tests
npm run test:engines  # Real gameslib contract/integration tests only

CI runs npm run test:ci via the reusable .github/workflows/ci-test.yml workflow. That job is a prerequisite for both dev and prod deploys (deploy-dev.js.yml / deploy-prod.js.yml use needs: test). The same job also runs standalone on pull requests via .github/workflows/test.yml; pushes to develop / main only run tests inside the deploy workflows (no duplicate run).

Two gameslib installs in CI:

Step gameslib Purpose
Test job (install-ap-deps --for-tests) @development (full registry) test:engines — includes experimental games
Deploy build (install-ap-deps) Pinned production version from ci-deps.json What ships to users

Renderer stays on the pinned ci-deps.json version in both cases.

Test layers

Layer Location What it guards
Mocked unit tests src/lib/GameMove/explorationMoves.test.js, src/lib/GameMove/gameStuff.test.js Edge cases with lightweight engine mocks; clear partial move routing in processNewMove
Real-engine contracts bin/test-exploration-contracts.mjs Partial/persist probe + Submit visibility against actual gameslib (includes Pinch partial F7)
Other lib tests src/lib/GameMove/exploration.test.js, src/lib/Lab/, etc. Save/merge, playground payloads

Why a Node script for real engines? Jest (via the old CRA toolchain) loaded @abstractplay/gameslib's TypeScript sources instead of the compiled build/ output. bin/test-exploration-contracts.mjs uses the same resolver as production. Vitest unit tests use a resolve.alias to the compiled gameslib build/ output (see vite.config.js).

Fixtures

Regression scenarios live under src/lib/GameMove/fixtures/. Each fixture is inline JSON (never read from bin/ or external files at runtime).

Production gameslib builds omit experimental games from the registry (APGAMES_PRODUCTION=1 in gameslib).

bin/test-exploration-contracts.mjs prints @abstractplay/gameslib@<version>, active/skipped contract counts, and runs only applicable contracts.

Clear-move-after-partial is covered by both gameStuff.test.js (mocked routing) and the Pinch pinch-partial-f7 engine contract.

To add a new regression case when you find a play-page bug:

  1. Add state + move(s) in a file under fixtures/ (or extend an existing game file).
  2. Export a contract object:
{
  id: "my-game-scenario",       // unique, used in test output
  metaGame: "mygame",
  state: JSON.stringify({...}), // or null for a fresh GameFactory(game)
  move: "the-move-string",
  developmentOnly: true,         // optional — experimental game; skipped if not in registry
  whileEditing: { partial: true, persistable: false },
  afterComplete: { partial: false, persistable: true },
  submitAfterComplete: true,    // enables integration assertions in test:engines
}
  1. Append to EXPLORATION_CONTRACTS in src/lib/GameMove/fixtures/index.js.

The engine runner in bin/ picks up new contracts automatically.

Shared helpers

Vitest config

vite.config.js test block: jsdom environment, src/setupTests.js (jest-dom matchers), @abstractplay/gameslib alias to compiled build/.

Adding component tests

  1. Colocate tests as Component.test.js next to the component, or under src/pages/ for page shells.
  2. Import from @testing-library/react and @testing-library/user-event.
  3. Mock heavy dependencies:
    • aws-amplify / Auth for auth flows
    • fetch for API calls
    • @abstractplay/gameslib / @abstractplay/renderer for game components (when not using test:engines)

What to prioritize next

High-value targets if expanding coverage:

Linting

ESLint config: .eslintrc.json (eslint:recommended, eslint-plugin-react, react-hooks, jsx-a11y, eslint-config-prettier).

npm run lint       # report issues in src/ and bin/
npm run lint:fix   # auto-fix where ESLint can
npm run format     # Prettier on src/**/*.{js,jsx}

CI runs npm run lint after tests (see .github/workflows/ci-test.yml). Lint fails on errors only; hook-deps and a11y findings are warnings for now.

Related