Skip to content

Commit aef79ca

Browse files
[test optimization] Support for @fast-check/jest (#6632)
1 parent 52c8a4d commit aef79ca

File tree

6 files changed

+100
-10
lines changed

6 files changed

+100
-10
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use strict'
2+
3+
const { test, fc } = require('@fast-check/jest')
4+
5+
describe('fast check', () => {
6+
test.prop([fc.string(), fc.string(), fc.string()])('will not include seed', (a, b, c) => {
7+
expect([a, b, c]).toContain(b)
8+
})
9+
})
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict'
2+
3+
describe('fast check with seed', () => {
4+
it('should include seed (with seed=12)', () => {
5+
expect(1 + 2).toEqual(3)
6+
})
7+
})

integration-tests/jest/jest.spec.js

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ describe('jest CommonJS', () => {
9191
'@happy-dom/jest-environment',
9292
'office-addin-mock',
9393
'winston',
94-
'jest-image-snapshot'
94+
'jest-image-snapshot',
95+
'@fast-check/jest'
9596
], true)
9697
cwd = sandbox.folder
9798
startupTestFile = path.join(cwd, testFile)
@@ -4487,7 +4488,7 @@ describe('jest CommonJS', () => {
44874488
})
44884489
})
44894490

4490-
describe('winston mocking', () => {
4491+
context('winston mocking', () => {
44914492
it('should allow winston to be mocked and verify createLogger is called', async () => {
44924493
childProcess = exec(
44934494
runTestsWithCoverageCommand,
@@ -4505,4 +4506,58 @@ describe('jest CommonJS', () => {
45054506
assert.equal(code, 0, `Jest should pass but failed with code ${code}`)
45064507
})
45074508
})
4509+
4510+
context('fast-check', () => {
4511+
it('should remove seed from the test name if @fast-check/jest is used in the test', async () => {
4512+
const eventsPromise = receiver
4513+
.gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), payloads => {
4514+
const events = payloads.flatMap(({ payload }) => payload.events)
4515+
const tests = events.filter(event => event.type === 'test').map(event => event.content)
4516+
assert.equal(tests.length, 1)
4517+
assert.equal(tests[0].meta[TEST_NAME], 'fast check will not include seed')
4518+
})
4519+
4520+
childProcess = exec(
4521+
runTestsWithCoverageCommand,
4522+
{
4523+
cwd,
4524+
env: {
4525+
...getCiVisAgentlessConfig(receiver.port),
4526+
TESTS_TO_RUN: 'jest-fast-check/jest-fast-check',
4527+
}
4528+
}
4529+
)
4530+
4531+
await Promise.all([
4532+
once(childProcess, 'exit'),
4533+
eventsPromise
4534+
])
4535+
})
4536+
4537+
it('should not remove seed if @fast-check/jest is not used', async () => {
4538+
const eventsPromise = receiver
4539+
.gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), payloads => {
4540+
const events = payloads.flatMap(({ payload }) => payload.events)
4541+
const tests = events.filter(event => event.type === 'test').map(event => event.content)
4542+
assert.equal(tests.length, 1)
4543+
assert.equal(tests[0].meta[TEST_NAME], 'fast check with seed should include seed (with seed=12)')
4544+
})
4545+
4546+
childProcess = exec(
4547+
runTestsWithCoverageCommand,
4548+
{
4549+
cwd,
4550+
env: {
4551+
...getCiVisAgentlessConfig(receiver.port),
4552+
TESTS_TO_RUN: 'jest-fast-check/jest-no-fast-check',
4553+
}
4554+
}
4555+
)
4556+
4557+
await Promise.all([
4558+
once(childProcess, 'exit'),
4559+
eventsPromise
4560+
])
4561+
})
4562+
})
45084563
})

packages/datadog-instrumentations/src/jest.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ const attemptToFixRetriedTestsStatuses = new Map()
8989
const wrappedWorkers = new WeakSet()
9090
const testSuiteMockedFiles = new Map()
9191
const testsToBeRetried = new Set()
92+
const testSuiteAbsolutePathsWithFastCheck = new Set()
9293

9394
const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
9495

@@ -304,9 +305,13 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
304305
}
305306
}
306307

