Skip to content

Commit

Permalink
feature: supertape: add per-test timeout option (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cadence Ember authored and coderaiser committed Mar 1, 2024
1 parent afe3ed4 commit 6433719
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 14 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ Options
- `SUPERTAPE_CHECK_DUPLICATES` - toggle check duplicates;
- `SUPERTAPE_CHECK_SCOPES` - check that test message has a scope: `scope: subject`;
- `SUPERTAPE_CHECK_ASSERTIONS_COUNT` - check that assertion count is no more then 1;
- `SUPERTAPE_CHECK_SKIPED` - check that skiped count equal to `0`, exit with status code;
- `SUPERTAPE_LOAD_LOOP_TIMEOUT` - timeout for load tests, defaults to `5ms`, when mocha used as runner - `50ms` optimal;

```js
test('tape: error', (t) => {
Expand Down
17 changes: 12 additions & 5 deletions packages/supertape/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,19 +211,26 @@ test('calc: sum', (t) => {
});
```

## test(name, cb)
## test(message: string, fn: (t: Test) => void, options?: TestOptions)

Create a new test with `name` string.
`cb(t)` fires with the new test object `t` once all preceding tests have
Create a new test with `message` string.
`fn(t)` fires with the new test object `t` once all preceding tests have
finished. Tests execute serially.

## test.only(name, cb)
Here is Possible `options` similar to [Environment Variables](#environment-variables) but relates to one test:

- `checkDuplicates`;
- `checkScopes`;-
- `checkAssertionsCount`;
- `timeout`;

## test.only(message, fn, options?)

Like `test(name, cb)` except if you use `.only` this is the only test case
that will run for the entire process, all other test cases using `tape` will
be ignored.

## test.skip(name, cb)
## test.skip(message, fn, options?)

Generate a new test that will be skipped over.

Expand Down
28 changes: 22 additions & 6 deletions packages/supertape/lib/run-tests.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict';

const process = require('process');
const fullstore = require('fullstore');
const wraptile = require('wraptile');
const tryToCatch = require('try-to-catch');
Expand All @@ -15,10 +14,9 @@ const notSkip = ({skip}) => !skip;

const getInitOperators = async () => await import('./operators.mjs');

const {SUPERTAPE_TIMEOUT = 3000} = process.env;
const DEBUG_TIME = 3000 * 1000;

const timeout = (time, value) => {
const doTimeout = (time, value) => {
let stop;

if (isDebug)
Expand Down Expand Up @@ -73,7 +71,7 @@ async function runTests(tests, {formatter, operators, skiped, isStop}) {
tests,
});

for (const {fn, message, extensions, at, validations} of tests) {
for (const {fn, message, timeout, extensions, at, validations} of tests) {
if (wasStop())
break;

Expand All @@ -95,6 +93,7 @@ async function runTests(tests, {formatter, operators, skiped, isStop}) {
incPassed,
getValidationMessage,
validations,
timeout,

extensions: {
...operators,
Expand All @@ -118,7 +117,24 @@ async function runTests(tests, {formatter, operators, skiped, isStop}) {
};
}

async function runOneTest({message, at, fn, extensions, formatter, count, total, failed, incCount, incPassed, incFailed, getValidationMessage, validations}) {
async function runOneTest(options) {
const {
message,
at,
fn,
extensions,
formatter,
count,
total,
failed,
incCount,
incPassed,
incFailed,
getValidationMessage,
validations,
timeout,
} = options;

const isReturn = fullstore(false);
const assertionsCount = fullstore(0);
const isEnded = fullstore(false);
Expand All @@ -145,7 +161,7 @@ async function runOneTest({message, at, fn, extensions, formatter, count, total,
});

if (!isReturn()) {
const [timer, stopTimer] = timeout(SUPERTAPE_TIMEOUT, ['timeout']);
const [timer, stopTimer] = doTimeout(timeout, ['timeout']);
const [error] = await Promise.race([tryToCatch(fn, t), timer]);

stopTimer();
Expand Down
33 changes: 32 additions & 1 deletion packages/supertape/lib/run-tests.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ test('supertape: runTests: fail: at', async (t) => {
t.end();
});

test('supertape: runTests: fail: timeout', async (t) => {
test('supertape: runTests: fail: timeout: SUPERTAPE_TIMEOUT', async (t) => {
const fn = async (t) => {
await once(new EventEmitter(), 'end');
t.end();
Expand Down Expand Up @@ -525,6 +525,37 @@ test('supertape: runTests: fail: timeout', async (t) => {
t.end();
});

test('supertape: runTests: fail: timeout', async (t) => {
const fn = async (t) => {
await once(new EventEmitter(), 'end');
t.end();
};

const message = 'hello world';

const supertape = reRequire('..');
supertape(message, fn, {
quiet: true,
timeout: 1,
});

const [result] = await Promise.all([
pull(supertape.createStream(), 5),
once(supertape.run(), 'end'),
]);

const expected = montag`
TAP version 13
# hello world
not ok 1 timeout
---
operator: fail
`;

t.equal(result, expected);
t.end();
});

test('supertape: runTests: equal', async (t) => {
const fn = (t) => {
t.equal('hello', 'hello');
Expand Down
1 change: 1 addition & 0 deletions packages/supertape/lib/supertape.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type TestOptions = {
checkAssertionsCount?: boolean;
checkScopes?: boolean;
checkDuplicates?: boolean;
timeout?: number;
};

declare function test(message: string, fn: (t: Test) => void, options?: TestOptions): void;
Expand Down
11 changes: 9 additions & 2 deletions packages/supertape/lib/supertape.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ const {assign} = Object;
const {stdout} = process;

// 5ms ought to be enough for anybody
const {SUPERTAPE_LOAD_LOOP_TIMEOUT = 5} = process.env;
const {
SUPERTAPE_LOAD_LOOP_TIMEOUT = 5,
SUPERTAPE_TIMEOUT = 3000,
} = process.env;

let mainEmitter;

Expand All @@ -44,13 +47,14 @@ const defaultOptions = {
checkIfEnded: true,
checkAssertionsCount: true,
checkScopes: true,
timeout: SUPERTAPE_TIMEOUT,
};

function _createEmitter({quiet, stream = stdout, format, getOperators, isStop, readyFormatter, workerFormatter}) {
const tests = [];
const emitter = new EventEmitter();

emitter.on('test', (message, fn, {skip, only, extensions, at, validations}) => {
emitter.on('test', (message, fn, {skip, only, extensions, at, validations, timeout}) => {
tests.push({
message,
fn,
Expand All @@ -59,6 +63,7 @@ function _createEmitter({quiet, stream = stdout, format, getOperators, isStop, r
extensions,
at,
validations,
timeout,
});
});

Expand Down Expand Up @@ -154,6 +159,7 @@ function test(message, fn, options = {}) {
checkAssertionsCount,
checkIfEnded,
workerFormatter,
timeout,
} = {
...defaultOptions,
...initedOptions,
Expand Down Expand Up @@ -186,6 +192,7 @@ function test(message, fn, options = {}) {
extensions,
at,
validations,
timeout,
});

if (run)
Expand Down
5 changes: 5 additions & 0 deletions packages/supertape/test/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ test('hello', (t: Test) => {
t.end();
}, {checkScopes: false});

test.only('hello', (t: Test) => {
t.end();
// THROWS Type 'string' is not assignable to type 'number'
}, {timeout: 'hello'});

test('hello', (t: Test) => {
t.end();
// THROWS Object literal may only specify known properties, and 'checkUnknown' does not exist in type 'TestOptions'
Expand Down

0 comments on commit 6433719

Please sign in to comment.