dd-trace 6.4.0 → 6.5.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.
- package/README.electron.md +7 -0
- package/README.md +17 -0
- package/ci/diagnose.js +2100 -0
- package/ci/init.js +23 -2
- package/ci/runbook.md +198 -0
- package/ci/test-optimization-validation/approval-artifacts.js +143 -0
- package/ci/test-optimization-validation/approval.js +299 -0
- package/ci/test-optimization-validation/artifact-id.js +20 -0
- package/ci/test-optimization-validation/bounded-json.js +121 -0
- package/ci/test-optimization-validation/ci-command-candidate.js +83 -0
- package/ci/test-optimization-validation/ci-discovery.js +181 -0
- package/ci/test-optimization-validation/ci-remediation.js +210 -0
- package/ci/test-optimization-validation/cli.js +903 -0
- package/ci/test-optimization-validation/command-blocker.js +81 -0
- package/ci/test-optimization-validation/command-output-policy.js +206 -0
- package/ci/test-optimization-validation/command-runner.js +707 -0
- package/ci/test-optimization-validation/command-suitability.js +471 -0
- package/ci/test-optimization-validation/executable-approval.js +48 -0
- package/ci/test-optimization-validation/executable.js +480 -0
- package/ci/test-optimization-validation/generated-file-policy.js +63 -0
- package/ci/test-optimization-validation/generated-files.js +351 -0
- package/ci/test-optimization-validation/generated-verifier.js +196 -0
- package/ci/test-optimization-validation/init-probe-preload.js +163 -0
- package/ci/test-optimization-validation/init-probe.js +246 -0
- package/ci/test-optimization-validation/late-initialization.js +63 -0
- package/ci/test-optimization-validation/local-command.js +163 -0
- package/ci/test-optimization-validation/manifest-loader.js +133 -0
- package/ci/test-optimization-validation/manifest-scaffold.js +738 -0
- package/ci/test-optimization-validation/manifest-schema.js +862 -0
- package/ci/test-optimization-validation/offline-fixtures.js +327 -0
- package/ci/test-optimization-validation/offline-output.js +406 -0
- package/ci/test-optimization-validation/payload-normalizer.js +72 -0
- package/ci/test-optimization-validation/plan-writer.js +1120 -0
- package/ci/test-optimization-validation/preflight-runner.js +114 -0
- package/ci/test-optimization-validation/redaction.js +331 -0
- package/ci/test-optimization-validation/report-writer.js +1508 -0
- package/ci/test-optimization-validation/safe-files.js +203 -0
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +159 -0
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +657 -0
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +780 -0
- package/ci/test-optimization-validation/scenarios/early-flake-detection.js +141 -0
- package/ci/test-optimization-validation/scenarios/helpers.js +539 -0
- package/ci/test-optimization-validation/scenarios/test-management.js +218 -0
- package/ci/test-optimization-validation/setup-runner.js +97 -0
- package/ci/test-optimization-validation/static-diagnosis.js +159 -0
- package/ci/test-optimization-validation/test-output.js +129 -0
- package/ci/test-optimization-validation-manifest.schema.json +1 -0
- package/ci/validate-test-optimization.js +3 -0
- package/ext/exporters.js +1 -0
- package/index.d.ts +121 -8
- package/init.js +18 -0
- package/initialize.mjs +1 -1
- package/loader-hook.mjs +28 -18
- package/openfeature.d.ts +1 -0
- package/openfeature.js +4 -0
- package/package.json +10 -7
- package/packages/datadog-instrumentations/src/ai.js +8 -2
- package/packages/datadog-instrumentations/src/child_process.js +1 -1
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +9 -3
- package/packages/datadog-instrumentations/src/cucumber.js +20 -7
- package/packages/datadog-instrumentations/src/cypress-config.js +398 -52
- package/packages/datadog-instrumentations/src/fastify.js +7 -11
- package/packages/datadog-instrumentations/src/helpers/hook.js +27 -6
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +8 -8
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +28 -2
- package/packages/datadog-instrumentations/src/jest.js +26 -0
- package/packages/datadog-instrumentations/src/mariadb.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +0 -2
- package/packages/datadog-instrumentations/src/mocha/utils.js +0 -4
- package/packages/datadog-instrumentations/src/mongodb.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +3 -3
- package/packages/datadog-instrumentations/src/mquery.js +2 -2
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +24 -0
- package/packages/datadog-instrumentations/src/nyc.js +0 -2
- package/packages/datadog-instrumentations/src/oracledb.js +2 -2
- package/packages/datadog-instrumentations/src/pg.js +2 -2
- package/packages/datadog-instrumentations/src/playwright.js +159 -22
- package/packages/datadog-instrumentations/src/process.js +3 -0
- package/packages/datadog-instrumentations/src/router.js +18 -8
- package/packages/datadog-instrumentations/src/selenium.js +52 -26
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +2 -2
- package/packages/datadog-instrumentations/src/vitest-main.js +0 -2
- package/packages/datadog-instrumentations/src/ws.js +2 -1
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/util.js +2 -2
- package/packages/datadog-plugin-cucumber/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -57
- package/packages/datadog-plugin-cypress/src/index.js +6 -1
- package/packages/datadog-plugin-cypress/src/support.js +92 -26
- package/packages/datadog-plugin-electron/src/ipc.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +87 -45
- package/packages/datadog-plugin-graphql/src/validate.js +2 -2
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-langchain/src/handlers/embedding.js +2 -1
- package/packages/datadog-plugin-langchain/src/handlers/language_models.js +2 -1
- package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +177 -64
- package/packages/datadog-plugin-router/src/index.js +11 -2
- package/packages/datadog-plugin-selenium/src/index.js +5 -36
- package/packages/datadog-plugin-ws/src/close.js +2 -1
- package/packages/datadog-plugin-ws/src/producer.js +2 -1
- package/packages/datadog-plugin-ws/src/receiver.js +2 -1
- package/packages/datadog-plugin-ws/src/server.js +2 -1
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +3 -1
- package/packages/dd-trace/src/azure_metadata.js +2 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +93 -28
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +160 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +288 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/payload-projection.js +84 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +369 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +60 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +41 -3
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +1 -1
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +6 -2
- package/packages/dd-trace/src/ci-visibility/rum.js +7 -0
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +137 -9
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +168 -8
- package/packages/dd-trace/src/ci-visibility/test-screenshot.js +90 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +16 -0
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +10 -0
- package/packages/dd-trace/src/config/supported-configurations.json +66 -0
- package/packages/dd-trace/src/exporter.js +2 -0
- package/packages/dd-trace/src/exporters/common/client-library-headers.js +21 -0
- package/packages/dd-trace/src/exporters/common/request.js +59 -30
- package/packages/dd-trace/src/exporters/common/url.js +15 -1
- package/packages/dd-trace/src/feature-registry.js +10 -3
- package/packages/dd-trace/src/llmobs/experiments/client.js +113 -0
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +154 -0
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +283 -0
- package/packages/dd-trace/src/llmobs/experiments/index.js +152 -0
- package/packages/dd-trace/src/llmobs/experiments/noop.js +30 -0
- package/packages/dd-trace/src/llmobs/experiments/result.js +34 -0
- package/packages/dd-trace/src/llmobs/noop.js +6 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +20 -3
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +2 -2
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +8 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +48 -14
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +43 -0
- package/packages/dd-trace/src/llmobs/sdk.js +17 -0
- package/packages/dd-trace/src/llmobs/tagger.js +68 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
- package/packages/dd-trace/src/llmobs/util.js +32 -0
- package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +322 -0
- package/packages/dd-trace/src/openfeature/configuration_source.js +90 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +14 -23
- package/packages/dd-trace/src/openfeature/index.js +0 -2
- package/packages/dd-trace/src/openfeature/noop.js +1 -28
- package/packages/dd-trace/src/openfeature/register.js +16 -27
- package/packages/dd-trace/src/openfeature/remote_config.js +15 -18
- package/packages/dd-trace/src/openfeature/require-provider.js +18 -0
- package/packages/dd-trace/src/opentelemetry/span-ending-hook.js +10 -0
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +20 -6
- package/packages/dd-trace/src/plugins/util/git.js +3 -2
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +29 -5
- package/packages/dd-trace/src/plugins/util/test.js +56 -4
- package/packages/dd-trace/src/proxy.js +84 -7
- package/packages/dd-trace/src/span_processor.js +5 -0
- package/packages/dd-trace/src/standalone/index.js +0 -14
- package/packages/dd-trace/src/telemetry/send-data.js +2 -2
- package/packages/dd-trace/src/util.js +26 -0
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- 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}}}
|