Browse Source

playwright: fix TOTP setup flow against bundled web vault

The `activateTOTP` helper had three issues against the current
bundled web vault (v2026.4.1):

1. The master-password reprompt's Continue button click was racing
   form validation: the form uses Angular's `updateOn: 'blur'` so
   clicking immediately after `fill()` saw an "invalid" form and the
   click was silently no-op. Submitting via `mpInput.press('Enter')`
   triggers the form's `ngSubmit` directly, which validates and
   submits in one go.

2. `getByLabel('Key').innerText()` was ambiguous: the same page has
   a `<bit-svg aria-label="Yubico OTP security key">` providers entry
   whose accessible name contains "Key" as a substring. Strict-mode
   violation on Firefox. Anchor with `{ exact: true }` to pick only
   the `<code aria-label="Key">` element holding the base32 secret.

3. After clicking "Turn on", the original code was effectively a
   no-op (`await page.getByRole('heading', { name: 'Turned on' })`
   creates a Locator without awaiting visibility), then clicked the
   "Close" button which is `bit-aria-disable=true` until the dialog
   finishes its transition. Chromium happens to tolerate the early
   click; Firefox doesn't. Wait for `networkidle` after Turn on so
   the activation request completes; drop the Close click because
   the dialog auto-closes on success on this web vault.

`disableTOTP` got the same reprompt-via-Enter fix for parity.

Verified empirically: with these changes, `login.spec.ts`'s
`Authenticator 2fa` test passes on Firefox against bundled
web-vault v2026.4.1.
pull/7248/head
Zaid Marji 3 weeks ago
parent
commit
c02ced432d
  1. 27
      playwright/tests/setups/2fa.ts

27
playwright/tests/setups/2fa.ts

@ -11,16 +11,27 @@ export async function activateTOTP(test: Test, page: Page, user: { name: string,
await page.getByRole('link', { name: 'Security' }).click();
await page.getByRole('link', { name: 'Two-step login' }).click();
await page.locator('bit-item').filter({ hasText: /Authenticator app/ }).getByRole('button').click();
await page.getByLabel('Master password').fill(user.password);
await page.getByRole('button', { name: 'Continue' }).click();
const mpInput = page.getByLabel('Master password');
await mpInput.fill(user.password);
// Submit via Enter — Angular form validation can race a click on
// the Continue button immediately after fill on the current
// bundled web vault.
await mpInput.press('Enter');
const secret = await page.getByLabel('Key').innerText();
// `getByLabel('Key')` alone is ambiguous: the providers list also
// has a Yubico SVG with aria-label "Yubico OTP security key" that
// matches "Key" via substring. Anchor with exact match.
const secret = (await page.getByLabel('Key', { exact: true }).innerText()).replace(/\s+/g, '');
let totp = new OTPAuth.TOTP({ secret, period: 30 });
await page.getByLabel(/Verification code/).fill(totp.generate());
await page.getByRole('button', { name: 'Turn on' }).click();
await page.getByRole('heading', { name: 'Turned on', exact: true });
await page.getByLabel('Close').click();
// Wait for the activation request to complete. The current
// bundled web vault uses an asynchronous Turn-on flow; we don't
// try to assert the exact success-heading text (it varies across
// vault versions) — instead we wait for network to settle, then
// the dialog closes itself.
await page.waitForLoadState('networkidle');
return totp;
})
@ -33,9 +44,9 @@ export async function disableTOTP(test: Test, page: Page, user: { password: stri
await page.getByRole('link', { name: 'Security' }).click();
await page.getByRole('link', { name: 'Two-step login' }).click();
await page.locator('bit-item').filter({ hasText: /Authenticator app/ }).getByRole('button').click();
await page.getByLabel('Master password').click();
await page.getByLabel('Master password').fill(user.password);
await page.getByRole('button', { name: 'Continue' }).click();
const mpInput = page.getByLabel('Master password');
await mpInput.fill(user.password);
await mpInput.press('Enter');
await page.getByRole('button', { name: 'Turn off' }).click();
await page.getByRole('button', { name: 'Yes' }).click();
await utils.checkNotification(page, 'Two-step login provider turned off');

Loading…
Cancel
Save