parent
ed7807e372
commit
b65c6d7da8
6 changed files with 123 additions and 2 deletions
|
|
@ -33,3 +33,7 @@ End the test. Only works with `test.cb()`.
|
|||
## `t.log(...values)`
|
||||
|
||||
Log values contextually alongside the test result instead of immediately printing them to `stdout`. Behaves somewhat like `console.log`, but without support for placeholder tokens.
|
||||
|
||||
## `t.timeout(ms)`
|
||||
|
||||
Set a timeout for the test, in milliseconds. The test will fail if this timeout is exceeded. The timeout is reset each time an assertion is made.
|
||||
|
|
|
|||
|
|
@ -13,3 +13,12 @@ npx ava --timeout=10s # 10 seconds
|
|||
npx ava --timeout=2m # 2 minutes
|
||||
npx ava --timeout=100 # 100 milliseconds
|
||||
```
|
||||
|
||||
Timeouts can also be set individually for each test. These timeouts are reset each time an assertion is made.
|
||||
|
||||
```js
|
||||
test('foo', t => {
|
||||
t.timeout(100); // 100 milliseconds
|
||||
// Write your assertions here
|
||||
});
|
||||
```
|
||||
|
|
|
|||
9
index.d.ts
vendored
9
index.d.ts
vendored
|
|
@ -348,6 +348,7 @@ export interface ExecutionContext<Context = {}> extends Assertions {
|
|||
|
||||
log: LogFn;
|
||||
plan: PlanFn;
|
||||
timeout: TimeoutFn;
|
||||
}
|
||||
|
||||
export interface LogFn {
|
||||
|
|
@ -369,6 +370,14 @@ export interface PlanFn {
|
|||
skip(count: number): void;
|
||||
}
|
||||
|
||||
export interface TimeoutFn {
|
||||
/**
|
||||
* Set a timeout for the test, in milliseconds. The test will fail if the timeout is exceeded.
|
||||
* The timeout is reset each time an assertion is made.
|
||||
*/
|
||||
(ms: number): void;
|
||||
}
|
||||
|
||||
/** The `t` value passed to implementations for tests & hooks declared with the `.cb` modifier. */
|
||||
export interface CbExecutionContext<Context = {}> extends ExecutionContext<Context> {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -361,6 +361,7 @@ export interface ExecutionContext<Context = {}> extends Assertions {
|
|||
|
||||
log: LogFn;
|
||||
plan: PlanFn;
|
||||
timeout: TimeoutFn;
|
||||
}
|
||||
|
||||
export interface LogFn {
|
||||
|
|
@ -382,6 +383,14 @@ export interface PlanFn {
|
|||
skip(count: number): void;
|
||||
}
|
||||
|
||||
export interface TimeoutFn {
|
||||
/**
|
||||
* Set a timeout for the test, in milliseconds. The test will fail if the timeout is exceeded.
|
||||
* The timeout is reset each time an assertion is made.
|
||||
*/
|
||||
(ms: number): void;
|
||||
}
|
||||
|
||||
/** The `t` value passed to implementations for tests & hooks declared with the `.cb` modifier. */
|
||||
export interface CbExecutionContext<Context = {}> extends ExecutionContext<Context> {
|
||||
/**
|
||||
|
|
|
|||
61
lib/test.js
61
lib/test.js
|
|
@ -53,6 +53,10 @@ function plan(count) {
|
|||
this.plan(count, captureStack(this.plan));
|
||||
}
|
||||
|
||||
function timeout(ms) {
|
||||
this.timeout(ms);
|
||||
}
|
||||
|
||||
const testMap = new WeakMap();
|
||||
class ExecutionContext {
|
||||
constructor(test) {
|
||||
|
|
@ -71,7 +75,8 @@ class ExecutionContext {
|
|||
return props;
|
||||
}, {
|
||||
log: {value: log.bind(test)},
|
||||
plan: {value: boundPlan}
|
||||
plan: {value: boundPlan},
|
||||
timeout: {value: timeout.bind(test)}
|
||||
}));
|
||||
|
||||
this.snapshot.skip = () => {
|
||||
|
|
@ -140,11 +145,14 @@ class Test {
|
|||
this.endCallbackFinisher = null;
|
||||
this.finishDueToAttributedError = null;
|
||||
this.finishDueToInactivity = null;
|
||||
this.finishDueToTimeout = null;
|
||||
this.finishing = false;
|
||||
this.pendingAssertionCount = 0;
|
||||
this.pendingThrowsAssertion = null;
|
||||
this.planCount = null;
|
||||
this.startedAt = 0;
|
||||
this.timeoutTimer = null;
|
||||
this.timeoutMs = 0;
|
||||
}
|
||||
|
||||
bindEndCallback() {
|
||||
|
|
@ -189,6 +197,7 @@ class Test {
|
|||
}
|
||||
|
||||
this.assertCount++;
|
||||
this.refreshTimeout();
|
||||
}
|
||||
|
||||
addLog(text) {
|
||||
|
|
@ -202,9 +211,14 @@ class Test {
|
|||
|
||||
this.assertCount++;
|
||||
this.pendingAssertionCount++;
|
||||
this.refreshTimeout();
|
||||
|
||||
promise
|
||||
.catch(error => this.saveFirstError(error))
|
||||
.then(() => this.pendingAssertionCount--);
|
||||
.then(() => {
|
||||
this.pendingAssertionCount--;
|
||||
this.refreshTimeout();
|
||||
});
|
||||
}
|
||||
|
||||
addFailedAssertion(error) {
|
||||
|
|
@ -213,6 +227,7 @@ class Test {
|
|||
}
|
||||
|
||||
this.assertCount++;
|
||||
this.refreshTimeout();
|
||||
this.saveFirstError(error);
|
||||
}
|
||||
|
||||
|
|
@ -234,6 +249,39 @@ class Test {
|
|||
this.planStack = planStack;
|
||||
}
|
||||
|
||||
timeout(ms) {
|
||||
if (this.finishing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearTimeout();
|
||||
this.timeoutMs = ms;
|
||||
this.timeoutTimer = nowAndTimers.setTimeout(() => {
|
||||
this.saveFirstError(new Error('Test timeout exceeded'));
|
||||
|
||||
if (this.finishDueToTimeout) {
|
||||
this.finishDueToTimeout();
|
||||
}
|
||||
}, ms);
|
||||
}
|
||||
|
||||
refreshTimeout() {
|
||||
if (!this.timeoutTimer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.timeoutTimer.refresh) {
|
||||
this.timeoutTimer.refresh();
|
||||
} else {
|
||||
this.timeout(this.timeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
clearTimeout() {
|
||||
clearTimeout(this.timeoutTimer);
|
||||
this.timeoutTimer = null;
|
||||
}
|
||||
|
||||
verifyPlan() {
|
||||
if (!this.assertError && this.planCount !== null && this.planCount !== this.assertCount) {
|
||||
this.saveFirstError(new assert.AssertionError({
|
||||
|
|
@ -370,6 +418,10 @@ class Test {
|
|||
resolve(this.finishPromised());
|
||||
};
|
||||
|
||||
this.finishDueToTimeout = () => {
|
||||
resolve(this.finishPromised());
|
||||
};
|
||||
|
||||
this.finishDueToInactivity = () => {
|
||||
this.saveFirstError(new Error('`t.end()` was never called'));
|
||||
resolve(this.finishPromised());
|
||||
|
|
@ -383,6 +435,10 @@ class Test {
|
|||
resolve(this.finishPromised());
|
||||
};
|
||||
|
||||
this.finishDueToTimeout = () => {
|
||||
resolve(this.finishPromised());
|
||||
};
|
||||
|
||||
this.finishDueToInactivity = () => {
|
||||
const error = returnedObservable ?
|
||||
new Error('Observable returned by test never completed') :
|
||||
|
|
@ -415,6 +471,7 @@ class Test {
|
|||
return this.waitForPendingThrowsAssertion();
|
||||
}
|
||||
|
||||
this.clearTimeout();
|
||||
this.verifyPlan();
|
||||
this.verifyAssertions();
|
||||
|
||||
|
|
|
|||
33
test/test.js
33
test/test.js
|
|
@ -764,3 +764,36 @@ test('implementation runs with null scope', t => {
|
|||
t.is(this, null);
|
||||
}).run();
|
||||
});
|
||||
|
||||
test('timeout with promise', t => {
|
||||
return ava(a => {
|
||||
a.timeout(10);
|
||||
return delay(200);
|
||||
}).run().then(result => {
|
||||
t.is(result.passed, false);
|
||||
t.match(result.error.message, /timeout/);
|
||||
});
|
||||
});
|
||||
|
||||
test('timeout with cb', t => {
|
||||
return ava.cb(a => {
|
||||
a.timeout(10);
|
||||
setTimeout(() => a.end(), 200);
|
||||
}).run().then(result => {
|
||||
t.is(result.passed, false);
|
||||
t.match(result.error.message, /timeout/);
|
||||
});
|
||||
});
|
||||
|
||||
test('timeout is refreshed on assert', t => {
|
||||
return ava.cb(a => {
|
||||
a.timeout(10);
|
||||
a.plan(3);
|
||||
setTimeout(() => a.pass(), 5);
|
||||
setTimeout(() => a.pass(), 10);
|
||||
setTimeout(() => a.pass(), 15);
|
||||
setTimeout(() => a.end(), 20);
|
||||
}).run().then(result => {
|
||||
t.is(result.passed, true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue