Skip to content

Commit 5484aae

Browse files
committed
Core: Remove deprecated QUnit.load()
* Move scheduleBegin() code into QUnit.start(). * Remove internal config.pageLoaded, unused. * Remove redundant `config.blocking` assignment, already set by begin(). == Reasoning for DOM-ready delay in QUnit.start == After the above changes, /test/startError.html failed when run via grunt-contrib-qunit (e.g. CI) due to a Chrome timeout with no test results. It passed in a browser. I realised that this is because the bridge is injected after QUnit.start() and our tiny test were already finishesd. I believe this would affect even simple cases where someone sets autostart=false and correctly calls QUnit.start(). This worked before because legacy QUnit.load/QUnit.autostart was implicitly delaying the begin() call even if you don't use autostart or call load+start both. The reason is the `config.pageLoaded`. If the user disables autostart, it is common for them to use async code loading (e.g. AMD) and thus start manually after DOM-ready. But, it is entirely valid for them to be ready before DOM-ready in some cases. In that case, our legacy logic was still kicking in by re-enabling autostart and then waiting for the original html.js event handler for window.onload to call QUnit.start() before we "really" begin. I don't remember if that was a lazy way to resolve conflicts between the two, or a deliberate feature. We deprecated the conflict handling part of this in QUnit 2 and this patch removes that. But, it seems this was also serving as a feature to always ensure we wait for DOM-ready no matter what, which is actually very useful to making sure that integrations and reporter plugins have a chance to listen for "runStart". Solution: If you call QUnit.start(), and there is a document, and we've not reached `document.readyStart=complete` (i.e. you called it manually, most likely with autostart=false), then we will now still explicitly wait for window.onload before calling begin(). For all intents and purposes, this delay is now invisible and internal to QUnit. We do consider QUnit to have "started" during this delay. We were already using setTimeout previously between `start` and `begin` and this is effectively and extension of that. Ref #1084
1 parent 205d5f3 commit 5484aae

File tree

5 files changed

+61
-102
lines changed

5 files changed

+61
-102
lines changed

docs/api/QUnit/load.md

+3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ title: QUnit.load()
44
excerpt: Inform the test runner that code has finished loading.
55
groups:
66
- deprecated
7+
- removed
78
redirect_from:
89
- "/QUnit/load/"
910
version_added: "1.0.0"
1011
version_deprecated: "2.21.0"
12+
version_removed: "unreleased"
1113
---
1214

