diff --git a/integration-tests/ci-visibility/run-jest.js b/integration-tests/ci-visibility/run-jest.js index a7f8054b720..efd1ab9f548 100644 --- a/integration-tests/ci-visibility/run-jest.js +++ b/integration-tests/ci-visibility/run-jest.js @@ -25,6 +25,10 @@ if (process.env.ENABLE_JSDOM) { options.testEnvironment = 'jsdom' } +if (process.env.ENABLE_HAPPY_DOM) { + options.testEnvironment = '@happy-dom/jest-environment' +} + if (process.env.COLLECT_COVERAGE_FROM) { options.collectCoverageFrom = process.env.COLLECT_COVERAGE_FROM.split(',') } diff --git a/integration-tests/ci-visibility/run-jest.mjs b/integration-tests/ci-visibility/run-jest.mjs index a9ecb24d0c6..f6a03076b40 100644 --- a/integration-tests/ci-visibility/run-jest.mjs +++ b/integration-tests/ci-visibility/run-jest.mjs @@ -26,6 +26,10 @@ if (process.env.ENABLE_JSDOM) { options.testEnvironment = 'jsdom' } +if (process.env.ENABLE_HAPPY_DOM) { + options.testEnvironment = '@happy-dom/jest-environment' +} + jest.runCLI( options, options.projects diff --git a/integration-tests/jest/jest.spec.js b/integration-tests/jest/jest.spec.js index 854c3e2d54b..d495199a29c 100644 --- a/integration-tests/jest/jest.spec.js +++ b/integration-tests/jest/jest.spec.js @@ -86,6 +86,7 @@ describe('jest CommonJS', () => { 'chai@v4', 'jest-jasmine2', 'jest-environment-jsdom', + '@happy-dom/jest-environment', 'office-addin-mock' ], true) cwd = sandbox.folder @@ -2283,6 +2284,80 @@ describe('jest CommonJS', () => { }) }) + it('works with happy-dom', (done) => { + // Tests from ci-visibility/test/ci-visibility-test-2.js will be considered new + receiver.setKnownTests({ + jest: { + 'ci-visibility/test/ci-visibility-test.js': ['ci visibility can report tests'] + } + }) + const NUM_RETRIES_EFD = 3 + receiver.setSettings({ + early_flake_detection: { + enabled: true, + slow_test_retries: { + '5s': NUM_RETRIES_EFD + }, + faulty_session_threshold: 100 + }, + known_tests_enabled: true + }) + + const eventsPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), (payloads) => { + const events = payloads.flatMap(({ payload }) => payload.events) + + const tests = events.filter(event => event.type === 'test').map(event => event.content) + + // no other tests are considered new + const oldTests = tests.filter(test => + test.meta[TEST_SUITE] === 'ci-visibility/test/ci-visibility-test.js' + ) + oldTests.forEach(test => { + assert.notProperty(test.meta, TEST_IS_NEW) + }) + assert.equal(oldTests.length, 1) + + const newTests = tests.filter(test => + test.meta[TEST_SUITE] === 'ci-visibility/test/ci-visibility-test-2.js' + ) + newTests.forEach(test => { + assert.propertyVal(test.meta, TEST_IS_NEW, 'true') + }) + const retriedTests = newTests.filter(test => test.meta[TEST_IS_RETRY] === 'true') + // all but one has been retried + assert.equal( + newTests.length - 1, + retriedTests.length + ) + assert.equal(retriedTests.length, NUM_RETRIES_EFD) + // Test name does not change + newTests.forEach(test => { + assert.equal(test.meta[TEST_NAME], 'ci visibility 2 can report tests 2') + }) + }) + + childProcess = exec( + runTestsWithCoverageCommand, + { + cwd, + env: { + ...getCiVisAgentlessConfig(receiver.port), // use agentless for this test, just for variety + TESTS_TO_RUN: 'test/ci-visibility-test', + ENABLE_HAPPY_DOM: true, + DD_TRACE_DEBUG: 1, + DD_TRACE_LOG_LEVEL: 'warn' + }, + stdio: 'inherit' + } + ) + childProcess.on('exit', () => { + eventsPromise.then(() => { + done() + }).catch(done) + }) + }) + it('disables early flake detection if known tests should not be requested', (done) => { receiver.setSettings({ early_flake_detection: { diff --git a/packages/datadog-instrumentations/src/helpers/hooks.js b/packages/datadog-instrumentations/src/helpers/hooks.js index eadf031a13b..a96119755e2 100644 --- a/packages/datadog-instrumentations/src/helpers/hooks.js +++ b/packages/datadog-instrumentations/src/helpers/hooks.js @@ -16,6 +16,7 @@ module.exports = { '@graphql-tools/executor': () => require('../graphql'), '@grpc/grpc-js': () => require('../grpc'), '@hapi/hapi': () => require('../hapi'), + '@happy-dom/jest-environment': () => require('../jest'), '@jest/core': () => require('../jest'), '@jest/reporters': () => require('../jest'), '@jest/test-sequencer': () => require('../jest'), diff --git a/packages/datadog-instrumentations/src/jest.js b/packages/datadog-instrumentations/src/jest.js index c56c1b205d5..de2a07d21d2 100644 --- a/packages/datadog-instrumentations/src/jest.js +++ b/packages/datadog-instrumentations/src/jest.js @@ -651,6 +651,11 @@ addHook({ versions: ['>=24.8.0'] }, getTestEnvironment) +addHook({ + name: '@happy-dom/jest-environment', + versions: ['>=10.0.0'] +}, getTestEnvironment) + function getWrappedScheduleTests (scheduleTests, frameworkVersion) { // `scheduleTests` is an async function return function (tests) { diff --git a/packages/datadog-plugin-jest/test/circus.spec.js b/packages/datadog-plugin-jest/test/circus.spec.js index b01e217b074..a1c51f1fb45 100644 --- a/packages/datadog-plugin-jest/test/circus.spec.js +++ b/packages/datadog-plugin-jest/test/circus.spec.js @@ -44,20 +44,25 @@ function loadAgent (moduleName, version, isAgentlessTest, isEvpProxyTest) { if (!isEvpProxyTest) { agent.setAvailableEndpoints([]) } + const isHappyDom = moduleName === '@happy-dom/jest-environment' return agent.load(['jest', 'http'], { service: 'test' }, { experimental: { exporter } }).then(() => { global.__libraryName__ = moduleName global.__libraryVersion__ = version return { - jestExecutable: require(`../../../versions/jest@${version}`).get(), + jestExecutable: isHappyDom + ? require('../../../versions/jest').get() + : require(`../../../versions/jest@${version}`).get(), jestCommonOptions: { projects: [__dirname], testPathIgnorePatterns: ['/node_modules/'], coverageReporters: ['none'], reporters: [], silent: true, - testEnvironment: path.join(__dirname, 'env.js'), - testRunner: require(`../../../versions/jest-circus@${version}`).getPath('jest-circus/runner'), + testEnvironment: isHappyDom ? '@happy-dom/jest-environment' : path.join(__dirname, 'env.js'), + testRunner: isHappyDom + ? require('../../../versions/jest-circus').getPath('jest-circus/runner') + : require(`../../../versions/jest-circus@${version}`).getPath('jest-circus/runner'), cache: false, maxWorkers: '50%' } @@ -72,7 +77,9 @@ describe('Plugin', function () { this.timeout(testTimeout) this.retries(2) - withVersions('jest', ['jest-environment-node', 'jest-environment-jsdom'], (version, moduleName) => { + const versions = ['jest-environment-node', 'jest-environment-jsdom', '@happy-dom/jest-environment'] + + withVersions('jest', versions, (version, moduleName) => { afterEach(() => { delete process.env.DD_API_KEY const jestTestFile = fs.readdirSync(__dirname).filter(name => name.startsWith('jest-')) diff --git a/packages/dd-trace/src/plugins/index.js b/packages/dd-trace/src/plugins/index.js index 164fe36fde3..ee0696642f5 100644 --- a/packages/dd-trace/src/plugins/index.js +++ b/packages/dd-trace/src/plugins/index.js @@ -13,6 +13,7 @@ module.exports = { get '@google-cloud/vertexai' () { return require('../../../datadog-plugin-google-cloud-vertexai/src') }, get '@grpc/grpc-js' () { return require('../../../datadog-plugin-grpc/src') }, get '@hapi/hapi' () { return require('../../../datadog-plugin-hapi/src') }, + get '@happy-dom/jest-environment' () { return require('../../../datadog-plugin-jest/src') }, get '@jest/core' () { return require('../../../datadog-plugin-jest/src') }, get '@jest/test-sequencer' () { return require('../../../datadog-plugin-jest/src') }, get '@jest/transform' () { return require('../../../datadog-plugin-jest/src') }, diff --git a/packages/dd-trace/test/plugins/externals.json b/packages/dd-trace/test/plugins/externals.json index 554a5065709..9014d1a5c9a 100644 --- a/packages/dd-trace/test/plugins/externals.json +++ b/packages/dd-trace/test/plugins/externals.json @@ -265,6 +265,12 @@ "versions": ["9.1.4"] } ], + "happy-dom": [ + { + "name": "@happy-dom/jest-environment", + "versions": [">=10.0.0"] + } + ], "hono": [ { "name": "@hono/node-server", diff --git a/packages/dd-trace/test/plugins/versions/package.json b/packages/dd-trace/test/plugins/versions/package.json index 61a849de81f..e3537b2f5f3 100644 --- a/packages/dd-trace/test/plugins/versions/package.json +++ b/packages/dd-trace/test/plugins/versions/package.json @@ -32,6 +32,7 @@ "@grpc/proto-loader": "0.8.0", "@hapi/boom": "10.0.1", "@hapi/hapi": "21.4.2", + "@happy-dom/jest-environment": "18.0.1", "@hono/node-server": "1.18.1", "@jest/core": "30.0.5", "@jest/globals": "30.0.5",