From 390a5b76e17c580e68034cecc1386845905d543e Mon Sep 17 00:00:00 2001 From: Timo Tijhof Date: Sun, 15 Sep 2024 17:23:16 -0700 Subject: [PATCH] CLI: Add `--seed=true` to generate a new seed Fixes https://github.com/qunitjs/qunit/issues/1691. --- bin/qunit.js | 6 +++--- docs/api/config/seed.md | 2 +- src/cli/run.js | 22 ++++++++-------------- src/core/config.js | 21 ++++++++++++++------- test/cli/cli-main.js | 8 ++++---- 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/bin/qunit.js b/bin/qunit.js index d7ba593fa..d1a01145c 100755 --- a/bin/qunit.js +++ b/bin/qunit.js @@ -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 ', + '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); diff --git a/docs/api/config/seed.md b/docs/api/config/seed.md index 0221c2511..f0cdf2871 100644 --- a/docs/api/config/seed.md +++ b/docs/api/config/seed.md @@ -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 diff --git a/src/cli/run.js b/src/cli/run.js index 265d87300..752bdd6fc 100644 --- a/src/cli/run.js +++ b/src/cli/run.js @@ -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}`); } diff --git a/src/core/config.js b/src/core/config.js index 45d747e2c..99bcc9a28 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -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]; @@ -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'); @@ -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; diff --git a/test/cli/cli-main.js b/test/cli/cli-main.js index 07a7dc844..6475bef7c 100644 --- a/test/cli/cli-main.js +++ b/test/cli/cli-main.js @@ -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 --- @@ -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