Skip to content

Commit bcf2f1d

Browse files
[test optimization] normalize seed suffix in test names in jest (#8587)
1 parent 600610b commit bcf2f1d

10 files changed

Lines changed: 155 additions & 89 deletions

File tree

integration-tests/ci-visibility/jest-fast-check/jest-fast-check.js

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use strict'
2+
3+
const assert = require('assert')
4+
5+
describe('seed suffix (with seed=12)', () => {
6+
it('should preserve describe seed suffix', () => {
7+
assert.deepStrictEqual(1 + 2, 3)
8+
})
9+
})

integration-tests/ci-visibility/jest-fast-check/jest-no-fast-check.js renamed to integration-tests/ci-visibility/jest-seed-suffix/jest-seed-suffix.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
const assert = require('assert')
44

5-
describe('fast check with seed', () => {
6-
it('should include seed (with seed=12)', () => {
5+
describe('seed suffix', () => {
6+
it('should strip seed (with seed=12)', () => {
77
assert.deepStrictEqual(1 + 2, 3)
88
})
99
})

integration-tests/jest/jest.core.spec.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ describe(`jest@${JEST_VERSION} commonJS`, () => {
8989
'office-addin-mock',
9090
'winston',
9191
'jest-image-snapshot',
92-
'@fast-check/jest',
9392
].filter(Boolean), true)
9493

9594
before(function () {

integration-tests/jest/jest.itr-efd.spec.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ describe(`jest@${JEST_VERSION} commonJS`, () => {
9494
'office-addin-mock',
9595
'winston',
9696
'jest-image-snapshot',
97-
'@fast-check/jest',
9897
].filter(Boolean), true)
9998

10099
before(function () {

integration-tests/jest/jest.test-management.spec.js

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ describe(`jest@${JEST_VERSION} commonJS`, () => {
7979
'office-addin-mock',
8080
'winston',
8181
'jest-image-snapshot',
82-
'@fast-check/jest',
8382
].filter(Boolean), true)
8483

8584
before(function () {
@@ -2552,14 +2551,14 @@ describe(`jest@${JEST_VERSION} commonJS`, () => {
25522551
})
25532552
})
25542553

2555-
context('fast-check', () => {
2556-
onlyLatestIt('should remove seed from the test name if @fast-check/jest is used in the test', async () => {
2554+
context('seed suffix normalization', () => {
2555+
onlyLatestIt('should remove seed suffix from reported test names', async () => {
25572556
const eventsPromise = receiver
25582557
.gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), payloads => {
25592558
const events = payloads.flatMap(({ payload }) => payload.events)
25602559
const tests = events.filter(event => event.type === 'test').map(event => event.content)
25612560
assert.strictEqual(tests.length, 1)
2562-
assert.strictEqual(tests[0].meta[TEST_NAME], 'fast check will not include seed')
2561+
assert.strictEqual(tests[0].meta[TEST_NAME], 'seed suffix should strip seed')
25632562
})
25642563

25652564
childProcess = exec(
@@ -2568,7 +2567,7 @@ describe(`jest@${JEST_VERSION} commonJS`, () => {
25682567
cwd,
25692568
env: {
25702569
...getCiVisAgentlessConfig(receiver.port),
2571-
TESTS_TO_RUN: 'jest-fast-check/jest-fast-check',
2570+
TESTS_TO_RUN: 'jest-seed-suffix/jest-seed-suffix',
25722571
},
25732572
}
25742573
)
@@ -2579,13 +2578,92 @@ describe(`jest@${JEST_VERSION} commonJS`, () => {
25792578
])
25802579
})
25812580

2582-
onlyLatestIt('should not remove seed if @fast-check/jest is not used', async () => {
2581+
onlyLatestIt('does not mark seed-suffixed tests as new when known tests use the stripped name', async () => {
2582+
receiver.setKnownTests({
2583+
jest: {
2584+
'ci-visibility/jest-seed-suffix/jest-seed-suffix.js': [
2585+
'seed suffix should strip seed',
2586+
],
2587+
},
2588+
})
2589+
receiver.setSettings({
2590+
early_flake_detection: {
2591+
enabled: true,
2592+
slow_test_retries: {
2593+
'5s': 2,
2594+
},
2595+
faulty_session_threshold: 100,
2596+
},
2597+
known_tests_enabled: true,
2598+
})
2599+
25832600
const eventsPromise = receiver
25842601
.gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), payloads => {
25852602
const events = payloads.flatMap(({ payload }) => payload.events)
25862603
const tests = events.filter(event => event.type === 'test').map(event => event.content)
25872604
assert.strictEqual(tests.length, 1)
2588-
assert.strictEqual(tests[0].meta[TEST_NAME], 'fast check with seed should include seed (with seed=12)')
2605+
assert.strictEqual(tests[0].meta[TEST_NAME], 'seed suffix should strip seed')
2606+
assert.ok(!(TEST_IS_NEW in tests[0].meta))
2607+
})
2608+
2609+
childProcess = exec(
2610+
runTestsCommand,
2611+
{
2612+
cwd,
2613+
env: {
2614+
...getCiVisAgentlessConfig(receiver.port),
2615+
TESTS_TO_RUN: 'jest-seed-suffix/jest-seed-suffix',
2616+
},
2617+
}
2618+
)
2619+
2620+
await Promise.all([
2621+
once(childProcess, 'exit'),
2622+
eventsPromise,
2623+
])
2624+
})
2625+
2626+
onlyLatestIt('keeps seed-like describe suffixes when matching test management tests', async () => {
2627+
const testName = 'seed suffix (with seed=12) should preserve describe seed suffix'
2628+
receiver.setSettings({ test_management: { enabled: true, attempt_to_fix_retries: 2 } })
2629+
receiver.setTestManagementTests({
2630+
jest: {
2631+
suites: {
2632+
'ci-visibility/jest-seed-suffix/jest-describe-seed-suffix.js': {
2633+
tests: {
2634+
[testName]: {
2635+
properties: {
2636+
attempt_to_fix: true,
2637+
},
2638+
},
2639+
},
2640+
},
2641+
},
2642+
},
2643+
})
2644+
2645+
const eventsPromise = receiver
2646+
.gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), payloads => {
2647+
const events = payloads.flatMap(({ payload }) => payload.events)
2648+
const tests = events.filter(event => event.type === 'test').map(event => event.content)
2649+
const retriedTests = tests.filter(test => test.meta[TEST_NAME] === testName)
2650+
2651+
assert.strictEqual(retriedTests.length, 3)
2652+
assert.ok(!(TEST_IS_RETRY in retriedTests[0].meta))
2653+
assert.deepStrictEqual(
2654+
retriedTests.map(test => test.meta[TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX]),
2655+
['true', 'true', 'true']
2656+
)
2657+
assert.deepStrictEqual(
2658+
retriedTests.slice(1).map(test => ({
2659+
reason: test.meta[TEST_RETRY_REASON],
2660+
retry: test.meta[TEST_IS_RETRY],
2661+
})),
2662+
[
2663+
{ reason: TEST_RETRY_REASON_TYPES.atf, retry: 'true' },
2664+
{ reason: TEST_RETRY_REASON_TYPES.atf, retry: 'true' },
2665+
]
2666+
)
25892667
})
25902668

25912669
childProcess = exec(
@@ -2594,7 +2672,7 @@ describe(`jest@${JEST_VERSION} commonJS`, () => {
25942672
cwd,
25952673
env: {
25962674
...getCiVisAgentlessConfig(receiver.port),
2597-
TESTS_TO_RUN: 'jest-fast-check/jest-no-fast-check',
2675+
TESTS_TO_RUN: 'jest-seed-suffix/jest-describe-seed-suffix',
25982676
},
25992677
}
26002678
)

packages/datadog-instrumentations/src/jest.js

Lines changed: 13 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ const {
3333
getTestOptimizationRequestResults,
3434
} = require('../../dd-trace/src/plugins/util/test')
3535
const {
36-
SEED_SUFFIX_RE,
3736
getFormattedJestTestParameters,
3837
getJestTestName,
38+
getRawJestTestName,
3939
getJestSuitesToRun,
40+
removeSeedSuffixFromTestName,
4041
} = require('../../datadog-plugin-jest/src/util')
4142
const { addHook, channel } = require('./helpers/instrument')
4243

@@ -130,8 +131,6 @@ const efdSlowAbortedTests = new Set()
130131
const efdNewTestCandidates = new Set()
131132
// Tests that are genuinely new (not in known tests list).
132133
const newTests = new Set()
133-
const testSuiteAbsolutePathsWithFastCheck = new Set()
134-
const testSuiteFastCheckUsage = new Map()
135134
const testSuiteJestObjects = new Map()
136135
const wrappedJestGlobals = new WeakSet()
137136
const wrappedJestObjects = new WeakSet()
@@ -293,9 +292,7 @@ function getAttemptToFixExecutionsFromJestResults (result) {
293292
if (!testManagementTestsForSuite) continue
294293

295294
for (const { fullName, status } of testResults) {
296-
const testName = testSuiteAbsolutePathsWithFastCheck.has(testFilePath)
297-
? fullName.replace(SEED_SUFFIX_RE, '')
298-
: fullName
295+
const testName = removeSeedSuffixFromTestName(fullName)
299296
const testStatus = getTestStatusFromJestResult(status)
300297
if (!testStatus) continue
301298

@@ -542,14 +539,11 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
542539
}
543540
}
544541

545-
getShouldStripSeedFromTestName () {
546-
return doesTestSuiteUseFastCheck(this.testSuiteAbsolutePath)
547-
}
548-
549542
// At the `add_test` event we don't have the test object yet, so we can't use it
550543
getTestNameFromAddTestEvent (event, state) {
551-
const describeSuffix = getJestTestName(state.currentDescribeBlock, this.getShouldStripSeedFromTestName())
552-
return describeSuffix ? `${describeSuffix} ${event.testName}` : event.testName
544+
const describeSuffix = getRawJestTestName(state.currentDescribeBlock)
545+
const testName = describeSuffix ? `${describeSuffix} ${event.testName}` : event.testName
546+
return removeSeedSuffixFromTestName(testName)
553547
}
554548

555549
async handleTestEvent (event, state) {
@@ -571,7 +565,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
571565
})
572566
}
573567
if (event.name === 'test_start') {
574-
const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
568+
const testName = getJestTestName(event.test)
575569
if (testsToBeRetried.has(testName)) {
576570
// This is needed because we're retrying tests with the same name
577571
this.resetSnapshotState()
@@ -775,7 +769,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
775769
let attemptToFixFailed = false
776770
let failedAllTests = false
777771
let isAttemptToFix = false
778-
const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
772+
const testName = getJestTestName(event.test)
779773
if (this.isTestManagementTestsEnabled) {
780774
isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(testName)
781775
if (isAttemptToFix) {
@@ -955,7 +949,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
955949
// so Jest doesn't see the failure (prevents --bail from stopping the run).
956950
const ctx = testContexts.get(test)
957951
if (ctx?.isQuarantined && !ctx.isAttemptToFix) {
958-
const testName = getJestTestName(test, this.getShouldStripSeedFromTestName())
952+
const testName = getJestTestName(test)
959953
quarantinedFailingTests.add(`${ctx.suite}${testName}`)
960954
} else {
961955
test.errors = errors
@@ -979,7 +973,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
979973
testsToBeRetried.clear()
980974
}
981975
if (event.name === 'test_skip' || event.name === 'test_todo') {
982-
const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
976+
const testName = getJestTestName(event.test)
983977
testSkippedCh.publish({
984978
test: {
985979
name: testName,
@@ -1383,9 +1377,7 @@ function getCliWrapper (isNewJestVersion) {
13831377
for (const { testResults, testFilePath } of result.results.testResults) {
13841378
const suite = getTestSuitePath(testFilePath, result.globalConfig.rootDir)
13851379
for (const { fullName } of testResults) {
1386-
const name = testSuiteAbsolutePathsWithFastCheck.has(testFilePath)
1387-
? fullName.replace(SEED_SUFFIX_RE, '')
1388-
: fullName
1380+
const name = removeSeedSuffixFromTestName(fullName)
13891381
fullNameToSuite.set(name, suite)
13901382
}
13911383
}
@@ -1424,10 +1416,8 @@ function getCliWrapper (isNewJestVersion) {
14241416
.testResults.flatMap(({ testResults, testFilePath: testSuiteAbsolutePath }) => (
14251417
testResults.map(({ fullName: testName, status }) => (
14261418
{
1427-
// Strip @fast-check/jest seed suffix so the name matches what was reported via TEST_NAME
1428-
testName: testSuiteAbsolutePathsWithFastCheck.has(testSuiteAbsolutePath)
1429-
? testName.replace(SEED_SUFFIX_RE, '')
1430-
: testName,
1419+
// Strip seed suffix so the name matches what was reported via TEST_NAME.
1420+
testName: removeSeedSuffixFromTestName(testName),
14311421
testSuiteAbsolutePath,
14321422
status,
14331423
}
@@ -1626,7 +1616,6 @@ function publishTestSuiteFinish (payload, waitForFinish) {
16261616

16271617
function cleanupTestSuiteState (testSuiteAbsolutePath) {
16281618
testSuiteMockedFiles.delete(testSuiteAbsolutePath)
1629-
testSuiteFastCheckUsage.delete(testSuiteAbsolutePath)
16301619
testSuiteJestObjects.delete(testSuiteAbsolutePath)
16311620
}
16321621

@@ -2080,38 +2069,6 @@ function wrapJestGlobalsForRuntime (runtime) {
20802069
})
20812070
}
20822071

2083-
function recordFastCheckUsage (runtime, from, moduleName) {
2084-
if (moduleName !== '@fast-check/jest') return
2085-
2086-
if (from) {
2087-
testSuiteAbsolutePathsWithFastCheck.add(from)
2088-
testSuiteFastCheckUsage.set(from, true)
2089-
}
2090-
if (runtime?._testPath) {
2091-
testSuiteAbsolutePathsWithFastCheck.add(runtime._testPath)
2092-
testSuiteFastCheckUsage.set(runtime._testPath, true)
2093-
}
2094-
}
2095-
2096-
function doesTestSuiteUseFastCheck (testSuiteAbsolutePath) {
2097-
if (!testSuiteAbsolutePath) return false
2098-
if (testSuiteFastCheckUsage.has(testSuiteAbsolutePath)) {
2099-
return testSuiteFastCheckUsage.get(testSuiteAbsolutePath)
2100-
}
2101-
2102-
try {
2103-
const usesFastCheck = readFileSync(testSuiteAbsolutePath, 'utf8').includes('@fast-check/jest')
2104-
testSuiteFastCheckUsage.set(testSuiteAbsolutePath, usesFastCheck)
2105-
if (usesFastCheck) {
2106-
testSuiteAbsolutePathsWithFastCheck.add(testSuiteAbsolutePath)
2107-
}
2108-
return usesFastCheck
2109-
} catch {
2110-
testSuiteFastCheckUsage.set(testSuiteAbsolutePath, false)
2111-
return false
2112-
}
2113-
}
2114-
21152072
function getLastLoggedReferenceError (runtime) {
21162073
const loggedReferenceErrors = runtime?.loggedReferenceErrors
21172074
if (!loggedReferenceErrors?.size) return
@@ -2220,8 +2177,6 @@ addHook({
22202177
// To bypass jest's own require engine
22212178
return requireOutsideJestRequireEngine(this, moduleName)
22222179
}
2223-
// This means that `@fast-check/jest` is used in the test file.
2224-
recordFastCheckUsage(this, from, moduleName)
22252180
let returnedValue
22262181
try {
22272182
returnedValue = requireModuleOrMock.apply(this, arguments)

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,16 @@ 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.
44+
// @fast-check/jest appends a random seed to the reported test name. A test name that keeps changing
45+
// breaks some Test Optimization features, so normalize this narrow suffix regardless of import style.
4646
const SEED_SUFFIX_RE = /\s*\(with seed=-?\d+\)\s*$/i
47+
48+
function removeSeedSuffixFromTestName (testName) {
49+
return testName.replace(SEED_SUFFIX_RE, '')
50+
}
51+
4752
// https://github.com/facebook/jest/blob/3e38157ad5f23fb7d24669d24fae8ded06a7ab75/packages/jest-circus/src/utils.ts#L396
48-
function getJestTestName (test, shouldStripSeed = false) {
53+
function getRawJestTestName (test) {
4954
const titles = []
5055
let parent = test
5156
do {
@@ -54,11 +59,11 @@ function getJestTestName (test, shouldStripSeed = false) {
5459

5560
titles.shift() // remove TOP_DESCRIBE_BLOCK_NAME
5661

57-
const testName = titles.join(' ')
58-
if (shouldStripSeed) {
59-
return testName.replace(SEED_SUFFIX_RE, '')
60-
}
61-
return testName
62+
return titles.join(' ')
63+
}
64+
65+
function getJestTestName (test) {
66+
return removeSeedSuffixFromTestName(getRawJestTestName(test))
6267
}
6368

6469
const globalDocblockRegExp = /^\s*(\/\*\*?(.|\r?\n)*?\*\/)/
@@ -170,6 +175,8 @@ module.exports = {
170175
SEED_SUFFIX_RE,
171176
getFormattedJestTestParameters,
172177
getJestTestName,
178+
getRawJestTestName,
173179
getJestSuitesToRun,
174180
isMarkedAsUnskippable,
181+
removeSeedSuffixFromTestName,
175182
}

0 commit comments

Comments
 (0)