diff --git a/integration-tests/cucumber/cucumber.spec.js b/integration-tests/cucumber/cucumber.spec.js index c6446bcbed6..3c37cad921a 100644 --- a/integration-tests/cucumber/cucumber.spec.js +++ b/integration-tests/cucumber/cucumber.spec.js @@ -33,7 +33,8 @@ const { TEST_NAME, CUCUMBER_IS_PARALLEL, TEST_SUITE, - TEST_CODE_OWNERS + TEST_CODE_OWNERS, + TEST_SESSION_NAME } = require('../../packages/dd-trace/src/plugins/util/test') const isOldNode = semver.satisfies(process.version, '<=16') @@ -129,12 +130,14 @@ versions.forEach(version => { assert.equal(testSessionEventContent.meta[CUCUMBER_IS_PARALLEL], 'true') } + assert.equal(testSessionEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(testSessionEventContent.test_session_id) assert.exists(testSessionEventContent.meta[TEST_COMMAND]) assert.exists(testSessionEventContent.meta[TEST_TOOLCHAIN]) assert.equal(testSessionEventContent.resource.startsWith('test_session.'), true) assert.equal(testSessionEventContent.meta[TEST_STATUS], 'fail') + assert.equal(testModuleEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(testModuleEventContent.test_session_id) assert.exists(testModuleEventContent.test_module_id) assert.exists(testModuleEventContent.meta[TEST_COMMAND]) @@ -163,6 +166,7 @@ versions.forEach(version => { test_session_id: testSessionId } }) => { + assert.equal(meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(meta[TEST_COMMAND]) assert.exists(meta[TEST_MODULE]) assert.exists(testSuiteId) @@ -193,6 +197,7 @@ versions.forEach(version => { test_session_id: testSessionId } }) => { + assert.equal(meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(meta[TEST_COMMAND]) assert.exists(meta[TEST_MODULE]) assert.exists(testSuiteId) @@ -219,7 +224,8 @@ versions.forEach(version => { cwd, env: { ...envVars, - DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2' + DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2', + DD_SESSION_NAME: 'my-test-session' }, stdio: 'pipe' } diff --git a/integration-tests/cypress/cypress.spec.js b/integration-tests/cypress/cypress.spec.js index be4493c6b8e..b6fe70dad78 100644 --- a/integration-tests/cypress/cypress.spec.js +++ b/integration-tests/cypress/cypress.spec.js @@ -32,7 +32,8 @@ const { TEST_IS_RETRY, TEST_EARLY_FLAKE_ENABLED, TEST_SUITE, - TEST_CODE_OWNERS + TEST_CODE_OWNERS, + TEST_SESSION_NAME } = require('../../packages/dd-trace/src/plugins/util/test') const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') const { NODE_MAJOR } = require('../../version') @@ -235,12 +236,14 @@ moduleTypes.forEach(({ const { content: testSessionEventContent } = testSessionEvent const { content: testModuleEventContent } = testModuleEvent + assert.equal(testSessionEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(testSessionEventContent.test_session_id) assert.exists(testSessionEventContent.meta[TEST_COMMAND]) assert.exists(testSessionEventContent.meta[TEST_TOOLCHAIN]) assert.equal(testSessionEventContent.resource.startsWith('test_session.'), true) assert.equal(testSessionEventContent.meta[TEST_STATUS], 'fail') + assert.equal(testModuleEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(testModuleEventContent.test_session_id) assert.exists(testModuleEventContent.test_module_id) assert.exists(testModuleEventContent.meta[TEST_COMMAND]) @@ -271,6 +274,7 @@ moduleTypes.forEach(({ test_session_id: testSessionId } }) => { + assert.equal(meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(meta[TEST_COMMAND]) assert.exists(meta[TEST_MODULE]) assert.exists(testSuiteId) @@ -298,6 +302,7 @@ moduleTypes.forEach(({ test_session_id: testSessionId } }) => { + assert.equal(meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(meta[TEST_COMMAND]) assert.exists(meta[TEST_MODULE]) assert.exists(testSuiteId) @@ -322,7 +327,8 @@ moduleTypes.forEach(({ env: { ...restEnvVars, CYPRESS_BASE_URL: `http://localhost:${webAppPort}`, - DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2' + DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2', + DD_SESSION_NAME: 'my-test-session' }, stdio: 'pipe' } diff --git a/integration-tests/jest/jest.spec.js b/integration-tests/jest/jest.spec.js index ebeed73796a..d90f8de2bcf 100644 --- a/integration-tests/jest/jest.spec.js +++ b/integration-tests/jest/jest.spec.js @@ -31,7 +31,8 @@ const { JEST_DISPLAY_NAME, TEST_EARLY_FLAKE_ABORT_REASON, TEST_SOURCE_START, - TEST_CODE_OWNERS + TEST_CODE_OWNERS, + TEST_SESSION_NAME } = require('../../packages/dd-trace/src/plugins/util/test') const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') @@ -149,19 +150,23 @@ describe('jest CommonJS', () => { ) assert.equal(suites.length, 2) assert.exists(sessionEventContent) + assert.equal(sessionEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(moduleEventContent) + assert.equal(moduleEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.include(testOutput, expectedStdout) - // Can read DD_TAGS tests.forEach(testEvent => { + assert.equal(testEvent.meta[TEST_SESSION_NAME], 'my-test-session') + assert.equal(testEvent.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test'), true) + assert.exists(testEvent.metrics[TEST_SOURCE_START]) + // Can read DD_TAGS assert.propertyVal(testEvent.meta, 'test.customtag', 'customvalue') assert.propertyVal(testEvent.meta, 'test.customtag2', 'customvalue2') }) - tests.forEach(testEvent => { - assert.equal(testEvent.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test'), true) - assert.exists(testEvent.metrics[TEST_SOURCE_START]) + suites.forEach(testSuite => { + assert.equal(testSuite.meta[TEST_SESSION_NAME], 'my-test-session') }) done() @@ -171,7 +176,8 @@ describe('jest CommonJS', () => { cwd, env: { ...envVars, - DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2' + DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2', + DD_SESSION_NAME: 'my-test-session' }, stdio: 'pipe' }) @@ -428,17 +434,29 @@ describe('jest CommonJS', () => { cwd, env: { ...getCiVisAgentlessConfig(receiver.port), - RUN_IN_PARALLEL: true + RUN_IN_PARALLEL: true, + DD_SESSION_NAME: 'my-test-session' }, stdio: 'pipe' }) receiver.gatherPayloads(({ url }) => url === '/api/v2/citestcycle', 5000).then(eventsRequests => { - const eventTypes = eventsRequests.map(({ payload }) => payload) + const events = eventsRequests.map(({ payload }) => payload) .flatMap(({ events }) => events) - .map(event => event.type) + const eventTypes = events.map(event => event.type) assert.includeMembers(eventTypes, ['test', 'test_suite_end', 'test_module_end', 'test_session_end']) + const tests = events.filter(event => event.type === 'test').map(event => event.content) + const testSuites = events.filter(event => event.type === 'test_suite_end').map(event => event.content) + + // it propagates test session name to the test and test suite events in parallel mode + tests.forEach(testEvent => { + assert.equal(testEvent.meta[TEST_SESSION_NAME], 'my-test-session') + }) + testSuites.forEach(testSuite => { + assert.equal(testSuite.meta[TEST_SESSION_NAME], 'my-test-session') + }) + done() }).catch(done) }) diff --git a/integration-tests/mocha/mocha.spec.js b/integration-tests/mocha/mocha.spec.js index d1a35302bbc..5fb00645d48 100644 --- a/integration-tests/mocha/mocha.spec.js +++ b/integration-tests/mocha/mocha.spec.js @@ -32,7 +32,8 @@ const { TEST_MODULE, MOCHA_IS_PARALLEL, TEST_SOURCE_START, - TEST_CODE_OWNERS + TEST_CODE_OWNERS, + TEST_SESSION_NAME } = require('../../packages/dd-trace/src/plugins/util/test') const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') @@ -148,20 +149,24 @@ describe('mocha CommonJS', function () { ) assert.equal(suites.length, 2) assert.exists(sessionEventContent) + assert.equal(sessionEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(moduleEventContent) + assert.equal(moduleEventContent.meta[TEST_SESSION_NAME], 'my-test-session') assert.include(testOutput, expectedStdout) assert.include(testOutput, extraStdout) - // Can read DD_TAGS tests.forEach(testEvent => { + assert.equal(testEvent.meta[TEST_SESSION_NAME], 'my-test-session') + assert.equal(testEvent.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test'), true) + assert.exists(testEvent.metrics[TEST_SOURCE_START]) + // Can read DD_TAGS assert.propertyVal(testEvent.meta, 'test.customtag', 'customvalue') assert.propertyVal(testEvent.meta, 'test.customtag2', 'customvalue2') }) - tests.forEach(testEvent => { - assert.equal(testEvent.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test'), true) - assert.exists(testEvent.metrics[TEST_SOURCE_START]) + suites.forEach(testSuite => { + assert.equal(testSuite.meta[TEST_SESSION_NAME], 'my-test-session') }) done() @@ -171,7 +176,8 @@ describe('mocha CommonJS', function () { cwd, env: { ...envVars, - DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2' + DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2', + DD_SESSION_NAME: 'my-test-session' }, stdio: 'pipe' }) @@ -324,6 +330,7 @@ describe('mocha CommonJS', function () { test_module_id: testModuleId, test_session_id: testSessionId }) => { + assert.equal(meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(meta[TEST_COMMAND]) assert.exists(meta[TEST_MODULE]) assert.exists(testSuiteId) @@ -338,6 +345,7 @@ describe('mocha CommonJS', function () { test_module_id: testModuleId, test_session_id: testSessionId }) => { + assert.equal(meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(meta[TEST_COMMAND]) assert.exists(meta[TEST_MODULE]) assert.exists(testSuiteId) @@ -354,7 +362,8 @@ describe('mocha CommonJS', function () { ...getCiVisAgentlessConfig(receiver.port), RUN_IN_PARALLEL: true, DD_TRACE_DEBUG: 1, - DD_TRACE_LOG_LEVEL: 'warn' + DD_TRACE_LOG_LEVEL: 'warn', + DD_SESSION_NAME: 'my-test-session' }, stdio: 'pipe' }) diff --git a/integration-tests/playwright/playwright.spec.js b/integration-tests/playwright/playwright.spec.js index 8cc1d04a8b3..49c0d097f82 100644 --- a/integration-tests/playwright/playwright.spec.js +++ b/integration-tests/playwright/playwright.spec.js @@ -22,7 +22,8 @@ const { TEST_IS_RETRY, TEST_EARLY_FLAKE_ENABLED, TEST_SUITE, - TEST_CODE_OWNERS + TEST_CODE_OWNERS, + TEST_SESSION_NAME } = require('../../packages/dd-trace/src/plugins/util/test') const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') @@ -80,8 +81,10 @@ versions.forEach((version) => { const stepEvents = events.filter(event => event.type === 'span') + assert.equal(testSessionEvent.content.meta[TEST_SESSION_NAME], 'my-test-session') assert.include(testSessionEvent.content.resource, 'test_session.playwright test') assert.equal(testSessionEvent.content.meta[TEST_STATUS], 'fail') + assert.equal(testModuleEvent.content.meta[TEST_SESSION_NAME], 'my-test-session') assert.include(testModuleEvent.content.resource, 'test_module.playwright test') assert.equal(testModuleEvent.content.meta[TEST_STATUS], 'fail') assert.equal(testSessionEvent.content.meta[TEST_TYPE], 'browser') @@ -103,6 +106,7 @@ versions.forEach((version) => { ]) testSuiteEvents.forEach(testSuiteEvent => { + assert.equal(testSuiteEvent.content.meta[TEST_SESSION_NAME], 'my-test-session') if (testSuiteEvent.content.meta[TEST_STATUS] === 'fail') { assert.exists(testSuiteEvent.content.meta[ERROR_MESSAGE]) } @@ -124,6 +128,7 @@ versions.forEach((version) => { ]) testEvents.forEach(testEvent => { + assert.equal(testEvent.content.meta[TEST_SESSION_NAME], 'my-test-session') assert.exists(testEvent.content.metrics[TEST_SOURCE_START]) assert.equal( testEvent.content.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/playwright-tests/'), true @@ -155,7 +160,8 @@ versions.forEach((version) => { env: { ...envVars, PW_BASE_URL: `http://localhost:${webAppPort}`, - DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2' + DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2', + DD_SESSION_NAME: 'my-test-session' }, stdio: 'pipe' } diff --git a/integration-tests/vitest/vitest.spec.js b/integration-tests/vitest/vitest.spec.js index 53cf0c21de7..ee079783316 100644 --- a/integration-tests/vitest/vitest.spec.js +++ b/integration-tests/vitest/vitest.spec.js @@ -14,7 +14,9 @@ const { TEST_TYPE, TEST_IS_RETRY, TEST_CODE_OWNERS, - TEST_CODE_COVERAGE_LINES_PCT + TEST_CODE_COVERAGE_LINES_PCT, + TEST_SESSION_NAME, + TEST_COMMAND } = require('../../packages/dd-trace/src/plugins/util/test') const versions = ['1.6.0', 'latest'] @@ -57,8 +59,10 @@ versions.forEach((version) => { const testSuiteEvents = events.filter(event => event.type === 'test_suite_end') const testEvents = events.filter(event => event.type === 'test') + assert.equal(testSessionEvent.content.meta[TEST_SESSION_NAME], 'my-test-session') assert.include(testSessionEvent.content.resource, 'test_session.vitest run') assert.equal(testSessionEvent.content.meta[TEST_STATUS], 'fail') + assert.equal(testModuleEvent.content.meta[TEST_SESSION_NAME], 'my-test-session') assert.include(testModuleEvent.content.resource, 'test_module.vitest run') assert.equal(testModuleEvent.content.meta[TEST_STATUS], 'fail') assert.equal(testSessionEvent.content.meta[TEST_TYPE], 'test') @@ -129,6 +133,16 @@ versions.forEach((version) => { 'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can programmatic skip' ] ) + + testEvents.forEach(test => { + assert.equal(test.content.meta[TEST_SESSION_NAME], 'my-test-session') + assert.equal(test.content.meta[TEST_COMMAND], 'vitest run') + }) + + testSuiteEvents.forEach(testSuite => { + assert.equal(testSuite.content.meta[TEST_SESSION_NAME], 'my-test-session') + assert.equal(testSuite.content.meta[TEST_COMMAND], 'vitest run') + }) // TODO: check error messages }).then(() => done()).catch(done) @@ -138,7 +152,8 @@ versions.forEach((version) => { cwd, env: { ...getCiVisAgentlessConfig(receiver.port), - NODE_OPTIONS: '--import dd-trace/register.js -r dd-trace/ci/init' // ESM requires more flags + NODE_OPTIONS: '--import dd-trace/register.js -r dd-trace/ci/init', // ESM requires more flags + DD_SESSION_NAME: 'my-test-session' }, stdio: 'pipe' } diff --git a/packages/datadog-instrumentations/src/jest.js b/packages/datadog-instrumentations/src/jest.js index cf6648c4399..07bcec4b269 100644 --- a/packages/datadog-instrumentations/src/jest.js +++ b/packages/datadog-instrumentations/src/jest.js @@ -765,6 +765,7 @@ addHook({ _ddTestModuleId, _ddTestSessionId, _ddTestCommand, + _ddTestSessionName, _ddForcedToRun, _ddUnskippable, _ddItrCorrelationId, diff --git a/packages/datadog-plugin-cucumber/src/index.js b/packages/datadog-plugin-cucumber/src/index.js index 98ed65cfbd4..2cd6c4d020b 100644 --- a/packages/datadog-plugin-cucumber/src/index.js +++ b/packages/datadog-plugin-cucumber/src/index.js @@ -25,7 +25,8 @@ const { TEST_MODULE, TEST_MODULE_ID, TEST_SUITE, - CUCUMBER_IS_PARALLEL + CUCUMBER_IS_PARALLEL, + TEST_SESSION_NAME } = require('../../dd-trace/src/plugins/util/test') const { RESOURCE_NAME } = require('../../../ext/tags') const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants') @@ -51,7 +52,8 @@ function getTestSuiteTags (testSuiteSpan) { [TEST_SUITE_ID]: testSuiteSpan.context().toSpanId(), [TEST_SESSION_ID]: testSuiteSpan.context().toTraceId(), [TEST_COMMAND]: testSuiteSpan.context()._tags[TEST_COMMAND], - [TEST_MODULE]: 'cucumber' + [TEST_MODULE]: 'cucumber', + [TEST_SESSION_NAME]: testSuiteSpan.context()._tags[TEST_SESSION_NAME] } if (testSuiteSpan.context()._parentId) { suiteTags[TEST_MODULE_ID] = testSuiteSpan.context()._parentId.toString(10) @@ -134,6 +136,9 @@ class CucumberPlugin extends CiPlugin { if (itrCorrelationId) { testSuiteMetadata[ITR_CORRELATION_ID] = itrCorrelationId } + if (this.testSessionName) { + testSuiteMetadata[TEST_SESSION_NAME] = this.testSessionName + } const testSuiteSpan = this.tracer.startSpan('cucumber.test_suite', { childOf: this.testModuleSpan, tags: { diff --git a/packages/datadog-plugin-cypress/src/cypress-plugin.js b/packages/datadog-plugin-cypress/src/cypress-plugin.js index e7c85ae483c..27347fcebdb 100644 --- a/packages/datadog-plugin-cypress/src/cypress-plugin.js +++ b/packages/datadog-plugin-cypress/src/cypress-plugin.js @@ -28,7 +28,9 @@ const { TEST_SOURCE_FILE, TEST_IS_NEW, TEST_IS_RETRY, - TEST_EARLY_FLAKE_ENABLED + TEST_EARLY_FLAKE_ENABLED, + getTestSessionName, + TEST_SESSION_NAME } = require('../../dd-trace/src/plugins/util/test') const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util') const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants') @@ -249,10 +251,12 @@ class CypressPlugin { const testSuiteSpanMetadata = getTestSuiteCommonTags(this.command, this.frameworkVersion, suite, TEST_FRAMEWORK_NAME) this.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite') + return this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_suite`, { childOf: this.testModuleSpan, tags: { [COMPONENT]: TEST_FRAMEWORK_NAME, + [TEST_SESSION_NAME]: this.testSessionName, ...this.testEnvironmentMetadata, ...testSuiteSpanMetadata } @@ -263,7 +267,8 @@ class CypressPlugin { const testSuiteTags = { [TEST_COMMAND]: this.command, [TEST_COMMAND]: this.command, - [TEST_MODULE]: TEST_FRAMEWORK_NAME + [TEST_MODULE]: TEST_FRAMEWORK_NAME, + [TEST_SESSION_NAME]: this.testSessionName } if (this.testSuiteSpan) { testSuiteTags[TEST_SUITE_ID] = this.testSuiteSpan.context().toSpanId() @@ -387,10 +392,13 @@ class CypressPlugin { testSessionSpanMetadata[TEST_EARLY_FLAKE_ENABLED] = 'true' } + this.testSessionName = getTestSessionName(this.tracer._tracer._config, this.command, this.testEnvironmentMetadata) + this.testSessionSpan = this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_session`, { childOf, tags: { [COMPONENT]: TEST_FRAMEWORK_NAME, + [TEST_SESSION_NAME]: this.testSessionName, ...this.testEnvironmentMetadata, ...testSessionSpanMetadata } @@ -401,6 +409,7 @@ class CypressPlugin { childOf: this.testSessionSpan, tags: { [COMPONENT]: TEST_FRAMEWORK_NAME, + [TEST_SESSION_NAME]: this.testSessionName, ...this.testEnvironmentMetadata, ...testModuleSpanMetadata } diff --git a/packages/datadog-plugin-jest/src/index.js b/packages/datadog-plugin-jest/src/index.js index 606fdcec538..c517f7560be 100644 --- a/packages/datadog-plugin-jest/src/index.js +++ b/packages/datadog-plugin-jest/src/index.js @@ -22,7 +22,8 @@ const { TEST_EARLY_FLAKE_ABORT_REASON, JEST_DISPLAY_NAME, TEST_IS_RUM_ACTIVE, - TEST_BROWSER_DRIVER + TEST_BROWSER_DRIVER, + TEST_SESSION_NAME } = require('../../dd-trace/src/plugins/util/test') const { COMPONENT } = require('../../dd-trace/src/constants') const id = require('../../dd-trace/src/id') @@ -149,6 +150,7 @@ class JestPlugin extends CiPlugin { config._ddTestSessionId = this.testSessionSpan.context().toTraceId() config._ddTestModuleId = this.testModuleSpan.context().toSpanId() config._ddTestCommand = this.testSessionSpan.context()._tags[TEST_COMMAND] + config._ddTestSessionName = this.testSessionName config._ddItrCorrelationId = this.itrCorrelationId config._ddIsEarlyFlakeDetectionEnabled = !!this.libraryConfig?.isEarlyFlakeDetectionEnabled config._ddEarlyFlakeDetectionNumRetries = this.libraryConfig?.earlyFlakeDetectionNumRetries ?? 0 @@ -162,6 +164,7 @@ class JestPlugin extends CiPlugin { const { _ddTestSessionId: testSessionId, _ddTestCommand: testCommand, + _ddTestSessionName: testSessionName, _ddTestModuleId: testModuleId, _ddItrCorrelationId: itrCorrelationId, _ddForcedToRun, @@ -196,6 +199,9 @@ class JestPlugin extends CiPlugin { if (displayName) { testSuiteMetadata[JEST_DISPLAY_NAME] = displayName } + if (testSessionName) { + testSuiteMetadata[TEST_SESSION_NAME] = testSessionName + } this.testSuiteSpan = this.tracer.startSpan('jest.test_suite', { childOf: testSessionSpanContext, diff --git a/packages/datadog-plugin-mocha/src/index.js b/packages/datadog-plugin-mocha/src/index.js index 79b0d14c62f..5a0cadc4770 100644 --- a/packages/datadog-plugin-mocha/src/index.js +++ b/packages/datadog-plugin-mocha/src/index.js @@ -29,7 +29,8 @@ const { TEST_SUITE, MOCHA_IS_PARALLEL, TEST_IS_RUM_ACTIVE, - TEST_BROWSER_DRIVER + TEST_BROWSER_DRIVER, + TEST_SESSION_NAME } = require('../../dd-trace/src/plugins/util/test') const { COMPONENT } = require('../../dd-trace/src/constants') const { @@ -52,7 +53,8 @@ function getTestSuiteLevelVisibilityTags (testSuiteSpan) { [TEST_SUITE_ID]: testSuiteSpanContext.toSpanId(), [TEST_SESSION_ID]: testSuiteSpanContext.toTraceId(), [TEST_COMMAND]: testSuiteSpanContext._tags[TEST_COMMAND], - [TEST_MODULE]: 'mocha' + [TEST_MODULE]: 'mocha', + [TEST_SESSION_NAME]: testSuiteSpanContext._tags[TEST_SESSION_NAME] } if (testSuiteSpanContext._parentId) { suiteTags[TEST_MODULE_ID] = testSuiteSpanContext._parentId.toString(10) @@ -124,6 +126,9 @@ class MochaPlugin extends CiPlugin { testSuiteMetadata[TEST_ITR_FORCED_RUN] = 'true' this.telemetry.count(TELEMETRY_ITR_FORCED_TO_RUN, { testLevel: 'suite' }) } + if (this.testSessionName) { + testSuiteMetadata[TEST_SESSION_NAME] = this.testSessionName + } const testSuiteSpan = this.tracer.startSpan('mocha.test_suite', { childOf: this.testModuleSpan, diff --git a/packages/datadog-plugin-playwright/src/index.js b/packages/datadog-plugin-playwright/src/index.js index 482bd6f10b9..0e9bf4f6c57 100644 --- a/packages/datadog-plugin-playwright/src/index.js +++ b/packages/datadog-plugin-playwright/src/index.js @@ -15,7 +15,8 @@ const { TEST_IS_NEW, TEST_IS_RETRY, TEST_EARLY_FLAKE_ENABLED, - TELEMETRY_TEST_SESSION + TELEMETRY_TEST_SESSION, + TEST_SESSION_NAME } = require('../../dd-trace/src/plugins/util/test') const { RESOURCE_NAME } = require('../../../ext/tags') const { COMPONENT } = require('../../dd-trace/src/constants') @@ -76,6 +77,9 @@ class PlaywrightPlugin extends CiPlugin { testSuite, 'playwright' ) + if (this.testSessionName) { + testSuiteMetadata[TEST_SESSION_NAME] = this.testSessionName + } const testSuiteSpan = this.tracer.startSpan('playwright.test_suite', { childOf: this.testModuleSpan, diff --git a/packages/datadog-plugin-vitest/src/index.js b/packages/datadog-plugin-vitest/src/index.js index a93eeb1ea4d..fc10935745f 100644 --- a/packages/datadog-plugin-vitest/src/index.js +++ b/packages/datadog-plugin-vitest/src/index.js @@ -9,7 +9,8 @@ const { TEST_SOURCE_FILE, TEST_IS_RETRY, TEST_CODE_COVERAGE_LINES_PCT, - TEST_CODE_OWNERS + TEST_CODE_OWNERS, + TEST_SESSION_NAME } = require('../../dd-trace/src/plugins/util/test') const { COMPONENT } = require('../../dd-trace/src/constants') const { @@ -128,11 +129,13 @@ class VitestPlugin extends CiPlugin { const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot) const testSuiteMetadata = getTestSuiteCommonTags( - this.command, + process.env.DD_CIVISIBILITY_TEST_COMMAND, this.frameworkVersion, testSuite, 'vitest' ) + testSuiteMetadata[TEST_SESSION_NAME] = process.env.DD_CIVISIBILITY_TEST_SESSION_NAME + const testSuiteSpan = this.tracer.startSpan('vitest.test_suite', { childOf: testSessionSpanContext, tags: { diff --git a/packages/dd-trace/src/config.js b/packages/dd-trace/src/config.js index c4cae9a9268..31592accab2 100644 --- a/packages/dd-trace/src/config.js +++ b/packages/dd-trace/src/config.js @@ -451,6 +451,7 @@ class Config { this._setValue(defaults, 'isGitUploadEnabled', false) this._setValue(defaults, 'isIntelligentTestRunnerEnabled', false) this._setValue(defaults, 'isManualApiEnabled', false) + this._setValue(defaults, 'ciVisibilitySessionName', '') this._setValue(defaults, 'logInjection', false) this._setValue(defaults, 'lookup', undefined) this._setValue(defaults, 'memcachedCommandEnabled', false) @@ -985,7 +986,8 @@ class Config { DD_CIVISIBILITY_AGENTLESS_URL, DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED, DD_CIVISIBILITY_FLAKY_RETRY_ENABLED, - DD_CIVISIBILITY_FLAKY_RETRY_COUNT + DD_CIVISIBILITY_FLAKY_RETRY_COUNT, + DD_SESSION_NAME } = process.env if (DD_CIVISIBILITY_AGENTLESS_URL) { @@ -1001,6 +1003,7 @@ class Config { this._setValue(calc, 'flakyTestRetriesCount', coalesce(maybeInt(DD_CIVISIBILITY_FLAKY_RETRY_COUNT), 5)) this._setBoolean(calc, 'isIntelligentTestRunnerEnabled', isTrue(this._isCiVisibilityItrEnabled())) this._setBoolean(calc, 'isManualApiEnabled', this._isCiVisibilityManualApiEnabled()) + this._setString(calc, 'ciVisibilitySessionName', DD_SESSION_NAME) } this._setString(calc, 'dogstatsd.hostname', this._getHostname()) this._setBoolean(calc, 'isGitUploadEnabled', diff --git a/packages/dd-trace/src/plugin_manager.js b/packages/dd-trace/src/plugin_manager.js index 8b23c965a47..9c871291c22 100644 --- a/packages/dd-trace/src/plugin_manager.js +++ b/packages/dd-trace/src/plugin_manager.js @@ -136,10 +136,19 @@ module.exports = class PluginManager { dbmPropagationMode, dsmEnabled, clientIpEnabled, - memcachedCommandEnabled + memcachedCommandEnabled, + ciVisibilitySessionName } = this._tracerConfig - const sharedConfig = {} + const sharedConfig = { + dbmPropagationMode, + dsmEnabled, + memcachedCommandEnabled, + site, + url, + headers: headerTags || [], + ciVisibilitySessionName + } if (logInjection !== undefined) { sharedConfig.logInjection = logInjection @@ -149,10 +158,6 @@ module.exports = class PluginManager { sharedConfig.queryStringObfuscation = queryStringObfuscation } - sharedConfig.dbmPropagationMode = dbmPropagationMode - sharedConfig.dsmEnabled = dsmEnabled - sharedConfig.memcachedCommandEnabled = memcachedCommandEnabled - if (serviceMapping && serviceMapping[name]) { sharedConfig.service = serviceMapping[name] } @@ -161,10 +166,6 @@ module.exports = class PluginManager { sharedConfig.clientIpEnabled = clientIpEnabled } - sharedConfig.site = site - sharedConfig.url = url - sharedConfig.headers = headerTags || [] - return sharedConfig } } diff --git a/packages/dd-trace/src/plugins/ci_plugin.js b/packages/dd-trace/src/plugins/ci_plugin.js index 8c8c15c8e55..3805224eac9 100644 --- a/packages/dd-trace/src/plugins/ci_plugin.js +++ b/packages/dd-trace/src/plugins/ci_plugin.js @@ -1,5 +1,6 @@ const { getTestEnvironmentMetadata, + getTestSessionName, getCodeOwnersFileEntries, getTestParentSpan, getTestCommonTags, @@ -13,6 +14,7 @@ const { TEST_SESSION_ID, TEST_COMMAND, TEST_MODULE, + TEST_SESSION_NAME, getTestSuiteCommonTags, TEST_STATUS, TEST_SKIPPED_BY_ITR, @@ -75,10 +77,13 @@ module.exports = class CiPlugin extends Plugin { // only for playwright this.rootDir = rootDir + this.testSessionName = getTestSessionName(this.config, this.command, this.testEnvironmentMetadata) + this.testSessionSpan = this.tracer.startSpan(`${this.constructor.id}.test_session`, { childOf, tags: { [COMPONENT]: this.constructor.id, + [TEST_SESSION_NAME]: this.testSessionName, ...this.testEnvironmentMetadata, ...testSessionSpanMetadata } @@ -88,6 +93,7 @@ module.exports = class CiPlugin extends Plugin { childOf: this.testSessionSpan, tags: { [COMPONENT]: this.constructor.id, + [TEST_SESSION_NAME]: this.testSessionName, ...this.testEnvironmentMetadata, ...testModuleSpanMetadata } @@ -97,6 +103,8 @@ module.exports = class CiPlugin extends Plugin { if (this.constructor.id === 'vitest') { process.env.DD_CIVISIBILITY_TEST_SESSION_ID = this.testSessionSpan.context().toTraceId() process.env.DD_CIVISIBILITY_TEST_MODULE_ID = this.testModuleSpan.context().toSpanId() + process.env.DD_CIVISIBILITY_TEST_COMMAND = this.command + process.env.DD_CIVISIBILITY_TEST_SESSION_NAME = this.testSessionName } this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'module') @@ -208,6 +216,11 @@ module.exports = class CiPlugin extends Plugin { ...extraTags } + // this.testSessionName might be empty for parallel workers + if (this.testSessionName) { + testTags[TEST_SESSION_NAME] = this.testSessionName + } + const { [TEST_SOURCE_FILE]: testSourceFile } = extraTags // We'll try with the test source file if available (it could be different from the test suite) let codeOwners = getCodeOwnersForFilename(testSourceFile, this.codeOwnersEntries) @@ -229,7 +242,8 @@ module.exports = class CiPlugin extends Plugin { [TEST_SUITE_ID]: testSuiteSpan.context().toSpanId(), [TEST_SESSION_ID]: testSuiteSpan.context().toTraceId(), [TEST_COMMAND]: testSuiteSpan.context()._tags[TEST_COMMAND], - [TEST_MODULE]: this.constructor.id + [TEST_MODULE]: this.constructor.id, + [TEST_SESSION_NAME]: testSuiteSpan.context()._tags[TEST_SESSION_NAME] } if (testSuiteSpan.context()._parentId) { suiteTags[TEST_MODULE_ID] = testSuiteSpan.context()._parentId.toString(10) diff --git a/packages/dd-trace/src/plugins/util/test.js b/packages/dd-trace/src/plugins/util/test.js index d1d1861ea5d..edd4291a974 100644 --- a/packages/dd-trace/src/plugins/util/test.js +++ b/packages/dd-trace/src/plugins/util/test.js @@ -19,7 +19,8 @@ const { GIT_COMMIT_AUTHOR_NAME, GIT_COMMIT_MESSAGE, CI_WORKSPACE_PATH, - CI_PIPELINE_URL + CI_PIPELINE_URL, + CI_JOB_NAME } = require('./tags') const id = require('../../id') @@ -28,6 +29,9 @@ const { SAMPLING_RULE_DECISION } = require('../../constants') const { AUTO_KEEP } = require('../../../../../ext/priority') const { version: ddTraceVersion } = require('../../../../../package.json') +// session tags +const TEST_SESSION_NAME = 'test_session.name' + const TEST_FRAMEWORK = 'test.framework' const TEST_FRAMEWORK_VERSION = 'test.framework_version' const TEST_TYPE = 'test.type' @@ -97,6 +101,7 @@ const EFD_TEST_NAME_REGEX = new RegExp(EFD_STRING + ' \\(#\\d+\\): ', 'g') module.exports = { TEST_CODE_OWNERS, + TEST_SESSION_NAME, TEST_FRAMEWORK, TEST_FRAMEWORK_VERSION, JEST_TEST_RUNNER, @@ -167,7 +172,8 @@ module.exports = { TEST_BROWSER_DRIVER, TEST_BROWSER_DRIVER_VERSION, TEST_BROWSER_NAME, - TEST_BROWSER_VERSION + TEST_BROWSER_VERSION, + getTestSessionName } // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19 @@ -615,3 +621,13 @@ function getIsFaultyEarlyFlakeDetection (projectSuites, testsBySuiteName, faulty newSuitesPercentage > faultyThresholdPercentage ) } + +function getTestSessionName (config, testCommand, envTags) { + if (config.ciVisibilitySessionName) { + return config.ciVisibilitySessionName + } + if (envTags[CI_JOB_NAME]) { + return `${envTags[CI_JOB_NAME]}-${testCommand}` + } + return testCommand +} diff --git a/packages/dd-trace/test/config.spec.js b/packages/dd-trace/test/config.spec.js index cd9ae1d661a..321f7d63534 100644 --- a/packages/dd-trace/test/config.spec.js +++ b/packages/dd-trace/test/config.spec.js @@ -321,6 +321,7 @@ describe('Config', () => { { name: 'isGitUploadEnabled', value: false, origin: 'default' }, { name: 'isIntelligentTestRunnerEnabled', value: false, origin: 'default' }, { name: 'isManualApiEnabled', value: false, origin: 'default' }, + { name: 'ciVisibilitySessionName', value: '', origin: 'default' }, { name: 'logInjection', value: false, origin: 'default' }, { name: 'lookup', value: undefined, origin: 'default' }, { name: 'openAiLogsEnabled', value: false, origin: 'default' }, @@ -1797,6 +1798,7 @@ describe('Config', () => { delete process.env.DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED delete process.env.DD_CIVISIBILITY_FLAKY_RETRY_ENABLED delete process.env.DD_CIVISIBILITY_FLAKY_RETRY_COUNT + delete process.env.DD_SESSION_NAME delete process.env.JEST_WORKER_ID options = {} }) @@ -1881,6 +1883,11 @@ describe('Config', () => { const config = new Config(options) expect(config).to.have.property('flakyTestRetriesCount', 5) }) + it('should set the session name if DD_SESSION_NAME is set', () => { + process.env.DD_SESSION_NAME = 'my-test-session' + const config = new Config(options) + expect(config).to.have.property('ciVisibilitySessionName', 'my-test-session') + }) }) context('ci visibility mode is not enabled', () => { it('should not activate intelligent test runner or git metadata upload', () => {