mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-14 17:43:04 +00:00
test(miss-53): gate WebAuthn ceremonies on authenticator readiness
The app's biometrics probe is one-shot per island mount: when isUserVerifyingPlatformAuthenticatorAvailable() resolves before the CDP virtual authenticator answers, the enrollment form / biometric button never mount and no wait flips them (prod-build CI flake). New expectCampaignBiometricsReady(page) polls the platform answer BEFORE the surface whose island probes it; adopted at both ceremony entries in campaignWebAuthn (verified green locally). Conventions pin: e2eNavigationConventions fails the build when a virtual-authenticator spec skips the readiness gate.
This commit is contained in:
parent
d28ecb831a
commit
5c59793e4c
2 changed files with 24 additions and 2 deletions
|
|
@ -2,7 +2,7 @@ import type { BrowserContext, Page } from '@playwright/test'
|
|||
|
||||
import { CAMPAIGN_SESSION_TTL_LONG } from '../../src/lib/campaignSessionTtl.js'
|
||||
import { CAMPAIGN_BIOMETRICS_PROMPT_DISMISSED_KEY } from '../../src/utilities/campaignBiometricsPrompt.js'
|
||||
import { expect, test } from './fixtures/campaignE2EFixtures.js'
|
||||
import { expect, expectCampaignBiometricsReady, test } from './fixtures/campaignE2EFixtures.js'
|
||||
|
||||
/**
|
||||
* Biometric login (B40) end to end, against a **virtual authenticator** driven
|
||||
|
|
@ -68,8 +68,10 @@ test('enrolls a passkey, signs in with it, then revokes it', async ({
|
|||
await addVirtualAuthenticator(context, page)
|
||||
|
||||
// 1. Enrollment is authenticated: the passkey card only exists behind a
|
||||
// password login.
|
||||
// password login. The platform answer is gated BEFORE the profile island
|
||||
// runs its one-shot probe (miss #53).
|
||||
await campaign.login(page, email, password)
|
||||
await expectCampaignBiometricsReady(page)
|
||||
await page.goto('/campanha/perfil')
|
||||
|
||||
await expect(page.getByText('Nenhum aparelho cadastrado ainda.')).toBeVisible()
|
||||
|
|
@ -86,6 +88,7 @@ test('enrolls a passkey, signs in with it, then revokes it', async ({
|
|||
// the "remember me" opt-in, so there is no checkbox in this path.
|
||||
await context.clearCookies()
|
||||
await page.goto('/campanha/login')
|
||||
await expectCampaignBiometricsReady(page)
|
||||
const biometricButton = page.getByRole('button', { name: 'Entrar com digital ou Face ID' })
|
||||
await expect(biometricButton).toBeVisible()
|
||||
|
||||
|
|
|
|||
|
|
@ -474,6 +474,25 @@ export const checkRadixWhenHydrated = async (page: Page, label: string) => {
|
|||
}).toPass({ timeout: 15_000 })
|
||||
}
|
||||
|
||||
/**
|
||||
* Miss #53: the app's biometrics probe is ONE-SHOT per island mount
|
||||
* (`useCampaignBiometricsAvailable`) — if
|
||||
* `isUserVerifyingPlatformAuthenticatorAvailable()` resolves before the CDP
|
||||
* virtual authenticator is ready, the enrollment form / biometric button
|
||||
* never mount and no amount of waiting flips them. Gate the ceremony on the
|
||||
* platform answer BEFORE navigating into the surface whose island probes it,
|
||||
* so the race is decided before the app even asks. Requires a loaded page
|
||||
* (secure context) — call after `campaign.login` / `page.goto`.
|
||||
*/
|
||||
export const expectCampaignBiometricsReady = async (page: Page): Promise<void> => {
|
||||
await expect(async () => {
|
||||
const available = await page.evaluate(
|
||||
() => window.PublicKeyCredential?.isUserVerifyingPlatformAuthenticatorAvailable?.() ?? false,
|
||||
)
|
||||
expect(available).toBe(true)
|
||||
}).toPass({ timeout: 15_000 })
|
||||
}
|
||||
|
||||
type CampaignE2EFixture = {
|
||||
baseURL: string
|
||||
fixtures: CampaignE2EOwnership
|
||||
|
|
|
|||
Loading…
Reference in a new issue