forked from testing-library/dom-testing-library
-
Notifications
You must be signed in to change notification settings - Fork 0
/
query-helpers.js
134 lines (121 loc) 路 4.23 KB
/
query-helpers.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import {getSuggestedQuery} from './suggestions'
import {fuzzyMatches, matches, makeNormalizer} from './matches'
import {waitFor} from './wait-for'
import {getConfig} from './config'
function getElementError(message, container) {
return getConfig().getElementError(message, container)
}
function getMultipleElementsFoundError(message, container) {
return getElementError(
`${message}\n\n(If this is intentional, then use the \`*AllBy*\` variant of the query (like \`queryAllByText\`, \`getAllByText\`, or \`findAllByText\`)).`,
container,
)
}
function queryAllByAttribute(
attribute,
container,
text,
{exact = true, collapseWhitespace, trim, normalizer} = {},
) {
const matcher = exact ? matches : fuzzyMatches
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
return Array.from(container.querySelectorAll(`[${attribute}]`)).filter(node =>
matcher(node.getAttribute(attribute), node, text, matchNormalizer),
)
}
function queryByAttribute(attribute, container, text, ...args) {
const els = queryAllByAttribute(attribute, container, text, ...args)
if (els.length > 1) {
throw getMultipleElementsFoundError(
`Found multiple elements by [${attribute}=${text}]`,
container,
)
}
return els[0] || null
}
// this accepts a query function and returns a function which throws an error
// if more than one elements is returned, otherwise it returns the first
// element or null
function makeSingleQuery(allQuery, getMultipleError) {
return (container, ...args) => {
const els = allQuery(container, ...args)
if (els.length > 1) {
throw getMultipleElementsFoundError(
getMultipleError(container, ...args),
container,
)
}
const element = els[0] || null
if (getConfig().showSuggestions) {
const suggestion = getSuggestedQuery(element)
if (suggestion && !allQuery.name.endsWith(suggestion.queryName)) {
throw getSuggestionError(suggestion.toString(), container)
}
}
return element
}
}
function getSuggestionError(suggestion, container) {
return getConfig().getElementError(
`A better query is available, try this:
*By${suggestion.toString()}
`,
container,
)
}
// this accepts a query function and returns a function which throws an error
// if an empty list of elements is returned
function makeGetAllQuery(allQuery, getMissingError) {
return (container, ...args) => {
const els = allQuery(container, ...args)
if (!els.length) {
throw getConfig().getElementError(
getMissingError(container, ...args),
container,
)
}
if (getConfig().showSuggestions) {
//get a unique list of all suggestion messages. We are only going to make a suggestion if
// all the suggestions are the same
const uniqueSuggestionMessages = [
...new Set(els.map(element => getSuggestedQuery(element)?.toString())),
]
if (
// only want to suggest if all the els have the same suggestion.
uniqueSuggestionMessages.length === 1 &&
!allQuery.name.endsWith(getSuggestedQuery(els[0]).queryName)
) {
throw getSuggestionError(uniqueSuggestionMessages[0], container)
}
}
return els
}
}
// this accepts a getter query function and returns a function which calls
// waitFor and passing a function which invokes the getter.
function makeFindQuery(getter) {
return (container, text, options, waitForOptions) =>
waitFor(() => getter(container, text, options), waitForOptions)
}
function buildQueries(queryAllBy, getMultipleError, getMissingError) {
const queryBy = makeSingleQuery(queryAllBy, getMultipleError)
const getAllBy = makeGetAllQuery(queryAllBy, getMissingError)
// Suggestions need to know how they're being used, so need to set the name of the allQuery
Object.defineProperty(getAllBy, 'name', {
value: queryAllBy.name.replace('query', 'get'),
})
const getBy = makeSingleQuery(getAllBy, getMultipleError)
const findAllBy = makeFindQuery(getAllBy)
const findBy = makeFindQuery(getBy)
return [queryBy, getAllBy, getBy, findAllBy, findBy]
}
export {
getElementError,
getMultipleElementsFoundError,
queryAllByAttribute,
queryByAttribute,
makeSingleQuery,
makeGetAllQuery,
makeFindQuery,
buildQueries,
}