Skip to content
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import React from 'react';
import { scenarioDescription } from './scenario-description';
import { createScenario } from '@apps/tests/shared/helpers';
import { StyleSheet, Text, View } from 'react-native';
import {
StackContainer,
useStackNavigationContext,
} from '@apps/shared/gamma/containers/stack';
import { CenteredLayoutView } from '@apps/shared/CenteredLayoutView';
import { Colors } from '@apps/shared/styling';
import { StackNavigationButtons } from '@apps/tests/shared/components/stack-v5/StackNavigationButtons';
import { StackRouteInformation } from '@apps/tests/shared/components/stack-v5/StackRouteInformation';

function TestStackSimpleNav() {
return <StackSetup />;
Expand All @@ -21,17 +20,32 @@ function StackSetup() {
{
name: 'Home',
Component: HomeScreen,
options: {},
// Rendering a header (via headerConfig) makes the native back
// button appear on non-root screens, including on Android. The
// root screen (Home) always hides the back button.
options: {
headerConfig: {
title: 'Home',
},
},
},
{
name: 'A',
Component: AScreen,
options: {},
options: {
headerConfig: {
title: 'A',
},
},
},
{
name: 'B',
Component: BScreen,
options: {},
options: {
headerConfig: {
title: 'B',
},
},
},
]}
/>
Expand All @@ -41,7 +55,7 @@ function StackSetup() {
function HomeScreen() {
return (
<CenteredLayoutView style={{ backgroundColor: Colors.BlueLight40 }}>
<RouteInformation routeName="Home" />
<StackRouteInformation routeName="Home" />
<StackNavigationButtons isPopEnabled={false} routeNames={['A', 'B']} />
</CenteredLayoutView>
);
Expand All @@ -50,7 +64,7 @@ function HomeScreen() {
function AScreen() {
return (
<CenteredLayoutView style={{ backgroundColor: Colors.YellowLight40 }}>
<RouteInformation routeName="A" />
<StackRouteInformation routeName="A" />
<StackNavigationButtons isPopEnabled={true} routeNames={['A', 'B']} />
</CenteredLayoutView>
);
Expand All @@ -59,29 +73,10 @@ function AScreen() {
function BScreen() {
return (
<CenteredLayoutView style={{ backgroundColor: Colors.GreenLight100 }}>
<RouteInformation routeName="B" />
<StackRouteInformation routeName="B" />
<StackNavigationButtons isPopEnabled={true} routeNames={['A', 'B']} />
</CenteredLayoutView>
);
}

function RouteInformation(props: { routeName: string }) {
const routeKey = useStackNavigationContext().routeKey;

return (
<View>
<Text style={styles.routeInformation}>Name: {props.routeName}</Text>
<Text style={styles.routeInformation}>Key: {routeKey}</Text>
</View>
);
}

const styles = StyleSheet.create({
routeInformation: {
color: 'black',
fontSize: 20,
fontWeight: 'bold',
},
});

export default createScenario(TestStackSimpleNav, scenarioDescription);
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ScenarioDescription } from '@apps/tests/shared/helpers';

export const scenarioDescription: ScenarioDescription = {
name: 'Simple navigation scenario',
name: 'Simple stack navigation',
key: 'test-stack-simple-nav',
details: 'Test simple push and pop operations',
platforms: ['android', 'ios'],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# Test Scenario: Simple navigation

## Details

**Description:** Verify basic push and pop navigation on the gamma/v5
`StackContainer`. The stack starts with **Home** and can push **A** or **B**
any number of times (including re-pushing a route already on the stack),
each push creating a new screen instance with its own unique `routeKey`;
non-root screens expose a **Pop** button. The test validates that push/pop
via the on-screen buttons, the native header back button, and the
edge-swipe-back / system gesture-back produce
consistent stack state, that `routeKey` values are unique per pushed
instance, and that the root screen (**Home**) cannot be popped. See the
Notes for `routeKey` behavior and how the two platforms are launched
(issue #1459).

**OS test creation version:** iOS 18.6 and 26.5, Android API Level 36.

## E2E test

TBD: Automation is planned and should be straightforward for the
button-driven push/pop steps (similar in scope to the tabs
`test-tabs-simple-nav` suite). Native back button, iOS edge-swipe gesture,
and Android system gesture-back steps may need platform-specific handling
and are not yet implemented.

## Prerequisites

- iOS device or simulator
- Android emulator or device

### iOS launch

- Run the app normally and navigate to the **Simple stack navigation**
screen (Stack v5) from the in-app scenario selection menu.

### Android launch

- To test on Android, run the screen **directly** by editing
[apps/App.tsx](../../../../../App.tsx): import and render
`TestStackSimpleNav` as the root component instead of `Example`, e.g.:

```tsx
import { TestStackSimpleNav as Example } from './src/tests/single-feature-tests';
```

With the gamma `StackContainer` at the root, the Android system
gesture-back pops the stack directly.

- The Android system gesture-back requires **Gesture navigation** to be
enabled on the emulator/device. If it is not already set, enable it
manually in **Settings → Navigation mode → select Gesture navigation**.

## Note

- Each screen shows two labels: `Name` (the route name: `Home`, `A`, or
`B`) and `Key` (the route's unique `routeKey`). Use the `Key` value to
tell apart multiple stacked instances of the same route name.
- `Key` values use a session-global counterthat increments on every push and is **never reset** for
the lifetime of the app session (it is shared across all Stack
containers). Only the **relationships** matter — every push produces a strictly
new `Key`, and a preserved (not recreated) screen keeps the same `Key`.
- **Android:** when the screen is launched **directly** via `App.tsx` (see
the Android launch prerequisite),both the **native header back button** and
the **system gesture-back** work — the same as on iOS. These only fail when the screen
is nested inside the example app's own navigation (issue
[#1459](https://github.com/software-mansion/react-native-screens-labs/issues/1459)),
which is exactly why Android is tested via the direct launch. On both
platforms the on-screen **Pop** button always works.

## Steps

### Baseline

1. Launch the app for the platform under test (see Prerequisites: iOS from
the selection menu, Android directly via `App.tsx`) so the **Home**
screen of the Simple stack navigation is shown.

- [ ] The **Home** screen is shown with `Name:
Home`, and a `Key`. No back button is visible in
the header. No **Pop** button is shown, only **Push A** and **Push B**.
Note the displayed `Key` value to compare against later steps.

### Push navigation

2. Tap **Push A**.

- [ ] Screen **A** is pushed. Background is light yellow, `Name: A`, and a
`Key` with a new value, distinct from Home's.
A native back button is visible in the header. **Push A**, **Push B**, and **Pop**
buttons are all shown. Note this `Key` value.

3. While on **A**, tap **Push B**.

- [ ] Screen **B** is pushed on top of **A**. Background is green, `Name:
B`, and a new `Key`is shown.
A native back button is visible in the header. Note this `Key` value.

### Re-pushing an already-present route

4. While on **B**, tap **Push A** again.

- [ ] A new instance of screen **A** is pushed on top of the stack (stack
is now Home, A, B, A). `Name: A` and a new
`Key` that is **different** from the `Key` shown in step 2.

### Pop via the on-screen button

5. Tap **Pop**.

- [ ] Returns to screen **B**. `Name: B` and the **same** `Key` value
observed in step 3 (the instance was preserved, not recreated).

6. Tap **Pop** again.

- [ ] Returns to the original screen **A**. `Name: A` and the **same** `Key`
value observed in step 2.

7. Tap **Pop** again.

- [ ] Returns to **Home**. No **Pop** button is shown and no header back
button appears (root screen reached).

### Native header back button

8. Tap **Push A**, then tap **Push B**. Then tap the native back button in the
header.

- [ ] Tapping the native back button behaves the same as tapping **Pop**:
returns to screen **A** with its `Key` unchanged. No crash and no
inconsistent state.

### Edge-swipe / system gesture-back

9. While on screen **A** or **B**, swipe from the left screen edge
to the right to trigger the interactive pop gesture.

- [ ] Completing the swipe/gesture pops the current screen, identical in
effect to tapping **Pop**. The screen below is shown with its original,
unchanged `Key`.
- [ ] Starting the swipe/gesture and releasing before the halfway point
cancels it: the current screen returns to place and no navigation change
occurs.

### Route key uniqueness (edge case)

10. From **Home**, tap **Push A**. While on the newly pushed **A**, tap
**Push A** again. While on that new **A**, tap **Push A** once more,
without popping in between.

- [ ] Three separate **A** instances are stacked. Each shows `Name: A`,
but the `Key` value is different every time you land on a new push.

### Rapid tapping (edge case)

11. From any screen, rapidly tap **Push A** several times in quick
succession, before each push transition finishes animating.

- [ ] Each tap results in exactly one additional **A** screen being
pushed. No crash, no dropped pushes, and no duplicate `Key` values.

12. From the deepest screen reached in step 11, rapidly tap **Pop**
several times in quick succession, before each pop transition
finishes animating.

- [ ] The stack pops one screen per completed transition, eventually
stabilizing on **Home**. No crash occurs, and no attempt is made to pop
past the root screen.
Loading