/
valid-expect.js
138 lines (127 loc) · 4.37 KB
/
valid-expect.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
'use strict';
/*
* This implementation is ported from from eslint-plugin-jasmine.
* MIT license, Tom Vincent.
*/
const { getDocsUrl } = require('./util');
const expectProperties = ['not', 'resolves', 'rejects'];
module.exports = {
meta: {
docs: {
url: getDocsUrl(__filename),
},
messages: {
multipleArgs: 'More than one argument was passed to expect().',
noArgs: 'No arguments were passed to expect().',
noAssertions: 'No assertion was called on expect().',
invalidProperty:
'"{{ propertyName }}" is not a valid property of expect.',
propertyWithoutMatcher: '"{{ propertyName }}" needs to call a matcher.',
matcherOnPropertyNotCalled: '"{{ propertyName }}" was not called.',
},
schema: [],
},
create(context) {
return {
CallExpression(node) {
const calleeName = node.callee.name;
if (calleeName === 'expect') {
// checking "expect()" arguments
if (node.arguments.length > 1) {
const secondArgumentLocStart = node.arguments[1].loc.start;
const lastArgumentLocEnd =
node.arguments[node.arguments.length - 1].loc.end;
context.report({
loc: {
end: {
column: lastArgumentLocEnd.column - 1,
line: lastArgumentLocEnd.line,
},
start: secondArgumentLocStart,
},
messageId: 'multipleArgs',
node,
});
} else if (node.arguments.length === 0) {
const expectLength = calleeName.length;
context.report({
loc: {
end: {
column: node.loc.start.column + expectLength + 1,
line: node.loc.start.line,
},
start: {
column: node.loc.start.column + expectLength,
line: node.loc.start.line,
},
},
messageId: 'noArgs',
node,
});
}
// something was called on `expect()`
if (
node.parent &&
node.parent.type === 'MemberExpression' &&
node.parent.parent
) {
let parentNode = node.parent;
let parentProperty = parentNode.property;
let propertyName = parentProperty.name;
let grandParent = parentNode.parent;
// a property is accessed, get the next node
if (grandParent.type === 'MemberExpression') {
// a modifier is used, just get the next one
if (expectProperties.indexOf(propertyName) > -1) {
grandParent = grandParent.parent;
} else {
// only a few properties are allowed
context.report({
// For some reason `endColumn` isn't set in tests if `loc` is
// not added
loc: parentProperty.loc,
messageId: 'invalidProperty',
data: { propertyName },
node: parentProperty,
});
}
// this next one should be the matcher
parentNode = parentNode.parent;
parentProperty = parentNode.property;
propertyName = parentProperty.name;
}
// matcher was not called
if (grandParent.type === 'ExpressionStatement') {
context.report({
// For some reason `endColumn` isn't set in tests if `loc` is not
// added
loc: parentProperty.loc,
data: { propertyName },
messageId:
expectProperties.indexOf(propertyName) > -1
? 'propertyWithoutMatcher'
: 'matcherOnPropertyNotCalled',
node: parentProperty,
});
}
}
}
},
// nothing called on "expect()"
'CallExpression:exit'(node) {
if (
node.callee.name === 'expect' &&
node.parent.type === 'ExpressionStatement'
) {
context.report({
// For some reason `endColumn` isn't set in tests if `loc` is not
// added
loc: node.loc,
messageId: 'noAssertions',
node,
});
}
},
};
},
};