Skip to content

Commit 3b90b33

Browse files
authored
feat: add find* queries (#44)
1 parent d58d17a commit 3b90b33

File tree

5 files changed

+163
-9
lines changed

5 files changed

+163
-9
lines changed

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,30 +73,47 @@ Unique methods, not part of `@testing-library/dom`
7373
- `queryAllByPlaceholderText`
7474
- `getByPlaceholderText`
7575
- `getAllByPlaceholderText`
76+
- `findByPlaceholderText`
77+
- `findAllByPlaceholderText`
7678
- `queryByText`
7779
- `queryAllByText`
7880
- `getByText`
7981
- `getAllByText`
82+
- `findByText`
83+
- `findAllByText`
8084
- `queryByLabelText`
8185
- `queryAllByLabelText`
8286
- `getByLabelText`
8387
- `getAllByLabelText`
88+
- `findByLabelText`
89+
- `findAllByLabelText`
8490
- `queryByAltText`
8591
- `queryAllByAltText`
8692
- `getByAltText`
8793
- `getAllByAltText`
94+
- `findByAltText`
95+
- `findAllByAltText`
8896
- `queryByTestId`
8997
- `queryAllByTestId`
9098
- `getByTestId`
9199
- `getAllByTestId`
100+
- `findByTestId`
101+
- `findAllByTestId`
92102
- `queryByTitle`
93103
- `queryAllByTitle`
94104
- `getByTitle`
95105
- `getAllByTitle`
106+
- `findByTitle`
107+
- `findAllByTitle`
108+
- `queryByDisplayValue`,
109+
- `queryAllByDisplayValue`,
110+
- `getByDisplayValue`,
111+
- `getAllByDisplayValue`,
112+
- `findByDisplayValue`,
113+
- `findAllByDisplayValue`,
96114

97115
## Known Limitations
98-
99-
- `waitForElement` method is not exposed. Puppeteer has its own set of wait utilities that somewhat conflict with the style used in `@testing-library/dom`. See [#3](https://github.com/testing-library/pptr-testing-library/issues/3).
116+
- Async utilities `waitForElement`, `waitForElementToBeRemoved` and `waitForDomChange` are not exposed. Consider using a `find*` query.
100117
- `fireEvent` method is not exposed, use puppeteer's built-ins instead.
101118
- `expect` assertion extensions are not available.
102119

lib/index.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,41 +161,57 @@ export function getQueriesForElement<T>(
161161
'queryAllByPlaceholderText',
162162
'getByPlaceholderText',
163163
'getAllByPlaceholderText',
164+
'findByPlaceholderText',
165+
'findAllByPlaceholderText',
164166

165167
'queryByText',
166168
'queryAllByText',
167169
'getByText',
168170
'getAllByText',
171+
'findByText',
172+
'findAllByText',
169173

170174
'queryByLabelText',
171175
'queryAllByLabelText',
172176
'getByLabelText',
173177
'getAllByLabelText',
178+
'findByLabelText',
179+
'findAllByLabelText',
174180

175181
'queryByAltText',
176182
'queryAllByAltText',
177183
'getByAltText',
178184
'getAllByAltText',
185+
'findByAltText',
186+
'findAllByAltText',
179187

180188
'queryByTestId',
181189
'queryAllByTestId',
182190
'getByTestId',
183191
'getAllByTestId',
192+
'findByTestId',
193+
'findAllByTestId',
184194

185195
'queryByTitle',
186196
'queryAllByTitle',
187197
'getByTitle',
188198
'getAllByTitle',
199+
'findByTitle',
200+
'findAllByTitle',
189201

190202
'queryByRole',
191203
'queryAllByRole',
192204
'getByRole',
193205
'getAllByRole',
206+
'findByRole',
207+
'findAllByRole',
194208

195209
'queryByDisplayValue',
196210
'queryAllByDisplayValue',
197211
'getByDisplayValue',
198212
'getAllByDisplayValue',
213+
'findByDisplayValue',
214+
'findAllByDisplayValue',
199215
]
200216
functionNames.forEach(functionName => {
201217
o[functionName] = createDelegateFor(functionName, contextFn)

lib/typedefs.ts

Lines changed: 97 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Matcher, MatcherOptions, SelectorMatcherOptions} from '@testing-library/dom'
1+
import {Matcher, MatcherOptions, SelectorMatcherOptions, WaitForElementOptions} from '@testing-library/dom'
22
import {ElementHandle} from 'puppeteer'
33

44
type Element = ElementHandle
@@ -8,54 +8,144 @@ interface IQueryMethods {
88
queryAllByPlaceholderText(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element[]>
99
getByPlaceholderText(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element>
1010
getAllByPlaceholderText(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element[]>
11+
findByPlaceholderText(
12+
el: Element,
13+
m: Matcher,
14+
opts?: SelectorMatcherOptions,
15+
waitForOpts?: WaitForElementOptions): Promise<Element>
16+
findAllByPlaceholderText(
17+
el: Element,
18+
m: Matcher,
19+
opts?: SelectorMatcherOptions,
20+
waitForOpts?: WaitForElementOptions): Promise<Element[]>
1121

1222
queryByText(el: Element, m: Matcher, opts?: SelectorMatcherOptions): Promise<Element | null>
1323
queryAllByText(el: Element, m: Matcher, opts?: SelectorMatcherOptions): Promise<Element[]>
1424
getByText(el: Element, m: Matcher, opts?: SelectorMatcherOptions): Promise<Element>
1525
getAllByText(el: Element, m: Matcher, opts?: SelectorMatcherOptions): Promise<Element[]>
26+
findByText(
27+
el: Element,
28+
m: Matcher,
29+
opts?: SelectorMatcherOptions,
30+
waitForOpts?: WaitForElementOptions): Promise<Element>
31+
findAllByText(
32+
el: Element,
33+
m: Matcher,
34+
opts?: SelectorMatcherOptions,
35+
waitForOpts?: WaitForElementOptions): Promise<Element[]>
1636

1737
queryByLabelText(el: Element, m: Matcher, opts?: SelectorMatcherOptions): Promise<Element | null>
1838
queryAllByLabelText(el: Element, m: Matcher, opts?: SelectorMatcherOptions): Promise<Element[]>
1939
getByLabelText(el: Element, m: Matcher, opts?: SelectorMatcherOptions): Promise<Element>
2040
getAllByLabelText(el: Element, m: Matcher, opts?: SelectorMatcherOptions): Promise<Element[]>
41+
findByLabelText(
42+
el: Element,
43+
m: Matcher,
44+
opts?: SelectorMatcherOptions,
45+
waitForOpts?: WaitForElementOptions): Promise<Element>
46+
findAllByLabelText(
47+
el: Element,
48+
m: Matcher,
49+
opts?: SelectorMatcherOptions,
50+
waitForOpts?: WaitForElementOptions): Promise<Element[]>
2151

2252
queryByAltText(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element | null>
2353
queryAllByAltText(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element[]>
2454
getByAltText(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element>
2555
getAllByAltText(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element[]>
56+
findByAltText(
57+
el: Element,
58+
m: Matcher,
59+
opts?: SelectorMatcherOptions,
60+
waitForOpts?: WaitForElementOptions): Promise<Element>
61+
findAllByAltText(
62+
el: Element,
63+
m: Matcher,
64+
opts?: SelectorMatcherOptions,
65+
waitForOpts?: WaitForElementOptions): Promise<Element[]>
2666

2767
queryByTestId(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element | null>
2868
queryAllByTestId(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element[]>
2969
getByTestId(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element>
3070
getAllByTestId(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element[]>
71+
findByTestId(
72+
el: Element,
73+
m: Matcher,
74+
opts?: SelectorMatcherOptions,
75+
waitForOpts?: WaitForElementOptions): Promise<Element>
76+
findAllByTestId(
77+
el: Element,
78+
m: Matcher,
79+
opts?: SelectorMatcherOptions,
80+
waitForOpts?: WaitForElementOptions): Promise<Element[]>
3181

3282
queryByTitle(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element | null>
3383
queryAllByTitle(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element[]>
3484
getByTitle(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element>
3585
getAllByTitle(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element[]>
86+
findByTitle(
87+
el: Element,
88+
m: Matcher,
89+
opts?: SelectorMatcherOptions,
90+
waitForOpts?: WaitForElementOptions): Promise<Element>
91+
findAllByTitle(
92+
el: Element,
93+
m: Matcher,
94+
opts?: SelectorMatcherOptions,
95+
waitForOpts?: WaitForElementOptions): Promise<Element[]>
3696

3797
queryByRole(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element | null>
3898
queryAllByRole(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element[]>
3999
getByRole(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element>
40100
getAllByRole(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element[]>
101+
findByRole(
102+
el: Element,
103+
m: Matcher,
104+
opts?: SelectorMatcherOptions,
105+
waitForOpts?: WaitForElementOptions): Promise<Element>
106+
findAllByRole(
107+
el: Element,
108+
m: Matcher,
109+
opts?: SelectorMatcherOptions,
110+
waitForOpts?: WaitForElementOptions): Promise<Element[]>
41111

42112
queryByDisplayValue(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element | null>
43113
queryAllByDisplayValue(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element[]>
44114
getByDisplayValue(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element>
45115
getAllByDisplayValue(el: Element, m: Matcher, opts?: MatcherOptions): Promise<Element[]>
116+
findByDisplayValue(
117+
el: Element,
118+
m: Matcher,
119+
opts?: SelectorMatcherOptions,
120+
waitForOpts?: WaitForElementOptions): Promise<Element>
121+
findAllByDisplayValue(
122+
el: Element,
123+
m: Matcher,
124+
opts?: SelectorMatcherOptions,
125+
waitForOpts?: WaitForElementOptions): Promise<Element[]>
46126
}
47127

48-
type IScopedQueryMethods = {
49-
[K in keyof IQueryMethods]: (m: Matcher, opts?: MatcherOptions) => ReturnType<IQueryMethods[K]>
50-
}
128+
export type BoundFunction<T> = T extends (
129+
attribute: string,
130+
element: Element,
131+
text: infer P,
132+
options: infer Q,
133+
) => infer R
134+
? (text: P, options?: Q) => R
135+
: T extends (a1: any, text: infer P, options: infer Q, waitForElementOptions: infer W) => infer R
136+
? (text: P, options?: Q, waitForElementOptions?: W) => R
137+
: T extends (a1: any, text: infer P, options: infer Q) => infer R
138+
? (text: P, options?: Q) => R
139+
: never
140+
export type BoundFunctions<T> = { [P in keyof T]: BoundFunction<T[P]> }
51141

52-
export interface IScopedQueryUtils extends IScopedQueryMethods {
53-
getQueriesForElement(): IQueryUtils & IScopedQueryUtils
142+
export interface IScopedQueryUtils extends BoundFunctions<IQueryMethods> {
143+
getQueriesForElement(): IScopedQueryUtils
54144
getNodeText(): Promise<string>
55145
}
56146

57147
export interface IQueryUtils extends IQueryMethods {
58-
getQueriesForElement(): IQueryUtils & IScopedQueryUtils
148+
getQueriesForElement(): IScopedQueryUtils
59149
getNodeText(el: Element): Promise<string>
60150
}
61151

test/extend.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,29 @@ describe('lib/extend.ts', () => {
100100
expect(await queryByText('Hello h3')).toBeTruthy()
101101
})
102102

103+
describe('deferred page', () => {
104+
beforeEach(async () => {
105+
await page.goto(`file://${path.join(__dirname, 'fixtures/late-page.html')}`)
106+
document = await page.getDocument()
107+
})
108+
109+
it('should handle the findBy* methods', async () => {
110+
expect(await document.findByText('Loaded!', {}, { timeout: 7000 })).toBeTruthy()
111+
}, 9000)
112+
113+
it('should handle the findByAll* methods', async () => {
114+
const elements = await document.findAllByText(/Hello/, {}, { timeout: 7000 })
115+
expect(elements).toHaveLength(2)
116+
117+
const text = await Promise.all([
118+
page.evaluate(el => el.textContent, elements[0]),
119+
page.evaluate(el => el.textContent, elements[1]),
120+
])
121+
122+
expect(text).toEqual(['Hello h1', 'Hello h2'])
123+
}, 9000)
124+
})
125+
103126
afterAll(async () => {
104127
await browser.close()
105128
})

test/fixtures/late-page.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77
const loaded = document.createElement('span')
88
loaded.textContent = 'Loaded!'
99
document.body.appendChild(loaded)
10+
11+
const heading1 = document.createElement('h1')
12+
heading1.textContent = 'Hello h1'
13+
document.body.appendChild(heading1)
14+
15+
const heading2 = document.createElement('h2')
16+
heading2.textContent = 'Hello h2'
17+
document.body.appendChild(heading2)
1018
}, 5000)
1119
</script>
1220
</body>

0 commit comments

Comments
 (0)