Skip to content

Commit 19f1acd

Browse files
committed
feat: Add check for container type on queries
This is intended to give an informative warning when a base-query is called with a container that's neitheir an Element, Document or DocumentFragment. Ref: #537
1 parent af7392f commit 19f1acd

File tree

11 files changed

+198
-7
lines changed

11 files changed

+198
-7
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import {
2+
getByLabelText,
3+
getAllByLabelText,
4+
queryAllByLabelText,
5+
queryByLabelText,
6+
findByLabelText,
7+
findAllByLabelText,
8+
getByPlaceholderText,
9+
getAllByPlaceholderText,
10+
queryAllByPlaceholderText,
11+
queryByPlaceholderText,
12+
findByPlaceholderText,
13+
findAllByPlaceholderText,
14+
getByText,
15+
getAllByText,
16+
queryAllByText,
17+
queryByText,
18+
findByText,
19+
findAllByText,
20+
getByAltText,
21+
getAllByAltText,
22+
queryAllByAltText,
23+
queryByAltText,
24+
findByAltText,
25+
findAllByAltText,
26+
getByTitle,
27+
getAllByTitle,
28+
queryAllByTitle,
29+
queryByTitle,
30+
findByTitle,
31+
findAllByTitle,
32+
getByDisplayValue,
33+
getAllByDisplayValue,
34+
queryAllByDisplayValue,
35+
queryByDisplayValue,
36+
findByDisplayValue,
37+
findAllByDisplayValue,
38+
getByRole,
39+
getAllByRole,
40+
queryAllByRole,
41+
queryByRole,
42+
findByRole,
43+
findAllByRole,
44+
getByTestId,
45+
getAllByTestId,
46+
queryAllByTestId,
47+
queryByTestId,
48+
findByTestId,
49+
findAllByTestId,
50+
} from '..'
51+
52+
describe('synchronous queries throw on invalid container type', () => {
53+
test.each([
54+
['getByLabelText', getByLabelText],
55+
['getAllByLabelText', getAllByLabelText],
56+
['queryByLabelText', queryByLabelText],
57+
['queryAllByLabelText', queryAllByLabelText],
58+
['getByPlaceholderText', getByPlaceholderText],
59+
['getAllByPlaceholderText', getAllByPlaceholderText],
60+
['queryByPlaceholderText', queryByPlaceholderText],
61+
['queryAllByPlaceholderText', queryAllByPlaceholderText],
62+
['getByText', getByText],
63+
['getAllByText', getAllByText],
64+
['queryByText', queryByText],
65+
['queryAllByText', queryAllByText],
66+
['getByAltText', getByAltText],
67+
['getAllByAltText', getAllByAltText],
68+
['queryByAltText', queryByAltText],
69+
['queryAllByAltText', queryAllByAltText],
70+
['getByTitle', getByTitle],
71+
['getAllByTitle', getAllByTitle],
72+
['queryByTitle', queryByTitle],
73+
['queryAllByTitle', queryAllByTitle],
74+
['getByDisplayValue', getByDisplayValue],
75+
['getAllByDisplayValue', getAllByDisplayValue],
76+
['queryByDisplayValue', queryByDisplayValue],
77+
['queryAllByDisplayValue', queryAllByDisplayValue],
78+
['getByRole', getByRole],
79+
['getAllByRole', getAllByRole],
80+
['queryByRole', queryByRole],
81+
['queryAllByRole', queryAllByRole],
82+
['getByTestId', getByTestId],
83+
['getAllByTestId', getAllByTestId],
84+
['queryByTestId', queryByTestId],
85+
['queryAllByTestId', queryAllByTestId],
86+
])('%s', (_queryName, query) => {
87+
expect(() =>
88+
query('invalid type for container', 'irrelevant text'),
89+
).toThrowErrorMatchingInlineSnapshot(
90+
`"Expected container to be an Element, a Document or a DocumentFragment but got string."`,
91+
)
92+
})
93+
})
94+
95+
describe('asynchronous queries throw on invalid container type', () => {
96+
test.each([
97+
['findByLabelText', findByLabelText],
98+
['findAllByLabelText', findAllByLabelText],
99+
['findByPlaceholderText', findByPlaceholderText],
100+
['findAllByPlaceholderText', findAllByPlaceholderText],
101+
['findByText', findByText],
102+
['findAllByText', findAllByText],
103+
['findByAltText', findByAltText],
104+
['findAllByAltText', findAllByAltText],
105+
['findByTitle', findByTitle],
106+
['findAllByTitle', findAllByTitle],
107+
['findByDisplayValue', findByDisplayValue],
108+
['findAllByDisplayValue', findAllByDisplayValue],
109+
['findByRole', findByRole],
110+
['findAllByRole', findAllByRole],
111+
['findByTestId', findByTestId],
112+
['findAllByTestId', findAllByTestId],
113+
])('%s', (_queryName, query) => {
114+
const queryOptions = {}
115+
const waitOptions = {timeout: 1}
116+
return expect(
117+
query(
118+
'invalid type for container',
119+
'irrelevant text',
120+
queryOptions,
121+
waitOptions,
122+
),
123+
).rejects.toThrowErrorMatchingInlineSnapshot(
124+
`"Expected container to be an Element, a Document or a DocumentFragment but got string."`,
125+
)
126+
})
127+
})

