"use strict"; const { chromium } = require("playwright"); const { BASE_URL, UI_TIMEOUT_MS, assert, cleanupDocuments, ensureAuthenticated, prepareTempTreeFixture, openDocument, requireMnoteWebSmokeBaseUrl, } = require("./tree-shell-smoke-helpers"); async function openRuntimeDebug(page, fixture) { const runtimeBaseUrl = requireMnoteWebSmokeBaseUrl(); await page.goto(`${BASE_URL}/api/auth/mnote-web-token`, { waitUntil: "commit", timeout: UI_TIMEOUT_MS }); await page.goto( `${runtimeBaseUrl}/document-debug?documentId=${fixture.parentId}&workspaceId=${fixture.workspaceId}&host=task105-smoke`, { waitUntil: "commit", timeout: UI_TIMEOUT_MS }, ); await page.getByText("Document Runtime Debug").waitFor({ state: "visible", timeout: UI_TIMEOUT_MS }); } async function main() { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ viewport: { width: 1440, height: 960 }, }); const page = await context.newPage(); let fixture = null; let caughtError = null; try { await ensureAuthenticated(page, context.request); fixture = await prepareTempTreeFixture(context.request); await openDocument(page, fixture.workspaceId, fixture.parentId); await openRuntimeDebug(page, fixture); const firstTextarea = page.locator("textarea[data-block-input-id]").first(); await firstTextarea.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS }); await firstTextarea.fill("AlphaBeta", { timeout: UI_TIMEOUT_MS }); await firstTextarea.evaluate((node) => { node.setSelectionRange(5, 5); }); await firstTextarea.press("Enter", { timeout: UI_TIMEOUT_MS }); const textareas = page.locator("textarea[data-block-input-id]"); await textareas.nth(1).waitFor({ state: "visible", timeout: UI_TIMEOUT_MS }); assert((await textareas.count()) === 2, "Enter 拆块后未出现第二个输入块"); assert((await textareas.nth(0).inputValue()) === "Alpha", "拆块后首块文本不正确"); assert((await textareas.nth(1).inputValue()) === "Beta", "拆块后次块文本不正确"); await textareas.nth(1).press("Tab", { timeout: UI_TIMEOUT_MS }); await page.locator(".editor-row-meta").nth(1).getByText("depth=1").waitFor({ state: "visible", timeout: UI_TIMEOUT_MS, }); await textareas.nth(1).press("Shift+Tab", { timeout: UI_TIMEOUT_MS }); await page.locator(".editor-row-meta").nth(1).getByText("depth=0").waitFor({ state: "visible", timeout: UI_TIMEOUT_MS, }); await textareas.nth(1).evaluate((node) => { node.setSelectionRange(0, 0); }); await textareas.nth(1).press("Backspace", { timeout: UI_TIMEOUT_MS }); assert((await page.locator("textarea[data-block-input-id]").count()) === 1, "合并后块数量未回到 1"); assert( (await page.locator("textarea[data-block-input-id]").first().inputValue()) === "AlphaBeta", "Backspace 合并后文本不正确", ); const saveStatus = ((await page.locator("#editor-save-status").textContent()) || "").trim().toLowerCase(); assert( saveStatus === "saved" || saveStatus === "saving" || saveStatus === "idle", `结构事务后保存状态异常:${saveStatus}`, ); console.log( JSON.stringify( { ok: true, baseUrl: BASE_URL, workspaceId: fixture.workspaceId, documentId: fixture.parentId, saveStatus, }, null, 2, ), ); } catch (error) { caughtError = error; } finally { if (fixture) { try { await cleanupDocuments(context.request, fixture.createdIds); } catch (cleanupError) { if (!caughtError) { caughtError = cleanupError; } } } await page.close().catch(() => undefined); await context.close().catch(() => undefined); await browser.close().catch(() => undefined); } if (caughtError) { throw caughtError; } } main().catch((error) => { console.error(error instanceof Error ? error.stack || error.message : String(error)); process.exit(1); });