From 6a84431e7f3138b5139b329d58d55753a6b5b3c3 Mon Sep 17 00:00:00 2001 From: Mark Skelton Date: Fri, 28 Jun 2024 11:00:44 -0500 Subject: [PATCH] feat: Support `test.expect` style Fixes #297 --- src/rules/expect-expect.test.ts | 1 + src/utils/ast.ts | 7 +++++-- src/utils/parseFnCall.test.ts | 26 ++++++++++++++++++++++++++ src/utils/parseFnCall.ts | 15 +++++++++++---- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/rules/expect-expect.test.ts b/src/rules/expect-expect.test.ts index 7912976..817bd4f 100644 --- a/src/rules/expect-expect.test.ts +++ b/src/rules/expect-expect.test.ts @@ -46,6 +46,7 @@ runRuleTester('expect-expect', rule, { '["bar"]();', 'testing("will test something eventually", () => {})', 'test("should pass", () => expect(true).toBeDefined())', + 'test("should pass", () => test.expect(true).toBeDefined())', 'test.slow("should pass", () => expect(true).toBeDefined())', 'test.skip("should pass", () => expect(true).toBeDefined())', // Config methods diff --git a/src/utils/ast.ts b/src/utils/ast.ts index 37c6d80..ffa0b65 100644 --- a/src/utils/ast.ts +++ b/src/utils/ast.ts @@ -19,9 +19,12 @@ export function getRawValue(node: ESTree.Node) { return node.type === 'Literal' ? node.raw : undefined } -export function isIdentifier(node: ESTree.Node, name?: string | RegExp) { +export function isIdentifier( + node: ESTree.Node | undefined, + name?: string | RegExp, +) { return ( - node.type === 'Identifier' && + node?.type === 'Identifier' && (!name || (typeof name === 'string' ? node.name === name : name.test(node.name))) ) diff --git a/src/utils/parseFnCall.test.ts b/src/utils/parseFnCall.test.ts index 82bbfa2..edb1699 100644 --- a/src/utils/parseFnCall.test.ts +++ b/src/utils/parseFnCall.test.ts @@ -464,6 +464,32 @@ runRuleTester('expect', rule, { `, errors: [{ column: 1, line: 3, messageId: 'modifier-unknown' }], }, + { + code: 'test.expect(x).toBe(y);', + errors: [ + { + column: 1, + data: expectedParsedFnCallResultData({ + args: ['x'], + group: 'expect', + head: { + local: 'test', + node: 'test', + original: null, + }, + matcher: 'toBe', + matcherArgs: ['y'], + matcherName: 'toBe', + members: ['toBe'], + modifiers: [], + name: 'expect', + type: 'expect', + }), + line: 1, + messageId: 'details', + }, + ], + }, ], valid: [], }) diff --git a/src/utils/parseFnCall.ts b/src/utils/parseFnCall.ts index 7a8765b..9a0dbf9 100644 --- a/src/utils/parseFnCall.ts +++ b/src/utils/parseFnCall.ts @@ -329,10 +329,6 @@ function parse( let name = resolved.original ?? resolved.local const links = [name, ...rest.map((link) => getStringValue(link))] - if (name !== 'expect' && !VALID_CHAINS.has(links.join('.'))) { - return null - } - // To support Playwright's convention of `test.describe`, `test.beforeEach`, // etc. we need to test the second link in the chain to find the true type. if (name === 'test' && links.length > 1) { @@ -344,6 +340,10 @@ function parse( } } + if (name !== 'expect' && !VALID_CHAINS.has(links.join('.'))) { + return null + } + const parsedFnCall: Omit = { head: { ...resolved, node: first }, // every member node must have a member expression as their parent @@ -355,6 +355,13 @@ function parse( const group = determinePlaywrightFnGroup(name) if (group === 'expect') { + // If using `test.expect` style, the `rest` array will start with `expect` + // and we need to remove it to ensure the chain accurately represents the + // `expect` call chain. + if (isIdentifier(rest[0], 'expect')) { + parsedFnCall.members.shift() + } + const result = parseExpectCall(parsedFnCall) // If the `expect` call chain is not valid, only report on the topmost node