Skip to content
Back to skills

Unit And Component Tests

ASecurity

Sets up Jest, checks unit, component and route tests (RNTL 14, renderWithProviders, SDK mocks, fakes, fast-check, Stryker). Use when writing a test or mock, or Jest fails or flakes. Not for goldens (golden-and-snapshot-tests).

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 18, 2026
developmenttypescriptgosqlreactnodetestinggitapi

Works with

  • api

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 19 files and shows the line behind each finding

Scanned October 18, 2026

npx -y skills add zakariaf/ReactNative-Skills --skill unit-and-component-tests --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Unit And Component Tests?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Unit And Component Tests
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/zakariaf-unit-and-component-tests/badge)](https://www.skillsdirectory.com/skills/zakariaf-unit-and-component-tests)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: unit-and-component-tests
description: Sets up Jest, checks unit, component and route tests (RNTL 14, renderWithProviders, SDK mocks, fakes, fast-check, Stryker). Use when writing a test or mock, or Jest fails or flakes. Not for goldens (golden-and-snapshot-tests).
---

# Unit and component tests

Every store, service, hook, component, route and pure module of an Expo app is proven in Jest (jest-expo, React Native Testing Library 14, fast-check, Stryker) through one root configuration, with components rendered inside the app's real providers on both iOS and Android presets. This skill builds that set-up, says how each kind of test is written, and its scripts prove the set-up, the test code and the mutation results.

## Rules that must hold

1. **One Jest config at the repo root, run from the root, and every test file runs in exactly one place.** Projects: `unit` (preset `jest-expo/ios`), `android` (`jest-expo/android`, `*.android.test.ts(x)` only) and `golden` (`*.golden.test.ts` only, in every app: data goldens with the unit set-up, and Skia's CanvasKit environment only while the app depends on `@shopify/react-native-skia`). Long-running simulations run only through `jest.sim.config.js`. Every test is a `<unit>.test.ts(x)` file in `src/`, `scripts/`, `plugins/` (local config plugins) or `test/` (no `__tests__/<name>-test.tsx`, no `.spec.ts`), and never inside the routes folder (`src/app/`). No second Jest config (a `package.json` `jest` key, a `jest.config.ts`). Why: Expo Router bundles every file in `src/app/` as a route, so a test there breaks `expo export` while Jest still passes; jest-expo reads Babel options from the working directory; `.android.tsx` files, `Platform.OS` branches and the hardware back button exist only under the Android preset; a CanvasKit environment without Skia fails every Jest run, and a golden project kept behind Skia silently never ran data goldens, and neither do tests in Expo's guide layout under this config; two configs stop Jest with "Multiple configurations found" (all verified).
2. **Exact test-tool pins and one React.** jest 29.7.0 (Expo's line), jest-expo from `expo/bundledNativeModules.json`, `@react-native/jest-preset` equal to `react-native`, RNTL 14.0.1 installed in the same command as test-renderer 1.2.0 (its minor equals React's), fast-check 4, the three Stryker packages on one version; `react` and `react-native` exact (when multi-package: root `overrides`); no `react-test-renderer`. Why: npm alone picked test-renderer 1.3.0 (React 19.3), a monorepo without overrides hoisted a second React for the tests, and React 19 deprecates `react-test-renderer`, which jest-expo still installs, so a test importing it keeps passing unnoticed (verified).
3. **Import through the path aliases**, `@/` to `src/` and `@test/` to `test/`, mirrored in `moduleNameMapper`; when multi-package, another workspace by its package name, mapped to its `src/`. Why: jest-expo derives aliases only from the working directory's `tsconfig.json`, and through workspace symlinks Stryker mutates a copy the tests never run (verified).
4. **Await every RNTL call** (`render`, `renderHook`, `act`, `waitFor`, `waitForElementToBeRemoved`, `fireEvent`, `findBy*`, `user.*`, the render-helper functions, and `rerender`/`unmount` on what a render returned). Query `getByRole` first, `findBy*` for what appears later, `queryBy*` only to prove absence; press through `userEvent.setup()`. Why: RNTL 14 is async; an un-awaited call runs after the assertions.
5. **Render every component and hook through the render helper** (`renderWithProviders`, `renderHookWithProviders`, `createProvidersWrapper` from `@test/render-with-providers`), and every route test through `renderRouterWithProviders` (`@test/render-router-with-providers`), never Expo Router's `renderRouter`. The helpers build the app's real providers fresh for each render from `test/test-app.tsx`: catalogs where a missing message throws, stores seeded through their real factories, only the services the test passes (the others throw and name themselves). They return what the tree holds. A test whose unit reads nothing from context, that supplies the provider under test itself (`<I18nProvider>` around `<T />`), or that proves a hook's no-provider default keeps plain `render`/`renderHook` with `// no-providers: <why>` in the file. Assert visible text, roles and accessible names; assert styles only in the design-system folders or under `// allow-style-assertion: <why>`; no component snapshots. Why: tests see what the app, VoiceOver and TalkBack see, and survive refactors; with RNTL 14, `renderRouter`'s matchers throw and it leaves fake timers on (verified).
6. **Native SDKs only through root `__mocks__/<sdk>.ts`.** An adapter test pairs `jest.mock('<sdk>')` with a locally typed `jest.requireMock`; no test imports the SDK. Skia is mocked once, in `jest.setup.ts`. Why: these SDKs crash on import in Jest, and `requireMock` without `jest.mock` reads a second instance with zero calls (verified).
7. **Everything above an adapter is tested through fakes** (`fake-<port>.ts`: a small real implementation with a shared call log); store state changes only by seeding and through the store's own actions, never by mocking a store module. Why: fakes make service rules millisecond tests and prove cross-service order with one assertion.
8. **Tests are deterministic.** `TZ=UTC` in every Jest config, an injected `ClockPort` and seeded RNG, `flushMicrotasks()` instead of sleeps, fake timers only for UI timers, no retries, and Android behaviour in `<unit>.android.test.ts(x)`, never by overwriting `Platform.OS`. Why: a flaky test is a bug; an overwritten `Platform.OS` leaks into later tests and skips `.android` files.
9. **Pure logic gets examples, fast-check properties and one pinned value**; a counterexample becomes an example test before the fix. Why: properties alone left 3 of 8 mutants of a random-number generator alive; the pinned sequence killed them.
10. **Tests that need Node APIs live under the root `test/`** (`node:sqlite`, `node:fs`, `Buffer`). Why: the app program has only `jest` types and lint bans `node:*` imports in `src/`.
11. **Coverage 90/90/90/85 globally and 95/95/95/90 for the logic keys; mutation score at least 75%, measured on mutants a test really ran**, survivors killed by boundary examples. Never lower a threshold, never `istanbul ignore`, never `// Stryker disable`. Why: one agent writes code and tests; coverage stood at 97.8% while the mutation score was 77.1%, and Stryker passes a run whose every mutant was skipped as static (score NaN, verified); Stryker obeys a `// Stryker disable` comment and reports those mutants as Ignored, outside the score, so quality-gates' bypass check fails both comments.
12. **Every source file ships with a test or starts with `// device-only: covered by <check>`** in its first 6 lines, and only native adapters, Skia and frame-callback modules, composition files and tooling entry points may carry it. `jest.config.js` leaves exactly the marked files out of coverage through the tested helper `scripts/quality/device-only.ts`; fakes and pure helpers always get tests. A route file in `src/app/` meets this through the route test that renders its default export from `src/features/` (never a test beside it) or the marker naming the flow that opens it. Why: untested composition files held a real app at 83.8% statements, and a hand-written exclusion hides real code.

## Workflow

1. **Check the set-up** from the repo root: `node ${CLAUDE_SKILL_DIR}/scripts/check-test-setup.mjs .`. For each missing piece copy the template of the same path from `templates/` (root configs, `scripts/quality/`, `__mocks__/`; `babel.config.js` as it is: its Stryker override compiles only the files Stryker instrumented without the Worklets and Reanimated plugins, see step 8), and merge `assets/package.test-deps.json` into `package.json` (scripts and exact devDependency pins). If the app followed Expo's unit-testing guide, delete its `"jest": { "preset": "jest-expo" }` key from `package.json` and its `"test": "jest --watchAll"` script: `jest.config.js` is the one config. The guide also puts tests in `__tests__/<name>-test.tsx`, which no project runs: move each next to its unit as `<unit>.test.tsx` and remove `react-test-renderer`, as the guide itself says (`jest-orphan-tests` and `test-dep-deprecated` name each one). Install the Testing Library with its companion in one command: `npm install -D --save-exact @testing-library/react-native@14.0.1 test-renderer@1.2.0`. `tsconfig.stryker.json` extends `tsconfig.base.json` from typescript-and-lint-rules' tsconfig set: adopt that set first, or point its `extends` at `./tsconfig.json` (`stryker-tsconfig` names it). Tests another runner owns (Playwright for the web build, Cypress, Deno under `supabase/functions/`) that the checker cannot detect go into `contracts/test-setup.json` `otherRunnerGlobs`. Read [references/jest-config.md](references/jest-config.md) before changing any config; the configs and the device-only helper are gated files (quality-gates).
2. **Wire the render helpers once.** Copy `templates/test/`: `render-with-providers.tsx` with its test, `test-app.tsx`, `flush-microtasks.ts`, `play-actions.ts` with its test, `render-router-with-providers.tsx` with its test when the app uses Expo Router (an app on React Navigation has no `expo-router/testing-library`; it follows the appendix of [references/component-tests.md](references/component-tests.md) instead), and `skia-mock.test.tsx` when the app depends on Skia. `test-app.tsx` already wraps `ServicesProvider` over `createTestServices(options.services)`, the composition root of architecture-and-boundaries, and takes the `language` and `direction` options (text locale, layout direction); each file it imports that the app lacks comes from `templates/src/` (synced copies; never over an existing file such as the app's `languages.ts`). Then grow `test/test-app.tsx` with the app's providers, in the order the root layout uses them, as each lands: catalogs, store factories with a seed, theme. [examples/test-app.tsx](examples/test-app.tsx) shows a Tasks app fully wired.
3. **Pick the kind of test** and read its reference once per session:
   - component, screen or hook: [references/component-tests.md](references/component-tests.md); models `templates/test/render-with-providers.test.tsx` and [examples/use-tasks-model.test.tsx](examples/use-tasks-model.test.tsx);
   - navigation (a link, a deep link, a redirect, back): the same reference, section "Routes and navigation"; model `templates/test/render-router-with-providers.test.tsx`;
   - Android-only behaviour (an `.android.tsx` file, the hardware back button, a `Platform.select` branch): the same reference, section "Android behaviour"; model [examples/task-editor.android.test.tsx](examples/task-editor.android.test.tsx);
   - adapter over a native SDK, fake, service or policy: [references/mocks-and-fakes.md](references/mocks-and-fakes.md); models [examples/expo-iap-purchase-adapter.test.ts](examples/expo-iap-purchase-adapter.test.ts), [examples/fake-purchase.ts](examples/fake-purchase.ts), [examples/premium-service.test.ts](examples/premium-service.test.ts);
   - reducer, policy or other pure logic: [references/properties.md](references/properties.md); model [examples/task-list-reducer.test.ts](examples/task-list-reducer.test.ts).
4. **Place and name the test**: next to its unit as `<unit>.test.ts(x)` (`<unit>.android.test.tsx` for Android-only behaviour, `test/integration/<area>/` when it needs Node), never inside `src/app/`: a route test goes to `src/features/<feature>/<feature>-navigation.test.tsx`. Titles read as the spec and pass lint (`jest/valid-title` `^(can|[a-z]+s)\b`): `describe('<exported name>')`, optional `describe('when …')`, then `it('clears …')`, never `it('should …')`; no `it.skip`, `it.only`, `xit`.
5. **Run the one file and read the failure**: `npx jest <file> --ci --selectProjects unit` (`--selectProjects android` for an Android test; paths always before the flag). Red must be an assertion diff, not an import or type error.
6. **Make it green, then check the test code**: `node ${CLAUDE_SKILL_DIR}/scripts/check-test-code.mjs .`. Fix each `FAIL` line (file, rule and fix are printed) and rerun until `RESULT: PASS`. A unit that really reads nothing from context (or a test of the provider itself or of a no-provider default), a timer helper, or a style that is the contract gets the reasoned marker the fix names (`no-providers`, `allow-fake-timers`, `allow-style-assertion`), not a rewrite; `--help` lists them.
7. **Run the whole suite with coverage**: `npm run test:coverage`. Below a threshold, add tests for the uncovered behaviour ([references/coverage-and-mutation.md](references/coverage-and-mutation.md)); a native adapter, Skia or frame-callback module, composition file or tooling entry point gets the device-only marker instead, naming the check that covers it. For a subset, put the paths first and switch the thresholds off: `npx jest <paths> --ci --selectProjects unit --coverage --collectCoverageFrom='<path>/**/*.ts' --coverageThreshold='{}'`.
8. **Mutation-test finished logic** (a reducer, policy, repository or `src/core` module): `npx stryker run --mutate <file>`, then `node ${CLAUDE_SKILL_DIR}/scripts/check-mutation-report.mjs . --changed <file>`. If Stryker stops with "There were failed tests in the initial test run" and the Reanimated error "`useAnimatedStyle` was used without a dependency array or Babel plugin" while `npx jest` passes, `babel.config.js` is an old form that compiles every file without the Worklets plugin: copy `templates/babel.config.js`, whose override applies only to the files Stryker instrumented ([references/jest-config.md](references/jest-config.md), section babel.config.js). Kill each survivor with a boundary example and rerun both; an equivalent mutant is accepted only with `--allow <id>` and a sentence in the change report. Nightly and before a release: `npm run test:mutation`, then the same check without `--changed` (it also fails any file none of whose mutants was tested).
9. **Rerun the three checks** until each prints `RESULT: PASS`.

## Definition of done

- [ ] `npx jest --ci` passes with no `act(...)` warnings, and `npm ls react react-native` shows one version of each.
- [ ] Every new test sits next to its unit (an Android-only case in `<unit>.android.test.tsx`, a Node-API test under `test/`, a route test in `src/features/`, none inside `src/app/`) and was seen failing on an assertion first.
- [ ] Components and hooks render through the render helper, routes through the router helper; adapters are tested against the root mocks; services over fakes.
- [ ] `npm run test:coverage` passes (90/90/90/85 global, 95/95/95/90 logic keys).
- [ ] Finished logic modules: `node ${CLAUDE_SKILL_DIR}/scripts/check-mutation-report.mjs . --changed <file>` prints `RESULT: PASS` (score at least 75%, no survivor in the file, its mutants actually tested).
- [ ] `node ${CLAUDE_SKILL_DIR}/scripts/check-test-setup.mjs .` prints `RESULT: PASS` (it prints no SKIP line; `NOT APPLICABLE` only in a repo without `expo`).
- [ ] `node ${CLAUDE_SKILL_DIR}/scripts/check-test-code.mjs .` prints `RESULT: PASS`

## Anti-patterns

- **`render(<X />)` without the providers, or without `await`.** The component misses its catalog, stores and services, and the assertions run before React commits. Use `await renderWithProviders(<X />, options)`.
- **Mocking a store or one of the app's own modules to "make the test easy".** The test then proves the mock. Seed through the render helper's options and call the actions of the returned `stores`.
- **A bag of `jest.fn()` standing in for a port.** It cannot prove order or state. Write or reuse `fake-<port>.ts` with a call log.
- **`import { fetchProducts } from 'expo-iap'` in a test.** It crashes or reads a second instance. `jest.mock('expo-iap')` plus a typed `jest.requireMock`.
- **`await new Promise((r) => setTimeout(r, 50))`.** Slow and flaky. `findBy*` for the screen, `flushMicrotasks()` for services.
- **`Platform.OS = 'android'` in an iOS-project test.** It leaks into the next test, and `.android.tsx` files still resolve to iOS. Write `<unit>.android.test.tsx`.
- **`renderRouter(...)` with `expect(screen).toHavePathname(...)`**, as Expo's SDK 57 testing guide shows it. `renderRouter` returns RNTL's render promise with the route getters attached to the promise, so the awaited result has none; with RNTL 14 its matchers and `testRouter` throw (`getPathname is not a function`), and it leaves fake timers on for the rest of the file (verified with expo-router 57.0.24). `await renderRouterWithProviders(routes)`, then `expect(view.getPathname())`.
- **Tests in `__tests__/<name>-test.tsx` or `*.spec.ts`**, as Expo's guide and Jest's defaults allow. No project runs them, so they pass by never running. Name every test `<unit>.test.ts(x)` beside its unit.
- **`expect(view.toJSON()).toMatchSnapshot()` for a component.** It fails on every harmless change and proves nothing a user sees. Assert roles, names and state; readable data snapshots belong in a golden test.
- **`queryByText(...)` with `toBeOnTheScreen()`, or `getByText(...)` with `.not.toBeOnTheScreen()`.** The first hides a missing element's error message, the second throws before asserting.
- **`const TASKS = sortTasks([...])` above the `describe`.** The unit runs at import, Stryker marks every mutant of it static and skips it, and reports a score of NaN as a pass (verified). Call the unit inside `it()`; module-level fixtures are plain data.
- **Weakening a gate to get green**: lower coverage numbers, a break under 75, `istanbul ignore`, `Stryker disable`, `jest.retryTimes`. This skill's checkers fail the numbers and the retries; quality-gates' bypass check fails both comments. Fix the test or the code; if a gate looks wrong, stop and ask the developer.

## Files in this skill

| File | What it is | Read/run when |
|---|---|---|
| [references/jest-config.md](references/jest-config.md) | Versions and re-verify commands, file layout, Babel, `jest.config.js` part by part, `jest.setup.ts`, the simulation config, scripts, the contract, when multi-package, known failures, flaky tests | Workflow step 1, and whenever a config or pin changes or Jest fails oddly |
| [references/component-tests.md](references/component-tests.md) | RNTL 14 async model, the render helper and `test/test-app.tsx`, queries, matchers, user-event, hooks, stores, gestures, Skia, Android behaviour, styles, what not to test | Workflow steps 2 and 3 for components, screens and hooks |
| [references/mocks-and-fakes.md](references/mocks-and-fakes.md) | Root native-SDK mocks and why each exists, adapter tests with `requireMock`, fakes with call logs, services and policies over fakes | Workflow step 3 for adapters, services, policies |
| [references/properties.md](references/properties.md) | Examples + fast-check properties + pinned values, `playActions`, the property checklist, counterexamples, simulations, `node:sqlite` placement | Workflow step 3 for pure logic |
| [references/coverage-and-mutation.md](references/coverage-and-mutation.md) | Thresholds and key semantics, the device-only marker, subsets, Stryker config, reading survivors, a worked boundary example, equivalent mutants | Workflow steps 7 and 8 |
| `templates/jest.config.js` | The one Jest config: unit, android and golden projects (CanvasKit with Skia), aliases, UTC, transforms, coverage gates (synced from the library; do not edit here) | Copy to the repo root (step 1) |
| `templates/jest.setup.ts` | Gesture Handler, Worklets and Reanimated setup and the one guarded Skia mock, for unit, android and (without Skia) golden (synced; do not edit here) | Copy to the repo root (step 1) |
| `templates/jest.sim.config.js` | Plain-Node config for `*.sim.test.ts` simulations (synced; do not edit here) | Copy only when the app has simulations (game-balance-and-bots) |
| `templates/babel.config.js` | Root Babel config whose Stryker override compiles only the instrumented files without the Worklets and Reanimated plugins (synced; do not edit here) | Copy to the repo root (step 1); replaces an old all-files branch (step 8) |
| `templates/stryker.config.json` | Stryker scope, thresholds, ignore list, type-check scope, reporters (synced; do not edit here) | Copy to the repo root (step 1) |
| `templates/tsconfig.stryker.json` | The jest-only program Stryker's type checker uses (synced; do not edit here) | Copy to the repo root (step 1) |
| `templates/scripts/package.json` | Marks `scripts/` as ES modules so Node loads the helper (synced; do not edit here) | Copy with the helper (step 1) |
| `templates/scripts/quality/device-only.ts` | The one helper that turns `// device-only: covered by ...` markers into coverage exclusions (synced; do not edit here) | Copy with `jest.config.js` (step 1) |
| `templates/scripts/quality/device-only.test.ts` | The helper's test (synced; do not edit here) | Copy with the helper |
| `templates/test/render-with-providers.tsx` | `renderWithProviders`, `renderHookWithProviders`, `createProvidersWrapper` (synced; do not edit here) | Copy once (step 2) |
| `templates/test/render-with-providers.test.tsx` | The helper's own test and the model of every component and hook pattern (synced; do not edit here) | Copy with the helper; read before the first component test |
| `templates/test/render-router-with-providers.tsx` | `renderRouterWithProviders`: Expo Router's in-memory router inside the same providers, with route getters (synced; do not edit here) | Copy once when the app uses Expo Router (step 2) |
| `templates/test/render-router-with-providers.test.tsx` | Its test and the model of a route test: link press, deep link, navigation from outside React (synced; do not edit here) | Copy with the helper; read before the first route test |
| `templates/test/test-app.tsx` | The app's provider tree for tests: `ServicesProvider` over `createTestServices`, the `language` and `direction` options (react-intl that throws on a missing message, the text locale, the layout direction), a fresh TanStack Query cache (synced; do not edit here) | Copy once, then grow it in the app (step 2) |
| `templates/src/` | What `test-app.tsx` imports, synced from their owners (do not edit here): the composition root's test side (`services-context.tsx`, `create-test-services.ts`, the clock, error-log and direction-guard ports, `fake-clock.ts`, `core/dates/date-key.ts`, `core/result.ts`), `i18n/{languages,digits,language-context,direction-context}` and `theme/text-locale.tsx` | Step 2, each file only when the app has none at that path (never over the app's `languages.ts`) |
| `templates/test/flush-microtasks.ts` | Lets queued promise callbacks run without fake timers (synced; do not edit here) | Copy with the helper |
| `templates/test/play-actions.ts` | Turns fast-check integers into legal action sequences for any pure model (synced; do not edit here) | Copy before the first property test of a reducer or policy |
| `templates/test/play-actions.test.ts` | Its test, a compact property-test model (synced; do not edit here) | Copy with `play-actions.ts` |
| `templates/test/skia-mock.test.tsx` | Proves the central Skia mock (synced; do not edit here) | Copy when the app depends on Skia |
| `templates/__mocks__/` | Root manual mocks: `expo-iap`, `expo-tracking-transparency`, `react-native-audio-api`, `react-native-google-mobile-ads`, `react-native-keyboard-controller` (synced; do not edit here) | Copy the mock of each SDK the app depends on |
| `templates/contracts/test-setup.json` | The optional contract with the app's facts (render and router helpers, root mocks, logic folders), filled with the defaults | Copy when the app's logic or helper lives elsewhere |
| `templates/contracts/test-setup.schema.json` | JSON Schema of the contract | With the contract |
| [examples/test-app.tsx](examples/test-app.tsx) | `test/test-app.tsx` fully wired for a Tasks app: services, seeded stores, catalogs, theme, query cache | Workflow step 2 |
| [examples/use-tasks-model.test.tsx](examples/use-tasks-model.test.tsx) | A hook test: seeded through the helper, changed by a store action | To imitate for hooks |
| [examples/task-editor.android.test.tsx](examples/task-editor.android.test.tsx) | An Android-only test: the hardware back button under `jest-expo/android`, on a screen and on the router stack | To imitate for Android behaviour |
| [examples/expo-iap-purchase-adapter.test.ts](examples/expo-iap-purchase-adapter.test.ts) | An adapter test against a root mock with a typed `requireMock` | To imitate for adapters |
| [examples/fake-purchase.ts](examples/fake-purchase.ts) | A fake port: a mutable script, a shared call log, emitted events | To imitate for fakes |
| [examples/premium-service.test.ts](examples/premium-service.test.ts) | A service test over fakes: "save, then finish the transaction", and its failure paths (a refused save, a failed restore sync, the launch re-check, an unfinished purchase) | To imitate for services |
| [examples/task-list-reducer.test.ts](examples/task-list-reducer.test.ts) | Pure logic: examples, invalid input, determinism, invariants over any action sequence, round trip, pinned value | To imitate for reducers and policies |
| `assets/package.test-deps.json` | Test scripts, exact devDependency pins, and the React overrides for a multi-package repo | Workflow step 1 |
| `assets/shared.json` | Declares the shared files this skill copies in | When adding a shared file |
| `scripts/check-test-setup.mjs` | Checks pins and companions, one React and no `react-test-renderer`, scripts, Babel, one Jest config, the Jest projects and that every file Jest or Expo would take for a test runs in exactly one (other runners' folders left out), no test in the routes folder, aliases, transforms, coverage scope and the device-only exclusions, setup, simulations, Stryker and its tsconfig, root mocks, the render helper | Workflow step 1 and the definition of done |
| `scripts/check-test-code.mjs` | Checks every test file of the repo (`__tests__/` and `*.spec` ones too): awaits, the render helpers, queries, styles, snapshots, `react-test-renderer`, SDK imports, `requireMock`, store mocks, timers, sleeps, retries, `Platform.OS`, Expo Router's test API, imports across roots | Workflow step 6 and the definition of done |
| `scripts/check-mutation-report.mjs` | Reads `reports/stryker/mutation.json`: score against the break threshold, survivors in `--changed` files, any file (or the whole report) with no tested mutant (a types-only file, or one whose every mutant failed to compile, needs none) | Workflow step 8 |
| `scripts/lib/test-contract.mjs` | Shared by the two repo checkers: the contract, its defaults, manifests, the known root-mock SDKs, the helper names, the files Jest or Expo would take for tests, and the folders other runners own | Read when changing a checker |
| `scripts/lib/config-lines.mjs` | Finds the line of a key in `package.json`, `stryker.config.json` and `jest.config.js` (per Jest project) for check-test-setup's problem lines | Read when changing check-test-setup |
| `scripts/lib/source-scan.mjs` | Shared lexer; check-test-code uses its `maskCode` to walk call chains without being misled by strings (synced; do not edit here) | Never by hand |
| `scripts/selftest.mjs` | Proves the three checkers pass their good fixtures and catch every planted bug | After changing a checker |
| `scripts/check-lib.mjs` | Shared script helper (synced; do not edit here) | Never by hand |
| `tests/bases/` | Base trees the fixtures start from: a single Expo app, and a layer that turns it into a monorepo | When adding a fixture |
| `tests/fixtures/` | Good, pass, bad and error cases for each checker (overlay format) | When adding a rule to a checker |

## Related skills

- `typescript-and-lint-rules` - the tsconfig set (`tsconfig.base.json`) that `tsconfig.stryker.json` extends, and the lint rules tests obey.
- `tdd-workflow` - the red-green loop, test names and the order of layers.
- `golden-and-snapshot-tests` - data and pixel goldens, the golden project's matcher and when `jest -u` is allowed.
- `e2e-maestro` - what flows prove on simulators and emulators instead of Jest; the device-only reason names a flow.
- `quality-gates` - `check:fast`, `verify` and the guardrail around these configs.
- `architecture-and-boundaries` - ports, adapters, `ServicesProvider` and `createTestServices`.
- `state-management-zustand` - the store factories the test app seeds.
- `persistence-and-migrations` - the `node:sqlite` driver and migration tests under `test/`.
- `i18n-strings-and-catalogs` - the catalogs the test app loads and the bidi isolates in accessible names.
- `seeded-determinism-and-golden-vectors` - pinned sequences and independent oracles.
- `skia-canvas-rendering` - draw-call tests and the frame clock.

Files in this skill

  • SKILL.md28.3 KB
  • assets/package.test-deps.json1.5 KB
  • assets/shared.json4.1 KB
  • examples/expo-iap-purchase-adapter.test.ts2.2 KB
  • examples/fake-purchase.ts2.5 KB
  • examples/premium-service.test.ts5.8 KB
  • examples/task-editor.android.test.tsx1.8 KB
  • examples/task-list-reducer.test.ts3.2 KB
  • examples/test-app.tsx6.1 KB
  • examples/use-tasks-model.test.tsx1.6 KB
  • references/component-tests.md24.7 KB
  • references/coverage-and-mutation.md14.6 KB
  • references/jest-config.md34.8 KB
  • references/mocks-and-fakes.md9.1 KB
  • references/properties.md6.6 KB
  • scripts/check-mutation-report.mjs11.2 KB
  • scripts/check-test-code.mjs35.6 KB
  • scripts/check-test-setup.mjs64.8 KB
  • scripts/lib/config-lines.mjs6.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.