@putout/plugin-tape
Tape-inspired TAP-compatible simplest high speed test runner with superpowers.
(c) πΌSupertape
πPutout plugin helps to apply best parctises for tests written with πΌSupertape.
Install
npm i @putout/plugin-tape -D
Rules
Config
{
"rules": {
"tape/convert-mock-require-to-mock-import": "off",
"tape/jest": "on",
"tape/apply-stub": "on",
"tape/apply-destructuring": "on",
"tape/apply-with-name": "on",
"tape/add-t-end": "on",
"tape/add-stop-all": "on",
"tape/add-await-to-re-import": "on",
"tape/add-node-prefix-to-mock-require": "on",
"tape/remove-useless-t-end": "on",
"tape/sync-with-name": "on",
"tape/switch-expected-with-result": "on",
"tape/convert-tape-to-supertape": "on",
"tape/convert-throws-to-try-catch": "on",
"tape/convert-does-not-throw-to-try-catch": "on",
"tape/convert-called-with-args": "on",
"tape/convert-called-with-to-called-with-no-args": "on",
"tape/convert-called-with-no-args-to-called-with": "on",
"tape/convert-equal-to-called-once": "on",
"tape/convert-equal-to-deep-equal": "on",
"tape/convert-equals-to-equal": "on",
"tape/convert-deep-equal-to-equal": "on",
"tape/convert-emitter-to-promise": "on",
"tape/convert-ok-to-match": "on",
"tape/convert-ok-to-called-with": "on",
"tape/convert-match-regexp-to-string": "on",
"tape/add-args": "on",
"tape/declare": "on",
"tape/remove-default-messages": "on",
"tape/remove-useless-not-called-args": "on",
"tape/remove-only": "on",
"tape/remove-skip": "on",
"tape/remove-stop-all": "on"
}
}
jest
πPutout gives ability to switch easily from Jest to πΌSupertape. Checkout in πPutout Editor.
β Example of incorrect code
it('should equal', () => {
expect(a).toEqual(b);
});
β
Example of correct code
import {test} from 'supertape';
test('should equal', () => {
t.equal(a, b);
t.end();
});
switch-expected-with-result
πΌSupertape uses more natural way of
comparing: first you pass result
and then expected
.
It gives you ability to use value instead of expected
and
understand code faster: no need to search for a second argument.
While result
is always a variable, so it most likely much shorter.
β Example of incorrect code
test('plugin-apply-destructuring: transform: array: destructuring', (t) => {
t.equal(expected, result);
t.end();
});
β
Example of correct code
test('plugin-apply-destructuring: transform: array: destructuring', (t) => {
t.equal(result, expected);
t.end();
});
convert-tape-to-supertape
β Example of incorrect code
const test = require('tape');
β
Example of correct code
const test = require('supertape');
convert-throws-to-try-catch
β Example of incorrect code
const test = require('supertape');
test('some message', (t) => {
t.throws(copymitter, /from should be a string!/, 'should throw when no args');
t.end();
});
β
Example of correct code
const tryCatch = require('try-catch');
const test = require('supertape');
test('some message', (t) => {
const [error] = tryCatch(copymitter);
t.equal(error.message, 'from shoulde be a string!', 'should throw when no args');
t.end();
});
convert-does-not-throw-to-try-catch
β Example of incorrect code
const test = require('supertape');
test('some message', (t) => {
t.doesNotThrow(copymitter, 'should throw when no args');
t.end();
});
β
Example of correct code
const test = require('supertape');
const tryCatch = require('try-catch');
test('some test', (t) => {
const [error] = tryCatch(copymitter);
t.notOk(error, 'should not throw when no args');
t.end();
});
convert-called-with-args
β Example of incorrect code
const test = require('supertape');
const {stub} = test;
test('some message', (t) => {
const fn = stub();
fn();
t.calledWith(fn, 'hello');
t.end();
});
β
Example of correct code
const test = require('supertape');
const {stub} = test;
test('some message', (t) => {
const fn = stub();
fn();
t.calledWith(fn, ['hello']);
t.end();
});
convert-equal-to-called-once
No need to use equal
, supertape
supports calledOnce
.
β Example of incorrect code
const test = require('supertape');
const {stub} = test;
test('some message', (t) => {
const fn = stub();
fn();
t.equal(fn.callCount, 1);
t.end();
});
β
Example of correct code
const test = require('supertape');
const {stub} = test;
test('some message', (t) => {
const fn = stub();
fn();
t.calledOnce(fn);
t.end();
});
convert-deep-equal-to-equal
Use equal
when comparing with primitives, deepEqual
for Objects
and Arrays
;
β Example of incorrect code
const test = require('supertape');
const {stub} = test;
test('some message', (t) => {
t.deepEqual(x, 5);
t.end();
});
β
Example of correct code
const test = require('supertape');
const {stub} = test;
test('some message', (t) => {
t.equal(x, 5);
t.end();
});
convert-called-with-to-called-with-no-args
β Example of incorrect code
const test = require('supertape');
const {stub} = test;
test('some message', (t) => {
const fn = stub();
fn();
t.calledWith(fn);
t.end();
});
β
Example of correct code
const test = require('supertape');
const {stub} = test;
test('some message', (t) => {
const fn = stub();
fn();
t.calledWithNoArgs(fn);
t.end();
});
convert-called-with-no-args-to-called-with
β Example of incorrect code
const test = require('supertape');
const {stub} = test;
test('some message', (t) => {
const fn = stub();
fn();
t.calledWithNoArgs(fn, [1, 2]);
t.end();
});
β
Example of correct code
const test = require('supertape');
const {stub} = test;
test('some message', (t) => {
const fn = stub();
fn();
t.calledWith(fn, [1, 2]);
t.end();
});
convert-emitter-to-promise
β Example of incorrect code
test('copymitter', (t) => {
const cp = copymitter(from, to, ['1']);
cp.on('end', (t) => {
t.end();
});
});
β
Example of correct code
const {once} = require('node:events');
test('copymitter', async (t) => {
const cp = copymitter(from, to, ['1']);
await once(cp, 'end');
t.end();
});
apply-destructuring
Check out in πPutout Editor.
β Example of incorrect code
const test = require('supertape');
const {stub} = test;
β
Example of correct code
const {test, stub} = require('supertape');
apply-stub
Apply stub functions created. Look how it works in πPutout Editor.
β Example of incorrect code
const a = async () => true;
const b = async () => {};
const c = async () => throwError('hello');
const d = async () => {
throw Error('hello');
};
β
Example of correct code
const a = stub().resolves(true);
const b = stub().resolves();
const c = stub().rejects(Error('hello'));
const d = stub().rejects(Error('hello'));
apply-with-name
β Example of incorrect code
test('should call init before show', (t) => {
const init = stub();
const show = stub();
t.calledInOrder([init, show]);
t.end();
});
β
Example of correct code
test('should call init before show', (t) => {
const init = stub().withName('init');
const show = stub().withName('show');
t.calledInOrder([init, show]);
t.end();
});
sync-with-name
β Example of incorrect code
test('should call init before show', (t) => {
const init = stub().withName('show');
const show = stub().withName('show');
t.calledInOrder([init, show]);
t.end();
});
β
Example of correct code
test('should call init before show', (t) => {
const init = stub().withName('init');
const show = stub().withName('show');
t.calledInOrder([init, show]);
t.end();
});
declare
mockImport
β Example of incorrect code
import {stub} from 'supertape';
mockImport('fs/promises', {
readFile: stub().resolves(''),
});
β
Example of correct code
import {stub} from 'supertape';
import {createMockImport} from 'mock-import';
const {
mockImport,
stopAll,
reImport,
} = createMockImport(import.meta.url);
mockImport('fs/promises', {
readFile: stub().resolves(''),
});
test
β Example of incorrect code
test('xxx', (t) => {
const a = stub();
t.end();
});
β
Example of correct code
import {test, stub} from 'supertape';
test('xxx', (t) => {
const a = stub();
t.end();
});
add-node-prefix-to-mock-require
Checkout in πPutout Editor.
β Example of incorrect code
mockRequire('fs/promises', {
readdir,
});
β
Example of correct code
mockRequire('node:fs/promises', {
readdir,
});
add-args
β Example of incorrect code
test('xxx', () => {
t.end();
});
β
Example of correct code
test('xxx', (t) => {
t.end();
});
add-t-end
β Example of incorrect code
test('xxx', () => {});
β
Example of correct code
test('xxx', (t) => {
t.end();
});
add-await-to-re-import
β Example of incorrect code
test('stop-all: should be called', (t) => {
const read = reImport('./read');
t.end();
});
β
Example of correct code
test('stop-all: should be called', async (t) => {
const read = await reImport('./read');
t.end();
});
add-stop-all
When you write test mocking ESM
with mockImport()
never forget to call stopAll()
when you no longer need it. This leads to bugs in tests which are hard to find, each test should be checked with the one which pass when called alone but fail when called with others.
β Example of incorrect code
test('stop-all: should be called', (t) => {
mockImport('fs/promises', {
readFile: stub(),
});
t.end();
});
β
Example of correct code
test('stop-all: should be called', (t) => {
mockImport('fs/promises', {
readFile: stub(),
});
stopAll();
t.end();
});
remove-useless-t-end
β Example of incorrect code
test('test: remove me', () => {
t.end();
t.end();
});
β
Example of correct code
test('test: remove me', () => {
t.end();
});
convert-ok-to-match
β Example of incorrect code
t.ok(result.includes('hello'));
β
Example of correct code
t.match(result, /hello/);
convert-ok-to-called-with
β Example of incorrect code
t.ok(set.calledWith(1, 2));
β
Example of correct code
t.calledWith(set, [1, 2]);
convert-equal-to-not-ok
β Example of incorrect code
t.equal(error, null);
β
Example of correct code
t.notOk(error);
convert-equal-to-ok
β Example of incorrect code
t.equal(result, true);
β
Example of correct code
t.ok(result);
convert-equal-to-deep-equal
β Example of incorrect code
const expected = {
hello: 'world',
};
t.equal(error, expected);
t.end();
β
Example of correct code
const expected = {
hello: 'world',
};
t.deepEqual(error, expected);
t.end();
convert-equals-to-equal
Checkout in πPutout Editor.
β Example of incorrect code
t.equals(e.message, 'token should be a string!', 'should throw');
β
Example of correct code
t.equal(e.message, 'token should be a string!', 'should throw');
convert-match-regexp-to-string
β Example of incorrect code
t.match(result, RegExp('hello'));
β
Example of correct code
t.match(result, 'hello');
remove-default-messages
πΌSupertape will put this information for you, and it is always the same.
No need to repeat the same information twice on one line, better to avoid it.
β Example of incorrect code
t.equal(result, expected, 'should equal');
β
Example of correct code
t.equal(result, expected);
remove-useless-not-called-args
β Example of incorrect code
t.notCalled(fn, []);
β
Example of correct code
t.notCalled(fn);
remove-only
β Example of incorrect code
test.only('some test', (t) => {
t.end();
});
β
Example of correct code
test('some test', (t) => {
t.end();
});
remove-skip
β Example of incorrect code
test.skip('some test', (t) => {
t.end();
});
β
Example of correct code
test('some test', (t) => {
t.end();
});
remove-stop-all
When reImport()
or reRequire
not called, stopAll()
is redundant and should be removed.
β Example of incorrect code
test('some test', (t) => {
stopAll();
t.end();
});
β
Example of correct code
test('some test', (t) => {
t.end();
});
convert-mock-import-to-require
Convert mockRequire to mockImport.
β Example of incorrect code
const mockRequire = require('mock-require');
const {reRequire, stopAll} = mockRequire;
test('', (t) => {
mockRequire('fs/promises', {
unlink: stub(),
});
const fn = reRequire('..');
fn();
stopAll();
t.end();
});
β
Example of correct code
import {createMockImport} from 'mock-import';
const {
mockImport,
reImport,
stopAll,
} = createMockImport(import.meta.url);
test('', async (t) => {
mockImport('fs/promises', {
unlink: stub(),
});
const fn = await reImport('..');
fn();
stopAll();
t.end();
});
License
MIT