221 lines
7.9 KiB
JavaScript
221 lines
7.9 KiB
JavaScript
"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);
|
|
});
|