Skip to content

Commit faf19c1

Browse files
chaxusclaude
andcommitted
test(v9): warm service-worker environment spec (open + save with the SW controlling page and editor frame)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent d27e399 commit faf19c1

2 files changed

Lines changed: 90 additions & 16 deletions

File tree

‎docs/test-matrix.md‎

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,17 @@ embed-save-default.spec、`HX` = html-as-xls.spec、`FN` = filename-matrix.spec
4343

4444
## C. 运行环境
4545

46-
| 环境 | 覆盖 |
47-
| ----------------------- | ------------------------------------------------- |
48-
| 全新 profile 冷启动 | 每个 Playwright 用例即冷 profile |
49-
| SW 已控制页面(老用户) | ⬜(**escape 来源**:PPT 致命弹窗疑似旧构建缓存) |
50-
| standalone 主站 | AS(加载 / manifest);编辑器路径 ⬜ |
51-
| embed(iframe) | ER / EA / 其余全部 |
52-
| Docker 镜像 | 同套 E2E(`test:e2e:docker`) |
53-
| Chromium | 全部 |
54-
| WebKit / Firefox | ⬜ |
55-
| 视觉基线(L3) | ⬜ |
56-
| 性能预算(L4) | ⬜(corpus 报告已含 load/save 耗时,未设阈值) |
46+
| 环境 | 覆盖 |
47+
| ----------------------- | -------------------------------------------------------------------------------------------------------- |
48+
| 全新 profile 冷启动 | 每个 Playwright 用例即冷 profile |
49+
| SW 已控制页面(老用户) | `SW` = sw-warm.spec(第二次加载由 SW 控制页面与编辑器 frame,打开+保存往返);"SW 缓存旧构建"升级路径 ⬜ |
50+
| standalone 主站 | AS(加载 / manifest);编辑器路径 ⬜ |
51+
| embed(iframe) | ER / EA / 其余全部 |
52+
| Docker 镜像 | 同套 E2E(`test:e2e:docker`) |
53+
| Chromium | 全部 |
54+
| WebKit / Firefox | ⬜ |
55+
| 视觉基线(L3) | ⬜ |
56+
| 性能预算(L4) | ⬜(corpus 报告已含 load/save 耗时,未设阈值) |
5757

5858
## D. 交互入口(策略第 9 节)
5959

@@ -68,8 +68,8 @@ embed-save-default.spec、`HX` = html-as-xls.spec、`FN` = filename-matrix.spec
6868

6969
## E. escape 表(用户报出而矩阵未抓)
7070

71-
| 日期 | 现象 | 缺的维度 | 处置 |
72-
| ---------- | -------------------------------- | -------------------------- | ------------------------------------------------ |
73-
| 2026-08-15 | 真实 35 页 PPTX 编辑标题弹致命框 | C. SW 已控制页面(旧构建) | 未复现于当前构建;等用户清 SW 复测;维度待补用例 |
74-
| 2026-08-15 | 插图后保存主线程假死 | A. 插图后保存 | 已修 + ER 用例 |
75-
| 2026-08-15 | Save 按钮常灰 | A. 键盘编辑→Save 按钮 | 已修 + ER 用例 |
71+
| 日期 | 现象 | 缺的维度 | 处置 |
72+
| ---------- | -------------------------------- | -------------------------- | ---------------------------------------------------------------------------------------- |
73+
| 2026-08-15 | 真实 35 页 PPTX 编辑标题弹致命框 | C. SW 已控制页面(旧构建) | 未复现于当前构建;等用户清 SW 复测;"SW 控制页面"维度已补 sw-warm.spec,"旧构建升级"待补 |
74+
| 2026-08-15 | 插图后保存主线程假死 | A. 插图后保存 | 已修 + ER 用例 |
75+
| 2026-08-15 | Save 按钮常灰 | A. 键盘编辑→Save 按钮 | 已修 + ER 用例 |

‎test/e2e/sw-warm.spec.ts‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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

Comments
 (0)