dd-trace 6.0.0-pre-a99e5ea → 6.0.0-pre-2d874ad
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/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
const semver = require('semver')
|
|
2
|
+
|
|
1
3
|
const { addHook, channel, AsyncResource } = require('./helpers/instrument')
|
|
2
4
|
const shimmer = require('../../datadog-shimmer')
|
|
3
|
-
const { parseAnnotations } = require('../../dd-trace/src/plugins/util/test')
|
|
5
|
+
const { parseAnnotations, getTestSuitePath } = require('../../dd-trace/src/plugins/util/test')
|
|
6
|
+
const log = require('../../dd-trace/src/log')
|
|
4
7
|
|
|
5
8
|
const testStartCh = channel('ci:playwright:test:start')
|
|
6
9
|
const testFinishCh = channel('ci:playwright:test:finish')
|
|
@@ -8,6 +11,9 @@ const testFinishCh = channel('ci:playwright:test:finish')
|
|
|
8
11
|
const testSessionStartCh = channel('ci:playwright:session:start')
|
|
9
12
|
const testSessionFinishCh = channel('ci:playwright:session:finish')
|
|
10
13
|
|
|
14
|
+
const libraryConfigurationCh = channel('ci:playwright:library-configuration')
|
|
15
|
+
const knownTestsCh = channel('ci:playwright:known-tests')
|
|
16
|
+
|
|
11
17
|
const testSuiteStartCh = channel('ci:playwright:test-suite:start')
|
|
12
18
|
const testSuiteFinishCh = channel('ci:playwright:test-suite:finish')
|
|
13
19
|
|
|
@@ -16,6 +22,8 @@ const testSuiteToAr = new Map()
|
|
|
16
22
|
const testSuiteToTestStatuses = new Map()
|
|
17
23
|
const testSuiteToErrors = new Map()
|
|
18
24
|
|
|
25
|
+
let applyRepeatEachIndex = null
|
|
26
|
+
|
|
19
27
|
let startedSuites = []
|
|
20
28
|
|
|
21
29
|
const STATUS_TO_TEST_STATUS = {
|
|
@@ -26,6 +34,44 @@ const STATUS_TO_TEST_STATUS = {
|
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
let remainingTestsByFile = {}
|
|
37
|
+
let isEarlyFlakeDetectionEnabled = false
|
|
38
|
+
let earlyFlakeDetectionNumRetries = 0
|
|
39
|
+
let knownTests = []
|
|
40
|
+
let rootDir = ''
|
|
41
|
+
const MINIMUM_SUPPORTED_VERSION_EFD = '1.38.0'
|
|
42
|
+
|
|
43
|
+
function isNewTest (test) {
|
|
44
|
+
const testSuite = getTestSuitePath(test._requireFile, rootDir)
|
|
45
|
+
const testsForSuite = knownTests?.playwright?.[testSuite] || []
|
|
46
|
+
|
|
47
|
+
return !testsForSuite.includes(test.title)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getSuiteType (test, type) {
|
|
51
|
+
let suite = test.parent
|
|
52
|
+
while (suite && suite._type !== type) {
|
|
53
|
+
suite = suite.parent
|
|
54
|
+
}
|
|
55
|
+
return suite
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Copy of Suite#_deepClone but with a function to filter tests
|
|
59
|
+
function deepCloneSuite (suite, filterTest) {
|
|
60
|
+
const copy = suite._clone()
|
|
61
|
+
for (const entry of suite._entries) {
|
|
62
|
+
if (entry.constructor.name === 'Suite') {
|
|
63
|
+
copy._addSuite(deepCloneSuite(entry, filterTest))
|
|
64
|
+
} else {
|
|
65
|
+
if (filterTest(entry)) {
|
|
66
|
+
const copiedTest = entry._clone()
|
|
67
|
+
copiedTest._ddIsNew = true
|
|
68
|
+
copiedTest._ddIsEfdRetry = true
|
|
69
|
+
copy._addTest(copiedTest)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return copy
|
|
74
|
+
}
|
|
29
75
|
|
|
30
76
|
function getTestsBySuiteFromTestGroups (testGroups) {
|
|
31
77
|
return testGroups.reduce((acc, { requireFile, tests }) => {
|
|
@@ -153,8 +199,11 @@ function getTestSuiteError (testSuiteAbsolutePath) {
|
|
|
153
199
|
function testBeginHandler (test, browserName) {
|
|
154
200
|
const {
|
|
155
201
|
_requireFile: testSuiteAbsolutePath,
|
|
156
|
-
title: testName,
|
|
157
|
-
|
|
202
|
+
title: testName,
|
|
203
|
+
_type,
|
|
204
|
+
location: {
|
|
205
|
+
line: testSourceLine
|
|
206
|
+
}
|
|
158
207
|
} = test
|
|
159
208
|
|
|
160
209
|
if (_type === 'beforeAll' || _type === 'afterAll') {
|
|
@@ -198,7 +247,14 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
|
|
|
198
247
|
const testResult = results[results.length - 1]
|
|
199
248
|
const testAsyncResource = testToAr.get(test)
|
|
200
249
|
testAsyncResource.runInAsyncScope(() => {
|
|
201
|
-
testFinishCh.publish({
|
|
250
|
+
testFinishCh.publish({
|
|
251
|
+
testStatus,
|
|
252
|
+
steps: testResult.steps,
|
|
253
|
+
error,
|
|
254
|
+
extraTags: annotationTags,
|
|
255
|
+
isNew: test._ddIsNew,
|
|
256
|
+
isEfdRetry: test._ddIsEfdRetry
|
|
257
|
+
})
|
|
202
258
|
})
|
|
203
259
|
|
|
204
260
|
if (testSuiteToTestStatuses.has(testSuiteAbsolutePath)) {
|
|
@@ -309,14 +365,54 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
309
365
|
|
|
310
366
|
function runnerHook (runnerExport, playwrightVersion) {
|
|
311
367
|
shimmer.wrap(runnerExport.Runner.prototype, 'runAllTests', runAllTests => async function () {
|
|
368
|
+
let onDone
|
|
369
|
+
|
|
312
370
|
const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
313
|
-
|
|
371
|
+
|
|
372
|
+
rootDir = getRootDir(this)
|
|
314
373
|
|
|
315
374
|
const processArgv = process.argv.slice(2).join(' ')
|
|
316
375
|
const command = `playwright ${processArgv}`
|
|
317
376
|
testSessionAsyncResource.runInAsyncScope(() => {
|
|
318
377
|
testSessionStartCh.publish({ command, frameworkVersion: playwrightVersion, rootDir })
|
|
319
378
|
})
|
|
379
|
+
|
|
380
|
+
const configurationPromise = new Promise((resolve) => {
|
|
381
|
+
onDone = resolve
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
testSessionAsyncResource.runInAsyncScope(() => {
|
|
385
|
+
libraryConfigurationCh.publish({ onDone })
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
const { err, libraryConfig } = await configurationPromise
|
|
390
|
+
if (!err) {
|
|
391
|
+
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
|
|
392
|
+
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
|
|
393
|
+
}
|
|
394
|
+
} catch (e) {
|
|
395
|
+
log.error(e)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (isEarlyFlakeDetectionEnabled && semver.gte(playwrightVersion, MINIMUM_SUPPORTED_VERSION_EFD)) {
|
|
399
|
+
const knownTestsPromise = new Promise((resolve) => {
|
|
400
|
+
onDone = resolve
|
|
401
|
+
})
|
|
402
|
+
testSessionAsyncResource.runInAsyncScope(() => {
|
|
403
|
+
knownTestsCh.publish({ onDone })
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
try {
|
|
407
|
+
const { err, knownTests: receivedKnownTests } = await knownTestsPromise
|
|
408
|
+
if (!err) {
|
|
409
|
+
knownTests = receivedKnownTests
|
|
410
|
+
}
|
|
411
|
+
} catch (err) {
|
|
412
|
+
log.error(err)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
320
416
|
const projects = getProjectsFromRunner(this)
|
|
321
417
|
|
|
322
418
|
const runAllTestsReturn = await runAllTests.apply(this, arguments)
|
|
@@ -334,7 +430,6 @@ function runnerHook (runnerExport, playwrightVersion) {
|
|
|
334
430
|
|
|
335
431
|
const sessionStatus = runAllTestsReturn.status || runAllTestsReturn
|
|
336
432
|
|
|
337
|
-
let onDone
|
|
338
433
|
const flushWait = new Promise(resolve => {
|
|
339
434
|
onDone = resolve
|
|
340
435
|
})
|
|
@@ -394,3 +489,53 @@ addHook({
|
|
|
394
489
|
file: 'lib/runner/dispatcher.js',
|
|
395
490
|
versions: ['>=1.38.0']
|
|
396
491
|
}, (dispatcher) => dispatcherHookNew(dispatcher, dispatcherRunWrapperNew))
|
|
492
|
+
|
|
493
|
+
// Hook used for early flake detection. EFD only works from >=1.38.0
|
|
494
|
+
addHook({
|
|
495
|
+
name: 'playwright',
|
|
496
|
+
file: 'lib/common/suiteUtils.js',
|
|
497
|
+
versions: [`>=${MINIMUM_SUPPORTED_VERSION_EFD}`]
|
|
498
|
+
}, suiteUtilsPackage => {
|
|
499
|
+
// We grab `applyRepeatEachIndex` to use it later
|
|
500
|
+
// `applyRepeatEachIndex` needs to be applied to a cloned suite
|
|
501
|
+
applyRepeatEachIndex = suiteUtilsPackage.applyRepeatEachIndex
|
|
502
|
+
return suiteUtilsPackage
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
// Hook used for early flake detection. EFD only works from >=1.38.0
|
|
506
|
+
addHook({
|
|
507
|
+
name: 'playwright',
|
|
508
|
+
file: 'lib/runner/loadUtils.js',
|
|
509
|
+
versions: [`>=${MINIMUM_SUPPORTED_VERSION_EFD}`]
|
|
510
|
+
}, (loadUtilsPackage) => {
|
|
511
|
+
const oldCreateRootSuite = loadUtilsPackage.createRootSuite
|
|
512
|
+
|
|
513
|
+
async function newCreateRootSuite () {
|
|
514
|
+
const rootSuite = await oldCreateRootSuite.apply(this, arguments)
|
|
515
|
+
if (!isEarlyFlakeDetectionEnabled) {
|
|
516
|
+
return rootSuite
|
|
517
|
+
}
|
|
518
|
+
const newTests = rootSuite
|
|
519
|
+
.allTests()
|
|
520
|
+
.filter(isNewTest)
|
|
521
|
+
|
|
522
|
+
newTests.forEach(newTest => {
|
|
523
|
+
newTest._ddIsNew = true
|
|
524
|
+
if (newTest.expectedStatus !== 'skipped') {
|
|
525
|
+
const fileSuite = getSuiteType(newTest, 'file')
|
|
526
|
+
const projectSuite = getSuiteType(newTest, 'project')
|
|
527
|
+
for (let repeatEachIndex = 0; repeatEachIndex < earlyFlakeDetectionNumRetries; repeatEachIndex++) {
|
|
528
|
+
const copyFileSuite = deepCloneSuite(fileSuite, isNewTest)
|
|
529
|
+
applyRepeatEachIndex(projectSuite._fullProject, copyFileSuite, repeatEachIndex + 1)
|
|
530
|
+
projectSuite._addSuite(copyFileSuite)
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
return rootSuite
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
loadUtilsPackage.createRootSuite = newCreateRootSuite
|
|
539
|
+
|
|
540
|
+
return loadUtilsPackage
|
|
541
|
+
})
|
|
@@ -11,7 +11,9 @@ const {
|
|
|
11
11
|
TEST_SOURCE_START,
|
|
12
12
|
TEST_CODE_OWNERS,
|
|
13
13
|
TEST_SOURCE_FILE,
|
|
14
|
-
TEST_CONFIGURATION_BROWSER_NAME
|
|
14
|
+
TEST_CONFIGURATION_BROWSER_NAME,
|
|
15
|
+
TEST_IS_NEW,
|
|
16
|
+
TEST_IS_RETRY
|
|
15
17
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
16
18
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
17
19
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
@@ -109,7 +111,7 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
109
111
|
|
|
110
112
|
this.enter(span, store)
|
|
111
113
|
})
|
|
112
|
-
this.addSub('ci:playwright:test:finish', ({ testStatus, steps, error, extraTags }) => {
|
|
114
|
+
this.addSub('ci:playwright:test:finish', ({ testStatus, steps, error, extraTags, isNew, isEfdRetry }) => {
|
|
113
115
|
const store = storage.getStore()
|
|
114
116
|
const span = store && store.span
|
|
115
117
|
if (!span) return
|
|
@@ -122,6 +124,12 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
122
124
|
if (extraTags) {
|
|
123
125
|
span.addTags(extraTags)
|
|
124
126
|
}
|
|
127
|
+
if (isNew) {
|
|
128
|
+
span.setTag(TEST_IS_NEW, 'true')
|
|
129
|
+
if (isEfdRetry) {
|
|
130
|
+
span.setTag(TEST_IS_RETRY, 'true')
|
|
131
|
+
}
|
|
132
|
+
}
|
|
125
133
|
|
|
126
134
|
steps.forEach(step => {
|
|
127
135
|
const stepStartTime = step.startTime.getTime()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const RateLimiter = require('./rate_limiter')
|
|
3
4
|
const Sampler = require('./sampler')
|
|
4
5
|
const { setSamplingRules } = require('./startup-log')
|
|
5
6
|
const SamplingRule = require('./sampling_rule')
|
|
@@ -43,6 +44,7 @@ class PrioritySampler {
|
|
|
43
44
|
configure (env, { sampleRate, rateLimit = 100, rules = [] } = {}) {
|
|
44
45
|
this._env = env
|
|
45
46
|
this._rules = this._normalizeRules(rules, sampleRate, rateLimit)
|
|
47
|
+
this._limiter = new RateLimiter(rateLimit)
|
|
46
48
|
|
|
47
49
|
setSamplingRules(this._rules)
|
|
48
50
|
}
|
|
@@ -136,14 +138,17 @@ class PrioritySampler {
|
|
|
136
138
|
context._trace[SAMPLING_RULE_DECISION] = rule.sampleRate
|
|
137
139
|
context._sampling.mechanism = SAMPLING_MECHANISM_RULE
|
|
138
140
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
+
return rule.sample() && this._isSampledByRateLimit(context)
|
|
142
|
+
? USER_KEEP
|
|
143
|
+
: USER_REJECT
|
|
144
|
+
}
|
|
141
145
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
146
|
+
_isSampledByRateLimit (context) {
|
|
147
|
+
const allowed = this._limiter.isAllowed()
|
|
148
|
+
|
|
149
|
+
context._trace[SAMPLING_LIMIT_DECISION] = this._limiter.effectiveRate()
|
|
145
150
|
|
|
146
|
-
return
|
|
151
|
+
return allowed
|
|
147
152
|
}
|
|
148
153
|
|
|
149
154
|
_getPriorityByAgent (context) {
|