Skip to content

Commit acbae50

Browse files
committed
fix(e2e): track WebKit teardown through document commit
1 parent 79cb19e commit acbae50

1 file changed

Lines changed: 21 additions & 13 deletions

File tree

‎apps/sim/scripts/test-mobile-e2e.ts‎

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -128,22 +128,28 @@ interface DocumentNavigation {
128128

129129
const documentNavigations = new WeakMap<Page, DocumentNavigation>()
130130

131-
/** Marks the outgoing document before Playwright starts navigation and releases it on completion. */
131+
/** Tracks teardown until run resolves at document commit, then waits for the new page to load. */
132132
async function withDocumentNavigation<T>(
133133
page: Page,
134134
destination: string,
135135
run: () => Promise<T>
136136
): Promise<T> {
137137
const navigation = { from: page.url(), to: new URL(destination, baseUrl).href }
138138
documentNavigations.set(page, navigation)
139+
let result: T
139140
try {
140-
return await run()
141+
result = await run()
141142
} finally {
142143
if (documentNavigations.get(page) === navigation) documentNavigations.delete(page)
143144
}
145+
await page.waitForLoadState('load', { timeout: 360_000 })
146+
return result
144147
}
145148

146-
/** WebKit reports handled same-origin fetch failures during document teardown as page errors. */
149+
/**
150+
* WebKit reports handled same-origin requests during teardown as page errors.
151+
* Playwright splits stackless EventSource diagnostics at the URL colon and drops its first slash.
152+
*/
147153
function getNavigationCancellationPath(
148154
error: Error,
149155
browserName: string,
@@ -159,8 +165,13 @@ function getNavigationCancellationPath(
159165
const match = error.stack?.match(
160166
/^Fetch API cannot load (https?:\/\/\S+) due to access control checks\./
161167
)
162-
if (!match) return
163-
const url = new URL(match[1])
168+
const eventSourceMatch =
169+
!error.stack && error.name === 'EventSource cannot load http'
170+
? error.message.match(/^\/([^/\s]\S*) due to access control checks\.$/)
171+
: null
172+
const target = match?.[1] ?? (eventSourceMatch ? `http://${eventSourceMatch[1]}` : undefined)
173+
if (!target) return
174+
const url = new URL(target)
164175
return url.origin === baseUrl.origin ? url.pathname : undefined
165176
}
166177

@@ -251,7 +262,7 @@ async function visit(page: Page, route: string) {
251262
{ timeout: 360_000 }
252263
),
253264
withDocumentNavigation(page, path, () =>
254-
page.goto(path, { waitUntil: 'load', timeout: 360_000 })
265+
page.goto(path, { waitUntil: 'commit', timeout: 360_000 })
255266
),
256267
])
257268
assert(response.status() < 400, `Could not load ${route}: ${response.status()}`)
@@ -478,9 +489,6 @@ async function exerciseViewport(
478489
await context.tracing.start({ screenshots: true, snapshots: true })
479490
const page = await context.newPage()
480491
const pageErrors: string[] = []
481-
page.on('framenavigated', (frame) => {
482-
if (frame === page.mainFrame()) documentNavigations.delete(page)
483-
})
484492
page.on('pageerror', (error) => {
485493
const pendingNavigation = documentNavigations.get(page) ?? null
486494
const path = getNavigationCancellationPath(error, browserName, pendingNavigation)
@@ -756,7 +764,7 @@ async function exerciseViewport(
756764
async () => {
757765
const chatPath = `/chat/${publicChatIdentifier}`
758766
const response = await withDocumentNavigation(page, chatPath, () =>
759-
page.goto(chatPath, { waitUntil: 'load', timeout: 360_000 })
767+
page.goto(chatPath, { waitUntil: 'commit', timeout: 360_000 })
760768
)
761769
assert(response && response.status() < 400)
762770
await expect(page.getByText('Public mobile welcome', { exact: true })).toBeVisible({
@@ -855,7 +863,7 @@ async function exerciseViewport(
855863
})
856864
.toBe(value)
857865
await withDocumentNavigation(page, page.url(), () =>
858-
page.reload({ waitUntil: 'load', timeout: 360_000 })
866+
page.reload({ waitUntil: 'commit', timeout: 360_000 })
859867
)
860868
await expect(cell).toHaveText(value)
861869
await page.getByRole('button', { name: 'Column options', exact: true }).first().tap()
@@ -907,7 +915,7 @@ async function exerciseViewport(
907915

908916
await check(`${prefix}/standalone settings sections remain accessible`, page, async () => {
909917
const response = await withDocumentNavigation(page, '/selfhost/settings/general', () =>
910-
page.goto('/selfhost/settings/general', { waitUntil: 'load', timeout: 360_000 })
918+
page.goto('/selfhost/settings/general', { waitUntil: 'commit', timeout: 360_000 })
911919
)
912920
assert(response && response.status() < 400)
913921
await expect(page.getByRole('button', { name: 'Theme', exact: true })).toBeVisible({
@@ -978,7 +986,7 @@ async function exerciseViewport(
978986
if (viewport.name === 'phone') {
979987
await sql`update workflow set locked = true where id = ${workflowId}`
980988
await withDocumentNavigation(page, page.url(), () =>
981-
page.reload({ waitUntil: 'load', timeout: 360_000 })
989+
page.reload({ waitUntil: 'commit', timeout: 360_000 })
982990
)
983991
const notifications = page.getByLabel('Notifications', { exact: true })
984992
await expect(notifications).toContainText('This workflow is locked', {

0 commit comments

Comments
 (0)