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