@@ -15,6 +15,7 @@ const { DD_MAJOR } = require('../../../version')
1515
1616const testStartCh = channel ( 'ci:playwright:test:start' )
1717const testFinishCh = channel ( 'ci:playwright:test:finish' )
18+ const testSkipCh = channel ( 'ci:playwright:test:skip' )
1819
1920const testSessionStartCh = channel ( 'ci:playwright:session:start' )
2021const 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,8 +338,20 @@ function testBeginHandler (test, browserName, isMainProcess) {
328338 }
329339}
330340
331- function testEndHandler ( test , annotations , testStatus , error , isTimeout , isMainProcess ) {
332- const { _requireFile : testSuiteAbsolutePath , results, _type } = test
341+ function testEndHandler ( {
342+ test,
343+ annotations,
344+ testStatus,
345+ error,
346+ isTimeout,
347+ shouldCreateTestSpan,
348+ projects
349+ } ) {
350+ const {
351+ _requireFile : testSuiteAbsolutePath ,
352+ results,
353+ _type,
354+ } = test
333355
334356 let annotationTags
335357 if ( annotations . length ) {
@@ -368,31 +390,34 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout, isMain
368390 }
369391
370392 // this handles tests that do not go through the worker process (because they're skipped)
371- if ( isMainProcess ) {
393+ if ( shouldCreateTestSpan ) {
372394 const testResult = results . at ( - 1 )
373395 const testCtx = testToCtx . get ( test )
374396 const isAtrRetry = testResult ?. retry > 0 &&
375397 isFlakyTestRetriesEnabled &&
376398 ! test . _ddIsAttemptToFix &&
377399 ! 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- } )
400+ // if there is no testCtx, the skipped test will be created later
401+ if ( testCtx ) {
402+ testFinishCh . publish ( {
403+ testStatus,
404+ steps : testResult ?. steps || [ ] ,
405+ isRetry : testResult ?. retry > 0 ,
406+ error,
407+ extraTags : annotationTags ,
408+ isNew : test . _ddIsNew ,
409+ isAttemptToFix : test . _ddIsAttemptToFix ,
410+ isAttemptToFixRetry : test . _ddIsAttemptToFixRetry ,
411+ isQuarantined : test . _ddIsQuarantined ,
412+ isEfdRetry : test . _ddIsEfdRetry ,
413+ hasFailedAllRetries : test . _ddHasFailedAllRetries ,
414+ hasPassedAttemptToFixRetries : test . _ddHasPassedAttemptToFixRetries ,
415+ hasFailedAttemptToFixRetries : test . _ddHasFailedAttemptToFixRetries ,
416+ isAtrRetry,
417+ isModified : test . _ddIsModified ,
418+ ...testCtx . currentStore
419+ } )
420+ }
396421 }
397422
398423 if ( testSuiteToTestStatuses . has ( testSuiteAbsolutePath ) ) {
@@ -410,8 +435,25 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout, isMain
410435 . filter ( currentTest => currentTest !== test )
411436 }
412437
413- // Last test, we finish the suite
414- if ( ! remainingTestsByFile [ testSuiteAbsolutePath ] . length ) {
438+ if ( shouldFinishTestSuite ( testSuiteAbsolutePath ) ) {
439+ const skippedTests = remainingTestsByFile [ testSuiteAbsolutePath ]
440+ . filter ( test => test . expectedStatus === 'skipped' )
441+
442+ for ( const test of skippedTests ) {
443+ const browserName = getBrowserNameFromProjects ( projects , test )
444+ testSkipCh . publish ( {
445+ testName : getTestFullname ( test ) ,
446+ testSuiteAbsolutePath,
447+ testSourceLine : test . location . line ,
448+ browserName,
449+ isNew : test . _ddIsNew ,
450+ isDisabled : test . _ddIsDisabled ,
451+ isModified : test . _ddIsModified ,
452+ isQuarantined : test . _ddIsQuarantined
453+ } )
454+ }
455+ remainingTestsByFile [ testSuiteAbsolutePath ] = [ ]
456+
415457 const testStatuses = testSuiteToTestStatuses . get ( testSuiteAbsolutePath )
416458 let testSuiteStatus = 'pass'
417459 if ( testStatuses . includes ( 'fail' ) ) {
@@ -450,26 +492,32 @@ function dispatcherHook (dispatcherExport) {
450492 shimmer . wrap ( dispatcherExport . Dispatcher . prototype , '_createWorker' , createWorker => function ( ) {
451493 const dispatcher = this
452494 const worker = createWorker . apply ( this , arguments )
495+ const projects = getProjectsFromDispatcher ( dispatcher )
496+
453497 worker . process . on ( 'message' , ( { method, params } ) => {
454498 if ( method === 'testBegin' ) {
455499 const { test } = dispatcher . _testById . get ( params . testId )
456- const projects = getProjectsFromDispatcher ( dispatcher )
457500 const browser = getBrowserNameFromProjects ( projects , test )
458- testBeginHandler ( test , browser , true )
501+ const shouldCreateTestSpan = test . expectedStatus === 'skipped'
502+ testBeginHandler ( test , browser , shouldCreateTestSpan )
459503 } else if ( method === 'testEnd' ) {
460504 const { test } = dispatcher . _testById . get ( params . testId )
461505
462506 const { results } = test
463507 const testResult = results . at ( - 1 )
464508
465509 const isTimeout = testResult . status === 'timedOut'
510+ const shouldCreateTestSpan = test . expectedStatus === 'skipped'
466511 testEndHandler (
467- test ,
468- params . annotations ,
469- STATUS_TO_TEST_STATUS [ testResult . status ] ,
470- testResult . error ,
471- isTimeout ,
472- true
512+ {
513+ test,
514+ annotations : params . annotations ,
515+ testStatus : STATUS_TO_TEST_STATUS [ testResult . status ] ,
516+ error : testResult . error ,
517+ isTimeout,
518+ shouldCreateTestSpan,
519+ projects
520+ }
473521 )
474522 }
475523 } )
@@ -484,18 +532,30 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
484532 shimmer . wrap ( dispatcherExport . Dispatcher . prototype , '_createWorker' , createWorker => function ( ) {
485533 const dispatcher = this
486534 const worker = createWorker . apply ( this , arguments )
535+ const projects = getProjectsFromDispatcher ( dispatcher )
487536
488537 worker . on ( 'testBegin' , ( { testId } ) => {
489538 const test = getTestByTestId ( dispatcher , testId )
490- const projects = getProjectsFromDispatcher ( dispatcher )
491539 const browser = getBrowserNameFromProjects ( projects , test )
492- testBeginHandler ( test , browser , false )
540+ const shouldCreateTestSpan = test . expectedStatus === 'skipped'
541+ testBeginHandler ( test , browser , shouldCreateTestSpan )
493542 } )
494543 worker . on ( 'testEnd' , ( { testId, status, errors, annotations } ) => {
495544 const test = getTestByTestId ( dispatcher , testId )
496545
497546 const isTimeout = status === 'timedOut'
498- testEndHandler ( test , annotations , STATUS_TO_TEST_STATUS [ status ] , errors && errors [ 0 ] , isTimeout , false )
547+ const shouldCreateTestSpan = test . expectedStatus === 'skipped'
548+ testEndHandler (
549+ {
550+ test,
551+ annotations,
552+ testStatus : STATUS_TO_TEST_STATUS [ status ] ,
553+ error : errors && errors [ 0 ] ,
554+ isTimeout,
555+ shouldCreateTestSpan,
556+ projects
557+ }
558+ )
499559 const testResult = test . results . at ( - 1 )
500560 const isAtrRetry = testResult ?. retry > 0 &&
501561 isFlakyTestRetriesEnabled &&
@@ -625,14 +685,25 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
625685
626686 let runAllTestsReturn = await runAllTests . apply ( this , arguments )
627687
688+ // Tests that have only skipped tests may reach this point
689+ // Skipped tests may or may not go through `testBegin` or `testEnd`
690+ // depending on the playwright configuration
628691 Object . values ( remainingTestsByFile ) . forEach ( tests => {
629692 // `tests` should normally be empty, but if it isn't,
630693 // there were tests that did not go through `testBegin` or `testEnd`,
631694 // because they were skipped
632695 tests . forEach ( test => {
633696 const browser = getBrowserNameFromProjects ( projects , test )
634697 testBeginHandler ( test , browser , true )
635- testEndHandler ( test , [ ] , 'skip' , null , false , true )
698+ testEndHandler ( {
699+ test,
700+ annotations : [ ] ,
701+ testStatus : 'skip' ,
702+ error : null ,
703+ isTimeout : false ,
704+ shouldCreateTestSpan : true ,
705+ projects
706+ } )
636707 } )
637708 } )
638709
@@ -1007,6 +1078,9 @@ addHook({
10071078 const stepInfoByStepId = { }
10081079
10091080 shimmer . wrap ( workerPackage . WorkerMain . prototype , '_runTest' , _runTest => async function ( test ) {
1081+ if ( test . expectedStatus === 'skipped' ) {
1082+ return _runTest . apply ( this , arguments )
1083+ }
10101084 steps = [ ]
10111085
10121086 const {
0 commit comments