dd-trace 6.8.0 → 6.9.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/ci/diagnose.js +27 -25
- package/ci/init.js +6 -6
- package/ci/runbook.md +114 -20
- package/ci/test-optimization-validation/approval-artifacts.js +36 -3
- package/ci/test-optimization-validation/approval.js +78 -22
- package/ci/test-optimization-validation/blocker-category.js +24 -0
- package/ci/test-optimization-validation/ci-discovery.js +23 -17
- package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
- package/ci/test-optimization-validation/ci-remediation.js +1 -1
- package/ci/test-optimization-validation/cli.js +280 -52
- package/ci/test-optimization-validation/command-blocker.js +159 -20
- package/ci/test-optimization-validation/command-output-policy.js +9 -3
- package/ci/test-optimization-validation/command-runner.js +9 -7
- package/ci/test-optimization-validation/environment.js +4 -2
- package/ci/test-optimization-validation/executable.js +15 -13
- package/ci/test-optimization-validation/execution-lock.js +70 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
- package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
- package/ci/test-optimization-validation/generated-files.js +38 -28
- package/ci/test-optimization-validation/generated-test-contract.js +8 -4
- package/ci/test-optimization-validation/generated-verifier.js +24 -17
- package/ci/test-optimization-validation/literal-glob.js +52 -0
- package/ci/test-optimization-validation/manifest-loader.js +3 -0
- package/ci/test-optimization-validation/manifest-scaffold.js +300 -50
- package/ci/test-optimization-validation/manifest-schema.js +80 -17
- package/ci/test-optimization-validation/offline-fixtures.js +13 -4
- package/ci/test-optimization-validation/package-check.js +94 -0
- package/ci/test-optimization-validation/plan-writer.js +183 -10
- package/ci/test-optimization-validation/preflight-runner.js +110 -31
- package/ci/test-optimization-validation/project-build-artifact.js +31 -0
- package/ci/test-optimization-validation/redaction.js +18 -27
- package/ci/test-optimization-validation/report-writer.js +182 -48
- package/ci/test-optimization-validation/result-semantics.js +20 -3
- package/ci/test-optimization-validation/runner-command.js +40 -20
- package/ci/test-optimization-validation/runner-contract.js +79 -43
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +59 -5
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
- package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
- package/ci/test-optimization-validation/static-diagnosis.js +10 -3
- package/ci/test-optimization-validation/test-output.js +2 -1
- package/ci/test-optimization-validation/validation-blocker.js +21 -0
- package/ci/vitest-no-worker-init-setup.mjs +30 -34
- package/index.d.ts +98 -1
- package/init.js +1 -17
- package/initialize.mjs +11 -0
- package/loader-hook.mjs +6 -4
- package/package.json +5 -3
- package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
- package/packages/datadog-esbuild/src/utils.js +16 -1
- package/packages/datadog-instrumentations/src/ai.js +61 -34
- package/packages/datadog-instrumentations/src/bluebird.js +6 -6
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +81 -5
- package/packages/datadog-instrumentations/src/cucumber.js +124 -47
- package/packages/datadog-instrumentations/src/dns.js +21 -2
- package/packages/datadog-instrumentations/src/electron.js +1 -0
- package/packages/datadog-instrumentations/src/fastify.js +3 -1
- package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +33 -9
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +118 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +37 -6
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +979 -233
- package/packages/datadog-instrumentations/src/mocha/main.js +107 -9
- package/packages/datadog-instrumentations/src/mocha/utils.js +64 -42
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +155 -23
- package/packages/datadog-instrumentations/src/openai.js +14 -0
- package/packages/datadog-instrumentations/src/pg.js +180 -6
- package/packages/datadog-instrumentations/src/playwright.js +173 -79
- package/packages/datadog-instrumentations/src/promise-js.js +4 -4
- package/packages/datadog-instrumentations/src/promise.js +3 -3
- package/packages/datadog-instrumentations/src/router.js +195 -19
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -10
- package/packages/datadog-instrumentations/src/vitest-main.js +360 -56
- package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
- package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
- package/packages/datadog-instrumentations/src/webdriverio.js +194 -18
- package/packages/datadog-instrumentations/src/when.js +3 -3
- package/packages/datadog-instrumentations/src/ws.js +5 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
- package/packages/datadog-plugin-cucumber/src/index.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
- package/packages/datadog-plugin-cypress/src/support.js +7 -4
- package/packages/datadog-plugin-electron/src/net.js +10 -4
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +1 -18
- package/packages/datadog-plugin-jest/src/util.js +2 -0
- package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
- package/packages/datadog-plugin-mocha/src/index.js +318 -1
- package/packages/datadog-plugin-next/src/index.js +57 -9
- package/packages/datadog-plugin-openai/src/tracing.js +4 -0
- package/packages/datadog-plugin-pg/src/index.js +65 -1
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +95 -7
- package/packages/datadog-shimmer/src/shimmer.js +6 -2
- package/packages/dd-trace/src/aiguard/sdk.js +3 -0
- package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
- package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
- package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
- package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
- package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +48 -476
- package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
- package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
- package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +27 -13
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
- package/packages/dd-trace/src/config/helper.js +2 -0
- package/packages/dd-trace/src/config/index.js +16 -5
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +8 -0
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
- package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
- package/packages/dd-trace/src/guardrails/log.js +1 -10
- package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
- package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
- package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
- package/packages/dd-trace/src/llmobs/noop.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +149 -68
- package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
- package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
- package/packages/dd-trace/src/log/channels.js +1 -9
- package/packages/dd-trace/src/log/levels.js +12 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span.js +4 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +6 -0
- package/packages/dd-trace/src/plugins/util/git.js +6 -4
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
- package/packages/dd-trace/src/plugins/util/test.js +319 -75
- package/packages/dd-trace/src/plugins/util/url.js +1 -1
- package/packages/dd-trace/src/plugins/util/web.js +23 -1
- package/packages/dd-trace/src/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
- package/packages/dd-trace/src/proxy.js +1 -0
- package/packages/dd-trace/src/ritm.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
- package/packages/dd-trace/src/sampling_rule.js +4 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
- package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
- package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/version.js +10 -8
- /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
|
@@ -116,6 +116,66 @@ function wrapModelWithLifecycle (model) {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Wraps an OTel span without changing its method receivers.
|
|
121
|
+
*
|
|
122
|
+
* OTel spans may use private fields, so methods must run against the original
|
|
123
|
+
* span. A per-invocation wrapper also preserves `ctx` without mutating the AI
|
|
124
|
+
* SDK's shared no-op span.
|
|
125
|
+
*
|
|
126
|
+
* @param {import('@opentelemetry/api').Span} span
|
|
127
|
+
* @param {object} ctx
|
|
128
|
+
* @returns {import('@opentelemetry/api').Span}
|
|
129
|
+
*/
|
|
130
|
+
function createDelegatingSpan (span, ctx) {
|
|
131
|
+
return {
|
|
132
|
+
spanContext () {
|
|
133
|
+
return span.spanContext.apply(span, arguments)
|
|
134
|
+
},
|
|
135
|
+
setAttribute () {
|
|
136
|
+
span.setAttribute.apply(span, arguments)
|
|
137
|
+
return this
|
|
138
|
+
},
|
|
139
|
+
setAttributes (attributes) {
|
|
140
|
+
vercelAiSpanSetAttributesChannel.publish({ ctx, attributes })
|
|
141
|
+
span.setAttributes.apply(span, arguments)
|
|
142
|
+
return this
|
|
143
|
+
},
|
|
144
|
+
addEvent () {
|
|
145
|
+
span.addEvent.apply(span, arguments)
|
|
146
|
+
return this
|
|
147
|
+
},
|
|
148
|
+
addLink () {
|
|
149
|
+
span.addLink.apply(span, arguments)
|
|
150
|
+
return this
|
|
151
|
+
},
|
|
152
|
+
addLinks () {
|
|
153
|
+
span.addLinks.apply(span, arguments)
|
|
154
|
+
return this
|
|
155
|
+
},
|
|
156
|
+
setStatus () {
|
|
157
|
+
span.setStatus.apply(span, arguments)
|
|
158
|
+
return this
|
|
159
|
+
},
|
|
160
|
+
updateName () {
|
|
161
|
+
span.updateName.apply(span, arguments)
|
|
162
|
+
return this
|
|
163
|
+
},
|
|
164
|
+
isRecording () {
|
|
165
|
+
return span.isRecording.apply(span, arguments)
|
|
166
|
+
},
|
|
167
|
+
recordException (exception) {
|
|
168
|
+
ctx.error = exception
|
|
169
|
+
vercelAiTracingChannel.error.publish(ctx)
|
|
170
|
+
return span.recordException.apply(span, arguments)
|
|
171
|
+
},
|
|
172
|
+
end () {
|
|
173
|
+
vercelAiTracingChannel.asyncEnd.publish(ctx)
|
|
174
|
+
return span.end.apply(span, arguments)
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
119
179
|
function wrapTracer (tracer) {
|
|
120
180
|
if (tracers.has(tracer)) {
|
|
121
181
|
return
|
|
@@ -136,40 +196,7 @@ function wrapTracer (tracer) {
|
|
|
136
196
|
|
|
137
197
|
args[args.length - 1] = shimmer.wrapFunction(cb, function (originalCb) {
|
|
138
198
|
return function (span) {
|
|
139
|
-
|
|
140
|
-
// Object.create (and Proxy) cannot cross private-field brand checks — any span
|
|
141
|
-
// method that reads a private field (e.g. BridgeSpanBase#statusCode) would throw
|
|
142
|
-
// "Cannot read private member" on a prototype clone. Every method here calls
|
|
143
|
-
// through to the real span instance so private fields always resolve correctly.
|
|
144
|
-
const freshSpan = {
|
|
145
|
-
spanContext () { return span.spanContext() },
|
|
146
|
-
setAttribute (key, value) { span.setAttribute(key, value); return freshSpan },
|
|
147
|
-
setAttributes (attributes) {
|
|
148
|
-
vercelAiSpanSetAttributesChannel.publish({ ctx, attributes })
|
|
149
|
-
span.setAttributes(attributes)
|
|
150
|
-
return freshSpan
|
|
151
|
-
},
|
|
152
|
-
addEvent (name, attributesOrStartTime, startTime) {
|
|
153
|
-
span.addEvent(name, attributesOrStartTime, startTime)
|
|
154
|
-
return freshSpan
|
|
155
|
-
},
|
|
156
|
-
addLink (link, attrs) { span.addLink(link, attrs); return freshSpan },
|
|
157
|
-
addLinks (links) { span.addLinks(links); return freshSpan },
|
|
158
|
-
setStatus (status) { span.setStatus(status); return freshSpan },
|
|
159
|
-
updateName (spanName) { span.updateName(spanName); return freshSpan },
|
|
160
|
-
isRecording () { return span.isRecording() },
|
|
161
|
-
recordException (exception, timeInput) {
|
|
162
|
-
ctx.error = exception
|
|
163
|
-
vercelAiTracingChannel.error.publish(ctx)
|
|
164
|
-
span.recordException(exception, timeInput)
|
|
165
|
-
},
|
|
166
|
-
end (...endArgs) {
|
|
167
|
-
vercelAiTracingChannel.asyncEnd.publish(ctx)
|
|
168
|
-
span.end(...endArgs)
|
|
169
|
-
},
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return originalCb.call(this, freshSpan)
|
|
199
|
+
return originalCb.call(this, createDelegatingSpan(span, ctx))
|
|
173
200
|
}
|
|
174
201
|
})
|
|
175
202
|
|
|
@@ -15,12 +15,12 @@ function createGetNewLibraryCopyWrap (originalLib) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
addHook({ name: 'bluebird', versions: ['>=2.0.2'] },
|
|
19
|
-
shimmer.wrap(
|
|
20
|
-
return
|
|
18
|
+
addHook({ name: 'bluebird', versions: ['>=2.0.2'] }, LibraryPromise => {
|
|
19
|
+
shimmer.wrap(LibraryPromise.prototype, '_then', wrapThen)
|
|
20
|
+
return LibraryPromise
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
-
addHook({ name: 'bluebird', versions: ['^2.11.0', '^3.4.1'] },
|
|
24
|
-
shimmer.wrap(
|
|
25
|
-
return
|
|
23
|
+
addHook({ name: 'bluebird', versions: ['^2.11.0', '^3.4.1'] }, LibraryPromise => {
|
|
24
|
+
shimmer.wrap(LibraryPromise, 'getNewLibraryCopy', createGetNewLibraryCopyWrap(LibraryPromise))
|
|
25
|
+
return LibraryPromise
|
|
26
26
|
})
|
|
@@ -13,6 +13,66 @@ const LOCAL_LIFECYCLE_LOOKAHEAD = 4
|
|
|
13
13
|
|
|
14
14
|
const chunkEmitTimes = new WeakMap()
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {{ session_id?: string } & Record<string, unknown>} Chunk
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {{
|
|
22
|
+
* id: string,
|
|
23
|
+
* name?: string,
|
|
24
|
+
* input?: unknown,
|
|
25
|
+
* output?: unknown,
|
|
26
|
+
* error?: unknown,
|
|
27
|
+
* isInterrupt?: boolean,
|
|
28
|
+
* sessionId?: string,
|
|
29
|
+
* hookStartTime?: number,
|
|
30
|
+
* hookFinishTime?: number,
|
|
31
|
+
* }} ToolState
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {{ taskStartedChunk?: Chunk, toolResultIndex?: number }} ToolLifecycle
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @typedef {(startIndex: number, toolUseId: string) => ToolLifecycle} GetLifecycle
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @typedef {{
|
|
44
|
+
* tools: Map<string, ToolState>,
|
|
45
|
+
* subagents: Map<string, Record<string, unknown>>,
|
|
46
|
+
* prompt?: string,
|
|
47
|
+
* model?: string,
|
|
48
|
+
* resume?: string,
|
|
49
|
+
* maxTurns?: number,
|
|
50
|
+
* permissionMode?: string,
|
|
51
|
+
* sessionId?: string,
|
|
52
|
+
* source?: string,
|
|
53
|
+
* cwd?: string,
|
|
54
|
+
* transcriptPath?: string,
|
|
55
|
+
* agentType?: string,
|
|
56
|
+
* endReason?: string,
|
|
57
|
+
* stopReason?: string,
|
|
58
|
+
* lastAssistantMessage?: string,
|
|
59
|
+
* }} SessionContext
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @typedef {{
|
|
64
|
+
* stepIndex: number,
|
|
65
|
+
* startTime?: number,
|
|
66
|
+
* finishTime?: number,
|
|
67
|
+
* parentToolUseId?: string,
|
|
68
|
+
* sessionId?: string,
|
|
69
|
+
* chunks?: Chunk[],
|
|
70
|
+
* llmStartIdx?: number,
|
|
71
|
+
* llmEndIdx?: number,
|
|
72
|
+
* toolOutputs?: unknown[],
|
|
73
|
+
* }} StepContext
|
|
74
|
+
*/
|
|
75
|
+
|
|
16
76
|
function hasDownstreamSubscribers () {
|
|
17
77
|
return queryChannel.asyncEnd.hasSubscribers ||
|
|
18
78
|
queryChannel.error.hasSubscribers ||
|
|
@@ -287,10 +347,12 @@ function getToolResultContent (chunk, toolUseId) {
|
|
|
287
347
|
}
|
|
288
348
|
|
|
289
349
|
/**
|
|
290
|
-
*
|
|
291
|
-
* @param {Array<Record<string, unknown>>} chunks
|
|
350
|
+
* @param {Chunk[]} chunks
|
|
292
351
|
* @param {number} startIndex
|
|
293
352
|
* @param {string} toolUseId
|
|
353
|
+
* @param {SessionContext | null | undefined} sessionCtx
|
|
354
|
+
* @param {GetLifecycle} getLifecycle
|
|
355
|
+
* @param {ToolLifecycle} lifecycle
|
|
294
356
|
* @returns {number} index in chunks where the next step should start iteration
|
|
295
357
|
*/
|
|
296
358
|
function processTool (chunks, startIndex, toolUseId, sessionCtx, getLifecycle, lifecycle) {
|
|
@@ -342,9 +404,14 @@ function processTool (chunks, startIndex, toolUseId, sessionCtx, getLifecycle, l
|
|
|
342
404
|
}
|
|
343
405
|
|
|
344
406
|
/**
|
|
345
|
-
*
|
|
346
|
-
* @param {Array<Record<string, unknown>>} chunks
|
|
407
|
+
* @param {Chunk[]} chunks
|
|
347
408
|
* @param {number} startIndex
|
|
409
|
+
* @param {number | undefined} stepStartTime
|
|
410
|
+
* @param {string | null | undefined} parentToolUseId
|
|
411
|
+
* @param {string | undefined} initialPrompt
|
|
412
|
+
* @param {StepContext | null | undefined} stepCtx
|
|
413
|
+
* @param {SessionContext | null | undefined} sessionCtx
|
|
414
|
+
* @param {GetLifecycle} getLifecycle
|
|
348
415
|
* @returns {number} index in chunks where the next step should start iteration
|
|
349
416
|
*/
|
|
350
417
|
function processStep (
|
|
@@ -447,7 +514,16 @@ function processStep (
|
|
|
447
514
|
}
|
|
448
515
|
|
|
449
516
|
/**
|
|
450
|
-
* @param {
|
|
517
|
+
* @param {Chunk[]} chunks
|
|
518
|
+
* @param {{
|
|
519
|
+
* sessionCtx?: SessionContext,
|
|
520
|
+
* session_id?: string,
|
|
521
|
+
* cwd?: string,
|
|
522
|
+
* permissionMode?: string,
|
|
523
|
+
* output?: unknown,
|
|
524
|
+
* arguments?: Array<{ prompt?: string }>,
|
|
525
|
+
* runInContext?: (fn: () => number) => number,
|
|
526
|
+
* }} agentCtx
|
|
451
527
|
*/
|
|
452
528
|
function processChunks (chunks, agentCtx) {
|
|
453
529
|
let chunkIndex = 0
|
|
@@ -7,6 +7,11 @@ const shimmer = require('../../datadog-shimmer')
|
|
|
7
7
|
const log = require('../../dd-trace/src/log')
|
|
8
8
|
const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
|
|
9
9
|
const { getSegment } = require('../../dd-trace/src/util')
|
|
10
|
+
const {
|
|
11
|
+
EMPTY_EFD_RETRY_POLICY,
|
|
12
|
+
getEfdRetryCountForDuration,
|
|
13
|
+
hasEfdRetries,
|
|
14
|
+
} = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
|
|
10
15
|
const {
|
|
11
16
|
getCoveredFilesFromCoverage,
|
|
12
17
|
getExecutableFilesFromCoverage,
|
|
@@ -16,15 +21,16 @@ const {
|
|
|
16
21
|
getTestSuitePath,
|
|
17
22
|
getRelativeCoverageFiles,
|
|
18
23
|
CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
|
|
24
|
+
CUCUMBER_WORKER_TELEMETRY_PAYLOAD_CODE,
|
|
19
25
|
getIsFaultyEarlyFlakeDetection,
|
|
20
|
-
getEfdRetryCount,
|
|
21
|
-
getMaxEfdRetryCount,
|
|
22
26
|
applySkippedCoverageToCoverage,
|
|
23
27
|
getTestCoverageLinesPercentage,
|
|
28
|
+
recordTestManagementExecution,
|
|
24
29
|
recordAttemptToFixExecution,
|
|
25
|
-
|
|
30
|
+
collectTestOptimizationSummariesFromTraces,
|
|
26
31
|
logAttemptToFixTestExecution,
|
|
27
32
|
logTestOptimizationSummary,
|
|
33
|
+
TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE,
|
|
28
34
|
getTestOptimizationRequestResults,
|
|
29
35
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
30
36
|
const { writeCoverageBackfillToCache } = require('../../dd-trace/src/ci-visibility/test-optimization-cache')
|
|
@@ -57,12 +63,25 @@ const modifiedFilesCh = channel('ci:cucumber:modified-files')
|
|
|
57
63
|
const isModifiedCh = channel('ci:cucumber:is-modified-test')
|
|
58
64
|
|
|
59
65
|
const workerReportTraceCh = channel('ci:cucumber:worker-report:trace')
|
|
66
|
+
const workerReportTelemetryCh = channel('ci:cucumber:worker-report:telemetry')
|
|
60
67
|
|
|
61
68
|
const itrSkippedSuitesCh = channel('ci:cucumber:itr:skipped-suites')
|
|
62
69
|
|
|
63
70
|
const getCodeCoverageCh = channel('ci:nyc:get-coverage')
|
|
64
71
|
|
|
65
72
|
const DD_EFD_RETRY_COUNT_MESSAGE = '_ddEfdRetryCount'
|
|
73
|
+
const CUCUMBER_RETRY_NAME_SUFFIX = / ?\(attempt \d+(?:, retried)?\) ?$/
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Removes Cucumber's generated retry suffix without changing literal scenario names.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} testName
|
|
79
|
+
* @param {boolean} isRetry
|
|
80
|
+
* @returns {string}
|
|
81
|
+
*/
|
|
82
|
+
function getCucumberTestName (testName, isRetry) {
|
|
83
|
+
return isRetry ? testName.replace(CUCUMBER_RETRY_NAME_SUFFIX, '') : testName
|
|
84
|
+
}
|
|
66
85
|
|
|
67
86
|
const isMarkedAsUnskippable = (pickle) => {
|
|
68
87
|
return pickle.tags.some(tag => tag.name === '@datadog:unskippable')
|
|
@@ -106,8 +125,7 @@ let isItrEnabled = false
|
|
|
106
125
|
let isSuitesSkippingEnabled = false
|
|
107
126
|
let isCoverageReportUploadEnabled = false
|
|
108
127
|
let isEarlyFlakeDetectionEnabled = false
|
|
109
|
-
let
|
|
110
|
-
let earlyFlakeDetectionSlowTestRetries = {}
|
|
128
|
+
let earlyFlakeDetectionRetryPolicy = EMPTY_EFD_RETRY_POLICY
|
|
111
129
|
let earlyFlakeDetectionFaultyThreshold = 0
|
|
112
130
|
let isEarlyFlakeDetectionFaulty = false
|
|
113
131
|
let isFlakyTestRetriesEnabled = false
|
|
@@ -121,8 +139,16 @@ let numTestRetries = 0
|
|
|
121
139
|
let knownTests = {}
|
|
122
140
|
let skippedSuites = []
|
|
123
141
|
let isSuitesSkipped = false
|
|
142
|
+
let areAllSuitesSkipped = false
|
|
124
143
|
let repositoryRoot
|
|
125
144
|
|
|
145
|
+
/**
|
|
146
|
+
* @returns {boolean}
|
|
147
|
+
*/
|
|
148
|
+
function shouldRunEarlyFlakeDetection () {
|
|
149
|
+
return isEarlyFlakeDetectionEnabled && hasEfdRetries(earlyFlakeDetectionRetryPolicy)
|
|
150
|
+
}
|
|
151
|
+
|
|
126
152
|
function isValidKnownTests (receivedKnownTests) {
|
|
127
153
|
return !!receivedKnownTests.cucumber
|
|
128
154
|
}
|
|
@@ -166,6 +192,7 @@ function resetSuiteSkippingRunState () {
|
|
|
166
192
|
skippedSuitesCoverage = {}
|
|
167
193
|
skippedSuites = []
|
|
168
194
|
isSuitesSkipped = false
|
|
195
|
+
areAllSuitesSkipped = false
|
|
169
196
|
repositoryRoot = undefined
|
|
170
197
|
writeCoverageBackfillToCache({})
|
|
171
198
|
}
|
|
@@ -180,11 +207,6 @@ function getSuiteStatusFromTestStatuses (testStatuses) {
|
|
|
180
207
|
return 'pass'
|
|
181
208
|
}
|
|
182
209
|
|
|
183
|
-
function getConfiguredEfdRetryCount () {
|
|
184
|
-
const maxSlowTestRetryCount = getMaxEfdRetryCount(earlyFlakeDetectionSlowTestRetries)
|
|
185
|
-
return maxSlowTestRetryCount ?? earlyFlakeDetectionNumRetries
|
|
186
|
-
}
|
|
187
|
-
|
|
188
210
|
function publishWorkerEfdRetryCount (pickle, retryCount) {
|
|
189
211
|
const message = {
|
|
190
212
|
[DD_EFD_RETRY_COUNT_MESSAGE]: {
|
|
@@ -221,15 +243,13 @@ function configureParallelWorkerWorldParameters (options) {
|
|
|
221
243
|
options.worldParameters._ddIsKnownTestsEnabled = true
|
|
222
244
|
options.worldParameters._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
|
|
223
245
|
options.worldParameters._ddKnownTests = knownTests
|
|
224
|
-
options.worldParameters.
|
|
225
|
-
options.worldParameters._ddEarlyFlakeDetectionSlowTestRetries = earlyFlakeDetectionSlowTestRetries
|
|
246
|
+
options.worldParameters._ddEarlyFlakeDetectionRetryPolicy = earlyFlakeDetectionRetryPolicy
|
|
226
247
|
} else {
|
|
227
248
|
isEarlyFlakeDetectionEnabled = false
|
|
228
249
|
isKnownTestsEnabled = false
|
|
229
250
|
options.worldParameters._ddIsEarlyFlakeDetectionEnabled = false
|
|
230
251
|
options.worldParameters._ddIsKnownTestsEnabled = false
|
|
231
|
-
options.worldParameters.
|
|
232
|
-
options.worldParameters._ddEarlyFlakeDetectionSlowTestRetries = {}
|
|
252
|
+
options.worldParameters._ddEarlyFlakeDetectionRetryPolicy = EMPTY_EFD_RETRY_POLICY
|
|
233
253
|
}
|
|
234
254
|
|
|
235
255
|
if (isImpactedTestsEnabled) {
|
|
@@ -261,10 +281,9 @@ function readParallelWorkerWorldParameters (options) {
|
|
|
261
281
|
}
|
|
262
282
|
}
|
|
263
283
|
isEarlyFlakeDetectionEnabled = !!worldParameters._ddIsEarlyFlakeDetectionEnabled
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
284
|
+
earlyFlakeDetectionRetryPolicy = isEarlyFlakeDetectionEnabled
|
|
285
|
+
? worldParameters._ddEarlyFlakeDetectionRetryPolicy ?? EMPTY_EFD_RETRY_POLICY
|
|
286
|
+
: EMPTY_EFD_RETRY_POLICY
|
|
268
287
|
isImpactedTestsEnabled = !!worldParameters._ddImpactedTestsEnabled
|
|
269
288
|
if (isImpactedTestsEnabled) {
|
|
270
289
|
modifiedFiles = worldParameters._ddModifiedFiles
|
|
@@ -282,10 +301,14 @@ function handleDdWorkerMessage (message) {
|
|
|
282
301
|
if (Array.isArray(message)) {
|
|
283
302
|
const [messageCode, payload] = message
|
|
284
303
|
if (messageCode === CUCUMBER_WORKER_TRACE_PAYLOAD_CODE) {
|
|
285
|
-
|
|
304
|
+
collectTestOptimizationSummariesFromTraces(payload, { attemptToFixExecutions })
|
|
286
305
|
workerReportTraceCh.publish(payload)
|
|
287
306
|
return true
|
|
288
307
|
}
|
|
308
|
+
if (messageCode === CUCUMBER_WORKER_TELEMETRY_PAYLOAD_CODE) {
|
|
309
|
+
workerReportTelemetryCh.publish(payload)
|
|
310
|
+
return true
|
|
311
|
+
}
|
|
289
312
|
}
|
|
290
313
|
|
|
291
314
|
if (message?.[DD_EFD_RETRY_COUNT_MESSAGE]) {
|
|
@@ -334,8 +357,10 @@ function maybeStartParallelSuite (pickle) {
|
|
|
334
357
|
})
|
|
335
358
|
}
|
|
336
359
|
|
|
337
|
-
function handleParallelTestCaseFinished (pickle, worstTestStepResult) {
|
|
338
|
-
const { status } =
|
|
360
|
+
function handleParallelTestCaseFinished (pickle, worstTestStepResult, usesNumericStatus = false) {
|
|
361
|
+
const { status } = usesNumericStatus
|
|
362
|
+
? getStatusFromResult(worstTestStepResult)
|
|
363
|
+
: getStatusFromResultLatest(worstTestStepResult)
|
|
339
364
|
let isNew = false
|
|
340
365
|
|
|
341
366
|
if (isKnownTestsEnabled) {
|
|
@@ -345,7 +370,7 @@ function handleParallelTestCaseFinished (pickle, worstTestStepResult) {
|
|
|
345
370
|
const testFileAbsolutePath = pickle.uri
|
|
346
371
|
const finished = (pickleResultByFile[testFileAbsolutePath] ||= [])
|
|
347
372
|
|
|
348
|
-
if (
|
|
373
|
+
if (shouldRunEarlyFlakeDetection() && isNew) {
|
|
349
374
|
const testFullname = `${pickle.uri}:${pickle.name}`
|
|
350
375
|
let testStatuses = newTestsByTestFullname.get(testFullname)
|
|
351
376
|
if (testStatuses) {
|
|
@@ -358,7 +383,7 @@ function handleParallelTestCaseFinished (pickle, worstTestStepResult) {
|
|
|
358
383
|
if (efdRetryCount === undefined) {
|
|
359
384
|
efdRetryCount = status === 'skip'
|
|
360
385
|
? 0
|
|
361
|
-
:
|
|
386
|
+
: earlyFlakeDetectionRetryPolicy.schedulingRetryCount
|
|
362
387
|
efdRetryCountByPickleId.set(pickle.id, efdRetryCount)
|
|
363
388
|
if (efdRetryCount === 0 && status !== 'skip') {
|
|
364
389
|
efdSlowAbortedPickleIds.add(pickle.id)
|
|
@@ -696,7 +721,7 @@ function publishRetriedAttempt (runner, state) {
|
|
|
696
721
|
|
|
697
722
|
// ATR: record this attempt as failed so when run().finally runs (after retry) we have all statuses
|
|
698
723
|
if (isFlakyTestRetriesEnabled) {
|
|
699
|
-
const nameForKey = runner.pickle.name
|
|
724
|
+
const nameForKey = getCucumberTestName(runner.pickle.name, currentAttempt > 0)
|
|
700
725
|
const atrKey = `${runner.pickle.uri}:${nameForKey}`
|
|
701
726
|
if (atrStatusesByScenarioKey.has(atrKey)) {
|
|
702
727
|
atrStatusesByScenarioKey.get(atrKey).push('fail')
|
|
@@ -802,6 +827,7 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
802
827
|
const { status, skipReason } = isLatestVersion
|
|
803
828
|
? getStatusFromResultLatest(result)
|
|
804
829
|
: getStatusFromResult(result)
|
|
830
|
+
const testName = getCucumberTestName(this.pickle.name, state.numAttempt > 0)
|
|
805
831
|
|
|
806
832
|
if (lastStatusByPickleId.has(this.pickle.id)) {
|
|
807
833
|
lastStatusByPickleId.get(this.pickle.id).push(status)
|
|
@@ -821,7 +847,7 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
821
847
|
|
|
822
848
|
if (isTestManagementTestsEnabled) {
|
|
823
849
|
const testSuitePath = getTestSuitePath(testFileAbsolutePath, process.cwd())
|
|
824
|
-
const testProperties = getTestProperties(testSuitePath,
|
|
850
|
+
const testProperties = getTestProperties(testSuitePath, testName)
|
|
825
851
|
const numRetries = numRetriesByPickleId.get(this.pickle.id)
|
|
826
852
|
isAttemptToFix = testProperties.attemptToFix
|
|
827
853
|
isAttemptToFixRetry = isAttemptToFix && numRetries > 0
|
|
@@ -857,20 +883,24 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
857
883
|
}
|
|
858
884
|
|
|
859
885
|
if (
|
|
860
|
-
|
|
886
|
+
shouldRunEarlyFlakeDetection() &&
|
|
861
887
|
status !== 'skip' &&
|
|
862
888
|
(isNew || isModified) &&
|
|
863
889
|
!isEfdRetry &&
|
|
864
890
|
!efdRetryCountByPickleId.has(this.pickle.id)
|
|
865
891
|
) {
|
|
866
|
-
const retryCount =
|
|
892
|
+
const retryCount = getEfdRetryCountForDuration(
|
|
893
|
+
performance.now() - executionStart,
|
|
894
|
+
earlyFlakeDetectionRetryPolicy
|
|
895
|
+
)
|
|
867
896
|
efdRetryCountByPickleId.set(this.pickle.id, retryCount)
|
|
868
897
|
if (retryCount === 0) {
|
|
869
898
|
efdSlowAbortedPickleIds.add(this.pickle.id)
|
|
870
899
|
}
|
|
871
900
|
}
|
|
872
901
|
|
|
873
|
-
const efdRetryCount = efdRetryCountByPickleId.get(this.pickle.id) ??
|
|
902
|
+
const efdRetryCount = efdRetryCountByPickleId.get(this.pickle.id) ??
|
|
903
|
+
earlyFlakeDetectionRetryPolicy.schedulingRetryCount
|
|
874
904
|
|
|
875
905
|
// Check if all EFD retries failed
|
|
876
906
|
if (isEfdRetry && (isNew || isModified)) {
|
|
@@ -889,8 +919,7 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
889
919
|
// ATR: accumulate statuses by stable scenario key (uri:name) so retries are grouped.
|
|
890
920
|
// Cucumber appends " (attempt N)" or " (attempt N, retried)" to the scenario name; normalize for keying.
|
|
891
921
|
if (isFlakyTestRetriesEnabled && !isAttemptToFix && !isEfdRetry && numTestRetries > 0) {
|
|
892
|
-
const
|
|
893
|
-
const atrKey = `${this.pickle.uri}:${nameForKey}`
|
|
922
|
+
const atrKey = `${this.pickle.uri}:${testName}`
|
|
894
923
|
if (atrStatusesByScenarioKey.has(atrKey)) {
|
|
895
924
|
atrStatusesByScenarioKey.get(atrKey).push(status)
|
|
896
925
|
} else {
|
|
@@ -909,10 +938,21 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
909
938
|
|
|
910
939
|
const error = getErrorFromCucumberResult(result)
|
|
911
940
|
|
|
941
|
+
if (!testStartPayload.isParallel) {
|
|
942
|
+
recordTestManagementExecution({
|
|
943
|
+
testSuite: testSuitePath,
|
|
944
|
+
testName,
|
|
945
|
+
status,
|
|
946
|
+
isAttemptToFix,
|
|
947
|
+
isDisabled,
|
|
948
|
+
isQuarantined,
|
|
949
|
+
})
|
|
950
|
+
}
|
|
951
|
+
|
|
912
952
|
if (isAttemptToFix) {
|
|
913
953
|
recordAttemptToFixExecution(attemptToFixExecutions, {
|
|
914
954
|
testSuite: testSuitePath,
|
|
915
|
-
testName
|
|
955
|
+
testName,
|
|
916
956
|
status,
|
|
917
957
|
isDisabled,
|
|
918
958
|
isQuarantined,
|
|
@@ -953,7 +993,8 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
953
993
|
error,
|
|
954
994
|
isNew,
|
|
955
995
|
isEfdRetry,
|
|
956
|
-
isFlakyRetry: state.numAttempt > 0,
|
|
996
|
+
isFlakyRetry: state.numAttempt > 0 && isFlakyTestRetriesEnabled,
|
|
997
|
+
isExternalRetry: state.numAttempt > 0 && !isFlakyTestRetriesEnabled,
|
|
957
998
|
isAttemptToFix,
|
|
958
999
|
isAttemptToFixRetry,
|
|
959
1000
|
hasFailedAllRetries,
|
|
@@ -1059,8 +1100,8 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
|
|
|
1059
1100
|
repositoryRoot = configurationResponse.repositoryRoot
|
|
1060
1101
|
isItrEnabled = configurationResponse.libraryConfig?.isItrEnabled
|
|
1061
1102
|
isEarlyFlakeDetectionEnabled = configurationResponse.libraryConfig?.isEarlyFlakeDetectionEnabled
|
|
1062
|
-
|
|
1063
|
-
|
|
1103
|
+
earlyFlakeDetectionRetryPolicy = configurationResponse.libraryConfig?.earlyFlakeDetectionRetryPolicy ??
|
|
1104
|
+
EMPTY_EFD_RETRY_POLICY
|
|
1064
1105
|
earlyFlakeDetectionFaultyThreshold = configurationResponse.libraryConfig?.earlyFlakeDetectionFaultyThreshold
|
|
1065
1106
|
isSuitesSkippingEnabled = isItrEnabled && configurationResponse.libraryConfig?.isSuitesSkippingEnabled
|
|
1066
1107
|
isCoverageReportUploadEnabled = configurationResponse.libraryConfig?.isCoverageReportUploadEnabled
|
|
@@ -1113,6 +1154,7 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
|
|
|
1113
1154
|
const oldPickles = isCoordinator ? this.sourcedPickles : this.pickleIds
|
|
1114
1155
|
|
|
1115
1156
|
isSuitesSkipped = picklesToRun.length !== oldPickles.length
|
|
1157
|
+
areAllSuitesSkipped = oldPickles.length > 0 && picklesToRun.length === 0
|
|
1116
1158
|
|
|
1117
1159
|
log.debug('%s out of %s suites are going to run.', picklesToRun.length, oldPickles.length)
|
|
1118
1160
|
|
|
@@ -1229,7 +1271,10 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
|
|
|
1229
1271
|
isParallel,
|
|
1230
1272
|
})
|
|
1231
1273
|
|
|
1232
|
-
logTestOptimizationSummary({
|
|
1274
|
+
logTestOptimizationSummary({
|
|
1275
|
+
attemptToFixExecutions,
|
|
1276
|
+
extraSections: areAllSuitesSkipped ? [TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE] : [],
|
|
1277
|
+
})
|
|
1233
1278
|
loggedAttemptToFixTests.clear()
|
|
1234
1279
|
eventDataCollector = null
|
|
1235
1280
|
await flushPromise
|
|
@@ -1317,7 +1362,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1317
1362
|
}
|
|
1318
1363
|
}
|
|
1319
1364
|
const originalRetry = this.options.retry
|
|
1320
|
-
const isManagedRetry = isAttemptToFix || (
|
|
1365
|
+
const isManagedRetry = isAttemptToFix || (shouldRunEarlyFlakeDetection() && (isNew || isModified))
|
|
1321
1366
|
if (isManagedRetry) {
|
|
1322
1367
|
this.options.retry = 0
|
|
1323
1368
|
}
|
|
@@ -1342,10 +1387,10 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1342
1387
|
}
|
|
1343
1388
|
|
|
1344
1389
|
// If it's a new test and it hasn't been skipped, we run it again
|
|
1345
|
-
if (
|
|
1390
|
+
if (shouldRunEarlyFlakeDetection() && lastTestStatus !== 'skip' && (isNew || isModified)) {
|
|
1346
1391
|
let efdRetryCount = efdRetryCountByPickleId.get(pickle.id)
|
|
1347
1392
|
if (efdRetryCount === undefined) {
|
|
1348
|
-
efdRetryCount =
|
|
1393
|
+
efdRetryCount = getEfdRetryCountForDuration(firstExecutionDurationMs, earlyFlakeDetectionRetryPolicy)
|
|
1349
1394
|
efdRetryCountByPickleId.set(pickle.id, efdRetryCount)
|
|
1350
1395
|
if (efdRetryCount === 0) {
|
|
1351
1396
|
efdSlowAbortedPickleIds.add(pickle.id)
|
|
@@ -1364,7 +1409,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1364
1409
|
let shouldBePassedByEFD = false
|
|
1365
1410
|
let shouldBePassedByTestManagement = false
|
|
1366
1411
|
let shouldBeFailedByAttemptToFix = false
|
|
1367
|
-
if ((isNew || isModified) &&
|
|
1412
|
+
if ((isNew || isModified) && shouldRunEarlyFlakeDetection()) {
|
|
1368
1413
|
/**
|
|
1369
1414
|
* If Early Flake Detection (EFD) is enabled the logic is as follows:
|
|
1370
1415
|
* - If all attempts for a test are failing, the test has failed and we will let the test process fail.
|
|
@@ -1422,7 +1467,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1422
1467
|
|
|
1423
1468
|
this.options.retry = originalRetry
|
|
1424
1469
|
|
|
1425
|
-
if (isNewerCucumberVersion &&
|
|
1470
|
+
if (isNewerCucumberVersion && shouldRunEarlyFlakeDetection() && (isNew || isModified)) {
|
|
1426
1471
|
return shouldBePassedByEFD
|
|
1427
1472
|
}
|
|
1428
1473
|
|
|
@@ -1451,7 +1496,7 @@ function patchCucumberWorkerRunTestCase (runtimeExecutorPackage, isWorker) {
|
|
|
1451
1496
|
)
|
|
1452
1497
|
}
|
|
1453
1498
|
|
|
1454
|
-
function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion) {
|
|
1499
|
+
function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion, usesNumericStatus = false) {
|
|
1455
1500
|
return function (worker, message) {
|
|
1456
1501
|
if (!testSuiteFinishCh.hasSubscribers) {
|
|
1457
1502
|
return parseWorkerMessageFunction.apply(this, arguments)
|
|
@@ -1502,13 +1547,31 @@ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion)
|
|
|
1502
1547
|
pickle = testCase.pickle
|
|
1503
1548
|
}
|
|
1504
1549
|
|
|
1505
|
-
handleParallelTestCaseFinished(pickle, worstTestStepResult)
|
|
1550
|
+
handleParallelTestCaseFinished(pickle, worstTestStepResult, usesNumericStatus)
|
|
1506
1551
|
}
|
|
1507
1552
|
|
|
1508
1553
|
return parseWorkerResponse
|
|
1509
1554
|
}
|
|
1510
1555
|
}
|
|
1511
1556
|
|
|
1557
|
+
/**
|
|
1558
|
+
* Adapts Cucumber 7's callback-based parallel coordinator to the Promise contract used by getWrappedStart.
|
|
1559
|
+
*
|
|
1560
|
+
* @param {Function} run
|
|
1561
|
+
* @param {string} frameworkVersion
|
|
1562
|
+
* @returns {Function}
|
|
1563
|
+
*/
|
|
1564
|
+
function getWrappedCoordinatorRun (run, frameworkVersion) {
|
|
1565
|
+
const runAsPromise = function (numberOfWorkers) {
|
|
1566
|
+
return new Promise(resolve => run.call(this, numberOfWorkers, resolve))
|
|
1567
|
+
}
|
|
1568
|
+
const wrappedStart = getWrappedStart(runAsPromise, frameworkVersion, true)
|
|
1569
|
+
|
|
1570
|
+
return function (numberOfWorkers, done) {
|
|
1571
|
+
return wrappedStart.call(this, numberOfWorkers).then(done)
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1512
1575
|
module.exports.patchCucumberWorkerRunTestCase = patchCucumberWorkerRunTestCase
|
|
1513
1576
|
|
|
1514
1577
|
// Test start / finish for older versions. The only hook executed in workers when in parallel mode
|
|
@@ -1556,19 +1619,33 @@ addHook({
|
|
|
1556
1619
|
return runtimePackage
|
|
1557
1620
|
})
|
|
1558
1621
|
|
|
1559
|
-
// Only executed in parallel mode.
|
|
1560
|
-
// `getWrappedStart` generates session start and finish events
|
|
1622
|
+
// Only executed in parallel mode in Cucumber 7 through 10.
|
|
1623
|
+
// `getWrappedCoordinatorRun` or `getWrappedStart` generates session start and finish events
|
|
1561
1624
|
// `getWrappedParseWorkerMessage` generates suite start and finish events
|
|
1625
|
+
// Shimmer is required because the coordinator must be changed before it starts workers and exposes no lifecycle hook.
|
|
1562
1626
|
addHook({
|
|
1563
1627
|
name: '@cucumber/cucumber',
|
|
1564
|
-
versions: ['>=
|
|
1628
|
+
versions: ['>=7.0.0 <11.0.0'],
|
|
1565
1629
|
file: 'lib/runtime/parallel/coordinator.js',
|
|
1566
1630
|
}, (coordinatorPackage, frameworkVersion) => {
|
|
1567
|
-
|
|
1631
|
+
const isCucumber7 = satisfies(frameworkVersion, '<8.0.0')
|
|
1632
|
+
if (isCucumber7) {
|
|
1633
|
+
shimmer.wrap(
|
|
1634
|
+
coordinatorPackage.default.prototype,
|
|
1635
|
+
'run',
|
|
1636
|
+
run => getWrappedCoordinatorRun(run, frameworkVersion)
|
|
1637
|
+
)
|
|
1638
|
+
} else {
|
|
1639
|
+
shimmer.wrap(
|
|
1640
|
+
coordinatorPackage.default.prototype,
|
|
1641
|
+
'start',
|
|
1642
|
+
start => getWrappedStart(start, frameworkVersion, true)
|
|
1643
|
+
)
|
|
1644
|
+
}
|
|
1568
1645
|
shimmer.wrap(
|
|
1569
1646
|
coordinatorPackage.default.prototype,
|
|
1570
1647
|
'parseWorkerMessage',
|
|
1571
|
-
parseWorkerMessage => getWrappedParseWorkerMessage(parseWorkerMessage)
|
|
1648
|
+
parseWorkerMessage => getWrappedParseWorkerMessage(parseWorkerMessage, false, isCucumber7)
|
|
1572
1649
|
)
|
|
1573
1650
|
return coordinatorPackage
|
|
1574
1651
|
})
|