test: NonScalingOverlay + ReactNativeZoomableView test suite (274 tests, 29 suites)#180
test: NonScalingOverlay + ReactNativeZoomableView test suite (274 tests, 29 suites)#180thomasttvo wants to merge 29 commits into
Conversation
- devDeps: jest, babel-jest, @testing-library/react-native, react-test-renderer, @types/jest - jest.setup.ts wires reanimated/mock + RNGH jestSetup - package.json jest config: setupFiles + explicit transformIgnorePatterns - `yarn jest --passWithNoTests` passes locally Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ests - Pure helper covering EC-NSO-3/4/5/6 transform-math contracts - Worklet directive preserved; useAnimatedStyle now delegates - Unit tests cover 5-element shape invariant, centering math, zoom scaling, pan in rotated frame, negative/zero/large inputs Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Zero-dim guard (contentWidth/Height = 0 -> null) - Rotation prop toggle does not change hook count - Static styles: position:absolute, top:0, left:0, overflow:visible - pointerEvents='none' prop - Children pass-through - Width/height/transform plumbing under reanimated mock Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-NSO-10–14 - Overlay branch gating by contentWidth/Height - Mount order: overlay paints under StaticPin (sibling order) - Overlay shares coordinate frame with GestureDetector (wrapper sibling) - onLayout 0×0 doesn't overwrite wrapperSize state - Identical onLayout dims dedup (no spurious re-renders) - Adds testID="zoom-subject-wrapper" on wrapper View for RTL reachability Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…stGestureState
Pure-math helper for SPEC-027/028/109 unit tests. movementSensitivity=0
returns {dxShift:0, dyShift:0} (SPEC-028 silently disables panning).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…math Pure-math helper for SPEC-095 unit tests. Formula deltaGrowth * (1 - sensitivity*9/100) is identical at the call site. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pure-math helper for SPEC-020/023/097 unit tests. null/undefined bounds mean unbounded on that side, matching the existing != null gate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pure-logic helper for SPEC-029/108 unit tests. Returns true when panEnabled is false OR (disablePanOnInitialZoom && zoom === initialZoom). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Enables Phase C gesture-driven integration tests to acquire the
Gesture.Manual() instance via getByGestureTestId('canvas-gesture').
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Covers SPEC-001/004/008/009/010 (exports + peer deps), SPEC-020/022/023/097/099-104 (getNextZoomStep cycle + clamp), SPEC-027/028/109 (calcShiftDelta), SPEC-029/108 (shouldSkipShift), SPEC-095 (applyPinchSensitivity), SPEC-096 (calcNewScaledOffsetForZoomCentering), SPEC-010/133 (coordinateConversion), plus regression test for the dy/dx swap previously seen in calcGestureTouchDistance (PR #151 thread #3076242724). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…4, 053-055, 065-080, 107, 137-149) - Default prop values + initial zoom/offset - 4 cancellation paths verified onZoomEnd does NOT fire - zoomTo timing, zoomCenter math, return-value contract - Programmatic methods bypass panEnabled - onLayoutWorklet unwrapped payload, onTransformWorklet centering invariant - movementSensibility legacy alias Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…7, 112-119, 047) - Wrapper tree: GestureDetector + StaticPin siblings (not nested) - StaticPin pinProps style merge (caller transform replaces anchor) - pointerEvents defaults (wrapper box-none, icon none); pinProps override survives destructure-before-spread (threads #3107340687, #3179480336) - Opacity 0->1 on icon onLayout, left/top from staticPinPosition Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…85, 117-122) - Settle 100ms debounce + ε dedup + cancellation (threads #3164939942, #3179477073) - Pin opacity gate, internal bottom-centre transform - onStaticPinPositionMoveWorklet content-dim gating + payload math - visualTouchFeedbackEnabled / debug conditional branches - useLatestWorklet ref identity update (threads #3179033549, #3238350220) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…31-033, 037-041, 060-063, 092, 105-106, 123-130, 135)
Direct-gesture invocation via getByGestureTestId('canvas-gesture').
Acquires Gesture.Manual() and invokes onTouchesDown/Move/Up directly.
Covers PR #151 threads #3179084848 (doubleTapZoomToCenter math),
#3179033552 (singleTapTimeoutId cleanup), and #3179193006 cluster
(sentinel survival across recovery).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…SPEC-056-059, 081, 082) - onPanResponderGrant/End/Terminate ordering across natural release, 3+ force-end, RNGH cancel - gestureStarted true during gesture, reset to false AFTER end-callbacks (SPEC-082 clear-ordering) - onPanResponderMoveWorklet intercept return-truthy short-circuits library (externallyHandled), preserves sentinel against spurious onSingleTap after intercepted drag Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-091, 093, 094, 098, 108, 110, 130, 150) Direct-gesture invocation. Pinch/shift gesture classification, 3+ finger force-end + recovery, tap-classification only on genuine release. Covers PR #151 threads #3179193006 (no spurious tap on cancel) and #3179193011 (firstTouch stability across finger lifts). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR SummaryMedium Risk Overview Introduces a large test suite covering Makes small production changes to support/enable this coverage: extracts pinch/shift math into new helper functions ( Reviewed by Cursor Bugbot for commit dd2672e. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit dd2672e. Configure here.
| jest.spyOn(global.console, 'warn').mockImplementation((msg: unknown) => { | ||
| if (typeof msg === 'string' && msg.includes('Reanimated 2')) return; | ||
| // fall through other warnings | ||
| }); |
There was a problem hiding this comment.
jest.setup suppresses all console.warn, not just Reanimated
Medium Severity
The mockImplementation replaces console.warn entirely. When the message does NOT contain 'Reanimated 2', the function still returns undefined without calling the original implementation. The comment says "fall through other warnings" but there is no actual fall-through — all warnings are silently swallowed. This masks legitimate warnings emitted by the code under test, potentially hiding real issues like deprecation warnings or incorrect usage patterns.
Reviewed by Cursor Bugbot for commit dd2672e. Configure here.
| - The overlay is mounted as a **sibling** of `GestureDetector`'s zoom-transformed layer, not under it — both share the wrapper's coordinate frame. | ||
| - The overlay appears **before** `StaticPin` in source order, so RN paints the overlay underneath the pin (last sibling renders on top). | ||
| - When `contentWidth` or `contentHeight` is missing or zero, the overlay returns `null` — no markers render. | ||
| - `renderOverlay` is wired through automatically; the consumer never instantiates `NonScalingOverlay` directly when going through the prop. |
There was a problem hiding this comment.
SPECS.md documents internal implementation details for NonScalingOverlay
Low Severity
The NonScalingOverlay contract section includes implementation details that violate the SPECS.md authoring rules. Specifically: the "Transform formula" subsection documents the exact 5-element transform array structure with index positions (transform[3..4]), the "Mounting rules" subsection references internal tree topology relative to GestureDetector, and source ordering within ReactNativeZoomableView. These are implementation internals, not consumer-observable behavior.
Triggered by learned rule: SPECS.md must document consumer-observable behavior only
Reviewed by Cursor Bugbot for commit dd2672e. Configure here.
Probe demonstrates that RNZV's Gesture.Manual() callbacks can be driven
through RNGH's REAL builder + registry, without the per-file
jest.mock('react-native-gesture-handler', ...) used by the existing
gesture suites. Single-tap → onSingleTap fires after doubleTapDelay.
The only added mock is RN's renderer shim — minimum needed to bypass
the documented ReactNativeRenderer-dev load crash. Reanimated/mock and
RNGH/jestSetup are inherited from the global setup unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per phase E probe finding §6.1 — the narrow renderer-shim mock unblocks real-RNGH integration tests without affecting existing mock-based tests. Hoisting eliminates the per-file repetition for the scale-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the custom Proxy-based RNGH mock with the real react-native-gesture-handler module (per phase E probe). Real Gesture.Manual() builder + handlersRegistry + withTestId resolution now exercise; touch-event dispatch remains direct-handler invocation per phase E §6.5 (fireGestureHandler doesn't support Manual gestures in RNGH 2.20.2). Same SPEC IDs, same coverage — strictly more realistic plumbing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the custom Proxy-based RNGH mock with the real react-native-gesture-handler module (per phase E probe). Real Gesture.Manual() builder + handlersRegistry + withTestId resolution now exercise; touch-event dispatch remains direct-handler invocation per phase E §6.5 (fireGestureHandler doesn't support Manual gestures in RNGH 2.20.2). Same SPEC IDs, same coverage — strictly more realistic plumbing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the custom Proxy-based RNGH mock with the real react-native-gesture-handler module (per phase E probe). Real Gesture.Manual() builder + handlersRegistry + withTestId resolution now exercise; touch-event dispatch remains direct-handler invocation per phase E §6.5 (fireGestureHandler doesn't support Manual gestures in RNGH 2.20.2). Same SPEC IDs, same coverage — strictly more realistic plumbing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the custom Proxy-based RNGH mock with the real react-native-gesture-handler module (per phase E probe). Real Gesture.Manual() builder + handlersRegistry + withTestId resolution now exercise; touch-event dispatch remains direct-handler invocation per phase E §6.5 (fireGestureHandler doesn't support Manual gestures in RNGH 2.20.2). Same SPEC IDs, same coverage — strictly more realistic plumbing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the custom Proxy-based RNGH mock with the real react-native-gesture-handler module (per phase E probe). Real Gesture.Manual() builder + handlersRegistry + withTestId resolution now exercise; touch-event dispatch remains direct-handler invocation per phase E §6.5 (fireGestureHandler doesn't support Manual gestures in RNGH 2.20.2). Same SPEC IDs, same coverage — strictly more realistic plumbing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the custom Proxy-based RNGH mock with the real react-native-gesture-handler module (per phase E probe). Real Gesture.Manual() builder + handlersRegistry + withTestId resolution now exercise; touch-event dispatch remains direct-handler invocation per phase E §6.5 (fireGestureHandler doesn't support Manual gestures in RNGH 2.20.2). Same SPEC IDs, same coverage — strictly more realistic plumbing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the custom Proxy-based RNGH mock with the real react-native-gesture-handler module (per phase E probe). Real Gesture.Manual() builder + handlersRegistry + withTestId resolution now exercise; touch-event dispatch remains direct-handler invocation per phase E §6.5 (fireGestureHandler doesn't support Manual gestures in RNGH 2.20.2). Same SPEC IDs, same coverage — strictly more realistic plumbing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…est.tsx Replaces the custom Proxy-based RNGH mock with the real react-native-gesture-handler module (per phase E probe). Real Gesture.Manual() builder + handlersRegistry + withTestId resolution now exercise; touch-event dispatch remains direct-handler invocation per phase E §6.5 (fireGestureHandler doesn't support Manual gestures in RNGH 2.20.2). Same SPEC IDs, same coverage — strictly more realistic plumbing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After scaling the Phase E probe across the gesture-test suite, document the test-layer fidelity profile (real RNGH builder + registry, reanimated/mock, direct-handler dispatch, renderer-shim stub) so future contributors know what's exercised and what's stubbed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous refactor extracted calcShiftDelta, applyPinchSensitivity, clampZoom, and shouldSkipShift from RNZV.tsx into src/helper/ for the sole purpose of unit-testing them. Per user direction: do not create new helpers just for testing. The semi-e2e scenario tests cover the same math via gesture-driven assertions on rendered output + callback payloads + SharedValue end-states; the new helpers are unnecessary. Reverts: - 2a13abc calcShiftDelta extraction - 3ea1c10 applyPinchSensitivity extraction - bebae3c clampZoom extraction - 0129282 shouldSkipShift extraction Also drops the 4 unit-test files that exercised those helpers (getNextZoomStep, calcGestureCenterPoint, calcGestureTouchDistance, calcNewScaledOffsetForZoomCentering, coordinateConversion, exports tests remain — they test pre-existing public helpers). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… sheet transform Multi-frame touch sequences via real RNGH + reanimated/mock. Each scenario drives a realistic gesture and asserts on: - end-state SharedValues (zoom, offsetX, offsetY) - callback payloads (ZoomableViewEvent shape and values) - rendered Animated.View transform on the zoom layer - NonScalingOverlay markers translated per the overlay formula Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>