1315
`QUnit.load()`
@@ -22,6 +24,7 @@ As of [QUnit 2.1.1](https://github.com/qunitjs/qunit/releases/tag/2.1.1), calls
2224

2325
## Changelog
2426

27+
| UNRELEASED | Removed.
2528
| [QUnit 2.21.0](https://github.com/qunitjs/qunit/releases/tag/2.21.0) | Deprecated. Use [`QUnit.start()`](./start.md) instead.
2629
| [QUnit 2.1.1](https://github.com/qunitjs/qunit/releases/tag/2.1.1) | `QUnit.start()` no longer requires calling `QUnit.load()` first.
2730

src/core.js

+24-82
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@ config.currentModule.suiteReport = runSuite;
3333

3434
config.pq = new ProcessingQueue(test);
3535

36-
let globalStartCalled = false;
37-
let runStarted = false;
38-
3936
// Figure out if we're running the tests from a server or not
4037
QUnit.isLocal = (window && window.location && window.location.protocol === 'file:');
4138

@@ -66,46 +63,35 @@ extend(QUnit, {
6663
skip: test.skip,
6764
only: test.only,
6865

69-
start: function (count) {
66+
start: function () {
7067
if (config.current) {
71-
throw new Error('QUnit.start cannot be called inside a test context.');
72-
}
73-
74-
const globalStartAlreadyCalled = globalStartCalled;
75-
globalStartCalled = true;
76-
77-
if (runStarted) {
78-
throw new Error('Called start() while test already started running');
79-
}
80-
if (globalStartAlreadyCalled || count > 1) {
81-
throw new Error('Called start() outside of a test context too many times');
68+
throw new Error('QUnit.start cannot be called inside a test.');
8269
}
83-
if (config.autostart) {
84-
throw new Error('Called start() outside of a test context when ' +
85-
'QUnit.config.autostart was true');
70+
if (config._runStarted) {
71+
if (document && config.autostart) {
72+
throw new Error('QUnit.start() called too many times. Did you call QUnit.start() in browser context when autostart is also enabled? https://qunitjs.com/api/QUnit/start/');
73+
}
74+
throw new Error('QUnit.start() called too many times.');
8675
}
8776

88-
// Until we remove QUnit.load() in QUnit 3, we keep `pageLoaded`.
89-
// It no longer serves any purpose other than to support old test runners
90-
// that still call only QUnit.load(), or that call both it and QUnit.start().
91-
if (!config.pageLoaded) {
92-
// If the test runner used `autostart = false` and is calling QUnit.start()
93-
// to tell is their resources are ready, but the browser isn't ready yet,
94-
// then enable autostart now, and we'll let the tests really start after
95-
// the browser's "load" event handler calls autostart().
96-
config.autostart = true;
97-
98-
// If we're in Node or another non-browser environment, we start now as there
99-
// won't be any "load" event. We return early either way since autostart
100-
// is responsible for calling scheduleBegin (avoid "beginning" twice).
101-
if (!document) {
102-
QUnit.autostart();
103-
}
77+
config._runStarted = true;
10478

105-
return;
79+
// Add a slight delay to allow definition of more modules and tests.
80+
if (document && document.readyState !== 'complete' && setTimeout) {
81+
// In browser environments, if QUnit.start() is called very early,
82+
// still wait for DOM ready to ensure reliable integration of reporters.
83+
window.addEventListener('load', function () {
84+
setTimeout(function () {
85+
begin();
86+
});
87+
});
88+
} else if (setTimeout) {
89+
setTimeout(function () {
90+
begin();
91+
});
92+
} else {
93+
begin();
10694
}
107-
108-
scheduleBegin();
10995
},
11096

11197
onUnhandledRejection: function (reason) {
@@ -114,37 +100,6 @@ extend(QUnit, {
114100
onUncaughtException(reason);
115101
},
116102

117-
load: function () {
118-
Logger.warn('QUnit.load is deprecated and will be removed in QUnit 3.0.' +
119-
' https://qunitjs.com/api/QUnit/load/');
120-
121-
QUnit.autostart();
122-
},
123-
124-
/**
125-
* @internal
126-
*/
127-
autostart: function () {
128-
config.pageLoaded = true;
129-
130-
// Initialize the configuration options
131-
// TODO: Move this to config.js in QUnit 3.
132-
extend(config, {
133-
started: 0,
134-
updateRate: 1000,
135-
autostart: true,
136-
filter: ''
137-
}, true);
138-
139-
if (!runStarted) {
140-
config.blocking = false;
141-
142-
if (config.autostart) {
143-
scheduleBegin();
144-
}
145-
}
146-
},
147-
148103
stack: function (offset) {
149104
offset = (offset || 0) + 2;
150105
return sourceFromStacktrace(offset);
@@ -153,25 +108,12 @@ extend(QUnit, {
153108

154109
registerLoggingCallbacks(QUnit);
155110

156-
function scheduleBegin () {
157-
runStarted = true;
158-
159-
// Add a slight delay to allow definition of more modules and tests.
160-
if (setTimeout) {
161-
setTimeout(function () {
162-
begin();
163-
});
164-
} else {
165-
begin();
166-
}
167-
}
168-
169111
function unblockAndAdvanceQueue () {
170112
config.blocking = false;
171113
config.pq.advance();
172114
}
173115

174-
export function begin () {
116+
function begin () {
175117
if (config.started) {
176118
unblockAndAdvanceQueue();
177119
return;

src/core/config.js

+6-8
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ const config = {
1010
// HTML Reporter: Modify document.title when suite is done
1111
altertitle: true,
1212

13-
// TODO: Move here from /src/core.js in QUnit 3.
14-
// autostart: true,
13+
autostart: true,
1514

1615
// HTML Reporter: collapse every test except the first failing test
1716
// If false, all failing tests will be expanded
@@ -25,7 +24,7 @@ const config = {
2524
failOnZeroTests: true,
2625

2726
// Select by pattern or case-insensitive substring match against "moduleName: testName"
28-
filter: undefined,
27+
filter: '',
2928

3029
// TODO: Make explicit in QUnit 3.
3130
// fixture: undefined,
@@ -62,8 +61,7 @@ const config = {
6261
// so that the browser can visually paint DOM changes with test results.
6362
// This also helps avoid causing browsers to prompt a warning about
6463
// long-running scripts.
65-
// TODO: Move here from /src/core.js in QUnit 3.
66-
// updateRate: 1000,
64+
updateRate: 1000,
6765

6866
// HTML Reporter: List of URL parameters that are given visual controls
6967
urlConfig: [],
@@ -122,11 +120,11 @@ const config = {
122120
// Internal: ProcessingQueue singleton, created in /src/core.js
123121
pq: null,
124122

125-
// Internal: Created in /src/core.js
126-
// TODO: Move definitions here in QUnit 3.0.
127-
// started: 0,
123+
// Internal: Used for runtime measurement to runEnd event and QUnit.done.
124+
started: 0,
128125

129126
// Internal state
127+
_runStarted: false,
130128
_deprecated_timeout_shown: false,
131129
blocking: true,
132130
callbacks: {},

src/html-reporter/html.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -1070,10 +1070,18 @@ const stats = {
10701070
testItem.className = 'fail';
10711071
});
10721072

1073+
function autostart () {
1074+
// Check as late as possible because if projecst set autostart=false,
1075+
// they generally do so in their own scripts, after qunit.js.
1076+
if (config.autostart) {
1077+
QUnit.start();
1078+
}
1079+
}
1080+
10731081
if (document.readyState === 'complete') {
1074-
QUnit.autostart();
1082+
autostart();
10751083
} else {
1076-
addEvent(window, 'load', QUnit.autostart);
1084+
addEvent(window, 'load', autostart);
10771085
}
10781086

10791087
// Wrap window.onerror. We will call the original window.onerror to see if

test/startError.js

+18-10
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,42 @@
11
/* eslint-env browser */
22

3+
QUnit.config.autostart = false;
4+
5+
// Real
6+
QUnit.start();
7+
8+
// Bad 1
9+
QUnit.config.autostart = true;
310
try {
4-
QUnit.config.autostart = true;
511
QUnit.start();
612
} catch (thrownError) {
713
window.autostartStartError = thrownError;
814
}
915

16+
// Bad 2
17+
QUnit.config.autostart = false;
1018
try {
1119
QUnit.start();
1220
} catch (thrownError) {
1321
window.tooManyStartsError = thrownError;
1422
}
1523

16-
QUnit.module('global start unrecoverable errors');
24+
QUnit.module('startError');
1725

18-
QUnit.test('start() throws when QUnit.config.autostart === true', function (assert) {
19-
assert.equal(window.autostartStartError.message,
20-
'Called start() outside of a test context when QUnit.config.autostart was true');
26+
QUnit.test('start() with autostart enabled [Bad 1]', function (assert) {
27+
assert.propContains(window.autostartStartError,
28+
{ message: 'QUnit.start() called too many times. Did you call QUnit.start() in browser context when autostart is also enabled? https://qunitjs.com/api/QUnit/start/' });
2129
});
2230

23-
QUnit.test('Throws after calling start() too many times outside of a test context', function (assert) {
24-
assert.equal(window.tooManyStartsError.message,
25-
'Called start() outside of a test context too many times');
31+
QUnit.test('start() again [Bad 2]', function (assert) {
32+
assert.propContains(window.tooManyStartsError,
33+
{ message: 'QUnit.start() called too many times.' });
2634
});
2735

28-
QUnit.test('QUnit.start cannot be called inside a test context.', function (assert) {
36+
QUnit.test('start() inside a test', function (assert) {
2937
assert.throws(function () {
3038
// eslint-disable-next-line qunit/no-qunit-start-in-tests
3139
QUnit.start();
3240
},
33-
/QUnit\.start cannot be called inside a test context\./);
41+
new Error('QUnit.start cannot be called inside a test.'));
3442
});

0 commit comments

Comments
 (0)