dd-trace 6.4.0 → 6.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/README.electron.md +7 -0
  2. package/README.md +17 -0
  3. package/ci/diagnose.js +2100 -0
  4. package/ci/init.js +23 -2
  5. package/ci/runbook.md +198 -0
  6. package/ci/test-optimization-validation/approval-artifacts.js +143 -0
  7. package/ci/test-optimization-validation/approval.js +299 -0
  8. package/ci/test-optimization-validation/artifact-id.js +20 -0
  9. package/ci/test-optimization-validation/bounded-json.js +121 -0
  10. package/ci/test-optimization-validation/ci-command-candidate.js +83 -0
  11. package/ci/test-optimization-validation/ci-discovery.js +181 -0
  12. package/ci/test-optimization-validation/ci-remediation.js +210 -0
  13. package/ci/test-optimization-validation/cli.js +903 -0
  14. package/ci/test-optimization-validation/command-blocker.js +81 -0
  15. package/ci/test-optimization-validation/command-output-policy.js +206 -0
  16. package/ci/test-optimization-validation/command-runner.js +707 -0
  17. package/ci/test-optimization-validation/command-suitability.js +471 -0
  18. package/ci/test-optimization-validation/executable-approval.js +48 -0
  19. package/ci/test-optimization-validation/executable.js +480 -0
  20. package/ci/test-optimization-validation/generated-file-policy.js +63 -0
  21. package/ci/test-optimization-validation/generated-files.js +351 -0
  22. package/ci/test-optimization-validation/generated-verifier.js +196 -0
  23. package/ci/test-optimization-validation/init-probe-preload.js +163 -0
  24. package/ci/test-optimization-validation/init-probe.js +246 -0
  25. package/ci/test-optimization-validation/late-initialization.js +63 -0
  26. package/ci/test-optimization-validation/local-command.js +163 -0
  27. package/ci/test-optimization-validation/manifest-loader.js +133 -0
  28. package/ci/test-optimization-validation/manifest-scaffold.js +738 -0
  29. package/ci/test-optimization-validation/manifest-schema.js +862 -0
  30. package/ci/test-optimization-validation/offline-fixtures.js +327 -0
  31. package/ci/test-optimization-validation/offline-output.js +406 -0
  32. package/ci/test-optimization-validation/payload-normalizer.js +72 -0
  33. package/ci/test-optimization-validation/plan-writer.js +1120 -0
  34. package/ci/test-optimization-validation/preflight-runner.js +114 -0
  35. package/ci/test-optimization-validation/redaction.js +331 -0
  36. package/ci/test-optimization-validation/report-writer.js +1508 -0
  37. package/ci/test-optimization-validation/safe-files.js +203 -0
  38. package/ci/test-optimization-validation/scenarios/auto-test-retries.js +159 -0
  39. package/ci/test-optimization-validation/scenarios/basic-reporting.js +657 -0
  40. package/ci/test-optimization-validation/scenarios/ci-wiring.js +780 -0
  41. package/ci/test-optimization-validation/scenarios/early-flake-detection.js +141 -0
  42. package/ci/test-optimization-validation/scenarios/helpers.js +539 -0
  43. package/ci/test-optimization-validation/scenarios/test-management.js +218 -0
  44. package/ci/test-optimization-validation/setup-runner.js +97 -0
  45. package/ci/test-optimization-validation/static-diagnosis.js +159 -0
  46. package/ci/test-optimization-validation/test-output.js +129 -0
  47. package/ci/test-optimization-validation-manifest.schema.json +1 -0
  48. package/ci/validate-test-optimization.js +3 -0
  49. package/ext/exporters.js +1 -0
  50. package/index.d.ts +121 -8
  51. package/init.js +18 -0
  52. package/initialize.mjs +1 -1
  53. package/loader-hook.mjs +28 -18
  54. package/openfeature.d.ts +1 -0
  55. package/openfeature.js +4 -0
  56. package/package.json +11 -8
  57. package/packages/datadog-instrumentations/src/ai.js +8 -2
  58. package/packages/datadog-instrumentations/src/child_process.js +1 -1
  59. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
  60. package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +9 -3
  61. package/packages/datadog-instrumentations/src/cucumber.js +31 -19
  62. package/packages/datadog-instrumentations/src/cypress-config.js +398 -52
  63. package/packages/datadog-instrumentations/src/fastify.js +7 -11
  64. package/packages/datadog-instrumentations/src/helpers/channel.js +74 -0
  65. package/packages/datadog-instrumentations/src/helpers/hook.js +27 -6
  66. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +8 -8
  67. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +28 -2
  68. package/packages/datadog-instrumentations/src/jest.js +44 -17
  69. package/packages/datadog-instrumentations/src/mariadb.js +1 -1
  70. package/packages/datadog-instrumentations/src/mocha/main.js +79 -32
  71. package/packages/datadog-instrumentations/src/mocha/utils.js +0 -4
  72. package/packages/datadog-instrumentations/src/mongodb.js +3 -3
  73. package/packages/datadog-instrumentations/src/mongoose.js +3 -3
  74. package/packages/datadog-instrumentations/src/mquery.js +2 -2
  75. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  76. package/packages/datadog-instrumentations/src/next.js +24 -0
  77. package/packages/datadog-instrumentations/src/nyc.js +0 -2
  78. package/packages/datadog-instrumentations/src/oracledb.js +2 -2
  79. package/packages/datadog-instrumentations/src/pg.js +2 -2
  80. package/packages/datadog-instrumentations/src/playwright.js +162 -46
  81. package/packages/datadog-instrumentations/src/process.js +3 -0
  82. package/packages/datadog-instrumentations/src/router.js +18 -8
  83. package/packages/datadog-instrumentations/src/selenium.js +52 -26
  84. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +3 -3
  85. package/packages/datadog-instrumentations/src/vitest-main.js +14 -28
  86. package/packages/datadog-instrumentations/src/vitest-util.js +0 -7
  87. package/packages/datadog-instrumentations/src/vitest-worker.js +5 -8
  88. package/packages/datadog-instrumentations/src/ws.js +2 -1
  89. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +2 -2
  90. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +2 -1
  91. package/packages/datadog-plugin-aws-sdk/src/util.js +2 -2
  92. package/packages/datadog-plugin-cucumber/src/index.js +3 -2
  93. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -57
  94. package/packages/datadog-plugin-cypress/src/index.js +6 -1
  95. package/packages/datadog-plugin-cypress/src/support.js +92 -26
  96. package/packages/datadog-plugin-electron/src/ipc.js +1 -1
  97. package/packages/datadog-plugin-graphql/src/execute.js +87 -45
  98. package/packages/datadog-plugin-graphql/src/validate.js +2 -2
  99. package/packages/datadog-plugin-http2/src/client.js +1 -1
  100. package/packages/datadog-plugin-langchain/src/handlers/embedding.js +2 -1
  101. package/packages/datadog-plugin-langchain/src/handlers/language_models.js +2 -1
  102. package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
  103. package/packages/datadog-plugin-mocha/src/index.js +2 -1
  104. package/packages/datadog-plugin-playwright/src/index.js +181 -65
  105. package/packages/datadog-plugin-router/src/index.js +11 -2
  106. package/packages/datadog-plugin-selenium/src/index.js +5 -36
  107. package/packages/datadog-plugin-vitest/src/index.js +5 -5
  108. package/packages/datadog-plugin-ws/src/close.js +2 -1
  109. package/packages/datadog-plugin-ws/src/producer.js +2 -1
  110. package/packages/datadog-plugin-ws/src/receiver.js +2 -1
  111. package/packages/datadog-plugin-ws/src/server.js +2 -1
  112. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  113. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
  114. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +3 -1
  115. package/packages/dd-trace/src/azure_metadata.js +2 -1
  116. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +93 -28
  117. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
  118. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +160 -0
  119. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +288 -0
  120. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/payload-projection.js +84 -0
  121. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +369 -0
  122. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +60 -0
  123. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +41 -3
  124. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -2
  125. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +1 -1
  126. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +6 -2
  127. package/packages/dd-trace/src/ci-visibility/rum.js +7 -0
  128. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
  129. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
  130. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +137 -9
  131. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +168 -8
  132. package/packages/dd-trace/src/ci-visibility/test-screenshot.js +90 -0
  133. package/packages/dd-trace/src/config/generated-config-types.d.ts +16 -0
  134. package/packages/dd-trace/src/config/helper.js +1 -0
  135. package/packages/dd-trace/src/config/index.js +10 -0
  136. package/packages/dd-trace/src/config/supported-configurations.json +66 -0
  137. package/packages/dd-trace/src/exporter.js +2 -0
  138. package/packages/dd-trace/src/exporters/common/client-library-headers.js +21 -0
  139. package/packages/dd-trace/src/exporters/common/request.js +59 -30
  140. package/packages/dd-trace/src/exporters/common/url.js +15 -1
  141. package/packages/dd-trace/src/feature-registry.js +10 -3
  142. package/packages/dd-trace/src/llmobs/experiments/client.js +113 -0
  143. package/packages/dd-trace/src/llmobs/experiments/dataset.js +154 -0
  144. package/packages/dd-trace/src/llmobs/experiments/experiment.js +283 -0
  145. package/packages/dd-trace/src/llmobs/experiments/index.js +152 -0
  146. package/packages/dd-trace/src/llmobs/experiments/noop.js +30 -0
  147. package/packages/dd-trace/src/llmobs/experiments/result.js +34 -0
  148. package/packages/dd-trace/src/llmobs/noop.js +6 -0
  149. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +20 -3
  150. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +2 -2
  151. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  152. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +8 -0
  153. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +48 -14
  154. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +43 -0
  155. package/packages/dd-trace/src/llmobs/sdk.js +17 -0
  156. package/packages/dd-trace/src/llmobs/tagger.js +68 -0
  157. package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
  158. package/packages/dd-trace/src/llmobs/util.js +32 -0
  159. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +322 -0
  160. package/packages/dd-trace/src/openfeature/configuration_source.js +90 -0
  161. package/packages/dd-trace/src/openfeature/flagging_provider.js +14 -23
  162. package/packages/dd-trace/src/openfeature/index.js +0 -2
  163. package/packages/dd-trace/src/openfeature/noop.js +1 -28
  164. package/packages/dd-trace/src/openfeature/register.js +16 -27
  165. package/packages/dd-trace/src/openfeature/remote_config.js +15 -18
  166. package/packages/dd-trace/src/openfeature/require-provider.js +18 -0
  167. package/packages/dd-trace/src/opentelemetry/span-ending-hook.js +10 -0
  168. package/packages/dd-trace/src/opentelemetry/span.js +5 -0
  169. package/packages/dd-trace/src/plugins/ci_plugin.js +20 -6
  170. package/packages/dd-trace/src/plugins/util/git.js +3 -2
  171. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +29 -5
  172. package/packages/dd-trace/src/plugins/util/test.js +56 -4
  173. package/packages/dd-trace/src/profiling/profilers/space.js +1 -1
  174. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -0
  175. package/packages/dd-trace/src/proxy.js +84 -7
  176. package/packages/dd-trace/src/span_processor.js +5 -0
  177. package/packages/dd-trace/src/standalone/index.js +0 -14
  178. package/packages/dd-trace/src/telemetry/send-data.js +2 -2
  179. package/packages/dd-trace/src/util.js +26 -0
  180. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  181. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  182. package/vendor/dist/protobufjs/index.js +1 -1
  183. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  184. package/vendor/dist/shell-quote/index.js +1 -1
  185. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +0 -17