308+
getShouldStripSeedFromTestName () {
309+
return testSuiteAbsolutePathsWithFastCheck.has(this.testSuiteAbsolutePath)
310+
}
311+
307312
// At the `add_test` event we don't have the test object yet, so we can't use it
308313
getTestNameFromAddTestEvent (event, state) {
309-
const describeSuffix = getJestTestName(state.currentDescribeBlock)
314+
const describeSuffix = getJestTestName(state.currentDescribeBlock, this.getShouldStripSeedFromTestName())
310315
return describeSuffix ? `${describeSuffix} ${event.testName}` : event.testName
311316
}
312317

@@ -329,7 +334,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
329334
})
330335
}
331336
if (event.name === 'test_start') {
332-
const testName = getJestTestName(event.test)
337+
const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
333338
if (testsToBeRetried.has(testName)) {
334339
// This is needed because we're trying tests with the same name
335340
this.resetSnapshotState()
@@ -491,7 +496,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
491496
let failedAllTests = false
492497
let isAttemptToFix = false
493498
if (this.isTestManagementTestsEnabled) {
494-
const testName = getJestTestName(event.test)
499+
const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
495500
isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(testName)
496501
if (isAttemptToFix) {
497502
if (attemptToFixRetriedTestsStatuses.has(testName)) {
@@ -519,7 +524,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
519524
let isEfdRetry = false
520525
// We'll store the test statuses of the retries
521526
if (this.isKnownTestsEnabled) {
522-
const testName = getJestTestName(event.test)
527+
const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
523528
const isNewTest = retriedTestsToNumAttempts.has(testName)
524529
if (isNewTest) {
525530
if (newTestsTestStatuses.has(testName)) {
@@ -579,16 +584,17 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
579584
}
580585
}
581586
if (event.name === 'test_skip' || event.name === 'test_todo') {
587+
const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
582588
testSkippedCh.publish({
583589
test: {
584-
name: getJestTestName(event.test),
590+
name: testName,
585591
suite: this.testSuite,
586592
testSourceFile: this.testSourceFile,
587593
displayName: this.displayName,
588594
frameworkVersion: jestVersion,
589595
testStartLine: getTestLineStart(event.test.asyncError, this.testSuite)
590596
},
591-
isDisabled: this.testManagementTestsForThisSuite?.disabled?.includes(getJestTestName(event.test))
597+
isDisabled: this.testManagementTestsForThisSuite?.disabled?.includes(testName)
592598
})
593599
}
594600
}
@@ -1300,6 +1306,10 @@ addHook({
13001306
// To bypass jest's own require engine
13011307
return this._requireCoreModule(moduleName)
13021308
}
1309+
// This means that `@fast-check/jest` is used in the test file.
1310+
if (moduleName === '@fast-check/jest') {
1311+
testSuiteAbsolutePathsWithFastCheck.add(this._testPath)
1312+
}
13031313
return requireModuleOrMock.apply(this, arguments)
13041314
})
13051315

packages/datadog-plugin-jest/src/util.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,24 @@ function getFormattedJestTestParameters (testParameters) {
4141
return formattedParameters
4242
}
4343

44+
// Support for `@fast-check/jest`: this library modifies the test name to include the seed
45+
// A test name that keeps changing breaks some Test Optimization's features.
46+
const SEED_SUFFIX_RE = /\s*\(with seed=-?\d+\)\s*$/i
4447
// https://github.com/facebook/jest/blob/3e38157ad5f23fb7d24669d24fae8ded06a7ab75/packages/jest-circus/src/utils.ts#L396
45-
function getJestTestName (test) {
48+
function getJestTestName (test, shouldStripSeed = false) {
4649
const titles = []
4750
let parent = test
4851
do {
4952
titles.unshift(parent.name)
5053
} while ((parent = parent.parent))
5154

5255
titles.shift() // remove TOP_DESCRIBE_BLOCK_NAME
53-
return titles.join(' ')
56+
57+
const testName = titles.join(' ')
58+
if (shouldStripSeed) {
59+
return testName.replace(SEED_SUFFIX_RE, '')
60+
}
61+
return testName
5462
}
5563

5664
function isMarkedAsUnskippable (test) {

packages/dd-trace/test/plugins/versions/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"@elastic/transport": "9.1.2",
3131
"@fastify/cookie": "11.0.2",
3232
"@fastify/multipart": "9.2.1",
33+
"@fast-check/jest": "2.1.1",
3334
"@google-cloud/pubsub": "5.2.0",
3435
"@google-cloud/vertexai": "1.10.0",
3536
"@graphql-tools/executor": "1.4.9",

0 commit comments

Comments
 (0)