Skip to content

Commit 420ee4c

Browse files
committed
feat: Add expect-expect rule
1 parent 654d716 commit 420ee4c

File tree

5 files changed

+102
-0
lines changed

5 files changed

+102
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ command line option.\
5757

5858
|| 🔧 | 💡 | Rule | Description |
5959
| :-: | :-: | :-: | --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
60+
|| | | [expect-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/expect-expect.md) | Enforce assertion to be made in a test body |
6061
|| | | [max-nested-describe](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/max-nested-describe.md) | Enforces a maximum depth to nested describe calls |
6162
|| 🔧 | | [missing-playwright-await](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/missing-playwright-await.md) | Enforce Playwright APIs to be awaited |
6263
|| | | [no-conditional-in-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-conditional-in-test.md) | Disallow conditional logic in tests |

docs/rules/expect-expect.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Enforce assertion to be made in a test body (`expect-expect`)
2+
3+
Ensure that there is at least one `expect` call made in a test.
4+
5+
## Rule Details
6+
7+
Examples of **incorrect** code for this rule:
8+
9+
```javascript
10+
test('should be a test', () => {
11+
console.log('no assertion');
12+
});
13+
14+
test('should assert something', () => {});
15+
```
16+
17+
Examples of **correct** code for this rule:
18+
19+
```javascript
20+
test('should be a test', async () => {
21+
await expect(page).toHaveTitle('foo');
22+
});
23+
24+
test('should work with callbacks/async', async () => {
25+
await test.step('step 1', async () => {
26+
await expect(page).toHaveTitle('foo');
27+
});
28+
});
29+
```

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import expectExpect from './rules/expect-expect';
12
import maxNestedDescribe from './rules/max-nested-describe';
23
import missingPlaywrightAwait from './rules/missing-playwright-await';
34
import noConditionalInTest from './rules/no-conditional-in-test';
@@ -30,6 +31,7 @@ const recommended = {
3031
plugins: ['playwright'],
3132
rules: {
3233
'no-empty-pattern': 'off',
34+
'playwright/expect-expect': 'warn',
3335
'playwright/max-nested-describe': 'warn',
3436
'playwright/missing-playwright-await': 'error',
3537
'playwright/no-conditional-in-test': 'warn',
@@ -87,6 +89,7 @@ export = {
8789
recommended,
8890
},
8991
rules: {
92+
'expect-expect': expectExpect,
9093
'max-nested-describe': maxNestedDescribe,
9194
'missing-playwright-await': missingPlaywrightAwait,
9295
'no-conditional-in-test': noConditionalInTest,

src/rules/expect-expect.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Rule } from 'eslint';
2+
import * as ESTree from 'estree';
3+
import { isExpectCall, isTest } from '../utils/ast';
4+
5+
export default {
6+
create(context) {
7+
const unchecked: ESTree.CallExpression[] = [];
8+
9+
function checkExpressions(nodes: ESTree.Node[]) {
10+
for (const node of nodes) {
11+
const index =
12+
node.type === 'CallExpression' ? unchecked.indexOf(node) : -1;
13+
14+
if (index !== -1) {
15+
unchecked.splice(index, 1);
16+
break;
17+
}
18+
}
19+
}
20+
21+
return {
22+
CallExpression(node) {
23+
if (isTest(node)) {
24+
unchecked.push(node);
25+
} else if (isExpectCall(node)) {
26+
checkExpressions(context.getAncestors());
27+
}
28+
},
29+
'Program:exit'() {
30+
unchecked.forEach((node) => {
31+
context.report({ messageId: 'noAssertions', node });
32+
});
33+
},
34+
};
35+
},
36+
meta: {
37+
docs: {
38+
category: 'Best Practices',
39+
description: 'Enforce assertion to be made in a test body',
40+
recommended: true,
41+
url: 'https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/expect-expect.md',
42+
},
43+
messages: {
44+
noAssertions: 'Test has no assertions',
45+
},
46+
type: 'problem',
47+
},
48+
} as Rule.RuleModule;

test/spec/expect-expect.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import rule from '../../src/rules/expect-expect';
2+
import { runRuleTester } from '../utils/rule-tester';
3+
4+
runRuleTester('expect-expect', rule, {
5+
invalid: [
6+
{
7+
code: 'test("should fail", () => {});',
8+
errors: [{ messageId: 'noAssertions' }],
9+
},
10+
{
11+
code: 'test.skip("should fail", () => {});',
12+
errors: [{ messageId: 'noAssertions' }],
13+
},
14+
],
15+
valid: [
16+
'foo();',
17+
'["bar"]();',
18+
'testing("will test something eventually", () => {})',
19+
'test("should pass", () => expect(true).toBeDefined())',
20+
],
21+
});

0 commit comments

Comments
 (0)