@@ -106,30 +106,39 @@ test.describe("Error page", () => {
106106} ) ;
107107
108108test . describe ( "External video" , ( ) => {
109- test . beforeEach ( async ( { page } ) => {
109+ test ( "renders a poster" , async ( { page } , testInfo ) => {
110+ // Register the route before navigation so the mp4 fetch is intercepted
111+ // from the start. The beforeEach can't be used here because it runs
112+ // before the test body, after which it's too late to register the route.
113+ const stubPath = testInfo . file . replace ( "VideoPlayer.spec.js" , "fixtures/stub.mp4" ) ;
114+ await page . route ( "**/*.mp4" , route =>
115+ route . fulfill ( {
116+ status : 200 ,
117+ contentType : "video/mp4" ,
118+ path : stubPath
119+ } )
120+ ) ;
110121 await page . goto ( "/p/external" ) ;
111- } ) ;
112122
113- test ( "renders a poster" , async ( { page } ) => {
114123 const widget = page . locator ( ".widget-video-player" ) ;
115124 const videoLocator = page . locator ( ".widget-video-player video" ) ;
116125 await widget . scrollIntoViewIfNeeded ( ) ;
117126 await expect ( widget ) . toBeVisible ( ) ;
118127 await expect ( videoLocator ) . toHaveAttribute ( "poster" , / .+ / ) ;
119- // Wait for poster image to decode in-page before screenshotting .
120- // page.evaluate with a separate Image() is unreliable — the promise can
121- // resolve before the <video> element itself has rendered the poster frame .
122- await videoLocator . evaluate ( el =>
123- el . poster
124- ? new Promise ( r => {
125- const i = new Image ( ) ;
126- i . onload = i . onerror = r ;
127- i . src = el . poster ;
128- if ( i . complete ) r ( ) ;
129- } )
130- : Promise . resolve ( )
128+ // Poll for a terminal state: metadata loaded OR all sources failed (networkState 3) .
129+ // Polling avoids the race where loadedmetadata/error events fire on <source> before
130+ // we can attach listeners on the <video> element inside evaluate() .
131+ await page . waitForFunction (
132+ ( ) => {
133+ const el = document . querySelector ( ".widget-video-player video" ) ;
134+ return (
135+ el !== null &&
136+ ( el . readyState >= 1 || el . networkState === HTMLMediaElement . NETWORK_NO_SOURCE || el . error !== null )
137+ ) ;
138+ } ,
139+ { timeout : 8000 }
131140 ) ;
132- // Wait two animation frames so the browser flushes layout and paints the poster frame .
141+ // Flush layout and paint after metadata is ready .
133142 await waitFrames ( page , 2 ) ;
134143 await expect ( widget ) . toHaveScreenshot ( "videoPlayerExternalPoster.png" ) ;
135144 } ) ;
0 commit comments