diff --git a/bin/qunit.js b/bin/qunit.js index 500594fcb..fdfdeb2b8 100755 --- a/bin/qunit.js +++ b/bin/qunit.js @@ -31,8 +31,8 @@ program 'reporters will be displayed') .option('--require ', 'specify a module or script to include before running ' + 'any tests.', collect, []) - .option('--seed [value]', 'specify a seed to re-order your tests; ' + - 'if specified without a value, a seed will be generated') + .option('--seed ', 'specify a seed to re-order your tests; ' + + 'set to "true" to generate a new seed') .option('-w, --watch', 'watch files for changes and re-run the test suite') .parse(process.argv); diff --git a/src/cli/run.js b/src/cli/run.js index 52587712b..77698d283 100644 --- a/src/cli/run.js +++ b/src/cli/run.js @@ -55,12 +55,15 @@ async function run (args, options) { const seed = options.seed; if (seed) { - if (seed === true) { - QUnit.config.seed = Math.random().toString(36).slice(2); + if (seed === 'true' || seed === true) { + // NOTE: QUnit 3 will set preconfig qunit_config_seed from here. + // NOTE: This duplicates logic from /src/core/config.js. Consolidated in QUnit 3. + QUnit.config.seed = (Math.random().toString(36) + '0000000000').slice(2, 12); } else { QUnit.config.seed = seed; } - + } + if (QUnit.config.seed) { console.log(`Running tests with seed: ${QUnit.config.seed}`); } diff --git a/src/core/config.js b/src/core/config.js index c1f1fb4ae..7ee1b225b 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -156,6 +156,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]; @@ -178,7 +184,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'); } @@ -200,4 +206,10 @@ if (preConfig) { // Push a loose unnamed module to the modules collection config.modules.push(config.currentModule); +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. + config.seed = (Math.random().toString(36) + '0000000000').slice(2, 12); +} export default config; diff --git a/src/html-runner/urlparams.js b/src/html-runner/urlparams.js index 8ee74cad1..feb645008 100644 --- a/src/html-runner/urlparams.js +++ b/src/html-runner/urlparams.js @@ -26,9 +26,11 @@ import { window } from '../globals'; 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); + // Generate a random seed if `?seed` is specified without a value (boolean true), + // or when set to the string "true". + if (urlParams.seed === 'true' || urlParams.seed === true) { + // NOTE: This duplicates logic from /src/core/config.js. Consolidated in QUnit 3. + QUnit.config.seed = (Math.random().toString(36) + '0000000000').slice(2, 12); } else if (urlParams.seed) { QUnit.config.seed = urlParams.seed; } diff --git a/test/cli/cli-main.js b/test/cli/cli-main.js index ddb9264b4..43d1f4afe 100644 --- a/test/cli/cli-main.js +++ b/test/cli/cli-main.js @@ -31,7 +31,8 @@ QUnit.module('CLI Main', () => { qunit_config_testtimeout: '7' } }); - 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 --- @@ -367,6 +368,38 @@ not ok 1 global failure # exit code: 1`); }); + 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 + .replace(/^(Running tests with seed: )([a-z0-9]{10,20})/g, (_m, m1) => { + return m1 + '0000000000'; + }) + .split('\n') + .filter(line => !line.startsWith('ok ')) + .join('\n'); + + const actualResults = execution.snapshot + .replace(/^ok \d/gm, 'ok 0') + .split('\n') + .filter(line => line.startsWith('ok ')) + .sort() + .join('\n'); + + assert.equal(actualHarness, `Running tests with seed: 0000000000 +TAP version 13 +1..3 +# pass 3 +# skip 0 +# todo 0 +# fail 0`); + + assert.equal(actualResults, `ok 0 First > 1 +ok 0 Second > 1 +ok 0 Single > has a test`); + }); + QUnit.test('--require loads unknown module', async assert => { const command = ['qunit', 'basic-one.js', '--require', 'does-not-exist-at-all']; const execution = await execute(command);