diff --git a/FabricExample/e2e/single-feature-tests/tabs/test-tabs-general-appearance-android.e2e.ts b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-general-appearance-android.e2e.ts new file mode 100644 index 0000000000..a277f1bbad --- /dev/null +++ b/FabricExample/e2e/single-feature-tests/tabs/test-tabs-general-appearance-android.e2e.ts @@ -0,0 +1,234 @@ +import { device, expect, element, by } from 'detox'; +import { + describeIfAndroid, + selectSingleFeatureTestsScreen, +} from '../../e2e-utils'; + +async function selectLabelVisibilityMode( + mode: 'auto' | 'selected' | 'labeled' | 'unlabeled', +) { + await element( + by.id('general-appearance-android-label-visibility-picker'), + ).tap(); + await element(by.id(`tabbaritemlabelvisibilitymode-${mode}`)).tap(); + await expect( + element(by.id('general-appearance-android-label-visibility-picker')), + ).toHaveLabel(`tabBarItemLabelVisibilityMode: ${mode}`); + await element( + by.id('general-appearance-android-label-visibility-picker'), + ).tap(); +} + +describeIfAndroid( + 'Tab Bar General Appearance (Android) - tabBarItemLabelVisibilityMode', + () => { + beforeAll(async () => { + await device.reloadReactNative(); + await selectSingleFeatureTestsScreen( + 'Tabs', + 'test-tabs-general-appearance-android', + ); + await element(by.id('general-appearance-android-tab-label')).tap(); + }); + + it('auto mode shows label only on the selected tab', async () => { + await expect( + element(by.id('general-appearance-android-label-visibility-picker')), + ).toHaveLabel('tabBarItemLabelVisibilityMode: auto'); + + await expect( + element( + by + .text('Label') + .withAncestor(by.id('general-appearance-android-tab-label')), + ), + ).toBeVisible(); + await expect( + element( + by + .text('Default') + .withAncestor(by.id('general-appearance-android-tab-default')), + ), + ).not.toExist(); + await expect( + element( + by + .text('Ripple') + .withAncestor(by.id('general-appearance-android-tab-ripple')), + ), + ).not.toExist(); + await expect( + element( + by + .text('Indicator') + .withAncestor(by.id('general-appearance-android-tab-indicator')), + ), + ).not.toExist(); + }); + + it('labeled mode makes all tab bar item titles visible', async () => { + await selectLabelVisibilityMode('labeled'); + + await expect( + element( + by + .text('Default') + .withAncestor(by.id('general-appearance-android-tab-default')), + ), + ).toBeVisible(); + await expect( + element( + by + .text('Label') + .withAncestor(by.id('general-appearance-android-tab-label')), + ), + ).toBeVisible(); + await expect( + element( + by + .text('Ripple') + .withAncestor(by.id('general-appearance-android-tab-ripple')), + ), + ).toBeVisible(); + await expect( + element( + by + .text('Indicator') + .withAncestor(by.id('general-appearance-android-tab-indicator')), + ), + ).toBeVisible(); + }); + + it('should fallback to default auto mode and persist custom label mode settings across tab switches', async () => { + await selectLabelVisibilityMode('labeled'); + await element(by.id('general-appearance-android-tab-default')).tap(); + + await expect( + element( + by + .text('Default') + .withAncestor(by.id('general-appearance-android-tab-default')), + ), + ).toBeVisible(); + await expect( + element( + by + .text('Label') + .withAncestor(by.id('general-appearance-android-tab-label')), + ), + ).not.toExist(); + await expect( + element( + by + .text('Ripple') + .withAncestor(by.id('general-appearance-android-tab-ripple')), + ), + ).not.toExist(); + await expect( + element( + by + .text('Indicator') + .withAncestor(by.id('general-appearance-android-tab-indicator')), + ), + ).not.toExist(); + + await element(by.id('general-appearance-android-tab-label')).tap(); + + await expect( + element( + by + .text('Default') + .withAncestor(by.id('general-appearance-android-tab-default')), + ), + ).toBeVisible(); + await expect( + element( + by + .text('Label') + .withAncestor(by.id('general-appearance-android-tab-label')), + ), + ).toBeVisible(); + await expect( + element( + by + .text('Ripple') + .withAncestor(by.id('general-appearance-android-tab-ripple')), + ), + ).toBeVisible(); + await expect( + element( + by + .text('Indicator') + .withAncestor(by.id('general-appearance-android-tab-indicator')), + ), + ).toBeVisible(); + }); + + it('unlabeled mode hides all tab bar item titles', async () => { + await selectLabelVisibilityMode('unlabeled'); + + await expect( + element( + by + .text('Label') + .withAncestor(by.id('general-appearance-android-tab-label')), + ), + ).not.toExist(); + await expect( + element( + by + .text('Default') + .withAncestor(by.id('general-appearance-android-tab-default')), + ), + ).not.toExist(); + await expect( + element( + by + .text('Ripple') + .withAncestor(by.id('general-appearance-android-tab-ripple')), + ), + ).not.toExist(); + await expect( + element( + by + .text('Indicator') + .withAncestor(by.id('general-appearance-android-tab-indicator')), + ), + ).not.toExist(); + }); + + it('selected mode shows label only on the selected tab', async () => { + await selectLabelVisibilityMode('selected'); + + await expect( + element( + by + .text('Label') + .withAncestor(by.id('general-appearance-android-tab-label')), + ), + ).toBeVisible(); + + await expect( + element( + by + .text('Default') + .withAncestor(by.id('general-appearance-android-tab-default')), + ), + ).not.toExist(); + await expect( + element( + by + .text('Ripple') + .withAncestor(by.id('general-appearance-android-tab-ripple')), + ), + ).not.toExist(); + await expect( + element( + by + .text('Indicator') + .withAncestor(by.id('general-appearance-android-tab-indicator')), + ), + ).not.toExist(); + }); + }, +); diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/index.tsx index d655393eca..d7481dc88d 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/index.tsx +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/index.tsx @@ -56,6 +56,7 @@ function LabelTab() { Only `tabBarItemLabelVisibilityMode` is defined.{'\n'} Labels follow the toggled value. + testID="general-appearance-android-label-visibility-picker" label="tabBarItemLabelVisibilityMode" value={labelVisibility} onValueChange={onLabelVisibilityChange} @@ -112,6 +113,7 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ Component: DefaultTab, options: { title: 'Default', + tabBarItemTestID: 'general-appearance-android-tab-default', android: { ...DEFAULT_TAB_ROUTE_OPTIONS.android, }, @@ -122,6 +124,7 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ Component: LabelTab, options: { title: 'Label', + tabBarItemTestID: 'general-appearance-android-tab-label', android: { ...DEFAULT_TAB_ROUTE_OPTIONS.android, standardAppearance: { @@ -135,6 +138,7 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ Component: RippleTab, options: { title: 'Ripple', + tabBarItemTestID: 'general-appearance-android-tab-ripple', android: { ...DEFAULT_TAB_ROUTE_OPTIONS.android, standardAppearance: { @@ -152,6 +156,7 @@ const ROUTE_CONFIGS: TabRouteConfig[] = [ Component: IndicatorTab, options: { title: 'Indicator', + tabBarItemTestID: 'general-appearance-android-tab-indicator', android: { ...DEFAULT_TAB_ROUTE_OPTIONS.android, standardAppearance: { diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/scenario.md b/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/scenario.md index 982bac3187..66e1c40406 100644 --- a/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/scenario.md +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/scenario.md @@ -11,9 +11,15 @@ animation and the persistent active indicator shape. ## E2E test -Incomplete: Not automated. Detox does not have access to color or visibility -attributes on Android views, so it is not possible to programmatically assert whether -a label is hidden or a specific color value has been applied. +Incomplete: Covers only the tabBarItemLabelVisibilityMode verification (steps 2-7). +Step 8, which involves cycling between modes, was omitted from having a dedicated +test because the preceding test cases already cover tab switching and sufficiently +validate this behavior. + +Other properties cannot be verified using Detox since it lacks access to color +or visibility attributes on Android views. Therefore, it is not possible to +programmatically assert whether a label is hidden or if a specific color value has been applied. + ## Prerequisites