Biometrics
Test Face ID, Touch ID and Android fingerprint unlocks against the real system prompt from a spec - enroll, match, fail, and the biometrics config key.
A spec controls the simulator's biometric sensor, so the real LAContext / BiometricPrompt flow runs, with no app-side bypass.
import { expect, test } from "@delacour/warden-e2e";
test("unlocks with a matching face", async ({ app, device, screen }) => {
await device.biometrics.enroll();
await app.open();
await screen.getByTestId("unlock").tap();
await device.alert("accept");
await device.biometrics.match();
await expect(screen.getByText("Unlocked")).toBeVisible();
});| Member | Does |
|---|---|
device.biometrics.status() | { enrolled, modality } without changing anything |
enroll() / unenroll() | iOS only |
match() / fail() | present a matching / non-matching face or finger to the pending prompt. fail() does not end the prompt: iOS shows a "Face Not Recognized" alert (Try Face ID Again / Cancel) and Android BiometricPrompt shows "Not recognized"; the app only hears back (user_cancel) when the prompt is cancelled (device.alert({ button: "Cancel" }) on iOS). A presentation sent just as the sheet appears can be dropped, so send it again when nothing changed |
Each returns BiometricState: { enrolled: boolean; modality: "face" | "fingerprint" }. Android enroll / unenroll throw ACTION_FAILED (driver E_UNSUPPORTED).
Enrolled devices: biometrics: true
export default defineConfig({
biometrics: true,
e2e: { login: { biometrics: false } },
});biometrics sits at the top level, on an e2e.<suite> and on a batches.<preset> (default false; the suite or preset beats the top level). --biometrics on e2e and batch turns it on for one run. Such devices get the profile <profile>+bio: their own pool and golden, never mixed with plain devices. How each platform enrolls them: Biometrics on a simulator.
Practice
- Enroll before the app is installed or launched: a new enrollment invalidates keys bound to the current biometry set.
- The first prompt per iOS app shows "Allow X to use Face ID?": answer it with
device.alert("accept")beforematch(). - Never add an app-side bypass; the point is to run the real prompt.
Errors and fixes are in Debugging. By hand with warden ui biometrics: Biometrics on a simulator.
Clock
Test trials, expiries and schedules without waiting, by moving the device's wall clock forward, to an instant or freezing it from a spec or a suite.
Permissions
Grant, deny or unset the app's privacy permissions from a spec or a suite before it launches, so the test reaches the screen behind the prompt, or the denied path.