Skip to content

Commit

Permalink
Core: Enable a default QUnit.config.testTimeout of 3 seconds
Browse files Browse the repository at this point in the history
Fixes #1132.
Fixes #1483.
  • Loading branch information
Krinkle committed Jun 25, 2024
1 parent 44a6ba1 commit 3c2b768
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 56 deletions.
12 changes: 6 additions & 6 deletions docs/api/config/testTimeout.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
---
layout: page-api
title: QUnit.config.testTimeout
excerpt: Set a global default timeout for async tests.
excerpt: Default timeout for async tests.
groups:
- config
redirect_from:
- "/config/testTimeout/"
version_added: "1.0.0"
---

Set a global default timeout in milliseconds after which an async test will fail. This helps to detect async tests that are broken, and prevents a test run from hanging indefinitely.
Default timeout in milliseconds after which an async test will fail. This helps to detect async tests that are broken, and prevents a test run from hanging indefinitely.

<table>
<tr>
<th>type</th>
<td markdown="span">`number` or `undefined`</td>
<td markdown="span">`number`</td>
</tr>
<tr>
<th>default</th>
<td markdown="span">`undefined`</td>
<td markdown="span">`3000`</td>
</tr>
</table>

Only async tests can timeout. An async test is any [QUnit.test](../QUnit/test.md) with an async function as callback, or that returns a Promise, or that calls [assert.async()](../assert/async.md).

The `testTimeout` config can be overridden via [assert.timeout()](../assert/timeout.md) to any lower or higher amount.
Individual tests can override the default `testTimeout` config via [assert.timeout()](../assert/timeout.md) to any lower or higher amount.

It is recommended to keep the global default at `3000` or higher (3 seconds), to avoid intermittent failures from unrelated delays that may periodically occur inside a browser or CI service.
It is recommended to set the default at `3000` or higher (3 seconds). A lower timeout may cause intermittent failures due to unrelated infrastructure delays that are known to sometimes occur inside CI services and other virtual servers.

## Introducing a default timeout

Expand Down
3 changes: 2 additions & 1 deletion src/core/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ const config = {

testId: undefined,

testTimeout: 3000,

// The updateRate controls how often QUnit will yield the main thread
// between tests. This is mainly for the benefit of the HTML Reporter,
// so that the browser can visually paint DOM changes with test results.
Expand Down Expand Up @@ -151,7 +153,6 @@ const config = {
_runStarted: false,
_event_listeners: Object.create(null),
_event_memory: {},
_deprecated_timeout_shown: false,
blocking: true,
callbacks: {},
modules: [],
Expand Down
18 changes: 5 additions & 13 deletions src/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from './core/utilities';
import { runLoggingCallbacks } from './core/logging';
import { extractStacktrace, sourceFromStacktrace } from './core/stacktrace';
import dump from './dump';

import TestReport from './reports/test';

Expand Down Expand Up @@ -725,9 +726,12 @@ Test.prototype = {
timeoutDuration = test.timeout;
} else if (typeof config.testTimeout === 'number') {
timeoutDuration = config.testTimeout;
} else {
Logger.warn(`QUnit.config.testTimeout was set to an an invalid value (${dump.typeOf(config.testTimeout)}). Using default. https://qunitjs.com/api/config/testTimeout/`);
timeoutDuration = 3000;
}

if (typeof timeoutDuration === 'number' && timeoutDuration > 0) {
if (timeoutDuration > 0) {
config.timeoutHandler = function (timeout) {
return function () {
config.timeout = null;
Expand All @@ -746,18 +750,6 @@ Test.prototype = {
config.timeoutHandler(timeoutDuration),
timeoutDuration
);
} else {
clearTimeout(config.timeout);
config.timeout = setTimeout(
function () {
config.timeout = null;
if (!config._deprecated_timeout_shown) {
config._deprecated_timeout_shown = true;
Logger.warn(`Test "${test.testName}" took longer than 3000ms, but no timeout was set. Set QUnit.config.testTimeout or call assert.timeout() to avoid a timeout in QUnit 3. https://qunitjs.com/api/config/testTimeout/`);
}
},
3000
);
}
}

Expand Down
17 changes: 0 additions & 17 deletions test/cli/fixtures/config-testTimeout-deprecated.js

This file was deleted.

14 changes: 0 additions & 14 deletions test/cli/fixtures/config-testTimeout-deprecated.tap.txt

This file was deleted.

17 changes: 17 additions & 0 deletions test/cli/fixtures/config-testTimeout-invalid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
QUnit.test('invalid [undefined]', function (assert) {
QUnit.config.testTimeout = undefined;
setTimeout(assert.async(), 7);
assert.true(true);
});

QUnit.test('invalid [null]', function (assert) {
QUnit.config.testTimeout = null;
setTimeout(assert.async(), 7);
assert.true(true);
});

QUnit.test('invalid [string]', function (assert) {
QUnit.config.testTimeout = '9412';
setTimeout(assert.async(), 7);
assert.true(true);
});
16 changes: 16 additions & 0 deletions test/cli/fixtures/config-testTimeout-invalid.tap.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# command: ["qunit", "config-testTimeout-invalid.js"]

TAP version 13
ok 1 invalid [undefined]
ok 2 invalid [null]
ok 3 invalid [string]
1..3
# pass 3
# skip 0
# todo 0
# fail 0

# stderr
QUnit.config.testTimeout was set to an an invalid value (undefined). Using default. https://qunitjs.com/api/config/testTimeout/
QUnit.config.testTimeout was set to an an invalid value (null). Using default. https://qunitjs.com/api/config/testTimeout/
QUnit.config.testTimeout was set to an an invalid value (string). Using default. https://qunitjs.com/api/config/testTimeout/
19 changes: 14 additions & 5 deletions test/cli/fixtures/hanging-test.tap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@
# command: ["qunit","hanging-test.js"]

TAP version 13

# stderr
Test "hanging" took longer than 3000ms, but no timeout was set. Set QUnit.config.testTimeout or call assert.timeout() to avoid a timeout in QUnit 3. https://qunitjs.com/api/config/testTimeout/
Error: Process exited before tests finished running
Last test to run (hanging) has an async hold. Ensure all assert.async() callbacks are invoked and Promises resolve. You should also set a standard timeout via QUnit.config.testTimeout.
not ok 1 hanging
---
message: Test took longer than 3000ms; test timed out.
severity: failed
actual : null
expected: undefined
stack: |
at internal
...
1..1
# pass 0
# skip 0
# todo 0
# fail 1

# exit code: 1

0 comments on commit 3c2b768

Please sign in to comment.