Skip to content

Commit 449013a

Browse files
committed
use-t-well: Suggest AVA equivalents for common assertion names from other test frameworks
Fixes #177
1 parent e798c78 commit 449013a

3 files changed

Lines changed: 190 additions & 1 deletion

File tree

docs/rules/use-t-well.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/rela
1212

1313
Prevent the use of unknown assertion methods and the access to members other than the assertion methods and `.context`, as well as some known misuses of `t`.
1414

15-
This rule is partly fixable. It can fix most misspelled assertion method names and incorrect usages of `.skip`.
15+
This rule is partly fixable. It can fix most misspelled assertion method names, suggest AVA equivalents for common assertion names from other test frameworks (tape, Jest, node:assert), and fix incorrect usages of `.skip`.
1616

1717
## Examples
1818

@@ -23,6 +23,8 @@ test('main', t => {
2323
//
2424
t(value); // `t` is not a function
2525
t.depEqual(value, [2]); // Misspelled `.deepEqual` as `.depEqual`, fixable
26+
t.ok(value); // Unknown assertion method `.ok`. Did you mean `.truthy`?, fixable
27+
t.equal(a, b); // Unknown assertion method `.equal`. Did you mean `.is`?, fixable
2628
t.contxt.foo = 100; // Misspelled `.context` as `.contxt`, fixable
2729
t.deepEqual.skip.skip(); // Too many chained uses of `.skip`, fixable
2830
t.skip.deepEqual(1, 1); // `.skip` modifier should be the last in chain, fixable

rules/use-t-well.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,53 @@ const MESSAGE_ID_NOT_FUNCTION = 'not-function';
77
const MESSAGE_ID_UNKNOWN_ASSERTION = 'unknown-assertion';
88
const MESSAGE_ID_UNKNOWN_MEMBER = 'unknown-member';
99
const MESSAGE_ID_MISSPELLED = 'misspelled';
10+
const MESSAGE_ID_SYNONYM = 'synonym';
1011
const MESSAGE_ID_CHAINING = 'chaining';
1112
const MESSAGE_ID_MISSING_ASSERTION = 'missing-assertion';
1213
const MESSAGE_ID_TOO_MANY_SKIPS = 'too-many-skips';
1314
const MESSAGE_ID_SKIP_POSITION = 'skip-position';
1415

16+
// Map of common assertion names from other test frameworks (tape, node:assert, Jest) to their AVA equivalents.
17+
const assertionSynonyms = new Map([
18+
// Truthy/Falsy
19+
['ok', 'truthy'],
20+
['notOk', 'falsy'],
21+
22+
// Equality
23+
['equal', 'is'],
24+
['equals', 'is'],
25+
['strictEqual', 'is'],
26+
['strictEquals', 'is'],
27+
['notEqual', 'not'],
28+
['notEquals', 'not'],
29+
['notStrictEqual', 'not'],
30+
['notStrictEquals', 'not'],
31+
32+
// Deep equality
33+
['same', 'deepEqual'],
34+
['deepStrictEqual', 'deepEqual'],
35+
['notSame', 'notDeepEqual'],
36+
['notDeepStrictEqual', 'notDeepEqual'],
37+
38+
// Throws
39+
['catch', 'throws'],
40+
['exception', 'throws'],
41+
['doesNotThrow', 'notThrows'],
42+
['rejects', 'throwsAsync'],
43+
['doesNotReject', 'notThrowsAsync'],
44+
45+
// Regex
46+
['match', 'regex'],
47+
['doesNotMatch', 'notRegex'],
48+
49+
// Error
50+
['error', 'ifError'],
51+
['ifErr', 'ifError'],
52+
53+
// Snapshot
54+
['matchSnapshot', 'snapshot'],
55+
]);
56+
1557
const properties = new Set([
1658
...util.executionMethods,
1759
'context',
@@ -65,6 +107,17 @@ const create = context => {
65107
for (const [i, member] of members.entries()) {
66108
const {name} = member;
67109

110+
const synonym = assertionSynonyms.get(name);
111+
if (synonym) {
112+
context.report({
113+
node,
114+
messageId: MESSAGE_ID_SYNONYM,
115+
data: {name, corrected: synonym},
116+
fix: fixer => fixer.replaceText(member, synonym),
117+
});
118+
return;
119+
}
120+
68121
let corrected = correcter.correct(name);
69122

70123
if (i !== 0 && (corrected === 'context' || corrected === 'title')) { // `context` and `title` can only be first
@@ -172,6 +225,7 @@ export default {
172225
[MESSAGE_ID_UNKNOWN_ASSERTION]: 'Unknown assertion method `.{{name}}`.',
173226
[MESSAGE_ID_UNKNOWN_MEMBER]: 'Unknown member `.{{name}}`. Use `.context.{{name}}` instead.',
174227
[MESSAGE_ID_MISSPELLED]: 'Misspelled `.{{corrected}}` as `.{{name}}`.',
228+
[MESSAGE_ID_SYNONYM]: 'Unknown assertion method `.{{name}}`. Did you mean `.{{corrected}}`?',
175229
[MESSAGE_ID_CHAINING]: 'Can\'t chain assertion methods.',
176230
[MESSAGE_ID_MISSING_ASSERTION]: 'Missing assertion method.',
177231
[MESSAGE_ID_TOO_MANY_SKIPS]: 'Too many chained uses of `.skip`.',

test/use-t-well.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,5 +223,138 @@ ruleTester.run('use-t-well', rule, {
223223
output: testCase('t_.deepEqual(a, a);'),
224224
errors: [error('misspelled')],
225225
},
226+
// Synonyms from other test frameworks
227+
{
228+
code: testCase('t.ok(v);'),
229+
output: testCase('t.truthy(v);'),
230+
errors: [error('synonym')],
231+
},
232+
{
233+
code: testCase('t.notOk(v);'),
234+
output: testCase('t.falsy(v);'),
235+
errors: [error('synonym')],
236+
},
237+
{
238+
code: testCase('t.equal(a, a);'),
239+
output: testCase('t.is(a, a);'),
240+
errors: [error('synonym')],
241+
},
242+
{
243+
code: testCase('t.equals(a, a);'),
244+
output: testCase('t.is(a, a);'),
245+
errors: [error('synonym')],
246+
},
247+
{
248+
code: testCase('t.strictEqual(a, a);'),
249+
output: testCase('t.is(a, a);'),
250+
errors: [error('synonym')],
251+
},
252+
{
253+
code: testCase('t.strictEquals(a, a);'),
254+
output: testCase('t.is(a, a);'),
255+
errors: [error('synonym')],
256+
},
257+
{
258+
code: testCase('t.notEqual(a, a);'),
259+
output: testCase('t.not(a, a);'),
260+
errors: [error('synonym')],
261+
},
262+
{
263+
code: testCase('t.notEquals(a, a);'),
264+
output: testCase('t.not(a, a);'),
265+
errors: [error('synonym')],
266+
},
267+
{
268+
code: testCase('t.notStrictEqual(a, a);'),
269+
output: testCase('t.not(a, a);'),
270+
errors: [error('synonym')],
271+
},
272+
{
273+
code: testCase('t.notStrictEquals(a, a);'),
274+
output: testCase('t.not(a, a);'),
275+
errors: [error('synonym')],
276+
},
277+
{
278+
code: testCase('t.same(a, a);'),
279+
output: testCase('t.deepEqual(a, a);'),
280+
errors: [error('synonym')],
281+
},
282+
{
283+
code: testCase('t.deepStrictEqual(a, a);'),
284+
output: testCase('t.deepEqual(a, a);'),
285+
errors: [error('synonym')],
286+
},
287+
{
288+
code: testCase('t.notSame(a, a);'),
289+
output: testCase('t.notDeepEqual(a, a);'),
290+
errors: [error('synonym')],
291+
},
292+
{
293+
code: testCase('t.notDeepStrictEqual(a, a);'),
294+
output: testCase('t.notDeepEqual(a, a);'),
295+
errors: [error('synonym')],
296+
},
297+
{
298+
code: testCase('t.catch(fn);'),
299+
output: testCase('t.throws(fn);'),
300+
errors: [error('synonym')],
301+
},
302+
{
303+
code: testCase('t.exception(fn);'),
304+
output: testCase('t.throws(fn);'),
305+
errors: [error('synonym')],
306+
},
307+
{
308+
code: testCase('t.doesNotThrow(fn);'),
309+
output: testCase('t.notThrows(fn);'),
310+
errors: [error('synonym')],
311+
},
312+
{
313+
code: testCase('t.rejects(fn);'),
314+
output: testCase('t.throwsAsync(fn);'),
315+
errors: [error('synonym')],
316+
},
317+
{
318+
code: testCase('t.doesNotReject(fn);'),
319+
output: testCase('t.notThrowsAsync(fn);'),
320+
errors: [error('synonym')],
321+
},
322+
{
323+
code: testCase('t.match(v, /v/);'),
324+
output: testCase('t.regex(v, /v/);'),
325+
errors: [error('synonym')],
326+
},
327+
{
328+
code: testCase('t.doesNotMatch(v, /v/);'),
329+
output: testCase('t.notRegex(v, /v/);'),
330+
errors: [error('synonym')],
331+
},
332+
{
333+
code: testCase('t.error(v);'),
334+
output: testCase('t.ifError(v);'),
335+
errors: [error('synonym')],
336+
},
337+
{
338+
code: testCase('t.ifErr(v);'),
339+
output: testCase('t.ifError(v);'),
340+
errors: [error('synonym')],
341+
},
342+
{
343+
code: testCase('t.matchSnapshot(v);'),
344+
output: testCase('t.snapshot(v);'),
345+
errors: [error('synonym')],
346+
},
347+
// Synonym with .skip modifier
348+
{
349+
code: testCase('t.ok.skip(v);'),
350+
output: testCase('t.truthy.skip(v);'),
351+
errors: [error('synonym')],
352+
},
353+
// Synonym with alternative test object name
354+
{
355+
code: testCase('tt.equal(a, a);'),
356+
output: testCase('tt.is(a, a);'),
357+
errors: [error('synonym')],
358+
},
226359
],
227360
});

0 commit comments

Comments
 (0)