dd-trace 6.9.0 → 6.11.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.md +0 -17
- package/ci/vitest-no-worker-init-setup.mjs +112 -10
- package/index.d.ts +150 -3
- package/initialize.mjs +28 -17
- package/openfeature.js +2 -2
- package/package.json +14 -10
- package/packages/datadog-esbuild/index.js +0 -34
- package/packages/datadog-instrumentations/src/anthropic.js +210 -15
- package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
- package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
- package/packages/datadog-instrumentations/src/bunyan.js +3 -16
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
- package/packages/datadog-instrumentations/src/cucumber.js +26 -11
- package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
- package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
- package/packages/datadog-instrumentations/src/cypress.js +2 -0
- package/packages/datadog-instrumentations/src/fastify.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +634 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +81 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
- package/packages/datadog-instrumentations/src/jest.js +26 -18
- package/packages/datadog-instrumentations/src/mariadb.js +278 -21
- package/packages/datadog-instrumentations/src/mocha/main.js +540 -56
- package/packages/datadog-instrumentations/src/mocha/utils.js +132 -25
- package/packages/datadog-instrumentations/src/mysql.js +45 -15
- package/packages/datadog-instrumentations/src/mysql2.js +119 -18
- package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
- package/packages/datadog-instrumentations/src/pg.js +116 -92
- package/packages/datadog-instrumentations/src/playwright-reporter.js +99 -0
- package/packages/datadog-instrumentations/src/playwright.js +149 -2
- package/packages/datadog-instrumentations/src/router.js +2 -0
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -4
- package/packages/datadog-instrumentations/src/vitest-main.js +180 -20
- package/packages/datadog-instrumentations/src/vitest-util.js +64 -5
- package/packages/datadog-instrumentations/src/vitest-worker.js +150 -25
- package/packages/datadog-instrumentations/src/webdriverio.js +31 -17
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +4 -13
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
- package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
- package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
- package/packages/datadog-plugin-bunyan/src/index.js +5 -8
- package/packages/datadog-plugin-cucumber/src/index.js +11 -0
- package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
- package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +82 -30
- package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
- package/packages/datadog-plugin-cypress/src/index.js +12 -6
- package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
- package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
- package/packages/datadog-plugin-electron/src/index.js +2 -0
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
- package/packages/datadog-plugin-jest/src/index.js +11 -4
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -2
- package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
- package/packages/datadog-plugin-mocha/src/index.js +606 -40
- package/packages/datadog-plugin-mysql/src/index.js +37 -0
- package/packages/datadog-plugin-nats/src/index.js +1 -1
- package/packages/datadog-plugin-next/src/index.js +1 -19
- package/packages/datadog-plugin-openai/src/tracing.js +41 -30
- package/packages/datadog-plugin-openai-agents/src/integration.js +279 -69
- package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
- package/packages/datadog-plugin-pg/src/index.js +1 -0
- package/packages/datadog-plugin-playwright/src/index.js +42 -13
- package/packages/datadog-plugin-vitest/src/index.js +14 -20
- package/packages/datadog-plugin-ws/src/util.js +2 -1
- package/packages/datadog-webpack/index.js +0 -11
- package/packages/dd-trace/index.js +0 -1
- package/packages/dd-trace/src/agent/info.js +6 -5
- package/packages/dd-trace/src/aiguard/client.js +100 -1
- package/packages/dd-trace/src/aiguard/errors.js +41 -0
- package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
- package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
- package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
- package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
- package/packages/dd-trace/src/aiguard/sdk.js +22 -278
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +13 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
- package/packages/dd-trace/src/carrier.js +356 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +27 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +6 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -0
- package/packages/dd-trace/src/config/index.js +13 -2
- package/packages/dd-trace/src/config/major-overrides.js +7 -0
- package/packages/dd-trace/src/config/supported-configurations.json +24 -0
- package/packages/dd-trace/src/datastreams/context.js +6 -1
- package/packages/dd-trace/src/datastreams/index.js +2 -0
- package/packages/dd-trace/src/datastreams/manager.js +14 -3
- package/packages/dd-trace/src/datastreams/pathway.js +23 -22
- package/packages/dd-trace/src/datastreams/processor.js +16 -13
- package/packages/dd-trace/src/datastreams/size.js +5 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
- package/packages/dd-trace/src/exporters/common/writer.js +11 -6
- package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +163 -5
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +241 -87
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +280 -20
- package/packages/dd-trace/src/llmobs/experiments/index.js +58 -59
- package/packages/dd-trace/src/llmobs/experiments/noop.js +71 -3
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +41 -5
- package/packages/dd-trace/src/llmobs/index.js +34 -2
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
- package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
- package/packages/dd-trace/src/llmobs/tagger.js +96 -0
- package/packages/dd-trace/src/llmobs/util.js +78 -0
- package/packages/dd-trace/src/noop/proxy.js +3 -4
- package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
- package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
- package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
- package/packages/dd-trace/src/opentracing/tracer.js +5 -2
- package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
- package/packages/dd-trace/src/plugin_manager.js +5 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +5 -65
- package/packages/dd-trace/src/plugins/util/test.js +93 -14
- package/packages/dd-trace/src/plugins/util/web.js +56 -2
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
- package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
- package/packages/dd-trace/src/proxy.js +38 -29
- package/packages/dd-trace/src/ritm.js +9 -2
- package/packages/dd-trace/src/serverless.js +37 -0
- package/packages/dd-trace/src/span_format.js +1 -0
- package/packages/dd-trace/src/span_stats.js +30 -3
- package/packages/dd-trace/src/storage-channels.js +86 -0
- package/packages/dd-trace/src/tracer.js +9 -2
- package/packages/dd-trace/src/tracer_metadata.js +14 -1
- package/packages/dd-trace/src/web-tags-cache.js +132 -0
- package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
- package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
- package/packages/dd-trace/src/feature-registry.js +0 -29
- package/packages/dd-trace/src/openfeature/register.js +0 -35
- package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { performance } = require('node:perf_hooks')
|
|
4
|
+
const dateNow = Date.now
|
|
3
5
|
const { fileURLToPath } = require('node:url')
|
|
4
6
|
|
|
5
7
|
const { channel } = require('dc-polyfill')
|
|
6
8
|
|
|
7
9
|
const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
|
|
8
10
|
const { storage } = require('../../datadog-core')
|
|
11
|
+
const {
|
|
12
|
+
getEfdRetryCountForDuration,
|
|
13
|
+
hasEfdRetries,
|
|
14
|
+
} = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
|
|
9
15
|
const log = require('../../dd-trace/src/log')
|
|
10
16
|
const {
|
|
11
17
|
sendWebdriverioWorkerMessage,
|
|
@@ -44,6 +50,8 @@ const {
|
|
|
44
50
|
TEST_FINAL_STATUS,
|
|
45
51
|
TEST_HAS_DYNAMIC_NAME,
|
|
46
52
|
TEST_FRAMEWORK_ADAPTER,
|
|
53
|
+
DYNAMIC_NAME_RE,
|
|
54
|
+
getFailedTestReplayPromise,
|
|
47
55
|
getTestSuiteExecutionKey,
|
|
48
56
|
isModifiedTest,
|
|
49
57
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
@@ -62,14 +70,24 @@ const {
|
|
|
62
70
|
|
|
63
71
|
const jasmineAdapterRunAsyncEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineAdapter_run:asyncEnd'
|
|
64
72
|
const jasmineDoneCh = 'ci:webdriverio:jasmine:done'
|
|
73
|
+
const jasmineExecuteAsyncEndCh = 'tracing:orchestrion:@wdio/utils:executeAsync:asyncEnd'
|
|
74
|
+
const jasmineExecuteAsyncErrorCh = 'tracing:orchestrion:@wdio/utils:executeAsync:error'
|
|
75
|
+
const jasmineExecuteAsyncStartCh = 'tracing:orchestrion:@wdio/utils:executeAsync:start'
|
|
65
76
|
const jasmineReporterSpecDoneEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_specDone:end'
|
|
77
|
+
const jasmineReporterSpecDoneStartCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_specDone:start'
|
|
66
78
|
const jasmineReporterSpecStartedEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_specStarted:end'
|
|
67
79
|
const jasmineReporterSuiteDoneEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_suiteDone:end'
|
|
68
80
|
const jasmineReporterSuiteStartedEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_suiteStarted:end'
|
|
81
|
+
const jasmineSpecAttemptDoneEndCh = 'tracing:orchestrion:jasmine-core:Spec_attemptDone:end'
|
|
82
|
+
const jasmineSpecExecuteStartCh = 'tracing:orchestrion:jasmine-core:Spec_execute:start'
|
|
69
83
|
const jasmineTestFunctionStartCh = 'tracing:orchestrion:@wdio/utils:testFrameworkFnWrapper:start'
|
|
70
84
|
const testFinishCh = channel('ci:mocha:test:finish')
|
|
85
|
+
const testRetryCh = channel('ci:mocha:test:retry')
|
|
71
86
|
const WEBDRIVERIO_JASMINE_ADAPTER = 'jasmine'
|
|
72
87
|
const workerFinishCh = channel('ci:mocha:worker:finish')
|
|
88
|
+
const WEBDRIVERIO_JASMINE_FAILED_EXPECTATION_COUNT = Symbol('webdriverioJasmineFailedExpectationCount')
|
|
89
|
+
const WEBDRIVERIO_JASMINE_FUNCTION_TYPE = Symbol('webdriverioJasmineFunctionType')
|
|
90
|
+
const WEBDRIVERIO_JASMINE_TEST = Symbol('webdriverioJasmineTest')
|
|
73
91
|
|
|
74
92
|
/**
|
|
75
93
|
* @typedef {object} WebdriverioJasmineResult
|
|
@@ -156,6 +174,15 @@ class MochaPlugin extends CiPlugin {
|
|
|
156
174
|
|
|
157
175
|
this._testTitleToParams = {}
|
|
158
176
|
this.sourceRoot = process.cwd()
|
|
177
|
+
this._pendingTestSuiteSpans = []
|
|
178
|
+
this._timeOrigin = dateNow()
|
|
179
|
+
this._perfOrigin = performance.now()
|
|
180
|
+
|
|
181
|
+
this.addSub('ci:mocha:session:start', () => {
|
|
182
|
+
this._pendingTestSuiteSpans = []
|
|
183
|
+
this._timeOrigin = dateNow()
|
|
184
|
+
this._perfOrigin = performance.now()
|
|
185
|
+
})
|
|
159
186
|
|
|
160
187
|
this.addSub('ci:mocha:worker:configuration', ({
|
|
161
188
|
libraryConfig,
|
|
@@ -170,7 +197,10 @@ class MochaPlugin extends CiPlugin {
|
|
|
170
197
|
this._setRepositoryRoot(repositoryRoot)
|
|
171
198
|
if (testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
172
199
|
this._webdriverioJasmineState = {
|
|
200
|
+
completedTestStatuses: new Map(),
|
|
173
201
|
currentResult: undefined,
|
|
202
|
+
pendingTestFinishCallbacks: [],
|
|
203
|
+
pendingTestFinishes: 0,
|
|
174
204
|
specs: specs || [],
|
|
175
205
|
suiteErrors: new Map(),
|
|
176
206
|
suiteFiles: new Map(),
|
|
@@ -188,11 +218,82 @@ class MochaPlugin extends CiPlugin {
|
|
|
188
218
|
const result = this._webdriverioJasmineState?.currentResult
|
|
189
219
|
const type = ctx.arguments?.[1]
|
|
190
220
|
if (type === 'Test' || (type === 'Hook' && result)) {
|
|
191
|
-
return this.#
|
|
221
|
+
return this.#configureWebdriverioJasmineFunction(ctx, result, type)
|
|
222
|
+
}
|
|
223
|
+
return storage('legacy').getStore()
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
this.addBind(jasmineExecuteAsyncStartCh, (ctx) => {
|
|
227
|
+
const currentStore = storage('legacy').getStore()
|
|
228
|
+
const test = currentStore?.[WEBDRIVERIO_JASMINE_TEST]
|
|
229
|
+
if (this.testFrameworkAdapter !== WEBDRIVERIO_JASMINE_ADAPTER || !test) {
|
|
230
|
+
return currentStore
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const functionType = currentStore[WEBDRIVERIO_JASMINE_FUNCTION_TYPE]
|
|
234
|
+
if (functionType === 'Test') {
|
|
235
|
+
ctx.retryCallback = error => this.#retryWebdriverioJasmineTest(test, error)
|
|
236
|
+
}
|
|
237
|
+
const nextStore = {
|
|
238
|
+
...test.currentStore,
|
|
239
|
+
[WEBDRIVERIO_JASMINE_FUNCTION_TYPE]: functionType,
|
|
240
|
+
}
|
|
241
|
+
if (functionType === 'Hook') {
|
|
242
|
+
nextStore[WEBDRIVERIO_JASMINE_FAILED_EXPECTATION_COUNT] =
|
|
243
|
+
this._webdriverioJasmineState.currentResult?.failedExpectations?.length || 0
|
|
244
|
+
}
|
|
245
|
+
return nextStore
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
this.addBind(jasmineSpecExecuteStartCh, (ctx) => {
|
|
249
|
+
if (this.testFrameworkAdapter !== WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
250
|
+
return storage('legacy').getStore()
|
|
192
251
|
}
|
|
252
|
+
|
|
253
|
+
this.#configureWebdriverioJasmineLifecycle(ctx)
|
|
193
254
|
return storage('legacy').getStore()
|
|
194
255
|
})
|
|
195
256
|
|
|
257
|
+
this.addSub(jasmineSpecAttemptDoneEndCh, (ctx) => {
|
|
258
|
+
if (this.testFrameworkAdapter !== WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const status = typeof ctx.result === 'string' ? ctx.result : ctx.self?.result?.status
|
|
263
|
+
const runnerStatus = this.#prepareWebdriverioJasmineAttempt(ctx.self, status)
|
|
264
|
+
if (typeof ctx.result === 'string') {
|
|
265
|
+
ctx.result = runnerStatus
|
|
266
|
+
} else if (ctx.self?.result) {
|
|
267
|
+
ctx.self.result.status = runnerStatus
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
this.addSub(jasmineExecuteAsyncErrorCh, (ctx) => {
|
|
272
|
+
const currentStore = ctx.currentStore || storage('legacy').getStore()
|
|
273
|
+
const test = currentStore?.[WEBDRIVERIO_JASMINE_TEST]
|
|
274
|
+
if (
|
|
275
|
+
this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER &&
|
|
276
|
+
test &&
|
|
277
|
+
currentStore[WEBDRIVERIO_JASMINE_FUNCTION_TYPE] === 'Hook'
|
|
278
|
+
) {
|
|
279
|
+
test.hasFinalHookFailure = true
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
this.addSub(jasmineExecuteAsyncEndCh, (ctx) => {
|
|
284
|
+
const currentStore = ctx.currentStore || storage('legacy').getStore()
|
|
285
|
+
const test = currentStore?.[WEBDRIVERIO_JASMINE_TEST]
|
|
286
|
+
const failedExpectationCount = currentStore?.[WEBDRIVERIO_JASMINE_FAILED_EXPECTATION_COUNT]
|
|
287
|
+
if (
|
|
288
|
+
this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER &&
|
|
289
|
+
test &&
|
|
290
|
+
currentStore[WEBDRIVERIO_JASMINE_FUNCTION_TYPE] === 'Hook' &&
|
|
291
|
+
this._webdriverioJasmineState.currentResult?.failedExpectations?.length > failedExpectationCount
|
|
292
|
+
) {
|
|
293
|
+
test.hasFinalHookFailure = true
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
|
|
196
297
|
this.addSub(jasmineReporterSuiteStartedEndCh, (ctx) => {
|
|
197
298
|
if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
198
299
|
const suite = ctx.arguments?.[0]
|
|
@@ -239,9 +340,32 @@ class MochaPlugin extends CiPlugin {
|
|
|
239
340
|
}
|
|
240
341
|
})
|
|
241
342
|
|
|
343
|
+
this.addBind(jasmineReporterSpecDoneStartCh, (ctx) => {
|
|
344
|
+
if (this.testFrameworkAdapter !== WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
345
|
+
return storage('legacy').getStore()
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const result = ctx.arguments?.[0]
|
|
349
|
+
const test = this._webdriverioJasmineState?.tests.get(result?.id)
|
|
350
|
+
const recoveredEarlyFlakeDetection = test?.isEarlyFlakeDetection &&
|
|
351
|
+
!test.hasFinalHookFailure && test.statuses.includes('pass')
|
|
352
|
+
if (
|
|
353
|
+
result.status === 'failed' &&
|
|
354
|
+
((test?.isQuarantined && !test.isAttemptToFix) || recoveredEarlyFlakeDetection)
|
|
355
|
+
) {
|
|
356
|
+
test.reportedStatus = result.status
|
|
357
|
+
result.status = 'passed'
|
|
358
|
+
}
|
|
359
|
+
return test?.currentStore || storage('legacy').getStore()
|
|
360
|
+
})
|
|
361
|
+
|
|
242
362
|
this.addSub(jasmineReporterSpecDoneEndCh, (ctx) => {
|
|
243
363
|
if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
244
|
-
|
|
364
|
+
const result = ctx.result
|
|
365
|
+
const finishPromise = this.#finishWebdriverioJasmineTest(ctx.arguments?.[0], ctx.self?._specs)
|
|
366
|
+
if (finishPromise) {
|
|
367
|
+
ctx.result = finishPromise.then(() => result)
|
|
368
|
+
}
|
|
245
369
|
}
|
|
246
370
|
})
|
|
247
371
|
|
|
@@ -358,7 +482,19 @@ class MochaPlugin extends CiPlugin {
|
|
|
358
482
|
if (!testSuiteSpan.context().getTag(TEST_STATUS)) {
|
|
359
483
|
testSuiteSpan.setTag(TEST_STATUS, status)
|
|
360
484
|
}
|
|
361
|
-
|
|
485
|
+
const exporter = this.tracer._exporter
|
|
486
|
+
if (exporter.deferTestSuiteSpan) {
|
|
487
|
+
exporter.deferTestSuiteSpan(testSuiteSpan)
|
|
488
|
+
testSuiteSpan.finish()
|
|
489
|
+
} else if (exporter.exportDeferredTestSuiteSpans) {
|
|
490
|
+
const finishTime = this._now()
|
|
491
|
+
this._pendingTestSuiteSpans.push({
|
|
492
|
+
span: testSuiteSpan,
|
|
493
|
+
finishTime,
|
|
494
|
+
})
|
|
495
|
+
} else {
|
|
496
|
+
testSuiteSpan.finish()
|
|
497
|
+
}
|
|
362
498
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
363
499
|
}
|
|
364
500
|
})
|
|
@@ -408,6 +544,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
408
544
|
})
|
|
409
545
|
|
|
410
546
|
this.addSub('ci:mocha:worker:finish', ({ onDone } = {}) => {
|
|
547
|
+
this._finishPendingTestSuiteSpans()
|
|
411
548
|
this.tracer._exporter.flush(onDone)
|
|
412
549
|
})
|
|
413
550
|
|
|
@@ -510,10 +647,13 @@ class MochaPlugin extends CiPlugin {
|
|
|
510
647
|
this.addSub('ci:mocha:test:retry', ({
|
|
511
648
|
span,
|
|
512
649
|
isFirstAttempt,
|
|
650
|
+
isFirstFailure = isFirstAttempt,
|
|
513
651
|
willBeRetried,
|
|
514
652
|
err,
|
|
515
653
|
test,
|
|
654
|
+
isAttemptToFixRetry,
|
|
516
655
|
isAtrRetry,
|
|
656
|
+
isEfdRetry,
|
|
517
657
|
promises,
|
|
518
658
|
}) => {
|
|
519
659
|
if (span) {
|
|
@@ -528,8 +668,12 @@ class MochaPlugin extends CiPlugin {
|
|
|
528
668
|
span.setTag(TEST_STATUS, 'fail')
|
|
529
669
|
if (!isFirstAttempt) {
|
|
530
670
|
span.setTag(TEST_IS_RETRY, 'true')
|
|
531
|
-
if (
|
|
671
|
+
if (isAttemptToFixRetry) {
|
|
672
|
+
span.setTag(TEST_RETRY_REASON, TEST_RETRY_REASON_TYPES.atf)
|
|
673
|
+
} else if (isAtrRetry) {
|
|
532
674
|
span.setTag(TEST_RETRY_REASON, TEST_RETRY_REASON_TYPES.atr)
|
|
675
|
+
} else if (isEfdRetry) {
|
|
676
|
+
span.setTag(TEST_RETRY_REASON, TEST_RETRY_REASON_TYPES.efd)
|
|
533
677
|
} else {
|
|
534
678
|
span.setTag(TEST_RETRY_REASON, TEST_RETRY_REASON_TYPES.ext)
|
|
535
679
|
}
|
|
@@ -543,7 +687,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
543
687
|
'test',
|
|
544
688
|
this.getTestTelemetryTags(span)
|
|
545
689
|
)
|
|
546
|
-
if (
|
|
690
|
+
if (isFirstFailure && willBeRetried && this.di && this.libraryConfig?.isDiEnabled) {
|
|
547
691
|
const probeInformation = this.addDiProbe(err)
|
|
548
692
|
if (probeInformation) {
|
|
549
693
|
const { file, line, stackIndex, setProbePromise } = probeInformation
|
|
@@ -557,7 +701,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
557
701
|
}
|
|
558
702
|
}
|
|
559
703
|
|
|
560
|
-
if (!
|
|
704
|
+
if (!isFirstFailure &&
|
|
561
705
|
willBeRetried &&
|
|
562
706
|
this.di &&
|
|
563
707
|
this.libraryConfig?.isDiEnabled &&
|
|
@@ -594,6 +738,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
594
738
|
isEarlyFlakeDetectionFaulty,
|
|
595
739
|
isTestManagementEnabled,
|
|
596
740
|
isParallel,
|
|
741
|
+
isFrameworkError,
|
|
597
742
|
onDone,
|
|
598
743
|
}) => {
|
|
599
744
|
this._exportPendingWorkerTraces()
|
|
@@ -609,8 +754,17 @@ class MochaPlugin extends CiPlugin {
|
|
|
609
754
|
if (error) {
|
|
610
755
|
this.testSessionSpan.setTag('error', error)
|
|
611
756
|
this.testModuleSpan.setTag('error', error)
|
|
757
|
+
if (isFrameworkError) {
|
|
758
|
+
this.tracer._exporter.setDeferredTestSuiteError?.(error)
|
|
759
|
+
for (const testSuiteSpan of this._testSuiteSpansByTestSuite.values()) {
|
|
760
|
+
testSuiteSpan.setTag(TEST_STATUS, 'fail')
|
|
761
|
+
testSuiteSpan.setTag('error', error)
|
|
762
|
+
}
|
|
763
|
+
}
|
|
612
764
|
}
|
|
613
765
|
|
|
766
|
+
this._finishPendingTestSuiteSpans()
|
|
767
|
+
|
|
614
768
|
if (isParallel) {
|
|
615
769
|
this.testSessionSpan.setTag(MOCHA_IS_PARALLEL, 'true')
|
|
616
770
|
}
|
|
@@ -648,6 +802,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
648
802
|
this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ABORT_REASON, 'faulty')
|
|
649
803
|
}
|
|
650
804
|
|
|
805
|
+
this.tracer._exporter.exportDeferredTestSuiteSpans?.()
|
|
651
806
|
this.testModuleSpan.finish()
|
|
652
807
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
653
808
|
this.testSessionSpan.finish()
|
|
@@ -669,6 +824,27 @@ class MochaPlugin extends CiPlugin {
|
|
|
669
824
|
})
|
|
670
825
|
}
|
|
671
826
|
|
|
827
|
+
/**
|
|
828
|
+
* Returns the current time in the coordinate system used by spans.
|
|
829
|
+
*
|
|
830
|
+
* @returns {number}
|
|
831
|
+
*/
|
|
832
|
+
_now () {
|
|
833
|
+
return this._timeOrigin + performance.now() - this._perfOrigin
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Finishes suites retained for a later framework finalization event.
|
|
838
|
+
*
|
|
839
|
+
* @returns {void}
|
|
840
|
+
*/
|
|
841
|
+
_finishPendingTestSuiteSpans () {
|
|
842
|
+
for (const { span, finishTime } of this._pendingTestSuiteSpans) {
|
|
843
|
+
span.finish(finishTime)
|
|
844
|
+
}
|
|
845
|
+
this._pendingTestSuiteSpans = []
|
|
846
|
+
}
|
|
847
|
+
|
|
672
848
|
/**
|
|
673
849
|
* Starts a Jasmine test span around WebdriverIO's test-function wrapper.
|
|
674
850
|
*
|
|
@@ -683,6 +859,9 @@ class MochaPlugin extends CiPlugin {
|
|
|
683
859
|
if (!state || !result?.id) {
|
|
684
860
|
return currentStore
|
|
685
861
|
}
|
|
862
|
+
if (state.completedTestStatuses.has(result.id)) {
|
|
863
|
+
return currentStore
|
|
864
|
+
}
|
|
686
865
|
|
|
687
866
|
const existingTest = state.tests.get(result.id)
|
|
688
867
|
if (existingTest) {
|
|
@@ -702,20 +881,333 @@ class MochaPlugin extends CiPlugin {
|
|
|
702
881
|
return currentStore
|
|
703
882
|
}
|
|
704
883
|
|
|
705
|
-
const
|
|
706
|
-
|
|
884
|
+
const testName = result.fullName || result.description
|
|
885
|
+
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.sourceRoot)
|
|
886
|
+
const properties = this.libraryConfig?.testManagementTests?.mocha
|
|
887
|
+
?.suites?.[testSuite]?.tests?.[testName]?.properties || {}
|
|
888
|
+
const isAttemptToFix = this.libraryConfig?.isTestManagementTestsEnabled && properties.attempt_to_fix
|
|
889
|
+
const isDisabled = this.libraryConfig?.isTestManagementTestsEnabled && properties.disabled
|
|
890
|
+
const isQuarantined = this.libraryConfig?.isTestManagementTestsEnabled && properties.quarantined
|
|
891
|
+
const isModified = this.libraryConfig?.isImpactedTestsEnabled && isModifiedTest(
|
|
892
|
+
getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot || this.sourceRoot),
|
|
893
|
+
null,
|
|
894
|
+
null,
|
|
895
|
+
this.libraryConfig.modifiedFiles,
|
|
896
|
+
this.constructor.id
|
|
897
|
+
)
|
|
898
|
+
const knownTests = this.libraryConfig?.knownTests?.mocha
|
|
899
|
+
const isNew = this.libraryConfig?.isKnownTestsEnabled && knownTests &&
|
|
900
|
+
!(knownTests[testSuite] || []).includes(testName)
|
|
901
|
+
const isEarlyFlakeDetection = this.libraryConfig?.isEarlyFlakeDetectionEnabled &&
|
|
902
|
+
hasEfdRetries(this.libraryConfig.earlyFlakeDetectionRetryPolicy) &&
|
|
903
|
+
!isAttemptToFix && !isDisabled && (isNew || isModified)
|
|
904
|
+
const isAtr = this.libraryConfig?.isFlakyTestRetriesEnabled &&
|
|
905
|
+
!isAttemptToFix && !isEarlyFlakeDetection
|
|
906
|
+
let retryCount = 0
|
|
907
|
+
if (isAttemptToFix) {
|
|
908
|
+
retryCount = this.libraryConfig.testManagementAttemptToFixRetries
|
|
909
|
+
} else if (isEarlyFlakeDetection) {
|
|
910
|
+
retryCount = this.libraryConfig.earlyFlakeDetectionRetryPolicy.schedulingRetryCount
|
|
911
|
+
} else if (isAtr) {
|
|
912
|
+
retryCount = this.libraryConfig.flakyTestRetriesCount
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
const test = {
|
|
916
|
+
attempt: 0,
|
|
917
|
+
attemptStart: undefined,
|
|
918
|
+
currentStore: undefined,
|
|
919
|
+
earlyFlakeAbortReason: undefined,
|
|
920
|
+
hasDynamicName: isNew && DYNAMIC_NAME_RE.test(testName),
|
|
921
|
+
hasFailedAttempt: false,
|
|
922
|
+
hasFinalHookFailure: false,
|
|
923
|
+
isAttemptToFix,
|
|
924
|
+
isAtr,
|
|
925
|
+
isDisabled,
|
|
926
|
+
isEarlyFlakeDetection,
|
|
927
|
+
isModified,
|
|
928
|
+
isNew,
|
|
929
|
+
isQuarantined,
|
|
930
|
+
reportedStatus: undefined,
|
|
931
|
+
retryCount,
|
|
932
|
+
retryWait: undefined,
|
|
933
|
+
runnerStatus: undefined,
|
|
934
|
+
span: undefined,
|
|
935
|
+
statuses: [],
|
|
936
|
+
testName,
|
|
707
937
|
testSuiteAbsolutePath,
|
|
708
938
|
title: result.description,
|
|
939
|
+
}
|
|
940
|
+
state.tests.set(result.id, test)
|
|
941
|
+
this.#startWebdriverioJasmineAttempt(test, currentStore)
|
|
942
|
+
|
|
943
|
+
return test.currentStore
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* Applies a managed Jasmine test's skip and retry policy to WebdriverIO's function wrapper.
|
|
948
|
+
*
|
|
949
|
+
* @param {object} context
|
|
950
|
+
* @param {WebdriverioJasmineResult|undefined} result
|
|
951
|
+
* @param {'Test'|'Hook'} type
|
|
952
|
+
* @returns {object|undefined}
|
|
953
|
+
*/
|
|
954
|
+
#configureWebdriverioJasmineFunction (context, result, type) {
|
|
955
|
+
const currentStore = this.#startWebdriverioJasmineTest(result)
|
|
956
|
+
const test = this._webdriverioJasmineState?.tests.get(result?.id)
|
|
957
|
+
if (!test) {
|
|
958
|
+
return currentStore
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
if (test.isAttemptToFix || test.isAtr || test.isEarlyFlakeDetection) {
|
|
962
|
+
context.arguments[6] = 0
|
|
963
|
+
}
|
|
964
|
+
return {
|
|
965
|
+
...test.currentStore,
|
|
966
|
+
[WEBDRIVERIO_JASMINE_FUNCTION_TYPE]: type,
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* Starts one WebdriverIO Jasmine test attempt under its suite span.
|
|
972
|
+
*
|
|
973
|
+
* @param {object} test
|
|
974
|
+
* @param {object|undefined} parentStore
|
|
975
|
+
* @returns {void}
|
|
976
|
+
*/
|
|
977
|
+
#startWebdriverioJasmineAttempt (test, parentStore) {
|
|
978
|
+
test.attemptStart = performance.now()
|
|
979
|
+
test.hasFinalHookFailure = false
|
|
980
|
+
const span = this.startTestSpan({
|
|
981
|
+
hasDynamicName: test.hasDynamicName,
|
|
982
|
+
isAttemptToFix: test.isAttemptToFix,
|
|
983
|
+
isDisabled: test.isDisabled,
|
|
984
|
+
isEfdRetry: test.isEarlyFlakeDetection && test.attempt > 0,
|
|
985
|
+
isModified: test.isModified,
|
|
986
|
+
isNew: test.isNew,
|
|
987
|
+
isParallel: true,
|
|
988
|
+
isQuarantined: test.isQuarantined,
|
|
989
|
+
testName: test.testName,
|
|
990
|
+
testSuiteAbsolutePath: test.testSuiteAbsolutePath,
|
|
991
|
+
title: test.title,
|
|
709
992
|
})
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
993
|
+
test.span = span
|
|
994
|
+
test.currentStore = {
|
|
995
|
+
...parentStore,
|
|
713
996
|
span,
|
|
714
|
-
|
|
715
|
-
}
|
|
997
|
+
[WEBDRIVERIO_JASMINE_TEST]: test,
|
|
998
|
+
}
|
|
716
999
|
this.activeTestSpan = span
|
|
1000
|
+
}
|
|
717
1001
|
|
|
718
|
-
|
|
1002
|
+
/**
|
|
1003
|
+
* Delays Jasmine's parent runner until every Datadog-managed spec execution has completed.
|
|
1004
|
+
*
|
|
1005
|
+
* @param {object} context
|
|
1006
|
+
* @returns {void}
|
|
1007
|
+
*/
|
|
1008
|
+
#configureWebdriverioJasmineLifecycle (context) {
|
|
1009
|
+
const isLegacyJasmine = Boolean(context.self?.queueableFn)
|
|
1010
|
+
const spec = isLegacyJasmine ? context.self : context.arguments?.[0]
|
|
1011
|
+
const onComplete = context.arguments?.[1]
|
|
1012
|
+
const originalTestFunction = spec?.queueableFn?.fn
|
|
1013
|
+
if (!spec?.id || typeof onComplete !== 'function' || typeof originalTestFunction !== 'function') {
|
|
1014
|
+
return
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
if (this.libraryConfig?.isTestManagementTestsEnabled) {
|
|
1018
|
+
this.#startWebdriverioJasmineTest(spec.result)
|
|
1019
|
+
const test = this._webdriverioJasmineState?.tests.get(spec.id)
|
|
1020
|
+
if (test?.isDisabled && !test.isAttemptToFix) {
|
|
1021
|
+
spec.pend('Skipped by Datadog Test Management')
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
const executionArguments = [...context.arguments]
|
|
1026
|
+
context.arguments[1] = (...completeArguments) => {
|
|
1027
|
+
const test = this._webdriverioJasmineState?.tests.get(spec.id)
|
|
1028
|
+
if (!test?.willRetry) {
|
|
1029
|
+
return onComplete(...completeArguments)
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
test.willRetry = false
|
|
1033
|
+
const retry = () => {
|
|
1034
|
+
try {
|
|
1035
|
+
this.#startNextWebdriverioJasmineAttempt(test)
|
|
1036
|
+
spec.reset()
|
|
1037
|
+
spec.queueableFn.fn = originalTestFunction
|
|
1038
|
+
if (isLegacyJasmine) {
|
|
1039
|
+
spec.execute(
|
|
1040
|
+
executionArguments[0],
|
|
1041
|
+
onComplete,
|
|
1042
|
+
executionArguments[2],
|
|
1043
|
+
executionArguments[3]
|
|
1044
|
+
)
|
|
1045
|
+
} else {
|
|
1046
|
+
context.self._executeSpec(spec, onComplete)
|
|
1047
|
+
}
|
|
1048
|
+
} catch (error) {
|
|
1049
|
+
log.error('WebdriverIO Jasmine retry error', error)
|
|
1050
|
+
onComplete(...completeArguments)
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
const retryWait = test.retryWait
|
|
1054
|
+
test.retryWait = undefined
|
|
1055
|
+
if (retryWait?.then) {
|
|
1056
|
+
retryWait.then(retry, retry)
|
|
1057
|
+
} else {
|
|
1058
|
+
retry()
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* Selects the runner-visible status and whether Jasmine should execute the full spec again.
|
|
1065
|
+
*
|
|
1066
|
+
* @param {object|undefined} spec
|
|
1067
|
+
* @param {string|undefined} status
|
|
1068
|
+
* @returns {string|undefined}
|
|
1069
|
+
*/
|
|
1070
|
+
#prepareWebdriverioJasmineAttempt (spec, status) {
|
|
1071
|
+
const state = this._webdriverioJasmineState
|
|
1072
|
+
const completedStatus = state?.completedTestStatuses.get(spec?.id)
|
|
1073
|
+
if (completedStatus) {
|
|
1074
|
+
return completedStatus
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
const test = state?.tests.get(spec?.id)
|
|
1078
|
+
if (!test || !status) {
|
|
1079
|
+
return status
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
const testStatus = getJasmineStatus(status)
|
|
1083
|
+
if (
|
|
1084
|
+
testStatus !== 'skip' &&
|
|
1085
|
+
test.isEarlyFlakeDetection &&
|
|
1086
|
+
test.attempt === 0
|
|
1087
|
+
) {
|
|
1088
|
+
test.retryCount = getEfdRetryCountForDuration(
|
|
1089
|
+
performance.now() - test.attemptStart,
|
|
1090
|
+
this.libraryConfig.earlyFlakeDetectionRetryPolicy
|
|
1091
|
+
)
|
|
1092
|
+
if (test.retryCount === 0) {
|
|
1093
|
+
test.earlyFlakeAbortReason = 'slow'
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
const hasManagedRetry = testStatus !== 'skip' && test.attempt < test.retryCount
|
|
1098
|
+
test.willRetry = hasManagedRetry && (
|
|
1099
|
+
test.isAttemptToFix ||
|
|
1100
|
+
test.isEarlyFlakeDetection ||
|
|
1101
|
+
(test.isAtr && testStatus === 'fail' && !test.hasFinalHookFailure)
|
|
1102
|
+
)
|
|
1103
|
+
|
|
1104
|
+
let runnerStatus = status
|
|
1105
|
+
const recoveredEarlyFlakeDetection = test.isEarlyFlakeDetection &&
|
|
1106
|
+
!test.hasFinalHookFailure && test.statuses.includes('pass')
|
|
1107
|
+
if (
|
|
1108
|
+
testStatus === 'fail' &&
|
|
1109
|
+
(test.willRetry || (test.isQuarantined && !test.isAttemptToFix) || recoveredEarlyFlakeDetection)
|
|
1110
|
+
) {
|
|
1111
|
+
runnerStatus = 'passed'
|
|
1112
|
+
} else if (testStatus !== 'skip' && !test.willRetry && test.isAttemptToFix) {
|
|
1113
|
+
runnerStatus = test.statuses.every(previousStatus => previousStatus === 'pass') && testStatus === 'pass'
|
|
1114
|
+
? 'passed'
|
|
1115
|
+
: 'failed'
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
if (runnerStatus !== status) {
|
|
1119
|
+
test.reportedStatus = status
|
|
1120
|
+
}
|
|
1121
|
+
if (!test.willRetry) {
|
|
1122
|
+
test.runnerStatus = runnerStatus
|
|
1123
|
+
}
|
|
1124
|
+
return runnerStatus
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/**
|
|
1128
|
+
* Finishes one Datadog-managed Jasmine attempt before the full spec is executed again.
|
|
1129
|
+
*
|
|
1130
|
+
* @param {object} test
|
|
1131
|
+
* @param {WebdriverioJasmineResult} result
|
|
1132
|
+
* @returns {void}
|
|
1133
|
+
*/
|
|
1134
|
+
#finishWebdriverioJasmineRetry (test, result) {
|
|
1135
|
+
const status = getJasmineStatus(test.reportedStatus || result.status)
|
|
1136
|
+
const error = getJasmineError(result)
|
|
1137
|
+
test.statuses.push(status)
|
|
1138
|
+
|
|
1139
|
+
if (error) {
|
|
1140
|
+
const isFirstFailure = !test.hasFailedAttempt
|
|
1141
|
+
test.hasFailedAttempt = true
|
|
1142
|
+
const promises = {}
|
|
1143
|
+
testRetryCh.publish({
|
|
1144
|
+
err: error,
|
|
1145
|
+
isAttemptToFixRetry: test.isAttemptToFix && test.attempt > 0,
|
|
1146
|
+
isAtrRetry: test.isAtr && test.attempt > 0,
|
|
1147
|
+
isEfdRetry: test.isEarlyFlakeDetection && test.attempt > 0,
|
|
1148
|
+
isFirstAttempt: test.attempt === 0,
|
|
1149
|
+
isFirstFailure,
|
|
1150
|
+
promises,
|
|
1151
|
+
test,
|
|
1152
|
+
willBeRetried: true,
|
|
1153
|
+
...test.currentStore,
|
|
1154
|
+
})
|
|
1155
|
+
test.retryWait = getFailedTestReplayPromise(promises)
|
|
1156
|
+
} else {
|
|
1157
|
+
testFinishCh.publish({
|
|
1158
|
+
hasBeenRetried: test.isAtr && test.attempt > 0,
|
|
1159
|
+
isAttemptToFixRetry: test.isAttemptToFix && test.attempt > 0,
|
|
1160
|
+
isAtrRetry: false,
|
|
1161
|
+
isLastRetry: false,
|
|
1162
|
+
status,
|
|
1163
|
+
...test.currentStore,
|
|
1164
|
+
})
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* Advances a Jasmine test and starts its next attempt span.
|
|
1170
|
+
*
|
|
1171
|
+
* @param {object} test
|
|
1172
|
+
* @returns {void}
|
|
1173
|
+
*/
|
|
1174
|
+
#startNextWebdriverioJasmineAttempt (test) {
|
|
1175
|
+
test.attempt++
|
|
1176
|
+
test.reportedStatus = undefined
|
|
1177
|
+
this.#startWebdriverioJasmineAttempt(test, test.currentStore)
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/**
|
|
1181
|
+
* Finishes an intermediate native WebdriverIO retry and starts its next span.
|
|
1182
|
+
*
|
|
1183
|
+
* @param {object} test
|
|
1184
|
+
* @param {Error|undefined} error
|
|
1185
|
+
* @returns {Promise<void>|undefined}
|
|
1186
|
+
*/
|
|
1187
|
+
#retryWebdriverioJasmineTest (test, error) {
|
|
1188
|
+
test.statuses.push('fail')
|
|
1189
|
+
const isFirstFailure = !test.hasFailedAttempt
|
|
1190
|
+
test.hasFailedAttempt = true
|
|
1191
|
+
const promises = {}
|
|
1192
|
+
testRetryCh.publish({
|
|
1193
|
+
err: error,
|
|
1194
|
+
isAttemptToFixRetry: false,
|
|
1195
|
+
isAtrRetry: false,
|
|
1196
|
+
isEfdRetry: false,
|
|
1197
|
+
isFirstAttempt: test.attempt === 0,
|
|
1198
|
+
isFirstFailure,
|
|
1199
|
+
promises,
|
|
1200
|
+
test,
|
|
1201
|
+
willBeRetried: true,
|
|
1202
|
+
...test.currentStore,
|
|
1203
|
+
})
|
|
1204
|
+
|
|
1205
|
+
const retryWait = getFailedTestReplayPromise(promises)
|
|
1206
|
+
if (retryWait?.then) {
|
|
1207
|
+
const startNextAttempt = () => this.#startNextWebdriverioJasmineAttempt(test)
|
|
1208
|
+
return retryWait.then(startNextAttempt, startNextAttempt)
|
|
1209
|
+
}
|
|
1210
|
+
this.#startNextWebdriverioJasmineAttempt(test)
|
|
719
1211
|
}
|
|
720
1212
|
|
|
721
1213
|
/**
|
|
@@ -723,7 +1215,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
723
1215
|
*
|
|
724
1216
|
* @param {WebdriverioJasmineResult|undefined} result
|
|
725
1217
|
* @param {string[]} [specs]
|
|
726
|
-
* @returns {void}
|
|
1218
|
+
* @returns {Promise<void>|undefined}
|
|
727
1219
|
*/
|
|
728
1220
|
#finishWebdriverioJasmineTest (result, specs) {
|
|
729
1221
|
const state = this._webdriverioJasmineState
|
|
@@ -737,20 +1229,86 @@ class MochaPlugin extends CiPlugin {
|
|
|
737
1229
|
return
|
|
738
1230
|
}
|
|
739
1231
|
|
|
740
|
-
|
|
1232
|
+
if (state.currentResult?.id === result.id) {
|
|
1233
|
+
state.currentResult = undefined
|
|
1234
|
+
}
|
|
1235
|
+
if (test.willRetry) {
|
|
1236
|
+
this.#finishWebdriverioJasmineRetry(test, result)
|
|
1237
|
+
return
|
|
1238
|
+
}
|
|
1239
|
+
if (test._ddShouldWaitForHitProbe) {
|
|
1240
|
+
delete test._ddShouldWaitForHitProbe
|
|
1241
|
+
state.pendingTestFinishes++
|
|
1242
|
+
const finishTest = () => {
|
|
1243
|
+
this.#completeWebdriverioJasmineTest(test, result)
|
|
1244
|
+
state.pendingTestFinishes--
|
|
1245
|
+
if (state.pendingTestFinishes === 0) {
|
|
1246
|
+
const callbacks = state.pendingTestFinishCallbacks
|
|
1247
|
+
state.pendingTestFinishCallbacks = []
|
|
1248
|
+
for (const callback of callbacks) {
|
|
1249
|
+
callback()
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
return this.waitForDiBreakpointHits().then(finishTest, finishTest)
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
this.#completeWebdriverioJasmineTest(test, result)
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/**
|
|
1260
|
+
* Finalizes the last reported attempt for one WebdriverIO Jasmine test.
|
|
1261
|
+
*
|
|
1262
|
+
* @param {object} test
|
|
1263
|
+
* @param {WebdriverioJasmineResult} result
|
|
1264
|
+
* @returns {void}
|
|
1265
|
+
*/
|
|
1266
|
+
#completeWebdriverioJasmineTest (test, result) {
|
|
1267
|
+
const state = this._webdriverioJasmineState
|
|
1268
|
+
|
|
1269
|
+
const status = getJasmineStatus(test.reportedStatus || result.status)
|
|
741
1270
|
const error = getJasmineError(result)
|
|
742
1271
|
if (error) {
|
|
743
1272
|
test.span.setTag('error', error)
|
|
744
1273
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
1274
|
+
if (!test.isEarlyFlakeDetection || status !== 'skip') {
|
|
1275
|
+
test.statuses.push(status)
|
|
1276
|
+
}
|
|
1277
|
+
const hasFailedAllRetries = test.attempt > 0 &&
|
|
1278
|
+
(test.isAttemptToFix || test.isAtr || test.isEarlyFlakeDetection) &&
|
|
1279
|
+
test.statuses.every(testStatus => testStatus === 'fail')
|
|
1280
|
+
const isSkipped = status === 'skip'
|
|
1281
|
+
const attemptToFixPassed = !isSkipped && test.isAttemptToFix &&
|
|
1282
|
+
test.statuses.every(testStatus => testStatus === 'pass')
|
|
1283
|
+
const attemptToFixFailed = !isSkipped && test.isAttemptToFix && !attemptToFixPassed
|
|
1284
|
+
let finalStatus = status
|
|
1285
|
+
if (isSkipped || (!test.isAttemptToFix && (test.isDisabled || test.isQuarantined))) {
|
|
1286
|
+
finalStatus = 'skip'
|
|
1287
|
+
} else if (test.isAttemptToFix) {
|
|
1288
|
+
finalStatus = attemptToFixPassed ? 'pass' : 'fail'
|
|
1289
|
+
} else if (test.isEarlyFlakeDetection && status !== 'skip' && !test.hasFinalHookFailure) {
|
|
1290
|
+
finalStatus = test.statuses.includes('pass') ? 'pass' : 'fail'
|
|
749
1291
|
}
|
|
1292
|
+
testFinishCh.publish({
|
|
1293
|
+
attemptToFixFailed,
|
|
1294
|
+
attemptToFixPassed,
|
|
1295
|
+
earlyFlakeAbortReason: test.earlyFlakeAbortReason,
|
|
1296
|
+
finalStatus,
|
|
1297
|
+
hasBeenRetried: !test.isAttemptToFix && !test.isEarlyFlakeDetection && test.attempt > 0,
|
|
1298
|
+
hasFailedAllRetries,
|
|
1299
|
+
isAttemptToFixRetry: test.isAttemptToFix && test.attempt > 0,
|
|
1300
|
+
isAtrRetry: test.isAtr && test.attempt > 0,
|
|
1301
|
+
isLastRetry: true,
|
|
1302
|
+
status,
|
|
1303
|
+
...test.currentStore,
|
|
1304
|
+
})
|
|
1305
|
+
state.completedTestStatuses.set(result.id, test.runnerStatus || result.status)
|
|
1306
|
+
state.tests.delete(result.id)
|
|
750
1307
|
|
|
1308
|
+
const suiteStatus = test.isQuarantined && !test.isAttemptToFix ? 'pass' : finalStatus
|
|
751
1309
|
const previousStatus = state.suiteStatuses.get(test.testSuiteAbsolutePath)
|
|
752
|
-
if (
|
|
753
|
-
state.suiteStatuses.set(test.testSuiteAbsolutePath,
|
|
1310
|
+
if (suiteStatus === 'fail' || !previousStatus || previousStatus === 'skip') {
|
|
1311
|
+
state.suiteStatuses.set(test.testSuiteAbsolutePath, suiteStatus)
|
|
754
1312
|
}
|
|
755
1313
|
}
|
|
756
1314
|
|
|
@@ -765,28 +1323,28 @@ class MochaPlugin extends CiPlugin {
|
|
|
765
1323
|
*/
|
|
766
1324
|
#finishWebdriverioJasmineWorker (context) {
|
|
767
1325
|
const state = this._webdriverioJasmineState
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
1326
|
+
const reportWorker = onDone => {
|
|
1327
|
+
const results = []
|
|
1328
|
+
const reportedFiles = new Set()
|
|
1329
|
+
for (const [file, status] of state.suiteStatuses) {
|
|
1330
|
+
const error = state.suiteErrors.get(file)
|
|
1331
|
+
const result = { file, status }
|
|
1332
|
+
if (error) {
|
|
1333
|
+
result.error = {
|
|
1334
|
+
message: error.message,
|
|
1335
|
+
stack: error.stack,
|
|
1336
|
+
}
|
|
777
1337
|
}
|
|
1338
|
+
results.push(result)
|
|
1339
|
+
reportedFiles.add(file)
|
|
778
1340
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
if (!reportedFiles.has(file)) {
|
|
785
|
-
results.push({ file, status: 'skip' })
|
|
1341
|
+
for (const spec of state.specs) {
|
|
1342
|
+
const file = normalizeJasmineFile(spec)
|
|
1343
|
+
if (!reportedFiles.has(file)) {
|
|
1344
|
+
results.push({ file, status: 'skip' })
|
|
1345
|
+
}
|
|
786
1346
|
}
|
|
787
|
-
}
|
|
788
1347
|
|
|
789
|
-
const waitForWorker = onDone => {
|
|
790
1348
|
sendWebdriverioWorkerMessage({
|
|
791
1349
|
origin: 'datadog',
|
|
792
1350
|
name: SUITE_FINISH,
|
|
@@ -797,6 +1355,14 @@ class MochaPlugin extends CiPlugin {
|
|
|
797
1355
|
}
|
|
798
1356
|
}, () => workerFinishCh.publish({ onDone }))
|
|
799
1357
|
}
|
|
1358
|
+
|
|
1359
|
+
const waitForWorker = onDone => {
|
|
1360
|
+
if (state?.pendingTestFinishes) {
|
|
1361
|
+
state.pendingTestFinishCallbacks.push(() => reportWorker(onDone))
|
|
1362
|
+
} else {
|
|
1363
|
+
reportWorker(onDone)
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
800
1366
|
context.resolveCallback = waitForWorker
|
|
801
1367
|
context.rejectCallback = waitForWorker
|
|
802
1368
|
}
|