@@ -76,8 +76,6 @@ export class AllureReporter implements ReporterV2 {
7676 private readonly attachmentTargets : Map < string , AttachmentTarget [ ] > = new Map ( ) ;
7777 private beforeHooksStepsStack : Map < string , ShallowStepsStack > = new Map ( ) ;
7878 private afterHooksStepsStack : Map < string , ShallowStepsStack > = new Map ( ) ;
79- private beforeHooksAttachmentsStack : Map < string , AttachStack [ ] > = new Map ( ) ;
80- private afterHooksAttachmentsStack : Map < string , AttachStack [ ] > = new Map ( ) ;
8179 private readonly pwStepUuid = new WeakMap < TestStep , string > ( ) ;
8280 private readonly testPlan = parseTestPlan ( ) ;
8381
@@ -280,6 +278,24 @@ export class AllureReporter implements ReporterV2 {
280278 return false ;
281279 }
282280
281+ #getOrCreateHookStack( testId : string , isBeforeHook : boolean , startTime : Date ) : ShallowStepsStack {
282+ const map = isBeforeHook ? this . beforeHooksStepsStack : this . afterHooksStepsStack ;
283+ let stack = map . get ( testId ) ;
284+ if ( ! stack ) {
285+ stack = new ShallowStepsStack ( ) ;
286+ const rootHookStep : StepResult = {
287+ ...createStepResult ( ) ,
288+ name : isBeforeHook ? BEFORE_HOOKS_ROOT_STEP_TITLE : AFTER_HOOKS_ROOT_STEP_TITLE ,
289+ start : startTime . getTime ( ) ,
290+ stage : Stage . RUNNING ,
291+ uuid : randomUuid ( ) ,
292+ } ;
293+ stack . startStep ( rootHookStep ) ;
294+ map . set ( testId , stack ) ;
295+ }
296+ return stack ;
297+ }
298+
283299 onStepBegin ( test : TestCase , _result : PlaywrightTestResult , step : TestStep ) : void {
284300 const isRootBeforeHook = step . title === BEFORE_HOOKS_ROOT_STEP_TITLE ;
285301 const isRootAfterHook = step . title === AFTER_HOOKS_ROOT_STEP_TITLE ;
@@ -310,48 +326,33 @@ export class AllureReporter implements ReporterV2 {
310326 }
311327
312328 if ( isHookStep ) {
313- // Lazily initialize the hook stack if it doesn't exist (e.g., when detail: false and root hook was ignored)
314- let stack = isBeforeHookDescendant
315- ? this . beforeHooksStepsStack . get ( test . id )
316- : this . afterHooksStepsStack . get ( test . id ) ;
317-
318- if ( ! stack ) {
319- stack = new ShallowStepsStack ( ) ;
320- const rootHookStep : StepResult = {
321- ...createStepResult ( ) ,
322- name : isBeforeHookDescendant ? BEFORE_HOOKS_ROOT_STEP_TITLE : AFTER_HOOKS_ROOT_STEP_TITLE ,
323- start : step . startTime . getTime ( ) ,
324- stage : Stage . RUNNING ,
325- uuid : randomUuid ( ) ,
326- } ;
327- stack . startStep ( rootHookStep ) ;
328- if ( isBeforeHookDescendant ) {
329- this . beforeHooksStepsStack . set ( test . id , stack ) ;
330- } else {
331- this . afterHooksStepsStack . set ( test . id , stack ) ;
332- }
333- }
334-
335329 if ( [ "test.attach" , "attach" ] . includes ( step . category ) ) {
330+ const attachmentName = normalizeAttachStepTitle ( step . title ) ;
331+ const stack = this . #getOrCreateHookStack( test . id , isBeforeHookDescendant , step . startTime ) ;
332+
336333 let hookStepWithUuid : AttachStack | undefined ;
334+ const parentHookStepUuid = stack . currentStep ( ) ?. uuid ;
337335 stack . startStep ( baseStep ) ;
338336
339- const attachStack = isBeforeHookDescendant ? this . beforeHooksAttachmentsStack : this . afterHooksAttachmentsStack ;
340-
341337 stack . updateStep ( ( stepResult ) => {
342338 hookStepWithUuid = { ...step , uuid : stepResult . uuid as string } as AttachStack ;
343339 stepResult . name = normalizeHookTitle ( stepResult . name ! ) ;
344340 stepResult . stage = Stage . FINISHED ;
345- attachStack . set ( test . id , [ ...( attachStack . get ( test . id ) ?? [ ] ) , hookStepWithUuid ] ) ;
346341 } ) ;
347342 stack . stopStep ( ) ;
348343
349344 const targets = this . attachmentTargets . get ( test . id ) ?? [ ] ;
350- targets . push ( { name : normalizeAttachStepTitle ( step . title ) , hookStep : hookStepWithUuid } ) ;
345+ targets . push ( {
346+ name : attachmentName ,
347+ hookStep : hookStepWithUuid ,
348+ stepUuid : attachmentName === "Allure Step Metadata" ? parentHookStepUuid : undefined ,
349+ } ) ;
351350 this . attachmentTargets . set ( test . id , targets ) ;
352351 return ;
353352 }
354353
354+ const stack = this . #getOrCreateHookStack( test . id , isBeforeHookDescendant , step . startTime ) ;
355+
355356 stack . startStep ( baseStep ) ;
356357 return ;
357358 }
@@ -505,8 +506,6 @@ export class AllureReporter implements ReporterV2 {
505506 ) ;
506507 }
507508
508- const attachmentsInBeforeHooks = this . beforeHooksAttachmentsStack . get ( test . id ) ?? [ ] ;
509-
510509 // FIFO
511510 const targets = this . attachmentTargets . get ( test . id ) ?? [ ] ;
512511 const targetsByName = new Map < string , AttachmentTarget [ ] > ( ) ;
@@ -529,15 +528,31 @@ export class AllureReporter implements ReporterV2 {
529528 const stepInfo = takeByName ( attachment . name ) ;
530529
531530 if ( isRuntimeMessage ) {
531+ const message = attachment . body ? ( JSON . parse ( attachment . body . toString ( ) ) as RuntimeMessage ) : undefined ;
532+
533+ if ( stepInfo ?. hookStep ) {
534+ const targetStack = isBeforeHookStep ( stepInfo . hookStep ) ? beforeHooksStack : afterHooksStack ;
535+ this . removeStepFromHookStack ( targetStack , stepInfo . hookStep . uuid ) ;
536+ }
537+
538+ if ( message ?. type === "step_metadata" && stepInfo ?. stepUuid ) {
539+ if ( stepInfo . hookStep ) {
540+ const targetStack = isBeforeHookStep ( stepInfo . hookStep ) ? beforeHooksStack : afterHooksStack ;
541+ this . processHookStepMetadataMessage ( targetStack , stepInfo . stepUuid , message ) ;
542+ } else {
543+ this . processStepMetadataMessage ( stepInfo . stepUuid , message ) ;
544+ }
545+ continue ;
546+ }
547+
532548 const stepUuid = stepInfo ?. hookStep ?. uuid ?? stepInfo ?. stepUuid ;
533549 await this . processAttachment ( testUuid , stepUuid , attachment ) ;
534550 continue ;
535551 }
536552
537553 if ( stepInfo ?. hookStep ) {
538554 const hookStep = stepInfo . hookStep ;
539- const isBeforeHook = attachmentsInBeforeHooks . includes ( hookStep ) ;
540- const targetStack = isBeforeHook ? beforeHooksStack : afterHooksStack ;
555+ const targetStack = isBeforeHookStep ( hookStep ) ? beforeHooksStack : afterHooksStack ;
541556
542557 if ( targetStack ) {
543558 const stepResult = targetStack . findStepByUuid ( hookStep . uuid ) ;
@@ -589,13 +604,13 @@ export class AllureReporter implements ReporterV2 {
589604
590605 if ( beforeHooksStack ) {
591606 testResult . steps . unshift ( ...beforeHooksStack . steps ) ;
592- this . beforeHooksStepsStack . delete ( test . id ) ;
593607 }
608+ this . beforeHooksStepsStack . delete ( test . id ) ;
594609
595610 if ( afterHooksStack ) {
596611 testResult . steps . push ( ...afterHooksStack . steps ) ;
597- this . afterHooksStepsStack . delete ( test . id ) ;
598612 }
613+ this . afterHooksStepsStack . delete ( test . id ) ;
599614
600615 testResult . labels = newLabels ;
601616 } ) ;
@@ -641,6 +656,30 @@ export class AllureReporter implements ReporterV2 {
641656 return false ;
642657 }
643658
659+ private removeStepFromHookStack ( stack : ShallowStepsStack | undefined , stepUuid : string ) {
660+ if ( ! stack ) {
661+ return ;
662+ }
663+
664+ const removeRecursively = ( steps : StepResult [ ] ) : StepResult [ ] => {
665+ return steps
666+ . filter ( ( step ) => step . uuid !== stepUuid )
667+ . map ( ( step ) => ( {
668+ ...step ,
669+ steps : removeRecursively ( step . steps ) ,
670+ } ) )
671+ . filter (
672+ ( step ) =>
673+ ( step . name !== BEFORE_HOOKS_ROOT_STEP_TITLE && step . name !== AFTER_HOOKS_ROOT_STEP_TITLE ) ||
674+ step . steps . length > 0 ||
675+ step . attachments . length > 0 ||
676+ step . status !== Status . PASSED ,
677+ ) ;
678+ } ;
679+
680+ stack . steps = removeRecursively ( stack . steps ) ;
681+ }
682+
644683 private getStaticTestMetadata ( test : TestCase ) {
645684 const titleMetadata = extractMetadataFromString ( test . title ) ;
646685 const project =
@@ -719,6 +758,26 @@ export class AllureReporter implements ReporterV2 {
719758 } ) ;
720759 }
721760
761+ private processHookStepMetadataMessage (
762+ stack : ShallowStepsStack | undefined ,
763+ attachmentStepUuid : string ,
764+ message : RuntimeStepMetadataMessage ,
765+ ) {
766+ const step = stack ?. findStepByUuid ( attachmentStepUuid ) ;
767+
768+ if ( ! step ) {
769+ return ;
770+ }
771+
772+ const { name, parameters = [ ] } = message . data ;
773+
774+ if ( name ) {
775+ step . name = name ;
776+ }
777+
778+ step . parameters . push ( ...parameters ) ;
779+ }
780+
722781 private async processAttachment (
723782 testUuid : string ,
724783 attachmentStepUuid : string | undefined ,
@@ -741,11 +800,6 @@ export class AllureReporter implements ReporterV2 {
741800
742801 if ( allureRuntimeMessage ) {
743802 const message = JSON . parse ( attachment . body ! . toString ( ) ) as RuntimeMessage ;
744-
745- if ( message . type === "step_metadata" ) {
746- this . processStepMetadataMessage ( attachmentStepUuid ! , message ) ;
747- return ;
748- }
749803 this . allureRuntime ! . applyRuntimeMessages ( testUuid , [ message ] ) ;
750804 return ;
751805 }
0 commit comments