Skip to content

feat: verify runtime contract states in verify:features - #66

Merged
BigDella merged 2 commits into
Parcel-Protocol:mainfrom
JamesHunt22:fix/issue-12-verify-features-runtime-contract
Sep 25, 2026
Merged

BigDella merged 2 commits into
Parcel-Protocol:mainfrom
JamesHunt22:fix/issue-12-verify-features-runtime-contract

Conversation

@JamesHunt22

Copy link
Copy Markdown
Contributor

Overview

Extends npm run verify:features so it checks a slice's runtime contract, not only its file layout. Today the checker answers "is there an __tests__/ directory with at least five files?" — a slice whose tests never touch the loading, error or empty state passes anyway. This adds a shared harness in core/testing/ and makes verify:features fail when a slice's tests do not drive those states, so "has a test file" and "the test file exercises the states" are no longer the same answer.

Related Issue

#12

Changes

Shared runtime contract harness

  • [ADD] core/testing/contract.tsx
    • renderFeatureSlice(id, ui, options) mounts a slice panel inside the real providers and returns contract assertions bound to the render result.
    • expectLoadingState(), expectErrorState(), expectEmptyState(), plus the generic expectState(state) and async waitForState(state).
    • Asserts on the data-contract-state attribute rather than on copy, so the same API works for a lookup form, a QR generator and a wallet-connect flow. The role/aria-busy fallbacks cover slices that hand-roll an indicator.
  • [MODIFY] core/testing/index.ts — re-exports the harness alongside renderFeature.

Shared state markers

  • [MODIFY] core/ui/EmptyState.tsx — renders data-contract-state="empty".
  • [MODIFY] core/ui/StatusMessage.tsx — renders data-contract-state={type}.
  • [MODIFY] core/ui/Skeleton.tsx — Skeleton and SkeletonRows render data-contract-state="loading".

Checker

  • [MODIFY] scripts/verify-features.mjs
    • New runtime-contract rule: reads every test module under a slice's __tests__/ and requires renderFeatureSlice plus a call for each of loading / error / empty (the named helper or the expectState("…") / waitForState("…") form). Failures name the missing state.
    • Exports CONTRACT_STATES, collectTestSources, evaluateContractCoverage, checkContractCoverage, and adds featuresDir / contractPending overrides to verifySlice so the rule is testable without touching the real features/ directory.
  • [ADD] scripts/verify-features.d.mts — types for the .mjs so the checker test compiles under strict TypeScript.
  • [ADD] scripts/feature-contract-migration.json — the 53 existing slices whose tests still assert the states by hand. They are reported as a warning, not a failure, while the migration is in flight; a slice that is not in the ledger must satisfy the contract, so CI fails clearly for any new slice that skips a state.

Checker test and fixtures

  • [ADD] core/__tests__/verifyFeaturesContract.test.ts — covers the good and bad fixture slices plus a partially-migrated case that must report exactly the skipped state.
  • [ADD] scripts/__tests__/fixtures/contract-good-slice/** and scripts/__tests__/fixtures/contract-bad-slice/** — deliberately minimal fixture slices, read as source by the checker (they are outside vitest's include globs and are never executed).

Documentation

  • [MODIFY] docs/FEATURE_CONTRACT.md — new section 11 documents the harness, the data-contract-state convention, the checker and the migration ledger, and the "done" checklist gains a harness line.

Verification Results

Not run — this change was authored entirely through the GitHub REST API with no local checkout of the repository, so no npm run command could be executed. A reviewer must run npm run check (or let the existing CI workflow run npm run verify:features, npm run test and npm run build).

What was actually executed, in isolation, against the exported checker logic:

node --check scripts/verify-features.mjs
# SYNTAX OK

node --input-type=module -e "<imports the exported checker functions and runs them>"
# good    {"slug":"contract-good-slice","ok":true,"usesHarness":true,"missing":[]}
# bad     {"slug":"contract-bad-slice","ok":false,"usesHarness":false,"missing":["loading","error","empty"]}
# partial {"slug":"partial","ok":false,"usesHarness":true,"missing":["loading"]}
# verifySlice on the fixture slices:
#   good            -> contractPending=false, no runtime-contract failure
#   bad             -> runtime-contract failure reported
#   bad + ledger    -> contractPending=true, no failure
# loadMigrationLedger -> 53 entries

The repo's own npm run test, npm run verify:features and npm run build were not run here. The checker logic above is the same code path the new vitest test exercises, but the vitest suite itself has not been executed.

Acceptance Criteria Status
verify:features distinguishes 'has a test file' from 'test file exercises loading/error/empty states' ✅ The new rule reads the slice's test sources and requires a harness call per state; the structural file-count rule is unchanged.
A shared harness utility exists in core and is documented in FEATURE_CONTRACT.md ✅ core/testing/contract.tsx, documented in docs/FEATURE_CONTRACT.md section 11.
At least the 7 existing tools are migrated to use the harness or flagged as needing migration ✅ All 53 existing slices are flagged in scripts/feature-contract-migration.json. No existing test was rewritten blind and no migration was claimed without running it.
CI fails clearly when a new slice's tests don't cover the required states ✅ Any slice absent from the ledger that skips a state fails verify:features with __tests__/ — runtime contract: …; CI already runs that step.
False positives are covered by a test of the checker itself using fixture 'good' and 'bad' slices ✅ core/__tests__/verifyFeaturesContract.test.ts runs the checker over scripts/__tests__/fixtures/contract-good-slice and contract-bad-slice.

Closes #12

Add a shared @/core/testing/contract harness (renderFeatureSlice plus
expectLoadingState/expectErrorState/expectEmptyState/waitForState) that
asserts the data-contract-state markers the shared state primitives render,
and teach verify:features to require each slice's tests to exercise the
loading, error and empty states. Existing slices are flagged in a migration
ledger; the checker itself is covered by good/bad fixture slices.

Refs Parcel-Protocol#12
@drips-wave

drips-wave Bot commented Sep 25, 2026

Copy link
Copy Markdown

@JamesHunt22 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@BigDella
BigDella merged commit 459e199 into Parcel-Protocol:main Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expand npm run verify:features to check runtime contract compliance, not just static shape

3 participants