refactor: extract filetree projection helpers
This commit is contained in:
@@ -147,3 +147,10 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据
|
||||
- 2026-05-25:Batch L Worker B `reasonix-2026-05-24T19-30-30-509Z-18f62949` 只读审计 `refreshLocalFolderSidebarSnapshot`;Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`,并核对 worktree diff 为空。
|
||||
- 审计结论:`refreshLocalFolderSidebarSnapshot` 仍是 shell 编排,包含 workspace/root/document 状态、两条 projection fetch、render 后副作用和 watcher 标记,不应整体迁入 `filetree-runtime.js`。
|
||||
- 下一刀建议:优先迁投影数据读取器层(如 `readProjection`、`readDatasetProjection`、`projectionItems`、row/id/title/file accessor、`groupRowsByParent`),或更保守地先迁未使用的 `replaceSidebarTreeFromDocument`;Codex 本轮不直接实现 3-20 代码,只记录候选。
|
||||
- 2026-05-25:Batch M 按用户纠正尝试并行派发 3 个 Reasonix 实现 worker,均使用独立 worktree;run `reasonix-2026-05-24T19-40-19-862Z-b3029108` / `reasonix-2026-05-24T19-40-19-873Z-4347ed16` 仅有 `prompt.md`、`memory-recall.json`、`reasonix-transcript.jsonl`,无 `final.md`、`result.json`、`process-handoff.md/json`,不采纳。
|
||||
- 失败原因:Reasonix ACP 并发串线,Worker C transcript 将 B/C/D 三个任务混为一个选择题并要求人工选择;不满足本文件 handoff 合同。
|
||||
- 主控处置:不从 Batch M 合入任何 diff;改用 Batch N 的独立 worktree 子 agent worker 继续推进,仍由 Codex 复核 diff 和验证。
|
||||
- 2026-05-25:Batch N 已启动 2 个 3-20 独立 worktree worker:Worker C `/mnt/Data1T/mnote-worktrees/0525-n-worker-c-3-20-projection-helpers` 负责 filetree projection data helper 外置;Worker D `/mnt/Data1T/mnote-worktrees/0525-n-worker-d-3-20-task435-smoke-assertion` 负责 `task435` local-folder watch projection 断言。Codex 已复核两个 worktree diff 并选择性移植。
|
||||
- Worker C:`filetree-runtime.js` 新增并导出 `readProjection`、`readSidebarDataset`、`readDatasetProjection`、`projectionItems`、`hasProjectionItems`、`nodeIdOf`、`rowIdOf`、`parentIdOf`、`titleOf`、`fileWorkspaceRelativePath`、`groupRowsByParent`;`layout.rs` 对应 inline fallback 优先委托 `fileTreeRuntimeFunction(...)`,未迁 `refreshLocalFolderSidebarSnapshot` / `startLocalFolderSidebarWatch` / fetch / post-render shell 副作用 / HTML renderer。
|
||||
- Worker D:`task435-local-folder-watch-no-reload-smoke.js` 新增 `waitForLocalFolderWatchProjectionApplied`,每个外部文件变化步骤前清理旧标记,步骤内显式等待 `data-mnote-local-folder-watch-applied="projection"`,未放宽 no-reload 断言;Codex 同步把初始 `Stable Page` 等待修正为当前 file tree 标题 `stable`。
|
||||
- 已验证:`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`node --check scripts/task435-local-folder-watch-no-reload-smoke.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_runtime_contains_row_accessor_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree_runtime_helpers_are_externalized_with_inline_fallback -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_tree_runtime_polls_local_folder_without_browser_reload -- --test-threads=1`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task435-local-folder-watch-no-reload-smoke.js`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
|
||||
|
||||
@@ -137,6 +137,83 @@ function defaultObjectIdentityAttr(identity) {
|
||||
}
|
||||
}
|
||||
|
||||
function readProjection(value) {
|
||||
if (!value || typeof value !== 'object') return null;
|
||||
if (value.result && typeof value.result === 'object') return value.result;
|
||||
if (value.snapshot && value.snapshot.tree) return value.snapshot.tree;
|
||||
if (value.data && value.data.tree) return value.data.tree;
|
||||
if (value.tree && typeof value.tree === 'object') return value.tree;
|
||||
return value;
|
||||
}
|
||||
|
||||
function readSidebarDataset(value) {
|
||||
if (!value || typeof value !== 'object') return null;
|
||||
if (value.snapshot && value.snapshot.dataset && typeof value.snapshot.dataset === 'object') return value.snapshot.dataset;
|
||||
if (value.data && value.data.dataset && typeof value.data.dataset === 'object') return value.data.dataset;
|
||||
if (value.dataset && typeof value.dataset === 'object') return value.dataset;
|
||||
if (value.sidebar && typeof value.sidebar === 'object') return value.sidebar;
|
||||
return null;
|
||||
}
|
||||
|
||||
function readDatasetProjection(value, snakeCaseKey, camelCaseKey) {
|
||||
var dataset = readSidebarDataset(value);
|
||||
if (!dataset || typeof dataset !== 'object') return null;
|
||||
if (dataset[snakeCaseKey] && typeof dataset[snakeCaseKey] === 'object') return dataset[snakeCaseKey];
|
||||
if (dataset[camelCaseKey] && typeof dataset[camelCaseKey] === 'object') return dataset[camelCaseKey];
|
||||
return null;
|
||||
}
|
||||
|
||||
function projectionItems(projection) {
|
||||
var resolved = readProjection(projection);
|
||||
return resolved && Array.isArray(resolved.items) ? resolved.items : [];
|
||||
}
|
||||
|
||||
function hasProjectionItems(projection) {
|
||||
var resolved = readProjection(projection);
|
||||
return Boolean(resolved && Array.isArray(resolved.items));
|
||||
}
|
||||
|
||||
function nodeIdOf(item) {
|
||||
return String(item && (item.nodeId || item.id || item.documentId) || '').trim();
|
||||
}
|
||||
|
||||
function rowIdOf(item) {
|
||||
return String(item && (item.rowId || item.nodeId || item.id) || '').trim();
|
||||
}
|
||||
|
||||
function parentIdOf(item) {
|
||||
return String(item && (item.parentNodeId || item.parentId || '') || '').trim();
|
||||
}
|
||||
|
||||
function titleOf(item) {
|
||||
return String(item && item.title || '无标题').trim() || '无标题';
|
||||
}
|
||||
|
||||
function fileWorkspaceRelativePath(item) {
|
||||
var meta = item && item.resourceMeta && typeof item.resourceMeta === 'object' ? item.resourceMeta : {};
|
||||
var workspacePath = meta.workspacePath && typeof meta.workspacePath === 'object' ? meta.workspacePath : {};
|
||||
var fromWorkspacePath = String(workspacePath.relativePath || '').trim();
|
||||
if (fromWorkspacePath) return fromWorkspacePath;
|
||||
var extra = meta.extra && typeof meta.extra === 'object' ? meta.extra : {};
|
||||
var source = extra.source && typeof extra.source === 'object' ? extra.source : {};
|
||||
var fromSource = String(source.relativePath || '').trim();
|
||||
if (fromSource) return fromSource;
|
||||
return String(item && (item.relativePath || item.rootRelativePath) || '').trim();
|
||||
}
|
||||
|
||||
function groupRowsByParent(rows) {
|
||||
rows = Array.isArray(rows) ? rows : [];
|
||||
var ids = new Set(rows.map(nodeIdOf).filter(Boolean));
|
||||
var grouped = new Map();
|
||||
rows.forEach(function(item) {
|
||||
var parentId = parentIdOf(item);
|
||||
if (!ids.has(parentId)) parentId = '';
|
||||
if (!grouped.has(parentId)) grouped.set(parentId, []);
|
||||
grouped.get(parentId).push(item);
|
||||
});
|
||||
return grouped;
|
||||
}
|
||||
|
||||
function revealFileTreeRow(row) {
|
||||
if (!(row instanceof HTMLElement)) return;
|
||||
var node = row.closest('.tree-node');
|
||||
@@ -263,6 +340,17 @@ function fileTreeUploadTargetFallback(detail, deps) {
|
||||
}
|
||||
|
||||
window.__mnoteFileTreeRuntime = {
|
||||
readProjection: readProjection,
|
||||
readSidebarDataset: readSidebarDataset,
|
||||
readDatasetProjection: readDatasetProjection,
|
||||
projectionItems: projectionItems,
|
||||
hasProjectionItems: hasProjectionItems,
|
||||
nodeIdOf: nodeIdOf,
|
||||
rowIdOf: rowIdOf,
|
||||
parentIdOf: parentIdOf,
|
||||
titleOf: titleOf,
|
||||
fileWorkspaceRelativePath: fileWorkspaceRelativePath,
|
||||
groupRowsByParent: groupRowsByParent,
|
||||
fileTreeRowKind: fileTreeRowKind,
|
||||
fileTreeRowDocumentId: fileTreeRowDocumentId,
|
||||
fileTreeRowAssetId: fileTreeRowAssetId,
|
||||
|
||||
@@ -2142,6 +2142,8 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
}
|
||||
|
||||
function readProjection(value) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('readProjection');
|
||||
if (runtimeFn) return runtimeFn(value);
|
||||
if (!value || typeof value !== 'object') return null;
|
||||
if (value.result && typeof value.result === 'object') return value.result;
|
||||
if (value.snapshot && value.snapshot.tree) return value.snapshot.tree;
|
||||
@@ -2151,6 +2153,8 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
}
|
||||
|
||||
function readSidebarDataset(value) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('readSidebarDataset');
|
||||
if (runtimeFn) return runtimeFn(value);
|
||||
if (!value || typeof value !== 'object') return null;
|
||||
if (value.snapshot && value.snapshot.dataset && typeof value.snapshot.dataset === 'object') return value.snapshot.dataset;
|
||||
if (value.data && value.data.dataset && typeof value.data.dataset === 'object') return value.data.dataset;
|
||||
@@ -2160,6 +2164,8 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
}
|
||||
|
||||
function readDatasetProjection(value, snakeCaseKey, camelCaseKey) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('readDatasetProjection');
|
||||
if (runtimeFn) return runtimeFn(value, snakeCaseKey, camelCaseKey);
|
||||
var dataset = readSidebarDataset(value);
|
||||
if (!dataset || typeof dataset !== 'object') return null;
|
||||
if (dataset[snakeCaseKey] && typeof dataset[snakeCaseKey] === 'object') return dataset[snakeCaseKey];
|
||||
@@ -2172,32 +2178,46 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
}
|
||||
|
||||
function projectionItems(projection) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('projectionItems');
|
||||
if (runtimeFn) return runtimeFn(projection);
|
||||
var resolved = readProjection(projection);
|
||||
return resolved && Array.isArray(resolved.items) ? resolved.items : [];
|
||||
}
|
||||
|
||||
function hasProjectionItems(projection) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('hasProjectionItems');
|
||||
if (runtimeFn) return runtimeFn(projection);
|
||||
var resolved = readProjection(projection);
|
||||
return Boolean(resolved && Array.isArray(resolved.items));
|
||||
}
|
||||
|
||||
function nodeIdOf(item) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('nodeIdOf');
|
||||
if (runtimeFn) return runtimeFn(item);
|
||||
return String(item && (item.nodeId || item.id || item.documentId) || '').trim();
|
||||
}
|
||||
|
||||
function rowIdOf(item) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('rowIdOf');
|
||||
if (runtimeFn) return runtimeFn(item);
|
||||
return String(item && (item.rowId || item.nodeId || item.id) || '').trim();
|
||||
}
|
||||
|
||||
function parentIdOf(item) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('parentIdOf');
|
||||
if (runtimeFn) return runtimeFn(item);
|
||||
return String(item && (item.parentNodeId || item.parentId || '') || '').trim();
|
||||
}
|
||||
|
||||
function titleOf(item) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('titleOf');
|
||||
if (runtimeFn) return runtimeFn(item);
|
||||
return String(item && item.title || '无标题').trim() || '无标题';
|
||||
}
|
||||
|
||||
function fileWorkspaceRelativePath(item) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('fileWorkspaceRelativePath');
|
||||
if (runtimeFn) return runtimeFn(item);
|
||||
var meta = item && item.resourceMeta && typeof item.resourceMeta === 'object' ? item.resourceMeta : {};
|
||||
var workspacePath = meta.workspacePath && typeof meta.workspacePath === 'object' ? meta.workspacePath : {};
|
||||
var fromWorkspacePath = String(workspacePath.relativePath || '').trim();
|
||||
@@ -2210,6 +2230,8 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
}
|
||||
|
||||
function groupRowsByParent(rows) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('groupRowsByParent');
|
||||
if (runtimeFn) return runtimeFn(rows);
|
||||
var ids = new Set(rows.map(nodeIdOf).filter(Boolean));
|
||||
var grouped = new Map();
|
||||
rows.forEach(function(item) {
|
||||
@@ -11054,6 +11076,17 @@ mod tests {
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('revealFileTreeAssetRow')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('appendUploadedAssetRow')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('fileTreeUploadTargetFallback')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('readProjection')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('readSidebarDataset')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('readDatasetProjection')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('projectionItems')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('hasProjectionItems')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('nodeIdOf')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('rowIdOf')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('parentIdOf')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('titleOf')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('fileWorkspaceRelativePath')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('groupRowsByParent')"));
|
||||
let document_id_body = js_function_body(SIDEBAR_TREE_JS, "fileTreeRowDocumentId");
|
||||
assert!(
|
||||
document_id_body.contains("data-document-id")
|
||||
@@ -11077,7 +11110,19 @@ mod tests {
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function revealFileTreeAssetRow"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function appendUploadedAssetRow"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function fileTreeUploadTargetFallback"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function readProjection"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function readSidebarDataset"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function readDatasetProjection"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function projectionItems"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function hasProjectionItems"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function nodeIdOf"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function rowIdOf"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function parentIdOf"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function titleOf"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function fileWorkspaceRelativePath"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("function groupRowsByParent"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("appendUploadedAssetRow: appendUploadedAssetRow"));
|
||||
assert!(FILETREE_RUNTIME_JS.contains("groupRowsByParent: groupRowsByParent"));
|
||||
assert!(FILETREE_RUNTIME_JS
|
||||
.contains("uploadIntent: String(detail.uploadIntent || 'filetree.folder.drop')"));
|
||||
assert!(FILETREE_RUNTIME_JS
|
||||
|
||||
@@ -106,8 +106,25 @@ async function waitForPageTreeNodeGone(page, documentId) {
|
||||
);
|
||||
}
|
||||
|
||||
async function runStep(label, navigationEvents, action) {
|
||||
async function waitForLocalFolderWatchProjectionApplied(page) {
|
||||
await page.waitForFunction(
|
||||
() => document.documentElement.getAttribute("data-mnote-local-folder-watch-applied") === "projection",
|
||||
{ timeout: UI_TIMEOUT_MS },
|
||||
);
|
||||
const appliedValue = await page.evaluate(
|
||||
() => document.documentElement.getAttribute("data-mnote-local-folder-watch-applied"),
|
||||
);
|
||||
assert(
|
||||
appliedValue === "projection",
|
||||
`local_folder watch 应通过 projection 应用,实际 data-mnote-local-folder-watch-applied=${appliedValue}`,
|
||||
);
|
||||
}
|
||||
|
||||
async function runStep(page, label, navigationEvents, action) {
|
||||
const before = navigationEvents.length;
|
||||
await page.evaluate(() => {
|
||||
document.documentElement.removeAttribute("data-mnote-local-folder-watch-applied");
|
||||
});
|
||||
await action();
|
||||
const after = navigationEvents.length;
|
||||
assert(after === before, `${label} 不应触发浏览器导航或 reload,before=${before} after=${after}`);
|
||||
@@ -147,30 +164,33 @@ async function run() {
|
||||
await quickLogin(page);
|
||||
await page.goto(treeUrl(root, "page"), { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
await waitForVisibleText(page, "Local Root");
|
||||
await waitForVisibleText(page, "Stable Page");
|
||||
await waitForVisibleText(page, "stable");
|
||||
await page.waitForResponse((response) => response.url().includes("/api/tree/local-folder-watch") && response.ok(), {
|
||||
timeout: UI_TIMEOUT_MS,
|
||||
}).catch(() => {});
|
||||
await page.waitForTimeout(100);
|
||||
navigationEvents.length = 0;
|
||||
|
||||
steps.push(await runStep("Markdown 外部创建后 page tree 原地更新", navigationEvents, async () => {
|
||||
steps.push(await runStep(page, "Markdown 外部创建后 page tree 原地更新", navigationEvents, async () => {
|
||||
fs.writeFileSync(path.join(root, "docs", "watcher-create.md"), "# Watcher Create\n", "utf8");
|
||||
await waitForPageTreeNode(page, localMdDocumentId("docs/watcher-create.md"));
|
||||
await waitForLocalFolderWatchProjectionApplied(page);
|
||||
}));
|
||||
|
||||
steps.push(await runStep("Markdown 外部重命名后 page tree 原地更新", navigationEvents, async () => {
|
||||
steps.push(await runStep(page, "Markdown 外部重命名后 page tree 原地更新", navigationEvents, async () => {
|
||||
fs.renameSync(
|
||||
path.join(root, "docs", "watcher-create.md"),
|
||||
path.join(root, "docs", "watcher-renamed.md"),
|
||||
);
|
||||
await waitForPageTreeNode(page, localMdDocumentId("docs/watcher-renamed.md"));
|
||||
await waitForPageTreeNodeGone(page, localMdDocumentId("docs/watcher-create.md"));
|
||||
await waitForLocalFolderWatchProjectionApplied(page);
|
||||
}));
|
||||
|
||||
steps.push(await runStep("Markdown 外部删除后 page tree 原地更新", navigationEvents, async () => {
|
||||
steps.push(await runStep(page, "Markdown 外部删除后 page tree 原地更新", navigationEvents, async () => {
|
||||
fs.rmSync(path.join(root, "docs", "watcher-renamed.md"));
|
||||
await waitForPageTreeNodeGone(page, localMdDocumentId("docs/watcher-renamed.md"));
|
||||
await waitForLocalFolderWatchProjectionApplied(page);
|
||||
}));
|
||||
|
||||
await page.goto(treeUrl(root, "filetree"), { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
@@ -182,61 +202,70 @@ async function run() {
|
||||
await page.waitForTimeout(100);
|
||||
navigationEvents.length = 0;
|
||||
|
||||
steps.push(await runStep("Markdown 外部创建后 filetree 原地更新", navigationEvents, async () => {
|
||||
steps.push(await runStep(page, "Markdown 外部创建后 filetree 原地更新", navigationEvents, async () => {
|
||||
fs.writeFileSync(path.join(root, "docs", "watcher-filetree-md.md"), "# Watcher Filetree Markdown\n", "utf8");
|
||||
await waitForFileTreeRow(page, "local:markdown:docs/watcher-filetree-md.md");
|
||||
await waitForLocalFolderWatchProjectionApplied(page);
|
||||
}));
|
||||
|
||||
steps.push(await runStep("Markdown 外部重命名后 filetree 原地更新", navigationEvents, async () => {
|
||||
steps.push(await runStep(page, "Markdown 外部重命名后 filetree 原地更新", navigationEvents, async () => {
|
||||
fs.renameSync(
|
||||
path.join(root, "docs", "watcher-filetree-md.md"),
|
||||
path.join(root, "docs", "watcher-filetree-md-renamed.md"),
|
||||
);
|
||||
await waitForFileTreeRow(page, "local:markdown:docs/watcher-filetree-md-renamed.md");
|
||||
await waitForFileTreeRowGone(page, "local:markdown:docs/watcher-filetree-md.md");
|
||||
await waitForLocalFolderWatchProjectionApplied(page);
|
||||
}));
|
||||
|
||||
steps.push(await runStep("Markdown 外部删除后 filetree 原地更新", navigationEvents, async () => {
|
||||
steps.push(await runStep(page, "Markdown 外部删除后 filetree 原地更新", navigationEvents, async () => {
|
||||
fs.rmSync(path.join(root, "docs", "watcher-filetree-md-renamed.md"));
|
||||
await waitForFileTreeRowGone(page, "local:markdown:docs/watcher-filetree-md-renamed.md");
|
||||
await waitForLocalFolderWatchProjectionApplied(page);
|
||||
}));
|
||||
|
||||
steps.push(await runStep("非 md 资源外部创建后 filetree 原地更新", navigationEvents, async () => {
|
||||
steps.push(await runStep(page, "非 md 资源外部创建后 filetree 原地更新", navigationEvents, async () => {
|
||||
fs.writeFileSync(path.join(root, "docs", "watcher-asset.txt"), "watcher asset", "utf8");
|
||||
await waitForFileTreeRow(page, "local:asset:docs/watcher-asset.txt");
|
||||
await waitForLocalFolderWatchProjectionApplied(page);
|
||||
}));
|
||||
|
||||
steps.push(await runStep("非 md 资源外部重命名后 filetree 原地更新", navigationEvents, async () => {
|
||||
steps.push(await runStep(page, "非 md 资源外部重命名后 filetree 原地更新", navigationEvents, async () => {
|
||||
fs.renameSync(
|
||||
path.join(root, "docs", "watcher-asset.txt"),
|
||||
path.join(root, "docs", "watcher-asset-renamed.txt"),
|
||||
);
|
||||
await waitForFileTreeRow(page, "local:asset:docs/watcher-asset-renamed.txt");
|
||||
await waitForFileTreeRowGone(page, "local:asset:docs/watcher-asset.txt");
|
||||
await waitForLocalFolderWatchProjectionApplied(page);
|
||||
}));
|
||||
|
||||
steps.push(await runStep("非 md 资源外部删除后 filetree 原地更新", navigationEvents, async () => {
|
||||
steps.push(await runStep(page, "非 md 资源外部删除后 filetree 原地更新", navigationEvents, async () => {
|
||||
fs.rmSync(path.join(root, "docs", "watcher-asset-renamed.txt"));
|
||||
await waitForFileTreeRowGone(page, "local:asset:docs/watcher-asset-renamed.txt");
|
||||
await waitForLocalFolderWatchProjectionApplied(page);
|
||||
}));
|
||||
|
||||
steps.push(await runStep("第二类非 md 资源外部创建后 filetree 原地更新", navigationEvents, async () => {
|
||||
steps.push(await runStep(page, "第二类非 md 资源外部创建后 filetree 原地更新", navigationEvents, async () => {
|
||||
fs.writeFileSync(path.join(root, "docs", "watcher-image.png"), "png", "utf8");
|
||||
await waitForFileTreeRow(page, "local:asset:docs/watcher-image.png");
|
||||
await waitForLocalFolderWatchProjectionApplied(page);
|
||||
}));
|
||||
|
||||
steps.push(await runStep("第二类非 md 资源外部重命名后 filetree 原地更新", navigationEvents, async () => {
|
||||
steps.push(await runStep(page, "第二类非 md 资源外部重命名后 filetree 原地更新", navigationEvents, async () => {
|
||||
fs.renameSync(
|
||||
path.join(root, "docs", "watcher-image.png"),
|
||||
path.join(root, "docs", "watcher-image-renamed.png"),
|
||||
);
|
||||
await waitForFileTreeRow(page, "local:asset:docs/watcher-image-renamed.png");
|
||||
await waitForFileTreeRowGone(page, "local:asset:docs/watcher-image.png");
|
||||
await waitForLocalFolderWatchProjectionApplied(page);
|
||||
}));
|
||||
|
||||
steps.push(await runStep("第二类非 md 资源外部删除后 filetree 原地更新", navigationEvents, async () => {
|
||||
steps.push(await runStep(page, "第二类非 md 资源外部删除后 filetree 原地更新", navigationEvents, async () => {
|
||||
fs.rmSync(path.join(root, "docs", "watcher-image-renamed.png"));
|
||||
await waitForFileTreeRowGone(page, "local:asset:docs/watcher-image-renamed.png");
|
||||
await waitForLocalFolderWatchProjectionApplied(page);
|
||||
}));
|
||||
|
||||
const result = {
|
||||
|
||||
Reference in New Issue
Block a user