From c83619e6beff4aa9ffd546d2a92db2c92b32e163 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Mon, 1 Jun 2026 06:59:21 +0200 Subject: [PATCH 01/22] first draft --- .../tabs/test-tabs-system-item-ios/index.tsx | 188 ++++++++++++++++++ .../scenario-description.ts | 13 ++ 2 files changed, 201 insertions(+) create mode 100644 apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx create mode 100644 apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx new file mode 100644 index 0000000000..df297b4787 --- /dev/null +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -0,0 +1,188 @@ +import React, { useState } from 'react'; +import { StyleSheet, Text, View, ScrollView } from 'react-native'; +import { scenarioDescription } from './scenario-description'; +import { createScenario } from '@apps/tests/shared/helpers'; +import { + TabsContainerWithHostConfigContext, + type TabRouteConfig, + DEFAULT_TAB_ROUTE_OPTIONS, +} from '@apps/shared/gamma/containers/tabs'; +import { SettingsPicker } from '@apps/shared'; +import type { TabsScreenSystemItem } from 'react-native-screens'; +import { Colors } from '@apps/shared/styling'; + +const SYSTEM_ITEMS: TabsScreenSystemItem[] = [ + 'bookmarks', + 'contacts', + 'downloads', + 'favorites', + 'featured', + 'history', + 'more', + 'mostRecent', + 'mostViewed', + 'recents', + 'search', + 'topRated', +]; + +function ConfigScreen() { + const [selectedSystemItem, setSelectedSystemItem] = useState( + 'bookmarks' + ); + + return ( + + + System Item Selection + + The selected system item will override the custom title and icon on the tab. Observe how the + system icon and appearance changes as you select different items. + + + label="systemItem" + value={selectedSystemItem} + onValueChange={setSelectedSystemItem} + items={[undefined, ...SYSTEM_ITEMS] as Array} + /> + {selectedSystemItem && ( + + Current: {selectedSystemItem} + + )} + + + ); +} + +function DemoScreen() { + return ( + + Demo Tab + + This tab demonstrates how the selected system item appears in the tab bar.{'\n'} + {'\n'} + When a system item is set, the tab bar automatically displays the + appropriate system icon and label, overriding any custom title or icon + you may have configured. + + + ); +} + +interface AppProps { + systemItem: TabsScreenSystemItem | undefined; +} + +function AppWithSystemItem({ systemItem }: AppProps) { + const routeConfigs: TabRouteConfig[] = [ + { + name: 'Config', + Component: ConfigScreen, + options: { + ...DEFAULT_TAB_ROUTE_OPTIONS, + title: 'Config', + }, + }, + { + name: 'Demo', + Component: DemoScreen, + options: { + ...DEFAULT_TAB_ROUTE_OPTIONS, + title: 'Demo Tab', + ios: { + ...(systemItem ? { systemItem } : {}), + }, + }, + }, + ]; + + return ; +} + +export function App() { + const [systemItem, setSystemItem] = useState('bookmarks'); + + return ( + + + System Item + + label="" + value={systemItem} + onValueChange={setSystemItem} + items={[undefined, ...SYSTEM_ITEMS] as Array} + /> + + + + ); +} + +const styles = StyleSheet.create({ + appContainer: { + flex: 1, + }, + controlPanel: { + paddingHorizontal: 16, + paddingVertical: 12, + backgroundColor: '#f5f5f5', + borderBottomWidth: 1, + borderBottomColor: '#e0e0e0', + }, + controlLabel: { + fontSize: 12, + fontWeight: '600', + color: '#666', + marginBottom: 8, + }, + container: { + flex: 1, + padding: 16, + }, + content: { + gap: 16, + }, + title: { + fontSize: 18, + fontWeight: '700', + textAlign: 'center', + color: '#000', + }, + description: { + fontSize: 13, + color: Colors.LightOffNavy, + textAlign: 'center', + lineHeight: 18, + }, + info: { + fontSize: 13, + color: '#555', + textAlign: 'center', + marginTop: 8, + }, + highlight: { + fontWeight: '600', + color: Colors.GreenDark100, + }, + centeredScreen: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + padding: 24, + gap: 12, + }, + label: { + fontSize: 17, + fontWeight: '600', + textAlign: 'center', + }, + hint: { + fontSize: 13, + color: Colors.LightOffNavy, + textAlign: 'center', + lineHeight: 20, + }, +}); + +export default createScenario(App, scenarioDescription); diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts new file mode 100644 index 0000000000..01d111c05b --- /dev/null +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts @@ -0,0 +1,13 @@ +import type { ScenarioDescription } from '@apps/tests/shared/helpers'; + +export const scenarioDescription: ScenarioDescription = { + name: 'Tab Bar Item System Item', + key: 'test-tabs-system-item-ios', + details: + 'Exercises the systemItem prop of TabsScreen on iOS. Demonstrates all 12 system item types ' + + '(bookmarks, contacts, downloads, favorites, featured, history, more, mostRecent, mostViewed, ' + + 'recents, search, topRated) and shows how systemItem overrides custom title and icon.', + platforms: ['ios'], + e2eCoverage: 'incomplete', + smokeTest: false, +}; From d19eafb8193f44a5aea145403f38a10266936b0c Mon Sep 17 00:00:00 2001 From: lkuchno Date: Mon, 1 Jun 2026 07:00:02 +0200 Subject: [PATCH 02/22] index with new test screen --- apps/src/tests/single-feature-tests/tabs/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/src/tests/single-feature-tests/tabs/index.ts b/apps/src/tests/single-feature-tests/tabs/index.ts index 39584ef9dc..421c8abe47 100644 --- a/apps/src/tests/single-feature-tests/tabs/index.ts +++ b/apps/src/tests/single-feature-tests/tabs/index.ts @@ -18,6 +18,7 @@ import TestTabsSpecialEffectsScrollToTop from './test-tabs-special-effects-scrol import TestTabsTabBarExperimentalUserInterfaceStyle from './test-tabs-tab-bar-experimental-user-interface-style-ios'; import TestTabsLifecycleEvents from './test-tabs-lifecycle-events'; import TestTabsItemTitle from './test-tabs-item-title-ios'; +import TestTabsSystemItem from './test-tabs-system-item-ios'; const scenarios = { TestTabBottomAccessory, @@ -38,6 +39,7 @@ const scenarios = { TestTabsTabBarExperimentalUserInterfaceStyle, TestTabsLifecycleEvents, TestTabsItemTitle, + TestTabsSystemItem, }; const TabsScenarioGroup: ScenarioGroup = { From aab1c133fa64cfb996fceba8eeab163ef33e1078 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Mon, 1 Jun 2026 13:28:56 +0200 Subject: [PATCH 03/22] chore(test): test-tabs-system-item-ios --- .../tabs/test-tabs-system-item-ios/index.tsx | 198 ++++++++---------- .../scenario-description.ts | 6 +- .../test-tabs-system-item-ios/scenario.md | 121 +++++++++++ 3 files changed, 213 insertions(+), 112 deletions(-) create mode 100644 apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx index df297b4787..d78c3aa3a0 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -1,5 +1,5 @@ -import React, { useState } from 'react'; -import { StyleSheet, Text, View, ScrollView } from 'react-native'; +import React from 'react'; +import { StyleSheet, Text, View } from 'react-native'; import { scenarioDescription } from './scenario-description'; import { createScenario } from '@apps/tests/shared/helpers'; import { @@ -7,114 +7,116 @@ import { type TabRouteConfig, DEFAULT_TAB_ROUTE_OPTIONS, } from '@apps/shared/gamma/containers/tabs'; -import { SettingsPicker } from '@apps/shared'; -import type { TabsScreenSystemItem } from 'react-native-screens'; import { Colors } from '@apps/shared/styling'; -const SYSTEM_ITEMS: TabsScreenSystemItem[] = [ - 'bookmarks', - 'contacts', - 'downloads', - 'favorites', - 'featured', - 'history', - 'more', - 'mostRecent', - 'mostViewed', - 'recents', - 'search', - 'topRated', -]; - -function ConfigScreen() { - const [selectedSystemItem, setSelectedSystemItem] = useState( - 'bookmarks' +function NormalTabScreen() { + return ( + + Normal Tab + + This tab does not use systemItem.{'\n'} + {'\n'} + Only the custom title and (if provided) custom icon appear in the tab bar. + This is the baseline behavior when systemItem is undefined. + + ); +} +function DefaultTitleScreen() { return ( - - - System Item Selection - - The selected system item will override the custom title and icon on the tab. Observe how the - system icon and appearance changes as you select different items. - - - label="systemItem" - value={selectedSystemItem} - onValueChange={setSelectedSystemItem} - items={[undefined, ...SYSTEM_ITEMS] as Array} - /> - {selectedSystemItem && ( - - Current: {selectedSystemItem} - - )} - - + + Default System Item + + This tab uses a systemItem: `bookmarks`{'\n'}with no custom title or icon override. + + ); } -function DemoScreen() { +function CustomTitleIconOverrideScreen() { return ( - - Demo Tab + + Custom Title and Icon Override - This tab demonstrates how the selected system item appears in the tab bar.{'\n'} + This tab uses a systemItem: `contacts`.{'\n'} {'\n'} - When a system item is set, the tab bar automatically displays the - appropriate system icon and label, overriding any custom title or icon - you may have configured. + Custom title and icon are provide and display in tab bar instead of the system-provided one. ); } -interface AppProps { - systemItem: TabsScreenSystemItem | undefined; +function SearchScreen() { + return ( + + Search + + This tab uses a systemItem: `search`.{'\n'} + {'\n'} + The tab bar iteam is displayed as a magnifying glass icon with no title, which is the expected appearance for the `search` system item. + + + ); } -function AppWithSystemItem({ systemItem }: AppProps) { - const routeConfigs: TabRouteConfig[] = [ - { - name: 'Config', - Component: ConfigScreen, - options: { - ...DEFAULT_TAB_ROUTE_OPTIONS, - title: 'Config', +const ROUTE_CONFIGS: TabRouteConfig[] = [ + { + name: 'NormalTab', + Component: NormalTabScreen, + options: { + ...DEFAULT_TAB_ROUTE_OPTIONS, + title: 'NormalTab', + }, + }, + { + name: 'DefaultTitle', + Component: DefaultTitleScreen, + options: { + ...DEFAULT_TAB_ROUTE_OPTIONS, + ios: { + systemItem: 'bookmarks', }, }, - { - name: 'Demo', - Component: DemoScreen, - options: { - ...DEFAULT_TAB_ROUTE_OPTIONS, - title: 'Demo Tab', - ios: { - ...(systemItem ? { systemItem } : {}), + }, + { + name: 'CustomTitleIconOverride', + Component: CustomTitleIconOverrideScreen, + options: { + ...DEFAULT_TAB_ROUTE_OPTIONS, + title: 'Custom', + ios: { + systemItem: 'contacts', + icon: { + type: 'sfSymbol', + name: 'house', + }, + selectedIcon: { + type: 'sfSymbol', + name: 'house.fill', }, }, }, - ]; - - return ; -} + }, + { + name: 'SearchScreen', + Component: SearchScreen, + options: { + ...DEFAULT_TAB_ROUTE_OPTIONS, + ios: { + systemItem: 'search', + }, + }, + }, +]; export function App() { - const [systemItem, setSystemItem] = useState('bookmarks'); - return ( - - System Item - - label="" - value={systemItem} - onValueChange={setSystemItem} - items={[undefined, ...SYSTEM_ITEMS] as Array} - /> - - + ); } @@ -129,43 +131,23 @@ const styles = StyleSheet.create({ backgroundColor: '#f5f5f5', borderBottomWidth: 1, borderBottomColor: '#e0e0e0', + gap: 8, }, controlLabel: { fontSize: 12, fontWeight: '600', color: '#666', - marginBottom: 8, - }, - container: { - flex: 1, - padding: 16, - }, - content: { - gap: 16, - }, - title: { - fontSize: 18, - fontWeight: '700', - textAlign: 'center', - color: '#000', - }, - description: { - fontSize: 13, - color: Colors.LightOffNavy, - textAlign: 'center', - lineHeight: 18, }, - info: { - fontSize: 13, + controlInfo: { + fontSize: 11, color: '#555', - textAlign: 'center', - marginTop: 8, + marginTop: 4, }, highlight: { fontWeight: '600', color: Colors.GreenDark100, }, - centeredScreen: { + screen: { flex: 1, justifyContent: 'center', alignItems: 'center', diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts index 01d111c05b..6ca7e38ba8 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts @@ -1,12 +1,10 @@ import type { ScenarioDescription } from '@apps/tests/shared/helpers'; export const scenarioDescription: ScenarioDescription = { - name: 'Tab Bar Item System Item', + name: 'Tab Bar System Item', key: 'test-tabs-system-item-ios', details: - 'Exercises the systemItem prop of TabsScreen on iOS. Demonstrates all 12 system item types ' + - '(bookmarks, contacts, downloads, favorites, featured, history, more, mostRecent, mostViewed, ' + - 'recents, search, topRated) and shows how systemItem overrides custom title and icon.', + 'Validates different cofigurations of the systemItem prop.', platforms: ['ios'], e2eCoverage: 'incomplete', smokeTest: false, diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md new file mode 100644 index 0000000000..6b0b4a94db --- /dev/null +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md @@ -0,0 +1,121 @@ +# Test Scenario: Tab Bar Item System Item (iOS) + +## Details + +**Description:** Validates the iOS `systemItem` property by verifying that +built-in icons and localized titles render correctly, +checking how they interact with custom title or icon overrides, +and ensuring the tab bar layout handles specific icons like search and adapts +seamlessly during device rotation. + +**OS test creation version:** iOS 18.6 and iOS 26.5 + +## E2E test + +TBD: Planned, but will be implemented separately. + +## Prerequisites + +- iOS device or simulator running iOS 18 or later. + +## Note + +- Test is iOS-only; `systemItem` has no effect on Android. +- On iOS 26 (Liquid Glass), the tab bar layout and icon rendering + differ visually from iOS 18, but the `systemItem` semantics are the + same across versions. +- "System icon" refers to the icon defined by UIKit for each system + item type. Custom `icon`/`selectedIcon` props override the system + icon +- System-defined title text can be override via custom `title`. +- iOS18 KI([Issue#1074](https://github.com/software-mansion/react-native-screens-labs/issues/1074)): SystemItem Icon is not override for compactInline appearance. + +## Steps + +### Baseline — tab without `systemItem` + +1. Launch the app and navigate to the **Tab Bar System Item** + screen. + +- [ ] Four tabs are visible in the tab bar. +- [ ] The first tab (**NormalTab**) is selected by default. Its tab bar item shows the + custom title `NormalTab` and the default system icon (from + `DEFAULT_TAB_ROUTE_OPTIONS`). + +--- + +### `systemItem: 'bookmarks'` — system icon and title, no override + +2. Tap the second tab in the tab bar (the one with the bookmarks + icon). + +- [ ] The second tab becomes selected. +- [ ] Its tab bar item shows the UIKit bookmarks icon and the iOS-localized title for + "bookmarks" (`Bookmarks`). + +3. Tap the **NormalTab** tab, then tap the **bookmarks** tab again. + +- [ ] The bookmarks tab item re-selects correctly. The icon + and localized title are unchanged on re-selection. + +--- + +### `systemItem: 'contacts'` with custom icon override + +4. Tap the third tab in the tab bar (**Custom**). + +- [ ] The third tab becomes selected. +- [ ] Its tab bar item icon is the SF Symbol `house.fill` (selected state). +- [ ] The title displayed in the tab bar is `Custom`. + +5. Tap the **NormalTab** tab (so **Custom** becomes unselected), then + observe the **Custom** tab item in its unselected state. + +- [ ] The unselected icon is the SF Symbol `house` (normal + state icon). The custom title is still shown. + +--- + +### `systemItem: 'search'` — magnifying glass, no title + +6. Tap the fourth tab in the tab bar (iOS18: **Search**). + +- [ ] The fourth tab becomes selected. +- [ ] Its tab bar item shows a magnifying glass icon. +- [ ] iOS18: Title label is visible (`Search`). +- [ ] iOS26: No visible title label, tab bar item is detached from other tab bar items. + +--- + +### Stability — cycle through all tabs + +7. Starting from the **Search** tab, tap each tab in order + (Search → Custom → bookmarks → NormalTab → Search). + +- [ ] Each tab switches correctly on every tap. Icon and + title for each tab item remain accurate after rapid cycling. + +--- + +### Orientation smoke test — portrait to landscape + +8. With the device in portrait orientation and **NormalTab** selected, + rotate the device to landscape orientation. + +- [ ] The layout adapts to landscape. +- [ ] The tab bar icon switch from above title to beside title layout. +- [ ] All four tab items remain visible and their titles are the same as in portrait orientation correct. +- [ ] iOS18: Override icon for `Custom` tab is set to systemItem icon (see KI in Notes section). +- [ ] iOS26: Icons are the same as in portrait orientation. + +9. While in landscape orientation, tap each of the four tabs in + sequence. + +- [ ] Tab switching works correctly in landscape. +- [ ] Each tab item's icon and title match the expected appearance as described in previous step. + +10. Rotate the device back to portrait orientation. + +- [ ] The tab bar reverts to its portrait layout. All tab + item icons and titles are correct. +- [ ] The previously selected tab remains selected. From 632e24b939e6711413db00824db48ac37dde4078 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Mon, 1 Jun 2026 13:38:02 +0200 Subject: [PATCH 04/22] scenario update --- .../test-tabs-system-item-ios/scenario.md | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md index 6b0b4a94db..1c5d47baa2 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md @@ -25,9 +25,9 @@ TBD: Planned, but will be implemented separately. differ visually from iOS 18, but the `systemItem` semantics are the same across versions. - "System icon" refers to the icon defined by UIKit for each system - item type. Custom `icon`/`selectedIcon` props override the system + item type. Custom `icon`/`selectedIcon` props overridden the system icon -- System-defined title text can be override via custom `title`. +- System-defined title text can be overridden via custom `title`. - iOS18 KI([Issue#1074](https://github.com/software-mansion/react-native-screens-labs/issues/1074)): SystemItem Icon is not override for compactInline appearance. ## Steps @@ -53,10 +53,10 @@ TBD: Planned, but will be implemented separately. - [ ] Its tab bar item shows the UIKit bookmarks icon and the iOS-localized title for "bookmarks" (`Bookmarks`). -3. Tap the **NormalTab** tab, then tap the **bookmarks** tab again. +1. Tap the **NormalTab**, then tap the **bookmarks** tab again. -- [ ] The bookmarks tab item re-selects correctly. The icon - and localized title are unchanged on re-selection. +- [ ] The bookmarks tab is successfully re-selected. +The icon and localized title remain unchanged on re-selection. --- @@ -82,8 +82,9 @@ TBD: Planned, but will be implemented separately. - [ ] The fourth tab becomes selected. - [ ] Its tab bar item shows a magnifying glass icon. -- [ ] iOS18: Title label is visible (`Search`). -- [ ] iOS26: No visible title label, tab bar item is detached from other tab bar items. +- [ ] iOS 18: Title label is visible (`Search`). +- [ ] iOS 26: No visible title label is present; the search tab bar item is +detached from other tab bar items. --- @@ -103,16 +104,16 @@ TBD: Planned, but will be implemented separately. rotate the device to landscape orientation. - [ ] The layout adapts to landscape. -- [ ] The tab bar icon switch from above title to beside title layout. -- [ ] All four tab items remain visible and their titles are the same as in portrait orientation correct. -- [ ] iOS18: Override icon for `Custom` tab is set to systemItem icon (see KI in Notes section). +- [ ] The tab bar icon switch from an above-title to beside-title layout. +- [ ] All four tab items remain visible and their titles are the same as in portrait orientation. +- [ ] iOS18: Override icon for the `Custom` tab reverts to the systemItem icon (see KI in Notes section). - [ ] iOS26: Icons are the same as in portrait orientation. -9. While in landscape orientation, tap each of the four tabs in +1. While in landscape orientation, tap each of the four tabs in sequence. - [ ] Tab switching works correctly in landscape. -- [ ] Each tab item's icon and title match the expected appearance as described in previous step. +- [ ] Each tab item's icon and title match the expected appearance described in the previous steps. 10. Rotate the device back to portrait orientation. From 02159764c412ddd78bf0ac79eb73aca08b592dd0 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Mon, 1 Jun 2026 13:42:49 +0200 Subject: [PATCH 05/22] changing e2e flag --- .../tabs/test-tabs-system-item-ios/scenario-description.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts index 6ca7e38ba8..ff876ad9ed 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts @@ -6,6 +6,6 @@ export const scenarioDescription: ScenarioDescription = { details: 'Validates different cofigurations of the systemItem prop.', platforms: ['ios'], - e2eCoverage: 'incomplete', + e2eCoverage: 'tbd', smokeTest: false, }; From 80f7988bbb2e948c432a1221711390f5133f359b Mon Sep 17 00:00:00 2001 From: lkuchno Date: Tue, 2 Jun 2026 09:48:13 +0200 Subject: [PATCH 06/22] appliyng review suggestion, refactor screen, remove tab without systemitem, add new tab with systemitem and empty title prop - no title --- .../tabs/test-tabs-system-item-ios/index.tsx | 56 ++++++------------- .../scenario-description.ts | 2 +- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx index d78c3aa3a0..332c705688 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -9,21 +9,20 @@ import { } from '@apps/shared/gamma/containers/tabs'; import { Colors } from '@apps/shared/styling'; -function NormalTabScreen() { +function NoTitleScreen() { return ( - Normal Tab + System Item Icon only - This tab does not use systemItem.{'\n'} + This tab uses a systemItem: `favorites`.{'\n'} {'\n'} - Only the custom title and (if provided) custom icon appear in the tab bar. - This is the baseline behavior when systemItem is undefined. + The `title` prop is used but no value is provided, so no title is displayed. ); } -function DefaultTitleScreen() { +function DefaultSystemItemScreen() { return ( Default System Item @@ -41,7 +40,7 @@ function CustomTitleIconOverrideScreen() { This tab uses a systemItem: `contacts`.{'\n'} {'\n'} - Custom title and icon are provide and display in tab bar instead of the system-provided one. + Custom title and icon are provided and displayed in tab bar instead of the system-provided one. ); @@ -54,28 +53,32 @@ function SearchScreen() { This tab uses a systemItem: `search`.{'\n'} {'\n'} - The tab bar iteam is displayed as a magnifying glass icon with no title, which is the expected appearance for the `search` system item. + The tab bar item is displayed as a magnifying glass icon.{'\n'} + On iOS 26: this tab bar item is detached and does not display a title. ); } const ROUTE_CONFIGS: TabRouteConfig[] = [ - { - name: 'NormalTab', - Component: NormalTabScreen, + { + name: 'DefaultSystemItem', + Component: DefaultSystemItemScreen, options: { ...DEFAULT_TAB_ROUTE_OPTIONS, - title: 'NormalTab', + ios: { + systemItem: 'bookmarks', + }, }, }, { - name: 'DefaultTitle', - Component: DefaultTitleScreen, + name: 'NoTitle', + Component: NoTitleScreen, options: { + title: '', ...DEFAULT_TAB_ROUTE_OPTIONS, ios: { - systemItem: 'bookmarks', + systemItem: 'favorites', }, }, }, @@ -115,7 +118,6 @@ export function App() { ); @@ -125,28 +127,6 @@ const styles = StyleSheet.create({ appContainer: { flex: 1, }, - controlPanel: { - paddingHorizontal: 16, - paddingVertical: 12, - backgroundColor: '#f5f5f5', - borderBottomWidth: 1, - borderBottomColor: '#e0e0e0', - gap: 8, - }, - controlLabel: { - fontSize: 12, - fontWeight: '600', - color: '#666', - }, - controlInfo: { - fontSize: 11, - color: '#555', - marginTop: 4, - }, - highlight: { - fontWeight: '600', - color: Colors.GreenDark100, - }, screen: { flex: 1, justifyContent: 'center', diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts index ff876ad9ed..cf66ade8db 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts @@ -4,7 +4,7 @@ export const scenarioDescription: ScenarioDescription = { name: 'Tab Bar System Item', key: 'test-tabs-system-item-ios', details: - 'Validates different cofigurations of the systemItem prop.', + 'Validates different configurations of the systemItem prop.', platforms: ['ios'], e2eCoverage: 'tbd', smokeTest: false, From c775c249f2b4416024444e711c09c734c1a1d3ba Mon Sep 17 00:00:00 2001 From: lkuchno Date: Tue, 2 Jun 2026 09:57:44 +0200 Subject: [PATCH 07/22] updateing scenario with remarks and to fit new screen look --- .../test-tabs-system-item-ios/scenario.md | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md index 1c5d47baa2..8867208ab6 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md @@ -25,38 +25,38 @@ TBD: Planned, but will be implemented separately. differ visually from iOS 18, but the `systemItem` semantics are the same across versions. - "System icon" refers to the icon defined by UIKit for each system - item type. Custom `icon`/`selectedIcon` props overridden the system - icon + item type. Custom `icon`/`selectedIcon` props override the system + icon. - System-defined title text can be overridden via custom `title`. -- iOS18 KI([Issue#1074](https://github.com/software-mansion/react-native-screens-labs/issues/1074)): SystemItem Icon is not override for compactInline appearance. +- iOS 18 KI([Issue#1074](https://github.com/software-mansion/react-native-screens-labs/issues/1074)): +SystemItem Icon is not overridden for compactInline appearance. ## Steps -### Baseline — tab without `systemItem` +### `systemItem: 'bookmarks'` — system icon and title, no override 1. Launch the app and navigate to the **Tab Bar System Item** screen. - [ ] Four tabs are visible in the tab bar. -- [ ] The first tab (**NormalTab**) is selected by default. Its tab bar item shows the - custom title `NormalTab` and the default system icon (from - `DEFAULT_TAB_ROUTE_OPTIONS`). +- [ ] The first tab (**Bookmarks**) is selected by default. +- [ ] Its tab bar item shows the iOS-localized title `Bookmarks` and icon. + +2. Tap the second tab, then tap the **Bookmarks** tab again. + +- [ ] The bookmarks tab is successfully re-selected. +- [ ] The icon and localized title remain unchanged on re-selection. --- -### `systemItem: 'bookmarks'` — system icon and title, no override +### `systemItem: 'favorites'` — system icon, empty title -2. Tap the second tab in the tab bar (the one with the bookmarks +3. Tap the second tab in the tab bar (the one with the star icon). - [ ] The second tab becomes selected. -- [ ] Its tab bar item shows the UIKit bookmarks icon and the iOS-localized title for - "bookmarks" (`Bookmarks`). - -1. Tap the **NormalTab**, then tap the **bookmarks** tab again. - -- [ ] The bookmarks tab is successfully re-selected. -The icon and localized title remain unchanged on re-selection. +- [ ] Its tab bar item shows the UIKit favorites icon (star). +- [ ] No title is displayed. --- @@ -104,12 +104,12 @@ detached from other tab bar items. rotate the device to landscape orientation. - [ ] The layout adapts to landscape. -- [ ] The tab bar icon switch from an above-title to beside-title layout. +- [ ] The tab bar icons switch from an above-title to beside-title layout. - [ ] All four tab items remain visible and their titles are the same as in portrait orientation. - [ ] iOS18: Override icon for the `Custom` tab reverts to the systemItem icon (see KI in Notes section). - [ ] iOS26: Icons are the same as in portrait orientation. -1. While in landscape orientation, tap each of the four tabs in +9. While in landscape orientation, tap each of the four tabs in sequence. - [ ] Tab switching works correctly in landscape. From 3e9d21ffa2c5c5b05cd95952a01c37b87eda9fa0 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Tue, 2 Jun 2026 11:07:30 +0200 Subject: [PATCH 08/22] removing NormaTab occurrance from scenario --- .../tabs/test-tabs-system-item-ios/scenario.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md index 8867208ab6..99352abb33 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md @@ -40,19 +40,18 @@ SystemItem Icon is not overridden for compactInline appearance. - [ ] Four tabs are visible in the tab bar. - [ ] The first tab (**Bookmarks**) is selected by default. -- [ ] Its tab bar item shows the iOS-localized title `Bookmarks` and icon. +- [ ] Its tab bar item shows the iOS-localized title `Bookmarks` and icon (open book). 2. Tap the second tab, then tap the **Bookmarks** tab again. - [ ] The bookmarks tab is successfully re-selected. -- [ ] The icon and localized title remain unchanged on re-selection. +- [ ] The localized icon and title remain unchanged on re-selection. --- ### `systemItem: 'favorites'` — system icon, empty title -3. Tap the second tab in the tab bar (the one with the star - icon). +3. Tap the second tab in the tab bar (star icon). - [ ] The second tab becomes selected. - [ ] Its tab bar item shows the UIKit favorites icon (star). @@ -68,7 +67,7 @@ SystemItem Icon is not overridden for compactInline appearance. - [ ] Its tab bar item icon is the SF Symbol `house.fill` (selected state). - [ ] The title displayed in the tab bar is `Custom`. -5. Tap the **NormalTab** tab (so **Custom** becomes unselected), then +5. Tap the second tab (so **Custom** becomes unselected), then observe the **Custom** tab item in its unselected state. - [ ] The unselected icon is the SF Symbol `house` (normal @@ -91,7 +90,7 @@ detached from other tab bar items. ### Stability — cycle through all tabs 7. Starting from the **Search** tab, tap each tab in order - (Search → Custom → bookmarks → NormalTab → Search). + (Search → Custom → Bookmarks → Second tab → Search). - [ ] Each tab switches correctly on every tap. Icon and title for each tab item remain accurate after rapid cycling. @@ -100,7 +99,7 @@ detached from other tab bar items. ### Orientation smoke test — portrait to landscape -8. With the device in portrait orientation and **NormalTab** selected, +8. With the device in portrait orientation and **Bookmarks** selected, rotate the device to landscape orientation. - [ ] The layout adapts to landscape. From 32f8caf1c33f88636df2a4a8950b334e0093d838 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Tue, 2 Jun 2026 11:08:10 +0200 Subject: [PATCH 09/22] changes in text deisplayed on screen for second tab --- .../tabs/test-tabs-system-item-ios/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx index 332c705688..4422388d0d 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -16,7 +16,7 @@ function NoTitleScreen() { This tab uses a systemItem: `favorites`.{'\n'} {'\n'} - The `title` prop is used but no value is provided, so no title is displayed. + The `title` prop is set to an empty string, so no title is displayed. ); @@ -61,7 +61,7 @@ function SearchScreen() { } const ROUTE_CONFIGS: TabRouteConfig[] = [ - { + { name: 'DefaultSystemItem', Component: DefaultSystemItemScreen, options: { From d31829d82b52783193d9d550f4af1aa418be7179 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Mon, 8 Jun 2026 10:59:18 +0200 Subject: [PATCH 10/22] removing unused ...DEFAULT_TAB_ROUTE_OPTION and reordering function definition to match route_cofig order --- .../tabs/test-tabs-system-item-ios/index.tsx | 31 ++++++------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx index 4422388d0d..e88a6daa0c 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -9,25 +9,25 @@ import { } from '@apps/shared/gamma/containers/tabs'; import { Colors } from '@apps/shared/styling'; -function NoTitleScreen() { +function DefaultSystemItemScreen() { return ( - System Item Icon only + Default System Item - This tab uses a systemItem: `favorites`.{'\n'} - {'\n'} - The `title` prop is set to an empty string, so no title is displayed. + This tab uses a systemItem: `bookmarks`{'\n'}with no custom title or icon override. ); } -function DefaultSystemItemScreen() { +function NoTitleScreen() { return ( - Default System Item + System Item Icon only - This tab uses a systemItem: `bookmarks`{'\n'}with no custom title or icon override. + This tab uses a systemItem: `favorites`.{'\n'} + {'\n'} + The `title` prop is set to an empty string, so no title is displayed. ); @@ -65,7 +65,6 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ name: 'DefaultSystemItem', Component: DefaultSystemItemScreen, options: { - ...DEFAULT_TAB_ROUTE_OPTIONS, ios: { systemItem: 'bookmarks', }, @@ -76,7 +75,6 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ Component: NoTitleScreen, options: { title: '', - ...DEFAULT_TAB_ROUTE_OPTIONS, ios: { systemItem: 'favorites', }, @@ -86,7 +84,6 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ name: 'CustomTitleIconOverride', Component: CustomTitleIconOverrideScreen, options: { - ...DEFAULT_TAB_ROUTE_OPTIONS, title: 'Custom', ios: { systemItem: 'contacts', @@ -102,10 +99,9 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ }, }, { - name: 'SearchScreen', + name: 'Search', Component: SearchScreen, options: { - ...DEFAULT_TAB_ROUTE_OPTIONS, ios: { systemItem: 'search', }, @@ -115,18 +111,11 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ export function App() { return ( - - - + ); } const styles = StyleSheet.create({ - appContainer: { - flex: 1, - }, screen: { flex: 1, justifyContent: 'center', From bfabf5a7a5f70923aa176cfb7f8d2f805bd9f581 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Mon, 8 Jun 2026 13:29:21 +0200 Subject: [PATCH 11/22] removing unused import --- .../tabs/test-tabs-system-item-ios/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx index e88a6daa0c..860d9d4357 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -5,7 +5,6 @@ import { createScenario } from '@apps/tests/shared/helpers'; import { TabsContainerWithHostConfigContext, type TabRouteConfig, - DEFAULT_TAB_ROUTE_OPTIONS, } from '@apps/shared/gamma/containers/tabs'; import { Colors } from '@apps/shared/styling'; From d5b5f5fbfc50f8455ef2a1d6c45466b8ac7ad3f0 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Tue, 9 Jun 2026 09:54:17 +0200 Subject: [PATCH 12/22] corrected screen and scenario --- .../tabs/test-tabs-system-item-ios/index.tsx | 244 ++++++++++++----- .../test-tabs-system-item-ios/scenario.md | 253 +++++++++++++----- 2 files changed, 368 insertions(+), 129 deletions(-) diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx index 860d9d4357..fe3401c37c 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -1,68 +1,166 @@ -import React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; +import React, { useState } from 'react'; +import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { scenarioDescription } from './scenario-description'; import { createScenario } from '@apps/tests/shared/helpers'; import { TabsContainerWithHostConfigContext, type TabRouteConfig, + useTabsNavigationContext, } from '@apps/shared/gamma/containers/tabs'; import { Colors } from '@apps/shared/styling'; -function DefaultSystemItemScreen() { +function StaticSystemItemScreen() { return ( - Default System Item + Static System Item This tab uses a systemItem: `bookmarks`{'\n'}with no custom title or icon override. - - - ); -} - -function NoTitleScreen() { - return ( - - System Item Icon only - - This tab uses a systemItem: `favorites`.{'\n'} {'\n'} - The `title` prop is set to an empty string, so no title is displayed. + {'\n'} + The system-provided icon (open book) and localized title (`Bookmarks`) are displayed. ); } -function CustomTitleIconOverrideScreen() { +type SystemItemOption = 'favorites' | 'history' | 'search'; +type TitleOption = 'system' | 'custom' | 'hidden'; +type IconOption = 'system' | 'house' | 'heart'; + +type RuntimeConfig = { + systemItem: SystemItemOption; + title: TitleOption; + icon: IconOption; +}; + +const INITIAL_CONFIG: RuntimeConfig = { + systemItem: 'favorites', + title: 'system', + icon: 'system', +}; + +const SYSTEM_ITEM_OPTIONS: SystemItemOption[] = ['favorites', 'history', 'search']; +const TITLE_OPTIONS: TitleOption[] = ['system', 'custom', 'hidden']; +const ICON_OPTIONS: IconOption[] = ['system', 'house', 'heart']; + +function OptionRow({ + options, + value, + onSelect, +}: { + options: T[]; + value: T; + onSelect: (option: T) => void; +}) { return ( - - Custom Title and Icon Override - - This tab uses a systemItem: `contacts`.{'\n'} - {'\n'} - Custom title and icon are provided and displayed in tab bar instead of the system-provided one. - + + {options.map(option => { + const isActive = value === option; + return ( + onSelect(option)}> + + {option} + + + ); + })} ); } -function SearchScreen() { +function RuntimeConfigScreen() { + const { routeKey, setRouteOptions } = useTabsNavigationContext(); + const [config, setConfig] = useState(INITIAL_CONFIG); + + const apply = (next: RuntimeConfig) => { + setConfig(next); + setRouteOptions(routeKey, { + title: + next.title === 'custom' ? 'Custom' : next.title === 'hidden' ? '' : undefined, + ios: { + systemItem: next.systemItem, + icon: + next.icon === 'house' + ? { type: 'sfSymbol', name: 'house' } + : next.icon === 'heart' + ? { type: 'sfSymbol', name: 'heart' } + : undefined, + selectedIcon: + next.icon === 'house' + ? { type: 'sfSymbol', name: 'house.fill' } + : next.icon === 'heart' + ? { type: 'sfSymbol', name: 'heart.fill' } + : undefined, + }, + }); + }; + + const setSystemItem = (systemItem: SystemItemOption) => + apply({ ...config, systemItem }); + const setTitle = (title: TitleOption) => apply({ ...config, title }); + const setIcon = (icon: IconOption) => apply({ ...config, icon }); + + const titleDisplay = + config.title === 'custom' + ? '"Custom"' + : config.title === 'hidden' + ? "'' (hidden)" + : 'undefined (system)'; + const iconDisplay = + config.icon === 'system' ? 'system (from systemItem)' : `custom \`${config.icon}\``; + return ( - - Search - - This tab uses a systemItem: `search`.{'\n'} - {'\n'} - The tab bar item is displayed as a magnifying glass icon.{'\n'} - On iOS 26: this tab bar item is detached and does not display a title. - - + + + Runtime Config + + Configure systemItem, title and icon at runtime{'\n'}in different combinations. + {'\n'} + {'\n'} + systemItem: `{config.systemItem}`{'\n'} + title: {titleDisplay} + {'\n'} + icon: {iconDisplay} + + + systemItem + + + title + + + icon + + + + Mix systemItem, title and icon freely. A custom icon{'\n'}overrides the systemItem + icon; `system` icon falls back{'\n'}to the systemItem default (no stale image). + + + ); } const ROUTE_CONFIGS: TabRouteConfig[] = [ { - name: 'DefaultSystemItem', - Component: DefaultSystemItemScreen, + name: 'StaticSystemItem', + Component: StaticSystemItemScreen, options: { ios: { systemItem: 'bookmarks', @@ -70,39 +168,11 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ }, }, { - name: 'NoTitle', - Component: NoTitleScreen, - options: { - title: '', - ios: { - systemItem: 'favorites', - }, - }, - }, - { - name: 'CustomTitleIconOverride', - Component: CustomTitleIconOverrideScreen, - options: { - title: 'Custom', - ios: { - systemItem: 'contacts', - icon: { - type: 'sfSymbol', - name: 'house', - }, - selectedIcon: { - type: 'sfSymbol', - name: 'house.fill', - }, - }, - }, - }, - { - name: 'Search', - Component: SearchScreen, + name: 'RuntimeConfig', + Component: RuntimeConfigScreen, options: { ios: { - systemItem: 'search', + systemItem: INITIAL_CONFIG.systemItem, }, }, }, @@ -110,7 +180,7 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ export function App() { return ( - + ); } @@ -133,6 +203,44 @@ const styles = StyleSheet.create({ textAlign: 'center', lineHeight: 20, }, + groupLabel: { + fontSize: 13, + fontWeight: '600', + textAlign: 'center', + marginTop: 8, + }, + buttonRow: { + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'center', + gap: 8, + }, + optionButton: { + paddingVertical: 6, + paddingHorizontal: 12, + borderRadius: 16, + borderWidth: 1, + borderColor: Colors.primary, + }, + optionButtonActive: { + backgroundColor: Colors.primary, + }, + optionText: { + fontSize: 12, + color: Colors.primary, + fontWeight: '600', + }, + optionTextActive: { + color: Colors.White, + }, + instructions: { + fontSize: 12, + color: Colors.LightOffNavy, + textAlign: 'center', + lineHeight: 18, + marginTop: 12, + fontStyle: 'italic', + }, }); export default createScenario(App, scenarioDescription); diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md index 99352abb33..7fbef5ac9b 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md @@ -2,11 +2,17 @@ ## Details -**Description:** Validates the iOS `systemItem` property by verifying that -built-in icons and localized titles render correctly, -checking how they interact with custom title or icon overrides, -and ensuring the tab bar layout handles specific icons like search and adapts -seamlessly during device rotation. +**Description:** Validates the iOS `systemItem` property across two +tabs. Verifies that a static `systemItem: 'bookmarks'` tab renders +the correct UIKit-provided icon and localized title with no override. +Exercises the Runtime Config tab, which combines three independent +toggle groups (systemItem, title, icon) applied atomically via +`setRouteOptions`, and verifies that all combinations produce the +correct tab bar item appearance — including that switching the icon +back to `system` immediately removes any stale custom SF Symbol image. +Verifies that the `search` item renders as a magnifying glass with +iOS-version-specific layout differences. Includes an orientation +smoke test. **OS test creation version:** iOS 18.6 and iOS 26.5 @@ -17,105 +23,230 @@ TBD: Planned, but will be implemented separately. ## Prerequisites - iOS device or simulator running iOS 18 or later. +- The iPhone in portrait orientation is the primary verification + surface. +- Compact inline orientation refers to the landscape device orientation for standard +iPhone Pro models (for iOS 18 excluding Max). ## Note - Test is iOS-only; `systemItem` has no effect on Android. - On iOS 26 (Liquid Glass), the tab bar layout and icon rendering - differ visually from iOS 18, but the `systemItem` semantics are the + differ visually from iOS 18, but `systemItem` semantics are the same across versions. -- "System icon" refers to the icon defined by UIKit for each system - item type. Custom `icon`/`selectedIcon` props override the system - icon. -- System-defined title text can be overridden via custom `title`. -- iOS 18 KI([Issue#1074](https://github.com/software-mansion/react-native-screens-labs/issues/1074)): -SystemItem Icon is not overridden for compactInline appearance. +- "System icon" means the icon UIKit provides for a given system item + type. A custom `icon` / `selectedIcon` prop overrides that icon; + removing those props (passing `undefined`) must restore the system + icon immediately with no stale image remaining. +- `title: undefined` falls back to the UIKit-localized system title + for the item type. `title: ''` (empty string) hides the label. +- Active toggle buttons in the Runtime Config tab are highlighted + dark blue. +- iOS 26: The `systemItem 'search'` tab bar item is detached from the other items + and has no label. +- iOS 18 KI: SystemItem icon is not overridden for the compactInline + (landscape orientation on iPhone Pro) tab bar appearance. ## Steps -### `systemItem: 'bookmarks'` — system icon and title, no override +### Static System Item tab -1. Launch the app and navigate to the **Tab Bar System Item** - screen. +1. Launch the app and navigate to the **Tab Bar System Item** screen. -- [ ] Four tabs are visible in the tab bar. -- [ ] The first tab (**Bookmarks**) is selected by default. -- [ ] Its tab bar item shows the iOS-localized title `Bookmarks` and icon (open book). +- [ ] Two tabs are visible in the tab bar. +- [ ] The first tab is selected by default. +- [ ] Its tab bar item shows the UIKit open-book icon and the + iOS-localized title `Bookmarks`. +- [ ] No custom title or icon override is present. -2. Tap the second tab, then tap the **Bookmarks** tab again. +2. Tap the second tab, then tap the first tab (**Bookmarks**) + again. -- [ ] The bookmarks tab is successfully re-selected. -- [ ] The localized icon and title remain unchanged on re-selection. +- [ ] The Bookmarks tab is re-selected. +- [ ] The localized open-book icon and `Bookmarks` title are + unchanged after re-selection. --- -### `systemItem: 'favorites'` — system icon, empty title +### Runtime Config tab — initial state -3. Tap the second tab in the tab bar (star icon). +3. Tap the second tab in the tab bar (**Favorites**). -- [ ] The second tab becomes selected. -- [ ] Its tab bar item shows the UIKit favorites icon (star). -- [ ] No title is displayed. +- [ ] The Favorites tab becomes selected. +- [ ] The Runtime Config title is displayed on the screen. +- [ ] The on-screen status reads: `systemItem: 'favorites'`, + `title: undefined (system)`, `icon: system (from systemItem)`. +- [ ] The tab bar item shows the UIKit favorites (star) icon with + the iOS-localized title `Favorites`. +- [ ] The **favorites** and **system** (systemItem, title, icon) + buttons are highlighted dark blue. --- -### `systemItem: 'contacts'` with custom icon override +### Runtime Config tab — systemItem cycling -4. Tap the third tab in the tab bar (**Custom**). +4. Tap **history** in the systemItem group. -- [ ] The third tab becomes selected. -- [ ] Its tab bar item icon is the SF Symbol `house.fill` (selected state). -- [ ] The title displayed in the tab bar is `Custom`. +- [ ] The tab bar item changes to the UIKit history (clock) icon and + the `History` title. +- [ ] The on-screen status updates to `systemItem: 'history'`. -5. Tap the second tab (so **Custom** becomes unselected), then - observe the **Custom** tab item in its unselected state. +5. Tap **search** in the systemItem group. -- [ ] The unselected icon is the SF Symbol `house` (normal - state icon). The custom title is still shown. +- [ ] The tab bar item changes to the UIKit magnifying glass icon. +- [ ] iOS 18: The title label `Search` is visible beneath the icon. +- [ ] iOS 26: No visible title label; the Search tab bar item is + detached from the other items. +- [ ] The on-screen status updates to `systemItem: 'search'`. + +6. Tap **favorites** to restore the initial systemItem. + +- [ ] The tab bar item reverts to the UIKit favorites icon and + `Favorites` title. +- iOS 26: The tab bar item correctly realigns and is no longer detached. + +--- + +### Runtime Config tab — title override cycling + +7. Tap **Custom** in the title group. + +- [ ] The tab bar item label changes to `Custom` immediately. +- [ ] The on-screen status updates to `title: "Custom"`. +- [ ] The **Custom** button is highlighted dark blue. +- [ ] The favorites icon remains visible and is unchanged. + +8. Tap **hidden** in the title group. + +- [ ] The tab bar item label disappears entirely (empty string). +- [ ] The on-screen status updates to `title: '' (hidden)`. +- [ ] The **hidden** button is highlighted dark blue. +- [ ] The favorites icon is still visible in the tab bar item. + +9. Tap **system** in the title group. + +- [ ] The tab bar item label returns to the UIKit-localized title + `Favorites` immediately. +- [ ] The on-screen status updates to `title: undefined (system)`. +- [ ] The **system** button is highlighted dark blue. --- -### `systemItem: 'search'` — magnifying glass, no title +### Runtime Config tab — icon override cycling + +10. Tap **house** in the icon group. + +- [ ] The tab bar item icon changes to the `house.fill` SF Symbol. +- [ ] The system favorites icon is no longer visible. +- [ ] The on-screen status updates to `icon: custom 'house'`. +- [ ] The **house** button is highlighted dark blue. + +11. Tap the first tab (**Bookmarks**) and observe icons, then + tap **Favorites** again. -6. Tap the fourth tab in the tab bar (iOS18: **Search**). +- [ ] While Bookmarks is selected, the Favorites tab bar item + shows the unselected `house` SF Symbol. +- [ ] On re-selection, the Favorites tab bar item shows + `house.fill`. -- [ ] The fourth tab becomes selected. -- [ ] Its tab bar item shows a magnifying glass icon. -- [ ] iOS 18: Title label is visible (`Search`). -- [ ] iOS 26: No visible title label is present; the search tab bar item is -detached from other tab bar items. +12. Tap **heart** in the icon group. + +- [ ] The tab bar item icon changes to `heart.fill` immediately. +- [ ] No `house` or `house.fill` image lingers. +- [ ] The on-screen status updates to `icon: custom 'heart'`. +- [ ] The **heart** button is highlighted dark blue. + +13. Tap **system** in the icon group. + +- [ ] The custom icon is removed. The tab bar item falls back to + the UIKit system icon for the current systemItem (`favorites` + star) immediately. +- [ ] No stale `heart` or `heart.fill` image remains in the tab bar + item. +- [ ] The on-screen status updates to + `icon: system (from systemItem)`. +- [ ] The **system** button is highlighted dark blue. --- -### Stability — cycle through all tabs +### Runtime Config tab — combined overrides + +14. Set systemItem to **search**, title to **Custom**, icon to + **heart** (all three groups in a non-default state). + +- [ ] The tab bar item shows the `heart` SF Symbol icon (custom + icon overrides the system search icon) +- [ ] iOS 18: The label reads `Custom`. +- [ ] iOS 26: No visible title label; the tab bar item is + detached from the Bookmarks item. +- [ ] The on-screen status reads: `systemItem: 'search'`, + `title: "Custom"`, `icon: custom 'heart'`. + +15. Tap the first tab (**Bookmarks**). + +- [ ] While Bookmarks is selected, the second tab bar item shows the unselected `heart` SF Symbol. +- [ ] iOS 26: The second tab bar item remains detached. + +16. Tap second tab again. Change systemItem to **history** while keeping title as + **Custom** and icon as **heart**. -7. Starting from the **Search** tab, tap each tab in order - (Search → Custom → Bookmarks → Second tab → Search). +- [ ] The icon remains `heart.fill`. +- [ ] The label reads `Custom`. +- [ ] The on-screen status reads: `systemItem: 'history'`, + `title: "Custom"`, `icon: custom 'heart'`. -- [ ] Each tab switches correctly on every tap. Icon and - title for each tab item remain accurate after rapid cycling. +17. Change icon to **system** while keeping systemItem as + **history** and title as **Custom**. + +- [ ] The icon falls back to the UIKit system history (clock) icon + immediately. +- [ ] No stale `heart` image remains. +- [ ] The label remains `Custom`. + +18. Change title to **hidden ('')** while keeping systemItem as + **history** and icon as **system**. + +- [ ] The tab bar label disappears. +- [ ] The UIKit history icon remains visible. + +19. Change title to **system** while keeping systemItem as + **history** and icon as **system**. + +- [ ] The tab bar label reads `History`. +- [ ] The UIKit history icon remains visible. --- -### Orientation smoke test — portrait to landscape +### Orientation smoke test -8. With the device in portrait orientation and **Bookmarks** selected, - rotate the device to landscape orientation. +20. Select **Bookmarks** tab and rotate device to landscape orientation. - [ ] The layout adapts to landscape. -- [ ] The tab bar icons switch from an above-title to beside-title layout. -- [ ] All four tab items remain visible and their titles are the same as in portrait orientation. -- [ ] iOS18: Override icon for the `Custom` tab reverts to the systemItem icon (see KI in Notes section). -- [ ] iOS26: Icons are the same as in portrait orientation. +- [ ] The tab bar switches to a compact inline layout + (icons and titles side by side). +- [ ] All two tab items remain visible with system icons and titles. + +21. While in landscape orientation, tap the second tab, + then tap **house** in the icon group, and **custom** in title group. + +- [ ] The tab bar label reads `Custom`. +- [ ] iOS 18 KI: Custom SF Symbol overrides may revert to the system icon in compactInline appearance. +- [ ] iOS 26: The icon changes to `house.fill`. -9. While in landscape orientation, tap each of the four tabs in - sequence. +22. Change systemItem to **search** and icon to **heart**. Keep title set to **custom**. -- [ ] Tab switching works correctly in landscape. -- [ ] Each tab item's icon and title match the expected appearance described in the previous steps. +- [ ] The tab bar label remains `Custom`. +- [ ] iOS 18 KI: Custom SF Symbol overrides may revert to the system icon in compactInline appearance. +- [ ] iOS 26: The icon changes to `heart.fill`. No visible title label; the Search tab bar item is + detached from the other items. -10. Rotate the device back to portrait orientation. +23. Rotate the device back to portrait orientation. -- [ ] The tab bar reverts to its portrait layout. All tab - item icons and titles are correct. +- [ ] The tab bar reverts to its portrait layout. +- [ ] The second tab bar item shows the `heart.fill` icon + (selected). +- [ ] The Bookmarks item is unchanged. - [ ] The previously selected tab remains selected. +- [ ] iOS 18: The title label `Custom` is visible beneath the icon. +- [ ] iOS 26: No visible title label; the Search tab bar item is + detached from the other items. From 6aa3545d270f5e5691c05d951b54052532764239 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Tue, 9 Jun 2026 10:23:11 +0200 Subject: [PATCH 13/22] style for scrollView and View change --- .../tabs/test-tabs-system-item-ios/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx index fe3401c37c..eb3f9bfe4a 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -114,8 +114,8 @@ function RuntimeConfigScreen() { config.icon === 'system' ? 'system (from systemItem)' : `custom \`${config.icon}\``; return ( - - + + Runtime Config Configure systemItem, title and icon at runtime{'\n'}in different combinations. From 2029c719cb9a88e952de4ef33fad628c45a3e5e5 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Tue, 9 Jun 2026 13:57:41 +0200 Subject: [PATCH 14/22] index and scenario files small adjustment after AI review --- .../tabs/test-tabs-system-item-ios/index.tsx | 6 ++++-- .../tabs/test-tabs-system-item-ios/scenario.md | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx index eb3f9bfe4a..d4f7e81fcc 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -111,7 +111,9 @@ function RuntimeConfigScreen() { ? "'' (hidden)" : 'undefined (system)'; const iconDisplay = - config.icon === 'system' ? 'system (from systemItem)' : `custom \`${config.icon}\``; + config.icon === 'system' + ? 'system (from systemItem)' + : `custom '${config.icon}'`; return ( @@ -121,7 +123,7 @@ function RuntimeConfigScreen() { Configure systemItem, title and icon at runtime{'\n'}in different combinations. {'\n'} {'\n'} - systemItem: `{config.systemItem}`{'\n'} + systemItem: {`'${config.systemItem}'`}{'\n'} title: {titleDisplay} {'\n'} icon: {iconDisplay} diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md index 7fbef5ac9b..c449bea8c4 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md @@ -103,7 +103,7 @@ iPhone Pro models (for iOS 18 excluding Max). - [ ] The tab bar item reverts to the UIKit favorites icon and `Favorites` title. -- iOS 26: The tab bar item correctly realigns and is no longer detached. +- [ ] iOS 26: The tab bar item correctly realigns and is no longer detached. --- @@ -113,7 +113,7 @@ iPhone Pro models (for iOS 18 excluding Max). - [ ] The tab bar item label changes to `Custom` immediately. - [ ] The on-screen status updates to `title: "Custom"`. -- [ ] The **Custom** button is highlighted dark blue. +- [ ] The **custom** button is highlighted dark blue. - [ ] The favorites icon remains visible and is unchanged. 8. Tap **hidden** in the title group. @@ -171,7 +171,7 @@ iPhone Pro models (for iOS 18 excluding Max). ### Runtime Config tab — combined overrides -14. Set systemItem to **search**, title to **Custom**, icon to +14. Set systemItem to **search**, title to **custom**, icon to **heart** (all three groups in a non-default state). - [ ] The tab bar item shows the `heart` SF Symbol icon (custom @@ -188,7 +188,7 @@ iPhone Pro models (for iOS 18 excluding Max). - [ ] iOS 26: The second tab bar item remains detached. 16. Tap second tab again. Change systemItem to **history** while keeping title as - **Custom** and icon as **heart**. + **custom** and icon as **heart**. - [ ] The icon remains `heart.fill`. - [ ] The label reads `Custom`. @@ -196,7 +196,7 @@ iPhone Pro models (for iOS 18 excluding Max). `title: "Custom"`, `icon: custom 'heart'`. 17. Change icon to **system** while keeping systemItem as - **history** and title as **Custom**. + **history** and title as **custom**. - [ ] The icon falls back to the UIKit system history (clock) icon immediately. From 8a45be0c5cf04aa07dc557ef7ebfdcc91bc081eb Mon Sep 17 00:00:00 2001 From: lkuchno Date: Wed, 10 Jun 2026 14:32:52 +0200 Subject: [PATCH 15/22] chore(test): new e2e test for test-tabs-system-items-ios scenario --- .../tabs/test-tabs-system-item-ios.e2e.ts | 409 ++++++++++++++++++ .../tabs/test-tabs-system-item-ios/index.tsx | 27 +- .../test-tabs-system-item-ios/scenario.md | 9 +- 3 files changed, 436 insertions(+), 9 deletions(-) create mode 100644 FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts diff --git a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts new file mode 100644 index 0000000000..bcdb66436b --- /dev/null +++ b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts @@ -0,0 +1,409 @@ +import { device, expect, element, by } from 'detox'; +import { + describeIfiOS, + forceTapByLabeliOS, + selectSingleFeatureTestsScreen, +} from '../../e2e-utils'; + +async function tapOptionButton(optionText: string) { + await element(by.text(optionText)).tap(); +} + +async function tapSystemTitleOption() { + await element(by.text('system')).atIndex(0).tap(); +} + +async function tapSystemIconOption() { + await element(by.text('system')).atIndex(1).tap(); +} + +describeIfiOS('Tab Bar System Item', () => { + beforeAll(async () => { + await device.reloadReactNative(); + await selectSingleFeatureTestsScreen('Tabs', 'test-tabs-system-item-ios'); + }); + + describe('Static System Item tab', () => { + it('should display the tab bar with system item titles and icons', async () => { + await expect(element(by.type('UITabBar'))).toBeVisible(); + await expect(element(by.text('Static System Item'))).toBeVisible(); + await expect(element(by.id('bookmark-tab-item'))).toHaveLabel( + 'Bookmarks', + ); + await expect(element(by.id('custom-tab-item'))).toHaveLabel('Favorites'); + await expect( + element(by.id('book.fill').and(by.label('bookmark'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); + + it('tab bar item icon and title should remain the same when switching between tabs', async () => { + await element(by.id('custom-tab-item')).tap(); + await expect(element(by.text('Runtime Config'))).toBeVisible(); + await expect( + element(by.id('book.fill').and(by.label('bookmark'))).atIndex(0), + ).toExist(); + await expect(element(by.id('bookmark-tab-item'))).toHaveLabel( + 'Bookmarks', + ); + + await element(by.id('bookmark-tab-item')).tap(); + await expect(element(by.text('Static System Item'))).toBeVisible(); + await expect( + element(by.id('book.fill').and(by.label('bookmark'))).atIndex(0), + ).toExist(); + await expect(element(by.id('bookmark-tab-item'))).toHaveLabel( + 'Bookmarks', + ); + }); + }); +}); + +describe('Runtime Config tab — initial state', () => { + beforeAll(async () => { + await element(by.id('custom-tab-item')).tap(); + }); + + it('should display the Runtime Config screen content', async () => { + await expect(element(by.text('Runtime Config'))).toBeVisible(); + await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); + await expect(element(by.text('title: undefined (system)'))).toBeVisible(); + await expect( + element(by.text('icon: system (from systemItem)')), + ).toBeVisible(); + await expect(element(by.id('system-item-selector'))).toBeVisible(); + await expect(element(by.text('favorites'))).toBeVisible(); + await expect(element(by.text('history'))).toBeVisible(); + await expect(element(by.text('search'))).toBeVisible(); + + await expect(element(by.id('title-selector'))).toBeVisible(); + await expect(element(by.text('custom'))).toBeVisible(); + await expect(element(by.text('hidden'))).toBeVisible(); + + await expect(element(by.id('icon-selector'))).toBeVisible(); + await expect(element(by.text('house'))).toBeVisible(); + await expect(element(by.text('heart'))).toBeVisible(); + }); +}); + +describe('Runtime Config tab — systemItem cycling', () => { + it('should update the tab bar item when switching to history systemItem', async () => { + await expect(element(by.text('Runtime Config'))).toBeVisible(); + await tapOptionButton('history'); + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toExist(); + + await expect(element(by.label('History'))).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).not.toExist(); + }); + + it('should update the tab bar item when switching to search systemItem', async () => { + await tapOptionButton('search'); + await expect( + element(by.id('config-systemitem').and(by.label("systemItem: 'search'"))), + ).toBeVisible(); + + await expect(element(by.label('Search'))).toExist(); + await expect( + element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).not.toExist(); + }); + + it('should update the tab bar item when switching to favorites systemItem', async () => { + await tapOptionButton('favorites'); + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'favorites'")), + ), + ).toBeVisible(); + + await expect(element(by.label('Favorites'))).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), + ).not.toExist(); + }); +}); + +describe('Runtime Config tab — title override cycling', () => { + it('should update the tab bar item label after tapping custom title', async () => { + await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); + await expect(element(by.text('title: undefined (system)'))).toBeVisible(); + await expect( + element(by.text('icon: system (from systemItem)')), + ).toBeVisible(); + + await tapOptionButton('custom'); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); + + it('should hide the tab bar item label after tapping hidden title', async () => { + await tapOptionButton('hidden'); + await expect( + element(by.id('config-title').and(by.label("title: '' (hidden)"))), + ).toBeVisible(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).not.toExist(); + await expect( + element(by.label('favorite').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); + + it('should update the tab bar item label after restoring system title', async () => { + await tapSystemTitleOption(); + await expect( + element(by.id('config-title').and(by.label('title: undefined (system)'))), + ).toBeVisible(); + await expect( + element(by.label('Favorites').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); +}); + +describe('Runtime Config tab — icon override cycling', () => { + it('should update tab bar item icon after tapping house icon', async () => { + await tapOptionButton('house'); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'house'"))), + ).toBeVisible(); + await expect( + element(by.id('house').and(by.label('home'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).not.toExist(); + }); + + it('should navigate to Bookmarks and back while house icon override is active', async () => { + await element(by.label('Bookmarks')).atIndex(0).tap(); + await expect(element(by.text('Static System Item'))).toBeVisible(); + await expect( + element(by.id('house').and(by.label('home'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('book.fill').and(by.label('bookmark'))).atIndex(0), + ).toExist(); + await expect(element(by.id('bookmark-tab-item'))).toHaveLabel('Bookmarks'); + + await element(by.label('Favorites')).atIndex(0).tap(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'house'"))), + ).toBeVisible(); + await expect( + element(by.id('house').and(by.label('home'))).atIndex(0), + ).toExist(); + }); + + it('should update tab bar item icon after tapping heart icon', async () => { + await tapOptionButton('heart'); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).toExist(); + await expect(element(by.id('house').and(by.label('home')))).not.toExist(); + }); + + it('should update tab bar item icon after restoring system icon', async () => { + await tapSystemIconOption(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).not.toExist(); + }); +}); + +describe('Runtime Config tab — combined overrides', () => { + it('should update tab bar item with combined selection of search systemItem + custom title + heart icon', async () => { + await tapOptionButton('search'); + await tapOptionButton('custom'); + await tapOptionButton('heart'); + + await expect( + element(by.id('config-systemitem').and(by.label("systemItem: 'search'"))), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + + await expect(element(by.label('Search'))).not.toExist(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), + ).not.toExist(); + }); + + it('should navigate to the Bookmarks tab and back with combined overrides active', async () => { + await element(by.label('Bookmarks')).atIndex(0).tap(); + await expect(element(by.text('Static System Item'))).toBeVisible(); + + await element(by.id('heart').and(by.label('love'))) + .atIndex(0) + .tap(); + + await expect( + element(by.id('config-systemitem').and(by.label("systemItem: 'search'"))), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + }); + + it('should retain custom title and heart icon after switching to history systemItem', async () => { + await tapOptionButton('history'); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + await expect(element(by.label('History'))).not.toExist(); + + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).not.toExist(); + }); + + it('should fall back to system history icon when icon is set to system', async () => { + await tapSystemIconOption(); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect(element(by.label('History'))).not.toExist(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).not.toExist(); + }); + + it('should hide the tab bar label when title is set to hidden', async () => { + await tapOptionButton('hidden'); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label("title: '' (hidden)"))), + ).toBeVisible(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect(element(by.label('History'))).not.toExist(); + + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).not.toExist(); + }); + + it('should restore the system localized title when title is set to system', async () => { + await tapSystemTitleOption(); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect(element(by.text('title: undefined (system)'))).toBeVisible(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect(element(by.label('History'))).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + }); +}); diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx index d4f7e81fcc..c43ac1405d 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -121,29 +121,38 @@ function RuntimeConfigScreen() { Runtime Config Configure systemItem, title and icon at runtime{'\n'}in different combinations. - {'\n'} - {'\n'} - systemItem: {`'${config.systemItem}'`}{'\n'} + + + systemItem: {`'${config.systemItem}'`} + + title: {titleDisplay} - {'\n'} + + icon: {iconDisplay} - systemItem + + systemItem + - title + + title + - icon + + icon + - + ); } @@ -164,6 +173,7 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ name: 'StaticSystemItem', Component: StaticSystemItemScreen, options: { + tabBarItemTestID: 'bookmark-tab-item', ios: { systemItem: 'bookmarks', }, @@ -173,6 +183,7 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ name: 'RuntimeConfig', Component: RuntimeConfigScreen, options: { + tabBarItemTestID: 'custom-tab-item', ios: { systemItem: INITIAL_CONFIG.systemItem, }, diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md index c449bea8c4..6224f6aee6 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md @@ -18,7 +18,14 @@ smoke test. ## E2E test -TBD: Planned, but will be implemented separately. +Incomplete: Automation covers steps 1-19 but not in the full scope - see list below. + +Not automated: + +- Validating the differences between icon and selectedIcon. +- Checking systemItem: 'search' tab bar item detached state and label hiding for iOS 26. +- The "hidden title" option is validated, but indirectly; it should be verified manually. +- Steps 20-23 (orientation changes). ## Prerequisites From d298452f3babe148028855498930a6f0528b0803 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Wed, 10 Jun 2026 15:51:06 +0200 Subject: [PATCH 16/22] changinf e2eCovarage status in scenario-description + applying ai review comments --- .../tabs/test-tabs-ime-insets-android.e2e.ts | 189 +++++ .../tabs/test-tabs-system-item-ios.e2e.ts | 662 +++++++++--------- .../test-tabs-ime-insets-android/index.tsx | 7 +- .../scenario-description.ts | 2 +- .../tabs/test-tabs-system-item-ios/index.tsx | 2 +- .../scenario-description.ts | 2 +- 6 files changed, 532 insertions(+), 332 deletions(-) create mode 100644 FabricExample/e2e/single-feature-tests/tabs/test-tabs-ime-insets-android.e2e.ts diff --git a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-ime-insets-android.e2e.ts b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-ime-insets-android.e2e.ts new file mode 100644 index 0000000000..62c2bd522a --- /dev/null +++ b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-ime-insets-android.e2e.ts @@ -0,0 +1,189 @@ +import { expect as jestExpect } from '@jest/globals'; +import { device, expect, element, by } from 'detox'; +import { AndroidElementAttributes } from 'detox/detox'; +import { + describeIfAndroid, + selectSingleFeatureTestsScreen, +} from '../../e2e-utils'; + +/** + * Retrieves the frame attributes for the tab bar Config item. + * Used to measure the tab bar's Y position before and after the keyboard appears, + * so we can assert whether the tab bar shifted above the keyboard or stayed at the bottom. + */ +async function getConfigTabItemFrame(): Promise< + AndroidElementAttributes['frame'] +> { + const attrs = (await element( + by.id('ime-insets-config-tab-item'), + ).getAttributes()) as AndroidElementAttributes; + return attrs.frame; +} + +/** + * Taps the TextInput to bring up the soft keyboard (IME). + * Detox synchronization waits for layout changes to settle before + * the call returns, so subsequent frame reads reflect the post-keyboard layout. + */ +async function openKeyboard() { + await element(by.id('ime-insets-text-input')).tap(); +} + +/** + * Dismisses the soft keyboard by pressing the Android back button. + */ +async function dismissKeyboard() { + await device.pressBack(); +} + +describeIfAndroid('Tabs IME insets (Android)', () => { + beforeAll(async () => { + await device.reloadReactNative(); + await selectSingleFeatureTestsScreen( + 'Tabs', + 'test-tabs-ime-insets-android', + ); + }); + + // --------------------------------------------------------------------------- + // Baseline + // --------------------------------------------------------------------------- + + it('should display the Config tab with correct default switch states', async () => { + await expect(element(by.id('safe-area-bottom-edge-switch'))).toBeVisible(); + await expect( + element(by.id('tab-bar-respects-ime-insets-switch')), + ).toBeVisible(); + await expect(element(by.id('ime-insets-text-input'))).toBeVisible(); + + // Default: safeAreaViewBottomEdgeEnabled=true, tabBarRespectsIMEInsets=false + await expect(element(by.id('safe-area-bottom-edge-switch'))).toHaveLabel( + 'safeAreaViewBottomEdgeEnabled: true', + ); + await expect( + element(by.id('tab-bar-respects-ime-insets-switch')), + ).toHaveLabel('tabBarRespectsIMEInsets: false'); + }); + + it('should show "TabsScreen bottom" text at the bottom of the layout', async () => { + await expect(element(by.id('tabs-screen-bottom-text'))).toBeVisible(); + await expect(element(by.id('tabs-screen-bottom-text'))).toHaveText( + 'TabsScreen bottom', + ); + }); + + // --------------------------------------------------------------------------- + // tabBarRespectsIMEInsets: false, safeAreaViewBottomEdgeEnabled: true + // --------------------------------------------------------------------------- + + it('should keep the tab bar at the bottom when keyboard opens and tabBarRespectsIMEInsets is false', async () => { + // Baseline tab bar position before keyboard + const frameBeforeKeyboard = await getConfigTabItemFrame(); + + await openKeyboard(); + + // With tabBarRespectsIMEInsets=false, the tab bar stays anchored at the + // bottom of the window. The tab bar item Y position should remain unchanged + // (the native view does not shift up to accommodate the keyboard). + const frameWithKeyboard = await getConfigTabItemFrame(); + + jestExpect( + Math.abs(frameWithKeyboard.y - frameBeforeKeyboard.y), + ).toBeLessThan(5); + + await dismissKeyboard(); + }); + + // --------------------------------------------------------------------------- + // tabBarRespectsIMEInsets: true, safeAreaViewBottomEdgeEnabled: true + // --------------------------------------------------------------------------- + + it('should toggle tabBarRespectsIMEInsets to true', async () => { + await element(by.id('tab-bar-respects-ime-insets-switch')).tap(); + await expect( + element(by.id('tab-bar-respects-ime-insets-switch')), + ).toHaveLabel('tabBarRespectsIMEInsets: true'); + }); + + it('should shift the tab bar above the keyboard when tabBarRespectsIMEInsets is true', async () => { + // Baseline tab bar position before keyboard + const frameBeforeKeyboard = await getConfigTabItemFrame(); + + await openKeyboard(); + + // With tabBarRespectsIMEInsets=true, the BottomNavigationView shifts upward + // to sit on top of the IME. The tab bar item's Y coordinate should be + // significantly smaller (higher on screen) than the pre-keyboard baseline. + const frameWithKeyboard = await getConfigTabItemFrame(); + + jestExpect(frameWithKeyboard.y).toBeLessThan(frameBeforeKeyboard.y); + + await dismissKeyboard(); + }); + + it('should restore the tab bar to its original position after dismissing the keyboard (tabBarRespectsIMEInsets: true)', async () => { + const frameBeforeKeyboard = await getConfigTabItemFrame(); + + await openKeyboard(); + await dismissKeyboard(); + + const frameAfterDismiss = await getConfigTabItemFrame(); + + // After dismissal the tab bar should return to its original bottom position. + jestExpect( + Math.abs(frameAfterDismiss.y - frameBeforeKeyboard.y), + ).toBeLessThan(5); + }); + + // --------------------------------------------------------------------------- + // tabBarRespectsIMEInsets: true, safeAreaViewBottomEdgeEnabled: false + // --------------------------------------------------------------------------- + + it('should toggle safeAreaViewBottomEdgeEnabled to false', async () => { + await element(by.id('safe-area-bottom-edge-switch')).tap(); + await expect(element(by.id('safe-area-bottom-edge-switch'))).toHaveLabel( + 'safeAreaViewBottomEdgeEnabled: false', + ); + }); + + it('should still shift the tab bar above the keyboard when tabBarRespectsIMEInsets is true and safeAreaViewBottomEdgeEnabled is false', async () => { + const frameBeforeKeyboard = await getConfigTabItemFrame(); + + await openKeyboard(); + + const frameWithKeyboard = await getConfigTabItemFrame(); + + // tabBarRespectsIMEInsets=true still controls tab bar elevation regardless + // of the safe-area edge setting. + jestExpect(frameWithKeyboard.y).toBeLessThan(frameBeforeKeyboard.y); + + await dismissKeyboard(); + }); + + // --------------------------------------------------------------------------- + // tabBarRespectsIMEInsets: false, safeAreaViewBottomEdgeEnabled: false + // --------------------------------------------------------------------------- + + it('should toggle tabBarRespectsIMEInsets back to false', async () => { + await element(by.id('tab-bar-respects-ime-insets-switch')).tap(); + await expect( + element(by.id('tab-bar-respects-ime-insets-switch')), + ).toHaveLabel('tabBarRespectsIMEInsets: false'); + }); + + it('should keep the tab bar at the bottom when keyboard opens and both props are in their "off" state', async () => { + const frameBeforeKeyboard = await getConfigTabItemFrame(); + + await openKeyboard(); + + const frameWithKeyboard = await getConfigTabItemFrame(); + + // tabBarRespectsIMEInsets=false, safeAreaViewBottomEdgeEnabled=false: + // the tab bar stays anchored at the bottom, not shifting for the keyboard. + jestExpect( + Math.abs(frameWithKeyboard.y - frameBeforeKeyboard.y), + ).toBeLessThan(5); + + await dismissKeyboard(); + }); +}); diff --git a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts index bcdb66436b..b15bb41799 100644 --- a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts +++ b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts @@ -1,9 +1,5 @@ import { device, expect, element, by } from 'detox'; -import { - describeIfiOS, - forceTapByLabeliOS, - selectSingleFeatureTestsScreen, -} from '../../e2e-utils'; +import { describeIfiOS, selectSingleFeatureTestsScreen } from '../../e2e-utils'; async function tapOptionButton(optionText: string) { await element(by.text(optionText)).tap(); @@ -59,351 +55,361 @@ describeIfiOS('Tab Bar System Item', () => { ); }); }); -}); -describe('Runtime Config tab — initial state', () => { - beforeAll(async () => { - await element(by.id('custom-tab-item')).tap(); - }); + describe('Runtime Config tab — initial state', () => { + beforeAll(async () => { + await element(by.id('custom-tab-item')).tap(); + }); - it('should display the Runtime Config screen content', async () => { - await expect(element(by.text('Runtime Config'))).toBeVisible(); - await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); - await expect(element(by.text('title: undefined (system)'))).toBeVisible(); - await expect( - element(by.text('icon: system (from systemItem)')), - ).toBeVisible(); - await expect(element(by.id('system-item-selector'))).toBeVisible(); - await expect(element(by.text('favorites'))).toBeVisible(); - await expect(element(by.text('history'))).toBeVisible(); - await expect(element(by.text('search'))).toBeVisible(); - - await expect(element(by.id('title-selector'))).toBeVisible(); - await expect(element(by.text('custom'))).toBeVisible(); - await expect(element(by.text('hidden'))).toBeVisible(); - - await expect(element(by.id('icon-selector'))).toBeVisible(); - await expect(element(by.text('house'))).toBeVisible(); - await expect(element(by.text('heart'))).toBeVisible(); + it('should display the Runtime Config screen content', async () => { + await expect(element(by.text('Runtime Config'))).toBeVisible(); + await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); + await expect(element(by.text('title: undefined (system)'))).toBeVisible(); + await expect( + element(by.text('icon: system (from systemItem)')), + ).toBeVisible(); + await expect(element(by.id('system-item-selector'))).toBeVisible(); + await expect(element(by.text('favorites'))).toBeVisible(); + await expect(element(by.text('history'))).toBeVisible(); + await expect(element(by.text('search'))).toBeVisible(); + + await expect(element(by.id('title-selector'))).toBeVisible(); + await expect(element(by.text('custom'))).toBeVisible(); + await expect(element(by.text('hidden'))).toBeVisible(); + + await expect(element(by.id('icon-selector'))).toBeVisible(); + await expect(element(by.text('house'))).toBeVisible(); + await expect(element(by.text('heart'))).toBeVisible(); + }); }); -}); -describe('Runtime Config tab — systemItem cycling', () => { - it('should update the tab bar item when switching to history systemItem', async () => { - await expect(element(by.text('Runtime Config'))).toBeVisible(); - await tapOptionButton('history'); - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toExist(); - - await expect(element(by.label('History'))).toExist(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).not.toExist(); - }); + describe('Runtime Config tab — systemItem cycling', () => { + it('should update the tab bar item when switching to history systemItem', async () => { + await expect(element(by.text('Runtime Config'))).toBeVisible(); + await tapOptionButton('history'); + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toExist(); - it('should update the tab bar item when switching to search systemItem', async () => { - await tapOptionButton('search'); - await expect( - element(by.id('config-systemitem').and(by.label("systemItem: 'search'"))), - ).toBeVisible(); - - await expect(element(by.label('Search'))).toExist(); - await expect( - element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).not.toExist(); - }); + await expect(element(by.label('History'))).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).not.toExist(); + }); - it('should update the tab bar item when switching to favorites systemItem', async () => { - await tapOptionButton('favorites'); - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'favorites'")), - ), - ).toBeVisible(); - - await expect(element(by.label('Favorites'))).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), - ).not.toExist(); - }); -}); + it('should update the tab bar item when switching to search systemItem', async () => { + await tapOptionButton('search'); + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'search'")), + ), + ).toBeVisible(); -describe('Runtime Config tab — title override cycling', () => { - it('should update the tab bar item label after tapping custom title', async () => { - await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); - await expect(element(by.text('title: undefined (system)'))).toBeVisible(); - await expect( - element(by.text('icon: system (from systemItem)')), - ).toBeVisible(); - - await tapOptionButton('custom'); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - }); + await expect(element(by.label('Search'))).toExist(); + await expect( + element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).not.toExist(); + }); - it('should hide the tab bar item label after tapping hidden title', async () => { - await tapOptionButton('hidden'); - await expect( - element(by.id('config-title').and(by.label("title: '' (hidden)"))), - ).toBeVisible(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).not.toExist(); - await expect( - element(by.label('favorite').and(by.type('_UITabButton'))), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - }); + it('should update the tab bar item when switching to favorites systemItem', async () => { + await tapOptionButton('favorites'); + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'favorites'")), + ), + ).toBeVisible(); - it('should update the tab bar item label after restoring system title', async () => { - await tapSystemTitleOption(); - await expect( - element(by.id('config-title').and(by.label('title: undefined (system)'))), - ).toBeVisible(); - await expect( - element(by.label('Favorites').and(by.type('_UITabButton'))), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); + await expect(element(by.label('Favorites'))).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), + ).not.toExist(); + }); }); -}); -describe('Runtime Config tab — icon override cycling', () => { - it('should update tab bar item icon after tapping house icon', async () => { - await tapOptionButton('house'); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'house'"))), - ).toBeVisible(); - await expect( - element(by.id('house').and(by.label('home'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).not.toExist(); - }); + describe('Runtime Config tab — title override cycling', () => { + it('should update the tab bar item label after tapping custom title', async () => { + await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); + await expect(element(by.text('title: undefined (system)'))).toBeVisible(); + await expect( + element(by.text('icon: system (from systemItem)')), + ).toBeVisible(); - it('should navigate to Bookmarks and back while house icon override is active', async () => { - await element(by.label('Bookmarks')).atIndex(0).tap(); - await expect(element(by.text('Static System Item'))).toBeVisible(); - await expect( - element(by.id('house').and(by.label('home'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('book.fill').and(by.label('bookmark'))).atIndex(0), - ).toExist(); - await expect(element(by.id('bookmark-tab-item'))).toHaveLabel('Bookmarks'); - - await element(by.label('Favorites')).atIndex(0).tap(); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'house'"))), - ).toBeVisible(); - await expect( - element(by.id('house').and(by.label('home'))).atIndex(0), - ).toExist(); - }); + await tapOptionButton('custom'); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); - it('should update tab bar item icon after tapping heart icon', async () => { - await tapOptionButton('heart'); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), - ).toBeVisible(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).toExist(); - await expect(element(by.id('house').and(by.label('home')))).not.toExist(); - }); + it('should hide the tab bar item label after tapping hidden title', async () => { + await tapOptionButton('hidden'); + await expect( + element(by.id('config-title').and(by.label("title: '' (hidden)"))), + ).toBeVisible(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).not.toExist(); + await expect( + element(by.label('favorite').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); - it('should update tab bar item icon after restoring system icon', async () => { - await tapSystemIconOption(); - await expect( - element( - by.id('config-icon').and(by.label('icon: system (from systemItem)')), - ), - ).toBeVisible(); - - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).not.toExist(); + it('should update the tab bar item label after restoring system title', async () => { + await tapSystemTitleOption(); + await expect( + element( + by.id('config-title').and(by.label('title: undefined (system)')), + ), + ).toBeVisible(); + await expect( + element(by.label('Favorites').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); }); -}); -describe('Runtime Config tab — combined overrides', () => { - it('should update tab bar item with combined selection of search systemItem + custom title + heart icon', async () => { - await tapOptionButton('search'); - await tapOptionButton('custom'); - await tapOptionButton('heart'); - - await expect( - element(by.id('config-systemitem').and(by.label("systemItem: 'search'"))), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), - ).toBeVisible(); - - await expect(element(by.label('Search'))).not.toExist(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), - ).not.toExist(); - }); + describe('Runtime Config tab — icon override cycling', () => { + it('should update tab bar item icon after tapping house icon', async () => { + await tapOptionButton('house'); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'house'"))), + ).toBeVisible(); + await expect( + element(by.id('house').and(by.label('home'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).not.toExist(); + }); - it('should navigate to the Bookmarks tab and back with combined overrides active', async () => { - await element(by.label('Bookmarks')).atIndex(0).tap(); - await expect(element(by.text('Static System Item'))).toBeVisible(); - - await element(by.id('heart').and(by.label('love'))) - .atIndex(0) - .tap(); - - await expect( - element(by.id('config-systemitem').and(by.label("systemItem: 'search'"))), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), - ).toBeVisible(); - }); + it('should navigate to Bookmarks and back while house icon override is active', async () => { + await element(by.label('Bookmarks')).atIndex(0).tap(); + await expect(element(by.text('Static System Item'))).toBeVisible(); + await expect( + element(by.id('house').and(by.label('home'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('book.fill').and(by.label('bookmark'))).atIndex(0), + ).toExist(); + await expect(element(by.id('bookmark-tab-item'))).toHaveLabel( + 'Bookmarks', + ); - it('should retain custom title and heart icon after switching to history systemItem', async () => { - await tapOptionButton('history'); - - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), - ).toBeVisible(); - - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); - await expect(element(by.label('History'))).not.toExist(); - - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).not.toExist(); - }); + await element(by.label('Favorites')).atIndex(0).tap(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'house'"))), + ).toBeVisible(); + await expect( + element(by.id('house').and(by.label('home'))).atIndex(0), + ).toExist(); + }); - it('should fall back to system history icon when icon is set to system', async () => { - await tapSystemIconOption(); - - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - await expect( - element( - by.id('config-icon').and(by.label('icon: system (from systemItem)')), - ), - ).toBeVisible(); - - await expect(element(by.label('History'))).not.toExist(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); - - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).not.toExist(); - }); + it('should update tab bar item icon after tapping heart icon', async () => { + await tapOptionButton('heart'); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).toExist(); + await expect(element(by.id('house').and(by.label('home')))).not.toExist(); + }); + + it('should update tab bar item icon after restoring system icon', async () => { + await tapSystemIconOption(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); - it('should hide the tab bar label when title is set to hidden', async () => { - await tapOptionButton('hidden'); - - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label("title: '' (hidden)"))), - ).toBeVisible(); - await expect( - element( - by.id('config-icon').and(by.label('icon: system (from systemItem)')), - ), - ).toBeVisible(); - - await expect(element(by.label('History'))).not.toExist(); - - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).toExist(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).not.toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).not.toExist(); + }); }); - it('should restore the system localized title when title is set to system', async () => { - await tapSystemTitleOption(); - - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toBeVisible(); - await expect(element(by.text('title: undefined (system)'))).toBeVisible(); - await expect( - element( - by.id('config-icon').and(by.label('icon: system (from systemItem)')), - ), - ).toBeVisible(); - - await expect(element(by.label('History'))).toExist(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).toExist(); + describe('Runtime Config tab — combined overrides', () => { + it('should update tab bar item with combined selection of search systemItem + custom title + heart icon', async () => { + await tapOptionButton('search'); + await tapOptionButton('custom'); + await tapOptionButton('heart'); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'search'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + + await expect(element(by.label('Search'))).not.toExist(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), + ).not.toExist(); + }); + + it('should navigate to the Bookmarks tab and back with combined overrides active', async () => { + await element(by.label('Bookmarks')).atIndex(0).tap(); + await expect(element(by.text('Static System Item'))).toBeVisible(); + + await element(by.id('heart').and(by.label('love'))) + .atIndex(0) + .tap(); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'search'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + }); + + it('should retain custom title and heart icon after switching to history systemItem', async () => { + await tapOptionButton('history'); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + await expect(element(by.label('History'))).not.toExist(); + + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).not.toExist(); + }); + + it('should fall back to system history icon when icon is set to system', async () => { + await tapSystemIconOption(); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect(element(by.label('History'))).not.toExist(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).not.toExist(); + }); + + it('should hide the tab bar label when title is set to hidden', async () => { + await tapOptionButton('hidden'); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label("title: '' (hidden)"))), + ).toBeVisible(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect(element(by.label('History'))).not.toExist(); + + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).not.toExist(); + }); + + it('should restore the system localized title when title is set to system', async () => { + await tapSystemTitleOption(); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect(element(by.text('title: undefined (system)'))).toBeVisible(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect(element(by.label('History'))).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + }); }); }); diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/index.tsx index dc0dabc98e..3e07009c1f 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/index.tsx @@ -34,6 +34,7 @@ function ConfigScreen() { Safe Area – Bottom Edge tabBarRespectsIMEInsets TextInput - TabsScreen bottom + TabsScreen bottom ); @@ -72,6 +75,7 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ options: { ...DEFAULT_TAB_ROUTE_OPTIONS, title: 'Config', + tabBarItemTestID: 'ime-insets-config-tab-item', safeAreaConfiguration: { edges: { bottom: true, @@ -85,6 +89,7 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ options: { ...DEFAULT_TAB_ROUTE_OPTIONS, title: 'Tab2', + tabBarItemTestID: 'ime-insets-tab2-tab-item', }, }, ]; diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/scenario-description.ts b/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/scenario-description.ts index 5283e267ea..f1321c0c56 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/scenario-description.ts +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/scenario-description.ts @@ -6,6 +6,6 @@ export const scenarioDescription: ScenarioDescription = { details: 'Tests prop that determines whether BottomNavigationView respects IME insets.', platforms: ['android'], - e2eCoverage: 'tbd', + e2eCoverage: 'incomplete', smokeTest: false, }; diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx index c43ac1405d..f613a7cfd2 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -164,7 +164,7 @@ function RuntimeConfigScreen() { icon; `system` icon falls back{'\n'}to the systemItem default (no stale image). - + ); } diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts index cf66ade8db..aaff589ab0 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts @@ -6,6 +6,6 @@ export const scenarioDescription: ScenarioDescription = { details: 'Validates different configurations of the systemItem prop.', platforms: ['ios'], - e2eCoverage: 'tbd', + e2eCoverage: 'incomplete', smokeTest: false, }; From 6f093b97da4ec686c9f93af0c74f613aa6f020d7 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Wed, 10 Jun 2026 15:52:40 +0200 Subject: [PATCH 17/22] Revert "changinf e2eCovarage status in scenario-description + applying ai review comments" This reverts commit d298452f3babe148028855498930a6f0528b0803. --- .../tabs/test-tabs-ime-insets-android.e2e.ts | 189 ----- .../tabs/test-tabs-system-item-ios.e2e.ts | 662 +++++++++--------- .../test-tabs-ime-insets-android/index.tsx | 7 +- .../scenario-description.ts | 2 +- .../tabs/test-tabs-system-item-ios/index.tsx | 2 +- .../scenario-description.ts | 2 +- 6 files changed, 332 insertions(+), 532 deletions(-) delete mode 100644 FabricExample/e2e/single-feature-tests/tabs/test-tabs-ime-insets-android.e2e.ts diff --git a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-ime-insets-android.e2e.ts b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-ime-insets-android.e2e.ts deleted file mode 100644 index 62c2bd522a..0000000000 --- a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-ime-insets-android.e2e.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { expect as jestExpect } from '@jest/globals'; -import { device, expect, element, by } from 'detox'; -import { AndroidElementAttributes } from 'detox/detox'; -import { - describeIfAndroid, - selectSingleFeatureTestsScreen, -} from '../../e2e-utils'; - -/** - * Retrieves the frame attributes for the tab bar Config item. - * Used to measure the tab bar's Y position before and after the keyboard appears, - * so we can assert whether the tab bar shifted above the keyboard or stayed at the bottom. - */ -async function getConfigTabItemFrame(): Promise< - AndroidElementAttributes['frame'] -> { - const attrs = (await element( - by.id('ime-insets-config-tab-item'), - ).getAttributes()) as AndroidElementAttributes; - return attrs.frame; -} - -/** - * Taps the TextInput to bring up the soft keyboard (IME). - * Detox synchronization waits for layout changes to settle before - * the call returns, so subsequent frame reads reflect the post-keyboard layout. - */ -async function openKeyboard() { - await element(by.id('ime-insets-text-input')).tap(); -} - -/** - * Dismisses the soft keyboard by pressing the Android back button. - */ -async function dismissKeyboard() { - await device.pressBack(); -} - -describeIfAndroid('Tabs IME insets (Android)', () => { - beforeAll(async () => { - await device.reloadReactNative(); - await selectSingleFeatureTestsScreen( - 'Tabs', - 'test-tabs-ime-insets-android', - ); - }); - - // --------------------------------------------------------------------------- - // Baseline - // --------------------------------------------------------------------------- - - it('should display the Config tab with correct default switch states', async () => { - await expect(element(by.id('safe-area-bottom-edge-switch'))).toBeVisible(); - await expect( - element(by.id('tab-bar-respects-ime-insets-switch')), - ).toBeVisible(); - await expect(element(by.id('ime-insets-text-input'))).toBeVisible(); - - // Default: safeAreaViewBottomEdgeEnabled=true, tabBarRespectsIMEInsets=false - await expect(element(by.id('safe-area-bottom-edge-switch'))).toHaveLabel( - 'safeAreaViewBottomEdgeEnabled: true', - ); - await expect( - element(by.id('tab-bar-respects-ime-insets-switch')), - ).toHaveLabel('tabBarRespectsIMEInsets: false'); - }); - - it('should show "TabsScreen bottom" text at the bottom of the layout', async () => { - await expect(element(by.id('tabs-screen-bottom-text'))).toBeVisible(); - await expect(element(by.id('tabs-screen-bottom-text'))).toHaveText( - 'TabsScreen bottom', - ); - }); - - // --------------------------------------------------------------------------- - // tabBarRespectsIMEInsets: false, safeAreaViewBottomEdgeEnabled: true - // --------------------------------------------------------------------------- - - it('should keep the tab bar at the bottom when keyboard opens and tabBarRespectsIMEInsets is false', async () => { - // Baseline tab bar position before keyboard - const frameBeforeKeyboard = await getConfigTabItemFrame(); - - await openKeyboard(); - - // With tabBarRespectsIMEInsets=false, the tab bar stays anchored at the - // bottom of the window. The tab bar item Y position should remain unchanged - // (the native view does not shift up to accommodate the keyboard). - const frameWithKeyboard = await getConfigTabItemFrame(); - - jestExpect( - Math.abs(frameWithKeyboard.y - frameBeforeKeyboard.y), - ).toBeLessThan(5); - - await dismissKeyboard(); - }); - - // --------------------------------------------------------------------------- - // tabBarRespectsIMEInsets: true, safeAreaViewBottomEdgeEnabled: true - // --------------------------------------------------------------------------- - - it('should toggle tabBarRespectsIMEInsets to true', async () => { - await element(by.id('tab-bar-respects-ime-insets-switch')).tap(); - await expect( - element(by.id('tab-bar-respects-ime-insets-switch')), - ).toHaveLabel('tabBarRespectsIMEInsets: true'); - }); - - it('should shift the tab bar above the keyboard when tabBarRespectsIMEInsets is true', async () => { - // Baseline tab bar position before keyboard - const frameBeforeKeyboard = await getConfigTabItemFrame(); - - await openKeyboard(); - - // With tabBarRespectsIMEInsets=true, the BottomNavigationView shifts upward - // to sit on top of the IME. The tab bar item's Y coordinate should be - // significantly smaller (higher on screen) than the pre-keyboard baseline. - const frameWithKeyboard = await getConfigTabItemFrame(); - - jestExpect(frameWithKeyboard.y).toBeLessThan(frameBeforeKeyboard.y); - - await dismissKeyboard(); - }); - - it('should restore the tab bar to its original position after dismissing the keyboard (tabBarRespectsIMEInsets: true)', async () => { - const frameBeforeKeyboard = await getConfigTabItemFrame(); - - await openKeyboard(); - await dismissKeyboard(); - - const frameAfterDismiss = await getConfigTabItemFrame(); - - // After dismissal the tab bar should return to its original bottom position. - jestExpect( - Math.abs(frameAfterDismiss.y - frameBeforeKeyboard.y), - ).toBeLessThan(5); - }); - - // --------------------------------------------------------------------------- - // tabBarRespectsIMEInsets: true, safeAreaViewBottomEdgeEnabled: false - // --------------------------------------------------------------------------- - - it('should toggle safeAreaViewBottomEdgeEnabled to false', async () => { - await element(by.id('safe-area-bottom-edge-switch')).tap(); - await expect(element(by.id('safe-area-bottom-edge-switch'))).toHaveLabel( - 'safeAreaViewBottomEdgeEnabled: false', - ); - }); - - it('should still shift the tab bar above the keyboard when tabBarRespectsIMEInsets is true and safeAreaViewBottomEdgeEnabled is false', async () => { - const frameBeforeKeyboard = await getConfigTabItemFrame(); - - await openKeyboard(); - - const frameWithKeyboard = await getConfigTabItemFrame(); - - // tabBarRespectsIMEInsets=true still controls tab bar elevation regardless - // of the safe-area edge setting. - jestExpect(frameWithKeyboard.y).toBeLessThan(frameBeforeKeyboard.y); - - await dismissKeyboard(); - }); - - // --------------------------------------------------------------------------- - // tabBarRespectsIMEInsets: false, safeAreaViewBottomEdgeEnabled: false - // --------------------------------------------------------------------------- - - it('should toggle tabBarRespectsIMEInsets back to false', async () => { - await element(by.id('tab-bar-respects-ime-insets-switch')).tap(); - await expect( - element(by.id('tab-bar-respects-ime-insets-switch')), - ).toHaveLabel('tabBarRespectsIMEInsets: false'); - }); - - it('should keep the tab bar at the bottom when keyboard opens and both props are in their "off" state', async () => { - const frameBeforeKeyboard = await getConfigTabItemFrame(); - - await openKeyboard(); - - const frameWithKeyboard = await getConfigTabItemFrame(); - - // tabBarRespectsIMEInsets=false, safeAreaViewBottomEdgeEnabled=false: - // the tab bar stays anchored at the bottom, not shifting for the keyboard. - jestExpect( - Math.abs(frameWithKeyboard.y - frameBeforeKeyboard.y), - ).toBeLessThan(5); - - await dismissKeyboard(); - }); -}); diff --git a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts index b15bb41799..bcdb66436b 100644 --- a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts +++ b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts @@ -1,5 +1,9 @@ import { device, expect, element, by } from 'detox'; -import { describeIfiOS, selectSingleFeatureTestsScreen } from '../../e2e-utils'; +import { + describeIfiOS, + forceTapByLabeliOS, + selectSingleFeatureTestsScreen, +} from '../../e2e-utils'; async function tapOptionButton(optionText: string) { await element(by.text(optionText)).tap(); @@ -55,361 +59,351 @@ describeIfiOS('Tab Bar System Item', () => { ); }); }); +}); - describe('Runtime Config tab — initial state', () => { - beforeAll(async () => { - await element(by.id('custom-tab-item')).tap(); - }); - - it('should display the Runtime Config screen content', async () => { - await expect(element(by.text('Runtime Config'))).toBeVisible(); - await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); - await expect(element(by.text('title: undefined (system)'))).toBeVisible(); - await expect( - element(by.text('icon: system (from systemItem)')), - ).toBeVisible(); - await expect(element(by.id('system-item-selector'))).toBeVisible(); - await expect(element(by.text('favorites'))).toBeVisible(); - await expect(element(by.text('history'))).toBeVisible(); - await expect(element(by.text('search'))).toBeVisible(); - - await expect(element(by.id('title-selector'))).toBeVisible(); - await expect(element(by.text('custom'))).toBeVisible(); - await expect(element(by.text('hidden'))).toBeVisible(); - - await expect(element(by.id('icon-selector'))).toBeVisible(); - await expect(element(by.text('house'))).toBeVisible(); - await expect(element(by.text('heart'))).toBeVisible(); - }); +describe('Runtime Config tab — initial state', () => { + beforeAll(async () => { + await element(by.id('custom-tab-item')).tap(); }); - describe('Runtime Config tab — systemItem cycling', () => { - it('should update the tab bar item when switching to history systemItem', async () => { - await expect(element(by.text('Runtime Config'))).toBeVisible(); - await tapOptionButton('history'); - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toExist(); - - await expect(element(by.label('History'))).toExist(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).not.toExist(); - }); - - it('should update the tab bar item when switching to search systemItem', async () => { - await tapOptionButton('search'); - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'search'")), - ), - ).toBeVisible(); - - await expect(element(by.label('Search'))).toExist(); - await expect( - element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).not.toExist(); - }); - - it('should update the tab bar item when switching to favorites systemItem', async () => { - await tapOptionButton('favorites'); - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'favorites'")), - ), - ).toBeVisible(); - - await expect(element(by.label('Favorites'))).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), - ).not.toExist(); - }); + it('should display the Runtime Config screen content', async () => { + await expect(element(by.text('Runtime Config'))).toBeVisible(); + await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); + await expect(element(by.text('title: undefined (system)'))).toBeVisible(); + await expect( + element(by.text('icon: system (from systemItem)')), + ).toBeVisible(); + await expect(element(by.id('system-item-selector'))).toBeVisible(); + await expect(element(by.text('favorites'))).toBeVisible(); + await expect(element(by.text('history'))).toBeVisible(); + await expect(element(by.text('search'))).toBeVisible(); + + await expect(element(by.id('title-selector'))).toBeVisible(); + await expect(element(by.text('custom'))).toBeVisible(); + await expect(element(by.text('hidden'))).toBeVisible(); + + await expect(element(by.id('icon-selector'))).toBeVisible(); + await expect(element(by.text('house'))).toBeVisible(); + await expect(element(by.text('heart'))).toBeVisible(); }); +}); - describe('Runtime Config tab — title override cycling', () => { - it('should update the tab bar item label after tapping custom title', async () => { - await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); - await expect(element(by.text('title: undefined (system)'))).toBeVisible(); - await expect( - element(by.text('icon: system (from systemItem)')), - ).toBeVisible(); - - await tapOptionButton('custom'); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - }); - - it('should hide the tab bar item label after tapping hidden title', async () => { - await tapOptionButton('hidden'); - await expect( - element(by.id('config-title').and(by.label("title: '' (hidden)"))), - ).toBeVisible(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).not.toExist(); - await expect( - element(by.label('favorite').and(by.type('_UITabButton'))), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - }); - - it('should update the tab bar item label after restoring system title', async () => { - await tapSystemTitleOption(); - await expect( - element( - by.id('config-title').and(by.label('title: undefined (system)')), - ), - ).toBeVisible(); - await expect( - element(by.label('Favorites').and(by.type('_UITabButton'))), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - }); +describe('Runtime Config tab — systemItem cycling', () => { + it('should update the tab bar item when switching to history systemItem', async () => { + await expect(element(by.text('Runtime Config'))).toBeVisible(); + await tapOptionButton('history'); + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toExist(); + + await expect(element(by.label('History'))).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).not.toExist(); }); - describe('Runtime Config tab — icon override cycling', () => { - it('should update tab bar item icon after tapping house icon', async () => { - await tapOptionButton('house'); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'house'"))), - ).toBeVisible(); - await expect( - element(by.id('house').and(by.label('home'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).not.toExist(); - }); - - it('should navigate to Bookmarks and back while house icon override is active', async () => { - await element(by.label('Bookmarks')).atIndex(0).tap(); - await expect(element(by.text('Static System Item'))).toBeVisible(); - await expect( - element(by.id('house').and(by.label('home'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('book.fill').and(by.label('bookmark'))).atIndex(0), - ).toExist(); - await expect(element(by.id('bookmark-tab-item'))).toHaveLabel( - 'Bookmarks', - ); - - await element(by.label('Favorites')).atIndex(0).tap(); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'house'"))), - ).toBeVisible(); - await expect( - element(by.id('house').and(by.label('home'))).atIndex(0), - ).toExist(); - }); - - it('should update tab bar item icon after tapping heart icon', async () => { - await tapOptionButton('heart'); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), - ).toBeVisible(); - - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).toExist(); - await expect(element(by.id('house').and(by.label('home')))).not.toExist(); - }); - - it('should update tab bar item icon after restoring system icon', async () => { - await tapSystemIconOption(); - await expect( - element( - by.id('config-icon').and(by.label('icon: system (from systemItem)')), - ), - ).toBeVisible(); - - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).not.toExist(); - }); + it('should update the tab bar item when switching to search systemItem', async () => { + await tapOptionButton('search'); + await expect( + element(by.id('config-systemitem').and(by.label("systemItem: 'search'"))), + ).toBeVisible(); + + await expect(element(by.label('Search'))).toExist(); + await expect( + element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).not.toExist(); }); - describe('Runtime Config tab — combined overrides', () => { - it('should update tab bar item with combined selection of search systemItem + custom title + heart icon', async () => { - await tapOptionButton('search'); - await tapOptionButton('custom'); - await tapOptionButton('heart'); - - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'search'")), - ), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), - ).toBeVisible(); - - await expect(element(by.label('Search'))).not.toExist(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), - ).not.toExist(); - }); - - it('should navigate to the Bookmarks tab and back with combined overrides active', async () => { - await element(by.label('Bookmarks')).atIndex(0).tap(); - await expect(element(by.text('Static System Item'))).toBeVisible(); - - await element(by.id('heart').and(by.label('love'))) - .atIndex(0) - .tap(); - - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'search'")), - ), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), - ).toBeVisible(); - }); - - it('should retain custom title and heart icon after switching to history systemItem', async () => { - await tapOptionButton('history'); + it('should update the tab bar item when switching to favorites systemItem', async () => { + await tapOptionButton('favorites'); + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'favorites'")), + ), + ).toBeVisible(); + + await expect(element(by.label('Favorites'))).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), + ).not.toExist(); + }); +}); - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), - ).toBeVisible(); +describe('Runtime Config tab — title override cycling', () => { + it('should update the tab bar item label after tapping custom title', async () => { + await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); + await expect(element(by.text('title: undefined (system)'))).toBeVisible(); + await expect( + element(by.text('icon: system (from systemItem)')), + ).toBeVisible(); + + await tapOptionButton('custom'); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); - await expect(element(by.label('History'))).not.toExist(); + it('should hide the tab bar item label after tapping hidden title', async () => { + await tapOptionButton('hidden'); + await expect( + element(by.id('config-title').and(by.label("title: '' (hidden)"))), + ).toBeVisible(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).not.toExist(); + await expect( + element(by.label('favorite').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).not.toExist(); - }); + it('should update the tab bar item label after restoring system title', async () => { + await tapSystemTitleOption(); + await expect( + element(by.id('config-title').and(by.label('title: undefined (system)'))), + ).toBeVisible(); + await expect( + element(by.label('Favorites').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); +}); - it('should fall back to system history icon when icon is set to system', async () => { - await tapSystemIconOption(); +describe('Runtime Config tab — icon override cycling', () => { + it('should update tab bar item icon after tapping house icon', async () => { + await tapOptionButton('house'); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'house'"))), + ).toBeVisible(); + await expect( + element(by.id('house').and(by.label('home'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).not.toExist(); + }); - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - await expect( - element( - by.id('config-icon').and(by.label('icon: system (from systemItem)')), - ), - ).toBeVisible(); + it('should navigate to Bookmarks and back while house icon override is active', async () => { + await element(by.label('Bookmarks')).atIndex(0).tap(); + await expect(element(by.text('Static System Item'))).toBeVisible(); + await expect( + element(by.id('house').and(by.label('home'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('book.fill').and(by.label('bookmark'))).atIndex(0), + ).toExist(); + await expect(element(by.id('bookmark-tab-item'))).toHaveLabel('Bookmarks'); + + await element(by.label('Favorites')).atIndex(0).tap(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'house'"))), + ).toBeVisible(); + await expect( + element(by.id('house').and(by.label('home'))).atIndex(0), + ).toExist(); + }); - await expect(element(by.label('History'))).not.toExist(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); + it('should update tab bar item icon after tapping heart icon', async () => { + await tapOptionButton('heart'); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).not.toExist(); - }); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).toExist(); + await expect(element(by.id('house').and(by.label('home')))).not.toExist(); + }); - it('should hide the tab bar label when title is set to hidden', async () => { - await tapOptionButton('hidden'); + it('should update tab bar item icon after restoring system icon', async () => { + await tapSystemIconOption(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).not.toExist(); + }); +}); - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label("title: '' (hidden)"))), - ).toBeVisible(); - await expect( - element( - by.id('config-icon').and(by.label('icon: system (from systemItem)')), - ), - ).toBeVisible(); +describe('Runtime Config tab — combined overrides', () => { + it('should update tab bar item with combined selection of search systemItem + custom title + heart icon', async () => { + await tapOptionButton('search'); + await tapOptionButton('custom'); + await tapOptionButton('heart'); + + await expect( + element(by.id('config-systemitem').and(by.label("systemItem: 'search'"))), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + + await expect(element(by.label('Search'))).not.toExist(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), + ).not.toExist(); + }); - await expect(element(by.label('History'))).not.toExist(); + it('should navigate to the Bookmarks tab and back with combined overrides active', async () => { + await element(by.label('Bookmarks')).atIndex(0).tap(); + await expect(element(by.text('Static System Item'))).toBeVisible(); + + await element(by.id('heart').and(by.label('love'))) + .atIndex(0) + .tap(); + + await expect( + element(by.id('config-systemitem').and(by.label("systemItem: 'search'"))), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + }); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).toExist(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).not.toExist(); - }); + it('should retain custom title and heart icon after switching to history systemItem', async () => { + await tapOptionButton('history'); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + await expect(element(by.label('History'))).not.toExist(); + + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).not.toExist(); + }); - it('should restore the system localized title when title is set to system', async () => { - await tapSystemTitleOption(); + it('should fall back to system history icon when icon is set to system', async () => { + await tapSystemIconOption(); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect(element(by.label('History'))).not.toExist(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).not.toExist(); + }); - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toBeVisible(); - await expect(element(by.text('title: undefined (system)'))).toBeVisible(); - await expect( - element( - by.id('config-icon').and(by.label('icon: system (from systemItem)')), - ), - ).toBeVisible(); + it('should hide the tab bar label when title is set to hidden', async () => { + await tapOptionButton('hidden'); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label("title: '' (hidden)"))), + ).toBeVisible(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect(element(by.label('History'))).not.toExist(); + + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).not.toExist(); + }); - await expect(element(by.label('History'))).toExist(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).toExist(); - }); + it('should restore the system localized title when title is set to system', async () => { + await tapSystemTitleOption(); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect(element(by.text('title: undefined (system)'))).toBeVisible(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect(element(by.label('History'))).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); }); }); diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/index.tsx index 3e07009c1f..dc0dabc98e 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/index.tsx @@ -34,7 +34,6 @@ function ConfigScreen() { Safe Area – Bottom Edge tabBarRespectsIMEInsets TextInput - TabsScreen bottom + TabsScreen bottom ); @@ -75,7 +72,6 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ options: { ...DEFAULT_TAB_ROUTE_OPTIONS, title: 'Config', - tabBarItemTestID: 'ime-insets-config-tab-item', safeAreaConfiguration: { edges: { bottom: true, @@ -89,7 +85,6 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ options: { ...DEFAULT_TAB_ROUTE_OPTIONS, title: 'Tab2', - tabBarItemTestID: 'ime-insets-tab2-tab-item', }, }, ]; diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/scenario-description.ts b/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/scenario-description.ts index f1321c0c56..5283e267ea 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/scenario-description.ts +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-ime-insets-android/scenario-description.ts @@ -6,6 +6,6 @@ export const scenarioDescription: ScenarioDescription = { details: 'Tests prop that determines whether BottomNavigationView respects IME insets.', platforms: ['android'], - e2eCoverage: 'incomplete', + e2eCoverage: 'tbd', smokeTest: false, }; diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx index f613a7cfd2..c43ac1405d 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -164,7 +164,7 @@ function RuntimeConfigScreen() { icon; `system` icon falls back{'\n'}to the systemItem default (no stale image). - + ); } diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts index aaff589ab0..cf66ade8db 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts @@ -6,6 +6,6 @@ export const scenarioDescription: ScenarioDescription = { details: 'Validates different configurations of the systemItem prop.', platforms: ['ios'], - e2eCoverage: 'incomplete', + e2eCoverage: 'tbd', smokeTest: false, }; From c342022f2009e9cd4535b739e170e67e93e9167f Mon Sep 17 00:00:00 2001 From: lkuchno Date: Wed, 10 Jun 2026 16:02:50 +0200 Subject: [PATCH 18/22] changing e2eCovarage status in scenario-description + applying ai review comments --- .../tabs/test-tabs-system-item-ios.e2e.ts | 656 +++++++++--------- .../tabs/test-tabs-system-item-ios/index.tsx | 2 +- .../scenario-description.ts | 2 +- 3 files changed, 335 insertions(+), 325 deletions(-) diff --git a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts index bcdb66436b..e42dfdc0ad 100644 --- a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts +++ b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts @@ -59,351 +59,361 @@ describeIfiOS('Tab Bar System Item', () => { ); }); }); -}); -describe('Runtime Config tab — initial state', () => { - beforeAll(async () => { - await element(by.id('custom-tab-item')).tap(); - }); + describe('Runtime Config tab — initial state', () => { + beforeAll(async () => { + await element(by.id('custom-tab-item')).tap(); + }); - it('should display the Runtime Config screen content', async () => { - await expect(element(by.text('Runtime Config'))).toBeVisible(); - await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); - await expect(element(by.text('title: undefined (system)'))).toBeVisible(); - await expect( - element(by.text('icon: system (from systemItem)')), - ).toBeVisible(); - await expect(element(by.id('system-item-selector'))).toBeVisible(); - await expect(element(by.text('favorites'))).toBeVisible(); - await expect(element(by.text('history'))).toBeVisible(); - await expect(element(by.text('search'))).toBeVisible(); - - await expect(element(by.id('title-selector'))).toBeVisible(); - await expect(element(by.text('custom'))).toBeVisible(); - await expect(element(by.text('hidden'))).toBeVisible(); - - await expect(element(by.id('icon-selector'))).toBeVisible(); - await expect(element(by.text('house'))).toBeVisible(); - await expect(element(by.text('heart'))).toBeVisible(); + it('should display the Runtime Config screen content', async () => { + await expect(element(by.text('Runtime Config'))).toBeVisible(); + await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); + await expect(element(by.text('title: undefined (system)'))).toBeVisible(); + await expect( + element(by.text('icon: system (from systemItem)')), + ).toBeVisible(); + await expect(element(by.id('system-item-selector'))).toBeVisible(); + await expect(element(by.text('favorites'))).toBeVisible(); + await expect(element(by.text('history'))).toBeVisible(); + await expect(element(by.text('search'))).toBeVisible(); + + await expect(element(by.id('title-selector'))).toBeVisible(); + await expect(element(by.text('custom'))).toBeVisible(); + await expect(element(by.text('hidden'))).toBeVisible(); + + await expect(element(by.id('icon-selector'))).toBeVisible(); + await expect(element(by.text('house'))).toBeVisible(); + await expect(element(by.text('heart'))).toBeVisible(); + }); }); -}); -describe('Runtime Config tab — systemItem cycling', () => { - it('should update the tab bar item when switching to history systemItem', async () => { - await expect(element(by.text('Runtime Config'))).toBeVisible(); - await tapOptionButton('history'); - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toExist(); - - await expect(element(by.label('History'))).toExist(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).not.toExist(); - }); + describe('Runtime Config tab — systemItem cycling', () => { + it('should update the tab bar item when switching to history systemItem', async () => { + await expect(element(by.text('Runtime Config'))).toBeVisible(); + await tapOptionButton('history'); + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toExist(); - it('should update the tab bar item when switching to search systemItem', async () => { - await tapOptionButton('search'); - await expect( - element(by.id('config-systemitem').and(by.label("systemItem: 'search'"))), - ).toBeVisible(); - - await expect(element(by.label('Search'))).toExist(); - await expect( - element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).not.toExist(); - }); + await expect(element(by.label('History'))).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).not.toExist(); + }); - it('should update the tab bar item when switching to favorites systemItem', async () => { - await tapOptionButton('favorites'); - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'favorites'")), - ), - ).toBeVisible(); - - await expect(element(by.label('Favorites'))).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), - ).not.toExist(); - }); -}); + it('should update the tab bar item when switching to search systemItem', async () => { + await tapOptionButton('search'); + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'search'")), + ), + ).toBeVisible(); -describe('Runtime Config tab — title override cycling', () => { - it('should update the tab bar item label after tapping custom title', async () => { - await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); - await expect(element(by.text('title: undefined (system)'))).toBeVisible(); - await expect( - element(by.text('icon: system (from systemItem)')), - ).toBeVisible(); - - await tapOptionButton('custom'); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - }); + await expect(element(by.label('Search'))).toExist(); + await expect( + element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).not.toExist(); + }); - it('should hide the tab bar item label after tapping hidden title', async () => { - await tapOptionButton('hidden'); - await expect( - element(by.id('config-title').and(by.label("title: '' (hidden)"))), - ).toBeVisible(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).not.toExist(); - await expect( - element(by.label('favorite').and(by.type('_UITabButton'))), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - }); + it('should update the tab bar item when switching to favorites systemItem', async () => { + await tapOptionButton('favorites'); + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'favorites'")), + ), + ).toBeVisible(); - it('should update the tab bar item label after restoring system title', async () => { - await tapSystemTitleOption(); - await expect( - element(by.id('config-title').and(by.label('title: undefined (system)'))), - ).toBeVisible(); - await expect( - element(by.label('Favorites').and(by.type('_UITabButton'))), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); + await expect(element(by.label('Favorites'))).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), + ).not.toExist(); + }); }); -}); -describe('Runtime Config tab — icon override cycling', () => { - it('should update tab bar item icon after tapping house icon', async () => { - await tapOptionButton('house'); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'house'"))), - ).toBeVisible(); - await expect( - element(by.id('house').and(by.label('home'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).not.toExist(); - }); + describe('Runtime Config tab — title override cycling', () => { + it('should update the tab bar item label after tapping custom title', async () => { + await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); + await expect(element(by.text('title: undefined (system)'))).toBeVisible(); + await expect( + element(by.text('icon: system (from systemItem)')), + ).toBeVisible(); - it('should navigate to Bookmarks and back while house icon override is active', async () => { - await element(by.label('Bookmarks')).atIndex(0).tap(); - await expect(element(by.text('Static System Item'))).toBeVisible(); - await expect( - element(by.id('house').and(by.label('home'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('book.fill').and(by.label('bookmark'))).atIndex(0), - ).toExist(); - await expect(element(by.id('bookmark-tab-item'))).toHaveLabel('Bookmarks'); - - await element(by.label('Favorites')).atIndex(0).tap(); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'house'"))), - ).toBeVisible(); - await expect( - element(by.id('house').and(by.label('home'))).atIndex(0), - ).toExist(); - }); + await tapOptionButton('custom'); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); - it('should update tab bar item icon after tapping heart icon', async () => { - await tapOptionButton('heart'); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), - ).toBeVisible(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).toExist(); - await expect(element(by.id('house').and(by.label('home')))).not.toExist(); - }); + it('should hide the tab bar item label after tapping hidden title', async () => { + await tapOptionButton('hidden'); + await expect( + element(by.id('config-title').and(by.label("title: '' (hidden)"))), + ).toBeVisible(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).not.toExist(); + await expect( + element(by.label('favorite').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); - it('should update tab bar item icon after restoring system icon', async () => { - await tapSystemIconOption(); - await expect( - element( - by.id('config-icon').and(by.label('icon: system (from systemItem)')), - ), - ).toBeVisible(); - - await expect( - element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).not.toExist(); + it('should update the tab bar item label after restoring system title', async () => { + await tapSystemTitleOption(); + await expect( + element( + by.id('config-title').and(by.label('title: undefined (system)')), + ), + ).toBeVisible(); + await expect( + element(by.label('Favorites').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + }); }); -}); -describe('Runtime Config tab — combined overrides', () => { - it('should update tab bar item with combined selection of search systemItem + custom title + heart icon', async () => { - await tapOptionButton('search'); - await tapOptionButton('custom'); - await tapOptionButton('heart'); - - await expect( - element(by.id('config-systemitem').and(by.label("systemItem: 'search'"))), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), - ).toBeVisible(); - - await expect(element(by.label('Search'))).not.toExist(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), - ).not.toExist(); - }); + describe('Runtime Config tab — icon override cycling', () => { + it('should update tab bar item icon after tapping house icon', async () => { + await tapOptionButton('house'); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'house'"))), + ).toBeVisible(); + await expect( + element(by.id('house').and(by.label('home'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).not.toExist(); + }); - it('should navigate to the Bookmarks tab and back with combined overrides active', async () => { - await element(by.label('Bookmarks')).atIndex(0).tap(); - await expect(element(by.text('Static System Item'))).toBeVisible(); - - await element(by.id('heart').and(by.label('love'))) - .atIndex(0) - .tap(); - - await expect( - element(by.id('config-systemitem').and(by.label("systemItem: 'search'"))), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), - ).toBeVisible(); - }); + it('should navigate to Bookmarks and back while house icon override is active', async () => { + await element(by.label('Bookmarks')).atIndex(0).tap(); + await expect(element(by.text('Static System Item'))).toBeVisible(); + await expect( + element(by.id('house').and(by.label('home'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('book.fill').and(by.label('bookmark'))).atIndex(0), + ).toExist(); + await expect(element(by.id('bookmark-tab-item'))).toHaveLabel( + 'Bookmarks', + ); - it('should retain custom title and heart icon after switching to history systemItem', async () => { - await tapOptionButton('history'); - - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - await expect( - element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), - ).toBeVisible(); - - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); - await expect(element(by.label('History'))).not.toExist(); - - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).not.toExist(); - }); + await element(by.label('Favorites')).atIndex(0).tap(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'house'"))), + ).toBeVisible(); + await expect( + element(by.id('house').and(by.label('home'))).atIndex(0), + ).toExist(); + }); - it('should fall back to system history icon when icon is set to system', async () => { - await tapSystemIconOption(); - - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label('title: "Custom"'))), - ).toBeVisible(); - await expect( - element( - by.id('config-icon').and(by.label('icon: system (from systemItem)')), - ), - ).toBeVisible(); - - await expect(element(by.label('History'))).not.toExist(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); - - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).toExist(); - await expect( - element(by.id('heart').and(by.label('love'))).atIndex(0), - ).not.toExist(); - }); + it('should update tab bar item icon after tapping heart icon', async () => { + await tapOptionButton('heart'); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).toExist(); + await expect(element(by.id('house').and(by.label('home')))).not.toExist(); + }); + + it('should update tab bar item icon after restoring system icon', async () => { + await tapSystemIconOption(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); - it('should hide the tab bar label when title is set to hidden', async () => { - await tapOptionButton('hidden'); - - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toBeVisible(); - await expect( - element(by.id('config-title').and(by.label("title: '' (hidden)"))), - ).toBeVisible(); - await expect( - element( - by.id('config-icon').and(by.label('icon: system (from systemItem)')), - ), - ).toBeVisible(); - - await expect(element(by.label('History'))).not.toExist(); - - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).toExist(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).not.toExist(); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).not.toExist(); + }); }); - it('should restore the system localized title when title is set to system', async () => { - await tapSystemTitleOption(); - - await expect( - element( - by.id('config-systemitem').and(by.label("systemItem: 'history'")), - ), - ).toBeVisible(); - await expect(element(by.text('title: undefined (system)'))).toBeVisible(); - await expect( - element( - by.id('config-icon').and(by.label('icon: system (from systemItem)')), - ), - ).toBeVisible(); - - await expect(element(by.label('History'))).toExist(); - await expect( - element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), - ).toExist(); + describe('Runtime Config tab — combined overrides', () => { + it('should update tab bar item with combined selection of search systemItem + custom title + heart icon', async () => { + await tapOptionButton('search'); + await tapOptionButton('custom'); + await tapOptionButton('heart'); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'search'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + + await expect(element(by.label('Search'))).not.toExist(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), + ).not.toExist(); + }); + + it('should navigate to the Bookmarks tab and back with combined overrides active', async () => { + await element(by.label('Bookmarks')).atIndex(0).tap(); + await expect(element(by.text('Static System Item'))).toBeVisible(); + + await element(by.id('heart').and(by.label('love'))) + .atIndex(0) + .tap(); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'search'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + }); + + it('should retain custom title and heart icon after switching to history systemItem', async () => { + await tapOptionButton('history'); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), + ).toBeVisible(); + + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + await expect(element(by.label('History'))).not.toExist(); + + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).not.toExist(); + }); + + it('should fall back to system history icon when icon is set to system', async () => { + await tapSystemIconOption(); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label('title: "Custom"'))), + ).toBeVisible(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect(element(by.label('History'))).not.toExist(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).toExist(); + + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + await expect( + element(by.id('heart').and(by.label('love'))).atIndex(0), + ).not.toExist(); + }); + + it('should hide the tab bar label when title is set to hidden', async () => { + await tapOptionButton('hidden'); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect( + element(by.id('config-title').and(by.label("title: '' (hidden)"))), + ).toBeVisible(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect(element(by.label('History'))).not.toExist(); + + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + await expect( + element(by.label('Custom').and(by.type('_UITabButton'))), + ).not.toExist(); + }); + + it('should restore the system localized title when title is set to system', async () => { + await tapSystemTitleOption(); + + await expect( + element( + by.id('config-systemitem').and(by.label("systemItem: 'history'")), + ), + ).toBeVisible(); + await expect(element(by.text('title: undefined (system)'))).toBeVisible(); + await expect( + element( + by.id('config-icon').and(by.label('icon: system (from systemItem)')), + ), + ).toBeVisible(); + + await expect(element(by.label('History'))).toExist(); + await expect( + element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), + ).toExist(); + }); }); }); diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx index c43ac1405d..f613a7cfd2 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/index.tsx @@ -164,7 +164,7 @@ function RuntimeConfigScreen() { icon; `system` icon falls back{'\n'}to the systemItem default (no stale image). - + ); } diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts index cf66ade8db..aaff589ab0 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario-description.ts @@ -6,6 +6,6 @@ export const scenarioDescription: ScenarioDescription = { details: 'Validates different configurations of the systemItem prop.', platforms: ['ios'], - e2eCoverage: 'tbd', + e2eCoverage: 'incomplete', smokeTest: false, }; From c69e2f112dcbcdafd8329bf2af08ed72684d2f5c Mon Sep 17 00:00:00 2001 From: lkuchno Date: Wed, 10 Jun 2026 16:04:04 +0200 Subject: [PATCH 19/22] removing unused import --- .../tabs/test-tabs-system-item-ios.e2e.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts index e42dfdc0ad..b15bb41799 100644 --- a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts +++ b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts @@ -1,9 +1,5 @@ import { device, expect, element, by } from 'detox'; -import { - describeIfiOS, - forceTapByLabeliOS, - selectSingleFeatureTestsScreen, -} from '../../e2e-utils'; +import { describeIfiOS, selectSingleFeatureTestsScreen } from '../../e2e-utils'; async function tapOptionButton(optionText: string) { await element(by.text(optionText)).tap(); From f6584cca38f1bdb2260d8244eef0715178630367 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Wed, 10 Jun 2026 17:19:03 +0200 Subject: [PATCH 20/22] mention that e2e test is not supporting ios18 --- .../tabs/test-tabs-system-item-ios.e2e.ts | 2 +- .../test-tabs-system-item-ios/scenario.md | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts index b15bb41799..de441a29d4 100644 --- a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts +++ b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts @@ -13,7 +13,7 @@ async function tapSystemIconOption() { await element(by.text('system')).atIndex(1).tap(); } -describeIfiOS('Tab Bar System Item', () => { +describeIfiOS('Tab Bar System Item (iOS 26)', () => { beforeAll(async () => { await device.reloadReactNative(); await selectSingleFeatureTestsScreen('Tabs', 'test-tabs-system-item-ios'); diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md index 301c3603b6..29bf36e0c7 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md @@ -8,7 +8,7 @@ the correct UIKit-provided icon and localized title with no override. Exercises the Runtime Config tab, which combines three independent toggle groups (systemItem, title, icon) applied atomically via `setRouteOptions`, and verifies that all combinations produce the -correct tab bar item appearance — including that switching the icon +correct tab bar item appearance - including that switching the icon back to `system` immediately removes any stale custom SF Symbol image. Verifies that the `search` item renders as a magnifying glass with iOS-version-specific layout differences. Includes an orientation @@ -18,14 +18,17 @@ smoke test. ## E2E test -Incomplete: Automation covers steps 1-19 but not in the full scope - see list below. +Incomplete: Automation was implemented for the iOS 26 simulator and covers steps 1–19, +but not in full scope - see the list below. Not automated: - Validating the differences between icon and selectedIcon. -- Checking systemItem: 'search' tab bar item detached state and label hiding for iOS 26. +- Checking systemItem: 'search' tab bar item detached state and label hiding. - The "hidden title" option is validated, but indirectly; it should be verified manually. - Steps 20-23 (orientation changes). +- iOS 18 is not supported: UI element identifiers differ from iOS 26 + (e.g. UITabBarButton on iOS 18 vs. _UITabButton on iOS 26), which causes tests to fail. ## Prerequisites @@ -75,7 +78,7 @@ iPhone Pro models (for iOS 18 excluding Max). --- -### Runtime Config tab — initial state +### Runtime Config tab - initial state 3. Tap the second tab in the tab bar (**Favorites**). @@ -88,7 +91,7 @@ iPhone Pro models (for iOS 18 excluding Max). --- -### Runtime Config tab — systemItem cycling +### Runtime Config tab - systemItem cycling 4. Tap **history** in the systemItem group. @@ -111,7 +114,7 @@ iPhone Pro models (for iOS 18 excluding Max). --- -### Runtime Config tab — title override cycling +### Runtime Config tab - title override cycling 7. Tap **Custom** in the title group. @@ -130,7 +133,7 @@ iPhone Pro models (for iOS 18 excluding Max). --- -### Runtime Config tab — icon override cycling +### Runtime Config tab - icon override cycling 10. Tap **house** in the icon group. @@ -160,7 +163,7 @@ iPhone Pro models (for iOS 18 excluding Max). --- -### Runtime Config tab — combined overrides +### Runtime Config tab - combined overrides 14. Set systemItem to **search**, title to **custom**, icon to **heart** (all three groups in a non-default state). From 7624fc1ef91212294df80c8a223aa6cd459feb36 Mon Sep 17 00:00:00 2001 From: lkuchno Date: Thu, 11 Jun 2026 12:17:34 +0200 Subject: [PATCH 21/22] rewrite test to be run on ios18 and ios26, add assertion for search tab detach --- .../tabs/test-tabs-system-item-ios.e2e.ts | 182 +++++++++++++++--- .../test-tabs-system-item-ios/scenario.md | 12 +- 2 files changed, 159 insertions(+), 35 deletions(-) diff --git a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts index de441a29d4..2801f52a73 100644 --- a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts +++ b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts @@ -1,10 +1,29 @@ +import { expect as jestExpect } from '@jest/globals'; import { device, expect, element, by } from 'detox'; -import { describeIfiOS, selectSingleFeatureTestsScreen } from '../../e2e-utils'; +import { IosElementAttributes } from 'detox/detox'; +import { selectSingleFeatureTestsScreen, describeIfiOS } from '../../e2e-utils'; +import isVersionEqualOrHigherThan from '../../helpers/isVersionEqualOrHigherThan'; +const { + getIOSVersionNumber, +} = require('../../../../scripts/e2e/ios-devices.js'); async function tapOptionButton(optionText: string) { await element(by.text(optionText)).tap(); } +async function getTabBarItemFrameX(tabLabel: string): Promise { + const attrs = (await element(by.label(tabLabel)) + .atIndex(0) + .getAttributes()) as + | IosElementAttributes + | { elements: IosElementAttributes[] }; + const frame = 'frame' in attrs ? attrs.frame : attrs.elements[0]?.frame; + if (!frame) { + throw new Error(`Could not read frame for tab labelled "${tabLabel}"`); + } + return frame.x; +} + async function tapSystemTitleOption() { await element(by.text('system')).atIndex(0).tap(); } @@ -12,13 +31,22 @@ async function tapSystemTitleOption() { async function tapSystemIconOption() { await element(by.text('system')).atIndex(1).tap(); } +function isIOSVersionAtLeast(version: string): boolean { + return ( + device.getPlatform() === 'ios' && + isVersionEqualOrHigherThan(getIOSVersionNumber(), version) + ); +} -describeIfiOS('Tab Bar System Item (iOS 26)', () => { +const tabBarButtonType = isIOSVersionAtLeast('26.0') + ? '_UITabButton' + : 'UITabBarButton'; + +describeIfiOS('Tab Bar System Item', () => { beforeAll(async () => { await device.reloadReactNative(); await selectSingleFeatureTestsScreen('Tabs', 'test-tabs-system-item-ios'); }); - describe('Static System Item tab', () => { it('should display the tab bar with system item titles and icons', async () => { await expect(element(by.type('UITabBar'))).toBeVisible(); @@ -80,6 +108,11 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { await expect(element(by.id('icon-selector'))).toBeVisible(); await expect(element(by.text('house'))).toBeVisible(); await expect(element(by.text('heart'))).toBeVisible(); + + await expect(element(by.id('custom-tab-item'))).toHaveLabel('Favorites'); + await expect( + element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), + ).toExist(); }); }); @@ -93,7 +126,9 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { ), ).toExist(); - await expect(element(by.label('History'))).toExist(); + await expect( + element(by.label('History').and(by.type(tabBarButtonType))).atIndex(0), + ).toExist(); await expect( element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), ).toExist(); @@ -103,6 +138,7 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { }); it('should update the tab bar item when switching to search systemItem', async () => { + const frameXBeforeSearch = await getTabBarItemFrameX('History'); await tapOptionButton('search'); await expect( element( @@ -117,9 +153,24 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { await expect( element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), ).not.toExist(); + + if (!isIOSVersionAtLeast(`26.0`)) { + await expect( + element(by.label('Search').and(by.type(tabBarButtonType))), + ).toBeVisible(); + } + + const frameXAfterSearch = await getTabBarItemFrameX('Search'); + + if (isIOSVersionAtLeast(`26.0`)) { + jestExpect(frameXAfterSearch).toBeGreaterThan(frameXBeforeSearch); + } else { + jestExpect(frameXAfterSearch).toEqual(frameXBeforeSearch); + } }); it('should update the tab bar item when switching to favorites systemItem', async () => { + const frameXBeforeFavorites = await getTabBarItemFrameX('Search'); await tapOptionButton('favorites'); await expect( element( @@ -134,11 +185,19 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { await expect( element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), ).not.toExist(); + + const frameXAfterFavorites = await getTabBarItemFrameX('Favorites'); + + if (isIOSVersionAtLeast(`26.0`)) { + jestExpect(frameXAfterFavorites).toBeLessThan(frameXBeforeFavorites); + } else { + jestExpect(frameXAfterFavorites).toEqual(frameXBeforeFavorites); + } }); }); describe('Runtime Config tab — title override cycling', () => { - it('should update the tab bar item label after tapping custom title', async () => { + it('should update the tab bar item label when switching to custom title', async () => { await expect(element(by.text("systemItem: 'favorites'"))).toBeVisible(); await expect(element(by.text('title: undefined (system)'))).toBeVisible(); await expect( @@ -150,40 +209,68 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { element(by.id('config-title').and(by.label('title: "Custom"'))), ).toBeVisible(); - await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), - ).toExist(); + if (isIOSVersionAtLeast(`26.0`)) { + await expect( + element(by.label('Custom').and(by.type(tabBarButtonType))), + ).toExist(); + await expect( + element(by.label('Favorites').and(by.type(tabBarButtonType))), + ).not.toExist(); + } else { + await expect( + element(by.label('Custom').and(by.type(tabBarButtonType))).atIndex(0), + ).toBeVisible(); + } + await expect( element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), ).toExist(); }); - it('should hide the tab bar item label after tapping hidden title', async () => { + it('should hide the tab bar item label when switching to hidden title', async () => { await tapOptionButton('hidden'); await expect( element(by.id('config-title').and(by.label("title: '' (hidden)"))), ).toBeVisible(); + await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), + element(by.label('Custom').and(by.type(tabBarButtonType))), ).not.toExist(); - await expect( - element(by.label('favorite').and(by.type('_UITabButton'))), - ).toExist(); + + if (isIOSVersionAtLeast(`26.0`)) { + await expect( + element(by.label('favorite').and(by.type(tabBarButtonType))), + ).toExist(); + } else { + await expect( + element(by.label('').and(by.type('UITabBarButtonLabel'))), + ).toExist(); + } + await expect( element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), ).toExist(); }); - it('should update the tab bar item label after restoring system title', async () => { + it('should update the tab bar item label when restoring system title', async () => { await tapSystemTitleOption(); await expect( element( by.id('config-title').and(by.label('title: undefined (system)')), ), ).toBeVisible(); - await expect( - element(by.label('Favorites').and(by.type('_UITabButton'))), - ).toExist(); + if (isIOSVersionAtLeast(`26.0`)) { + await expect( + element(by.label('Favorites').and(by.type(tabBarButtonType))), + ).toExist(); + } else { + await expect( + element(by.label('Favorites').and(by.type(tabBarButtonType))).atIndex( + 0, + ), + ).toBeVisible(); + } + await expect( element(by.id('star.fill').and(by.label('favorite'))).atIndex(0), ).toExist(); @@ -191,7 +278,7 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { }); describe('Runtime Config tab — icon override cycling', () => { - it('should update tab bar item icon after tapping house icon', async () => { + it('should update tab bar item icon when switching to house icon', async () => { await tapOptionButton('house'); await expect( element(by.id('config-icon').and(by.label("icon: custom 'house'"))), @@ -226,7 +313,7 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { ).toExist(); }); - it('should update tab bar item icon after tapping heart icon', async () => { + it('should update tab bar item icon when switching to heart icon', async () => { await tapOptionButton('heart'); await expect( element(by.id('config-icon').and(by.label("icon: custom 'heart'"))), @@ -238,7 +325,7 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { await expect(element(by.id('house').and(by.label('home')))).not.toExist(); }); - it('should update tab bar item icon after restoring system icon', async () => { + it('should update tab bar item icon when restoring system icon', async () => { await tapSystemIconOption(); await expect( element( @@ -257,6 +344,8 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { describe('Runtime Config tab — combined overrides', () => { it('should update tab bar item with combined selection of search systemItem + custom title + heart icon', async () => { + const frameXBeforeSearch = await getTabBarItemFrameX('Favorites'); + await tapOptionButton('search'); await tapOptionButton('custom'); await tapOptionButton('heart'); @@ -275,7 +364,7 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { await expect(element(by.label('Search'))).not.toExist(); await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), + element(by.label('Custom').and(by.type(tabBarButtonType))), ).toExist(); await expect( element(by.id('heart').and(by.label('love'))).atIndex(0), @@ -283,11 +372,23 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { await expect( element(by.id('magnifyingglass').and(by.label('Search'))).atIndex(0), ).not.toExist(); + + const frameXAfterSearch = await getTabBarItemFrameX('Custom'); + + if (isIOSVersionAtLeast(`26.0`)) { + jestExpect(frameXAfterSearch).toBeGreaterThan(frameXBeforeSearch); + } else { + jestExpect(frameXAfterSearch).toEqual(frameXBeforeSearch); + } }); it('should navigate to the Bookmarks tab and back with combined overrides active', async () => { + const frameXBeforeSwitch = await getTabBarItemFrameX('Custom'); + await element(by.label('Bookmarks')).atIndex(0).tap(); await expect(element(by.text('Static System Item'))).toBeVisible(); + const frameXAfterSwitch = await getTabBarItemFrameX('Custom'); + jestExpect(frameXAfterSwitch).toEqual(frameXBeforeSwitch); await element(by.id('heart').and(by.label('love'))) .atIndex(0) @@ -306,7 +407,9 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { ).toBeVisible(); }); - it('should retain custom title and heart icon after switching to history systemItem', async () => { + it('should retain custom title and heart icon when switching to history systemItem', async () => { + const frameXBeforeHistory = await getTabBarItemFrameX('Custom'); + await tapOptionButton('history'); await expect( @@ -322,9 +425,12 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { ).toBeVisible(); await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), + element(by.label('Custom').and(by.type(tabBarButtonType))), ).toExist(); - await expect(element(by.label('History'))).not.toExist(); + + await expect( + element(by.label('History').and(by.type(tabBarButtonType))), + ).not.toExist(); await expect( element(by.id('heart').and(by.label('love'))).atIndex(0), @@ -332,9 +438,17 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { await expect( element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), ).not.toExist(); + + const frameXAfterHistory = await getTabBarItemFrameX('Custom'); + + if (isIOSVersionAtLeast(`26.0`)) { + jestExpect(frameXAfterHistory).toBeLessThan(frameXBeforeHistory); + } else { + jestExpect(frameXAfterHistory).toEqual(frameXBeforeHistory); + } }); - it('should fall back to system history icon when icon is set to system', async () => { + it('should fall back to system history icon when switching icon to system', async () => { await tapSystemIconOption(); await expect( @@ -353,7 +467,7 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { await expect(element(by.label('History'))).not.toExist(); await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), + element(by.label('Custom').and(by.type(tabBarButtonType))), ).toExist(); await expect( @@ -364,7 +478,7 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { ).not.toExist(); }); - it('should hide the tab bar label when title is set to hidden', async () => { + it('should hide the tab bar label when switching title to hidden', async () => { await tapOptionButton('hidden'); await expect( @@ -381,17 +495,25 @@ describeIfiOS('Tab Bar System Item (iOS 26)', () => { ), ).toBeVisible(); - await expect(element(by.label('History'))).not.toExist(); + if (isIOSVersionAtLeast(`26.0`)) { + await expect( + element(by.label('History').and(by.type(tabBarButtonType))), + ).not.toExist(); + } else { + await expect( + element(by.label('').and(by.type('UITabBarButtonLabel'))), + ).toExist(); + } await expect( element(by.id('clock.fill').and(by.label('clock'))).atIndex(0), ).toExist(); await expect( - element(by.label('Custom').and(by.type('_UITabButton'))), + element(by.label('Custom').and(by.type(tabBarButtonType))), ).not.toExist(); }); - it('should restore the system localized title when title is set to system', async () => { + it('should restore the system localized title when switching title to system', async () => { await tapSystemTitleOption(); await expect( diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md index 29bf36e0c7..116fc1cc28 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-system-item-ios/scenario.md @@ -18,17 +18,19 @@ smoke test. ## E2E test -Incomplete: Automation was implemented for the iOS 26 simulator and covers steps 1–19, -but not in full scope - see the list below. +Incomplete: Automation covers steps 1–19, but not in full scope - see the +list below. A single suite runs on both iOS versions, with version-specific +conditions where behavior diverges: the tab bar button class name resolves +dynamically (UITabBarButton on iOS 18 and lower vs. _UITabButton on iOS 26), +and the search item is asserted differently because it renders differently +(visible label on iOS 18 vs. detached/no-label on iOS 26). Not automated: - Validating the differences between icon and selectedIcon. -- Checking systemItem: 'search' tab bar item detached state and label hiding. +- Checking visual icon and label changes. - The "hidden title" option is validated, but indirectly; it should be verified manually. - Steps 20-23 (orientation changes). -- iOS 18 is not supported: UI element identifiers differ from iOS 26 - (e.g. UITabBarButton on iOS 18 vs. _UITabButton on iOS 26), which causes tests to fail. ## Prerequisites From 484046b51262108331dba271d5af1633a5f3dbad Mon Sep 17 00:00:00 2001 From: lkuchno Date: Thu, 11 Jun 2026 14:24:19 +0200 Subject: [PATCH 22/22] change reference label to switch tabs --- .../tabs/test-tabs-system-item-ios.e2e.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts index 2801f52a73..1b4e0a4459 100644 --- a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts +++ b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts @@ -389,10 +389,7 @@ describeIfiOS('Tab Bar System Item', () => { await expect(element(by.text('Static System Item'))).toBeVisible(); const frameXAfterSwitch = await getTabBarItemFrameX('Custom'); jestExpect(frameXAfterSwitch).toEqual(frameXBeforeSwitch); - - await element(by.id('heart').and(by.label('love'))) - .atIndex(0) - .tap(); + await element(by.label('Custom')).atIndex(0).tap(); await expect( element(