@@ -202,16 +202,43 @@ const performStepAction = async ({
202202
203203 if ( trigger . clickOnTooltipButton ) {
204204 const buttonLabel = getEnglishMessage ( trigger . clickOnTooltipButton ) ;
205+ // There can be several tooltip poppers in the DOM (a previous step's
206+ // tooltip whose exit transition did not complete keeps the same id):
207+ // only consider the visible button.
205208 const button = page
206- . locator ( '# in-app-tutorial-tooltip-displayer' )
209+ . locator ( '[id=" in-app-tutorial-tooltip-displayer"] ' )
207210 . getByRole ( 'button' , { name : buttonLabel } )
211+ . filter ( { visible : true } )
208212 . first ( ) ;
209213 // The tooltip is often anchored to the bouncing avatar: it never stops
210214 // moving, so a regular click (which waits for the element to be stable)
211215 // would time out. Click at the current position instead.
212216 try {
213217 await button . waitFor ( { state : 'visible' , timeout : 8 * 1000 } ) ;
214218 } catch ( error ) {
219+ const tooltipState = await page
220+ . evaluate ( ( ) =>
221+ [
222+ ...document . querySelectorAll (
223+ '[id="in-app-tutorial-tooltip-displayer"]'
224+ ) ,
225+ ] . map ( ( popper ) => ( {
226+ visibility : getComputedStyle ( popper ) . visibility ,
227+ display : getComputedStyle ( popper ) . display ,
228+ width : Math . round ( popper . getBoundingClientRect ( ) . width ) ,
229+ height : Math . round ( popper . getBoundingClientRect ( ) . height ) ,
230+ buttons : [ ...popper . querySelectorAll ( 'button' ) ] . map ( ( button ) => ( {
231+ text : ( button . textContent || '' ) . slice ( 0 , 25 ) ,
232+ width : Math . round ( button . getBoundingClientRect ( ) . width ) ,
233+ height : Math . round ( button . getBoundingClientRect ( ) . height ) ,
234+ visibility : getComputedStyle ( button ) . visibility ,
235+ } ) ) ,
236+ } ) )
237+ )
238+ . catch ( ( ) => null ) ;
239+ log (
240+ `Tooltip button not visible. Poppers: ${ JSON . stringify ( tooltipState ) } `
241+ ) ;
215242 // The tooltip is probably folded (only the avatar is visible):
216243 // clicking the avatar unfolds it.
217244 await clickAtCurrentPosition (
@@ -522,15 +549,25 @@ const playTutorial = async ({ page, context, tutorial, log = () => {} }) => {
522549 // those in the report as they usually explain "element not found"
523550 // failures.
524551 const pageDiagnostics = await page
525- . evaluate ( ( ) => {
526- const dialogTitles = [
527- ... document . querySelectorAll ( '[role="dialog"] h2' ) ,
528- ] . map ( ( title ) => ( title . textContent || '' ) . slice ( 0 , 60 ) ) ;
552+ . evaluate ( ( elementToHighlightId ) => {
553+ const tooltip = document . querySelector (
554+ '#in-app-tutorial-tooltip-displayer'
555+ ) ;
529556 return {
530557 hasErrorBoundary : ! ! document . querySelector ( '[data-error-boundary]' ) ,
531- dialogTitles,
558+ openDialogsCount :
559+ document . querySelectorAll ( '[role="dialog"]' ) . length ,
560+ highlightedElementExists : elementToHighlightId
561+ ? ! ! document . querySelector ( elementToHighlightId )
562+ : null ,
563+ tooltipText : tooltip
564+ ? ( tooltip . textContent || '' ) . trim ( ) . slice ( 0 , 120 )
565+ : null ,
566+ avatarDisplayed : ! ! document . querySelector (
567+ '#in-app-tutorial-avatar'
568+ ) ,
532569 } ;
533- } )
570+ } , state . elementToHighlightId || null )
534571 . catch ( ( ) => null ) ;
535572 throw new TutorialStepError (
536573 `Tutorial "${ tutorial . id } " is broken at step ${ state . stepIndex } ` +
@@ -540,9 +577,7 @@ const playTutorial = async ({ page, context, tutorial, log = () => {} }) => {
540577 ? `. The action could not be performed: ${ actionError . message } `
541578 : '. The action was performed but the tutorial did not advance.' ) +
542579 ( pageDiagnostics
543- ? ` Page state: error boundary displayed: ${
544- pageDiagnostics . hasErrorBoundary
545- } , open dialogs: [${ pageDiagnostics . dialogTitles . join ( ' | ' ) } ].`
580+ ? ` Page state: ${ JSON . stringify ( pageDiagnostics ) } .`
546581 : '' ) ,
547582 { stepIndex : state . stepIndex , step, state }
548583 ) ;
0 commit comments