UNPKG

3.57 kBJavaScriptView Raw
1const {docsUrl} = require('../utilities');
2
3// see http://sinonjs.org/docs/#assertions
4const SINON_ASSERT = [
5 'alwaysCalledOn',
6 'alwaysCalledWith',
7 'alwaysCalledWithExactly',
8 'alwaysCalledWithMatch',
9 'alwaysThrew',
10 'callCount',
11 'called',
12 'calledOn',
13 'calledOnce',
14 'calledThrice',
15 'calledTwice',
16 'calledWith',
17 'calledWithExactly',
18 'calledWithMatch',
19 'callOrder',
20 'neverCalledWith',
21 'neverCalledWithMatch',
22 'notCalled',
23 'threw',
24];
25
26// see https://github.com/domenic/sinon-chai
27const SINON_CHAI = [
28 'alwaysCalledOn',
29 'alwaysCalledWith',
30 'alwaysCalledWithExactly',
31 'alwaysCalledWithMatch',
32 'alwaysCalledWithNew',
33 'alwaysReturned',
34 'alwaysThrew',
35 'callCount',
36 'called',
37 'calledAfter',
38 'calledBefore',
39 'calledOn',
40 'calledOnce',
41 'calledThrice',
42 'calledTwice',
43 'calledWith',
44 'calledWithExactly',
45 'calledWithMatch',
46 'calledWithNew',
47 'notCalled',
48 'returned',
49 'threw',
50];
51
52const PREFER_SINON_CHAI_MESSAGE =
53 'Use the more meaningful sinon-chai assertions.';
54const PREFER_SINON_ASSERT_MESSAGE =
55 'Use the more meaningful sinon.assert assertions.';
56
57module.exports = {
58 meta: {
59 docs: {
60 description:
61 'Require the use of meaningful sinon assertions through sinon.assert or sinon-chai.',
62 category: 'Best Practices',
63 recommended: false,
64 uri: docsUrl('sinon-prefer-meaningful-assertions'),
65 },
66 },
67
68 create(context) {
69 function getNearestFullExpression(node) {
70 while (
71 node.parent &&
72 (node.parent.type === 'MemberExpression' ||
73 node.parent.type === 'CallExpression')
74 ) {
75 // eslint-disable-next-line no-param-reassign
76 node = node.parent;
77 }
78
79 return node;
80 }
81
82 function isAssertExpression(node) {
83 return node.type === 'MemberExpression' && node.object.name === 'assert';
84 }
85
86 function isRewritableSinonExpression(node, methods) {
87 if (!node) {
88 return false;
89 }
90 return (
91 (node.type === 'MemberExpression' &&
92 methods.indexOf(node.property.name) >= 0) ||
93 (node.type === 'CallExpression' &&
94 isRewritableSinonExpression(node.callee, methods))
95 );
96 }
97
98 function isInvalidAssertCallExpression(node) {
99 return (
100 isAssertExpression(node.callee) &&
101 node.arguments.some((arg) => {
102 return isRewritableSinonExpression(arg, SINON_ASSERT);
103 })
104 );
105 }
106
107 function isInvalidShouldExpression(node) {
108 return (
109 node.property.name === 'should' &&
110 isRewritableSinonExpression(node.object, SINON_CHAI)
111 );
112 }
113
114 function isInvalidExpectCallExpression(node) {
115 return (
116 node.callee.name === 'expect' &&
117 isRewritableSinonExpression(node.arguments[0], SINON_CHAI)
118 );
119 }
120
121 function handleCallExpression(node) {
122 if (isInvalidAssertCallExpression(node)) {
123 context.report({
124 node,
125 message: PREFER_SINON_ASSERT_MESSAGE,
126 });
127 }
128
129 if (isInvalidExpectCallExpression(node)) {
130 context.report({
131 node: getNearestFullExpression(node),
132 message: PREFER_SINON_CHAI_MESSAGE,
133 });
134 }
135 }
136
137 function handleMemberExpression(node) {
138 if (isInvalidShouldExpression(node)) {
139 context.report({
140 node: getNearestFullExpression(node),
141 message: PREFER_SINON_CHAI_MESSAGE,
142 });
143 }
144 }
145
146 return {
147 CallExpression: handleCallExpression,
148 MemberExpression: handleMemberExpression,
149 };
150 },
151};