Skip to content

Commit db61b10

Browse files
committed
feat: allow baseElement and container in prefer-screen-queries
1 parent 7b82eb9 commit db61b10

File tree

4 files changed

+101
-9
lines changed

4 files changed

+101
-9
lines changed

docs/rules/prefer-screen-queries.md

+5
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ const { rerender, unmount, asFragment } = render(<Foo />);
6060
rerender(<Foo />);
6161
asFragment();
6262
unmount();
63+
64+
// using baseElement
65+
const { getByText } = render(<Foo />, { baseElement: treeA });
66+
// using container
67+
const { getAllByText } = render(<Foo />, { container: treeA });
6368
```
6469

6570
## Further Reading

lib/node-utils.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { TSESTree } from '@typescript-eslint/experimental-utils';
1+
import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/experimental-utils';
22

33
export function isCallExpression(
44
node: TSESTree.Node
@@ -105,4 +105,8 @@ export function hasThenProperty(node: TSESTree.Node) {
105105

106106
export function isArrowFunctionExpression(node: TSESTree.Node): node is TSESTree.ArrowFunctionExpression {
107107
return node && node.type === 'ArrowFunctionExpression'
108+
}
109+
110+
export function isObjectExpression(node: TSESTree.Expression): node is TSESTree.ObjectExpression {
111+
return node?.type === AST_NODE_TYPES.ObjectExpression
108112
}

lib/rules/prefer-screen-queries.ts

+18-4
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,21 @@ import {
66
isCallExpression,
77
isProperty,
88
isIdentifier,
9+
isObjectExpression,
910
} from '../node-utils';
1011

1112
export const RULE_NAME = 'prefer-screen-queries';
1213
export type MessageIds = 'preferScreenQueries';
1314
type Options = [];
1415

16+
const ALLOWED_RENDER_PROPERTIES_FOR_DESTRUCTURING = ['container', 'baseElement']
1517
const ALL_QUERIES_COMBINATIONS_REGEXP = ALL_QUERIES_COMBINATIONS.join('|');
1618

19+
function usesContainerOrBaseElement(node: TSESTree.CallExpression) {
20+
const secondArgument = node.arguments[1]
21+
return isObjectExpression(secondArgument) && secondArgument.properties.some((property) => isProperty(property) && isIdentifier(property.key) && ALLOWED_RENDER_PROPERTIES_FOR_DESTRUCTURING.includes(property.key.name))
22+
}
23+
1724
export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
1825
name: RULE_NAME,
1926
meta: {
@@ -50,9 +57,14 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
5057

5158
return {
5259
VariableDeclarator(node) {
53-
const isWithinFunction = isCallExpression(node.init) && isIdentifier(node.init.callee) && node.init.callee.name === 'within';
60+
if (!isCallExpression(node.init) || !isIdentifier(node.init.callee)) {
61+
return
62+
}
63+
const isWithinFunction = node.init.callee.name === 'within';
64+
// TODO add the custom render option #198
65+
const usesRenderOptions = node.init.callee.name === 'render' && usesContainerOrBaseElement(node.init);
5466

55-
if (!isWithinFunction) {
67+
if (!isWithinFunction && !usesRenderOptions) {
5668
return
5769
}
5870

@@ -94,11 +106,13 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
94106
isMemberExpression(node.parent) &&
95107
isCallExpression(node.parent.object) &&
96108
isIdentifier(node.parent.object.callee) &&
97-
node.parent.object.callee.name !== 'within'
109+
node.parent.object.callee.name !== 'within' &&
110+
node.parent.object.callee.name === 'render' && !usesContainerOrBaseElement(node.parent.object)
98111
) {
99112
reportInvalidUsage(node);
100113
return;
101114
}
115+
102116
if (
103117
isMemberExpression(node.parent) &&
104118
isIdentifier(node.parent.object) &&
@@ -109,4 +123,4 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
109123
},
110124
};
111125
},
112-
});
126+
});

tests/lib/rules/prefer-screen-queries.test.ts

+73-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ const ruleTester = createRuleTester();
66

77
ruleTester.run(RULE_NAME, rule, {
88
valid: [
9+
{
10+
code: `const baz = () => 'foo'`
11+
},
912
...ALL_QUERIES_COMBINATIONS.map(queryMethod => ({
1013
code: `screen.${queryMethod}()`,
1114
})),
@@ -80,12 +83,55 @@ ruleTester.run(RULE_NAME, rule, {
8083
const utils = render(baz);
8184
utils.unmount();
8285
`
83-
}
86+
},
87+
...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({
88+
code: `
89+
const { ${queryMethod} } = render(baz, { baseElement: treeA })
90+
expect(${queryMethod}(baz)).toBeDefined()
91+
`
92+
})),
93+
...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({
94+
code: `
95+
const { ${queryMethod}: aliasMethod } = render(baz, { baseElement: treeA })
96+
expect(aliasMethod(baz)).toBeDefined()
97+
`
98+
})),
99+
...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({
100+
code: `
101+
const { ${queryMethod} } = render(baz, { container: treeA })
102+
expect(${queryMethod}(baz)).toBeDefined()
103+
`
104+
})),
105+
...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({
106+
code: `
107+
const { ${queryMethod}: aliasMethod } = render(baz, { container: treeA })
108+
expect(aliasMethod(baz)).toBeDefined()
109+
`
110+
})),
111+
...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({
112+
code: `
113+
const { ${queryMethod} } = render(baz, { baseElement: treeB, container: treeA })
114+
expect(${queryMethod}(baz)).toBeDefined()
115+
`
116+
})),
117+
...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({
118+
code: `
119+
const { ${queryMethod}: aliasMethod } = render(baz, { baseElement: treeB, container: treeA })
120+
expect(aliasMethod(baz)).toBeDefined()
121+
`
122+
})),
123+
...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({
124+
code: `
125+
render(foo, { baseElement: treeA }).${queryMethod}()
126+
`
127+
}))
84128
],
85129

