@@ -308,9 +308,27 @@ export class AllureReporter implements ReporterV2 {
308308 }
309309
310310 if ( isHookStep ) {
311- const stack = isBeforeHookDescendant
312- ? this . beforeHooksStepsStack . get ( test . id ) !
313- : this . afterHooksStepsStack . get ( test . id ) ! ;
311+ // Lazily initialize the hook stack if it doesn't exist (e.g., when detail: false and root hook was ignored)
312+ let stack = isBeforeHookDescendant
313+ ? this . beforeHooksStepsStack . get ( test . id )
314+ : this . afterHooksStepsStack . get ( test . id ) ;
315+
316+ if ( ! stack ) {
317+ stack = new ShallowStepsStack ( ) ;
318+ const rootHookStep : StepResult = {
319+ ...createStepResult ( ) ,
320+ name : isBeforeHookDescendant ? BEFORE_HOOKS_ROOT_STEP_TITLE : AFTER_HOOKS_ROOT_STEP_TITLE ,
321+ start : step . startTime . getTime ( ) ,
322+ stage : Stage . RUNNING ,
323+ uuid : randomUuid ( ) ,
324+ } ;
325+ stack . startStep ( rootHookStep ) ;
326+ if ( isBeforeHookDescendant ) {
327+ this . beforeHooksStepsStack . set ( test . id , stack ) ;
328+ } else {
329+ this . afterHooksStepsStack . set ( test . id , stack ) ;
330+ }
331+ }
314332
315333 if ( [ "test.attach" , "attach" ] . includes ( step . category ) ) {
316334 let hookStepWithUuid : AttachStack | undefined ;
@@ -356,6 +374,31 @@ export class AllureReporter implements ReporterV2 {
356374 }
357375
358376 onStepEnd ( test : TestCase , _result : PlaywrightTestResult , step : TestStep ) : void {
377+ const isRootBeforeHook = step . title === BEFORE_HOOKS_ROOT_STEP_TITLE ;
378+ const isRootAfterHook = step . title === AFTER_HOOKS_ROOT_STEP_TITLE ;
379+ const isRootHook = isRootBeforeHook || isRootAfterHook ;
380+
381+ // For root hooks, check if we have a lazily created stack that needs to be finalized
382+ if ( isRootHook ) {
383+ const stack = isRootAfterHook ? this . afterHooksStepsStack . get ( test . id ) : this . beforeHooksStepsStack . get ( test . id ) ;
384+
385+ // If stack exists (was lazily created), finalize the root hook step
386+ if ( stack ) {
387+ stack . updateStep ( ( stepResult ) => {
388+ const { status = Status . PASSED } = getWorstTestStepResult ( stepResult . steps ) ?? { } ;
389+ stepResult . status = step . error ? Status . FAILED : status ;
390+ stepResult . stage = Stage . FINISHED ;
391+ if ( step . error ) {
392+ stepResult . statusDetails = { ...getMessageAndTraceFromError ( step . error ) } ;
393+ }
394+ } ) ;
395+ stack . stopStep ( {
396+ duration : step . duration ,
397+ } ) ;
398+ }
399+ return ;
400+ }
401+
359402 if ( this . #shouldIgnoreStep( step ) ) {
360403 return ;
361404 }
@@ -364,15 +407,18 @@ export class AllureReporter implements ReporterV2 {
364407 return ;
365408 }
366409 const testUuid = this . allureResultsUuids . get ( test . id ) ! ;
367- const isRootBeforeHook = step . title === BEFORE_HOOKS_ROOT_STEP_TITLE ;
368- const isRootAfterHook = step . title === AFTER_HOOKS_ROOT_STEP_TITLE ;
369410 const isBeforeHookDescendant = isBeforeHookStep ( step ) ;
370411 const isAfterHookDescendant = isAfterHookStep ( step ) ;
371- const isAfterHook = isRootAfterHook || isAfterHookDescendant ;
372- const isHook = isRootBeforeHook || isRootAfterHook || isBeforeHookDescendant || isAfterHookDescendant ;
412+ const isAfterHook = isAfterHookDescendant ;
413+ const isHook = isBeforeHookDescendant || isAfterHookDescendant ;
373414
374415 if ( isHook ) {
375- const stack = isAfterHook ? this . afterHooksStepsStack . get ( test . id ) ! : this . beforeHooksStepsStack . get ( test . id ) ! ;
416+ const stack = isAfterHook ? this . afterHooksStepsStack . get ( test . id ) : this . beforeHooksStepsStack . get ( test . id ) ;
417+
418+ // Stack might not exist if root hook was ignored (e.g., detail: false) and no test.step was called inside it
419+ if ( ! stack ) {
420+ return ;
421+ }
376422
377423 stack . updateStep ( ( stepResult ) => {
378424 const { status = Status . PASSED } = getWorstTestStepResult ( stepResult . steps ) ?? { } ;
0 commit comments