Skip to content

Commit c49b867

Browse files
committed
feat: Improve performance of findBy*/findAllBy* by not generating informative error messages
in the function passed to waitFor. The work of generating these error messages almost entirely wasted because all of them, except possibly the last one (if the test is red), will be discarded by waitFor. Instead, if the call to waitFor failed, another attempt to find the element(s) is made, this time generating a useful error message if it also fails.
1 parent b6b9b5b commit c49b867

File tree

3 files changed

+64
-6
lines changed

3 files changed

+64
-6
lines changed

src/__tests__/role.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {configure, getConfig} from '../config'
22
import {getQueriesForElement} from '../get-queries-for-element'
3+
import {waitFor} from '../wait-for'
34
import {render, renderIntoDocument} from './helpers/test-utils'
45

56
test('by default logs accessible roles when it fails', () => {
@@ -356,11 +357,11 @@ test('does not include the container in the queryable roles', () => {
356357
})
357358

358359
test('has no useful error message in findBy', async () => {
359-
const {findByRole} = render(`<li />`)
360+
const {getByRole} = render(`<li />`)
360361

361-
await expect(findByRole('option', {timeout: 1})).rejects.toThrow(
362-
'Unable to find role="option"',
363-
)
362+
await expect(
363+
waitFor(() => getByRole('option'), {timeout: 1}),
364+
).rejects.toThrow('Unable to find role="option"')
364365
})
365366

366367
test('explicit role is most specific', () => {

src/queries/label-text.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,34 @@ const findAllByLabelText = makeFindQuery(
191191
// @ts-expect-error -- See `wrapAllByQueryWithSuggestion` Argument constraint comment
192192
[labelText: Matcher, options?: SelectorMatcherOptions]
193193
>(getAllByLabelText, getAllByLabelText.name, 'findAll'),
194+
wrapAllByQueryWithSuggestion(
195+
(container, text, options) => {
196+
const elements = queryAllByLabelText(container, text, options)
197+
if (elements.length === 0) {
198+
throw new Error('no element found')
199+
}
200+
return elements
201+
},
202+
getAllByLabelText.name,
203+
'findAll',
204+
),
194205
)
195206
const findByLabelText = makeFindQuery(
196207
wrapSingleQueryWithSuggestion<
197208
// @ts-expect-error -- See `wrapAllByQueryWithSuggestion` Argument constraint comment
198209
[labelText: Matcher, options?: SelectorMatcherOptions]
199210
>(getByLabelText, getAllByLabelText.name, 'find'),
211+
wrapSingleQueryWithSuggestion(
212+
(container, text, options) => {
213+
const elements = queryAllByLabelText(container, text, options)
214+
if (elements.length !== 1) {
215+
throw new Error('no element or more than one elements found')
216+
}
217+
return elements[0]
218+
},
219+
getAllByLabelText.name,
220+
'find',
221+
),
200222
)
201223

202224
const getAllByLabelTextWithSuggestions = wrapAllByQueryWithSuggestion<

src/query-helpers.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,16 @@ function makeGetAllQuery<Arguments extends unknown[]>(
118118
// this accepts a getter query function and returns a function which calls
119119
// waitFor and passing a function which invokes the getter.
120120
function makeFindQuery<QueryFor>(
121-
getter: (
121+
getterWithInformativeErrorMessage: (
122122
container: HTMLElement,
123123
text: Matcher,
124124
options: MatcherOptions,
125125
) => QueryFor,
126+
getterWithQuickToGenerateErrorMessage: (
127+
container: HTMLElement,
128+
text: Matcher,
129+
options: MatcherOptions,
130+
) => QueryFor = getterWithInformativeErrorMessage,
126131
) {
127132
return (
128133
container: HTMLElement,
@@ -132,9 +137,17 @@ function makeFindQuery<QueryFor>(
132137
) => {
133138
return waitFor(
134139
() => {
135-
return getter(container, text, options)
140+
// Don't waste time generating an informative error message since they will be discarded
141+
// anyway.
142+
return getterWithQuickToGenerateErrorMessage(container, text, options)
136143
},
137144
{container, ...waitForOptions},
145+
).then(
146+
x => x,
147+
() => {
148+
// Try one last time, this time generating an informative error message in case of failure
149+
return getterWithInformativeErrorMessage(container, text, options)
150+
},
138151
)
139152
}
140153
}
@@ -242,9 +255,31 @@ function buildQueries(
242255

243256
const findAllBy = makeFindQuery(
244257
wrapAllByQueryWithSuggestion(getAllBy, queryAllBy.name, 'findAll'),
258+
wrapAllByQueryWithSuggestion(
259+
(container, text, options) => {
260+
const elements = queryAllBy(container, text, options)
261+
if (elements.length === 0) {
262+
throw new Error('no element found')
263+
}
264+
return elements
265+
},
266+
queryAllBy.name,
267+
'findAll',
268+
),
245269
)
246270
const findBy = makeFindQuery(
247271
wrapSingleQueryWithSuggestion(getBy, queryAllBy.name, 'find'),
272+
wrapSingleQueryWithSuggestion(
273+
(container, text, options) => {
274+
const elements = queryAllBy(container, text, options)
275+
if (elements.length !== 1) {
276+
throw new Error('no element or more than one elements found')
277+
}
278+
return elements[0]
279+
},
280+
queryAllBy.name,
281+
'find',
282+
),
248283
)
249284

250285
return [

0 commit comments

Comments
 (0)