Skip to content

Commit

Permalink
✨(jest) Take seed coming from Jest whenever provided (#3332)
Browse files Browse the repository at this point in the history
* ✨(jest) Take seed coming from Jest whenever provided

Fixes #3287

* versions

* Update packages/jest/src/jest-fast-check.ts
  • Loading branch information
dubzzz committed Oct 19, 2022
1 parent 3c9b5e5 commit 94c4d30
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 10 deletions.
2 changes: 2 additions & 0 deletions .yarn/versions/ce9dc069.yml
@@ -0,0 +1,2 @@
releases:
"@fast-check/jest": minor
11 changes: 9 additions & 2 deletions 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;
Expand Down Expand Up @@ -29,7 +29,14 @@ function internalTestPropExecute<Ts extends [any] | any[]>(
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);
}
}
}

Expand Down
55 changes: 47 additions & 8 deletions packages/jest/test/jest-fast-check.spec.ts
Expand Up @@ -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\)/);
});

Expand All @@ -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
Expand Down Expand Up @@ -271,11 +286,17 @@ async function writeToFile(
return { specFileName, jestConfigRelativePath };
}

async function runSpec(jestConfigRelativePath: string): Promise<string> {
async function runSpec(jestConfigRelativePath: string, opts: { jestSeed?: number } = {}): Promise<string> {
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;
Expand All @@ -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]'));
}

0 comments on commit 94c4d30

Please sign in to comment.