From 9032612ec8954f88a62b8f3f0888d5fc957f2de3 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Thu, 9 Jul 2026 13:34:40 +0200 Subject: [PATCH 1/6] new test-stack-lifecycle-events added --- .../single-feature-tests/stack-v5/index.ts | 3 + .../test-stack-lifecycle-events/index.tsx | 179 ++++++++++++++++++ .../scenario-description.ts | 10 + .../test-stack-lifecycle-events/scenario.md | 170 +++++++++++++++++ 4 files changed, 362 insertions(+) create mode 100644 apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/index.tsx create mode 100644 apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario-description.ts create mode 100644 apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md diff --git a/apps/src/tests/single-feature-tests/stack-v5/index.ts b/apps/src/tests/single-feature-tests/stack-v5/index.ts index f8dd9a5e45..6f08d9925f 100644 --- a/apps/src/tests/single-feature-tests/stack-v5/index.ts +++ b/apps/src/tests/single-feature-tests/stack-v5/index.ts @@ -4,6 +4,7 @@ import type { ScenarioGroup } from '@apps/tests/shared/helpers'; // scenario group consumed by the selection menu. import TestStackPreventNativeDismissSingleStack from './prevent-native-dismiss-single-stack'; import TestStackPreventNativeDismissNestedStack from './prevent-native-dismiss-nested-stack'; +import TestStackLifecycleEvents from './test-stack-lifecycle-events'; import TestStackAnimationAndroid from './test-animation-android'; import TestStackSimpleNav from './test-stack-simple-nav'; import TestStackSubviewsAndroid from './test-stack-subviews-android'; @@ -24,6 +25,7 @@ import TestStackHeaderSelectiveUpdates from './test-stack-header-selective-updat // under a name for direct rendering (e.g. from App.tsx or e2e harnesses). export { default as TestStackPreventNativeDismissSingleStack } from './prevent-native-dismiss-single-stack'; export { default as TestStackPreventNativeDismissNestedStack } from './prevent-native-dismiss-nested-stack'; +export { default as TestStackLifecycleEvents } from './test-stack-lifecycle-events'; export { default as TestStackAnimationAndroid } from './test-animation-android'; export { default as TestStackSimpleNav } from './test-stack-simple-nav'; export { default as TestStackSubviewsAndroid } from './test-stack-subviews-android'; @@ -41,6 +43,7 @@ export { default as TestStackToolbarNestedMenu } from './test-stack-toolbar-nest const scenarios = { TestStackPreventNativeDismissSingleStack, TestStackPreventNativeDismissNestedStack, + TestStackLifecycleEvents, TestStackAnimationAndroid, TestStackSimpleNav, TestStackSubviewsAndroid, diff --git a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/index.tsx b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/index.tsx new file mode 100644 index 0000000000..3da3c4d029 --- /dev/null +++ b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/index.tsx @@ -0,0 +1,179 @@ +import React, { useCallback } 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 { ToastProvider, useToast } from '@apps/shared'; +import { StackNavigationButtons } from '@apps/tests/shared/components/stack-v5/StackNavigationButtons'; + +function TestStackLifecycleEvents() { + return ( + + + + ); +} + +function useMakeLifecycleCallbacks() { + const toast = useToast(); + + return useCallback( + (screenName: string) => ({ + onWillAppear: () => + toast.push({ + message: `${screenName}: onWillAppear`, + backgroundColor: Colors.GreenLight60, + }), + onDidAppear: () => + toast.push({ + message: `${screenName}: onDidAppear`, + backgroundColor: Colors.BlueLight100, + }), + onWillDisappear: () => + toast.push({ + message: `${screenName}: onWillDisappear`, + backgroundColor: Colors.NavyLight60, + }), + onDidDisappear: () => + toast.push({ + message: `${screenName}: onDidDisappear`, + backgroundColor: Colors.NavyLight100, + }), + }), + [toast], + ); +} + +function StackSetup() { + const makeCallbacks = useMakeLifecycleCallbacks(); + + return ( + + ); +} + +function HomeScreen() { + return ( + + + + + ); +} + +function AScreen() { + return ( + + + + + ); +} + +function NestedStackScreen() { + const makeCallbacks = useMakeLifecycleCallbacks(); + + return ( + + ); +} + +function NestedHomeScreen() { + return ( + + + + + ); +} + +function NestedAScreen() { + return ( + + + + + ); +} + +function RouteInformation(props: { routeName: string }) { + const routeKey = useStackNavigationContext().routeKey; + + return ( + + Name: {props.routeName} + Key: {routeKey} + + ); +} + +const styles = StyleSheet.create({ + routeInformation: { + color: 'black', + fontSize: 20, + fontWeight: 'bold', + }, +}); + +export default createScenario( + TestStackLifecycleEvents, + scenarioDescription, +); diff --git a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario-description.ts b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario-description.ts new file mode 100644 index 0000000000..93ab0952b8 --- /dev/null +++ b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario-description.ts @@ -0,0 +1,10 @@ +import type { ScenarioDescription } from '@apps/tests/shared/helpers'; + +export const scenarioDescription: ScenarioDescription = { + name: 'Stack lifecycle events', + key: 'test-stack-lifecycle-events', + details: 'Verify lifecycle events (onWillAppear, etc.) fire on stack navigation', + platforms: ['android', 'ios'], + e2eCoverage: 'tbd', + smokeTest: false, +}; diff --git a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md new file mode 100644 index 0000000000..8ee333425d --- /dev/null +++ b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md @@ -0,0 +1,170 @@ +# Test Scenario: Stack lifecycle events + +## Details + +**Description:** Verifies that `onWillAppear`, `onDidAppear`, +`onWillDisappear`, and `onDidDisappear` fire in the correct order on stack +navigation, covering push, pop via the **Pop** button, pop via the **header +back button**, pop via the **native back gesture / system back**, and a +nested stack. + +**OS test creation version:** iOS: 18.6 and 26.2, Android: API LEVEL 36. + +## E2E test + +- **TBD:** E2E test has to be implemented in future. + +## Prerequisites + +- iOS simulator or device (iPhone) +- Android emulator or device + +## Note + +- **iOS and Android fire a fundamentally different set of events**, so verify + each platform against its own column below. + + **Android** — only the screen entering or leaving the stack fires; the + screen underneath stays silent: + - **Push (Y pushed over X):** only the pushed screen fires — + `Y: onWillAppear`, then `Y: onDidAppear`. X fires nothing. + - **Pop (Y popped, back to X):** only the popped screen fires — + `Y: onWillDisappear`, then `Y: onDidDisappear`. X fires nothing. + + **iOS** — both screens fire; push and pop share the same interleaving — + the leaving screen's disappear brackets the entering screen's appear + (`willDisappear` → `willAppear` → `didDisappear` → `didAppear`): + - **Push (Y pushed over X)** — X is leaving, Y is entering: + 1. `X: onWillDisappear` + 2. `Y: onWillAppear` + 3. `X: onDidDisappear` + 4. `Y: onDidAppear` + - **Pop (Y popped, back to X)** — Y is leaving, X is entering: + 1. `Y: onWillDisappear` + 2. `X: onWillAppear` + 3. `Y: onDidDisappear` + 4. `X: onDidAppear` + +- **Nested stack:** pushing the `NestedStack` route also mounts its inner + stack's initial screen (`NestedHome`), so the appearance events are + **duplicated** — both `NestedStack` and `NestedHome` fire their appear + events on both platforms. The same duplication applies on pop. + +- The dismissal method (Pop button, header back button, or native back + gesture / system back) must **not** change which events fire — all three + produce the same pop event set for the same transition. + +- Toasts stack and dismiss automatically. To dismiss a toast manually, tap + it. Toast background colors by event type: `onWillAppear` — green, + `onWillDisappear` — light navy, `onDidAppear` — light blue, + `onDidDisappear` — dark navy. + +## Steps + +### Baseline + +1. Launch the app and navigate to **Stack lifecycle events**. + +- [ ] The **Home** screen is visible with buttons **Push A** and **Push + NestedStack**. Two toasts appear for the initial Home appearance + (both platforms): + - `Home: onWillAppear` + - `Home: onDidAppear` + +--- + +### Push — Home → A + +2. Tap **Push A**. + +- [ ] Screen **A** (header title "A") is pushed. + + **iOS** — four toasts: + 1. `Home: onWillDisappear` + 2. `A: onWillAppear` + 3. `Home: onDidDisappear` + 4. `A: onDidAppear` + + **Android** — two toasts (only the pushed screen fires): + 1. `A: onWillAppear` + 2. `A: onDidAppear` + +--- + +### Pop via header back button — A → Home + +3. On screen **A**, tap the **header back button** (top-left). + +- [ ] Screen **Home** is shown again. + + **iOS** — four toasts: + 1. `A: onWillDisappear` + 2. `Home: onWillAppear` + 3. `A: onDidDisappear` + 4. `Home: onDidAppear` + + **Android** — two toasts (only the popped screen fires): + 1. `A: onWillDisappear` + 2. `A: onDidDisappear` + +--- + +### Pop via native back gesture / system back — A → Home + +4. Tap **Push A** again. Then dismiss screen **A** using the **native back + gesture** (iOS: swipe from the left screen edge) or the **Android system + back** (gesture / hardware button). + +- [ ] Screen **Home** is shown again. The same pop toasts appear as in step 3 + (iOS: four; Android: two), confirming the native gesture / system back + produces the identical pop event set. + +--- + +### Pop via Pop button — A → Home + +5. Tap **Push A**, then on screen **A** tap the **Pop** button. + +- [ ] Screen **Home** is shown again. The same pop toasts appear as in step 3 + (iOS: four; Android: two). The Pop button, the header back button + (step 3), and the native gesture (step 4) all produce the identical pop + event set. + +--- + +### Nested stack — push + +6. From **Home**, tap **Push NestedStack**. + +- [ ] The **NestedStack** route is pushed and its inner stack shows + **NestedHome** (buttons **Push NestedA**, **Pop**). The appearance events + are **duplicated** across the outer route and the nested initial screen — + both `NestedStack` and `NestedHome` fire. + + **iOS** — six toasts (both containers' `onWillAppear` fire before the + previous screen's `onDidDisappear`): + 1. `Home: onWillDisappear` + 2. `NestedStack: onWillAppear` + 3. `NestedHome: onWillAppear` + 4. `Home: onDidDisappear` + 5. `NestedStack: onDidAppear` + 6. `NestedHome: onDidAppear` + + **Android** — four toasts (`Home` fires nothing): + 1. `NestedStack: onWillAppear` + 2. `NestedHome: onWillAppear` + 3. `NestedHome: onDidAppear` + 4. `NestedStack: onDidAppear` + +--- + +### Nested stack — inner push / pop + +7. On **NestedHome**, tap **Push NestedA**, then pop back with the **header + back button**. + +- [ ] Screen **NestedA** (header title "NestedA") is pushed inside the nested + stack, then popped back to **NestedHome**. Push and pop fire the same + per-platform event sets as at the top level (iOS: four per transition; + Android: two per transition — appear for the entering screen on push, + disappear for the leaving screen on pop). From e52cf955a25d224c5264e6cff6c5e649c18cda2d Mon Sep 17 00:00:00 2001 From: lkuchno <45803783+LKuchno@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:50:42 +0200 Subject: [PATCH 2/6] Updating uppercase LEVEL to Level Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../stack-v5/test-stack-lifecycle-events/scenario.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md index 8ee333425d..31c96c7767 100644 --- a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md +++ b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md @@ -8,7 +8,7 @@ navigation, covering push, pop via the **Pop** button, pop via the **header back button**, pop via the **native back gesture / system back**, and a nested stack. -**OS test creation version:** iOS: 18.6 and 26.2, Android: API LEVEL 36. +**OS test creation version:** iOS: 18.6 and 26.2, Android: API Level 36. ## E2E test From e822b7e2fcf1739ac6347948a308af8c2d5ea544 Mon Sep 17 00:00:00 2001 From: lkuchno <45803783+LKuchno@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:50:58 +0200 Subject: [PATCH 3/6] Adding 'the' to sentence Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../stack-v5/test-stack-lifecycle-events/scenario.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md index 31c96c7767..017dd5da60 100644 --- a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md +++ b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md @@ -12,7 +12,7 @@ nested stack. ## E2E test -- **TBD:** E2E test has to be implemented in future. +- **TBD:** E2E test has to be implemented in the future. ## Prerequisites From e237ff7eaddac9f2a96dcf03759e7b8f2710018d Mon Sep 17 00:00:00 2001 From: lkuchno <45803783+LKuchno@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:51:10 +0200 Subject: [PATCH 4/6] Formatting fix Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../stack-v5/test-stack-lifecycle-events/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/index.tsx b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/index.tsx index 3da3c4d029..bbf652a4a4 100644 --- a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/index.tsx +++ b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/index.tsx @@ -103,7 +103,7 @@ function AScreen() { return ( - + ); } From 340212bfbb4af7da0af0744ba33dc5c6d29bd4c3 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Fri, 10 Jul 2026 10:18:08 +0200 Subject: [PATCH 5/6] adding test for dissmis/pop nested stack --- .../test-stack-lifecycle-events/scenario.md | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md index 8ee333425d..79528d09a4 100644 --- a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md +++ b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md @@ -19,6 +19,28 @@ nested stack. - iOS simulator or device (iPhone) - Android emulator or device +## Android launch + +- To test the native-back / gesture-back pop flows, run the screen + **directly** by editing [apps/App.tsx](../../../../../App.tsx): import and + render `TestStackLifecycleEvents` as the root component instead of + `Example`, e.g.: + + ```tsx + import { TestStackLifecycleEvents as Example } from './src/tests/single-feature-tests'; + ``` + + With the gamma `StackContainer` at the root, native back and gesture-back + interact with the stack directly. When the screen is nested inside the + example app's own navigation, native back navigates out to the + system/selection menu instead of popping the stack (issue + [#1459](https://github.com/software-mansion/react-native-screens-labs/issues/1459)), + which is why Android is tested via the direct launch. + +- The 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 - **iOS and Android fire a fundamentally different set of events**, so verify @@ -153,8 +175,8 @@ nested stack. **Android** — four toasts (`Home` fires nothing): 1. `NestedStack: onWillAppear` 2. `NestedHome: onWillAppear` - 3. `NestedHome: onDidAppear` - 4. `NestedStack: onDidAppear` + 3. `NestedStack: onDidAppear` + 4. `NestedHome: onDidAppear` --- @@ -168,3 +190,25 @@ nested stack. per-platform event sets as at the top level (iOS: four per transition; Android: two per transition — appear for the entering screen on push, disappear for the leaving screen on pop). + +8. On **NestedHome** dismiss screen using the **native back + gesture** (iOS: swipe from the left screen edge) or the **Android system + back** (gesture / hardware button). + +- [ ] Screen **Home** is shown again. The pop toasts appear - + both `NestedStack` and `NestedHome` fire. + + **iOS** — six toasts (both containers' `onWillAppear` fire before the + previous screen's `onDidDisappear`): + 1. `Home: onWillDisappear` + 2. `NestedStack: onWillAppear` + 3. `NestedHome: onWillAppear` + 4. `Home: onDidDisappear` + 5. `NestedStack: onDidAppear` + 6. `NestedHome: onDidAppear` + + **Android** — four toasts (`Home` fires nothing): + 1. `NestedHome: onWillDisappear` + 2. `NestedStack: onWillDisappear` + 3. `NestedHome: onDidDisappear` + 4. `NestedStack: onDidDisappear` From 125d7069ba3c8c9e461816f0cdc009c677f8cd25 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Fri, 10 Jul 2026 12:08:13 +0200 Subject: [PATCH 6/6] changing header for nestedStack to be visible with title, add new steps to confim pop/push events for nested stacks --- .../test-stack-lifecycle-events/index.tsx | 2 +- .../test-stack-lifecycle-events/scenario.md | 135 ++++++++++++++---- 2 files changed, 112 insertions(+), 25 deletions(-) diff --git a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/index.tsx b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/index.tsx index bbf652a4a4..ec650d8a3a 100644 --- a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/index.tsx +++ b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/index.tsx @@ -78,7 +78,7 @@ function StackSetup() { options: { ...makeCallbacks('NestedStack'), headerConfig: { - hidden: true, + title: 'NestedStack', }, }, }, diff --git a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md index 7bc918d0cd..3613c846b3 100644 --- a/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md +++ b/apps/src/tests/single-feature-tests/stack-v5/test-stack-lifecycle-events/scenario.md @@ -5,8 +5,10 @@ **Description:** Verifies that `onWillAppear`, `onDidAppear`, `onWillDisappear`, and `onDidDisappear` fire in the correct order on stack navigation, covering push, pop via the **Pop** button, pop via the **header -back button**, pop via the **native back gesture / system back**, and a -nested stack. +back button**, and pop via the **native back gesture / system back**. The same +push and pop checks are then repeated **inside a nested stack** and **across +the nested-stack boundary** (popping the whole container back to the outer +stack), exercising every dismissal method available at each level. **OS test creation version:** iOS: 18.6 and 26.2, Android: API Level 36. @@ -72,9 +74,20 @@ nested stack. **duplicated** — both `NestedStack` and `NestedHome` fire their appear events on both platforms. The same duplication applies on pop. + Navigation **inside** the nested stack (`NestedHome` ↔ `NestedA`) fires only + the inner screens and behaves exactly like a top-level push/pop — the outer + `NestedStack` route and `Home` stay silent. Crossing the **boundary** back + out (`NestedStack` → `Home`) pops the whole container and fires the + duplicated events described above. + - The dismissal method (Pop button, header back button, or native back gesture / system back) must **not** change which events fire — all three - produce the same pop event set for the same transition. + produce the same pop event set for the same transition. This holds at every + level: the top-level stack, the inner nested stack, and the container + boundary. At the container boundary there is **no header back button** (the + `NestedStack` route's header is hidden and `NestedHome` is the inner root), + so that crossing is exercised only via the Pop button and the native + gesture / system back — both of which pop the whole container. - Toasts stack and dismiss automatically. To dismiss a toast manually, tap it. Toast background colors by event type: `onWillAppear` — green, @@ -180,35 +193,109 @@ nested stack. --- -### Nested stack — inner push / pop +### Nested stack — inner push — NestedHome → NestedA -7. On **NestedHome**, tap **Push NestedA**, then pop back with the **header - back button**. +7. On **NestedHome**, tap **Push NestedA**. -- [ ] Screen **NestedA** (header title "NestedA") is pushed inside the nested - stack, then popped back to **NestedHome**. Push and pop fire the same - per-platform event sets as at the top level (iOS: four per transition; - Android: two per transition — appear for the entering screen on push, - disappear for the leaving screen on pop). +- [ ] Screen **NestedA** (header title "NestedA") is pushed **inside the + nested stack**. Only the inner screens fire — the outer `NestedStack` route + and `Home` stay silent — so this behaves exactly like a top-level push + (step 2): -8. On **NestedHome** dismiss screen using the **native back - gesture** (iOS: swipe from the left screen edge) or the **Android system - back** (gesture / hardware button). + **iOS** — four toasts: + 1. `NestedHome: onWillDisappear` + 2. `NestedA: onWillAppear` + 3. `NestedHome: onDidDisappear` + 4. `NestedA: onDidAppear` -- [ ] Screen **Home** is shown again. The pop toasts appear - - both `NestedStack` and `NestedHome` fire. + **Android** — two toasts (only the pushed screen fires): + 1. `NestedA: onWillAppear` + 2. `NestedA: onDidAppear` - **iOS** — six toasts (both containers' `onWillAppear` fire before the - previous screen's `onDidDisappear`): - 1. `Home: onWillDisappear` - 2. `NestedStack: onWillAppear` - 3. `NestedHome: onWillAppear` - 4. `Home: onDidDisappear` - 5. `NestedStack: onDidAppear` - 6. `NestedHome: onDidAppear` +--- + +### Nested stack — inner pop via header back button — NestedA → NestedHome + +8. On screen **NestedA**, tap the **header back button** (top-left). + +- [ ] Screen **NestedHome** is shown again inside the nested stack. Only the + inner screens fire — this is the inner mirror of the top-level pop (step 3): + + **iOS** — four toasts: + 1. `NestedA: onWillDisappear` + 2. `NestedHome: onWillAppear` + 3. `NestedA: onDidDisappear` + 4. `NestedHome: onDidAppear` + + **Android** — two toasts (only the popped screen fires): + 1. `NestedA: onWillDisappear` + 2. `NestedA: onDidDisappear` + +--- + +### Nested stack — inner pop via native gesture — NestedA → NestedHome + +9. Tap **Push NestedA** again. Then dismiss screen **NestedA** using the + **native back gesture** (iOS: swipe from the left screen edge) or the + **Android system back** (gesture / hardware button). + +- [ ] Screen **NestedHome** is shown again inside the nested stack. The same + inner pop toasts appear as in step 8 (iOS: four; Android: two), confirming + the native gesture / system back produces the identical inner pop event set. + +--- + +### Nested stack — inner pop via Pop button — NestedA → NestedHome + +10. Tap **Push NestedA**, then on screen **NestedA** tap the **Pop** button. + +- [ ] Screen **NestedHome** is shown again inside the nested stack. The same + inner pop toasts appear as in step 8 (iOS: four; Android: two). The Pop + button, the header back button (step 8), and the native gesture (step 9) all + produce the identical inner pop event set. + +--- + +### Nested stack — pop to Home via native gesture — NestedStack → Home + +11. Back on **NestedHome** (the nested stack's initial screen), dismiss the + screen using the **native back gesture** (iOS: swipe from the left screen + edge) or the **Android system back** (gesture / hardware button). + +- [ ] Screen **Home** is shown again. This pops the whole **NestedStack** + container, so the appearance events are **duplicated** — both `NestedStack` + and `NestedHome` fire their disappear events (the mirror of the push in + step 6): + + **iOS** — six toasts (both containers' `onWillDisappear` fire before the + entering screen's `onWillAppear`): + 1. `NestedHome: onWillDisappear` + 2. `NestedStack: onWillDisappear` + 3. `Home: onWillAppear` + 4. `NestedHome: onDidDisappear` + 5. `NestedStack: onDidDisappear` + 6. `Home: onDidAppear` **Android** — four toasts (`Home` fires nothing): 1. `NestedHome: onWillDisappear` 2. `NestedStack: onWillDisappear` 3. `NestedHome: onDidDisappear` 4. `NestedStack: onDidDisappear` + +--- + +### Nested stack — pop to Home via Pop button — NestedStack → Home + +12. From **Home**, tap **Push NestedStack** again. Then on **NestedHome** tap + the **Pop** button. + +- [ ] Screen **Home** is shown again. Because `NestedHome` is the nested + stack's only (root) screen, the Pop button pops the whole **NestedStack** + container rather than a screen within it, so the same container-pop toasts + appear as in step 11 (iOS: six; Android: four). + + The Pop button and the native gesture (step 11) produce the identical + container-pop event set. There is **no header back button** at this + boundary — the `NestedStack` route's header is hidden and `NestedHome` is + the inner root — so the container pop is exercised only via these two + methods.