Skip to content

Commit b434289

Browse files
authored
Fix standalone expect in fixture (#403)
1 parent fdd0253 commit b434289

File tree

2 files changed

+58
-4
lines changed

2 files changed

+58
-4
lines changed

src/rules/no-standalone-expect.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,5 +128,50 @@ runRuleTester('no-standalone-expect', rule, {
128128
},
129129
},
130130
},
131+
// Fixtures
132+
{
133+
code: javascript`
134+
import { test as base, expect } from '@playwright/test';
135+
136+
export const test = base.extend({
137+
clipboardContents: async ({ page }, use) => {
138+
await expect(page).toHaveURL('example.com');
139+
const text = await page.evaluate(() => navigator.clipboard.readText());
140+
await expect(page).toHaveURL('example.com');
141+
await use(text);
142+
}
143+
});
144+
145+
export { expect } from '@playwright/test';
146+
`,
147+
name: 'Allows expect in fixture definitions',
148+
},
149+
{
150+
code: javascript`
151+
import { test, expect } from '@playwright/test';
152+
153+
const customTest = test.extend({
154+
myFixture: async ({ page }, use) => {
155+
await expect(page).toBeDefined();
156+
await use(page);
157+
}
158+
});
159+
`,
160+
name: 'Allows expect in test.extend fixtures',
161+
},
162+
{
163+
code: javascript`
164+
import { test as base } from '@playwright/test';
165+
166+
const test = base.extend({
167+
fixture: async ({}, use) => {
168+
const value = await Promise.resolve(42);
169+
expect(value).toBe(42);
170+
await use(value);
171+
}
172+
});
173+
`,
174+
name: 'Allows expect in fixture without await',
175+
},
131176
],
132177
})

src/rules/no-standalone-expect.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Rule } from 'eslint'
22
import * as ESTree from 'estree'
3-
import { getParent, isFunction } from '../utils/ast.js'
3+
import { getParent, isFunction, isPropertyAccessor } from '../utils/ast.js'
44
import { createRule } from '../utils/createRule.js'
55
import { isTypeOfFnCall, parseFnCall } from '../utils/parseFnCall.js'
66

@@ -47,6 +47,7 @@ const getBlockType = (
4747
type BlockType =
4848
| 'arrow'
4949
| 'describe'
50+
| 'fixture'
5051
| 'function'
5152
| 'hook'
5253
| 'template'
@@ -67,7 +68,6 @@ export default createRule({
6768
callStack.pop()
6869
}
6970
},
70-
7171
BlockStatement(statement) {
7272
const blockType = getBlockType(context, statement)
7373

@@ -80,7 +80,6 @@ export default createRule({
8080
callStack.pop()
8181
}
8282
},
83-
8483
CallExpression(node) {
8584
const call = parseFnCall(context, node)
8685

@@ -108,6 +107,13 @@ export default createRule({
108107
callStack.push('hook')
109108
}
110109

110+
if (
111+
node.callee.type === 'MemberExpression' &&
112+
isPropertyAccessor(node.callee, 'extend')
113+
) {
114+
callStack.push('fixture')
115+
}
116+
111117
if (node.callee.type === 'TaggedTemplateExpression') {
112118
callStack.push('template')
113119
}
@@ -120,7 +126,10 @@ export default createRule({
120126
isTypeOfFnCall(context, node, ['test']) &&
121127
node.callee.type !== 'MemberExpression') ||
122128
(top === 'template' &&
123-
node.callee.type === 'TaggedTemplateExpression')
129+
node.callee.type === 'TaggedTemplateExpression') ||
130+
(top === 'fixture' &&
131+
node.callee.type === 'MemberExpression' &&
132+
isPropertyAccessor(node.callee, 'extend'))
124133
) {
125134
callStack.pop()
126135
}

0 commit comments

Comments
 (0)