oh-my-muse/muse-studio/e2e/block-autosave.spec.ts
lili a581fec913 fix(studio): 正文保存补真后端 e2e + 修 block/work revision 字段漂移(D 类反假绿)
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>
2026-06-22 07:02:13 -07:00

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');
});