Problem
SPECS.md is the authoritative consumer-visible behavior contract for ReactNativeZoomableView and StaticPin. Today it is enforced only by human/agent review against src/, which means:
- Drift between spec and code is caught only when someone notices
- Contract breaks ship when a reviewer misses them
- New contributors have no fast feedback loop ("did I break the contract?")
- Refactors require manual re-verification of every documented behavior
The spec already enumerates the contract in a structured way (props, ref methods, callback fire order/count, gesture classification, zoom/pan math, coordinate spaces). That structure maps naturally onto executable tests.
Proposal
Migrate the behavior described in SPECS.md into a test suite that runs in CI on every PR. Each section of SPECS.md becomes one or more tests; failing tests = contract break OR spec drift, surfaced automatically.
Suggested layering
- Unit tests — pure helpers (
applyContainResizeMode, getImageOriginOnTransformSubject, viewportPositionToImagePosition, sensitivity math, double-tap zoom-step ceiling derivation). No RN runtime needed; fast.
- Component tests (RN Testing Library + jest) — props, ref methods, callback fire order/count, the legacy
movementSensibility warning + forwarding, zoomEnabled: false cancel-and-snap behavior. Mock gesture-handler/reanimated where needed.
- Gesture / interaction tests — pinch classification, pan resistance, double-tap (delay window,
doubleTapZoomToCenter), tap handling. May need react-native-gesture-handler/jest-utils or a lightweight gesture simulator.
- (Optional) E2E on the example app — Maestro/Detox for the static-pin flows and worklet callback contracts that are hardest to fake in unit tests.
Migration approach
- Walk
SPECS.md section by section; for each documented behavior, write the test and link the test back to the spec section in a comment (e.g. // SPECS.md §7 Zoom behavior — double-tap).
- Keep
SPECS.md as the human-readable contract; tests are the executable mirror. When they diverge, that's the bug — fix one or the other.
- Consider a CI check that flags PRs touching
src/ without a corresponding test update, similar to the current CLAUDE.md review guidance.
Out of scope (for this issue)
- Rewriting any documented behavior — migration is a transcription, not a redesign.
- Internal implementation details that produce identical observable output (listener ordering, private SharedValue names) —
SPECS.md excludes these and the test suite should too.
Acceptance
- Test suite runs in CI on every PR
- Each
SPECS.md section has at least one corresponding test
- Failing tests block merge
- README/CONTRIBUTING updated to describe how spec ↔ tests relate
Problem
SPECS.mdis the authoritative consumer-visible behavior contract forReactNativeZoomableViewandStaticPin. Today it is enforced only by human/agent review againstsrc/, which means:The spec already enumerates the contract in a structured way (props, ref methods, callback fire order/count, gesture classification, zoom/pan math, coordinate spaces). That structure maps naturally onto executable tests.
Proposal
Migrate the behavior described in
SPECS.mdinto a test suite that runs in CI on every PR. Each section ofSPECS.mdbecomes one or more tests; failing tests = contract break OR spec drift, surfaced automatically.Suggested layering
applyContainResizeMode,getImageOriginOnTransformSubject,viewportPositionToImagePosition, sensitivity math, double-tap zoom-step ceiling derivation). No RN runtime needed; fast.movementSensibilitywarning + forwarding,zoomEnabled: falsecancel-and-snap behavior. Mock gesture-handler/reanimated where needed.doubleTapZoomToCenter), tap handling. May needreact-native-gesture-handler/jest-utilsor a lightweight gesture simulator.Migration approach
SPECS.mdsection by section; for each documented behavior, write the test and link the test back to the spec section in a comment (e.g.// SPECS.md §7 Zoom behavior — double-tap).SPECS.mdas the human-readable contract; tests are the executable mirror. When they diverge, that's the bug — fix one or the other.src/without a corresponding test update, similar to the currentCLAUDE.mdreview guidance.Out of scope (for this issue)
SPECS.mdexcludes these and the test suite should too.Acceptance
SPECS.mdsection has at least one corresponding test