diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 00000000..f8981dcb
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,2 @@
+ignore:
+ - "tests/**"
diff --git a/components/SettingsView.tsx b/components/SettingsView.tsx
index d294e9b1..9ed262bf 100644
--- a/components/SettingsView.tsx
+++ b/components/SettingsView.tsx
@@ -38,14 +38,17 @@ import { ViewErrorBoundary } from './ui/ViewErrorBoundary';
// --- SUB-COMPONENTS ---
+// QNBS-v3: stable data-testid lets E2E recovery navigation target a category without matching translated label text
const NavButton: FC<{
+ id: string;
icon: React.ReactNode;
label: string;
isActive: boolean;
onClick: () => void;
-}> = React.memo(({ icon, label, isActive, onClick }) => (
+}> = React.memo(({ id, icon, label, isActive, onClick }) => (
+ {/* QNBS-v3: stable data-testid lets E2E recovery navigation target this button without matching translated label text */}
setModal({ state: 'factoryReset', payload: {} })}
className="shrink-0"
>
diff --git a/components/settings/SettingsModals.tsx b/components/settings/SettingsModals.tsx
index 932c6cc2..40b9b879 100644
--- a/components/settings/SettingsModals.tsx
+++ b/components/settings/SettingsModals.tsx
@@ -138,7 +138,12 @@ export const SettingsModals: FC = () => {
setModal({ state: 'closed', payload: {} })}>
{t('common.cancel')}
- void handleFactoryReset()}>
+ {/* QNBS-v3: stable data-testid lets E2E recovery navigation target this button without matching translated label text */}
+ void handleFactoryReset()}
+ data-testid="factory-reset-confirm-button"
+ >
{t('settings.data.dangerZone.factoryReset.modalConfirm')}
diff --git a/tests/e2e/helpers.ts b/tests/e2e/helpers.ts
index 8ac3f72a..cb09a4e3 100644
--- a/tests/e2e/helpers.ts
+++ b/tests/e2e/helpers.ts
@@ -29,6 +29,21 @@ export async function clickNavItem(page: Page, name: RegExp): Promise {
await page.locator('#sidebar-mobile').getByRole('button', { name }).click();
}
+/** Locale-independent Settings navigation: same mobile-aware fallback as clickNavItem, keyed on the stable `data-tour="nav-settings"` anchor instead of translated visible text. */
+async function clickSettingsNavItem(page: Page): Promise {
+ const desktopBtn = page.locator('#sidebar [data-tour="nav-settings"]');
+ if (await desktopBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
+ await desktopBtn.click();
+ return;
+ }
+ // QNBS-v3: data-tour="nav-more" (not a translated /More/i label) so this stays locale-independent on a non-English mobile boot.
+ const moreBtn = page.locator('[data-tour="nav-more"]');
+ await expect(moreBtn).toBeVisible({ timeout: 8000 });
+ await moreBtn.click();
+ await page.locator('#sidebar-mobile').waitFor({ state: 'visible' });
+ await page.locator('#sidebar-mobile [data-tour="nav-settings"]').click();
+}
+
// QNBS-v3: Stable Writer `#writer-section-select` + option handling avoids Playwright strict-mode / native-