D 类(缺口盘点最高优先):正文自动保存是写作台心脏却零 e2e、仅 MSW 掩盖。补
block-autosave.spec.ts 真打 PUT /works/{id}/blocks/{id} 暴露并修真 bug:
- block revision 漂移:WorkspacePage 裸读 activeBlock.version(真后端返 revision、无 version)
→ expectedRevision 恒为 1 → 真后端乐观锁必冲突(1041000002,curl 实证)。提 blockRevision()
到 useBlockStructure 共享、WorkspacePage 复用。
- work revision 漂移(tsc -b 增量重检暴露):WorkVO 类型无 revision、真后端 detail 返 revision,
chapter create/work delete 裸读致 TS 报错。加 workRevision() helper。
- MuseEditor saveBlock 返回 { revision }(非 openapi 标的 newVersion),L110 改 revision 优先兜底。
- market 申诉 deadline fixture 老化(纯时间推进、非代码):reviewedAt+7天过期致提交被拒
1044000027,globalSetup 刷新被驳回 request.reviewed_at 到近期。
验证:block-autosave + block-structure/chapter/work 回归 4/4 绿;全量 e2e 44/0;tsc/eslint/vitest 79/79。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
53 lines
2.8 KiB
TypeScript
53 lines
2.8 KiB
TypeScript
import { expect, test, type Page } from '@playwright/test';
|
|
|
|
/**
|
|
* 正文自动保存写路 e2e(真实后端,MSW off)——写作台心脏,此前零 e2e 真验证(D 类反假绿)。
|
|
*
|
|
* 反假绿背景:正文保存 PUT /works/{workId}/blocks/{id} 是作品工作台最关键写命令,但此前无任何 e2e 真打,
|
|
* 仅 MSW(不校验乐观锁)掩盖、"全量 e2e 全绿"绿在 mock 上。盘点 + curl 暴露真 bug:
|
|
* WorkspacePage 此前裸读 `activeBlock?.version`,而真后端 BlockRespVO 返回 `revision`(字段漂移,无 version),
|
|
* → expectedRevision 恒回退为 1 → 真后端乐观锁必冲突 `code:1041000002 Block版本冲突`(正文从未真保存成功)。
|
|
* 修复:WorkspacePage 复用 blockRevision()(revision 优先、兼容 mock version),MuseEditor 读返回 revision。
|
|
* 本测打真实 PUT 验 200/code:0 + 返回 revision——证乐观锁通过、正文真落库。
|
|
*
|
|
* 设计:进 work1 chapter1 → 编辑器(tiptap)加载首 block → 打字触发 debounce 2s 自动保存 → 等真实 PUT。
|
|
* 用 blockRevision 动态读当前 revision(不硬编码),故 block1 revision 每轮递增无害、无需 globalSetup 复位 content。
|
|
* 注:与 accept-suggestion(work3/block3)、block-structure(只删多余 block)隔离,互不污染。
|
|
*/
|
|
const TOKEN = 'test1';
|
|
|
|
async function seedToken(page: Page): Promise<void> {
|
|
await page.addInitScript((t) => {
|
|
window.localStorage.setItem('accessToken', t);
|
|
window.localStorage.setItem('tenantId', '1');
|
|
}, TOKEN);
|
|
}
|
|
|
|
test('用户编辑正文触发自动保存(真实后端写命令:乐观锁 expectedRevision 读真实 revision)', async ({ page }) => {
|
|
await seedToken(page);
|
|
|
|
await page.goto('/workspace/1');
|
|
await expect(page.getByRole('heading', { name: /正在创作/ })).toBeVisible({ timeout: 15_000 });
|
|
|
|
// tiptap 编辑器(contenteditable)加载——chapter1 首 block 激活后可编辑。
|
|
const editor = page.locator('[contenteditable="true"]').first();
|
|
await expect(editor).toBeVisible({ timeout: 15_000 });
|
|
|
|
// 点编辑器聚焦 → 打字,触发 onUpdate → debouncedAPISave(2000ms) → PUT /works/1/blocks/{id}。
|
|
await editor.click();
|
|
await page.keyboard.type(`正文自动保存 e2e ${Date.now()}`);
|
|
|
|
// 等真实自动保存 PUT(debounce 2s 后发出)→ 验 200 + code:0 + 返回 revision(乐观锁通过、真落库)。
|
|
const saveResp = await page.waitForResponse(
|
|
(r) =>
|
|
/\/works\/1\/blocks\/[^/]+$/.test(new URL(r.url()).pathname) &&
|
|
r.request().method() === 'PUT',
|
|
{ timeout: 15_000 }
|
|
);
|
|
expect(saveResp.status()).toBe(200);
|
|
const body = await saveResp.json();
|
|
// 修复前此处必为 1041000002(Block版本冲突,expectedRevision 恒为 1);修复后 code:0。
|
|
expect(body.code).toBe(0);
|
|
expect(typeof body.data?.revision).toBe('number');
|
|
});
|