UNPKG

3.67 kBJavaScriptView Raw
1'use strict';
2
3const {visitIf} = require('enhance-visitors');
4const MicroSpellingCorrecter = require('micro-spelling-correcter');
5const util = require('../util');
6const createAvaRule = require('../create-ava-rule');
7
8const properties = new Set([
9 ...util.executionMethods,
10 'context',
11 'title',
12 'skip',
13]);
14
15const correcter = new MicroSpellingCorrecter([...properties]);
16
17const isCallExpression = node =>
18 node.parent.type === 'CallExpression'
19 && node.parent.callee === node;
20
21const getMemberNodes = node => {
22 if (node.object.type === 'MemberExpression') {
23 return [...getMemberNodes(node.object), node.property];
24 }
25
26 return [node.property];
27};
28
29const create = context => {
30 const ava = createAvaRule();
31
32 return ava.merge({
33 CallExpression: visitIf([
34 ava.isInTestFile,
35 ava.isInTestNode,
36 ])(node => {
37 if (node.callee.type !== 'MemberExpression'
38 && node.callee.name === 't') {
39 context.report({
40 node,
41 message: '`t` is not a function.',
42 });
43 }
44 }),
45 MemberExpression: visitIf([
46 ava.isInTestFile,
47 ava.isInTestNode,
48 ])(node => {
49 if (node.parent.type === 'MemberExpression'
50 || util.getNameOfRootNodeObject(node) !== 't') {
51 return;
52 }
53
54 const members = getMemberNodes(node);
55
56 const skipPositions = [];
57 let hadCall = false;
58 for (const [i, member] of members.entries()) {
59 const {name} = member;
60
61 let corrected = correcter.correct(name);
62
63 if (i !== 0 && (corrected === 'context' || corrected === 'title')) { // `context` and `title` can only be first
64 corrected = undefined;
65 }
66
67 if (corrected !== name) {
68 if (corrected === undefined) {
69 if (isCallExpression(node)) {
70 context.report({
71 node,
72 message: `Unknown assertion method \`.${name}\`.`,
73 });
74 } else {
75 context.report({
76 node,
77 message: `Unknown member \`.${name}\`. Use \`.context.${name}\` instead.`,
78 });
79 }
80 } else {
81 context.report({
82 node,
83 message: `Misspelled \`.${corrected}\` as \`.${name}\`.`,
84 fix: fixer => fixer.replaceText(member, corrected),
85 });
86 }
87
88 return; // Don't check further
89 }
90
91 if (name === 'context' || name === 'title') {
92 if (members.length === 1 && isCallExpression(node)) {
93 context.report({
94 node,
95 message: `Unknown assertion method \`.${name}\`.`,
96 });
97 }
98
99 return; // Don't check further
100 }
101
102 if (name === 'skip') {
103 skipPositions.push(i);
104 } else {
105 if (hadCall) {
106 context.report({
107 node,
108 message: 'Can\'t chain assertion methods.',
109 });
110 }
111
112 hadCall = true;
113 }
114 }
115
116 if (!hadCall) {
117 context.report({
118 node,
119 message: 'Missing assertion method.',
120 });
121 }
122
123 if (skipPositions.length > 1) {
124 context.report({
125 node,
126 message: 'Too many chained uses of `.skip`.',
127 fix(fixer) {
128 const chain = ['t', ...members.map(member => member.name).filter(name => name !== 'skip'), 'skip'];
129 return fixer.replaceText(node, chain.join('.'));
130 },
131 });
132 }
133
134 if (skipPositions.length === 1 && skipPositions[0] !== members.length - 1) {
135 context.report({
136 node,
137 message: '`.skip` modifier should be the last in chain.',
138 fix(fixer) {
139 const chain = ['t', ...members.map(member => member.name).filter(name => name !== 'skip'), 'skip'];
140 return fixer.replaceText(node, chain.join('.'));
141 },
142 });
143 }
144 }),
145 });
146};
147
148module.exports = {
149 create,
150 meta: {
151 type: 'problem',
152 docs: {
153 description: 'Disallow the incorrect use of `t`.',
154 url: util.getDocsUrl(__filename),
155 },
156 fixable: 'code',
157 schema: [],
158 },
159};