Skip to content

Commit ca8ea45

Browse files
jonathansaminesnovemberborn
andauthoredJul 4, 2020
Support assertion message in t.timeout()
Fixes #2443. Co-authored-by: Mark Wubben <mark@novemberborn.net>
1 parent f72fab4 commit ca8ea45

File tree

11 files changed

+109
-19
lines changed

11 files changed

+109
-19
lines changed
 

Diff for: ‎docs/07-test-timeouts.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,22 @@ npx ava --timeout=2m # 2 minutes
1414
npx ava --timeout=100 # 100 milliseconds
1515
```
1616

17-
Timeouts can also be set individually for each test. These timeouts are reset each time an assertion is made.
17+
### `t.timeout(ms, message?)`
18+
19+
Timeouts can also be set individually for each test These timeouts are reset each time an assertion is made. The test fails if it takes more than `ms` for an assertion to be made or the test to complete.
1820

1921
```js
2022
test('foo', t => {
2123
t.timeout(100); // 100 milliseconds
2224
// Write your assertions here
2325
});
2426
```
27+
28+
An optional message string can be provided. This can be useful if your test depends on some other setup that may not have been completed:
29+
30+
```js
31+
test('foo', t => {
32+
t.timeout(100, 'make sure database has started'); // 100 milliseconds
33+
// Write your assertions here
34+
});
35+
```

Diff for: ‎index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ export interface TimeoutFn {
353353
* Set a timeout for the test, in milliseconds. The test will fail if the timeout is exceeded.
354354
* The timeout is reset each time an assertion is made.
355355
*/
356-
(ms: number): void;
356+
(ms: number, message?: string): void;
357357
}
358358

359359
export interface TeardownFn {

Diff for: ‎lib/assert.js

+20-11
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,21 @@ class AssertionError extends Error {
6464
}
6565
exports.AssertionError = AssertionError;
6666

67+
function checkAssertionMessage(assertion, message) {
68+
if (typeof message === 'undefined' || typeof message === 'string') {
69+
return true;
70+
}
71+
72+
return new AssertionError({
73+
assertion,
74+
improperUsage: true,
75+
message: 'The assertion message must be a string',
76+
values: [formatWithLabel('Called with:', message)]
77+
});
78+
}
79+
80+
exports.checkAssertionMessage = checkAssertionMessage;
81+
6782
function getErrorWithLongStackTrace() {
6883
const limitBefore = Error.stackTraceLimit;
6984
Error.stackTraceLimit = Infinity;
@@ -268,22 +283,16 @@ class Assertions {
268283
});
269284

270285
const checkMessage = (assertion, message, powerAssert = false) => {
271-
if (typeof message === 'undefined' || typeof message === 'string') {
272-
return true;
286+
const result = checkAssertionMessage(assertion, message);
287+
if (result === true) {
288+
return this.true;
273289
}
274290

275-
const error = new AssertionError({
276-
assertion,
277-
improperUsage: true,
278-
message: 'The assertion message must be a string',
279-
values: [formatWithLabel('Called with:', message)]
280-
});
281-
282291
if (powerAssert) {
283-
throw error;
292+
throw result;
284293
}
285294

286-
fail(error);
295+
fail(result);
287296
return false;
288297
};
289298

Diff for: ‎lib/test.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ class ExecutionContext extends assert.Assertions {
6565

6666
this.plan.skip = () => {};
6767

68-
this.timeout = ms => {
69-
test.timeout(ms);
68+
this.timeout = (ms, message) => {
69+
test.timeout(ms, message);
7070
};
7171

7272
this.teardown = callback => {
@@ -431,15 +431,22 @@ class Test {
431431
this.planError = planError;
432432
}
433433

434-
timeout(ms) {
434+
timeout(ms, message) {
435+
const result = assert.checkAssertionMessage('timeout', message);
436+
if (result !== true) {
437+
this.saveFirstError(result);
438+
// Allow the timeout to be set even when the message is invalid.
439+
message = '';
440+
}
441+
435442
if (this.finishing) {
436443
return;
437444
}
438445

439446
this.clearTimeout();
440447
this.timeoutMs = ms;
441448
this.timeoutTimer = nowAndTimers.setTimeout(() => {
442-
this.saveFirstError(new Error('Test timeout exceeded'));
449+
this.saveFirstError(new Error(message || 'Test timeout exceeded'));
443450

444451
if (this.finishDueToTimeout) {
445452
this.finishDueToTimeout();

Diff for: ‎test/helpers/exec.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ exports.fixture = async (...args) => {
1919
serialization
2020
});
2121

22+
const errors = new WeakMap();
2223
const stats = {
2324
failed: [],
2425
skipped: [],
2526
unsavedSnapshots: [],
26-
passed: []
27+
passed: [],
28+
getError(statObject) {
29+
return errors.get(statObject);
30+
}
2731
};
2832

2933
running.on('message', message => {
@@ -55,7 +59,9 @@ exports.fixture = async (...args) => {
5559

5660
case 'test-failed': {
5761
const {title, testFile} = message;
58-
stats.failed.push({title, file: normalizePath(cwd, testFile)});
62+
const statObject = {title, file: normalizePath(cwd, testFile)};
63+
errors.set(statObject, message.err);
64+
stats.failed.push(statObject);
5965
break;
6066
}
6167

Diff for: ‎test/test-timeouts/fixtures/custom-message.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const test = require('ava');
2+
3+
test('timeout with custom message', async t => {
4+
t.timeout(10, 'time budget exceeded'); // eslint-disable-line ava/assertion-arguments
5+
await new Promise(() => {});
6+
});

Diff for: ‎test/test-timeouts/fixtures/invalid-message.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const test = require('ava');
2+
3+
test('timeout with invalid message', t => {
4+
t.timeout(10, 20); // eslint-disable-line ava/assertion-arguments
5+
});
6+

Diff for: ‎test/test-timeouts/fixtures/package.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"ava": {
3+
"files": [
4+
"*.js"
5+
]
6+
},
7+
"dependencies": {
8+
"ava": "file:../../.."
9+
}
10+
}

Diff for: ‎test/test-timeouts/snapshots/test.js.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Snapshot report for `test/test-timeouts/test.js`
2+
3+
The actual snapshot is saved in `test.js.snap`.
4+
5+
Generated by [AVA](https://avajs.dev).
6+
7+
## timeout messages must be strings
8+
9+
> error message
10+
11+
'The assertion message must be a string'
12+
13+
> formatted values
14+
15+
[
16+
{
17+
formatted: '20',
18+
label: 'Called with:',
19+
},
20+
]

Diff for: ‎test/test-timeouts/snapshots/test.js.snap

255 Bytes
Binary file not shown.

Diff for: ‎test/test-timeouts/test.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const test = require('@ava/test');
2+
const exec = require('../helpers/exec');
3+
4+
test('timeout message can be specified', async t => {
5+
const result = await t.throwsAsync(exec.fixture('custom-message.js'));
6+
const error = result.stats.getError(result.stats.failed[0]);
7+
t.is(error.message, 'time budget exceeded');
8+
});
9+
10+
test('timeout messages must be strings', async t => {
11+
const result = await t.throwsAsync(exec.fixture('invalid-message.js'));
12+
const error = result.stats.getError(result.stats.failed[0]);
13+
t.snapshot(error.message, 'error message');
14+
t.snapshot(error.values, 'formatted values');
15+
});

0 commit comments

Comments
 (0)
Please sign in to comment.