86130
invalid: [
87131
...ALL_QUERIES_COMBINATIONS.map(queryMethod => ({
88-
code: `${queryMethod}()`,
132+
code: `
133+
const { ${queryMethod} } = render(foo)
134+
${queryMethod}()`,
89135
errors: [
90136
{
91137
messageId: 'preferScreenQueries',
@@ -95,7 +141,6 @@ ruleTester.run(RULE_NAME, rule, {
95141
},
96142
],
97143
})),
98-
99144
...ALL_QUERIES_COMBINATIONS.map(queryMethod => ({
100145
code: `render().${queryMethod}()`,
101146
errors: [
@@ -107,7 +152,17 @@ ruleTester.run(RULE_NAME, rule, {
107152
},
108153
],
109154
})),
110-
155+
...ALL_QUERIES_COMBINATIONS.map(queryMethod => ({
156+
code: `render(foo, { hydrate: true }).${queryMethod}()`,
157+
errors: [
158+
{
159+
messageId: 'preferScreenQueries',
160+
data: {
161+
name: queryMethod,
162+
},
163+
},
164+
],
165+
})),
111166
...ALL_QUERIES_COMBINATIONS.map(queryMethod => ({
112167
code: `component.${queryMethod}()`,
113168
errors: [
@@ -161,6 +216,20 @@ ruleTester.run(RULE_NAME, rule, {
161216
},
162217
],
163218
})),
219+
...ALL_QUERIES_COMBINATIONS.map(queryMethod => ({
220+
code: `
221+
const { ${queryMethod} } = render(baz, { hydrate: true })
222+
${queryMethod}(baz)
223+
`,
224+
errors: [
225+
{
226+
messageId: 'preferScreenQueries',
227+
data: {
228+
name: queryMethod,
229+
},
230+
},
231+
],
232+
})),
164233
...ALL_QUERIES_COMBINATIONS.map(queryMethod => ({
165234
code: `
166235
const [myVariable] = within()

0 commit comments

Comments
 (0)