playwright + dom-testing-library
npm install @playwright-testing-library/test
🔍 Find elements in playwright like your users with queries from @testing-library/dom
[![Build Status][build-badge]][build-link]
[![Test Coverage][codecov-badge]][codecov-link]
[![Code Style][prettier-badge]][prettier-link]
[![Package Version][npm-badge]][npm-link]
[![MIT License][license-badge]][license-link]
[![Conventional Commits][conventional-commits-badge]][conventional-commits-link]
[![Maintenance][maintenance-badge]][maintenance-link]
All of your favorite user-centric querying functions from @testing-library/react and @testing-library/dom available from within Playwright!
- Test fixture for @playwright/test via @playwright-testing-library/test
- ✨ New — Locator queries fixture (locatorFixtures) ↓
- ElementHandle queries fixture (fixtures) ↓
- Standalone queries for playwright via playwright-testing-library
- ElementHandle queries (getDocument + queries) ↓
- Asynchronous waitFor assertion helper (via wait-for-expect)
``bashFor use with Playwright Test (@playwright/test)
npm install --save-dev @playwright-testing-library/test
📝 Usage
There are currently a few different ways to use Playwright Testing Library, depending on how you use Playwright. However, the recommended approach is to use the
Locator queries fixture with Playwright Test (@playwright/test).> ⚠️ The
ElementHandle query APIs were created before Playwright introduced its Locator API and will be replaced in the next major version of Playwright Testing Library. If you can't use @playwright/test at the moment, you'll need to use the ElementHandle query API, but a migration path will be provided when we switch to the new Locator APIs.$3
> 🔖 Added in 4.4.0
Using the
Locator Playwright Test (@playwright/test) fixture with @playwright-testing-library/test.#### Setup
`ts
import {test as base} from '@playwright/test'
import {
locatorFixtures as fixtures,
LocatorFixtures as TestingLibraryFixtures,
} from '@playwright-testing-library/test/fixture'const test = base.extend(fixtures)
const {expect} = test
test('my form', async ({screen, within}) => {
// Screen provides
Locator queries scoped to current Playwright Page
const formLocator = screen.getByTestId('my-form') // Scope queries to
Locator with within
// (note that this is a fixture from test, not the within import)
const emailInputLocator = within(formLocator).getByLabelText('Email') // Interact via
Locator API 🥳
await emailInputLocator.fill('email@playwright.dev')
await emailInputLocator.press('Enter') // Screen also provides Playwright's
Page API
screen.goto('/account') const emailLocator = screen.getByRole('heading', {level: 2})
// Assert via
Locator APIs 🎉
await expect(emailLocator).toHaveText('email@playwright.dev')
})
`#### Async Methods
The
findBy queries work the same way as they do in Testing Library core in that they return Promise and are intended to be used to defer test execution until an element appears on the page.`ts
test('my modal', async ({screen, within}) => {
// Here we wait for a modal to appear asynchronously before continuing
// Note: the timeout for findBy queries is configured with asyncUtilTimeout
const modalLocator = await screen.findByRole('dialog') // Once the modal is visible, we can interact with its contents and assert
await expect(modalLocator).toHaveText(/My Modal/)
await within(modalLocator).getByRole('button', {name: 'Okay'}).click()
// We can also use
queryBy methods to take advantage of Playwright's Locator auto-waiting
// See: https://playwright.dev/docs/actionability
// Note: this will use Playwright's timeout, not asyncUtilTimeout
await expect(screen.queryByRole('dialog')).toBeHidden()
})
`#### Chaining
> 🔖 Added in 4.5.0
As an alternative to the
within(locator: Locator) function you're familiar with from Testing Library, Playwright Testing Library also supports chaining queries together.All synchronous queries (
get + query) return Locator instances augmented with a .within() method (TestingLibraryLocator). All asynchronous queries (find) return a special LocatorPromise that also supports .within(). This makes it possible to chain queries, including chaining get, query and find interchangeably.> ⚠️ Note that including any
find* query in the chain will make the entire chain asynchronous##### Synchronous
`ts
test('chaining synchronous queries', async ({screen}) => {
const locator = screen.getByRole('figure').within().findByRole('img') expect(await locator.getAttribute('alt')).toEqual('Some image')
})
`##### Synchronous + Asynchronous
`ts
test('chaining synchronous queries + asynchronous queries', ({screen}) => {
// ↓↓↓↓↓ including any find* queries makes the whole chain asynchronous
const locator = await screen
.getByTestId('modal-container') // Get "modal container" or throw (sync)
.within()
.findByRole('dialog') // Wait for modal to appear (async, until asyncUtilTimeout)
.within()
.getByRole('button', {name: 'Close'}) // Get close button within modal (sync) expect(await locator.textContent()).toEqual('Close')
})
`#### Configuration
The
Locator query API is configured using Playwright's use API. See Playwright's documentation for global, project, and test.##### Global
Configuring Testing Library globally in
playwright.config.ts`ts
import type {PlaywrightTestConfig} from '@playwright/test'const config: PlaywrightTestConfig = {
use: {
// These are the defaults
testIdAttribute: 'data-testid',
asyncUtilTimeout: 1000,
asyncUtilExpectedState: 'visible',
},
}
export default config
`##### Local
Scoping Testing Library configuration to test suites or
describe blocks`ts
import {test as base} from '@playwright/test'
import {
locatorFixtures as fixtures,
LocatorFixtures as TestingLibraryFixtures,
} from '@playwright-testing-library/test/fixture'const test = base.extend(fixtures)
const {describe, expect, use} = test
// Entire test suite
use({testIdAttribute: 'data-custom-test-id'})
describe(() => {
// Specific block
use({
testIdAttribute: 'some-other-test-id',
asyncUtilsTimeout: 5000,
asyncUtilExpectedState: 'attached',
})
test('my form', async ({screen}) => {
// ...
})
})
`$3
Using the
ElementHandle Playwright Test (@playwright/test) fixture with @playwright-testing-library/test.> ⚠️ See note in Usage as you should be using the
Locator fixture if possible#### Setup
`ts
import {test as base} from '@playwright/test'
import {fixtures, within, TestingLibraryFixtures} from '@playwright-testing-library/test/fixture'const test = base.extend(fixtures)
const {expect} = test
test('my form', async ({page, queries}) => {
// Query methods are available in
test blocks
const formHandle = await queries.getByTestId('my-form') // Scope queries to an
ElementHandle with within
const emailInputHandle = await within(formHandle).getByLabelText('Email') // Interact via
ElementHandle API
await emailInputHandle.fill('email@playwright.dev')
await emailInputHandle.press('Enter') page.goto('/account')
const emailHandle = queries.getByRole('heading', {level: 2})
// Assert via
ElementHandle APIs
expect(await emailHandle.textContent()).toEqual('email@playwright.dev')
})
`#### Configuration
`ts
import {test as base} from '@playwright/test'
import {
configure,
fixtures,
within,
TestingLibraryFixtures,
} from '@playwright-testing-library/test/fixture'const test = base.extend(fixtures)
const {beforeEach, describe, expect} = test
// Global (these are the defaults)
configure({asyncUtilTimeout: 1000, testIdAttribute: 'data-testid'})
// Specific block
describe('my page', () => {
beforeEach(() => configure({asyncUtilTimeout: 5000, testIdAttribute: 'data-custom-test-id'}))
afterEach(() => configure({}))
test('my form', async ({page, queries}) => {
// ...
})
})
`$3
Using the
ElementHandle queries with Playwright (playwright) and playwright-testing-library.> ⚠️ See note in Usage as you should be using @playwright/test with the
Locator fixture if possible. The Locator queries will be made available for standalone playwright in the next major release.`ts
import {beforeAll, expect, jest, test} from '@jest/globals'
import {webkit} from 'playwright' // or 'firefox' or 'chromium'
import {getDocument, queries, within} from 'playwright-testing-library'let browser: playwright.Browser
let page: playwright.Page
beforeAll(() => {
const browser = await webkit.launch()
const page = await browser.newPage()
})
test('my form', () => {
// Get
ElementHandle for document from Page
const documentHandle = await getDocument(page) // Global query methods take document handle as the first parameter
const formHandle = await queries.getByTestId(documentHandle, 'my-form')
// Scope queries to an
ElementHandle with within
const emailInputHandle = await within(formHandle).getByLabelText('Email') // Interact via
ElementHandle API
await emailInputHandle.fill('email@playwright.dev')
await emailInputHandle.press('Enter') page.goto('/account')
const accountHandle = getDocument(page)
const emailHandle = queries.getByRole(accountHandle, 'heading', {level: 2})
// Assert via
ElementHandle APIs
expect(await emailHandle.textContent()).toEqual('email@playwright.dev')
})
`#### Configuration
`ts
import {beforeEach, afterEach, expect, jest, test} from '@jest/globals'
import {configure, getDocument, queries, within} from 'playwright-testing-library'// Global (these are the defaults)
configure({asyncUtilTimeout: 1000, testIdAttribute: 'data-testid'})
// Specific block
describe('my page', () => {
beforeEach(() => configure({asyncUtilTimeout: 5000, testIdAttribute: 'data-custom-test-id'}))
afterEach(() => configure({}))
test('my form', async ({page, queries}) => {
// ...
})
})
`🔌 API
$3
All queries from @testing-library/dom are supported.
> 📝 The
find* queries for the Locator queries return Promise which resolves when the element is found before the timeout specified via asyncUtilTimeout$3
Unique methods, not part of @testing-library/dom
> ⚠️ These only apply to the
ElementHandle queries- Get an
ElementHandle for the document
`ts
getDocument(page: playwright.Page): ElementHandle
`- Wait for an assertion (wrapper around wait-for-expect)
`ts
waitFor(
expectation: () => void | Promise,
timeout?: number,
interval?: number
): Promise<{}>
`Known Limitations
- Only
testIdAttribute and asyncUtilTimeout are supported as configuration options
- Async utilities waitForElement, waitForElementToBeRemoved and waitForDomChange are not exposed. Consider using a find* query or a Playwright built-in like Locator.waitFor().
- The fireEvent method is not exposed, use Playwright's built-ins instead.
- Assertion extensions from jest-dom are not compatible, use Playwright Test if possible.$3
getNodeText() function is currently unsupported.
- When using a function for TextMatch, the function cannot reference its closure scope
`ts
// ✅ This is supported
screen.getByText(content => content.startsWith('Foo')) // ❌ This is not supported
const startsWithFoo = (content: string) => content.startsWith('Foo')
screen.getByText(content => startsWithFoo(content))
``- pptr-testing-library
- @testing-library/dom
- jest-playwright
- expect-playwright
MIT
This project is actively maintained by engineers at
@hoverinc 😀.
[codecov-badge]: https://codecov.io/gh/hoverinc/playwright-testing-library/branch/main/graph/badge.svg
[codecov-link]: https://codecov.io/gh/hoverinc/playwright-testing-library
[conventional-commits-badge]: https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg
[conventional-commits-link]: https://conventionalcommits.org
[david-badge]: https://david-dm.org/hoverinc/playwright-testing-library.svg
[david-link]: https://david-dm.org/hoverinc/playwright-testing-library
[license-badge]: https://img.shields.io/npm/l/@hover/javascript.svg
[license-link]: https://github.com/hoverinc/hover-javascript/blob/master/LICENSE
[maintenance-badge]: https://img.shields.io/badge/maintenance-active-247ddc?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAcCAYAAACUJBTQAAAB1ElEQVRIibWWPUtdQRCGH0VNF/wCCVjYCxr/gZWdhYVgLQYbm/wACTYxxA8SSBDtbKwUbfQWkiJFAgkkmHBBY6U2CXaCGlDDG1buxePOnt17bsgD28zOzjtnZvbuRVKR1SFpVdKepEe1njOGnOWCz0q60B1lSa05/oVE2iTNSfqdCZ7lSyWB0NmkSJekeUmXJqzlayWZUJxckUUTJs23mFAjlhNjSdMHfAQ6g54hZUnDdXyN44ek7iKNH4w0PMaeX7pQ8Ox6HQkWww3Dw1hPWoAJ4BxoB4aNR5oB4APQ5vekUdITSceZDLcreyORrGPcfpEL0CBpVNJRwLmUSWLS7NbGpju8FXEteT2qR+jQ9aS3QK2XgUljjXPpRC6iLpYV4KmxRghNVy28Aqb+t4jjLbBhrAH+RcRxZSwBUiINxlIHKZE/xlIHTTlHBDwHjoDPwHtgF/gEnBnvFJVfzSrXkpYyfxKGvIu14F3ONXP1LOWmzEPjpuWl92j55XyQyDnEjRN5AbwD9gMOPkV7tAPMOJE3ZuuOFmOpjS3gGfCdQDl8fgGnGVtzwt8F7wdGqgKOvOmq4iarB3gMjAFlb78qug5MAwehIO4tKViJe4wDP4FSrgfwF/ntR8JxRSf3AAAAAElFTkSuQmCC
[maintenance-link]: https://github.com/hoverinc/playwright-testing-library#maintenance
[npm-badge]: https://img.shields.io/npm/v/playwright-testing-library
[npm-link]: https://www.npmjs.com/package/playwright-testing-library
[prettier-badge]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg?logo=prettier
[prettier-link]: https://prettierjs.org/en/download/
[build-badge]: https://github.com/hoverinc/playwright-testing-library/actions/workflows/build.yml/badge.svg
[build-link]: https://github.com/hoverinc/playwright-testing-library/actions/workflows/build.yml