src/__tests__/helpers.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
1-
import {getDocument} from '../helpers'
1+
import {getDocument, checkContainerType} from '../helpers'
22

33
test('returns global document if exists', () => {
44
expect(getDocument()).toBe(document)
55
})
6+
7+
describe('query container validation throws when validation fails', () => {
8+
test('undefined as container', () => {
9+
expect(() =>
10+
checkContainerType(undefined),
11+
).toThrowErrorMatchingInlineSnapshot(
12+
`"Expected container to be an Element, a Document or a DocumentFragment but got undefined."`,
13+
)
14+
})
15+
test('null as container', () => {
16+
expect(() => checkContainerType(null)).toThrowErrorMatchingInlineSnapshot(
17+
`"Expected container to be an Element, a Document or a DocumentFragment but got null."`,
18+
)
19+
})
20+
test('array as container', () => {
21+
expect(() => checkContainerType([])).toThrowErrorMatchingInlineSnapshot(
22+
`"Expected container to be an Element, a Document or a DocumentFragment but got Array."`,
23+
)
24+
})
25+
test('object as container', () => {
26+
expect(() => checkContainerType({})).toThrowErrorMatchingInlineSnapshot(
27+
`"Expected container to be an Element, a Document or a DocumentFragment but got Object."`,
28+
)
29+
})
30+
})

src/helpers.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,33 @@ function getWindowFromNode(node) {
6565
}
6666
}
6767

68+
function checkContainerType(container) {
69+
if (
70+
!container ||
71+
!(typeof container.querySelector === 'function') ||
72+
!(typeof container.querySelectorAll === 'function')
73+
) {
74+
throw new TypeError(
75+
`Expected container to be an Element, a Document or a DocumentFragment but got ${getTypeName(
76+
container,
77+
)}.`,
78+
)
79+
}
80+
81+
function getTypeName(object) {
82+
if (typeof object === 'object') {
83+
return object === null ? 'null' : object.constructor.name
84+
}
85+
return typeof object
86+
}
87+
}
88+
6889
export {
6990
getWindowFromNode,
7091
getDocument,
7192
clearTimeoutFn as clearTimeout,
7293
setImmediateFn as setImmediate,
7394
setTimeoutFn as setTimeout,
7495
runWithRealTimers,
96+
checkContainerType,
7597
}

src/queries/alt-text.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import {matches, fuzzyMatches, makeNormalizer, buildQueries} from './all-utils'
21
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
2+
import {checkContainerType} from '../helpers'
3+
import {matches, fuzzyMatches, makeNormalizer, buildQueries} from './all-utils'
34

