Skip to content

Commit

Permalink
CLI: Add --seed=true to generate a new seed
Browse files Browse the repository at this point in the history
Fixes #1691.
  • Loading branch information
Krinkle authored Sep 29, 2024
1 parent d37b877 commit a0027db
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 29 deletions.
6 changes: 3 additions & 3 deletions bin/qunit.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ program
collect,
[]
)
.option('--seed [value]',
'specify a seed to re-order your tests; '
+ 'if specified without a value, a seed will be generated'
.option('--seed <value>',
'specify a seed to enable randomized ordering of tests.\n'
+ 'set to "true" to generate a new seed.'
)
.option('-w, --watch', 'watch files for changes and re-run the test suite')
.parse(process.argv);
Expand Down
2 changes: 1 addition & 1 deletion docs/api/config/seed.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The provided string will be used as the seed in a pseudo-random number generator

Randomly ordering your tests can help identify non-atomic tests which either depend on a previous test or are leaking state to subsequent tests.

If `seed` is boolean true (or set as URL query parameter without a value), then QUnit will generate on-demand a new random value to use as seed. You can then read the seed at runtime from the configuration value, and use it to reproduce the same test sequence later.
If `seed` is boolean true (or set as URL query parameter without a value), then QUnit will generate on-demand a new random value to use as seed. You can then read the seed from `QUnit.config.seed` at runtime, and use it to reproduce the same test sequence later.

## See also

Expand Down
22 changes: 8 additions & 14 deletions src/cli/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,20 @@ async function run (args, options) {

const files = utils.getFilesFromArgs(args);

// Replace any previous instance, e.g. in watch mode
QUnit = globalThis.QUnit = requireQUnit();

if (options.filter) {
QUnit.config.filter = options.filter;
globalThis.qunit_config_filter = options.filter;
}

if (options.module) {
QUnit.config.module = options.module;
globalThis.qunit_config_module = options.module;
}
if (options.seed) {
globalThis.qunit_config_seed = options.seed;
}

const seed = options.seed;
if (seed) {
if (seed === true) {
// Length may vary from 6-14, pad to 10
QUnit.config.seed = Math.random().toString(36).slice(2).padEnd(10, '0');
} else {
QUnit.config.seed = seed;
}
// Replace any previous instance, e.g. in watch mode
QUnit = globalThis.QUnit = requireQUnit();

if (QUnit.config.seed) {
console.log(`Running tests with seed: ${QUnit.config.seed}`);
}

Expand Down
21 changes: 14 additions & 7 deletions src/core/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ function readFlatPreconfigString (val, dest) {
}
}

function readFlatPreconfigStringOrBoolean (val, dest) {
if (typeof val === 'boolean' || (typeof val === 'string' && val !== '')) {
config[dest] = val;
}
}

function readFlatPreconfigStringArray (val, dest) {
if (typeof val === 'string' && val !== '') {
config[dest] = [val];
Expand All @@ -190,7 +196,7 @@ function readFlatPreconfig (obj) {
readFlatPreconfigBoolean(obj.qunit_config_reorder, 'reorder');
readFlatPreconfigBoolean(obj.qunit_config_requireexpects, 'requireExpects');
readFlatPreconfigBoolean(obj.qunit_config_scrolltop, 'scrolltop');
readFlatPreconfigString(obj.qunit_config_seed, 'seed');
readFlatPreconfigStringOrBoolean(obj.qunit_config_seed, 'seed');
readFlatPreconfigStringArray(obj.qunit_config_testid, 'testId');
readFlatPreconfigNumber(obj.qunit_config_testtimeout, 'testTimeout');

Expand Down Expand Up @@ -238,12 +244,13 @@ if (urlParams.testId) {
readFlatPreconfigBoolean(urlParams.hidepassed, 'hidepassed');
readFlatPreconfigBoolean(urlParams.noglobals, 'noglobals');
readFlatPreconfigBoolean(urlParams.notrycatch, 'notrycatch');
if (urlParams.seed === true) {
// Generate a random seed if the option is specified without a value
readFlatPreconfigStringOrBoolean(urlParams.seed, 'seed');

if (config.seed === 'true' || config.seed === true) {
// Generate a random seed
// Length of `Math.random()` fraction, in base 36, may vary from 6-14.
// Pad and take slice to a consistent 10-digit value.
// TODO: Present this in HtmlReporter
config.seed = Math.random().toString(36).slice(2);
} else {
readFlatPreconfigString(urlParams.seed, 'seed');
config.seed = (Math.random().toString(36) + '0000000000').slice(2, 12);
}

export default config;
8 changes: 4 additions & 4 deletions test/cli/cli-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ QUnit.module('CLI Main', () => {
qunit_config_notrycatch: 'false'
}
});
assert.equal(execution.snapshot, `TAP version 13
assert.equal(execution.snapshot, `Running tests with seed: dummyfirstyes
TAP version 13
ok 1 dummy
not ok 2 slow
---
Expand Down Expand Up @@ -213,9 +214,8 @@ Bail out! Error: No tests matched the filter "no matches".
# exit code: 1`);
});

QUnit.test('--seed generates new random seed', async assert => {
// https://github.com/qunitjs/qunit/issues/1691
const command = ['qunit', '--seed', '--', 'basic-one.js', 'test/'];
QUnit.test('--seed=true generates new random seed', async assert => {
const command = ['qunit', '--seed', 'true', 'basic-one.js', 'test/'];
const execution = await execute(command);

const actualHarness = execution.snapshot
Expand Down

0 comments on commit a0027db

Please sign in to comment.