fix(mnote-web): 即时显示页面内新建思维导图
This commit is contained in:
+12
-4
@@ -145,6 +145,10 @@
|
|||||||
- `create` 成功后立即插入 Page Tree 行,以及 File Tree 的 `doc:<id>` / `index:<id>` 行。
|
- `create` 成功后立即插入 Page Tree 行,以及 File Tree 的 `doc:<id>` / `index:<id>` 行。
|
||||||
- `purge/delete/archive` 成功后立即从 Page Tree 与 File Tree 移除目标页面行。
|
- `purge/delete/archive` 成功后立即从 Page Tree 与 File Tree 移除目标页面行。
|
||||||
- 命令成功后清理按钮 pending 状态,避免无整页刷新时按钮残留禁用。
|
- 命令成功后清理按钮 pending 状态,避免无整页刷新时按钮残留禁用。
|
||||||
|
6. `rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||||
|
- 追加修复 Rust 3000 页面内 mindmap 创建路径:主文档壳现在同时监听 `wolai:assets-changed`,并观察同页 `/api/mindmap/<doc>/<mindmap>` POST 成功请求。
|
||||||
|
- mindmap 创建成功后立即把 `mindmap-<id>.json` 作为 File Tree asset row 插入当前页面下,避免等刷新后 projection 才出现。
|
||||||
|
- 这覆盖 Leptos/Rust island 未触发 React `emitAssetsChanged()` 的路径。
|
||||||
|
|
||||||
2026-05-14 回归复现与补修:
|
2026-05-14 回归复现与补修:
|
||||||
|
|
||||||
@@ -152,6 +156,8 @@
|
|||||||
- 复现证据:旧 3000 进程为 `/mnt/Data1T/mnote/rust/target/debug/mnote-web (deleted)`;点击“新建页面”后接口返回成功并跳转到 `/documents/tree_1778714360642_10?...`,但 5 秒后 `#sidebar-tree-root` 仍不包含该 document id。
|
- 复现证据:旧 3000 进程为 `/mnt/Data1T/mnote/rust/target/debug/mnote-web (deleted)`;点击“新建页面”后接口返回成功并跳转到 `/documents/tree_1778714360642_10?...`,但 5 秒后 `#sidebar-tree-root` 仍不包含该 document id。
|
||||||
- 当前源码 3002 在补修前也复现同类问题:新建后跳转到 `/documents/tree_1778714400752_12?...`,但 Page Tree DOM 不包含当前 document id。
|
- 当前源码 3002 在补修前也复现同类问题:新建后跳转到 `/documents/tree_1778714400752_12?...`,但 Page Tree DOM 不包含当前 document id。
|
||||||
- 补修后已重启 3000,并在 3000 主入口完成新建/删除无刷新 smoke。
|
- 补修后已重启 3000,并在 3000 主入口完成新建/删除无刷新 smoke。
|
||||||
|
- 用户继续复测确认页面新建/删除已恢复,但页面内新建思维导图仍需刷新。真实 smoke 复核后确认:3000 页面内导图创建会走 `/api/mindmap/<doc>/<mindmap>` 写入链路,主文档壳原先没有把该成功写入同步 materialize 到 File Tree asset row。
|
||||||
|
- 补修后已在 3000 主入口通过 slash 菜单真实新建思维导图,并确认 File Tree 无刷新出现 `asset:<mindmapId>` 行。
|
||||||
|
|
||||||
## 7. 验收标准
|
## 7. 验收标准
|
||||||
|
|
||||||
@@ -159,7 +165,7 @@
|
|||||||
|
|
||||||
1. [x] 在 `http://127.0.0.1:3000/documents/<id>` 主页面壳中新建页面后,不刷新浏览器即可在 Page Tree 与 File Tree 中看到新页面。代码证据:React Sidebar 路径由 `handleCreate()` 本地 upsert documents;Rust 3000 主文档壳由 `tree:local-command` 本地插入 Page Tree 行与 File Tree `doc:<id>` / `index:<id>` 行;单测覆盖:`sidebar-local-projection.test.ts`,Rust runtime 覆盖:`cargo test -p mnote-web sidebar_tree_runtime -- --nocapture`。
|
1. [x] 在 `http://127.0.0.1:3000/documents/<id>` 主页面壳中新建页面后,不刷新浏览器即可在 Page Tree 与 File Tree 中看到新页面。代码证据:React Sidebar 路径由 `handleCreate()` 本地 upsert documents;Rust 3000 主文档壳由 `tree:local-command` 本地插入 Page Tree 行与 File Tree `doc:<id>` / `index:<id>` 行;单测覆盖:`sidebar-local-projection.test.ts`,Rust runtime 覆盖:`cargo test -p mnote-web sidebar_tree_runtime -- --nocapture`。
|
||||||
2. [x] 在主页面壳中删除页面后,不刷新浏览器即可在 Page Tree 与 File Tree 中看到目标页面消失。代码证据:React Sidebar 路径由 `handleDelete()` / `handleDeleteResourceSelection()` 同步 `removeSidebarDocumentRecords()`;Rust 3000 主文档壳由 `tree:local-command` 在 `purge/delete/archive` 成功后调用 remove delta 本地移除;单测覆盖:`sidebar-local-projection.test.ts` 与 `sidebar-delete-preflight-source.test.ts`,Rust runtime 覆盖:`cargo test -p mnote-web sidebar_tree_runtime -- --nocapture`。
|
2. [x] 在主页面壳中删除页面后,不刷新浏览器即可在 Page Tree 与 File Tree 中看到目标页面消失。代码证据:React Sidebar 路径由 `handleDelete()` / `handleDeleteResourceSelection()` 同步 `removeSidebarDocumentRecords()`;Rust 3000 主文档壳由 `tree:local-command` 在 `purge/delete/archive` 成功后调用 remove delta 本地移除;单测覆盖:`sidebar-local-projection.test.ts` 与 `sidebar-delete-preflight-source.test.ts`,Rust runtime 覆盖:`cargo test -p mnote-web sidebar_tree_runtime -- --nocapture`。
|
||||||
3. [x] 在主页面壳中新建 mindmap 后,不刷新浏览器即可在 File Tree 中看到对应 `mindmap-<id>.json` 行。代码证据:`emitAssetsChanged(asset)` 已进入本地 `mindmapAssets`,`localFileTreeProjection` 用本地 mindmap assets 重建 asset row;单测覆盖:`sidebar-local-projection.test.ts`。
|
3. [x] 在主页面壳中新建 mindmap 后,不刷新浏览器即可在 File Tree 中看到对应 `mindmap-<id>.json` 行。代码证据:React Sidebar 路径由 `emitAssetsChanged(asset)` 进入本地 `mindmapAssets` 并重建 asset row;Rust 3000 主文档壳由 `wolai:assets-changed` listener 和 `/api/mindmap/<doc>/<mindmap>` POST observer 本地插入 File Tree asset row;单测覆盖:`sidebar-local-projection.test.ts`,Rust runtime 覆盖:`cargo test -p mnote-web sidebar_tree_runtime -- --nocapture`。
|
||||||
4. [x] 上述三条路径均不得依赖 `window.location.reload()` 或顶层 navigation。代码证据:本轮没有新增 reload;create/delete 以本地 state/projection apply 为主,显式 refetch 仅作 snapshot 校准。
|
4. [x] 上述三条路径均不得依赖 `window.location.reload()` 或顶层 navigation。代码证据:本轮没有新增 reload;create/delete 以本地 state/projection apply 为主,显式 refetch 仅作 snapshot 校准。
|
||||||
5. [x] smoke 需要记录事件或本地 apply 标记、最终 DOM 状态与相关网络请求。React Sidebar 路径保留 `tmp/task426-sidebar-main-no-reload-smoke/result.json` 作为 Next/3001 与 mindmap asset 证据;Rust 3000 主文档壳新增 `tmp/task426-mnote-web-main-no-reload-smoke/result.json`,覆盖真实 3000 页面新建/删除无刷新 DOM 结果。代码级测试同时覆盖:`sidebar-delete-preflight-source.test.ts` 锁定 create/delete 事件与本地 documents apply;`use-sidebar-data.test.tsx` 锁定 Convex live 显式 refetch 会真实拉 `/api/sidebar`;`cargo test -p mnote-web sidebar_tree_runtime -- --nocapture` 锁定 Rust 主文档壳 runtime 基础行为。
|
5. [x] smoke 需要记录事件或本地 apply 标记、最终 DOM 状态与相关网络请求。React Sidebar 路径保留 `tmp/task426-sidebar-main-no-reload-smoke/result.json` 作为 Next/3001 与 mindmap asset 证据;Rust 3000 主文档壳新增 `tmp/task426-mnote-web-main-no-reload-smoke/result.json`,覆盖真实 3000 页面新建/删除无刷新 DOM 结果。代码级测试同时覆盖:`sidebar-delete-preflight-source.test.ts` 锁定 create/delete 事件与本地 documents apply;`use-sidebar-data.test.tsx` 锁定 Convex live 显式 refetch 会真实拉 `/api/sidebar`;`cargo test -p mnote-web sidebar_tree_runtime -- --nocapture` 锁定 Rust 主文档壳 runtime 基础行为。
|
||||||
6. [x] 修复后补测试,防止 `sidebarQuery.refetch()` 在 Convex live 模式下继续作为空操作吞掉显式刷新请求。测试覆盖:`use-sidebar-data.test.tsx`。
|
6. [x] 修复后补测试,防止 `sidebarQuery.refetch()` 在 Convex live 模式下继续作为空操作吞掉显式刷新请求。测试覆盖:`use-sidebar-data.test.tsx`。
|
||||||
@@ -195,10 +201,12 @@ MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task426-mnote-web-main-no-r
|
|||||||
|
|
||||||
- `ok=true`
|
- `ok=true`
|
||||||
- `baseUrl=http://127.0.0.1:3000`
|
- `baseUrl=http://127.0.0.1:3000`
|
||||||
- `createdDocumentId=tree_1778715292685_1`
|
- `createdDocumentId=tree_1778720530823_2`
|
||||||
- 新建后:`localApplied=create`、`pageRows=1`、`fileRows=["doc:tree_1778715292685_1","index:tree_1778715292685_1"]`
|
- `mindmapId=mindmap_1778720531308`
|
||||||
|
- 新建页面后:`localApplied=create`、`pageRows=1`、`fileRows=["doc:tree_1778720530823_2","index:tree_1778720530823_2"]`
|
||||||
|
- 新建思维导图后:`localApplied=true`、`rows=[{"rowId":"asset:mindmap_1778720531308","docId":"tree_1778720530823_2","title":"mindmap-mindmap_1778720531308.json…","kind":"mindmap"}]`
|
||||||
- 删除后:`localApplied=remove`、`pageRows=0`、`fileRows=[]`
|
- 删除后:`localApplied=remove`、`pageRows=0`、`fileRows=[]`
|
||||||
- `requests` 仅包含两次 `POST /api/tree/commands`,无浏览器刷新依赖
|
- `requests` 包含两次 `POST /api/tree/commands`、一次 mindmap projection GET、两次 `/api/mindmap/<doc>/<mindmap>` POST;无浏览器刷新依赖
|
||||||
- 结果文件:`tmp/task426-mnote-web-main-no-reload-smoke/result.json`
|
- 结果文件:`tmp/task426-mnote-web-main-no-reload-smoke/result.json`
|
||||||
|
|
||||||
补充对比:
|
补充对比:
|
||||||
|
|||||||
@@ -1997,6 +1997,120 @@ const SIDEBAR_TREE_JS: &str = r##"
|
|||||||
document.documentElement.setAttribute('data-mnote-last-upload-asset-id', assetId);
|
document.documentElement.setAttribute('data-mnote-last-upload-asset-id', assetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeFileTreeAssetRow(assetId) {
|
||||||
|
var normalized = String(assetId || '').trim();
|
||||||
|
if (!normalized) return false;
|
||||||
|
var row = document.querySelector('.tree-row[data-shell-mode="filetree"][data-asset-id="' + cssEscape(normalized) + '"]');
|
||||||
|
var node = row ? row.closest('.tree-node') : null;
|
||||||
|
if (!node || !node.parentElement) return false;
|
||||||
|
node.parentElement.removeChild(node);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyAssetsChangedToFileTree(detail) {
|
||||||
|
detail = detail || {};
|
||||||
|
var docId = String(detail.docId || detail.documentId || currentDocumentId() || '').trim();
|
||||||
|
var changed = false;
|
||||||
|
var explicitAsset = detail.asset && typeof detail.asset === 'object' ? detail.asset : null;
|
||||||
|
if (explicitAsset) {
|
||||||
|
appendUploadedAssetRow(explicitAsset, docId);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
var mindmapAssetIds = Array.isArray(detail.mindmapAssetIds) ? detail.mindmapAssetIds : [];
|
||||||
|
if (detail.mindmapDeleted === true) {
|
||||||
|
mindmapAssetIds.forEach(function(assetId) {
|
||||||
|
if (removeFileTreeAssetRow(assetId)) changed = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
document.documentElement.setAttribute('data-mnote-assets-local-applied', 'true');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mindmapAssetFromTarget(documentId, mindmapId) {
|
||||||
|
var docId = String(documentId || currentDocumentId() || '').trim();
|
||||||
|
var assetId = String(mindmapId || '').trim();
|
||||||
|
if (!docId || !assetId) return null;
|
||||||
|
var fileName = 'mindmap-' + assetId + '.json';
|
||||||
|
return {
|
||||||
|
id: assetId,
|
||||||
|
document_id: docId,
|
||||||
|
asset_type: 'mindmap',
|
||||||
|
file_name: fileName,
|
||||||
|
file_url: '/documents/' + encodeURIComponent(docId) + '/' + encodeURIComponent(fileName)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseMindmapApiTarget(input) {
|
||||||
|
try {
|
||||||
|
var rawUrl = typeof input === 'string'
|
||||||
|
? input
|
||||||
|
: input && typeof input.url === 'string'
|
||||||
|
? input.url
|
||||||
|
: '';
|
||||||
|
if (!rawUrl) return null;
|
||||||
|
var url = new URL(rawUrl, window.location.origin);
|
||||||
|
var match = /^\/api\/mindmap\/([^/]+)\/([^/]+)$/.exec(url.pathname);
|
||||||
|
if (!match) return null;
|
||||||
|
return {
|
||||||
|
documentId: decodeURIComponent(match[1]),
|
||||||
|
mindmapId: decodeURIComponent(match[2])
|
||||||
|
};
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestMethod(input, init) {
|
||||||
|
return String(
|
||||||
|
init && init.method
|
||||||
|
? init.method
|
||||||
|
: input && typeof input.method === 'string'
|
||||||
|
? input.method
|
||||||
|
: 'GET'
|
||||||
|
).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestBodyHasMindmapCreateOnly(init) {
|
||||||
|
var body = init && typeof init.body === 'string' ? init.body : '';
|
||||||
|
if (!body) return false;
|
||||||
|
try {
|
||||||
|
var payload = JSON.parse(body);
|
||||||
|
return payload && payload.createOnly === true;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyMindmapApiMutationToFileTree(target) {
|
||||||
|
if (!target || !target.documentId || !target.mindmapId) return;
|
||||||
|
var asset = mindmapAssetFromTarget(target.documentId, target.mindmapId);
|
||||||
|
if (!asset) return;
|
||||||
|
appendUploadedAssetRow(asset, target.documentId);
|
||||||
|
document.documentElement.setAttribute('data-mnote-assets-local-applied', 'true');
|
||||||
|
document.documentElement.setAttribute('data-mnote-last-mindmap-asset-id', target.mindmapId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function installMindmapAssetFetchObserver() {
|
||||||
|
if (window.__mnoteMindmapAssetFetchObserverInstalled === true) return;
|
||||||
|
if (typeof window.fetch !== 'function') return;
|
||||||
|
window.__mnoteMindmapAssetFetchObserverInstalled = true;
|
||||||
|
var originalFetch = window.fetch.bind(window);
|
||||||
|
window.fetch = function(input, init) {
|
||||||
|
var target = parseMindmapApiTarget(input);
|
||||||
|
var method = requestMethod(input, init);
|
||||||
|
var createOnly = requestBodyHasMindmapCreateOnly(init || {});
|
||||||
|
return originalFetch(input, init).then(function(response) {
|
||||||
|
if (target && method === 'POST' && response && response.ok) {
|
||||||
|
if (createOnly || target.documentId === currentDocumentId()) {
|
||||||
|
applyMindmapApiMutationToFileTree(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function insertUploadedAssetIntoEditor(asset) {
|
async function insertUploadedAssetIntoEditor(asset) {
|
||||||
var editorRoot = document.querySelector('.editor-surface .ProseMirror');
|
var editorRoot = document.querySelector('.editor-surface .ProseMirror');
|
||||||
var editor = editorRoot && editorRoot.editor;
|
var editor = editorRoot && editorRoot.editor;
|
||||||
@@ -2090,6 +2204,11 @@ const SIDEBAR_TREE_JS: &str = r##"
|
|||||||
return payload.asset;
|
return payload.asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.addEventListener('wolai:assets-changed', function(event) {
|
||||||
|
applyAssetsChangedToFileTree(event.detail || {});
|
||||||
|
});
|
||||||
|
installMindmapAssetFetchObserver();
|
||||||
|
|
||||||
async function uploadFilesWithResolvedTarget(files, detail, options) {
|
async function uploadFilesWithResolvedTarget(files, detail, options) {
|
||||||
var list = Array.from(files || []).filter(Boolean);
|
var list = Array.from(files || []).filter(Boolean);
|
||||||
if (!list.length) return [];
|
if (!list.length) return [];
|
||||||
@@ -4614,6 +4733,11 @@ mod tests {
|
|||||||
assert!(SIDEBAR_TREE_JS.contains("applyCreatedDocumentLocally"));
|
assert!(SIDEBAR_TREE_JS.contains("applyCreatedDocumentLocally"));
|
||||||
assert!(SIDEBAR_TREE_JS.contains("data-mnote-tree-local-command-applied', 'create"));
|
assert!(SIDEBAR_TREE_JS.contains("data-mnote-tree-local-command-applied', 'create"));
|
||||||
assert!(SIDEBAR_TREE_JS.contains("data-mnote-tree-local-command-applied', 'remove"));
|
assert!(SIDEBAR_TREE_JS.contains("data-mnote-tree-local-command-applied', 'remove"));
|
||||||
|
assert!(SIDEBAR_TREE_JS.contains("wolai:assets-changed"));
|
||||||
|
assert!(SIDEBAR_TREE_JS.contains("applyAssetsChangedToFileTree"));
|
||||||
|
assert!(SIDEBAR_TREE_JS.contains("installMindmapAssetFetchObserver"));
|
||||||
|
assert!(SIDEBAR_TREE_JS.contains("parseMindmapApiTarget"));
|
||||||
|
assert!(SIDEBAR_TREE_JS.contains("data-mnote-assets-local-applied"));
|
||||||
assert!(SIDEBAR_TREE_JS.contains("application/x-mnote-page-tree-node"));
|
assert!(SIDEBAR_TREE_JS.contains("application/x-mnote-page-tree-node"));
|
||||||
assert!(SIDEBAR_TREE_JS.contains("dragstart"));
|
assert!(SIDEBAR_TREE_JS.contains("dragstart"));
|
||||||
assert!(SIDEBAR_TREE_JS.contains("drop"));
|
assert!(SIDEBAR_TREE_JS.contains("drop"));
|
||||||
|
|||||||
@@ -41,6 +41,44 @@ async function readCreatedState(page, documentId) {
|
|||||||
}, documentId);
|
}, documentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function insertMindmapThroughSlash(page) {
|
||||||
|
const editor = page
|
||||||
|
.locator('[data-testid="mnote-leptos-tiptap-island-editor-root"] .editor-surface .ProseMirror[contenteditable="true"]')
|
||||||
|
.first();
|
||||||
|
await editor.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
||||||
|
await editor.click({ timeout: UI_TIMEOUT_MS });
|
||||||
|
await page.keyboard.type("/");
|
||||||
|
const item = page.getByTestId("slash-item-mindmap").first();
|
||||||
|
await item.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
||||||
|
await item.click({ timeout: UI_TIMEOUT_MS });
|
||||||
|
const root = page.getByTestId("mnote-mindmap-editor-root").first();
|
||||||
|
await root.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
||||||
|
const mindmapId = await root.evaluate((node) => node instanceof HTMLElement ? node.dataset.mnoteMindmapId || "" : "");
|
||||||
|
assert(mindmapId, "新建思维导图后缺少 mindmapId");
|
||||||
|
return mindmapId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readMindmapAssetState(page, documentId, mindmapId) {
|
||||||
|
return page.evaluate(
|
||||||
|
({ documentId: docId, mindmapId: assetId }) => {
|
||||||
|
const rows = Array.from(document.querySelectorAll("#sidebar-file-tree-root .tree-row[data-shell-mode=\"filetree\"]"))
|
||||||
|
.filter((row) => row.getAttribute("data-asset-id") === assetId)
|
||||||
|
.map((row) => ({
|
||||||
|
rowId: row.getAttribute("data-row-id"),
|
||||||
|
docId: row.getAttribute("data-doc-id") || row.getAttribute("data-document-id"),
|
||||||
|
title: row.textContent?.trim() || "",
|
||||||
|
kind: row.querySelector(".tree-kind-badge")?.getAttribute("data-kind") || "",
|
||||||
|
}));
|
||||||
|
return {
|
||||||
|
rows,
|
||||||
|
localApplied: document.documentElement.getAttribute("data-mnote-assets-local-applied") || "",
|
||||||
|
expectedDocId: docId,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ documentId, mindmapId },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
await fs.mkdir(OUT_DIR, { recursive: true });
|
await fs.mkdir(OUT_DIR, { recursive: true });
|
||||||
const browser = await chromium.launch({ headless: process.env.HEADFUL !== "1" });
|
const browser = await chromium.launch({ headless: process.env.HEADFUL !== "1" });
|
||||||
@@ -49,7 +87,7 @@ async function main() {
|
|||||||
const requests = [];
|
const requests = [];
|
||||||
page.on("response", (response) => {
|
page.on("response", (response) => {
|
||||||
const url = response.url();
|
const url = response.url();
|
||||||
if (url.includes("/api/tree/commands") || url.includes("/api/sidebar") || url.includes("/api/tree/events")) {
|
if (url.includes("/api/tree/commands") || url.includes("/api/sidebar") || url.includes("/api/tree/events") || url.includes("/api/mindmap/")) {
|
||||||
requests.push({ url, status: response.status() });
|
requests.push({ url, status: response.status() });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -78,6 +116,20 @@ async function main() {
|
|||||||
assert.equal(afterCreate.pageRows, 1, "Page Tree 应立即出现新页面");
|
assert.equal(afterCreate.pageRows, 1, "Page Tree 应立即出现新页面");
|
||||||
assert.deepEqual(afterCreate.fileRows.sort(), [`doc:${createdDocumentId}`, `index:${createdDocumentId}`].sort(), "File Tree 应立即出现新页面 doc/index 行");
|
assert.deepEqual(afterCreate.fileRows.sort(), [`doc:${createdDocumentId}`, `index:${createdDocumentId}`].sort(), "File Tree 应立即出现新页面 doc/index 行");
|
||||||
|
|
||||||
|
const mindmapId = await insertMindmapThroughSlash(page);
|
||||||
|
await page.waitForFunction(
|
||||||
|
({ assetId }) => {
|
||||||
|
return Boolean(document.querySelector(`#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-asset-id="${CSS.escape(assetId)}"]`));
|
||||||
|
},
|
||||||
|
{ assetId: mindmapId },
|
||||||
|
{ timeout: UI_TIMEOUT_MS },
|
||||||
|
);
|
||||||
|
const afterMindmapCreate = await readMindmapAssetState(page, createdDocumentId, mindmapId);
|
||||||
|
assert.equal(afterMindmapCreate.localApplied, "true", "新建思维导图应由 assets-changed 本地 apply 到 File Tree");
|
||||||
|
assert.equal(afterMindmapCreate.rows.length, 1, "File Tree 应立即出现新建思维导图 asset 行");
|
||||||
|
assert.equal(afterMindmapCreate.rows[0].rowId, `asset:${mindmapId}`, "File Tree mindmap rowId 应匹配 asset id");
|
||||||
|
assert.equal(afterMindmapCreate.rows[0].docId, createdDocumentId, "File Tree mindmap asset 应挂在当前页面下");
|
||||||
|
|
||||||
const rowSelector = `#sidebar-tree-root .tree-row[data-shell-mode="page"][data-node-id="${cssString(createdDocumentId)}"]`;
|
const rowSelector = `#sidebar-tree-root .tree-row[data-shell-mode="page"][data-node-id="${cssString(createdDocumentId)}"]`;
|
||||||
await page.locator(rowSelector).scrollIntoViewIfNeeded({ timeout: UI_TIMEOUT_MS });
|
await page.locator(rowSelector).scrollIntoViewIfNeeded({ timeout: UI_TIMEOUT_MS });
|
||||||
page.once("dialog", async (dialog) => {
|
page.once("dialog", async (dialog) => {
|
||||||
@@ -105,7 +157,9 @@ async function main() {
|
|||||||
baseUrl: BASE_URL,
|
baseUrl: BASE_URL,
|
||||||
beforeCount,
|
beforeCount,
|
||||||
createdDocumentId,
|
createdDocumentId,
|
||||||
|
mindmapId,
|
||||||
afterCreate,
|
afterCreate,
|
||||||
|
afterMindmapCreate,
|
||||||
afterDelete,
|
afterDelete,
|
||||||
requests,
|
requests,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user