Skip to content

Commit

Permalink
fix(no-await-sync-events): false positive for delay from vars (#641)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
sjarva committed Sep 7, 2022
1 parent 60fc742 commit 3c2cbbd
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 2 deletions.
43 changes: 41 additions & 2 deletions lib/rules/no-await-sync-events.ts
Expand Up @@ -56,13 +56,38 @@ export default createTestingLibraryRule<Options, MessageIds>({

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

// userEvent.type() and userEvent.keyboard() are exceptions, which returns a
// Promise. But it is only necessary to wait when delay option other than 0
// is specified. So this rule has a special exception for the case await:
// - userEvent.type(element, 'abc', {delay: 1234})
// - userEvent.keyboard('abc', {delay: 1234})
return {
VariableDeclaration(node: TSESTree.VariableDeclaration) {
// Case delay has been declared outside of call expression's arguments
// Let's save the info if it is greater than zero
hasDelayDeclarationOrAssignmentGTZero = node.declarations.some(
(property) =>
ASTUtils.isIdentifier(property.id) &&
property.id.name === 'delay' &&
isLiteral(property.init) &&
property.init.value &&
property.init.value > 0
);
},
AssignmentExpression(node: TSESTree.AssignmentExpression) {
// Case delay has been assigned or re-assigned outside of call expression's arguments
// Let's save the info if it is greater than zero
if (
ASTUtils.isIdentifier(node.left) &&
node.left.name === 'delay' &&
isLiteral(node.right) &&
node.right.value !== null
) {
hasDelayDeclarationOrAssignmentGTZero = node.right.value > 0;
}
},
'AwaitExpression > CallExpression'(node: TSESTree.CallExpression) {
const simulateEventFunctionIdentifier = getDeepestIdentifierNode(node);

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

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

const hasDelay =
// Checking if there's a delay property
// Note: delay's value may have declared or assigned somewhere else (as a variable declaration or as an assignment expression)
// or right after this (as a literal)
const hasDelayProperty =
isObjectExpression(lastArg) &&
lastArg.properties.some(
(property) =>
isProperty(property) &&
ASTUtils.isIdentifier(property.key) &&
property.key.name === 'delay'
);

// In case delay's value has been declared as a literal
const hasDelayLiteralGTZero =
isObjectExpression(lastArg) &&
lastArg.properties.some(
(property) =>
Expand All @@ -107,7 +145,8 @@ export default createTestingLibraryRule<Options, MessageIds>({

if (
USER_EVENT_ASYNC_EXCEPTIONS.includes(simulateEventFunctionName) &&
hasDelay
hasDelayProperty &&
(hasDelayDeclarationOrAssignmentGTZero || hasDelayLiteralGTZero)
) {
return;
}
Expand Down
73 changes: 73 additions & 0 deletions tests/lib/rules/no-await-sync-events.test.ts
Expand Up @@ -148,6 +148,28 @@ ruleTester.run(RULE_NAME, rule, {
code: `() => {
await userEvent.keyboard('foo', {delay: 1234})
}
`,
},
{
code: `async() => {
const delay = 10
await userEvent.keyboard('foo', {delay})
}
`,
},
{
code: `async() => {
const delay = 10
await userEvent.type(element, text, {delay})
}
`,
},
{
code: `async() => {
let delay = 0
delay = 10
await userEvent.type(element, text, {delay})
}
`,
},
{
Expand Down Expand Up @@ -369,5 +391,56 @@ ruleTester.run(RULE_NAME, rule, {
},
],
},
{
code: `async() => {
const delay = 0
await userEvent.type('foo', { delay });
}
`,
errors: [
{
line: 3,
column: 17,
messageId: 'noAwaitSyncEvents',
data: { name: 'userEvent.type' },
},
],
},
{
code: `async() => {
const delay = 0
const somethingElse = true
const skipHover = true
await userEvent.type('foo', { delay, skipHover });
}
`,
errors: [
{
line: 5,
column: 17,
messageId: 'noAwaitSyncEvents',
data: { name: 'userEvent.type' },
},
],
},
{
code: `async() => {
let delay = 0
const somethingElse = true
const skipHover = true
delay = 15
delay = 0
await userEvent.type('foo', { delay, skipHover });
}
`,
errors: [
{
line: 7,
column: 17,
messageId: 'noAwaitSyncEvents',
data: { name: 'userEvent.type' },
},
],
},
],
});

0 comments on commit 3c2cbbd

Please sign in to comment.