-
Notifications
You must be signed in to change notification settings - Fork 781
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Core: Expose QUnit.urlParams, fix preconfig conflict, set initial exp…
…licitly * Always expose QUnit.urlParams Previously this API was left undefined in non-browser environments. To make usage easier and safer in environment-agnostic code, export the object always, with it remaining an empty object if globalThis.window.location is undefined. * Fix broken preconfig for built-in urlConfig options. When the preconfig feature was first introduced in QUnit 2.1.0 (2016), it did not work for certain config keys. In urlparams.js we unconditionally re-assign some keys. Thus, even if preconfig sets one of these, and QUnit.urlParams does not, we'd effectively set it to undefined, wiping away the preconfig value. - QUnit.config.moduleId - QUnit.config.testId - QUnit.config.module - QUnit.config.filter * Fix missing "unknown" option in custom menu defined by urlConfig. `let selection = false;` was scoped incorrectly in the urlConfig loop in html.js, causing it to only work for the first custom menu. When registering two or more custom menus, any non-first menu with an unknown value would be missing `<option selected disabled>c</option>` in the dropdown menu. * Apply type validation for built-in curlConfig options. This fixes cases where, via urlParams, one could set a core config key to an invalid value (wrong type): - ?hidepassed=true (was string "true", now bool true) Previously, hidepassed=false was treated as truthy, which will now be treated as false/off. Invalid values like ?hidepassed=x will now also be treated as false. Since hidepassed=1 is not unheard of in the wild, this is now a supported boolean value. - ?filter=one&filter=two (was array, now undefined) Previously this would inject an invalid value, causing a runtime TypeError due to internal code calling String methods on it. * Set the initial default values of `hidepassed`, `noglobals`, and `notrycatch` explicitly in config.js, instead of leaving them as implicitly undefined when not turned on. Especially because "noglboals" and "notrycatch" are valid in CLI as well.
- Loading branch information
Showing
15 changed files
with
246 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,96 +1,30 @@ | ||
import QUnit from '../core'; | ||
import { window } from '../globals'; | ||
|
||
(function () { | ||
// Only interact with URLs via window.location | ||
const location = typeof window !== 'undefined' && window.location; | ||
if (!location) { | ||
return; | ||
} | ||
|
||
const urlParams = getUrlParams(); | ||
|
||
// TODO: Move to /src/core/ in QUnit 3 | ||
// TODO: Document this as public API (read-only) | ||
QUnit.urlParams = urlParams; | ||
|
||
// TODO: Move to /src/core/config.js in QUnit 3, | ||
// in accordance with /docs/api/config.index.md#order | ||
QUnit.config.filter = urlParams.filter; | ||
QUnit.config.module = urlParams.module; | ||
QUnit.config.moduleId = [].concat(urlParams.moduleId || []); | ||
QUnit.config.testId = [].concat(urlParams.testId || []); | ||
|
||
// Test order randomization | ||
if (urlParams.seed === true) { | ||
// Generate a random seed if the option is specified without a value | ||
QUnit.config.seed = Math.random().toString(36).slice(2); | ||
} else if (urlParams.seed) { | ||
QUnit.config.seed = urlParams.seed; | ||
} | ||
|
||
// Add URL-parameter-mapped config values with UI form rendering data | ||
QUnit.config.urlConfig.push( | ||
{ | ||
id: 'hidepassed', | ||
label: 'Hide passed tests', | ||
tooltip: 'Only show tests and assertions that fail. Stored as query-strings.' | ||
}, | ||
{ | ||
id: 'noglobals', | ||
label: 'Check for Globals', | ||
tooltip: 'Enabling this will test if any test introduces new properties on the ' + | ||
'global object (`window` in Browsers). Stored as query-strings.' | ||
}, | ||
{ | ||
id: 'notrycatch', | ||
label: 'No try-catch', | ||
tooltip: 'Enabling this will run tests outside of a try-catch block. Makes debugging ' + | ||
'exceptions in IE reasonable. Stored as query-strings.' | ||
} | ||
); | ||
|
||
QUnit.begin(function () { | ||
const urlConfig = QUnit.config.urlConfig; | ||
const hasOwn = Object.prototype.hasOwnProperty; | ||
|
||
for (let i = 0; i < urlConfig.length; i++) { | ||
// Options can be either strings or objects with nonempty "id" properties | ||
let option = QUnit.config.urlConfig[i]; | ||
if (typeof option !== 'string') { | ||
option = option.id; | ||
} | ||
// Wait until QUnit.begin() so that users can add their keys to urlConfig | ||
// any time during test loading, including during `QUnit.on('runStart')`. | ||
QUnit.begin(function () { | ||
const urlConfig = QUnit.config.urlConfig; | ||
|
||
if (QUnit.config[option] === undefined) { | ||
QUnit.config[option] = urlParams[option]; | ||
} | ||
for (let i = 0; i < urlConfig.length; i++) { | ||
// Options can be either strings or objects with nonempty "id" properties | ||
let option = QUnit.config.urlConfig[i]; | ||
if (typeof option !== 'string') { | ||
option = option.id; | ||
} | ||
}); | ||
|
||
function getUrlParams () { | ||
const urlParams = Object.create(null); | ||
const params = location.search.slice(1).split('&'); | ||
const length = params.length; | ||
|
||
for (let i = 0; i < length; i++) { | ||
if (params[i]) { | ||
const param = params[i].split('='); | ||
const name = decodeQueryParam(param[0]); | ||
|
||
// Allow just a key to turn on a flag, e.g., test.html?noglobals | ||
const value = param.length === 1 || | ||
decodeQueryParam(param.slice(1).join('=')); | ||
if (name in urlParams) { | ||
urlParams[name] = [].concat(urlParams[name], value); | ||
} else { | ||
urlParams[name] = value; | ||
} | ||
} | ||
// only create new property for user-defined QUnit.config.urlConfig keys | ||
// that don't conflict with a built-in QUnit.config option or are otherwise | ||
// already set. This prevents internal TypeError from bad urls where keys | ||
// could otherwise unexpectedly be set to type string or array. | ||
// | ||
// Given that HTML Reporter renders checkboxes based on QUnit.config | ||
// instead of QUnit.urlParams, this also helps make sure that checkboxes | ||
// for built-in keys are correctly shown as off if a urlParams value exists | ||
// but was invalid and discarded by config.js. | ||
if (!hasOwn.call(QUnit.config, option)) { | ||
QUnit.config[option] = QUnit.urlParams[option]; | ||
} | ||
|
||
return urlParams; | ||
} | ||
|
||
function decodeQueryParam (param) { | ||
return decodeURIComponent(param.replace(/\+/g, '%20')); | ||
} | ||
}()); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { window } from './globals'; | ||
|
||
function getUrlParams () { | ||
const urlParams = Object.create(null); | ||
// Only interact with URLs via window.location | ||
const location = typeof window !== 'undefined' && window.location; | ||
// Silently skip in non-browser environment | ||
if (location) { | ||
const params = location.search.slice(1).split('&'); | ||
const length = params.length; | ||
|
||
for (let i = 0; i < length; i++) { | ||
if (params[i]) { | ||
const param = params[i].split('='); | ||
const name = decodeQueryParam(param[0]); | ||
|
||
// Allow just a key to turn on a flag, e.g., test.html?noglobals | ||
const value = param.length === 1 || | ||
decodeQueryParam(param.slice(1).join('=')); | ||
if (name in urlParams) { | ||
urlParams[name] = [].concat(urlParams[name], value); | ||
} else { | ||
urlParams[name] = value; | ||
} | ||
} | ||
} | ||
} | ||
|
||
return urlParams; | ||
} | ||
|
||
function decodeQueryParam (param) { | ||
return decodeURIComponent(param.replace(/\+/g, '%20')); | ||
} | ||
|
||
export const urlParams = getUrlParams(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.