From 94c4d30c7df1ec062fee09719d38eafd95decf17 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Wed, 19 Oct 2022 21:53:02 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(jest)=20Take=20seed=20coming=20from?= =?UTF-8?q?=20Jest=20whenever=20provided=20(#3332)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨(jest) Take seed coming from Jest whenever provided Fixes #3287 * versions * Update packages/jest/src/jest-fast-check.ts --- .yarn/versions/ce9dc069.yml | 2 + packages/jest/src/jest-fast-check.ts | 11 ++++- packages/jest/test/jest-fast-check.spec.ts | 55 ++++++++++++++++++---- 3 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 .yarn/versions/ce9dc069.yml diff --git a/.yarn/versions/ce9dc069.yml b/.yarn/versions/ce9dc069.yml new file mode 100644 index 00000000000..24da0f62700 --- /dev/null +++ b/.yarn/versions/ce9dc069.yml @@ -0,0 +1,2 @@ +releases: + "@fast-check/jest": minor diff --git a/packages/jest/src/jest-fast-check.ts b/packages/jest/src/jest-fast-check.ts index ae0f1595a7c..0f1d5f80807 100644 --- a/packages/jest/src/jest-fast-check.ts +++ b/packages/jest/src/jest-fast-check.ts @@ -1,4 +1,4 @@ -import { it, test } from '@jest/globals'; +import { it, test, jest } from '@jest/globals'; import * as fc from 'fast-check'; type It = typeof it; @@ -29,7 +29,14 @@ function internalTestPropExecute( if (seedFromGlobals !== undefined) { customParams.seed = seedFromGlobals; } else { - customParams.seed = Date.now() ^ (Math.random() * 0x100000000); + // This option is only available since v29.2.0 of Jest + // See official release note: https://github.com/facebook/jest/releases/tag/v29.2.0 + const seedFromJest = typeof jest.getSeed === 'function' ? jest.getSeed() : undefined; + if (seedFromJest !== undefined) { + customParams.seed = seedFromJest; + } else { + customParams.seed = Date.now() ^ (Math.random() * 0x100000000); + } } } diff --git a/packages/jest/test/jest-fast-check.spec.ts b/packages/jest/test/jest-fast-check.spec.ts index bd640c139fc..a838062c11f 100644 --- a/packages/jest/test/jest-fast-check.spec.ts +++ b/packages/jest/test/jest-fast-check.spec.ts @@ -105,7 +105,7 @@ describe.each<{ runner: RunnerType }>([{ runner: 'testProp' }, { runner: 'itProp // Assert expectFail(out, specFileName); - expectAlignedSeeds(out); + expectAlignedSeeds(out, { noAlignWithJest: true }); expect(out).toMatch(/[×✕] property fail with locally requested seed \(with seed=4242\)/); }); @@ -121,10 +121,25 @@ describe.each<{ runner: RunnerType }>([{ runner: 'testProp' }, { runner: 'itProp // Assert expectFail(out, specFileName); - expectAlignedSeeds(out); + expectAlignedSeeds(out, { noAlignWithJest: true }); expect(out).toMatch(/[×✕] property fail with globally requested seed \(with seed=4848\)/); }); + it.concurrent('should fail with seed requested at jest level', async () => { + // Arrange + const { specFileName, jestConfigRelativePath } = await writeToFile(runner, () => { + runnerProp('property fail with globally requested seed', [fc.constant(null)], (_unused) => false); + }); + + // Act + const out = await runSpec(jestConfigRelativePath, { jestSeed: 6969 }); + + // Assert + expectFail(out, specFileName); + expectAlignedSeeds(out); + expect(out).toMatch(/[×✕] property fail with globally requested seed \(with seed=6969\)/); + }); + describe('.skip', () => { it.concurrent('should never be executed', async () => { // Arrange @@ -271,11 +286,17 @@ async function writeToFile( return { specFileName, jestConfigRelativePath }; } -async function runSpec(jestConfigRelativePath: string): Promise { +async function runSpec(jestConfigRelativePath: string, opts: { jestSeed?: number } = {}): Promise { const { stdout: jestBinaryPathCommand } = await execFile('yarn', ['bin', 'jest'], { shell: true }); const jestBinaryPath = jestBinaryPathCommand.split('\n')[0]; try { - const { stderr: specOutput } = await execFile('node', [jestBinaryPath, '--config', jestConfigRelativePath]); + const { stderr: specOutput } = await execFile('node', [ + jestBinaryPath, + '--config', + jestConfigRelativePath, + '--show-seed', + ...(opts.jestSeed !== undefined ? ['--seed', String(opts.jestSeed)] : []), + ]); return specOutput; } catch (err) { return (err as any).stderr; @@ -290,8 +311,26 @@ function expectFail(out: string, specFileName: string): void { expect(out).toMatch(new RegExp('FAIL .*/' + specFileName)); } -function expectAlignedSeeds(out: string): void { - expect(out).toMatch(/[×✕] .* \(with seed=-?\d+\)/); - const receivedSeed = out.split('seed=')[1].split(')')[0]; - expect(out).toMatch(new RegExp('seed\\s*:\\s*' + receivedSeed + '[^\\d]')); +function expectAlignedSeeds(out: string, opts: { noAlignWithJest?: boolean } = {}): void { + // Seed printed by jest has the shape: + // > Seed: -518086725 + // > Test Suites: 1 failed, 1 total + // > Tests: 1 failed, 1 total + // > Snapshots: 0 total + // > Time: 0.952 s + // > Ran all test suites + const JestSeedMatcher = /Seed:\s+(-?\d+)/; + expect(out).toMatch(JestSeedMatcher); + const jestSeed = JestSeedMatcher.exec(out)![1]; + // Seed printed by jest-fast-check next to test name has the shape: + // > × property fail on falsy property (with seed=-518086725) + const JestFastCheckSeedMatcher = opts.noAlignWithJest + ? /[×✕] .* \(with seed=(-?\d+)\)/ + : new RegExp('[×✕] .* \\(with seed=(' + jestSeed + ')\\)'); + expect(out).toMatch(JestFastCheckSeedMatcher); + const jestFastCheckSeed = JestFastCheckSeedMatcher.exec(out)![1]; + // Seed printed by fast-check in case of failure has the shape: + // > Property failed after 1 tests + // > { seed: -518086725, path: \"0\", endOnFailure: true } + expect(out).toMatch(new RegExp('\\{[^}]*seed\\s*:\\s*' + jestFastCheckSeed + '[^\\d]')); }