Skip to content

Commit 3c2cbbd

Browse files
authored
fix(no-await-sync-events): false positive for delay from vars (#641)
fix: no-await-sync-events false positive Check if delay is declared or assigned a value elsewhere than in the call expression's arguments. Add test cases that have declarations and assignments. Closes #403
1 parent 60fc742 commit 3c2cbbd

File tree

2 files changed

+114
-2
lines changed

2 files changed

+114
-2
lines changed

lib/rules/no-await-sync-events.ts

+41-2
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,38 @@ export default createTestingLibraryRule<Options, MessageIds>({
5656

5757
create(context, [options], helpers) {
5858
const { eventModules = VALID_EVENT_MODULES } = options;
59+
let hasDelayDeclarationOrAssignmentGTZero: boolean;
5960

6061
// userEvent.type() and userEvent.keyboard() are exceptions, which returns a
6162
// Promise. But it is only necessary to wait when delay option other than 0
6263
// is specified. So this rule has a special exception for the case await:
6364
// - userEvent.type(element, 'abc', {delay: 1234})
6465
// - userEvent.keyboard('abc', {delay: 1234})
6566
return {
67+
VariableDeclaration(node: TSESTree.VariableDeclaration) {
68+
// Case delay has been declared outside of call expression's arguments
69+
// Let's save the info if it is greater than zero
70+
hasDelayDeclarationOrAssignmentGTZero = node.declarations.some(
71+
(property) =>
72+
ASTUtils.isIdentifier(property.id) &&
73+
property.id.name === 'delay' &&
74+
isLiteral(property.init) &&
75+
property.init.value &&
76+
property.init.value > 0
77+
);
78+
},
79+
AssignmentExpression(node: TSESTree.AssignmentExpression) {
80+
// Case delay has been assigned or re-assigned outside of call expression's arguments
81+
// Let's save the info if it is greater than zero
82+
if (
83+
ASTUtils.isIdentifier(node.left) &&
84+
node.left.name === 'delay' &&
85+
isLiteral(node.right) &&
86+
node.right.value !== null
87+
) {
88+
hasDelayDeclarationOrAssignmentGTZero = node.right.value > 0;
89+
}
90+
},
6691
'AwaitExpression > CallExpression'(node: TSESTree.CallExpression) {
6792
const simulateEventFunctionIdentifier = getDeepestIdentifierNode(node);
6893

@@ -91,7 +116,20 @@ export default createTestingLibraryRule<Options, MessageIds>({
91116

92117
const lastArg = node.arguments[node.arguments.length - 1];
93118

94-
const hasDelay =
119+
// Checking if there's a delay property
120+
// Note: delay's value may have declared or assigned somewhere else (as a variable declaration or as an assignment expression)
121+
// or right after this (as a literal)
122+
const hasDelayProperty =
123+
isObjectExpression(lastArg) &&
124+
lastArg.properties.some(
125+
(property) =>
126+
isProperty(property) &&
127+
ASTUtils.isIdentifier(property.key) &&
128+
property.key.name === 'delay'
129+
);
130+
131+
// In case delay's value has been declared as a literal
132+
const hasDelayLiteralGTZero =
95133
isObjectExpression(lastArg) &&
96134
lastArg.properties.some(
97135
(property) =>
@@ -107,7 +145,8 @@ export default createTestingLibraryRule<Options, MessageIds>({
107145

108146
if (
109147
USER_EVENT_ASYNC_EXCEPTIONS.includes(simulateEventFunctionName) &&
110-
hasDelay
148+
hasDelayProperty &&
149+
(hasDelayDeclarationOrAssignmentGTZero || hasDelayLiteralGTZero)
111150
) {
112151
return;
113152
}

tests/lib/rules/no-await-sync-events.test.ts

+73
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,28 @@ ruleTester.run(RULE_NAME, rule, {
148148
code: `() => {
149149
await userEvent.keyboard('foo', {delay: 1234})
150150
}
151+
`,
152+
},
153+
{
154+
code: `async() => {
155+
const delay = 10
156+
await userEvent.keyboard('foo', {delay})
157+
}
158+
`,
159+
},
160+
{
161+
code: `async() => {
162+
const delay = 10
163+
await userEvent.type(element, text, {delay})
164+
}
165+
`,
166+
},
167+
{
168+
code: `async() => {
169+
let delay = 0
170+
delay = 10
171+
await userEvent.type(element, text, {delay})
172+
}
151173
`,
152174
},
153175
{
@@ -369,5 +391,56 @@ ruleTester.run(RULE_NAME, rule, {
369391
},
370392
],
371393
},
394+
{
395+
code: `async() => {
396+
const delay = 0
397+
await userEvent.type('foo', { delay });
398+
}
399+
`,
400+
errors: [
401+
{
402+
line: 3,
403+
column: 17,
404+
messageId: 'noAwaitSyncEvents',
405+
data: { name: 'userEvent.type' },
406+
},
407+
],
408+
},
409+
{
410+
code: `async() => {
411+
const delay = 0
412+
const somethingElse = true
413+
const skipHover = true
414+
await userEvent.type('foo', { delay, skipHover });
415+
}
416+
`,
417+
errors: [
418+
{
419+
line: 5,
420+
column: 17,
421+
messageId: 'noAwaitSyncEvents',
422+
data: { name: 'userEvent.type' },
423+
},
424+
],
425+
},
426+
{
427+
code: `async() => {
428+
let delay = 0
429+
const somethingElse = true
430+
const skipHover = true
431+
delay = 15
432+
delay = 0
433+
await userEvent.type('foo', { delay, skipHover });
434+
}
435+
`,
436+
errors: [
437+
{
438+
line: 7,
439+
column: 17,
440+
messageId: 'noAwaitSyncEvents',
441+
data: { name: 'userEvent.type' },
442+
},
443+
],
444+
},
372445
],
373446
});

0 commit comments

Comments
 (0)