|
| 1 | +import { expect, test } from './lib/l0'; |
| 2 | + |
| 3 | +declare const XLSX: any; |
| 4 | +declare function post(type: string, payload?: Record<string, unknown>): Promise<any>; |
| 5 | + |
| 6 | +/** |
| 7 | + * Environment dimension "service worker already controls the page" (matrix |
| 8 | + * section C, escape table row 1). Every other E2E runs on a cold profile |
| 9 | + * where the first navigation is uncontrolled; returning users always load |
| 10 | + * through the SW, and the campaign's live report (fatal dialog on a real |
| 11 | + * deck) is suspected to come from that path. Warm the SW, reload so it |
| 12 | + * controls the demo page and every editor frame, then open + save. |
| 13 | + */ |
| 14 | +test.describe('warm service worker (real editor)', () => { |
| 15 | + test.describe.configure({ timeout: 150_000 }); |
| 16 | + |
| 17 | + test('opens and round-trips a workbook when the SW controls the page', async ({ page }) => { |
| 18 | + await page.goto('/embed-demo.html'); |
| 19 | + await expect(page.locator('#status')).toHaveText('ready', { timeout: 60_000 }); |
| 20 | + // Let the SW install/activate, then reload into a controlled document. |
| 21 | + await page.evaluate(async () => { |
| 22 | + const reg = await navigator.serviceWorker.ready; |
| 23 | + if (!reg.active) throw new Error('service worker never activated'); |
| 24 | + }); |
| 25 | + await page.reload(); |
| 26 | + await expect(page.locator('#status')).toHaveText('ready', { timeout: 60_000 }); |
| 27 | + const controlled = await page.evaluate(() => Boolean(navigator.serviceWorker.controller)); |
| 28 | + expect(controlled, 'the demo page must be SW-controlled on the second load').toBe(true); |
| 29 | + |
| 30 | + const result = await page.evaluate(async () => { |
| 31 | + const wb = XLSX.utils.book_new(); |
| 32 | + XLSX.utils.book_append_sheet( |
| 33 | + wb, |
| 34 | + XLSX.utils.aoa_to_sheet([ |
| 35 | + ['warm', 'sw'], |
| 36 | + ['a', 1], |
| 37 | + ]), |
| 38 | + 'S', |
| 39 | + ); |
| 40 | + const data = XLSX.write(wb, { bookType: 'xlsx', type: 'array' }); |
| 41 | + await post('document:open-buffer', { |
| 42 | + fileName: 'warm.xlsx', |
| 43 | + buffer: new Uint8Array(data).buffer, |
| 44 | + readonly: false, |
| 45 | + }); |
| 46 | + const saved = await post('document:save', {}); |
| 47 | + const out = XLSX.read(new Uint8Array(await saved.file.arrayBuffer()), { type: 'array' }); |
| 48 | + const frameControlled = (() => { |
| 49 | + const visit = (win: Window): boolean | null => { |
| 50 | + try { |
| 51 | + if (win.location.pathname.includes('/web-apps/apps/')) |
| 52 | + return Boolean(win.navigator.serviceWorker.controller); |
| 53 | + } catch { |
| 54 | + return null; |
| 55 | + } |
| 56 | + for (let i = 0; i < win.frames.length; i++) { |
| 57 | + const r = visit(win.frames[i]); |
| 58 | + if (r !== null) return r; |
| 59 | + } |
| 60 | + return null; |
| 61 | + }; |
| 62 | + return visit(window); |
| 63 | + })(); |
| 64 | + return { |
| 65 | + name: saved.file.name as string, |
| 66 | + csv: XLSX.utils.sheet_to_csv(out.Sheets[out.SheetNames[0]]).trim(), |
| 67 | + frameControlled, |
| 68 | + }; |
| 69 | + }); |
| 70 | + expect(result.frameControlled, 'the editor frame itself must be SW-controlled').toBe(true); |
| 71 | + expect(result.name).toBe('warm.xlsx'); |
| 72 | + expect(result.csv).toBe('warm,sw\na,1'); |
| 73 | + }); |
| 74 | +}); |
0 commit comments