Skip to content

Commit 4effb3c

Browse files
tushardholeSimenB
authored andcommitted
feat: implement prefer-expect-assertions rule (#43)
* feat: implement missing_expect_assertions_call rule Intial code for missing_expect_assertions_call rule * style: lint auto fix * refactor: enhance missing_expect_assertions_call rule Report error when expect.assertions() call is present but the argument provided is not a number * refactor: enhance missing_expect_assertions_call rule Rule will not report any error, if expect.hasAssertions() call is present. After this change, either expect.hasAssertions() or expect.assertions({number of assertions}) is required to pass the rule * refactor(readability): improving missing-expect-assertions-call rule msg * refactor: missing-expect-assertions-call rule rename Rename missing-expect-assertions-call to prefer-expect-assertions * refactor: remove unnecessary return * fix: do not add the rule to recommended * refactor: update test message * test: adding missing tests and refactoring
1 parent 439c1f1 commit 4effb3c

File tree

3 files changed

+174
-0
lines changed

3 files changed

+174
-0
lines changed

index.js

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const preferToBeNull = require('./rules/prefer_to_be_null');
88
const preferToBeUndefined = require('./rules/prefer_to_be_undefined');
99
const preferToHaveLength = require('./rules/prefer_to_have_length');
1010
const validExpect = require('./rules/valid_expect');
11+
const preferExpectAssertions = require('./rules/prefer_expect_assertions');
1112

1213
const snapshotProcessor = require('./processors/snapshot-processor');
1314

@@ -62,5 +63,6 @@ module.exports = {
6263
'prefer-to-be-undefined': preferToBeUndefined,
6364
'prefer-to-have-length': preferToHaveLength,
6465
'valid-expect': validExpect,
66+
'prefer-expect-assertions': preferExpectAssertions,
6567
},
6668
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
'use strict';
2+
3+
const RuleTester = require('eslint').RuleTester;
4+
const rules = require('../..').rules;
5+
6+
const ruleTester = new RuleTester({
7+
parserOptions: {
8+
ecmaVersion: 6,
9+
},
10+
});
11+
12+
const expectedMsg =
13+
'Every test should have either `expect.assertions(<number of assertions>)` or `expect.hasAssertions()` as its first expression';
14+
15+
ruleTester.run('prefer-expect-assertions', rules['prefer-expect-assertions'], {
16+
invalid: [
17+
{
18+
code: 'it("it1", () => {})',
19+
errors: [
20+
{
21+
message: expectedMsg,
22+
},
23+
],
24+
},
25+
{
26+
code: 'it("it1", () => { foo()})',
27+
errors: [
28+
{
29+
message: expectedMsg,
30+
},
31+
],
32+
},
33+
{
34+
code:
35+
'it("it1", function() {' +
36+
'\n\t\t\tsomeFunctionToDo();' +
37+
'\n\t\t\tsomeFunctionToDo2();\n' +
38+
'\t\t\t})',
39+
errors: [
40+
{
41+
message: expectedMsg,
42+
},
43+
],
44+
},
45+
{
46+
code: 'it("it1", function() {var a = 2;})',
47+
errors: [
48+
{
49+
message: expectedMsg,
50+
},
51+
],
52+
},
53+
{
54+
code: 'it("it1", function() {expect.assertions();})',
55+
errors: [
56+
{
57+
message: expectedMsg,
58+
},
59+
],
60+
},
61+
{
62+
code: 'it("it1", function() {expect.assertions(1,2);})',
63+
errors: [
64+
{
65+
message: expectedMsg,
66+
},
67+
],
68+
},
69+
{
70+
code: 'it("it1", function() {expect.assertions("1");})',
71+
errors: [
72+
{
73+
message: expectedMsg,
74+
},
75+
],
76+
},
77+
],
78+
79+
valid: [
80+
{
81+
code: 'test("it1", () => {expect.assertions(0);})',
82+
},
83+
'test("it1", function() {expect.assertions(0);})',
84+
'test("it1", function() {expect.hasAssertions();})',
85+
'it("it1", function() {expect.assertions(0);})',
86+
'it("it1", function() {\n\t\t\texpect.assertions(1);' +
87+
'\n\t\t\texpect(someValue).toBe(true)\n' +
88+
'\t\t\t})',
89+
'test("it1")',
90+
],
91+
});

rules/prefer_expect_assertions.js

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
'use strict';
2+
3+
const ruleMsg =
4+
'Every test should have either `expect.assertions(<number of assertions>)` or `expect.hasAssertions()` as its first expression';
5+
6+
const validateArguments = expression => {
7+
return (
8+
expression.arguments &&
9+
expression.arguments.length === 1 &&
10+
Number.isInteger(expression.arguments[0].value)
11+
);
12+
};
13+
14+
const isExpectAssertionsOrHasAssertionsCall = expression => {
15+
try {
16+
const expectAssertionOrHasAssertionCall =
17+
expression.type === 'CallExpression' &&
18+
expression.callee.type === 'MemberExpression' &&
19+
expression.callee.object.name === 'expect' &&
20+
(expression.callee.property.name === 'assertions' ||
21+
expression.callee.property.name === 'hasAssertions');
22+
23+
if (expression.callee.property.name === 'assertions') {
24+
return expectAssertionOrHasAssertionCall && validateArguments(expression);
25+
}
26+
return expectAssertionOrHasAssertionCall;
27+
} catch (e) {
28+
return false;
29+
}
30+
};
31+
32+
const isTestOrItFunction = node => {
33+
return (
34+
node.type === 'CallExpression' &&
35+
node.callee &&
36+
(node.callee.name === 'it' || node.callee.name === 'test')
37+
);
38+
};
39+
40+
const getFunctionFirstLine = functionBody => {
41+
return functionBody[0] && functionBody[0].expression;
42+
};
43+
44+
const isFirstLineExprStmt = functionBody => {
45+
return functionBody[0] && functionBody[0].type === 'ExpressionStatement';
46+
};
47+
48+
const getTestFunctionBody = node => {
49+
try {
50+
return node.arguments[1].body.body;
51+
} catch (e) {
52+
return undefined;
53+
}
54+
};
55+
56+
const reportMsg = (context, node) => {
57+
context.report({
58+
message: ruleMsg,
59+
node,
60+
});
61+
};
62+
63+
module.exports = context => {
64+
return {
65+
CallExpression(node) {
66+
if (isTestOrItFunction(node)) {
67+
const testFuncBody = getTestFunctionBody(node);
68+
if (testFuncBody) {
69+
if (!isFirstLineExprStmt(testFuncBody)) {
70+
reportMsg(context, node);
71+
} else {
72+
const testFuncFirstLine = getFunctionFirstLine(testFuncBody);
73+
if (!isExpectAssertionsOrHasAssertionsCall(testFuncFirstLine)) {
74+
reportMsg(context, node);
75+
}
76+
}
77+
}
78+
}
79+
},
80+
};
81+
};

0 commit comments

Comments
 (0)