Skip to content

Commit 7997e11

Browse files
fix timing of skipped tests
1 parent e955673 commit 7997e11

File tree

2 files changed

+141
-58
lines changed

2 files changed

+141
-58
lines changed

packages/datadog-instrumentations/src/playwright.js

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const { DD_MAJOR } = require('../../../version')
1515

1616
const testStartCh = channel('ci:playwright:test:start')
1717
const testFinishCh = channel('ci:playwright:test:finish')
18+
const testSkipCh = channel('ci:playwright:test:skip')
1819

1920
const testSessionStartCh = channel('ci:playwright:session:start')
2021
const testSessionFinishCh = channel('ci:playwright:session:finish')
@@ -285,7 +286,12 @@ function getTestFullname (test) {
285286
return names.join(' ')
286287
}
287288

288-
function testBeginHandler (test, browserName, isMainProcess) {
289+
function shouldFinishTestSuite (testSuiteAbsolutePath) {
290+
const remainingTests = remainingTestsByFile[testSuiteAbsolutePath]
291+
return !remainingTests.length || remainingTests.every(test => test.expectedStatus === 'skipped')
292+
}
293+
294+
function testBeginHandler (test, browserName, shouldCreateTestSpan) {
289295
const {
290296
_requireFile: testSuiteAbsolutePath,
291297
location: {
@@ -297,6 +303,10 @@ function testBeginHandler (test, browserName, isMainProcess) {
297303
if (_type === 'beforeAll' || _type === 'afterAll') {
298304
return
299305
}
306+
// this means that a skipped test is being handled
307+
if (!remainingTestsByFile[testSuiteAbsolutePath].length) {
308+
return
309+
}
300310

301311
const isNewTestSuite = !startedSuites.includes(testSuiteAbsolutePath)
302312

@@ -313,7 +323,7 @@ function testBeginHandler (test, browserName, isMainProcess) {
313323
}
314324

315325
// this handles tests that do not go through the worker process (because they're skipped)
316-
if (isMainProcess) {
326+
if (shouldCreateTestSpan) {
317327
const testName = getTestFullname(test)
318328
const testCtx = {
319329
testName,
@@ -328,7 +338,7 @@ function testBeginHandler (test, browserName, isMainProcess) {
328338
}
329339
}
330340

331-
function testEndHandler (test, annotations, testStatus, error, isTimeout, isMainProcess) {
341+
function testEndHandler (test, annotations, testStatus, error, isTimeout, shouldCreateTestSpan) {
332342
const { _requireFile: testSuiteAbsolutePath, results, _type } = test
333343

334344
let annotationTags
@@ -368,31 +378,34 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout, isMain
368378
}
369379

370380
// this handles tests that do not go through the worker process (because they're skipped)
371-
if (isMainProcess) {
381+
if (shouldCreateTestSpan) {
372382
const testResult = results.at(-1)
373383
const testCtx = testToCtx.get(test)
374384
const isAtrRetry = testResult?.retry > 0 &&
375385
isFlakyTestRetriesEnabled &&
376386
!test._ddIsAttemptToFix &&
377387
!test._ddIsEfdRetry
378-
testFinishCh.publish({
379-
testStatus,
380-
steps: testResult?.steps || [],
381-
isRetry: testResult?.retry > 0,
382-
error,
383-
extraTags: annotationTags,
384-
isNew: test._ddIsNew,
385-
isAttemptToFix: test._ddIsAttemptToFix,
386-
isAttemptToFixRetry: test._ddIsAttemptToFixRetry,
387-
isQuarantined: test._ddIsQuarantined,
388-
isEfdRetry: test._ddIsEfdRetry,
389-
hasFailedAllRetries: test._ddHasFailedAllRetries,
390-
hasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
391-
hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
392-
isAtrRetry,
393-
isModified: test._ddIsModified,
394-
...testCtx.currentStore
395-
})
388+
// if there is no testCtx, the skipped test will be created later
389+
if (testCtx) {
390+
testFinishCh.publish({
391+
testStatus,
392+
steps: testResult?.steps || [],
393+
isRetry: testResult?.retry > 0,
394+
error,
395+
extraTags: annotationTags,
396+
isNew: test._ddIsNew,
397+
isAttemptToFix: test._ddIsAttemptToFix,
398+
isAttemptToFixRetry: test._ddIsAttemptToFixRetry,
399+
isQuarantined: test._ddIsQuarantined,
400+
isEfdRetry: test._ddIsEfdRetry,
401+
hasFailedAllRetries: test._ddHasFailedAllRetries,
402+
hasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
403+
hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
404+
isAtrRetry,
405+
isModified: test._ddIsModified,
406+
...testCtx.currentStore
407+
})
408+
}
396409
}
397410

398411
if (testSuiteToTestStatuses.has(testSuiteAbsolutePath)) {
@@ -410,8 +423,24 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout, isMain
410423
.filter(currentTest => currentTest !== test)
411424
}
412425

413-
// Last test, we finish the suite
414-
if (!remainingTestsByFile[testSuiteAbsolutePath].length) {
426+
if (shouldFinishTestSuite(testSuiteAbsolutePath)) {
427+
const skippedTests = remainingTestsByFile[testSuiteAbsolutePath]
428+
.filter(test => test.expectedStatus === 'skipped')
429+
430+
skippedTests.forEach(test => {
431+
testSkipCh.publish({
432+
testName: getTestFullname(test),
433+
testSuiteAbsolutePath: test._requireFile,
434+
testSourceLine: test.location.line,
435+
browserName: 'chromium', // TODO: REMOVE HARDCODE
436+
isNew: test._ddIsNew,
437+
isDisabled: test._ddIsDisabled,
438+
isModified: test._ddIsModified,
439+
isQuarantined: test._ddIsQuarantined
440+
})
441+
})
442+
remainingTestsByFile[testSuiteAbsolutePath] = []
443+
415444
const testStatuses = testSuiteToTestStatuses.get(testSuiteAbsolutePath)
416445
let testSuiteStatus = 'pass'
417446
if (testStatuses.includes('fail')) {
@@ -455,21 +484,23 @@ function dispatcherHook (dispatcherExport) {
455484
const { test } = dispatcher._testById.get(params.testId)
456485
const projects = getProjectsFromDispatcher(dispatcher)
457486
const browser = getBrowserNameFromProjects(projects, test)
458-
testBeginHandler(test, browser, true)
487+
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
488+
testBeginHandler(test, browser, shouldCreateTestSpan)
459489
} else if (method === 'testEnd') {
460490
const { test } = dispatcher._testById.get(params.testId)
461491

462492
const { results } = test
463493
const testResult = results.at(-1)
464494

465495
const isTimeout = testResult.status === 'timedOut'
496+
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
466497
testEndHandler(
467498
test,
468499
params.annotations,
469500
STATUS_TO_TEST_STATUS[testResult.status],
470501
testResult.error,
471502
isTimeout,
472-
true
503+
shouldCreateTestSpan
473504
)
474505
}
475506
})
@@ -489,13 +520,22 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
489520
const test = getTestByTestId(dispatcher, testId)
490521
const projects = getProjectsFromDispatcher(dispatcher)
491522
const browser = getBrowserNameFromProjects(projects, test)
492-
testBeginHandler(test, browser, false)
523+
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
524+
testBeginHandler(test, browser, shouldCreateTestSpan)
493525
})
494526
worker.on('testEnd', ({ testId, status, errors, annotations }) => {
495527
const test = getTestByTestId(dispatcher, testId)
496528

497529
const isTimeout = status === 'timedOut'
498-
testEndHandler(test, annotations, STATUS_TO_TEST_STATUS[status], errors && errors[0], isTimeout, false)
530+
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
531+
testEndHandler(
532+
test,
533+
annotations,
534+
STATUS_TO_TEST_STATUS[status],
535+
errors && errors[0],
536+
isTimeout,
537+
shouldCreateTestSpan
538+
)
499539
const testResult = test.results.at(-1)
500540
const isAtrRetry = testResult?.retry > 0 &&
501541
isFlakyTestRetriesEnabled &&
@@ -625,6 +665,9 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
625665

626666
let runAllTestsReturn = await runAllTests.apply(this, arguments)
627667

668+
// Tests that have only skipped tests may reach this point
669+
// Skipped tests may or may not go through `testBegin` or `testEnd`
670+
// depending on the playwright configuration
628671
Object.values(remainingTestsByFile).forEach(tests => {
629672
// `tests` should normally be empty, but if it isn't,
630673
// there were tests that did not go through `testBegin` or `testEnd`,
@@ -1007,6 +1050,10 @@ addHook({
10071050
const stepInfoByStepId = {}
10081051

10091052
shimmer.wrap(workerPackage.WorkerMain.prototype, '_runTest', _runTest => async function (test) {
1053+
if (test.expectedStatus === 'skipped') {
1054+
console.log('skipped test', test.title)
1055+
return _runTest.apply(this, arguments)
1056+
}
10101057
steps = []
10111058

10121059
const {

packages/datadog-plugin-playwright/src/index.js

Lines changed: 66 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -198,36 +198,6 @@ class PlaywrightPlugin extends CiPlugin {
198198
}
199199
})
200200

201-
this.addBind('ci:playwright:test:start', (ctx) => {
202-
const {
203-
testName,
204-
testSuiteAbsolutePath,
205-
testSourceLine,
206-
browserName,
207-
isDisabled
208-
} = ctx
209-
const store = storage('legacy').getStore()
210-
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.rootDir)
211-
const testSourceFile = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
212-
const span = this.startTestSpan(
213-
testName,
214-
testSuiteAbsolutePath,
215-
testSuite,
216-
testSourceFile,
217-
testSourceLine,
218-
browserName
219-
)
220-
221-
if (isDisabled) {
222-
span.setTag(TEST_MANAGEMENT_IS_DISABLED, 'true')
223-
}
224-
225-
ctx.parentStore = store
226-
ctx.currentStore = { ...store, span }
227-
228-
return ctx.currentStore
229-
})
230-
231201
this.addSub('ci:playwright:worker:report', (serializedTraces) => {
232202
const traces = JSON.parse(serializedTraces)
233203
const formattedTraces = []
@@ -277,6 +247,36 @@ class PlaywrightPlugin extends CiPlugin {
277247
})
278248
})
279249

250+
this.addBind('ci:playwright:test:start', (ctx) => {
251+
const {
252+
testName,
253+
testSuiteAbsolutePath,
254+
testSourceLine,
255+
browserName,
256+
isDisabled
257+
} = ctx
258+
const store = storage('legacy').getStore()
259+
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.rootDir)
260+
const testSourceFile = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
261+
const span = this.startTestSpan(
262+
testName,
263+
testSuiteAbsolutePath,
264+
testSuite,
265+
testSourceFile,
266+
testSourceLine,
267+
browserName
268+
)
269+
270+
if (isDisabled) {
271+
span.setTag(TEST_MANAGEMENT_IS_DISABLED, 'true')
272+
}
273+
274+
ctx.parentStore = store
275+
ctx.currentStore = { ...store, span }
276+
277+
return ctx.currentStore
278+
})
279+
280280
this.addSub('ci:playwright:test:finish', ({
281281
span,
282282
testStatus,
@@ -393,6 +393,42 @@ class PlaywrightPlugin extends CiPlugin {
393393
this.tracer._exporter.flush(onDone)
394394
}
395395
})
396+
397+
this.addSub('ci:playwright:test:skip', ({
398+
testName,
399+
testSuiteAbsolutePath,
400+
testSourceLine,
401+
browserName,
402+
isNew,
403+
isDisabled,
404+
isModified,
405+
isQuarantined
406+
}) => {
407+
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.rootDir)
408+
const testSourceFile = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
409+
const span = this.startTestSpan(
410+
testName,
411+
testSuiteAbsolutePath,
412+
testSuite,
413+
testSourceFile,
414+
testSourceLine,
415+
browserName
416+
)
417+
if (isNew) {
418+
span.setTag(TEST_IS_NEW, 'true')
419+
}
420+
if (isDisabled) {
421+
span.setTag(TEST_MANAGEMENT_IS_DISABLED, 'true')
422+
}
423+
if (isModified) {
424+
span.setTag(TEST_IS_MODIFIED, 'true')
425+
}
426+
if (isQuarantined) {
427+
span.setTag(TEST_MANAGEMENT_IS_QUARANTINED, 'true')
428+
}
429+
430+
span.finish()
431+
})
396432
}
397433

398434
// TODO: this runs both in worker and main process (main process: skipped tests that do not go through _runTest)

0 commit comments

Comments
 (0)