feat: 0.10.0 快照内容级对照、忽略规则高级 glob 与扫描进度反馈 - 快照v2可选携带差异条目双侧内容(folder:read-batch批量读取IPC受控并发、单文件2MB与总量32MB双限超限整体降级为纯状态快照保存永不失败、对照模式双击进入快照基线vs当前文件的内容对比完整复用单文件能力、v1旧快照兼容加载、contents解析防御绝不部分加载、保存期间读取遮罩)、忽略规则匹配器重写支持多段路径/前导/锚定/整段**跨段/段内*通配/!否定规则(最后匹配优先与gitignore一致、父目录剪枝后子项无法反否定、无效规则UI实时提示不再静默丢弃、纯逻辑迁src/shared两进程共用源码)、扫描进度推送(首个push型IPC、scanId复用扫描会话序号旧扫描迟到进度天然丢弃、遮罩显示已比对N/M)、新增33单测与4条E2E(596单测+28E2E全过覆盖率98.16/93.28/93.69/98.16),文档四件套同步

This commit is contained in:
2026-08-19 13:24:35 +08:00
parent c1e63c3328
commit c209ae291a
25 changed files with 1456 additions and 316 deletions
+136 -2
View File
@@ -447,6 +447,86 @@ test.describe('文件夹对比(主进程真实扫描链路)', () => {
}
})
test('忽略规则高级 glob:** 跨段剪枝、否定规则救回与无效规则提示', async () => {
const root = await makeTmpDir('difflens-e2e-glob-')
try {
const leftDir = join(root, 'left')
const rightDir = join(root, 'right')
const { mkdir, writeFile: wf } = await import('fs/promises')
// build 下深层 temp 目录(** 跨段目标)+ 保留文件 + log 类差异文件
for (const d of [leftDir, rightDir]) {
await mkdir(join(d, 'build', 'a', 'temp'), { recursive: true })
await mkdir(join(d, 'build', 'b', 'deep', 'temp'), { recursive: true })
await wf(join(d, 'build', 'a', 'temp', 't1.txt'), 'L')
await wf(join(d, 'build', 'b', 'deep', 'temp', 't2.txt'), 'L')
await wf(join(d, 'build', 'keep.txt'), 'L')
await wf(join(d, 'app.log'), 'L')
await wf(join(d, 'keep.log'), 'L')
}
await wf(join(rightDir, 'build', 'a', 'temp', 't1.txt'), 'R')
await wf(join(rightDir, 'build', 'b', 'deep', 'temp', 't2.txt'), 'R')
await wf(join(rightDir, 'build', 'keep.txt'), 'R')
await wf(join(rightDir, 'app.log'), 'R')
await wf(join(rightDir, 'keep.log'), 'R')
const { app, window } = await launchApp()
await stubOpenDialogSeq(app, [[leftDir], [rightDir]])
await window.getByRole('button', { name: '对比文件夹' }).click()
await expect(window.getByText('不同 5')).toBeVisible()
await window.getByRole('button', { name: '忽略规则' }).click()
// 先输入含无效规则的草稿:提示出现(不再静默丢弃)
await window.getByPlaceholder(/每行一条/).fill('a**b')
await expect(window.getByText(/将被忽略的无效规则:a\*\*b/)).toBeVisible()
// 替换为正式规则:build 下任意深度 temp 剪枝 + *.log 忽略 + keep.log 否定救回
await window
.getByPlaceholder(/每行一条/)
.fill('build/**/temp\n*.log\n!keep.log')
await window.getByText('应用并重扫').click()
// t1/t2 被剪(** 跨段)、app.log 被忽略、keep.log 被否定救回 → 仅剩 keep.txt 与 keep.log 差异
await expect(window.getByText('不同 2')).toBeVisible()
await expect(window.getByText('t1.txt')).toHaveCount(0)
await expect(window.getByText('keep.log')).toBeVisible()
await app.close()
} finally {
await removeTmpDir(root)
}
})
test('扫描进度反馈:大目录比对期间遮罩显示已比对进度', async () => {
const root = await makeTmpDir('difflens-e2e-progress-')
try {
const leftDir = join(root, 'left')
const rightDir = join(root, 'right')
const { mkdir, writeFile: wf } = await import('fs/promises')
await mkdir(leftDir, { recursive: true })
await mkdir(rightDir, { recursive: true })
// 2000 个大小一致条目需读内容比对(125 个并发批次),进度事件可见窗口足够
for (let i = 0; i < 2000; i++) {
const name = `f${String(i).padStart(4, '0')}.txt`
await wf(join(leftDir, name), `L${i}`)
await wf(join(rightDir, name), `R${i}`)
}
const { app, window } = await launchApp()
await stubOpenDialogSeq(app, [[leftDir], [rightDir]])
await window.getByRole('button', { name: '对比文件夹' }).click()
// 瞬态进度文案:轮询抓取扫描期间的遮罩文本(完成后遮罩消失)
await expect
.poll(
async () =>
/已比对 \d+ \/ 2000/.test((await window.locator('.computing-overlay').textContent()) ?? '')
)
.toBe(true)
// 扫描完成:全量判定与遮罩消失
await expect(window.getByText('不同 2000')).toBeVisible()
await expect(window.locator('.computing-overlay')).toHaveCount(0)
await app.close()
} finally {
await removeTmpDir(root)
}
})
test('增量缓存跨会话持久化:落盘文件存在且重启后重扫正确', async () => {
const root = await makeTmpDir('difflens-e2e-cachestore-')
try {
@@ -515,14 +595,16 @@ test.describe('文件夹对比(主进程真实扫描链路)', () => {
await expect(window.getByText('相同 1')).toBeVisible()
await expect(window.getByText('不同 1')).toBeVisible()
// 保存快照(真实写盘)
// 保存快照(真实写盘):v2 结构含差异条目双侧内容(0.10.0 内容级对照)
const snapPath = join(root, 'baseline.json')
await stubSaveDialog(app, snapPath)
await window.getByRole('button', { name: '保存快照' }).click()
await expect(window.getByText(/快照已保存/)).toBeVisible()
const snapRaw = await readFile(snapPath, 'utf-8')
expect(snapRaw).toContain('"version": 1')
expect(snapRaw).toContain('"version": 2')
expect(snapRaw).toContain('diff.txt')
expect(snapRaw).toContain('left v1')
expect(snapRaw).toContain('right v1')
// 修复 diff.txt(两侧一致)+ 新增 new.txt → 重扫
await wf(join(rightDir, 'diff.txt'), 'left v1')
@@ -559,6 +641,58 @@ test.describe('文件夹对比(主进程真实扫描链路)', () => {
await removeTmpDir(root)
}
})
test('快照内容级对照:保存含内容快照 → 修改文件 → 对照双击查看基线 vs 当前', async () => {
const root = await makeTmpDir('difflens-e2e-snapcontent-')
try {
const leftDir = join(root, 'left')
const rightDir = join(root, 'right')
const { mkdir, writeFile: wf } = await import('fs/promises')
await mkdir(leftDir, { recursive: true })
await mkdir(rightDir, { recursive: true })
await wf(join(leftDir, 'same.txt'), 'identical')
await wf(join(rightDir, 'same.txt'), 'identical')
await wf(join(leftDir, 'diff.txt'), 'snapshot baseline value A')
await wf(join(rightDir, 'diff.txt'), 'snapshot baseline value B')
const { app, window } = await launchApp()
await stubOpenDialogSeq(app, [[leftDir], [rightDir]])
await window.getByRole('button', { name: '对比文件夹' }).click()
await expect(window.getByText('相同 1')).toBeVisible()
await expect(window.getByText('不同 1')).toBeVisible()
// 保存快照(v2):差异条目双侧内容写入 JSON
const snapPath = join(root, 'baseline.json')
await stubSaveDialog(app, snapPath)
await window.getByRole('button', { name: '保存快照' }).click()
await expect(window.getByText(/快照已保存/)).toBeVisible()
const snapRaw = await readFile(snapPath, 'utf-8')
expect(snapRaw).toContain('"version": 2')
expect(snapRaw).toContain('snapshot baseline value A')
expect(snapRaw).toContain('snapshot baseline value B')
// 修改左侧 diff.txt → 重扫(相对快照基线出现内容变化)
await wf(join(leftDir, 'diff.txt'), 'current changed value A')
await window.getByRole('button', { name: '重新扫描' }).click()
await expect(window.getByText('不同 1')).toBeVisible()
// 加载快照对照 → 双击 diff.txt 进入内容对照(左=快照基线,右=当前左侧文件)
await stubOpenDialogSeq(app, [[snapPath]])
await window.getByRole('button', { name: '加载快照' }).click()
await expect(window.getByText(/已加载快照/)).toBeVisible()
await window.getByText('diff.txt').dblclick()
await expect(
window.locator('.pane-file').filter({ hasText: '快照 · diff.txt' })
).toBeVisible()
// 左面板渲染快照基线旧词、右面板渲染当前新词(词级高亮拆分为独立段)
await expect(window.getByText('snapshot', { exact: true })).toBeVisible()
await expect(window.getByText('current', { exact: true })).toBeVisible()
await expect(window.getByText('有差异')).toBeVisible()
await app.close()
} finally {
await removeTmpDir(root)
}
})
})
test.describe('拖拽导入(真实 Chromium DataTransfer 链路)', () => {