id | title |
---|---|
testing-library |
Migrating from Testing Library |
This guide describes migration to Playwright's Experimental Component Testing from DOM Testing Library, React Testing Library, Vue Testing Library and Svelte Testing Library.
:::note
If you use DOM Testing Library in the browser (for example, you bundle end-to-end tests with webpack), you can switch directly to Playwright Test. Examples below are focused on component tests, but for end-to-end test you just need to replace await mount
with await page.goto('http://localhost:3000/')
to open the page under test.
:::
Testing Library | Playwright |
---|---|
screen | page and component |
queries | locators |
async helpers | assertions |
user events | actions |
await user.click(screen.getByText('Click me')) |
await component.getByText('Click me').click() |
await user.click(await screen.findByText('Click me')) |
await component.getByText('Click me').click() |
await user.type(screen.getByLabelText('Password'), 'secret') |
await component.getByLabel('Password').fill('secret') |
expect(screen.getByLabelText('Password')).toHaveValue('secret') |
await expect(component.getByLabel('Password')).toHaveValue('secret') |
screen.getByRole('button', { pressed: true }) |
component.getByRole('button', { pressed: true }) |
screen.getByLabelText('...') |
component.getByLabel('...') |
screen.queryByPlaceholderText('...') |
component.getByPlaceholder('...') |
screen.findByText('...') |
component.getByText('...') |
screen.getByTestId('...') |
component.getByTestId('...') |
render(<Component />); |
mount(<Component />); |
const { unmount } = render(<Component />); |
const { unmount } = await mount(<Component />); |
const { rerender } = render(<Component />); |
const { update } = await mount(<Component />); |
Testing Library:
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('sign in', async () => {
// Setup the page.
const user = userEvent.setup();
render(<SignInPage />);
// Perform actions.
await user.type(screen.getByLabelText('Username'), 'John');
await user.type(screen.getByLabelText('Password'), 'secret');
await user.click(screen.getByRole('button', { name: 'Sign in' }));
// Verify signed in state by waiting until "Welcome" message appears.
expect(await screen.findByText('Welcome, John')).toBeInTheDocument();
});
Line-by-line migration to Playwright Test:
const { test, expect } = require('@playwright/experimental-ct-react'); // 1
test('sign in', async ({ mount }) => { // 2
// Setup the page.
const component = await mount(<SignInPage />); // 3
// Perform actions.
await component.getByLabel('Username').fill('John'); // 4
await component.getByLabel('Password').fill('secret');
await component.getByRole('button', { name: 'Sign in' }).click();
// Verify signed in state by waiting until "Welcome" message appears.
await expect(component.getByText('Welcome, John')).toBeVisible(); // 5
});
Migration highlights (see inline comments in the Playwright Test code snippet):
- Import everything from
@playwright/experimental-ct-react
(or -vue, -svelte) for component tests, or from@playwright/test
for end-to-end tests. - Test function is given a
page
that is isolated from other tests, andmount
that renders a component in this page. These are two of the useful fixtures in Playwright Test. - Replace
render
withmount
that returns a component locator. - Use locators created with [
method: Locator.locator
] or [method: Page.locator
] to perform most of the actions. - Use assertions to verify the state.
All queries like getBy...
, findBy...
, queryBy...
and their multi-element counterparts are replaced with component.getBy...
locators. Locators always auto-wait and retry when needed, so you don't have to worry about choosing the right method. When you want to do a list operation, e.g. assert a list of texts, Playwright automatically performs multi-element operations.
Playwright includes assertions that automatically wait for the condition, so you don't usually need an explicit waitFor
/waitForElementToBeRemoved
call.
// Testing Library
await waitFor(() => {
expect(getByText('the lion king')).toBeInTheDocument();
});
await waitForElementToBeRemoved(() => queryByText('the mummy'));
// Playwright
await expect(page.getByText('the lion king')).toBeVisible();
await expect(page.getByText('the mummy')).toBeHidden();
When you cannot find a suitable assertion, use expect.poll
instead.
await expect.poll(async () => {
const response = await page.request.get('https://api.example.com');
return response.status();
}).toBe(200);
You can create a locator inside another locator with [method: Locator.locator
] method.
// Testing Library
const messages = document.getElementById('messages');
const helloMessage = within(messages).getByText('hello');
// Playwright
const messages = component.getByTestId('messages');
const helloMessage = messages.getByText('hello');
Once you're on Playwright Test, you get a lot!
- Full zero-configuration TypeScript support
- Run tests across all web engines (Chrome, Firefox, Safari) on any popular operating system (Windows, macOS, Ubuntu)
- Full support for multiple origins, (i)frames, tabs and contexts
- Run tests in isolation in parallel across multiple browsers
- Built-in test artifact collection
You also get all these ✨ awesome tools ✨ that come bundled with Playwright Test:
- Visual Studio Code integration
- UI mode for debugging tests with a time travel experience complete with watch mode.
- Playwright Inspector
- Playwright Test Code generation
- Playwright Tracing for post-mortem debugging
Learn more about Playwright Test runner: