Browser automation: Playwright and browser extensions

How a program drives a real browser through WebDriver and the DevTools Protocol, how Playwright is built and why it waits for you, how to find elements by role and accessible name, how to stop flaky tests, how a Manifest V3 extension records clicks, and how to keep a test browser away from private networks.

In short

Browser automation means a program, not a person, drives a real browser: it opens pages, clicks, types and checks what appears. Tools talk to the browser through a protocol - WebDriver or the Chrome DevTools Protocol - and Playwright wraps that protocol in an API that waits for the page by itself. A browser extension works from the other side, inside the person's own browser, which is how a recorder can watch real clicks. In {{brand}}, UI Tester records steps through an extension and replays them on the server with Playwright, in a browser that can only navigate to the site under test and can never reach private networks.

After this lesson you can Explain how WebDriver and the DevTools Protocol move a click into a browser and choose between Selenium, Cypress, Puppeteer and Playwright; write selectors and tests that survive a redesign and do not flake; explain how a Manifest V3 extension records events; and keep a server-side test browser away from private networks.

Words you need

How a program drives a browser

Every automation tool sits in one of two places. Most sit outside the browser and send commands through a protocol: Selenium, Puppeteer and Playwright. Cypress sits inside the page, running the test code next to the application. The outside tools differ mainly in which protocol they speak, and that choice decides speed and how well they know what the page is doing.

Cypress is the odd one out Cypress runs your test code inside the browser, in the same loop as the application, with a proxy in front of the page. That gives excellent debugging and automatic retries, but the test lives in one tab, crossing origins needs a special command with limits, and it must be written in JavaScript or TypeScript.

The options in the market

What {{brand}} chose, and why

{{brand}} replays recorded steps on its own servers, unattended, for many workspaces at once. Three things mattered most: a clean, isolated session for every run, waiting that is built in rather than written into every step, and finding elements the way a person does. Playwright gives all three, and it has a first-class .NET library, which fits a .NET backend.

The trade-off accepted A real browser per run costs far more CPU and memory than an HTTP request. {{brand}} limits concurrent runs and meters browser time so one busy workspace cannot starve the others.

Another tool is the right one in other situations: Selenium when you need Safari on real devices or already run a Selenium Grid; Cypress for a front-end team testing its own app in JavaScript; Puppeteer for a small Chrome-only script, such as turning pages into PDFs. {{brand}}'s own team also uses Playwright outside the product, to walk the app and record Help pictures and clips - the clip at the top of this lesson was recorded with Playwright's video feature.

Inside Playwright: browser, context, page, locator

Launch once, create contexts often Starting a browser is the expensive part. Keep one browser per worker process and give every test or run its own context: you get full isolation for the price of a few milliseconds.

Auto-waiting: why Playwright tests flake less

The most common bug in browser tests is acting too early: clicking a button that is not there yet, or reading text before it changed. A fixed sleep is either too short, so the test flakes, or too long, so the suite crawls. Playwright replaces guesses with checks: before every action it waits until the element is ready, and assertions retry until they pass or time out.

Web-first assertions An assertion on a locator, such as toHaveText, re-checks the page until it passes or times out. An assertion on a plain value checks once, at whatever moment the line runs. {{brand}}'s check steps work the first way: they re-check many times a second until the step's time runs out.

Seeing what happened: screenshots, video, traces

{{brand}} keeps a screenshot after every step, a video of the run, and on a failure the page address and an excerpt of the page around the element, so a person can see why a step failed without running it again. A common pattern for sites that need a sign-in is to sign in once in a separate context and reuse the saved storage state for the tests.

Robust selectors and the accessibility tree

Next to the DOM, every browser builds an accessibility tree for screen readers. Each node has a role (button, link, textbox, heading, row), an accessible name and a state such as checked or disabled. A selector built from role and name describes an element the way a person sees it, so it survives restyling, new class names and moved containers.

A sensible ranking for a recorder, best first. Another tool may order it a little differently, but the idea is the same: contracts and meaning first, structure last.

When a locator matches several elements - three rows, each with a Delete button - a good recorder adds an anchor: a nearby element that is unique, used to scope the locator.

Self-healing selectors When the first locator fails but a later one matches, a good tool reports the fallback as a healed result and lets a person decide whether to adopt it. A tool that silently rewrites tests can accept a real regression without anyone noticing.

Flaky tests: why they happen and how to stop them

A flaky test passes and fails on the same code. Each flake teaches the team to ignore red, and soon a real failure is ignored too. Almost every flake has one of a handful of causes, and each cause has a known cure.

{{brand}} applies one flakiness rule across its test tools, so flaky means the same everywhere.

Retries hide flakes An automatic retry turns red into green but keeps the cause. Use retries to collect evidence - a trace of the second attempt - and track how often a retry was needed. A test that needs retries is a flaky test.

Browser extensions with Manifest V3

A browser extension is a package of HTML, JavaScript and a manifest.json that the browser loads with extra powers: it can see tabs, run code in pages and capture what is on screen. Manifest V3 is the current format in Chrome and Edge, and Firefox and Safari support it too; Chrome has switched the older Manifest V2 off.

The service worker dies Chrome stops an idle extension service worker after about 30 seconds. A setTimeout may never fire and a queue kept only in memory is lost. Write state to chrome.storage.session (in memory, cleared when the browser closes) or chrome.storage.local, and schedule later work with chrome.alarms, which wake the worker again.

How a recorder captures events

A recorder is a content script that watches a person and writes down their intent, not every raw event. Typing a name produces dozens of input events; the test needs one step: fill the Name field with Ada. The hard parts are deciding what counts as one action, describing the element so it can be found again, and never writing down a password.

Why an extension and not an iframe A web page cannot look inside another site's frames, and most sites refuse to be framed at all. An extension can follow the person across pages and origins - the reason Selenium IDE and many commercial recorders are extensions, while Chrome's DevTools Recorder gets the same reach by being built into the browser.

Screenshots can still show secrets Masking protects what was typed. A screenshot shows whatever was on screen - an email address, an account number - so review a recorded test before sharing it.

Isolating test browsers on the network

A browser on your server is a powerful client inside your network. If a page under test redirects it to http://169.254.169.254/ - the cloud metadata address on most clouds - or to http://localhost:6379, it will happily go. That is SSRF, and a browser makes it easier than plain HTTP code because pages load images, frames, scripts and sockets on their own.

Below are the typical ways a test browser escapes; a real setup covers each one. A request hook (Playwright calls it routing) sees each request the page makes and can allow or abort it. An egress proxy is a program that all outgoing connections of the browser must pass through, so it can check every one of them.

A request hook is not enough on its own Routing sees the request the page made, but the browser follows redirects and resolves names by itself. Only a check at the level of each connection - a proxy every connection of the run must pass - sees every hop. {{brand}} applies several layers of this kind to every run, so no single check is trusted on its own.

Try it yourself: record, replay and harden one test

Common mistakes

Interview questions

Related