Skip to content
This repository was archived by the owner on Aug 6, 2025. It is now read-only.

Commit 1f66fa6

Browse files
committed
testing-library as a selector engine
1 parent a6d9bf4 commit 1f66fa6

File tree

7 files changed

+31632
-5583
lines changed

7 files changed

+31632
-5583
lines changed

README.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,37 @@ or
4646
```bash
4747
yarn add --dev playwright-testing-library
4848
```
49+
### 2a. Use _extend @playwright/test selectors_
4950

50-
### 2a. Use _standalone queries_
51+
@playwright/test allows you to [extend the selectors](https://playwright.dev/docs/extensibility/#custom-selector-engines) that are available when making a locator. When testing this way it doesn't make sense to differentiate the queryBy and queryAllBy queries or use the get and find queries since we can leave it to playwright to be angry when you try to click a locator that matches multiple elements.
52+
53+
You can use a Regex in these selectors, but since they are strings remember to escape any special characters as normal.
54+
55+
You can use >> as normal to chain selectors in a locator.
56+
57+
```js
58+
import baseTest from '@playwright/test'
59+
import { mixinFixtures, registerSelectorEngines } from '???';
60+
61+
// extends the page fixture to include dom-testing-library
62+
const test = mixinFixtures(baseTest)
63+
// registers all of the By... selector engines
64+
registerSelectorEngines()
65+
66+
const { expect, beforeEach } = test
67+
68+
test('something', async ({page}) => {
69+
//...
70+
71+
await page.locator('form >> ByLabelText=/username/i').type('Slartibartfast')
72+
73+
//...
74+
})
75+
76+
```
77+
78+
79+
### 2b. Use _standalone queries_
5180

5281
```js
5382
const {webkit} = require('playwright') // or 'firefox' or 'chromium'
@@ -73,7 +102,7 @@ await $email.type('playwright@example.com')
73102
// ...
74103
```
75104

76-
### 2b. Use _extensions_
105+
### 2c. Use _extensions_
77106

78107
```js
79108
const {webkit} = require('playwright') // or 'firefox' or 'chromium'

lib/engine.ts

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { readFileSync } from 'fs'
2+
import * as path from 'path'
3+
import { selectors } from '@playwright/test'
4+
import type {
5+
Fixtures,
6+
PlaywrightTestArgs,
7+
PlaywrightTestOptions,
8+
PlaywrightWorkerArgs,
9+
PlaywrightWorkerOptions,
10+
TestType,
11+
} from '@playwright/test';
12+
13+
const domLibraryAsString = readFileSync(
14+
path.join(__dirname, '../dom-testing-library.js'),
15+
'utf8',
16+
).replace(/process.env/g, '{}')
17+
18+
const queryFixtures: Fixtures<
19+
{},
20+
{},
21+
PlaywrightTestArgs & PlaywrightTestOptions,
22+
PlaywrightWorkerArgs & PlaywrightWorkerOptions
23+
> = {
24+
page: async ({ page }, use) => {
25+
await page.addInitScript(domLibraryAsString)
26+
27+
await use(page)
28+
}
29+
}
30+
31+
export function mixinFixtures<
32+
T extends PlaywrightTestArgs & PlaywrightTestOptions,
33+
W extends PlaywrightWorkerArgs & PlaywrightWorkerOptions,
34+
>(base: TestType<T, W>): TestType<T, W> {
35+
return base.extend(queryFixtures);
36+
}
37+
38+
39+
export function registerSelectorEngines() {
40+
41+
selectors.register('ByPlaceholderText', () => ({
42+
queryAll(root: unknown, selector: string) {
43+
const turtle = /^\/(.+)\/([dumysig]*)/
44+
const matches = turtle.exec(selector)
45+
46+
const match = matches !== null ? new RegExp(matches[1], matches[2]) : selector
47+
// @ts-ignore
48+
return __dom_testing_library__.__moduleExports.queryAllByPlaceholderText(root, match)
49+
}
50+
}))
51+
selectors.register('ByText', () => ({
52+
queryAll(root: unknown, selector: string) {
53+
const turtle = /^\/(.+)\/([dumysig]*)/
54+
const matches = turtle.exec(selector)
55+
56+
const match = matches !== null ? new RegExp(matches[1], matches[2]) : selector
57+
// @ts-ignore
58+
return __dom_testing_library__.__moduleExports.queryAllByText(root, match)
59+
}
60+
}))
61+
selectors.register('ByLabelText', () => ({
62+
queryAll(root: unknown, selector: string) {
63+
const turtle = /^\/(.+)\/([dumysig]*)/
64+
const matches = turtle.exec(selector)
65+
66+
const match = matches !== null ? new RegExp(matches[1], matches[2]) : selector
67+
// @ts-ignore
68+
return __dom_testing_library__.__moduleExports.queryAllByLabelText(root, match)
69+
}
70+
}))
71+
selectors.register('ByAltText', () => ({
72+
queryAll(root: unknown, selector: string) {
73+
const turtle = /^\/(.+)\/([dumysig]*)/
74+
const matches = turtle.exec(selector)
75+
76+
const match = matches !== null ? new RegExp(matches[1], matches[2]) : selector
77+
// @ts-ignore
78+
return __dom_testing_library__.__moduleExports.queryAllByAltText(root, match)
79+
}
80+
}))
81+
selectors.register('ByTestId', () => ({
82+
queryAll(root: unknown, selector: string) {
83+
const turtle = /^\/(.+)\/([dumysig]*)/
84+
const matches = turtle.exec(selector)
85+
86+
const match = matches !== null ? new RegExp(matches[1], matches[2]) : selector
87+
// @ts-ignore
88+
return __dom_testing_library__.__moduleExports.queryAllByTestId(root, match)
89+
}
90+
}))
91+
selectors.register('ByTitle', () => ({
92+
queryAll(root: unknown, selector: string) {
93+
const turtle = /^\/(.+)\/([dumysig]*)/
94+
const matches = turtle.exec(selector)
95+
96+
const match = matches !== null ? new RegExp(matches[1], matches[2]) : selector
97+
// @ts-ignore
98+
return __dom_testing_library__.__moduleExports.queryAllByTitle(root, match)
99+
}
100+
}))
101+
selectors.register('ByRole', () => ({
102+
queryAll(root: unknown, selector: string) {
103+
const turtle = /^\/(.+)\/([dumysig]*)/
104+
const matches = turtle.exec(selector)
105+
106+
const match = matches !== null ? new RegExp(matches[1], matches[2]) : selector
107+
// @ts-ignore
108+
return __dom_testing_library__.__moduleExports.queryAllByRole(root, match)
109+
}
110+
}))
111+
selectors.register('ByDisplayValue', () => ({
112+
queryAll(root: unknown, selector: string) {
113+
const turtle = /^\/(.+)\/([dumysig]*)/
114+
const matches = turtle.exec(selector)
115+
116+
const match = matches !== null ? new RegExp(matches[1], matches[2]) : selector
117+
// @ts-ignore
118+
return __dom_testing_library__.__moduleExports.queryAllByDisplayValue(root, match)
119+
}
120+
}))
121+
}

0 commit comments

Comments
 (0)