Improve local filetree view state and sidebar performance

This commit is contained in:
lix-2026
2026-05-27 11:31:12 +08:00
parent 58e2fdb5d8
commit 3ae33cc21d
56 changed files with 8614 additions and 461 deletions
@@ -81,11 +81,21 @@ async function main() {
});
const page = await context.newPage();
const treeCommands = [];
const treeCommandResponses = [];
const unexpectedDialogs = [];
page.on("request", (request) => {
if (!request.url().includes("/api/tree/commands")) return;
const body = request.postDataJSON?.() || null;
treeCommands.push({ method: request.method(), body });
});
page.on("response", async (response) => {
if (!response.url().includes("/api/tree/commands")) return;
let body = "";
try {
body = await response.text();
} catch (_) {}
treeCommandResponses.push({ status: response.status(), body });
});
try {
await quickLogin(page);
@@ -95,6 +105,14 @@ async function main() {
url.searchParams.set("treeView", "filetree");
await page.goto(url.toString(), { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
const docsRow = page.locator('#sidebar-file-tree-root .tree-row[data-local-relative-path="docs"]').first();
if (await docsRow.isVisible({ timeout: UI_TIMEOUT_MS }).catch(() => false)) {
const expanded = await docsRow.getAttribute("aria-expanded").catch(() => null);
if (expanded !== "true") {
await docsRow.locator('[data-rust-action="toggle"]').click({ timeout: UI_TIMEOUT_MS });
}
}
const selectors = assets.map((asset) => `#sidebar-file-tree-root .tree-row[data-asset-id="${asset.assetId}"]`);
await page.locator(selectors[0]).waitFor({
state: "visible",
@@ -137,8 +155,16 @@ async function main() {
await page.locator('.mnote-tree-context-menu__item[data-action="delete-trash"]').click({ timeout: UI_TIMEOUT_MS });
const confirmText = await dialogPromise;
assert.match(confirmText, /2 个附件/, `右键 bulk delete 确认文案应包含附件数量: ${confirmText}`);
page.on("dialog", async (dialog) => {
unexpectedDialogs.push(dialog.message());
await dialog.accept().catch(() => undefined);
});
await page.waitForFunction(() => document.documentElement.getAttribute("data-mnote-filetree-bulk-delete-applied") === "true", null, {
await page.waitForFunction(() => {
const status = document.documentElement.getAttribute("data-mnote-filetree-last-action-status");
return document.documentElement.getAttribute("data-mnote-filetree-bulk-delete-applied") === "true"
|| status === "failed";
}, null, {
timeout: UI_TIMEOUT_MS,
});
const actionState = await page.evaluate(() => ({
@@ -146,8 +172,11 @@ async function main() {
status: document.documentElement.getAttribute("data-mnote-filetree-last-action-status"),
rowId: document.documentElement.getAttribute("data-mnote-filetree-last-action-row-id"),
applied: document.documentElement.getAttribute("data-mnote-filetree-bulk-delete-applied"),
batchId: document.documentElement.getAttribute("data-mnote-filetree-bulk-delete-batch-id"),
batchRefresh: document.documentElement.getAttribute("data-mnote-filetree-batch-refresh-applied"),
}));
assert.equal(actionState.action, "bulk-delete", `bulk delete 应记录 action 名: ${JSON.stringify(actionState)}`);
assert.deepEqual(unexpectedDialogs, [], `bulk delete 不应出现失败 alert: ${JSON.stringify({ unexpectedDialogs, treeCommands, treeCommandResponses })}`);
assert.equal(actionState.status, "archived", `bulk delete 应记录 undo/archive 状态: ${JSON.stringify(actionState)}`);
assert.equal(actionState.rowId, rowIds[1], `bulk delete 应记录触发目标 row: ${JSON.stringify(actionState)}`);
for (const asset of assets) {
@@ -159,6 +188,13 @@ async function main() {
}
const archiveCommands = treeCommands.filter((entry) => entry.body && entry.body.action === "archive");
assert.equal(archiveCommands.length, 2, `bulk delete 应发出两个 archive tree command: ${JSON.stringify(treeCommands)}`);
assert(actionState.batchId, `bulk delete 应生成 batch id: ${JSON.stringify(actionState)}`);
assert.equal(actionState.batchRefresh, actionState.batchId, `bulk delete 应按 batch 完成后统一刷新: ${JSON.stringify(actionState)}`);
assert.deepEqual(
Array.from(new Set(archiveCommands.map((entry) => entry.body.batchId))).sort(),
[actionState.batchId],
`archive command 应共享同一个 batchId: ${JSON.stringify(archiveCommands)}`,
);
assert.deepEqual(
archiveCommands.map((entry) => entry.body.documentId).sort(),
assets.map((asset) => asset.assetId).sort(),