Skip to content

Commit df7fb26

Browse files
committed
HTML Reporter: Fix missing state for custom checkboxes (unreleased regression)
Fixes #1792.
1 parent c79a25d commit df7fb26

File tree

3 files changed

+108
-79
lines changed

3 files changed

+108
-79
lines changed

src/core/reporters/HtmlReporter.js

+84-79
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import diff from '../diff.js';
33
import dump from '../dump.js';
44
import { prioritySymbol } from '../events.js';
55
import { window, document, navigator, StringMap } from '../globals.js';
6-
import { urlParams } from '../urlparams.js';
76
import version from '../version.js';
87
import fuzzysort from 'fuzzysort';
98

@@ -52,70 +51,6 @@ const DOM = {
5251
}
5352
};
5453

55-
function getUrlConfigHtml (config) {
56-
const urlConfig = config.urlConfig;
57-
let urlConfigHtml = '';
58-
59-
for (let i = 0; i < urlConfig.length; i++) {
60-
// Options can be either strings or objects with nonempty "id" properties
61-
let val = urlConfig[i];
62-
if (typeof val === 'string') {
63-
val = {
64-
id: val,
65-
label: val
66-
};
67-
}
68-
const currentVal = config[val.id];
69-
70-
let escaped = escapeText(val.id);
71-
let escapedTooltip = escapeText(val.tooltip);
72-
73-
if (!val.value || typeof val.value === 'string') {
74-
urlConfigHtml += '<label for="qunit-urlconfig-' + escaped
75-
+ '" title="' + escapedTooltip + '"><input id="qunit-urlconfig-' + escaped
76-
+ '" name="' + escaped + '" type="checkbox"'
77-
+ (val.value ? ' value="' + escapeText(val.value) + '"' : '')
78-
+ (currentVal ? ' checked="checked"' : '')
79-
+ ' title="' + escapedTooltip + '" />' + escapeText(val.label) + '</label>';
80-
} else {
81-
let selection = false;
82-
urlConfigHtml += '<label for="qunit-urlconfig-' + escaped
83-
+ '" title="' + escapedTooltip + '">' + escapeText(val.label)
84-
+ ': <select id="qunit-urlconfig-' + escaped
85-
+ '" name="' + escaped + '" title="' + escapedTooltip + '"><option></option>';
86-
87-
if (Array.isArray(val.value)) {
88-
for (let j = 0; j < val.value.length; j++) {
89-
escaped = escapeText(val.value[j]);
90-
urlConfigHtml += '<option value="' + escaped + '"'
91-
+ (currentVal === val.value[j]
92-
? (selection = true) && ' selected="selected"'
93-
: '')
94-
+ '>' + escaped + '</option>';
95-
}
96-
} else {
97-
for (let j in val.value) {
98-
if (hasOwn.call(val.value, j)) {
99-
urlConfigHtml += '<option value="' + escapeText(j) + '"'
100-
+ (currentVal === j
101-
? (selection = true) && ' selected="selected"'
102-
: '')
103-
+ '>' + escapeText(val.value[j]) + '</option>';
104-
}
105-
}
106-
}
107-
if (currentVal && !selection) {
108-
escaped = escapeText(currentVal);
109-
urlConfigHtml += '<option value="' + escaped
110-
+ '" selected="selected" disabled="disabled">' + escaped + '</option>';
111-
}
112-
urlConfigHtml += '</select></label>';
113-
}
114-
}
115-
116-
return urlConfigHtml;
117-
}
118-
11954
function stripHtml (string) {
12055
// Strip tags, html entity and whitespaces
12156
return string
@@ -131,6 +66,7 @@ export default class HtmlReporter {
13166
*/
13267
static init (QUnit, options = {}) {
13368
return new HtmlReporter(QUnit, extend(options, {
69+
urlParams: QUnit.urlParams,
13470
// This must use a live reference (i.e. not store a copy), because
13571
// users may apply their settings to QUnit.config anywhere between
13672
// loading qunit.js and the last QUnit.begin() listener finishing.
@@ -146,13 +82,14 @@ export default class HtmlReporter {
14682
*
14783
* @param {QUnit} QUnit
14884
* @param {Object} options
85+
* @param {Object} options.urlParams
14986
* @param {Object} options.config
150-
* @param {boolean} options.config.hidepassed
151-
* @param {boolean} options.config.collapse For test result
152-
* @param {string} options.config.filter
153-
* @param {?string} options.config.moduleId For module selector
154-
* @param {?string} options.config.testId For test result, rerun link
155-
* @param {number} options.config.maxDepth For test result, error message
87+
* - `boolean` options.config.hidepassed
88+
* - `boolean` options.config.collapse For test result
89+
* - `string` options.config.filter
90+
* - `?string` options.config.moduleId For module selector
91+
* - `?string` options.config.testId For test result, rerun link
92+
* - `number` options.config.maxDepth For test result, error message
15693
* @param {Function} options.abort
15794
* @param {HTMLElement|undefined|null} options.element Output element
15895
* If set to HTMLElement, the report will be written to this element.
@@ -170,6 +107,7 @@ export default class HtmlReporter {
170107
defined: 0,
171108
completed: 0
172109
};
110+
this.urlParams = options.urlParams;
173111
this.config = options.config;
174112
this.abort = options.abort;
175113
this.hiddenTests = [];
@@ -201,11 +139,10 @@ export default class HtmlReporter {
201139
// potential internal errors when the HTML Reporter is disabled.
202140
this.listen = function () {
203141
this.listen = null;
204-
// Use prioritySignal for begin() to ensure the UI shows up
205-
// reliably to render errors from onError.
206-
// Without this, user-defined "QUnit.begin()" callbacks will end
207-
// up in the queue before ours, and if those throw an error,
208-
// then this handler will never run, thus leaving the page blank.
142+
// It's important that we're in the callback queue before any user-defined
143+
// "QUnit.begin()", because, if those may throw, ours wouldn't run and
144+
// the UI would remain blank or incomplete.
145+
// https://github.com/qunitjs/qunit/issues/1792
209146
QUnit.begin(this.onBegin.bind(this), prioritySymbol);
210147
// Use prioritySignal for testStart() to increase availability
211148
// of the HTML API for TESTID elements toward other event listeners.
@@ -256,7 +193,7 @@ export default class HtmlReporter {
256193
// Check if we can apply the change without a page refresh
257194
if (field.name === 'hidepassed' && 'replaceState' in window.history) {
258195
// Set either true or undefined, which will now take precedence over
259-
// the original urlParams in makeUrl()
196+
// the original QUnit.urlParams in makeUrl()
260197
this.hidepassed = value;
261198
const tests = this.elementTests;
262199

@@ -298,7 +235,7 @@ export default class HtmlReporter {
298235
* @return string
299236
*/
300237
makeUrl (linkParams) {
301-
const params = extend({}, urlParams);
238+
const params = extend({}, this.urlParams);
302239
if (this.hidepassed !== null) {
303240
params.hidepassed = this.hidepassed;
304241
}
@@ -609,13 +546,81 @@ export default class HtmlReporter {
609546
return moduleFilter;
610547
}
611548

549+
getUrlConfigHtml () {
550+
const urlConfig = this.config.urlConfig;
551+
let urlConfigHtml = '';
552+
553+
for (let i = 0; i < urlConfig.length; i++) {
554+
// Options can be either strings or objects with nonempty "id" properties
555+
let val = urlConfig[i];
556+
if (typeof val === 'string') {
557+
val = {
558+
id: val,
559+
label: val
560+
};
561+
}
562+
563+
// https://github.com/qunitjs/qunit/issues/1792
564+
const currentVal = this.config[val.id] !== undefined
565+
? this.config[val.id]
566+
: this.urlParams[val.id];
567+
568+
let escaped = escapeText(val.id);
569+
let escapedTooltip = escapeText(val.tooltip);
570+
571+
if (!val.value || typeof val.value === 'string') {
572+
urlConfigHtml += '<label for="qunit-urlconfig-' + escaped
573+
+ '" title="' + escapedTooltip + '"><input id="qunit-urlconfig-' + escaped
574+
+ '" name="' + escaped + '" type="checkbox"'
575+
+ (val.value ? ' value="' + escapeText(val.value) + '"' : '')
576+
+ (currentVal ? ' checked="checked"' : '')
577+
+ ' title="' + escapedTooltip + '" />' + escapeText(val.label) + '</label>';
578+
} else {
579+
let selection = false;
580+
urlConfigHtml += '<label for="qunit-urlconfig-' + escaped
581+
+ '" title="' + escapedTooltip + '">' + escapeText(val.label)
582+
+ ': <select id="qunit-urlconfig-' + escaped
583+
+ '" name="' + escaped + '" title="' + escapedTooltip + '"><option></option>';
584+
585+
if (Array.isArray(val.value)) {
586+
for (let j = 0; j < val.value.length; j++) {
587+
escaped = escapeText(val.value[j]);
588+
urlConfigHtml += '<option value="' + escaped + '"'
589+
+ (currentVal === val.value[j]
590+
? (selection = true) && ' selected="selected"'
591+
: '')
592+
+ '>' + escaped + '</option>';
593+
}
594+
} else {
595+
for (let j in val.value) {
596+
if (hasOwn.call(val.value, j)) {
597+
urlConfigHtml += '<option value="' + escapeText(j) + '"'
598+
+ (currentVal === j
599+
? (selection = true) && ' selected="selected"'
600+
: '')
601+
+ '>' + escapeText(val.value[j]) + '</option>';
602+
}
603+
}
604+
}
605+
if (currentVal && !selection) {
606+
escaped = escapeText(currentVal);
607+
urlConfigHtml += '<option value="' + escaped
608+
+ '" selected="selected" disabled="disabled">' + escaped + '</option>';
609+
}
610+
urlConfigHtml += '</select></label>';
611+
}
612+
}
613+
614+
return urlConfigHtml;
615+
}
616+
612617
appendToolbarControls (beginDetails) {
613618
const toolbarControls = this.element.querySelector('#qunit-testrunner-toolbar');
614619
if (toolbarControls) {
615620
const urlConfigContainer = document.createElement('span');
616621
urlConfigContainer.id = 'qunit-toolbar-urlconfig';
617622
urlConfigContainer.className = 'qunit-url-config';
618-
urlConfigContainer.innerHTML = getUrlConfigHtml(this.config);
623+
urlConfigContainer.innerHTML = this.getUrlConfigHtml();
619624
DOM.onEach(urlConfigContainer.getElementsByTagName('input'), 'change', this.onToolbarChanged.bind(this));
620625
DOM.onEach(urlConfigContainer.getElementsByTagName('select'), 'change', this.onToolbarChanged.bind(this));
621626

test/main/HtmlReporter.js

+2
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ QUnit.test('options [urlConfig]', function (assert) {
366366
var element = document.createElement('div');
367367
new QUnit.reporters.html(this.MockQUnit, {
368368
element: element,
369+
urlParams: {},
369370
config: {
370371
urlConfig: [
371372
'xid',
@@ -514,6 +515,7 @@ QUnit.test('overall escaping', function (assert) {
514515
var element = document.createElement('div');
515516
new QUnit.reporters.html(this.MockQUnit, {
516517
element: element,
518+
urlParams: {},
517519
config: {
518520
urlConfig: [
519521
{

test/reporter-urlparams.js

+22
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,26 @@ QUnit.module('urlParams module', function () {
7575

7676
assert.strictEqual(QUnit.config.collapse, true, 'conflicting key preserves default');
7777
});
78+
79+
QUnit.test.each('HtmlReporter integration checkbox', {
80+
notrycatch: ['notrycatch', true],
81+
custom: ['custom', true],
82+
customArray: ['customArray', true],
83+
altertitle: ['altertitle', true]
84+
}, function (assert, dataset) {
85+
var element = document.querySelector('#qunit');
86+
var node = element.querySelector('#qunit-urlconfig-' + dataset[0]);
87+
assert.strictEqual(node.nodeName.toUpperCase(), 'INPUT');
88+
assert.strictEqual(node.checked, dataset[1]);
89+
});
90+
91+
QUnit.test.each('HtmlReporter integration select', {
92+
customMenu: ['customMenu', 'b'],
93+
customMenuUnknown: ['customMenuUnknown', 'c']
94+
}, function (assert, dataset) {
95+
var element = document.querySelector('#qunit');
96+
var node = element.querySelector('#qunit-urlconfig-' + dataset[0]);
97+
assert.strictEqual(node.nodeName.toUpperCase(), 'SELECT');
98+
assert.strictEqual(node.value, dataset[1]);
99+
});
78100
});

0 commit comments

Comments
 (0)