Skip to content

test: add TS API contract test suite scaffold#1203

Closed
msluszniak wants to merge 5 commits into
mainfrom
@ms/api-tests-scaffold
Closed

test: add TS API contract test suite scaffold#1203
msluszniak wants to merge 5 commits into
mainfrom
@ms/api-tests-scaffold

Conversation

@msluszniak
Copy link
Copy Markdown
Member

Description

Adds a Jest-based scaffold and twelve API contract test suites under packages/react-native-executorch/tests/api/, with typecheck:tests + test scripts wired into a new CI job. Catches drift across the public TypeScript surface (modules, hooks, types, registry, error codes, voices, URLs) without exercising native code.

Introduces a breaking change?

  • Yes
  • No

Type of change

  • Bug fix (change which fixes an issue)
  • New feature (change which adds functionality)
  • Documentation update (improves or adds clarity to existing documentation)
  • Other (chores, tests, code style improvements etc.)

Tested on

  • iOS
  • Android

Testing instructions

yarn workspace react-native-executorch typecheck:tests
yarn workspace react-native-executorch test

Expected: 12 test suites, 971 passed, 9 skipped (documented in #1202), 1 snapshot. The new test job in .github/workflows/ci.yml runs the same on PRs.

Screenshots

N/A — no UI changes.

Related issues

Closes #1018. Findings catalogued in #1202.

Checklist

  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have updated the documentation accordingly
  • My changes generate no new warnings

Additional notes

Tests do not touch native code: `global.loadXxx` and `ResourceFetcher` are stubbed at the JS layer. Module classes are discovered via `Object.entries(RNE)` and the hook return contract is enforced via `satisfies` (tsc-checked).

Inconsistencies the tests surface are encoded as explicit exception sets (`SKIPS_BASE_MODULE`, `SKIPS_STATIC_FACTORY`, `SKIPS_DELETE`, `HOOK_NAME_EXCEPTIONS`, `PARAMETERIZED_ACCESSORS`) so the suite stays green on today's codebase. Each exception entry maps to a bullet in #1202 and can be removed as the underlying API is normalized.

Adds Jest-based scaffold and two representative tests that catch drift
across the package's public API surface. The tests intentionally do not
exercise the JSI runtime — they discover modules/hooks via the index
exports and assert shared structural contracts.

Refs #1018
Two more layers on the API contract scaffold:

- hookContracts.test.ts: compile-time assertion that every public
  useXxx hook returns at least { error, isReady, isGenerating,
  downloadProgress }. Drift in any hook surfaces as a tsc error
  naming the offending hook.

- modelUrls.test.ts: walks every accessor in the model registry,
  collects every string field that looks like a URL, and asserts
  each one is a non-empty https URL pointing at the
  software-mansion HuggingFace org.

Refs #1018, #1202.
Three more API consistency layers:

- errorCodes.test.ts: walks RnExecutorchErrorCode and asserts every
  entry is a unique non-negative integer with a working reverse
  lookup, and that constructing RnExecutorchError(code) produces a
  non-empty message.

- ttsVoices.test.ts: walks every Kokoro voice constant and asserts
  the voice variable-name region (e.g. KOKORO_FRENCH_*) matches the
  phonemizerConfig.lang, that the voiceSource URL points at the
  voices/ directory, and that every phonemizer URL lives under the
  matching /phonemizer/<lang>/ tree. Catches copy-paste bugs across
  voice configs.

- apiSurface.test.ts: snapshots the sorted list of public exports
  from src/index.ts. Accidental adds/removals show up in the diff;
  intentional changes need --updateSnapshot.

Refs #1018, #1202.
…ests

Three more API consistency layers:

- hookPropsContract.test.ts: compile-time check that every *Props
  type exposes preventLoad?: boolean, and that every public useXxx
  hook takes a single object argument. Surfaces useTextToSpeech as
  the lone two-arg outlier.

- registryHookCompatibility.test.ts: compile-time assertion that
  every category sample from the model registry is assignable to
  the matching hook's model prop type. Catches drift between the
  registry's static return shape and the hook prop shapes.

- modulePrototype.test.ts: walks each concrete module's prototype
  chain (using property descriptors so accessor getters aren't
  invoked) and asserts at least one public method is reachable and
  delete() is callable. Also snapshots BaseModule's intrinsic
  surface so silent additions/renames there fail loudly.

Surfaces TokenizerModule's missing delete() as a documented opt-out
in SKIPS_DELETE (tracked in #1202).

Refs #1018, #1202.
- moduleConstruction.test.ts: mocks the ResourceFetcher adapter and
  constructs every from*-factory-bearing module against a sample
  config from the registry. Asserts the awaited result is the
  expected instance and that delete() is callable on the stubbed
  native module.

- moduleHookSignatureAlignment.test.ts: compile-time alignment check
  between non-generic module prototype methods and the matching
  hook return field. Catches drift between e.g.
  LLMModule.prototype.generate and useLLM().generate. Surfaces the
  TextToImageModule.forward → useTextToImage().generate rename via
  a dedicated row.

- setup-globals.ts: the stubbed loadXxx now resolves to a native
  module shape with unload() and generateFromFrame() so module
  delete() and VisionModule's worklet getter work in tests.

- .github/workflows/ci.yml: adds a `test` job that runs
  typecheck:tests and `jest --ci` so the contract suite gates PRs.

Refs #1018, #1202.
@msluszniak msluszniak self-assigned this May 29, 2026
@msluszniak msluszniak added the test Issue and PR related to tests or testing infrastructure label May 29, 2026
@msluszniak
Copy link
Copy Markdown
Member Author

This PR will be updated after issues from #1202

@msluszniak
Copy link
Copy Markdown
Member Author

Superseded by #1204, which bundles this test scaffold together with the source-level fixes for every finding in #1202 — so the contract suite enforces every rule unconditionally (0 skipped) the moment it lands.

@msluszniak msluszniak closed this May 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test Issue and PR related to tests or testing infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add API test in TypeScript

1 participant