45
function queryAllByAltText(
56
container,
67
alt,
78
{exact = true, collapseWhitespace, trim, normalizer} = {},
89
) {
10+
checkContainerType(container)
911
const matcher = exact ? matches : fuzzyMatches
1012
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
1113
return Array.from(container.querySelectorAll('img,input,area')).filter(node =>

src/queries/display-value.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1+
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
2+
import {checkContainerType} from '../helpers'
13
import {
24
getNodeText,
35
matches,
46
fuzzyMatches,
57
makeNormalizer,
68
buildQueries,
79
} from './all-utils'
8-
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
910

1011
function queryAllByDisplayValue(
1112
container,
1213
value,
1314
{exact = true, collapseWhitespace, trim, normalizer} = {},
1415
) {
16+
checkContainerType(container)
1517
const matcher = exact ? matches : fuzzyMatches
1618
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
1719
return Array.from(container.querySelectorAll(`input,textarea,select`)).filter(

src/queries/label-text.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {getConfig} from '../config'
2+
import {checkContainerType} from '../helpers'
23
import {
34
fuzzyMatches,
45
matches,
@@ -42,6 +43,8 @@ function queryAllByLabelText(
4243
text,
4344
{selector = '*', exact = true, collapseWhitespace, trim, normalizer} = {},
4445
) {
46+
checkContainerType(container)
47+
4548
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
4649
const labels = queryAllLabelsByText(container, text, {
4750
exact,

src/queries/placeholder-text.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import {queryAllByAttribute, buildQueries} from './all-utils'
21
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
2+
import {checkContainerType} from '../helpers'
3+
import {queryAllByAttribute, buildQueries} from './all-utils'
34

45
function queryAllByPlaceholderText(...args) {
6+
checkContainerType(...args)
57
return queryAllByAttribute('placeholder', ...args)
68
}
79
const getMultipleError = (c, text) =>

src/queries/role.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import {
77
isInaccessible,
88
isSubtreeInaccessible,
99
} from '../role-helpers'
10+
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
11+
import {checkContainerType} from '../helpers'
1012
import {
1113
buildQueries,
1214
fuzzyMatches,
1315
getConfig,
1416
makeNormalizer,
1517
matches,
1618
} from './all-utils'
17-
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
1819

1920
function queryAllByRole(
2021
container,
@@ -30,6 +31,7 @@ function queryAllByRole(
3031
selected,
3132
} = {},
3233
) {
34+
checkContainerType(container)
3335
const matcher = exact ? matches : fuzzyMatches
3436
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
3537

src/queries/test-id.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import {queryAllByAttribute, getConfig, buildQueries} from './all-utils'
1+
import {checkContainerType} from '../helpers'
22
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
3+
import {queryAllByAttribute, getConfig, buildQueries} from './all-utils'
34

45
const getTestIdAttribute = () => getConfig().testIdAttribute
56

67
function queryAllByTestId(...args) {
8+
checkContainerType(...args)
79
return queryAllByAttribute(getTestIdAttribute(), ...args)
810
}
911

src/queries/text.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
2+
import {checkContainerType} from '../helpers'
23
import {DEFAULT_IGNORE_TAGS} from '../config'
34
import {
45
fuzzyMatches,
@@ -20,6 +21,7 @@ function queryAllByText(
2021
normalizer,
2122
} = {},
2223
) {
24+
checkContainerType(container)
2325
const matcher = exact ? matches : fuzzyMatches
2426
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
2527
let baseArray = []

src/queries/title.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1+
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
2+
import {checkContainerType} from '../helpers'
13
import {
24
fuzzyMatches,
35
matches,
46
makeNormalizer,
57
getNodeText,
68
buildQueries,
79
} from './all-utils'
8-
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
910

1011
function queryAllByTitle(
1112
container,
1213
text,
1314
{exact = true, collapseWhitespace, trim, normalizer} = {},
1415
) {
16+
checkContainerType(container)
1517
const matcher = exact ? matches : fuzzyMatches
1618
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
1719
return Array.from(container.querySelectorAll('[title], svg > title')).filter(

0 commit comments

Comments
 (0)