-
-
Notifications
You must be signed in to change notification settings - Fork 643
chore(test): test-tabs-events screen with scenario #3936
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
1a5e4f2
6c2a437
1131216
7dafc96
872dfc4
d6891d7
81f501b
6a44305
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| import React, { useCallback } from 'react'; | ||
| import { StyleSheet, Text } from 'react-native'; | ||
| import type { ScenarioDescription } from '@apps/tests/shared/helpers'; | ||
| import { createScenario } from '@apps/tests/shared/helpers'; | ||
| import { | ||
| TabsContainer, | ||
| type TabRouteConfig, | ||
| DEFAULT_TAB_ROUTE_OPTIONS, | ||
| useTabsNavigationContext, | ||
| } from '@apps/shared/gamma/containers/tabs'; | ||
| import { CenteredLayoutView } from '@apps/shared/CenteredLayoutView'; | ||
| import { ToastProvider, useToast } from '@apps/shared/'; | ||
| import { Colors } from '@apps/shared/styling'; | ||
|
|
||
| const scenarioDescription: ScenarioDescription = { | ||
| name: 'Tabs lifecycle events', | ||
| key: 'test-tabs-events', | ||
| details: | ||
| 'Verifies onWillAppear, onDidAppear, onWillDisappear, onDidDisappear fire in the correct order when switching tabs.', | ||
| platforms: ['ios', 'android'], | ||
| }; | ||
|
|
||
| function TabScreen() { | ||
| const { routeKey } = useTabsNavigationContext(); | ||
|
|
||
| return ( | ||
| <CenteredLayoutView testID={`tabContent-${routeKey}`}> | ||
| <Text style={styles.tabLabel} testID={`tabLabel-${routeKey}`}> | ||
| {routeKey} | ||
| </Text> | ||
| <Text style={styles.tabHint}>Switch tabs to trigger lifecycle events</Text> | ||
| </CenteredLayoutView> | ||
| ); | ||
| } | ||
|
|
||
| function AppContents() { | ||
| const toast = useToast(); | ||
|
|
||
| const makeCallbacks = useCallback( | ||
| (tabName: string) => ({ | ||
| onWillAppear: () => | ||
| toast.push({ | ||
| message: `${tabName}: onWillAppear`, | ||
| backgroundColor: Colors.GreenLight100, | ||
| }), | ||
| onDidAppear: () => | ||
| toast.push({ | ||
| message: `${tabName}: onDidAppear`, | ||
| backgroundColor: Colors.BlueLight100, | ||
| }), | ||
| onWillDisappear: () => | ||
| toast.push({ | ||
| message: `${tabName}: onWillDisappear`, | ||
| backgroundColor: Colors.NavyLight60, | ||
| }), | ||
| onDidDisappear: () => | ||
| toast.push({ | ||
| message: `${tabName}: onDidDisappear`, | ||
| backgroundColor: Colors.NavyLight100, | ||
| }), | ||
| }), | ||
| [toast], | ||
| ); | ||
|
|
||
| const TAB_CONFIGS: TabRouteConfig[] = [ | ||
| { | ||
| name: 'TabA', | ||
| Component: TabScreen, | ||
| options: { | ||
| ...DEFAULT_TAB_ROUTE_OPTIONS, | ||
| title: 'Tab A', | ||
| ...makeCallbacks('TabA'), | ||
| }, | ||
| }, | ||
| { | ||
| name: 'TabB', | ||
| Component: TabScreen, | ||
| options: { | ||
| ...DEFAULT_TAB_ROUTE_OPTIONS, | ||
| title: 'Tab B', | ||
| ...makeCallbacks('TabB'), | ||
| }, | ||
| }, | ||
| { | ||
| name: 'TabC', | ||
| Component: TabScreen, | ||
| options: { | ||
| ...DEFAULT_TAB_ROUTE_OPTIONS, | ||
| title: 'Tab C', | ||
| ...makeCallbacks('TabC'), | ||
| }, | ||
| }, | ||
| ]; | ||
|
|
||
| return <TabsContainer routeConfigs={TAB_CONFIGS} />; | ||
| } | ||
|
|
||
| export function App() { | ||
| return ( | ||
| <ToastProvider> | ||
| <AppContents /> | ||
| </ToastProvider> | ||
| ); | ||
| } | ||
|
|
||
| export default createScenario(App, scenarioDescription); | ||
|
|
||
| const styles = StyleSheet.create({ | ||
| tabLabel: { | ||
| fontSize: 24, | ||
| fontWeight: 'bold', | ||
| marginBottom: 8, | ||
| }, | ||
| tabHint: { | ||
| color: '#666', | ||
| fontSize: 13, | ||
| textAlign: 'center', | ||
| }, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| # Test Scenario: Tabs lifecycle events | ||
|
|
||
| ## Details | ||
|
|
||
| **Description:** Verifies that `onWillAppear`, `onDidAppear`, | ||
| `onWillDisappear`, and `onDidDisappear` fire in the correct order on tab | ||
| switches, covering happy-path transitions, re-tapping the active tab, and | ||
| rapid switching. | ||
|
|
||
| **OS test creation version:** iOS: 18.6 and 26.2, Android: 18.6. | ||
|
|
||
| ## E2E test | ||
|
|
||
| Other: On going research | ||
|
LKuchno marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## Prerequisites | ||
|
|
||
| - iOS simulator or device (iPhone) | ||
| - Android emulator or device | ||
|
|
||
| ## Note | ||
|
|
||
| - All four events should fire on every tab switch. The expected order for | ||
| a switch between TabX and TabY depends on platform | ||
| For iOS: | ||
| 1. `TabY: onWillAppear` | ||
| 2. `TabX: onWillDisappear` | ||
| 3. `TabY: onDidAppear` | ||
| 4. `TabX: onDidDisappear` | ||
| For Android: | ||
| 1. `TabX: onWillDisappear` | ||
| 2. `TabX: onDidDisappear` | ||
| 3. `TabY: onWillAppear` | ||
| 4. `TabY: onDidAppear` | ||
| - Toasts stack and dismiss automatically; observe each toast color and | ||
| label as it appears. To dismiss a toast manually, tap it. | ||
| - Re-tapping the currently active tab must not fire any lifecycle events. | ||
|
|
||
| ## Steps | ||
|
|
||
| ### Baseline | ||
|
|
||
| 1. Launch the app and navigate to **Tabs lifecycle events**. | ||
|
|
||
| - [ ] Expected: Three tabs are visible in the tab bar: **Tab A**, **Tab B**, | ||
| and **Tab C**. **Tab A** is selected. Two toasts | ||
| appear for the initial Tab A appearance: | ||
| - `TabA: onWillAppear` | ||
| - `TabA: onDidAppear` | ||
|
|
||
| --- | ||
|
|
||
| ### Tab A → Tab B transition | ||
|
|
||
| 2. Tap **Tab B** in the tab bar. | ||
|
|
||
| - [ ] Expected: The content area switches to show "TabB". Four toast | ||
| notifications appear in order specific platform: | ||
| - `TabB: onWillAppear` (green background) | ||
|
||
| - `TabA: onWillDisappear` (light navy background) | ||
| - `TabB: onDidAppear` (light blue background) | ||
| - `TabA: onDidDisappear` (dark navy background) | ||
|
|
||
|
||
| --- | ||
|
|
||
| ### Tab B → Tab C transition | ||
|
|
||
| 3. Tap **Tab C** in the tab bar. | ||
|
|
||
| - [ ] Expected: The content area switches to show "TabC". Four toast | ||
| notifications appear in order specific platform: | ||
| - `TabC: onWillAppear` (green background) | ||
| - `TabB: onWillDisappear` (light navy background) | ||
| - `TabC: onDidAppear` (light blue background) | ||
| - `TabB: onDidDisappear` (dark navy background) | ||
|
|
||
| --- | ||
|
|
||
| ### Tab C → Tab A transition | ||
|
|
||
| 4. Tap **Tab A** in the tab bar. | ||
|
|
||
| - [ ] Expected: The content area switches to show "TabA". Four toast | ||
| notifications appear in order specific platform: | ||
| - `TabA: onWillAppear` (green background) | ||
| - `TabC: onWillDisappear` (light navy background) | ||
| - `TabA: onDidAppear` (light blue background) | ||
| - `TabC: onDidDisappear` (dark navy background) | ||
|
|
||
| --- | ||
|
|
||
| ### Re-tapping the active tab (edge case) | ||
|
|
||
| 5. With **Tab A** selected, tap **Tab A** again in the tab bar. | ||
|
|
||
| - [ ] Expected: The content area does not change. No toast notifications | ||
| appear. No lifecycle events fire for a tap on the already-active tab. | ||
|
|
||
| --- | ||
|
|
||
| ### Rapid tab switching (edge case) | ||
|
|
||
| 6. Tap **Tab B**, then immediately tap **Tab C** before the toasts from | ||
| the previous step have finished dismissing. | ||
|
|
||
| - [ ] Expected: Both transitions complete. Toasts from the B→C transition | ||
| appear after the A→B toasts. The final selected | ||
| tab is **Tab C** and its content area shows "TabC". No events are | ||
| missing or duplicated — all eight toasts from both transitions are | ||
| eventually shown. | ||
|
|
||
| --- | ||
|
|
||
| ### Full round-trip verification | ||
|
|
||
| 7. From **Tab C**, tap **Tab A**, then **Tab B**, then **Tab C**. | ||
|
|
||
| - [ ] Expected: Each tab switch produces exactly four toasts (will/did | ||
| disappear for the leaving tab, will/did appear for the arriving tab). | ||
| After three switches, twelve toasts in total have been fired. The final | ||
| selected tab is **Tab C**. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TAB_CONFIGSis recreated on every render ofAppContents(). In this codebase other tab scenarios typically keepROUTE_CONFIGS/TAB_CONFIGSstable (module-level const oruseMemo), which avoids unnecessary work inTabsContainer(includinguseSanitizeRouteConfigs) and makes the component tree less noisy during re-renders (e.g., when a toast is pushed).