Skip to content

Commit ae7c8b2

Browse files
committed
feat(expect-expect): support additionalTestBlockFunctions option
1 parent 9d89c29 commit ae7c8b2

File tree

3 files changed

+115
-12
lines changed

3 files changed

+115
-12
lines changed

docs/rules/expect-expect.md

+42-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ it('should work with callbacks/async', () => {
3434
"jest/expect-expect": [
3535
"error",
3636
{
37-
"assertFunctionNames": ["expect"]
37+
"assertFunctionNames": ["expect"],
38+
"additionalTestBlockFunctions": []
3839
}
3940
]
4041
}
@@ -102,3 +103,43 @@ describe('GET /user', function () {
102103
});
103104
});
104105
```
106+
107+
### `additionalTestBlockFunctions`
108+
109+
This array can be used to specify the names of functions that should also be
110+
treated as test blocks:
111+
112+
```json
113+
{
114+
"rules": {
115+
"jest/expect-expect": [
116+
"error",
117+
{ "additionalTestBlockFunctions": ["theoretically"] }
118+
]
119+
}
120+
}
121+
```
122+
123+
The following is _correct_ when using the above configuration:
124+
125+
```js
126+
import theoretically from 'jest-theories';
127+
128+
describe('NumberToLongString', () => {
129+
const theories = [
130+
{ input: 100, expected: 'One hundred' },
131+
{ input: 1000, expected: 'One thousand' },
132+
{ input: 10000, expected: 'Ten thousand' },
133+
{ input: 100000, expected: 'One hundred thousand' },
134+
];
135+
136+
theoretically(
137+
'the number {input} is correctly translated to string',
138+
theories,
139+
theory => {
140+
const output = NumberToLongString(theory.input);
141+
expect(output).toBe(theory.expected);
142+
},
143+
);
144+
});
145+
```

src/rules/__tests__/expect-expect.test.ts

+49-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const ruleTester = new TSESLint.RuleTester({
1515

1616
ruleTester.run('expect-expect', rule, {
1717
valid: [
18+
"['x']();",
1819
'it("should pass", () => expect(true).toBeDefined())',
1920
'test("should pass", () => expect(true).toBeDefined())',
2021
'it("should pass", () => somePromise().then(() => expect(true).toBeDefined()))',
@@ -69,7 +70,21 @@ ruleTester.run('expect-expect', rule, {
6970
},
7071
{
7172
code: 'it("should pass", () => expect(true).toBeDefined())',
72-
options: [{ assertFunctionNames: undefined }],
73+
options: [
74+
{
75+
assertFunctionNames: undefined,
76+
additionalTestBlockFunctions: undefined,
77+
},
78+
],
79+
},
80+
{
81+
code: dedent`
82+
theoretically('the number {input} is correctly translated to string', theories, theory => {
83+
const output = NumberToLongString(theory.input);
84+
expect(output).toBe(theory.expected);
85+
})
86+
`,
87+
options: [{ additionalTestBlockFunctions: ['theoretically'] }],
7388
},
7489
],
7590

@@ -101,6 +116,39 @@ ruleTester.run('expect-expect', rule, {
101116
},
102117
],
103118
},
119+
{
120+
code: 'test.skip("should fail", () => {});',
121+
errors: [
122+
{
123+
messageId: 'noAssertions',
124+
type: AST_NODE_TYPES.CallExpression,
125+
},
126+
],
127+
},
128+
{
129+
code: 'afterEach(() => {});',
130+
options: [{ additionalTestBlockFunctions: ['afterEach'] }],
131+
errors: [
132+
{
133+
messageId: 'noAssertions',
134+
type: AST_NODE_TYPES.CallExpression,
135+
},
136+
],
137+
},
138+
{
139+
code: dedent`
140+
theoretically('the number {input} is correctly translated to string', theories, theory => {
141+
const output = NumberToLongString(theory.input);
142+
})
143+
`,
144+
options: [{ additionalTestBlockFunctions: ['theoretically'] }],
145+
errors: [
146+
{
147+
messageId: 'noAssertions',
148+
type: AST_NODE_TYPES.CallExpression,
149+
},
150+
],
151+
},
104152
{
105153
code: 'it("should fail", () => { somePromise.then(() => {}); });',
106154
errors: [

src/rules/expect-expect.ts

+24-10
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import {
88
TSESTree,
99
} from '@typescript-eslint/experimental-utils';
1010
import {
11-
TestCaseName,
1211
createRule,
1312
getNodeName,
1413
getTestCallExpressionsFromDeclaredVariables,
14+
isTestCaseCall,
1515
} from './utils';
1616

1717
/**
@@ -41,7 +41,12 @@ function matchesAssertFunctionName(
4141
}
4242

4343
export default createRule<
44-
[Partial<{ assertFunctionNames: readonly string[] }>],
44+
[
45+
Partial<{
46+
assertFunctionNames: readonly string[];
47+
additionalTestBlockFunctions: readonly string[];
48+
}>,
49+
],
4550
'noAssertions'
4651
>({
4752
name: __filename,
@@ -62,14 +67,23 @@ export default createRule<
6267
type: 'array',
6368
items: [{ type: 'string' }],
6469
},
70+
additionalTestBlockFunctions: {
71+
type: 'array',
72+
items: { type: 'string' },
73+
},
6574
},
6675
additionalProperties: false,
6776
},
6877
],
6978
type: 'suggestion',
7079
},
71-
defaultOptions: [{ assertFunctionNames: ['expect'] }],
72-
create(context, [{ assertFunctionNames = ['expect'] }]) {
80+
defaultOptions: [
81+
{ assertFunctionNames: ['expect'], additionalTestBlockFunctions: [] },
82+
],
83+
create(
84+
context,
85+
[{ assertFunctionNames = ['expect'], additionalTestBlockFunctions = [] }],
86+
) {
7387
const unchecked: TSESTree.CallExpression[] = [];
7488

7589
function checkCallExpressionUsed(nodes: TSESTree.Node[]) {
@@ -96,14 +110,14 @@ export default createRule<
96110

97111
return {
98112
CallExpression(node) {
99-
const name = getNodeName(node.callee);
113+
const name = getNodeName(node.callee) ?? '';
100114

101-
if (name === TestCaseName.it || name === TestCaseName.test) {
102-
unchecked.push(node);
103-
} else if (
104-
name &&
105-
matchesAssertFunctionName(name, assertFunctionNames)
115+
if (
116+
isTestCaseCall(node) ||
117+
additionalTestBlockFunctions.includes(name)
106118
) {
119+
unchecked.push(node);
120+
} else if (matchesAssertFunctionName(name, assertFunctionNames)) {
107121
// Return early in case of nested `it` statements.
108122
checkCallExpressionUsed(context.getAncestors());
109123
}

0 commit comments

Comments
 (0)