Summary
Adds a Jest + React Testing Library test suite covering NonScalingOverlay,
renderOverlayintegration, and the full SPECS.md contract forReactNativeZoomableView. 274 tests across 29 suites, all green locally.Targets
thomas/nonscaling-overlay(PR #178) — when #178 merges to master, this PR's base auto-updates and the tests come along.Test coverage
SPECS.md(with documented device-only and type-only gaps).Suite breakdown
computeOverlayTransform.test.tsNonScalingOverlay.test.tsxrenderOverlay.test.tsxhelper/__tests__/*(9 files)RNZV.{props,imperativeHandle,callbacks}.test.tsxRNZV.{staticPin,feedback}.test.tsx+useLatestWorklet.test.tstreeShape.test.tsx+StaticPin.styling.test.tsxgestures/*(5 files)getByGestureTestId('canvas-gesture')(tap, double-tap, long-press, pinch, shift, multi-finger, pan-responder callbacks, intercept)Test infrastructure (first commit)
@testing-library/react-native@^12.5.0,react-test-renderer@18.3.1,babel-jest,@types/jest.jest.setup.tswiresreact-native-reanimated/mock+react-native-gesture-handler/jestSetup.Gesture.Manual()mock with awithTestIdregistry (Path 2 from research) so gesture-driven tests can invokeonTouchesDown/Move/Up/Cancelleddirectly under reanimated/mock..github/workflows/lint.yml:yarn test --ci --runInBand.Pure-helper extractions (non-behavior-changing)
4 inline expressions in
ReactNativeZoomableView.tsxextracted tosrc/helper/for unit-testability — math is byte-equivalent to the inline original:calcShiftDelta(from_calcOffsetShiftSinceLastGestureState)applyPinchSensitivity(from_handlePinchingresistance math)clampZoom(pinch-frame clamp;publicZoomTo's reject-on-out-of-range was left untouched — different semantics)shouldSkipShift(pan-gate predicate)SPECS.md changes
## NonScalingOverlay contractcodifying the prop shape, 5-element transform formula, static-style rules, mount-order rules, andwrapperSizemirror semantics.Known gaps (documented in
phase5-specs-research.md§5)disablePanOnInitialZoom, native momentum absence, dual-version Reanimated matrix (Elliott's #3691782346).tsc --noEmitin CI (noexpect-type/tsddep added).withTiming/cancelAnimation/runOnUIare no-ops under the mock.Test plan
yarn testlocally and confirms 29 suites / 274 tests pass.Run unit testsstep passes on push.it('SPEC-NNN: …', …)cases to confirm SPEC IDs map to the items inSPECS.md.🤖 Generated with Claude Code