Skip to content

Commit

Permalink
Merge pull request #100 from zhyd1997/fix/await-interactions-rule-scope
Browse files Browse the repository at this point in the history
fix: await interactions rule scope.
  • Loading branch information
yannbf authored Oct 11, 2022
2 parents 9fc571b + 51c986a commit aa4a242
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 9 deletions.
41 changes: 39 additions & 2 deletions lib/rules/await-interactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
* @author Yann Braga
*/

import type { CallExpression, Identifier, Node } from '@typescript-eslint/types/dist/ast-spec'
import type {
ImportDeclaration,
CallExpression,
Identifier,
Node,
VariableDeclarator,
} from '@typescript-eslint/types/dist/ast-spec'

import { createStorybookRule } from '../utils/create-storybook-rule'
import { CategoryId } from '../utils/constants'
Expand All @@ -18,6 +24,7 @@ import {
isFunctionDeclaration,
isFunctionExpression,
isProgram,
isImportSpecifier,
} from '../utils/ast'
import { ReportFixFunction } from '@typescript-eslint/experimental-utils/dist/ts-eslint'

Expand Down Expand Up @@ -130,24 +137,54 @@ export = createStorybookRule({
return getClosestFunctionAncestor(parent)
}

const isUserEventFromStorybookImported = (node: ImportDeclaration) => {
return (
node.source.value === '@storybook/testing-library' &&
node.specifiers.find(
(spec) =>
isImportSpecifier(spec) &&
spec.imported.name === 'userEvent' &&
spec.local.name === 'userEvent'
) !== undefined
)
}

const isExpectFromStorybookImported = (node: ImportDeclaration) => {
return (
node.source.value === '@storybook/jest' &&
node.specifiers.find(
(spec) => isImportSpecifier(spec) && spec.imported.name === 'expect'
) !== undefined
)
}

//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
/**
* @param {import('eslint').Rule.Node} node
*/

let isImportedFromStorybook = true
let invocationsThatShouldBeAwaited = [] as Array<{ node: Node; method: Identifier }>

return {
ImportDeclaration(node: ImportDeclaration) {
isImportedFromStorybook =
isUserEventFromStorybookImported(node) || isExpectFromStorybookImported(node)
},
VariableDeclarator(node: VariableDeclarator) {
isImportedFromStorybook =
isImportedFromStorybook && isIdentifier(node.id) && node.id.name !== 'userEvent'
},
CallExpression(node: CallExpression) {
const method = getMethodThatShouldBeAwaited(node)
if (method && !isAwaitExpression(node.parent) && !isAwaitExpression(node.parent?.parent)) {
invocationsThatShouldBeAwaited.push({ node, method })
}
},
'Program:exit': function () {
if (invocationsThatShouldBeAwaited.length) {
if (isImportedFromStorybook && invocationsThatShouldBeAwaited.length) {
invocationsThatShouldBeAwaited.forEach(({ node, method }) => {
const parentFnNode = getClosestFunctionAncestor(node)
const parentFnNeedsAsync =
Expand Down
45 changes: 38 additions & 7 deletions tests/lib/rules/await-interactions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,22 @@ ruleTester.run('await-interactions', rule, {
await waitForElementToBeRemoved(() => userEvent.hover(canvas.getByTestId('password-error-info')))
}
`,
// // @TODO: https:/storybookjs/eslint-plugin-storybook/issues/47
// dedent`
// import { userEvent } from '../utils'
// Basic.play = async () => {
// userEvent.click(canvas.getByRole('button'))
// }
// `,
dedent`
import { userEvent } from '../utils'
import { within } from '@storybook/testing-library'
Basic.play = async (context) => {
const canvas = within(context)
userEvent.click(canvas.getByRole('button'))
}
`,
dedent`
Basic.play = async () => {
const userEvent = { test: () => {} }
// should not complain
userEvent.test()
}
`,
// // @TODO: https:/storybookjs/eslint-plugin-storybook/issues/28
// dedent`
// Block.parameters = {
Expand Down Expand Up @@ -103,6 +112,28 @@ ruleTester.run('await-interactions', rule, {
`,
],
invalid: [
{
code: dedent`
import { expect } from '@storybook/jest'
WithModalOpen.play = async ({ args }) => {
// should complain
expect(args.onClick).toHaveBeenCalled()
}
`,
output: dedent`
import { expect } from '@storybook/jest'
WithModalOpen.play = async ({ args }) => {
// should complain
await expect(args.onClick).toHaveBeenCalled()
}
`,
errors: [
{
messageId: 'interactionShouldBeAwaited',
data: { method: 'toHaveBeenCalled' },
},
],
},
{
code: dedent`
WithModalOpen.play = ({ canvasElement }) => {
Expand Down

0 comments on commit aa4a242

Please sign in to comment.