fix: 收口批次D并修复本地垃圾箱清空

This commit is contained in:
lix-2026
2026-05-21 15:45:11 +08:00
parent e709c0705c
commit dc18ec0a60
14 changed files with 525 additions and 72 deletions
@@ -159,11 +159,15 @@ async function main() {
const root = fs.mkdtempSync(path.join(os.tmpdir(), `mnote-${TASK}-`));
const rootUri = fileUrl(root);
// ── 测试数据:松散 Markdown 页面和非 md 资源 ──
// ── 测试数据:两个松散 Markdown 页面和非 md 资源 ──
const mdRelativePath = "docs/test-page.md";
const mdDocId = `local-md:${encodeLocalIdSegment(mdRelativePath)}`; // local-md:docs~2Ftest-page.md
const mdSourcePath = path.join(root, mdRelativePath);
const md2RelativePath = "docs/second-page.md";
const md2DocId = `local-md:${encodeLocalIdSegment(md2RelativePath)}`; // local-md:docs~2Fsecond-page.md
const md2SourcePath = path.join(root, md2RelativePath);
const assetRelativePath = "docs/resource.txt";
const assetId = `local-file:${assetRelativePath}`; // local-file:docs/resource.txt
const assetSourcePath = path.join(root, assetRelativePath);
@@ -173,6 +177,7 @@ async function main() {
writeWorkspaceManifest(root);
fs.writeFileSync(path.join(root, "README.md"), "# Local Root\n", "utf8");
fs.writeFileSync(mdSourcePath, "# Test Page\n\nPage content for empty-trash smoke.\n", "utf8");
fs.writeFileSync(md2SourcePath, "# Second Page\n\nSecond page content for multi-purge smoke.\n", "utf8");
fs.writeFileSync(assetSourcePath, "resource body for empty-trash smoke\n", "utf8");
const browser = await chromium.launch({
@@ -203,6 +208,8 @@ async function main() {
root,
mdRelativePath,
mdDocId,
md2RelativePath,
md2DocId,
assetRelativePath,
assetId,
steps: [],
@@ -227,6 +234,16 @@ async function main() {
|| getTrashFilePath(root, mdDocId);
assert.ok(mdTrashPath, `MD archive 应返回 trashPath, got: ${JSON.stringify(mdArchiveResult)}`);
// 归档第二个 Markdown 页面,覆盖清空页面垃圾箱多条目串行 purge
const md2ArchiveResult = await postTreeCommand(rootUri, "delete", md2DocId);
assert.equal(md2ArchiveResult.result?.execution?.resourceKind, "markdown",
`MD2 archive resourceKind should be markdown, got: ${JSON.stringify(md2ArchiveResult)}`);
assert.equal(fs.existsSync(md2SourcePath), false, "archive 后第二个 Markdown 源文件应移入垃圾箱");
const md2TrashPath = md2ArchiveResult.result?.execution?.trashPath
|| md2ArchiveResult.trashPath
|| getTrashFilePath(root, md2DocId);
assert.ok(md2TrashPath, `MD2 archive 应返回 trashPath, got: ${JSON.stringify(md2ArchiveResult)}`);
// 归档非 md 资源
const assetArchiveResult = await postTreeCommand(rootUri, "delete", assetId);
assert.equal(assetArchiveResult.result?.execution?.canonicalCommand, "tree.resource.archive",
@@ -242,12 +259,14 @@ async function main() {
label: "通过 tree command 归档 Markdown 页面和非 md 资源到垃圾箱",
ok: true,
mdTrashPath,
md2TrashPath,
assetTrashPath,
});
// 验证 trash-index.json 包含个 entry
// 验证 trash-index.json 包含个 entry
const entriesBefore = readTrashIndex(root);
assert.ok(entriesBefore[mdDocId], `trash-index 应包含 MD entry: ${mdDocId}`);
assert.ok(entriesBefore[md2DocId], `trash-index 应包含第二个 MD entry: ${md2DocId}`);
assert.ok(entriesBefore[assetId], `trash-index 应包含 asset entry: ${assetId}`);
// ════════════════════════════════════════════
@@ -263,6 +282,11 @@ async function main() {
const mdResourceKind = await mdTrashRow.getAttribute("data-resource-kind");
assert.equal(mdResourceKind, "markdown", `MD trash row data-resource-kind 应为 "markdown", 实际: ${mdResourceKind}`);
const md2TrashRow = page.locator(`article[data-trash-row="local"][data-trash-entry-id="${md2DocId}"]`);
await md2TrashRow.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
const md2ResourceKind = await md2TrashRow.getAttribute("data-resource-kind");
assert.equal(md2ResourceKind, "markdown", `第二个 MD trash row data-resource-kind 应为 "markdown", 实际: ${md2ResourceKind}`);
// Asset 行(资源垃圾箱)
const assetTrashRow = page.locator(`article[data-trash-row="local"][data-trash-entry-id="${assetId}"]`);
await assetTrashRow.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
@@ -305,11 +329,19 @@ async function main() {
mdDocId,
{ timeout: UI_TIMEOUT_MS },
);
await page.waitForFunction(
(id) => !document.querySelector(`[data-trash-entry-id="${id}"]`),
md2DocId,
{ timeout: UI_TIMEOUT_MS },
);
// 现在重新获取 DOM 引用验证
const mdRowAfterEmptyDocs = page.locator(`article[data-trash-row="local"][data-trash-entry-id="${mdDocId}"]`);
const mdRowCountAfterEmptyDocs = await mdRowAfterEmptyDocs.count();
assert.equal(mdRowCountAfterEmptyDocs, 0, "清空页面垃圾箱后 MD 行应消失");
const md2RowAfterEmptyDocs = page.locator(`article[data-trash-row="local"][data-trash-entry-id="${md2DocId}"]`);
const md2RowCountAfterEmptyDocs = await md2RowAfterEmptyDocs.count();
assert.equal(md2RowCountAfterEmptyDocs, 0, "清空页面垃圾箱后第二个 MD 行应消失");
// 文档行消失后,资源行应仍然存在
const assetRowAfterEmptyDocs = page.locator(`article[data-trash-row="local"][data-trash-entry-id="${assetId}"]`);
@@ -323,9 +355,13 @@ async function main() {
// 验证文件系统:trash 文件和 trash-index
const mdEntryAfterEmptyDocs = getTrashEntry(root, mdDocId);
assert.equal(mdEntryAfterEmptyDocs, null, `清空页面垃圾箱后 trash-index 不应包含 MD entry: ${mdDocId}`);
const md2EntryAfterEmptyDocs = getTrashEntry(root, md2DocId);
assert.equal(md2EntryAfterEmptyDocs, null, `清空页面垃圾箱后 trash-index 不应包含第二个 MD entry: ${md2DocId}`);
const resolvedMdTrashPath = path.resolve(root, mdTrashPath);
assert.equal(fs.existsSync(resolvedMdTrashPath), false, `清空页面垃圾箱后 trash 文件应删除: ${resolvedMdTrashPath}`);
const resolvedMd2TrashPath = path.resolve(root, md2TrashPath);
assert.equal(fs.existsSync(resolvedMd2TrashPath), false, `清空页面垃圾箱后第二个 trash 文件应删除: ${resolvedMd2TrashPath}`);
// 导航断言:清空操作不应触发页面导航
const navAfterEmptyDocs = navigationEvents.length;
@@ -338,7 +374,9 @@ async function main() {
navDelta: emptyDocsNavDelta,
dialogAccepted: true,
mdEntryClean: mdEntryAfterEmptyDocs === null,
md2EntryClean: md2EntryAfterEmptyDocs === null,
mdFileGone: !fs.existsSync(resolvedMdTrashPath),
md2FileGone: !fs.existsSync(resolvedMd2TrashPath),
};
evidence.steps.push({
step: 3,
@@ -415,6 +453,7 @@ async function main() {
// 验证原始源文件不应恢复
assert.equal(fs.existsSync(mdSourcePath), false, "清空垃圾箱后 MD 源文件不应恢复");
assert.equal(fs.existsSync(md2SourcePath), false, "清空垃圾箱后第二个 MD 源文件不应恢复");
assert.equal(fs.existsSync(assetSourcePath), false, "清空垃圾箱后资源源文件不应恢复");
evidence.steps.push({