Skip to content

Commit

Permalink
feat(no-useless-await): Remove useless awaits from expect methods
Browse files Browse the repository at this point in the history
Fixes #306
  • Loading branch information
mskelton committed Oct 19, 2024
1 parent ef8cfa5 commit e283efc
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 23 deletions.
9 changes: 9 additions & 0 deletions docs/rules/no-useless-await.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ Examples of **incorrect** code for this rule:
```javascript
await page.locator('.my-element')
await page.getByRole('.my-element')

await expect(1).toBe(1)
await expect(true).toBeTruthy()
```

Examples of **correct** code for this rule:
Expand All @@ -20,4 +23,10 @@ page.getByRole('.my-element')

await page.$('.my-element')
await page.goto('.my-element')

expect(1).toBe(1)
expect(true).toBeTruthy()

await expect(page.locator('.foo')).toBeVisible()
await expect(page.locator('.foo')).toHaveText('bar')
```
24 changes: 24 additions & 0 deletions src/rules/no-useless-await.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,23 @@ runRuleTester('no-useless-await', rule, {
errors: [{ column: 1, endColumn: 6, line: 1, messageId }],
output: 'frame.parentFrame()',
},

// Expect methods
{
code: 'await expect(true).toBe(true)',
errors: [{ column: 1, endColumn: 6, line: 1, messageId }],
output: 'expect(true).toBe(true)',
},
{
code: 'await expect(true).toBeTruthy()',
errors: [{ column: 1, endColumn: 6, line: 1, messageId }],
output: 'expect(true).toBeTruthy()',
},
{
code: 'await expect(true).toEqual(true)',
errors: [{ column: 1, endColumn: 6, line: 1, messageId }],
output: 'expect(true).toEqual(true)',
},
],
valid: [
'await foo()',
Expand All @@ -195,5 +212,12 @@ runRuleTester('no-useless-await', rule, {
'await page.waitForLoadState({ waitUntil: "load" })',
'await page.waitForURL(url, { waitUntil: "load" })',
'await page.locator(".hello-world").waitFor()',

'expect(true).toBe(true)',
'expect(true).toBeTruthy()',
'expect(true).toEqual(true)',

'await expect(page.locator(".my-element")).toBeVisible()',
'await expect(page.locator(".my-element")).toHaveText("test")',
],
})
82 changes: 59 additions & 23 deletions src/rules/no-useless-await.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Rule } from 'eslint'
import ESTree from 'estree'
import { getStringValue, isPageMethod } from '../utils/ast'
import { createRule } from '../utils/createRule'
import { parseFnCall } from '../utils/parseFnCall'

const locatorMethods = new Set([
'and',
Expand Down Expand Up @@ -38,6 +40,32 @@ const pageMethods = new Set([
'workers',
])

const expectMatchers = new Set([
'toBe',
'toBeCloseTo',
'toBeDefined',
'toBeFalsy',
'toBeGreaterThan',
'toBeGreaterThanOrEqual',
'toBeInstanceOf',
'toBeLessThan',
'toBeLessThanOrEqual',
'toBeNaN',
'toBeNull',
'toBeTruthy',
'toBeUndefined',
'toContain',
'toContainEqual',
'toEqual',
'toHaveLength',
'toHaveProperty',
'toMatch',
'toMatchObject',
'toStrictEqual',
'toThrow',
'toThrowError',
])

function isSupportedMethod(node: ESTree.CallExpression) {
if (node.callee.type !== 'MemberExpression') return false

Expand All @@ -50,32 +78,40 @@ function isSupportedMethod(node: ESTree.CallExpression) {

export default createRule({
create(context) {
return {
AwaitExpression(node) {
// Must be a call expression
if (node.argument.type !== 'CallExpression') return

// Must be a foo.bar() call, bare calls are ignored
const { callee } = node.argument
if (callee.type !== 'MemberExpression') return
function fix(node: ESTree.Node) {
const start = node.loc!.start
const range = node.range!

// Must be a method we care about
if (!isSupportedMethod(node.argument)) return
context.report({
fix: (fixer) => fixer.removeRange([range[0], range[0] + 6]),
loc: {
end: {
column: start.column + 5,
line: start.line,
},
start,
},
messageId: 'noUselessAwait',
})
}

const start = node.loc!.start
const range = node.range!
return {
'AwaitExpression > CallExpression'(
node: ESTree.CallExpression & Rule.NodeParentExtension,
) {
// await page.locator('.foo')
if (
node.callee.type === 'MemberExpression' &&
isSupportedMethod(node)
) {
return fix(node.parent)
}

context.report({
fix: (fixer) => fixer.removeRange([range[0], range[0] + 6]),
loc: {
end: {
column: start.column + 5,
line: start.line,
},
start,
},
messageId: 'noUselessAwait',
})
// await expect(true).toBe(true)
const call = parseFnCall(context, node)
if (call?.type === 'expect' && expectMatchers.has(call.matcherName)) {
return fix(node.parent)
}
},
}
},
Expand Down

0 comments on commit e283efc

Please sign in to comment.