@@ -0,0 +1,218 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+
5
+ const {
6
+ discoverScenarioTests,
7
+ discoveryEvidence,
8
+ error,
9
+ failWithDebugRerun,
10
+ pass,
11
+ prepareGeneratedScenario,
12
+ requireGeneratedScenario,
13
+ runInstrumentedCommand,
14
+ testEventSamples,
15
+ testsForDiscoveredScenario,
16
+ } = require('./helpers')
17
+
18
+ async function runTestManagement ({ framework, out, options }) {
19
+ const scenarioName = 'test-management'
20
+ const skipResult = requireGeneratedScenario(framework, 'test-management-target', scenarioName)
21
+ if (skipResult) return skipResult
22
+
23
+ let outDir
24
+ try {
25
+ const { scenario } = await prepareGeneratedScenario(framework, 'test-management-target')
26
+ const discovery = await discoverScenarioTests({ framework, out, scenarioName, scenario, options })
27
+ if (discovery.tests.length === 0) {
28
+ return failWithDebugRerun({
29
+ command: scenario.runCommand,
30
+ diagnosis: 'The test-management target was not reported during baseline identity discovery.',
31
+ evidence: discoveryEvidence(discovery),
32
+ framework,
33
+ options,
34
+ out,
35
+ outDir: discovery.outDir,
36
+ scenarioName,
37
+ })
38
+ }
39
+
40
+ const testManagementTests = buildQuarantinedResponse(framework, scenario, discovery.testIdentities)
41
+ const fixtureConfig = {
42
+ settings: {
43
+ test_management: {
44
+ enabled: true,
45
+ attempt_to_fix_retries: 2,
46
+ },
47
+ },
48
+ testManagementTests,
49
+ }
50
+
51
+ const run = await runInstrumentedCommand({
52
+ framework,
53
+ out,
54
+ scenarioName,
55
+ command: scenario.runCommand,
56
+ options,
57
+ fixtureConfig,
58
+ })
59
+ outDir = run.outDir
60
+
61
+ const tests = testsForDiscoveredScenario(run.events, scenario, discovery)
62
+ const quarantinedTests = tests.filter(test => test.isQuarantined)
63
+ const evidence = {
64
+ ...discoveryEvidence(discovery),
65
+ commandExitCode: run.result.exitCode,
66
+ commandTimedOut: run.result.timedOut,
67
+ settingsLoadedFromCache: run.offline.inputs.settings?.status === 'loaded',
68
+ testManagementLoadedFromCache: run.offline.inputs.test_management?.status === 'loaded',
69
+ configuredManagedTests: summarizeManagedTests(testManagementTests),
70
+ matchingTestEvents: tests.length,
71
+ quarantinedEvents: quarantinedTests.length,
72
+ samples: testEventSamples(tests),
73
+ }
74
+
75
+ if (run.result.exitCode !== 0) {
76
+ return failWithDebugRerun({
77
+ command: scenario.runCommand,
78
+ fixtureConfig,
79
+ diagnosis: 'The generated Test Management command reported quarantined-test evidence, but the command ' +
80
+ `exited ${run.result.exitCode}. Test Management is only valid when the command completes successfully ` +
81
+ 'with the managed test applied.',
82
+ evidence,
83
+ framework,
84
+ options,
85
+ out,
86
+ outDir,
87
+ scenarioName,
88
+ })
89
+ }
90
+
91
+ if (!evidence.settingsLoadedFromCache || !evidence.testManagementLoadedFromCache) {
92
+ return failWithDebugRerun({
93
+ command: scenario.runCommand,
94
+ fixtureConfig,
95
+ diagnosis: 'Test Management settings or managed-test data were not loaded from the offline cache fixture.',
96
+ evidence,
97
+ framework,
98
+ options,
99
+ out,
100
+ outDir,
101
+ scenarioName,
102
+ })
103
+ }
104
+
105
+ if (tests.length === 0) {
106
+ return failWithDebugRerun({
107
+ command: scenario.runCommand,
108
+ fixtureConfig,
109
+ diagnosis: 'The test-management target test was not reported.',
110
+ evidence,
111
+ framework,
112
+ options,
113
+ out,
114
+ outDir,
115
+ scenarioName,
116
+ })
117
+ }
118
+
119
+ if (quarantinedTests.length === 0) {
120
+ return failWithDebugRerun({
121
+ command: scenario.runCommand,
122
+ fixtureConfig,
123
+ diagnosis: 'Test Management was enabled, but the generated target was not tagged as quarantined.',
124
+ evidence,
125
+ framework,
126
+ options,
127
+ out,
128
+ outDir,
129
+ scenarioName,
130
+ })
131
+ }
132
+
133
+ return pass(
134
+ framework,
135
+ scenarioName,
136
+ 'The generated target test was matched by Test Management and tagged as quarantined.',
137
+ evidence,
138
+ outDir
139
+ )
140
+ } catch (err) {
141
+ return error(framework, scenarioName, err, outDir)
142
+ }
143
+ }
144
+
145
+ function buildQuarantinedResponse (framework, scenario, discoveredIdentities = []) {
146
+ const suites = {}
147
+ const identities = [...(scenario.testIdentities || []), ...discoveredIdentities]
148
+ for (const identity of identities) {
149
+ for (const suite of getSuiteCandidates(identity, scenario)) {
150
+ for (const name of getNameCandidates(identity)) {
151
+ suites[suite] = suites[suite] || { tests: {} }
152
+ suites[suite].tests[name] = {
153
+ properties: {
154
+ quarantined: true,
155
+ },
156
+ }
157
+ }
158
+ }
159
+ }
160
+
161
+ return {
162
+ [framework.framework]: {
163
+ suites,
164
+ },
165
+ }
166
+ }
167
+
168
+ function getSuiteCandidates (identity, scenario) {
169
+ const candidates = new Set()
170
+ addCandidate(candidates, identity.suite)
171
+ addCandidate(candidates, identity.file)
172
+
173
+ if (identity.file) {
174
+ addCandidate(candidates, normalizePath(path.basename(identity.file)))
175
+ if (scenario.runCommand?.cwd) {
176
+ addCandidate(candidates, normalizePath(path.relative(scenario.runCommand.cwd, identity.file)))
177
+ }
178
+ }
179
+
180
+ return [...candidates]
181
+ }
182
+
183
+ function getNameCandidates (identity) {
184
+ const candidates = new Set()
185
+ addCandidate(candidates, identity.name)
186
+ if (!identity.discovered && identity.suite && identity.name && !identity.name.startsWith(`${identity.suite} `)) {
187
+ addCandidate(candidates, `${identity.suite} ${identity.name}`)
188
+ }
189
+ return [...candidates]
190
+ }
191
+
192
+ function addCandidate (candidates, value) {
193
+ if (value) candidates.add(normalizePath(value))
194
+ }
195
+
196
+ function normalizePath (value) {
197
+ return value.replaceAll(path.sep, '/')
198
+ }
199
+
200
+ function summarizeManagedTests (testManagementTests) {
201
+ const managed = testManagementTests && testManagementTests[Object.keys(testManagementTests)[0]]
202
+ const suites = managed?.suites || {}
203
+ const summary = new Map()
204
+ for (const [suite, { tests = {} }] of Object.entries(suites)) {
205
+ const displaySuite = path.isAbsolute(suite) ? path.basename(suite) : suite
206
+ const testNames = summary.get(displaySuite) || new Set()
207
+ for (const testName of Object.keys(tests)) {
208
+ testNames.add(testName)
209
+ }
210
+ summary.set(displaySuite, testNames)
211
+ }
212
+ return [...summary.entries()].slice(0, 5).map(([suite, tests]) => ({
213
+ suite,
214
+ tests: [...tests].slice(0, 5),
215
+ }))
216
+ }
217
+
218
+ module.exports = { runTestManagement, buildQuarantinedResponse }
@@ -0,0 +1,97 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+
5
+ const { getArtifactId } = require('./artifact-id')
6
+ const { runCommand } = require('./command-runner')
7
+
8
+ async function runSetupCommands ({ framework, out, options }) {
9
+ const commands = framework.setup?.commands || []
10
+ const results = []
11
+ const artifacts = []
12
+
13
+ for (let index = 0; index < commands.length; index++) {
14
+ const command = commands[index]
15
+ const outDir = path.join(
16
+ out,
17
+ 'setup',
18
+ getArtifactId(framework.id),
19
+ `${index + 1}-${getArtifactId(command.id || 'setup')}`
20
+ )
21
+ // eslint-disable-next-line no-await-in-loop
22
+ const result = await runCommand(command, {
23
+ artifactRoot: out,
24
+ envMode: 'clean',
25
+ outDir,
26
+ label: `${framework.id}:setup:${command.id || index + 1}`,
27
+ repositoryRoot: options.repositoryRoot,
28
+ requireExecutableApproval: options.requireExecutableApproval,
29
+ verbose: options.verbose,
30
+ })
31
+ const summary = summarizeSetupCommand(command, result, outDir)
32
+ results.push(summary)
33
+ artifacts.push(...Object.values(result.artifacts))
34
+
35
+ if (command.required !== false && result.exitCode !== 0) {
36
+ const failure = getSetupFailure(framework, command, result, results)
37
+ failure.artifacts.push(...artifacts)
38
+ return {
39
+ ok: false,
40
+ results,
41
+ artifacts,
42
+ failure,
43
+ }
44
+ }
45
+ }
46
+
47
+ return { ok: true, results, artifacts }
48
+ }
49
+
50
+ function getSetupFailure (framework, command, result, setupCommands) {
51
+ const setupName = command.description || command.id || result.command
52
+
53
+ return {
54
+ frameworkId: framework.id,
55
+ scenario: 'all',
56
+ status: 'blocked',
57
+ diagnosis: `Validation is blocked by required project setup: ${setupName}. ` +
58
+ 'No Test Optimization conclusion was reached for this framework.',
59
+ evidence: {
60
+ blockedByProjectSetup: true,
61
+ setupFailed: true,
62
+ setupCommand: {
63
+ id: command.id,
64
+ description: command.description,
65
+ command: result.command,
66
+ cwd: result.cwd,
67
+ exitCode: result.exitCode,
68
+ timedOut: result.timedOut,
69
+ stdoutSummary: tail(result.stdout),
70
+ stderrSummary: tail(result.stderr),
71
+ },
72
+ setupCommands,
73
+ recommendation: 'Run or fix the documented project setup command, then rerun validation for this framework.',
74
+ },
75
+ artifacts: [],
76
+ }
77
+ }
78
+
79
+ function summarizeSetupCommand (command, result, outDir) {
80
+ return {
81
+ id: command.id,
82
+ description: command.description,
83
+ required: command.required !== false,
84
+ command: result.command,
85
+ cwd: result.cwd,
86
+ exitCode: result.exitCode,
87
+ timedOut: result.timedOut,
88
+ durationMs: result.durationMs,
89
+ artifactDirectory: outDir,
90
+ }
91
+ }
92
+
93
+ function tail (value) {
94
+ return String(value || '').trim().split(/\r?\n/).slice(-20).join('\n')
95
+ }
96
+
97
+ module.exports = { runSetupCommands }
@@ -0,0 +1,159 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+
5
+ const satisfies = require('../../vendor/dist/semifies')
6
+ const { DD_MAJOR } = require('../../version')
7
+ const {
8
+ getFrameworkDefinitions,
9
+ runDiagnosis,
10
+ } = require('../diagnose')
11
+ const { sanitizeForReport } = require('./redaction')
12
+ const { writeFileSafely } = require('./safe-files')
13
+
14
+ const SUPPORTED_FRAMEWORKS = new Set([
15
+ 'jest',
16
+ 'mocha',
17
+ 'cucumber',
18
+ 'cypress',
19
+ 'playwright',
20
+ 'vitest',
21
+ ])
22
+
23
+ const UNSUPPORTED_FRAMEWORK_NAMES = {
24
+ ava: 'AVA',
25
+ jasmine: 'Jasmine',
26
+ karma: 'Karma',
27
+ 'node:test': 'Node.js test runner',
28
+ tap: 'tap',
29
+ testcafe: 'TestCafe',
30
+ uvu: 'uvu',
31
+ }
32
+
33
+ function runStaticDiagnosis ({ manifest, out }) {
34
+ const report = runDiagnosis({ root: manifest.repository.root, excludePaths: [out] })
35
+ const reportPath = path.join(out, 'static-diagnosis.json')
36
+ writeFileSafely(
37
+ out,
38
+ reportPath,
39
+ `${JSON.stringify(sanitizeForReport(report), null, 2)}\n`,
40
+ 'static diagnosis artifact'
41
+ )
42
+ return { report, reportPath }
43
+ }
44
+
45
+ function getStaticBlocker (framework, diagnosis) {
46
+ if (!SUPPORTED_FRAMEWORKS.has(framework.framework)) {
47
+ return {
48
+ reason: `Unsupported test framework detected: ${getUnsupportedFrameworkName(framework)}.`,
49
+ recommendation: 'Choose Jest, Mocha, Cucumber, Cypress, Playwright, or Vitest for live validation.',
50
+ }
51
+ }
52
+
53
+ const definition = getFrameworkDefinition(framework, diagnosis)
54
+ if (!definition) return null
55
+
56
+ const version = parseVersion(framework.frameworkVersion)
57
+ if (version && !satisfies(version, definition.supportedRange)) {
58
+ return {
59
+ reason:
60
+ `${definition.name} ${version} is not supported. Supported range is ${definition.supportedRange}.`,
61
+ recommendation: definition.recommendation,
62
+ }
63
+ }
64
+
65
+ const staticVersionError = findStaticVersionError(definition, diagnosis, framework, version)
66
+ if (staticVersionError) {
67
+ return {
68
+ reason: staticVersionError.title,
69
+ recommendation: staticVersionError.recommendation || staticVersionError.message,
70
+ }
71
+ }
72
+
73
+ return null
74
+ }
75
+
76
+ function getFrameworkDefinition (framework, diagnosis) {
77
+ const definitions = getFrameworkDefinitions(diagnosis.ddTraceMajor || DD_MAJOR)
78
+ return definitions.find(definition => definition.id === framework.framework)
79
+ }
80
+
81
+ function getUnsupportedFrameworkName (framework) {
82
+ return UNSUPPORTED_FRAMEWORK_NAMES[framework.framework] || framework.framework || framework.id
83
+ }
84
+
85
+ function findStaticVersionError (definition, diagnosis, framework, manifestVersion) {
86
+ const results = Array.isArray(diagnosis.results) ? diagnosis.results : []
87
+ return results.find(result => {
88
+ return result.status === 'error' &&
89
+ typeof result.title === 'string' &&
90
+ result.title.startsWith(`${definition.name} `) &&
91
+ result.title.includes(' is not supported') &&
92
+ staticVersionErrorAppliesToFramework(result, diagnosis, framework, definition, manifestVersion)
93
+ })
94
+ }
95
+
96
+ function staticVersionErrorAppliesToFramework (result, diagnosis, framework, definition, manifestVersion) {
97
+ const locations = Array.isArray(result.locations) ? result.locations : []
98
+ if (locations.length === 0) {
99
+ return !(manifestVersion && satisfies(manifestVersion, definition.supportedRange))
100
+ }
101
+
102
+ return locations.some(location => locationMatchesFramework(location, diagnosis, framework))
103
+ }
104
+
105
+ function locationMatchesFramework (location, diagnosis, framework) {
106
+ const relativeLocation = normalizeRelativePath(location)
107
+ const exactLocations = getExactFrameworkLocations(diagnosis, framework)
108
+
109
+ if (exactLocations.has(relativeLocation)) return true
110
+
111
+ const projectRoot = getRelativeFrameworkProjectRoot(diagnosis, framework)
112
+ return projectRoot !== '' &&
113
+ (relativeLocation === projectRoot || relativeLocation.startsWith(`${projectRoot}/`))
114
+ }
115
+
116
+ function getExactFrameworkLocations (diagnosis, framework) {
117
+ const locations = new Set()
118
+ addRelativeFrameworkLocation(locations, diagnosis, framework.project?.packageJson)
119
+
120
+ for (const configFile of framework.project?.configFiles || []) {
121
+ addRelativeFrameworkLocation(locations, diagnosis, configFile)
122
+ }
123
+
124
+ return locations
125
+ }
126
+
127
+ function addRelativeFrameworkLocation (locations, diagnosis, location) {
128
+ if (typeof location !== 'string' || location.length === 0) return
129
+ locations.add(getRelativeLocation(diagnosis, location))
130
+ }
131
+
132
+ function getRelativeFrameworkProjectRoot (diagnosis, framework) {
133
+ return getRelativeLocation(diagnosis, framework.project?.root)
134
+ }
135
+
136
+ function getRelativeLocation (diagnosis, location) {
137
+ if (typeof location !== 'string' || location.length === 0) return ''
138
+ const root = typeof diagnosis.root === 'string' ? diagnosis.root : ''
139
+ const relativeLocation = path.isAbsolute(location) && root
140
+ ? path.relative(root, location)
141
+ : location
142
+
143
+ return normalizeRelativePath(relativeLocation)
144
+ }
145
+
146
+ function normalizeRelativePath (location) {
147
+ return location.split(path.sep).join('/').replace(/^\.\//, '')
148
+ }
149
+
150
+ function parseVersion (rawVersion) {
151
+ if (typeof rawVersion !== 'string') return null
152
+ const match = rawVersion.match(/\d+\.\d+\.\d+/)
153
+ return match ? match[0] : null
154
+ }
155
+
156
+ module.exports = {
157
+ getStaticBlocker,
158
+ runStaticDiagnosis,
159
+ }
@@ -0,0 +1,129 @@
1
+ 'use strict'
2
+
3
+ const ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}${String.raw`\[[0-?]*[ -/]*[@-~]`}`, 'g')
4
+
5
+ /**
6
+ * Extracts the final test count from common JavaScript test-runner summaries.
7
+ *
8
+ * @param {string} framework test framework name
9
+ * @param {string} stdout captured stdout
10
+ * @param {string} stderr captured stderr
11
+ * @returns {number|null} observed count when a supported summary was found
12
+ */
13
+ function getObservedTestCount (framework, stdout = '', stderr = '') {
14
+ const output = `${stdout}\n${stderr}`.replaceAll(ANSI_PATTERN, '')
15
+ if (framework === 'jest') return getJestObservedTestCount(output)
16
+ if (framework === 'vitest') return getVitestObservedTestCount(output)
17
+ if (framework === 'playwright') return getPlaywrightObservedTestCount(output)
18
+
19
+ const totalPatterns = framework === 'node:test'
20
+ ? [/^# tests\s+(\d+)\s*$/gim]
21
+ : framework === 'cucumber'
22
+ ? [/\b(\d+)\s+scenarios?\b/gi]
23
+ : []
24
+
25
+ for (const pattern of totalPatterns) {
26
+ const count = getLastMatchCount(output, pattern)
27
+ if (count !== null) return count
28
+ }
29
+
30
+ if (framework === 'mocha') {
31
+ return sumLastMatchCounts(output, [
32
+ /\b(\d+)\s+passing\b/gi,
33
+ /\b(\d+)\s+failing\b/gi,
34
+ /\b(\d+)\s+pending\b/gi,
35
+ ])
36
+ }
37
+
38
+ return getLastMatchCount(output, /\b(\d+)\s+tests?\s+(?:passed|failed)\b/gi)
39
+ }
40
+
41
+ /**
42
+ * Counts Playwright tests that completed instead of treating skipped tests as executions.
43
+ *
44
+ * @param {string} output test output without ANSI codes
45
+ * @returns {number|null} executed test count
46
+ */
47
+ function getPlaywrightObservedTestCount (output) {
48
+ const observed = sumLastMatchCounts(output, [
49
+ /^\s*(\d+)\s+passed\b/gim,
50
+ /^\s*(\d+)\s+failed\b/gim,
51
+ /^\s*(\d+)\s+flaky\b/gim,
52
+ ])
53
+ if (observed !== null) return observed
54
+ return /^\s*\d+\s+skipped\b/im.test(output) ? 0 : null
55
+ }
56
+
57
+ /**
58
+ * Counts Jest tests that actually ran, excluding tests skipped by a name filter.
59
+ *
60
+ * @param {string} output test output without ANSI codes
61
+ * @returns {number|null} executed test count
62
+ */
63
+ function getJestObservedTestCount (output) {
64
+ return getExecutedTestSummaryCount(output, /^\s*Tests:\s+/)
65
+ }
66
+
67
+ /**
68
+ * Counts Vitest tests that actually ran, excluding tests skipped by a name filter.
69
+ *
70
+ * @param {string} output test output without ANSI codes
71
+ * @returns {number|null} executed test count
72
+ */
73
+ function getVitestObservedTestCount (output) {
74
+ return getExecutedTestSummaryCount(output, /^\s*Tests\s+/)
75
+ }
76
+
77
+ /**
78
+ * Extracts passed and failed counts from the final matching runner summary.
79
+ *
80
+ * @param {string} output test output without ANSI codes
81
+ * @param {RegExp} summaryPattern test-summary line pattern
82
+ * @returns {number|null} executed test count
83
+ */
84
+ function getExecutedTestSummaryCount (output, summaryPattern) {
85
+ const summaryLines = output.split(/\r?\n/).filter(line => summaryPattern.test(line))
86
+ const summary = summaryLines.at(-1)
87
+ if (!summary) return null
88
+
89
+ const observed = sumLastMatchCounts(summary, [
90
+ /\b(\d+)\s+passed\b/gi,
91
+ /\b(\d+)\s+failed\b/gi,
92
+ ])
93
+ if (observed !== null) return observed
94
+ return /\b\d+\s+skipped\b/i.test(summary) ? 0 : null
95
+ }
96
+
97
+ /**
98
+ * Returns the count captured by the last match of one summary pattern.
99
+ *
100
+ * @param {string} output test output
101
+ * @param {RegExp} pattern global summary pattern
102
+ * @returns {number|null} final captured count
103
+ */
104
+ function getLastMatchCount (output, pattern) {
105
+ let count = null
106
+ for (const match of output.matchAll(pattern)) count = Number(match[1])
107
+ return count
108
+ }
109
+
110
+ /**
111
+ * Sums the final counts from multiple summary categories.
112
+ *
113
+ * @param {string} output test output
114
+ * @param {RegExp[]} patterns global summary patterns
115
+ * @returns {number|null} summed count when any category was found
116
+ */
117
+ function sumLastMatchCounts (output, patterns) {
118
+ let found = false
119
+ let count = 0
120
+ for (const pattern of patterns) {
121
+ const value = getLastMatchCount(output, pattern)
122
+ if (value === null) continue
123
+ found = true
124
+ count += value
125
+ }
126
+ return found ? count : null
127
+ }
128
+
129
+ module.exports = { getObservedTestCount }
@@ -0,0 +1 @@
1
+ {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://github.com/DataDog/dd-trace-js/ci/test-optimization-validation-manifest.schema.json","title":"Datadog Test Optimization validation manifest","type":"object","required":["schemaVersion","repository","environment","frameworks"],"properties":{"schemaVersion":{"type":"string"},"generatedAt":{"type":"string"},"repository":{"$ref":"#/$defs/repository"},"environment":{"$ref":"#/$defs/environment"},"ciDiscovery":{"$ref":"#/$defs/ciDiscovery"},"frameworks":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/framework"}},"omitted":{"type":"array","items":{"type":"string"}},"warnings":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"$defs":{"repository":{"type":"object","required":["root"],"properties":{"root":{"$ref":"#/$defs/absolutePathString"},"gitRemote":{"type":["string","null"]},"gitSha":{"type":["string","null"]},"packageManager":{"enum":["npm","yarn","pnpm","bun","mixed","unknown"]},"workspaceManager":{"enum":["npm","yarn","pnpm","lerna","nx","turbo","rush","none","unknown"]}},"additionalProperties":true},"environment":{"type":"object","properties":{"os":{"enum":["darwin","linux","windows","unknown"]},"shell":{"type":["string","null"],"minLength":1,"pattern":"^[^\\u0000-\\u001F\\u007F-\\u009F\\u00AD\\u0600-\\u0605\\u061C\\u06DD\\u070F\\u0890\\u0891\\u08E2\\u180B-\\u180F\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u206F\\uFE00-\\uFE0F\\uFEFF\\uFFF9-\\uFFFB]*$"},"nodeVersion":{"type":["string","null"]},"requiredEnvVars":{"type":"array","items":{"type":"string"}},"safeEnv":{"type":"object"}},"additionalProperties":true},"ciDiscovery":{"type":"object","properties":{"searched":{"type":"array","items":{"type":"string"}},"found":{"type":"array","items":{"type":"string"}},"staticFound":{"type":"array","items":{"type":"string"}},"method":{"type":"string"},"warnings":{"type":"array","items":{"type":"string"}},"notes":{"type":"array","items":{"type":"string"}},"contradictions":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"framework":{"type":"object","required":["id","framework","status","project"],"properties":{"id":{"type":"string"},"framework":{"enum":["jest","vitest","mocha","cucumber","cypress","playwright","node:test","ava","tap","jasmine","karma","uvu","testcafe","custom","unknown"]},"frameworkVersion":{"type":["string","null"]},"language":{"enum":["javascript","typescript","mixed","unknown"]},"status":{"enum":["runnable","detected_not_runnable","requires_external_service","requires_manual_setup","unsupported_by_validator","unknown"]},"supportLevel":{"enum":["validator_supported","dd_trace_supported_but_validator_missing_adapter","detected_only","unknown"]},"project":{"$ref":"#/$defs/project"},"setup":{"$ref":"#/$defs/setup"},"existingTestCommand":{"$ref":"#/$defs/command"},"ciWiring":{"$ref":"#/$defs/ciWiring"},"ciWiringCommand":{"$ref":"#/$defs/command"},"forcedLocalCommand":false,"preflight":{"$ref":"#/$defs/preflight"},"generatedTestStrategy":{"$ref":"#/$defs/generatedTestStrategy"},"notes":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"allOf":[{"if":{"properties":{"status":{"const":"runnable"}}},"then":{"required":["existingTestCommand","preflight","ciWiring"]}},{"if":{"properties":{"status":{"enum":["detected_not_runnable","requires_external_service","requires_manual_setup","unsupported_by_validator","unknown"]}}},"then":{"required":["notes"],"properties":{"notes":{"minItems":1}}}},{"if":{"required":["ciWiringCommand"]},"then":{"properties":{"ciWiring":{"required":["provider","configFile","job","step","workingDirectory","whySelected"]}}}},{"if":{"required":["ciWiring"],"properties":{"ciWiring":{"required":["replayability"],"properties":{"replayability":{"const":"replayable"}}}}},"then":{"required":["ciWiringCommand"]}},{"if":{"required":["ciWiring"],"properties":{"ciWiring":{"required":["replayability"],"properties":{"replayability":{"const":"not_replayable"}}}}},"then":{"not":{"required":["ciWiringCommand"]}}}]},"project":{"type":"object","required":["root"],"properties":{"name":{"type":["string","null"]},"root":{"$ref":"#/$defs/absolutePathString"},"packageJson":{"anyOf":[{"$ref":"#/$defs/absolutePathString"},{"type":"null"}]},"configFiles":{"type":"array","items":{"$ref":"#/$defs/absolutePathString"}},"evidence":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"setup":{"type":"object","properties":{"commands":{"type":"array","items":{"$ref":"#/$defs/setupCommand"}},"services":{"type":"array","items":{"$ref":"#/$defs/service"}}},"additionalProperties":true},"setupCommand":{"allOf":[{"$ref":"#/$defs/command"},{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"},"required":{"type":"boolean"}}}]},"service":{"type":"object","properties":{"name":{"type":"string"},"required":{"type":"boolean"},"description":{"type":"string"}},"additionalProperties":true},"command":{"type":"object","required":["cwd"],"properties":{"description":{"type":"string"},"id":{"type":"string"},"required":{"type":"boolean"},"cwd":{"$ref":"#/$defs/absolutePathString"},"argv":{"type":"array","items":{"$ref":"#/$defs/executableString"}},"env":{"type":"object","propertyNames":{"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"additionalProperties":{"$ref":"#/$defs/executableString"}},"requiredEnvVars":{"type":"array","items":{"type":"string"}},"outputPaths":{"type":"array","items":{"$ref":"#/$defs/absolutePathString"}},"timeoutMs":{"type":"number","exclusiveMinimum":0,"maximum":1800000},"usesShell":{"type":"boolean"},"shell":{"$ref":"#/$defs/executableString"},"shellCommand":{"anyOf":[{"$ref":"#/$defs/executableString"},{"type":"null"}]},"shellReason":{"type":["string","null"]}},"additionalProperties":false,"allOf":[{"if":{"required":["usesShell"],"properties":{"usesShell":{"const":true}}},"then":{"required":["shellCommand"]}},{"if":{"required":["shell"]},"then":{"required":["usesShell"],"properties":{"usesShell":{"const":true}}}}],"anyOf":[{"required":["argv"]},{"properties":{"usesShell":{"const":true}},"required":["usesShell","shellCommand"]}]},"executableString":{"type":"string","not":{"pattern":"\\$\\{[^}]+\\}"}},"absolutePathString":{"allOf":[{"$ref":"#/$defs/executableString"},{"pattern":"^(?:/|[A-Za-z]:[\\\\/]|\\\\\\\\)"}]},"preflight":{"type":"object","properties":{"status":{"enum":["pending","verified"]},"ran":{"type":"boolean"},"command":{"type":"string"},"exitCode":{"type":["number","null"]},"durationMs":{"type":["number","null"]},"observedTestCount":{"type":["number","null"]},"stdoutSummary":{"type":"string"},"stderrSummary":{"type":"string"},"maxTestCount":{"type":"integer","minimum":1,"maximum":1000}},"additionalProperties":true,"required":["maxTestCount"]},"ciWiring":{"type":"object","required":["status","replayability"],"properties":{"status":{"enum":["pass","fail","skip","unknown"]},"provider":{"type":"string"},"configFile":{"$ref":"#/$defs/absolutePathString"},"workflow":{"type":["string","null"]},"job":{"type":["string","null"]},"step":{"type":["string","null"]},"matrix":{"type":"object"},"runner":{"type":["string","null"]},"shell":{"type":["string","null"],"minLength":1,"pattern":"^[^\\u0000-\\u001F\\u007F-\\u009F\\u00AD\\u0600-\\u0605\\u061C\\u06DD\\u070F\\u0890\\u0891\\u08E2\\u180B-\\u180F\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u206F\\uFE00-\\uFE0F\\uFEFF\\uFFF9-\\uFFFB]*$"},"workingDirectory":{"anyOf":[{"$ref":"#/$defs/absolutePathString"},{"type":"null"}]},"inheritedEnv":{"type":"object"},"requiredSecretEnvVars":{"type":"array","items":{"type":"string"}},"setupCommandIds":{"type":"array","items":{"type":"string"}},"whySelected":{"type":"string"},"workflowEnv":{"type":"object"},"jobEnv":{"type":"object"},"stepEnv":{"type":"object"},"initialization":{"type":"object","required":["status","evidence"],"properties":{"status":{"enum":["configured","not_configured","unknown"]},"evidence":{"type":"array","items":{"type":"string"}}},"allOf":[{"if":{"properties":{"status":{"enum":["configured","not_configured"]}},"required":["status"]},"then":{"properties":{"evidence":{"minItems":1}}}}],"additionalProperties":false},"packageScriptExpansionChain":{"type":"array","items":{"type":"string"}},"runnerToolChain":{"type":"array","items":{"type":"string"}},"unresolved":{"type":"array","items":{"type":"string"}},"diagnosis":{"type":"string"},"reason":{"type":"string"},"replayability":{"enum":["replayable","not_replayable"]},"replayBlocker":{"type":"string","minLength":1},"ciWiringCommand":false},"additionalProperties":true,"allOf":[{"if":{"properties":{"status":{"enum":["skip","unknown"]}}},"then":{"anyOf":[{"required":["diagnosis"]},{"required":["reason"]}]}},{"if":{"properties":{"replayability":{"const":"not_replayable"}},"required":["replayability"]},"then":{"required":["replayBlocker"],"properties":{"status":{"enum":["skip","unknown"]}}}}]},"generatedTestStrategy":{"type":"object","required":["status"],"properties":{"status":{"enum":["planned","verified","proposed","not_possible"]},"reason":{"type":["string","null"]},"adapter":{"enum":["jest","vitest","mocha","cucumber","cypress","playwright","node:test","generic-js","custom","unknown"]},"testDirectory":{"anyOf":[{"$ref":"#/$defs/absolutePathString"},{"type":"null"}]},"moduleSystem":{"enum":["commonjs","esm","typescript","unknown"]},"fileExtension":{"type":"string"},"supportsFocusedSingleFileRun":{"type":"boolean"},"usesMultipleFiles":{"type":"boolean"},"files":{"type":"array","maxItems":8,"items":{"$ref":"#/$defs/generatedFile"}},"scenarios":{"type":"array","items":{"$ref":"#/$defs/scenario"}},"verification":{"$ref":"#/$defs/verification"},"cleanupPaths":{"type":"array","items":{"$ref":"#/$defs/absolutePathString"}},"limitations":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"allOf":[{"if":{"properties":{"status":{"enum":["planned","verified"]}}},"then":{"required":["files","scenarios","cleanupPaths"],"properties":{"scenarios":{"allOf":[{"items":{"required":["testIdentities","expectedWithoutDatadog"],"properties":{"testIdentities":{"minItems":1}}}},{"contains":{"properties":{"id":{"const":"basic-pass"}},"required":["id"]}},{"contains":{"properties":{"id":{"const":"atr-fail-once"}},"required":["id"]}},{"contains":{"properties":{"id":{"const":"test-management-target"}},"required":["id"]}}]}}}}]},"generatedFile":{"type":"object","required":["path","contentLines"],"properties":{"path":{"$ref":"#/$defs/absolutePathString"},"role":{"enum":["test","feature","steps","support","state","config"]},"contentLines":{"type":"array","maxItems":256,"items":{"type":"string","maxLength":4096,"pattern":"^[^\\r\\n\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F\\u061C\\u180B-\\u180F\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u206F\\uFE00-\\uFE0F\\uFEFF]*$"}}},"additionalProperties":true},"scenario":{"type":"object","required":["id","runCommand"],"properties":{"id":{"type":"string"},"purpose":{"type":"string"},"runCommand":{"$ref":"#/$defs/command"},"expectedWithoutDatadog":{"$ref":"#/$defs/expectedWithoutDatadog"},"testIdentities":{"type":"array","items":{"$ref":"#/$defs/testIdentity"}}},"additionalProperties":true,"allOf":[{"if":{"required":["id"],"properties":{"id":{"const":"basic-pass"}}},"then":{"properties":{"expectedWithoutDatadog":{"properties":{"exitCode":{"const":0},"observedTestCount":{"const":1}}}}}},{"if":{"required":["id"],"properties":{"id":{"const":"atr-fail-once"}}},"then":{"properties":{"expectedWithoutDatadog":{"properties":{"exitCode":{"const":1},"observedTestCount":{"const":1}}}}}},{"if":{"required":["id"],"properties":{"id":{"const":"test-management-target"}}},"then":{"properties":{"expectedWithoutDatadog":{"properties":{"exitCode":{"const":0},"observedTestCount":{"const":1}}}}}}]},"expectedWithoutDatadog":{"type":"object","required":["exitCode","observedTestCount"],"properties":{"exitCode":{"type":"number"},"observedTestCount":{"type":"number"}},"additionalProperties":true},"testIdentity":{"type":"object","required":["name"],"properties":{"suite":{"type":["string","null"]},"name":{"type":"string","minLength":1},"file":{"anyOf":[{"$ref":"#/$defs/absolutePathString"},{"type":"null"}]},"parameters":{"type":["object","null"]}},"additionalProperties":true},"verification":{"type":"object","properties":{"createdTemporaryFiles":{"type":"boolean"},"ranScenarioIds":{"type":"array","items":{"type":"string"}},"exitCode":{"type":["number","null"]},"durationMs":{"type":["number","null"]},"observedTestCount":{"type":["number","null"]},"cleanupCompleted":{"type":"boolean"},"stdoutSummary":{"type":"string"},"stderrSummary":{"type":"string"}},"additionalProperties":true}}}
@@ -0,0 +1,3 @@
1
+ 'use strict'
2
+
3
+ require('./test-optimization-validation/cli').main(process.argv.slice(2))
package/ext/exporters.js CHANGED
@@ -5,6 +5,7 @@ module.exports = {
5
5
  AGENTLESS: 'agentless',
6
6
  DATADOG: 'datadog',
7
7
  AGENT_PROXY: 'agent_proxy',
8
+ CI_VALIDATION: 'ci_validation',
8
9
  JEST_WORKER: 'jest_worker',
9
10
  CUCUMBER_WORKER: 'cucumber_worker',
10
11
  MOCHA_WORKER: 'mocha_worker',