oh-my-muse/muse-studio/e2e/block-structure.spec.ts
lili c76cdec60a test(studio): D-c block 分割/合并 e2e 真跑 + 幂等复位[P1]
- block-structure.spec.ts:真后端端到端——进 work1 workspace→章节结构条(1 小节)→分割(真实 split POST 200→2 小节)→合并(真实 merge POST 200→1 小节);'N 个小节'来自 chapterDetail 真数据,证明 split 真产生 block。

- global-setup section 8:幂等复位 work1 chapter1 为单 Block(删 split 残留,保留 order_no 最小首 Block)。

- BlockStructureBar.extractText 兼容纯文本 content(基础种子 block content 为纯文本非 ProseMirror JSON,JSON.parse 失败时退回原串,使 split 可用)。

验证:playwright block-structure 1 passed(真后端活体) + eslint 0。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 09:01:11 -07:00

44 lines
1.9 KiB
TypeScript

import { expect, test, type Page } from '@playwright/test';
/**
* Block 分割/合并端到端(真实后端,MSW off)。
*
* 覆盖 D 链路:进 work1 workspace → BlockStructureBar 显示章节结构 → 分割首节(真实 split,1→2)→
* 验证 2 个小节 → 合并首节与下一节(真实 merge,2→1)→ 验证回到 1 个小节。
* globalSetup 已复位 work1 chapter1 为单 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('用户分割与合并章节内 Block(真实后端结构编辑)', async ({ page }) => {
await seedToken(page);
await page.goto('/workspace/1');
await expect(page.getByRole('heading', { name: /正在创作/ })).toBeVisible({ timeout: 15_000 });
// 章节结构条出现,初始 1 个小节(globalSetup 已复位)。
await expect(page.getByText(/章节结构 · 1 个小节/)).toBeVisible({ timeout: 15_000 });
// 分割首节(真实 split)→ chapterDetail 重拉 → 2 个小节。
const splitResp = page.waitForResponse(
(r) => /\/blocks\/[^/]+\/split/.test(r.url()) && r.request().method() === 'POST'
);
await page.getByRole('button', { name: '分割小节 1' }).click();
expect((await splitResp).status()).toBe(200);
await expect(page.getByText(/章节结构 · 2 个小节/)).toBeVisible({ timeout: 10_000 });
// 合并首节与下一节(真实 merge)→ 回到 1 个小节。
const mergeResp = page.waitForResponse(
(r) => /\/blocks\/[^/]+\/merge/.test(r.url()) && r.request().method() === 'POST'
);
await page.getByRole('button', { name: '合并小节 1 与下一节' }).click();
expect((await mergeResp).status()).toBe(200);
await expect(page.getByText(/章节结构 · 1 个小节/)).toBeVisible({ timeout: 10_000 });
});