// The gallery renders one story on demand and exposes two globals.window.mount = ({ id, props }) => { const Story = stories[id]; // a wrapper: fixed props, mock data, providers render(Story(props), root); // your framework, your build, your root element};window.unmount = () => clear(root);Playwright 1.62,
released July 24, rebuilds component testing around stories and galleries. The
mount() you write no longer imports your component and lets Playwright bundle it.
It navigates to a gallery page you serve and renders one story there.
What the old mount() did behind you
The deprecated experimental component testing shipped as framework-specific packages,
@playwright/experimental-ct-react, -vue, -svelte. You imported the component
into the spec and called mount(<Button label="Save" />). Playwright hoisted that
import out of the test, handed it to a Vite server it configured for you, and rendered
the component in a browser it controlled.
The convenience carried a cost. Your component ran inside Playwright’s bundle, not the build your application ships, so a provider, a global stylesheet, or a Vite plugin your real app relies on had to be re-declared in Playwright’s config. The import-hoisting rules tripped anyone who put a helper in the wrong scope. Each framework needed its own package to stay current with that framework’s compiler.
Stories and galleries
1.62 hands the render back to you. You write a story: a small wrapper that pins the
component into one scenario, with fixed props, mock data, the providers it needs, and
recorded callbacks. You serve a gallery page at testOptions.baseURL that knows how
to render any story into a root element. The gallery exposes two functions on window,
mount({ id, props }) and unmount(), and it renders with your framework and your
build.
The mount fixture drives that page:
test('expands on click', async ({ mount }) => { // mount() navigates to the gallery, then calls window.mount for one story id. const component = await mount('Expandable/Collapsed'); await component.getByRole('button', { name: 'Show more' }).click(); await expect(component.getByTestId('body')).toBeVisible();
// Re-render the same story with new props, no page reload. await component.update({ open: true });});Each call to mount() navigates to baseURL and calls window.mount() with the story
id, so a mount is a full page navigation. Two tests never share a rendered tree.
mount() returns a Locator scoped to
the story’s root element, so you scope queries from component, not from page, and a
second story on the page cannot leak into your assertions. Pass the story type as a
template argument and the props you send get type-checked against the component.
update(props) re-renders the same story with new props inside the test, and
unmount() tears it down.
The trade you make is real work for real fidelity. You own the gallery page, which is a route in your app that imports a story map. In return the component runs against your build, your bundler config, and your providers, and Playwright drops the per-framework CT package. You test the component through a page you wrote, with the tools you ship.
AbortSignal cancels a running action
Most operations and web-first assertions now take a signal option that accepts an
AbortSignal. Abort the
controller and the click, navigation, wait, or assertion stops:
const controller = new AbortController();page.on('dialog', () => controller.abort()); // bail the moment a modal steals focusawait expect(page.getByTestId('status')).toHaveText('Ready', { signal: controller.signal,});The signal runs alongside the timeout. A signal leaves the default timeout in place;
pass timeout: 0 to lean on the signal alone.
Also in 1.62
Screenshots gain WebP. expect(page).toHaveScreenshot() and the locator variant store
their snapshots in WebP, and page.screenshot() and locator.screenshot() accept
webp as a type. Quality 100, the default, stays lossless; a lower value switches
to lossy compression and a smaller file for the snapshots you keep in the repo.
Reporters gain a filtering hook. reporter.preprocess() runs after Playwright resolves
the config and lets a reporter mark individual tests skipped, excluded, fixed, or failing
before the run starts, so a reporter that reads flake history can quarantine a test
without a config edit.
Retries gain a strategy. testConfig.retryStrategy defaults to 'immediate', which
retries a failed test as soon as a worker frees up. Set it to 'isolated' and Playwright
holds every retry to the end and runs them in a single worker, away from the parallel
load that a resource-contention flake needs to reproduce.
The bundled browsers move to Chromium 151, Firefox 153, and WebKit 26.5, and Debian 11 drops off the support list.
You test a component by serving a page and driving it, the same way you drive any other page in the suite.
If your suite ran the experimental CT packages, 1.62 is the migration: write the gallery
route, move each mounted component into a story, and point mount() at the id.