"use strict"; const { chromium } = require("playwright"); const { BASE_URL, UI_TIMEOUT_MS, assert, cleanupDocuments, createTempDocument, ensureAuthenticated, openDocument, openFilesystemView, openSectionView, renameDocument, } = require("./tree-shell-smoke-helpers"); async function waitForPageTitleInput(page) { const input = page.locator('[data-page-title-input="true"][data-pane-role="primary"]').first(); await input.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS }); return input; } async function waitForTitleSave(page, documentId, expectedTitle) { await page.waitForResponse( async (response) => { if (!response.url().includes("/api/documents/title") || response.request().method() !== "POST") { return false; } const payload = response.request().postDataJSON(); return ( payload?.documentId === documentId && payload?.title === expectedTitle && payload?.commandName === "page.head.updateTitle" && response.ok() ); }, { timeout: UI_TIMEOUT_MS }, ); } async function renameThroughPageHead(page, documentId, title) { const titleInput = await waitForPageTitleInput(page); await titleInput.click({ timeout: UI_TIMEOUT_MS }); await page.keyboard.press("Control+a"); await page.keyboard.type(title, { delay: 20 }); await titleInput.blur(); await waitForTitleSave(page, documentId, title); } async function waitForBreadcrumbTitle(page, title) { await page.waitForFunction( (expectedTitle) => { const nav = document.querySelector("header nav"); return (nav?.textContent ?? "").includes(expectedTitle); }, title, { timeout: UI_TIMEOUT_MS }, ); } async function waitForSidebarRowTitle(page, documentId, title) { await page.waitForFunction( ({ docId, expectedTitle }) => { const row = document.querySelector( `[data-node-id="${docId}"] .tree-link-title, [data-testid="page-tree-row"][data-node-id="${docId}"], .wolai-page-row[data-node-id="${docId}"] > .wolai-row-title`, ); return (row?.textContent ?? "").includes(expectedTitle); }, { docId: documentId, expectedTitle: title }, { timeout: UI_TIMEOUT_MS }, ); } async function waitForPageTreeTitle(page, documentId, title) { await openSectionView(page); await page.waitForFunction( ({ nodeId, expectedTitle }) => { const row = document.querySelector( `#sidebar-tree-root .tree-row[data-shell-mode="page"][data-node-id="${nodeId}"] .tree-link-title, [data-testid="page-tree-row"][data-node-id="${nodeId}"], .wolai-page-row[data-node-id="${nodeId}"] > .wolai-row-title`, ); return (row?.textContent ?? "").includes(expectedTitle); }, { nodeId: documentId, expectedTitle: title }, { timeout: UI_TIMEOUT_MS }, ); } async function waitForFileTreeTitle(page, documentId, title) { await openFilesystemView(page); await page.waitForFunction( ({ docId, expectedTitle }) => { const row = document.querySelector( `#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-doc-id="${docId}"] .tree-link-title, #sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-document-id="${docId}"] .tree-link-title, [data-testid="filetree-doc-row"][data-doc-id="${docId}"]`, ); return (row?.textContent ?? "").includes(expectedTitle); }, { docId: documentId, expectedTitle: title }, { timeout: UI_TIMEOUT_MS }, ); } async function readVisibleTitle(page) { const titleInput = page.locator('[data-page-title-input="true"][data-pane-role="primary"]').first(); if (await titleInput.isVisible().catch(() => false)) { return (await titleInput.inputValue()).trim(); } const heading = page.locator("h1").first(); return ((await heading.textContent()) ?? "").trim(); } 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 caughtError = null; let fixture = null; try { const viewer = await ensureAuthenticated(page, context.request); const first = await createTempDocument(context.request, null); const second = await createTempDocument(context.request, null); const initialTitle = `phase-i-initial-${Date.now().toString().slice(-6)}`; const renamedTitle = `phase-i-renamed-${(Date.now() + 1).toString().slice(-6)}`; const siblingTitle = `phase-i-sibling-${(Date.now() + 2).toString().slice(-6)}`; await renameDocument(context.request, first.workspaceId, first.documentId, initialTitle); await renameDocument(context.request, second.workspaceId, second.documentId, siblingTitle); fixture = { workspaceId: first.workspaceId, primaryId: first.documentId, siblingId: second.documentId, createdIds: [first.documentId, second.documentId], initialTitle, renamedTitle, siblingTitle, }; await openDocument(page, fixture.workspaceId, fixture.primaryId); await waitForPageTitleInput(page); assert((await readVisibleTitle(page)).includes(initialTitle), "页头未回填初始标题"); await renameThroughPageHead(page, fixture.primaryId, fixture.renamedTitle); assert((await readVisibleTitle(page)).includes(fixture.renamedTitle), "页头未显示最新标题"); await waitForBreadcrumbTitle(page, fixture.renamedTitle); await waitForSidebarRowTitle(page, fixture.primaryId, fixture.renamedTitle); await waitForPageTreeTitle(page, fixture.primaryId, fixture.renamedTitle); await waitForFileTreeTitle(page, fixture.primaryId, fixture.renamedTitle); await openDocument(page, fixture.workspaceId, fixture.siblingId); await waitForPageTitleInput(page); assert((await readVisibleTitle(page)).includes(fixture.siblingTitle), "切到兄弟页后标题不正确"); await openDocument(page, fixture.workspaceId, fixture.primaryId); await waitForPageTitleInput(page); await waitForBreadcrumbTitle(page, fixture.renamedTitle); await waitForSidebarRowTitle(page, fixture.primaryId, fixture.renamedTitle); assert((await readVisibleTitle(page)).includes(fixture.renamedTitle), "切页往返后页头标题回闪"); await page.reload({ waitUntil: "commit", timeout: UI_TIMEOUT_MS }); await waitForPageTitleInput(page); await waitForBreadcrumbTitle(page, fixture.renamedTitle); await waitForSidebarRowTitle(page, fixture.primaryId, fixture.renamedTitle); await waitForPageTreeTitle(page, fixture.primaryId, fixture.renamedTitle); await waitForFileTreeTitle(page, fixture.primaryId, fixture.renamedTitle); assert((await readVisibleTitle(page)).includes(fixture.renamedTitle), "刷新后页头标题未保持一致"); console.log( JSON.stringify( { ok: true, baseUrl: BASE_URL, viewerUserId: viewer.userId, workspaceId: fixture.workspaceId, primaryId: fixture.primaryId, siblingId: fixture.siblingId, renamedTitle: fixture.renamedTitle, }, null, 2, ), ); } catch (error) { caughtError = error; } finally { if (fixture) { try { await cleanupDocuments(context.request, fixture.createdIds); } catch (cleanupError) { if (!caughtError) { caughtError = cleanupError; } else { console.error( `清理临时页面失败:${ cleanupError instanceof Error ? cleanupError.stack || cleanupError.message : String(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); });