Implement sidebar tree view state persistence
This commit is contained in:
@@ -74,6 +74,10 @@ function fileUrlToPath(value) {
|
||||
return decodeURIComponent(url.pathname);
|
||||
}
|
||||
|
||||
function cssEscape(value) {
|
||||
return String(value).replace(/["\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
async function signUp(context, baseUrl, actorId) {
|
||||
const authResponse = await context.request.fetch(`${baseUrl}/api/auth`, {
|
||||
method: "POST",
|
||||
@@ -126,6 +130,23 @@ async function expandFolder(page, relativePath) {
|
||||
}
|
||||
}
|
||||
|
||||
async function setPageRowExpanded(page, nodeId, expanded) {
|
||||
const selector = `#sidebar-tree-root .tree-row[data-shell-mode="page"][data-node-id="${cssEscape(nodeId)}"]`;
|
||||
await page.locator(selector).waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
||||
const current = await page.locator(selector).first().getAttribute("aria-expanded");
|
||||
if ((current === "true") !== expanded) {
|
||||
await page.locator(`${selector} [data-rust-action="toggle"]`).first().click({ timeout: UI_TIMEOUT_MS });
|
||||
}
|
||||
await page.waitForFunction(
|
||||
({ targetNodeId, expected }) => {
|
||||
const row = document.querySelector(`#sidebar-tree-root .tree-row[data-shell-mode="page"][data-node-id="${CSS.escape(targetNodeId)}"]`);
|
||||
return row && row.getAttribute("aria-expanded") === String(expected);
|
||||
},
|
||||
{ targetNodeId: nodeId, expected: expanded },
|
||||
{ timeout: UI_TIMEOUT_MS },
|
||||
);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
fs.mkdirSync(RESULT_DIR, { recursive: true });
|
||||
const port = await pickPort();
|
||||
@@ -219,6 +240,7 @@ async function main() {
|
||||
const url = request.url();
|
||||
if (
|
||||
url.includes("/api/tree/projections/")
|
||||
|| url.includes("/api/tree/view-state")
|
||||
|| url.includes("/api/page-aggregate/")
|
||||
|| url.includes("/api/leptos-tiptap-runtime/")
|
||||
|| url.includes("/documents/")
|
||||
@@ -329,6 +351,49 @@ async function main() {
|
||||
const staleRowsAfterStarredOpen = await page.locator('#sidebar-tree-root .tree-row[data-shell-mode="page"][data-node-id="stale-my-space-page"]').count();
|
||||
assert.equal(staleRowsAfterStarredOpen, 0, "星标 design 打开后旧我的空间页面树不应长期残留");
|
||||
|
||||
await page.locator('[data-mnote-sidebar-tree-tab="page"]').click({ timeout: UI_TIMEOUT_MS });
|
||||
const pageStatePut = page.waitForResponse((response) => {
|
||||
const postData = response.request().postData() || "";
|
||||
return response.url().includes("/api/tree/view-state")
|
||||
&& response.request().method() === "PUT"
|
||||
&& /"treeKind":"pagetree"/.test(postData)
|
||||
&& /"scope":"design"/.test(postData);
|
||||
}, { timeout: UI_TIMEOUT_MS });
|
||||
const collapsedPageNodeId = "local-dir:design~2F05-editor-mainline~2Fprocess";
|
||||
const expandedPageNodeId = "local-dir:design~2F05-editor-mainline~2Freference";
|
||||
await setPageRowExpanded(page, expandedPageNodeId, false);
|
||||
await setPageRowExpanded(page, collapsedPageNodeId, false);
|
||||
await setPageRowExpanded(page, expandedPageNodeId, true);
|
||||
const pageStateResponse = await pageStatePut;
|
||||
assert(pageStateResponse.ok(), `PageTree scoped view-state 保存失败: ${pageStateResponse.status()}`);
|
||||
await page.reload({ waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
await page.locator('[data-mnote-sidebar-tree-tab="page"]').click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForFunction(
|
||||
({ collapsedPageNodeId, expandedPageNodeId }) => {
|
||||
const collapsed = document.querySelector(`#sidebar-tree-root .tree-row[data-shell-mode="page"][data-node-id="${CSS.escape(collapsedPageNodeId)}"]`);
|
||||
const expanded = document.querySelector(`#sidebar-tree-root .tree-row[data-shell-mode="page"][data-node-id="${CSS.escape(expandedPageNodeId)}"]`);
|
||||
return collapsed && expanded
|
||||
&& collapsed.getAttribute("aria-expanded") === "false"
|
||||
&& expanded.getAttribute("aria-expanded") === "true";
|
||||
},
|
||||
{ collapsedPageNodeId, expandedPageNodeId },
|
||||
{ timeout: UI_TIMEOUT_MS },
|
||||
).catch(async (error) => {
|
||||
const state = await page.evaluate(() => ({
|
||||
href: location.href,
|
||||
pageApplied: document.documentElement.getAttribute("data-mnote-page-tree-view-state-applied") || "",
|
||||
viewStateSaved: document.documentElement.getAttribute("data-mnote-sidebar-tree-view-state-saved") || "",
|
||||
pageTree: Array.from(document.querySelectorAll("#sidebar-tree-root .tree-row[data-shell-mode='page']")).map((row) => ({
|
||||
nodeId: row.getAttribute("data-node-id") || "",
|
||||
expanded: row.getAttribute("aria-expanded") || "",
|
||||
text: row.textContent || "",
|
||||
})),
|
||||
}));
|
||||
throw new Error(`${error.message}; page_tree_state=${JSON.stringify(state)}; requestTimings=${JSON.stringify(requestTimings.filter((entry) => entry.url.includes("/api/tree/view-state")))}`);
|
||||
});
|
||||
timeline.pageTreeUserStateRestoredAt = Date.now();
|
||||
await page.locator('[data-mnote-sidebar-tree-tab="filetree"]').click({ timeout: UI_TIMEOUT_MS });
|
||||
|
||||
await page.screenshot({ path: path.join(RESULT_DIR, "after-starred-design.png"), fullPage: true });
|
||||
timeline.filetreeInteractiveAt = Date.now();
|
||||
await expandFolder(page, "design/05-editor-mainline");
|
||||
|
||||
Reference in New Issue
Block a user