·7 min read
How to Test Passkey (WebAuthn) Login in Playwright (2026)
Playwright 1.61 added a virtual authenticator. You can now test passkey login with no hardware key, in every browser. Here is a complete working test.

Published: · 3 min read
Shift-left made developers own testing. Here are the five questions every developer asks when handed Playwright, answered with code.
On this page
Your team moved testing to the left. That is the industry's word for it: shift-left. Testing that used to happen at the end, done by a separate QA team, now happens earlier, done by the people writing the code.
On my most recent project, that meant the developers. Most had never opened Playwright. None of them thought like a tester. And they still had to ship tested code every sprint.
The same five questions came in every week. This is the field guide I wish I could have handed each of them on day one. If testing just landed on your desk, start here.
The instinct is to sleep for a few seconds and hope. Do not. A sleep is a guess. Guess too short and the test is flaky. Guess too long and your suite crawls.
Playwright already waits for you. When you act on an element, it waits until that element is ready to use. Visible, stable, clickable. Then it acts.
// Fragile: a blind guess
await page.waitForTimeout(3000);
await page.locator('#save').click();
// Correct: Playwright waits for the button to be ready, then clicks
await page.getByRole('button', { name: 'Save' }).click();
Wait for the thing, never for the clock.
That is a flaky test. It is not bad luck. It means two things raced and your test did not say which one had to finish first.
The fix is not to add a sleep. The fix is to wait for the real signal. The row appears, the spinner is gone, the network call returned.
// Flaky: asserts before the row has rendered
await page.getByRole('button', { name: 'Add' }).click();
await expect(page.getByRole('row')).toHaveCount(1);
// Stable: web-first assertion retries until the state is true
await page.getByRole('button', { name: 'Add' }).click();
await expect(page.getByRole('row', { name: 'New item' })).toBeVisible();
A flaky test is not a small annoyance. It teaches the team to ignore red, and an ignored red is where real bugs ship.
Check what the user gets. Not what the console logs, not that the page loaded. The actual outcome on the screen.
// Weak: proves the page rendered, nothing more
await expect(page).toHaveURL(/checkout/);
// Strong: proves the user sees the right total
await expect(page.getByTestId('order-total')).toHaveText('$41.00');
If your assertion would still pass when the feature is broken, it is decoration, not proof.
Most slow suites log in through the UI before every single test. If you have 200 tests, that is 200 logins. Do it once.
Sign in a single time, save the session, and start every test already inside.
// One-time setup: save the signed-in state
await page.context().storageState({ path: 'auth.json' });
// Every test reuses it, no login step
test.use({ storageState: 'auth.json' });
Testing the login belongs in one test. The other 199 should assume it works.
Then it is not tested. It is remembered. A test that only passes with your leftover data and your logged-in browser is not proof anyone else can trust.
Run it clean and isolated, every time: fresh data, no shared login, no state left behind from the last run. If it passes on a machine that knows nothing about you, it is real.
A test is a claim that the software works. Your job is to make that claim honest. Wait for real signals, check what the user sees, keep each test independent, and when a test fails, read the evidence before you touch it. Playwright's trace viewer records exactly what the browser did, so you can tell a real bug from a broken test in about two minutes.
Shift-left handed you testing. Nobody handed you the manual. Now you have a start.
Anton Gulin is the AI QA Architect, the first person to claim this title on LinkedIn. He builds AI-powered test automation systems where AI agents and human engineers collaborate on quality. Former Apple SDET (Apple.com / Apple Card pre-release testing). Find him at anton.qa or on LinkedIn.
Get notified when I publish something new, and unsubscribe at any time.
·7 min read
Playwright 1.61 added a virtual authenticator. You can now test passkey login with no hardware key, in every browser. Here is a complete working test.

·6 min read
Playwright v1.59.0 ships the Screencast API, letting AI agents produce verifiable video evidence of their work. Engineers can replay agent actions with chapter markers and action annotations—no manual test replay required. Setup is three lines: start the screencast, run your agent logic, stop and save. This is the observability layer agentic workflows have been missing.
