Skip to content

Commit fb8902a

Browse files
authored
refactor(test): add scenario.md and restructure stale update rejection test (#4019)
## Description This PR finalises the manual test coverage for the `rejectStaleNavStateUpdates` prop on `TabsHost` and the companion `onTabSelectionRejected` callback. The existing single-file test screen (`test-tabs-stale-update-rejection.tsx`) is promoted to a directory-based module so that a `scenario.md` can live alongside the implementation, following the established pattern for tab test scenarios in this repo. The scenario covers three distinct cases: baseline state verification, stale rejection triggered by a heavy render race, and runtime toggling of `rejectStaleNavStateUpdates` to confirm the prop controls rejection behaviour dynamically. Both iOS (18.x and 26.x) and Android (API 36) are listed as supported platforms. An automated E2E Detox test is noted as planned but intentionally deferred. The toast message on `onTabSelectionRejected` is also simplified from a JSON-stringified payload snippet to the plain string `onTabSelectionRejected`, making future test assertions straightforward. Closes: software-mansion/react-native-screens-labs#1456 ## Changes - **Tests/Tabs**: Converted `test-tabs-stale-update-rejection.tsx` from a flat file to a directory module (`test-tabs-stale-update-rejection/index.tsx`) and fixed all relative import paths accordingly - **Tests/Tabs**: Simplified the `onTabSelectionRejected` toast message from a JSON-stringified event excerpt to the literal string `onTabSelectionRejected` for easier test assertions - **Tests/Tabs**: Added `scenario.md` with a full 7-step manual test plan covering baseline, stale rejection under heavy render, and runtime toggling of `rejectStaleNavStateUpdates`
1 parent 3002fd6 commit fb8902a

2 files changed

Lines changed: 92 additions & 5 deletions

File tree

apps/src/tests/single-feature-tests/tabs/test-tabs-stale-update-rejection.tsx renamed to apps/src/tests/single-feature-tests/tabs/test-tabs-stale-update-rejection/index.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import {
88
useTabsNavigationContext,
99
TabsContainerWithHostConfigContext,
1010
useTabsHostConfig,
11-
} from '../../../shared/gamma/containers/tabs';
12-
import { CenteredLayoutView } from '../../../shared/CenteredLayoutView';
13-
import { ToastProvider, useToast } from '../../../shared/';
11+
} from '@apps/shared/gamma/containers/tabs';
12+
import { CenteredLayoutView } from '@apps/shared/CenteredLayoutView';
13+
import { ToastProvider, useToast } from '@apps/shared';
1414
import { Colors } from '@apps/shared/styling';
1515

1616
const scenarioDescription: ScenarioDescription = {
@@ -116,7 +116,7 @@ function AppContents() {
116116
2,
117117
)}`;
118118
console.warn(message);
119-
toast.push({ message: message, backgroundColor: Colors.GreenLight60 });
119+
toast.push({ message: `onTabSelectionRejected: ${event.nativeEvent.rejectedScreenKey}`, backgroundColor: Colors.GreenLight60 });
120120
}}
121121
/>
122122
);
@@ -143,7 +143,7 @@ function HeavyRenderHierarchy({
143143

144144
function blockThread(ms: number) {
145145
const end = Date.now() + ms;
146-
while (Date.now() < end) {}
146+
while (Date.now() < end) { }
147147
}
148148

149149
export default createScenario(App, scenarioDescription);
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Test Scenario: Stale update rejection
2+
3+
## Details
4+
5+
**Description:** Verifies the `rejectStaleNavStateUpdates` prop on `TabsHost`
6+
and the `onTabSelectionRejected` callback that fires when the native container
7+
rejects a navigation state update. When `rejectStaleNavStateUpdates` is `true`,
8+
the native side rejects any state update whose provenance baseline has been
9+
superseded - meaning a newer state of a different origin
10+
was committed before the JS update reached the UI thread. Each rejection fires
11+
`onTabSelectionRejected` with a `TabSelectionRejectedEvent` payload.
12+
13+
**OS test creation version:** iOS: 18.6 and 26.4, Android: API Level 36.
14+
15+
## E2E test
16+
17+
Other: Planned, but will be implemented separately after research.
18+
19+
## Prerequisites
20+
21+
- iOS device or simulator
22+
- Android emulator
23+
24+
## Note
25+
26+
- A toast labeled `onTabSelectionRejected` appears at the bottom of the
27+
screen whenever the callback fires.
28+
- `heavyRender` is per-tab state. Toggling it on a given tab blocks the
29+
JS thread for 3 000 ms on every render of that tab, simulating a slow
30+
update that can arrive after the user has already acted.
31+
32+
## Steps
33+
34+
### Baseline
35+
36+
1. Launch the app and navigate to **Stale update rejection**.
37+
38+
- [ ] Expected: The **First** tab is selected. The content area shows
39+
`heavyRender: false` and `rejectStaleNavStateUpdates: true`.
40+
No toast is visible.
41+
42+
---
43+
44+
### Stale rejection triggered by heavy render
45+
46+
2. Tap the **Third** tab in the native tab bar to navigate to it. Tap
47+
**Toggle heavyRender** on the Third tab to enable heavy render.
48+
49+
- [ ] Expected: The label updates to `heavyRender: true`. No toast
50+
appears.
51+
52+
3. Tap the **First** tab bar item to go back to the First tab. Tap
53+
**Select Third** (JS dispatches a navigation update to Third), then
54+
immediately tap the **Second** tab bar item.
55+
56+
- [ ] Expected: The tab bar changes to **Second** immediately. After
57+
the heavy render on Third finishes, a toast labeled
58+
`onTabSelectionRejected` appears. The final active tab is
59+
**Second**, not Third.
60+
61+
---
62+
63+
### Disabling rejectStaleNavStateUpdates at runtime
64+
65+
4. Navigate to the **Third** tab (heavy render still enabled). Tap
66+
**Toggle rejectStaleNavStateUpdates** to disable it.
67+
68+
- [ ] Expected: The label updates to `rejectStaleNavStateUpdates:
69+
false`. No toast fires from this action.
70+
71+
5. Navigate back to **First**. Tap **Select Third**, then immediately
72+
tap **Second** in the tab bar before the heavy render ends.
73+
74+
- [ ] Expected: No `onTabSelectionRejected` toast appears. Tab Second is selected
75+
immediately but after the 3 000 ms block, the final active tab is
76+
**Third**.
77+
78+
6. Tap **Toggle rejectStaleNavStateUpdates** to re-enable it.
79+
80+
- [ ] Expected: Label updates to `rejectStaleNavStateUpdates: true`.
81+
82+
7. Repeat the actions from step 5.
83+
84+
- [ ] Expected: The tab bar changes to **Second** immediately. After
85+
the heavy render on Third finishes, a toast labeled
86+
`onTabSelectionRejected` appears. The final active tab is
87+
**Second**, not Third.

0 commit comments

Comments
 (0)