• Stars
    star
    248
  • Rank 163,560 (Top 4 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 4 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

πŸ” Find elements in Playwright with queries from Testing Library

😲 Heads up β€” Playwright introduced native Testing Library queries in version 1.27.

πŸ’¬ #558 ← We're discussing what this means for Playwright Testing Library in this issue. You can find a more detailed comparison of the new Playwright API and this library here. Please ask any questions you may have or share thoughts and suggestions!




playwright-testing-library

πŸ” Find elements in playwright like your users with queries from @testing-library/dom

Build Status Test Coverage Code Style Package Version
MIT License Conventional Commits Maintenance


πŸŽ› Features

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)

🌱 Installation

# For use with Playwright Test (@playwright/test)
npm install --save-dev @playwright-testing-library/test

# For use with Playwright (playwright)
npm install --save-dev playwright-testing-library

πŸ“ 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.

Playwright Test Fixture

πŸ”– Added in 4.4.0

Using the Locator Playwright Test (@playwright/test) fixture with @playwright-testing-library/test.

Setup

import {test as base} from '@playwright/test'
import {
  locatorFixtures as fixtures,
  LocatorFixtures as TestingLibraryFixtures,
} from '@playwright-testing-library/test/fixture'

const test = base.extend<TestingLibraryFixtures>(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 protected]')
  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 protected]')
})

Async Methods

The findBy queries work the same way as they do in Testing Library core in that they return Promise<Locator> and are intended to be used to defer test execution until an element appears on the page.

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
test('chaining synchronous queries', async ({screen}) => {
  const locator = screen.getByRole('figure').within().findByRole('img')

  expect(await locator.getAttribute('alt')).toEqual('Some image')
})
Synchronous + Asynchronous
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

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

import {test as base} from '@playwright/test'
import {
  locatorFixtures as fixtures,
  LocatorFixtures as TestingLibraryFixtures,
} from '@playwright-testing-library/test/fixture'

const test = base.extend<TestingLibraryFixtures>(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}) => {
    // ...
  })
})

Legacy Playwright Test Fixture

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

import {test as base} from '@playwright/test'
import {fixtures, within, TestingLibraryFixtures} from '@playwright-testing-library/test/fixture'

const test = base.extend<TestingLibraryFixtures>(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 protected]')
  await emailInputHandle.press('Enter')

  page.goto('/account')

  const emailHandle = queries.getByRole('heading', {level: 2})

  // Assert via `ElementHandle` APIs
  expect(await emailHandle.textContent()).toEqual('[email protected]')
})

Configuration

import {test as base} from '@playwright/test'
import {
  configure,
  fixtures,
  within,
  TestingLibraryFixtures,
} from '@playwright-testing-library/test/fixture'

const test = base.extend<TestingLibraryFixtures>(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}) => {
    // ...
  })
})

Standalone Playwright Queries

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.

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 protected]')
  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 protected]')
})

Configuration

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

Testing Library

All queries from @testing-library/dom are supported.

πŸ“ The find* queries for the Locator queries return Promise<Locator> which resolves when the element is found before the timeout specified via asyncUtilTimeout

Additional

Unique methods, not part of @testing-library/dom

⚠️ These only apply to the ElementHandle queries

  • Get an ElementHandle for the document

    getDocument(page: playwright.Page): ElementHandle
  • Wait for an assertion (wrapper around wait-for-expect)

    waitFor(
      expectation: () => void | Promise<void>,
      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.

Locator Queries

  • The getNodeText() function is currently unsupported.

  • When using a function for TextMatch, the function cannot reference its closure scope

    // βœ… 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))

Special Thanks

Related Playwright Test Utilities

LICENSE

MIT

Maintenance

This project is actively maintained by engineers at @hoverinc πŸ˜€.

More Repositories

1

react-testing-library

🐐 Simple and complete React DOM testing utilities that encourage good testing practices.
JavaScript
18,952
star
2

react-hooks-testing-library

🐏 Simple and complete React hooks testing utilities that encourage good testing practices.
TypeScript
5,083
star
3

jest-dom

πŸ¦‰ Custom jest matchers to test the state of the DOM
JavaScript
4,422
star
4

dom-testing-library

πŸ™ Simple and complete DOM testing utilities that encourage good testing practices.
JavaScript
3,271
star
5

user-event

πŸ• Simulate user events
TypeScript
2,181
star
6

cypress-testing-library

πŸ… Simple and complete custom Cypress commands and utilities that encourage good testing practices.
JavaScript
1,798
star
7

vue-testing-library

🦎 Simple and complete Vue.js testing utilities that encourage good testing practices.
JavaScript
1,071
star
8

eslint-plugin-testing-library

ESLint plugin to follow best practices and anticipate common mistakes when writing tests with Testing Library
TypeScript
979
star
9

testing-playground

Simple and complete DOM testing playground that encourage good testing practices.
JavaScript
776
star
10

angular-testing-library

πŸ¦” Simple and complete Angular testing utilities that encourage good testing practices
TypeScript
626
star
11

svelte-testing-library

🐿️ Simple and complete Svelte DOM testing utilities that encourage good testing practices
JavaScript
615
star
12

native-testing-library

🐳 Simple and complete React Native testing utilities that encourage good testing practices.
JavaScript
515
star
13

testing-library-docs

docs site for @testing-library/*
JavaScript
449
star
14

jest-native

πŸ¦… Custom jest matchers to test the state of React Native
TypeScript
436
star
15

eslint-plugin-jest-dom

eslint rules for use with jest-dom
JavaScript
360
star
16

pptr-testing-library

puppeteer + dom-testing-library = πŸ’–
TypeScript
283
star
17

testing-library-recorder-extension

Testing Library Extension for Chrome DevTools Recorder
TypeScript
144
star
18

preact-testing-library

Simple and complete Preact DOM testing utilities that encourage good testing practices.
JavaScript
139
star
19

which-query

🦩 Which query should I use?
CSS
124
star
20

testcafe-testing-library

πŸ‚ Simple and complete custom Selectors for Testcafe that encourage good testing practices.
TypeScript
71
star
21

preact-hooks-testing-library

Simple and complete Preact hooks testing utilities that encourage good testing practices.
TypeScript
56
star
22

jasmine-dom

πŸ¦₯ Custom Jasmine matchers to test the state of the DOM
JavaScript
45
star
23

nightwatch-testing-library

πŸ¦‡Simple and complete custom queries for Nightwatch that encourage good testing practices.
JavaScript
31
star
24

dom-testing-library-template

Template repository for bug reports to @testing-library/dom, @testing-library/react, and @testing-library/jest-dom
JavaScript
17
star
25

webdriverio-testing-library

πŸ•·οΈ Simple and complete WebdriverIO DOM testing utilities that encourage good testing practices.
TypeScript
16
star
26

native-testing-library-docs

🐳 Docs site for native-testing-library
JavaScript
16
star
27

react-testing-library-help

Fork this repo to reproduce your issue
HTML
12
star
28

web-testing-library

πŸ™ Experimental Web testing utilities that encourage good testing practices.
JavaScript
3
star