| 1 | // Run with Playwright CLI on a disposable room page containing a thread link. |
| 2 | // Uses real link clicks, the bundled Fixi and morph, and browser history. |
| 3 | async page => { |
| 4 | const roomURL = page.url().split("#" )[0]; |
| 5 | if (!/\/rooms\/[a-z0-9_-]+$/.test(roomURL)) throw Error("Open a room with a thread before running this check." ); |
| 6 | const results = [], requests = []; |
| 7 | const record = request => requests.push(request.url().split("?" )[0]); |
| 8 | page.on("request" , record); |
| 9 | const shell = async expected => { |
| 10 | const state = await page.evaluate(() => ({ |
| 11 | headers: document.querySelectorAll("#topbar" ).length, |
| 12 | contents: document.querySelectorAll("#content" ).length, |
| 13 | sections: [...document.querySelectorAll("#content>section" )].map(node => node.id), |
| 14 | composers: document.querySelectorAll("room-compose" ).length |
| 15 | })); |
| 16 | if (state.headers !== 1 || state.contents !== 1 || state.sections.length !== 1 || state.sections[0] !== expected || state.composers > 1) throw Error(JSON.stringify(state)); |
| 17 | return state; |
| 18 | }; |
| 19 | try { |
| 20 | for (const width of [320, 390, 600]) { |
| 21 | await page.setViewportSize({width, height:844}); |
| 22 | await page.goto(roomURL); |
| 23 | await page.evaluate(() => { window.__threadNavigation = true; }); |
| 24 | await page.locator('#messages a[href*="/thread/"]' ).first().click(); |
| 25 | await page.waitForURL("**/thread/**" ); |
| 26 | await shell("thread" ); |
| 27 | if (!await page.evaluate(() => window.__threadNavigation)) throw Error("Thread navigation unexpectedly reloaded the page." ); |
| 28 | const back = page.locator("#mobile-back" ); |
| 29 | const destination = await back.evaluate(node => node.href); |
| 30 | if (!destination.includes("#msg-" )) throw Error("Back link lost its message target." ); |
| 31 | await back.click(); |
| 32 | await page.waitForURL(destination); |
| 33 | const returned = await shell("room" ); |
| 34 | await page.locator("#mobile-back" ).click(); |
| 35 | await page.waitForURL("**/chat" ); |
| 36 | const chat = await shell("chat" ); |
| 37 | if (chat.composers !== 0) throw Error("Chat list kept the thread composer." ); |
| 38 | await page.goBack(); |
| 39 | await page.waitForFunction(() => document.querySelector("#content>section" )?.id === "room" ); |
| 40 | await shell("room" ); |
| 41 | await page.goForward(); |
| 42 | await page.waitForFunction(() => document.querySelector("#content>section" )?.id === "chat" ); |
| 43 | await shell("chat" ); |
| 44 | results.push({width, returned, history:true}); |
| 45 | } |
| 46 | if (requests.some(path => /\/(?:null|undefined)$/.test(path))) throw Error("A stale handler requested an invalid navigation URL." ); |
| 47 | return results; |
| 48 | } finally { page.off("request" , record); } |
| 49 | } |