Files
new-markdown/tests/tabs.test.ts
Muse 9bd3f3de97 fix(acceptance): 首轮 Windows 验收 7 项缺陷回修(docs/14)
P0 快捷键让行判定补 recoveryDismissed,点过「稍后再说」不再全失效;
P1 冲突重载加 force 绕过防竞态守卫;路径分隔符 joinPath 原生化 +
samePath 归一化比较;.source-cm 补文档字色;
P2 turndown 启用 GFM 插件(HTML 导入不再丢表格);任务复选框去掉
click preventDefault(浏览器激活撤销覆盖视觉同步);
P3 新增 syncEditorContent:干净标签的挂载/卸载序列化回写同步基线,
切标签不再误标未保存。vitest 74 通过 + 2 跳过。
2026-11-14 11:08:46 +08:00

81 lines
3.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect } from 'vitest'
import { tabsReducer, createTab, isDirty, type TabsState } from '../src/stores/tabs'
function stateWith(contents: string[]): TabsState {
const tabs = contents.map((c, i) => createTab({ title: `文档${i + 1}`, content: c }))
return { tabs, activeId: tabs[0]?.id ?? null }
}
describe('标签页会话模型', () => {
it('同路径文档重复打开只激活已有标签', () => {
const a = createTab({ title: 'a.md', path: '/x/a.md', content: 'A' })
let s: TabsState = { tabs: [a], activeId: a.id }
s = tabsReducer(s, { type: 'open', tab: createTab({ title: 'a.md', path: '/x/a.md', content: 'A2' }) })
expect(s.tabs).toHaveLength(1)
expect(s.activeId).toBe(a.id)
})
it('dirty 由 content 与 savedContent 比较得出,保存后清除,改回原样也清除', () => {
const t = createTab({ content: '原文' })
expect(isDirty(t)).toBe(false)
let s: TabsState = { tabs: [t], activeId: t.id }
s = tabsReducer(s, { type: 'updateContent', id: t.id, content: '原文改' })
expect(isDirty(s.tabs[0])).toBe(true)
s = tabsReducer(s, { type: 'updateContent', id: t.id, content: '原文' })
expect(isDirty(s.tabs[0])).toBe(false)
s = tabsReducer(s, { type: 'updateContent', id: t.id, content: '新版' })
s = tabsReducer(s, { type: 'saved', id: t.id, savedContent: '新版' })
expect(isDirty(s.tabs[0])).toBe(false)
expect(s.tabs[0].savedContent).toBe('新版')
})
it('保存竞态:写盘期间的新输入不被 saved 误标(审计回归)', () => {
const t = createTab({ content: '快照' })
let s: TabsState = { tabs: [t], activeId: t.id }
// 写盘用的是「快照」;await 期间用户又输入了新内容
s = tabsReducer(s, { type: 'updateContent', id: t.id, content: '快照+新输入' })
s = tabsReducer(s, { type: 'saved', id: t.id, savedContent: '快照' })
expect(s.tabs[0].savedContent).toBe('快照')
expect(isDirty(s.tabs[0])).toBe(true) // 新输入仍是脏的,等待下一次保存
})
it('关闭活动标签后激活相邻标签;关闭其他/右侧范围正确', () => {
let s = stateWith(['a', 'b', 'c'])
const [t1, t2, t3] = s.tabs
s = tabsReducer(s, { type: 'activate', id: t2.id })
s = tabsReducer(s, { type: 'close', id: t2.id })
expect(s.tabs.map((t) => t.id)).toEqual([t1.id, t3.id])
expect(s.activeId).toBe(t3.id)
s = tabsReducer(s, { type: 'open', tab: createTab({ title: 'd', content: 'd' }) })
const t4 = s.tabs[2]
s = tabsReducer(s, { type: 'closeRight', id: t1.id })
expect(s.tabs.map((t) => t.id)).toEqual([t1.id])
void t4
})
it('拖拽排序 move 到目标下标', () => {
let s = stateWith(['a', 'b', 'c'])
const [t1, , t3] = s.tabs
s = tabsReducer(s, { type: 'move', id: t1.id, toIndex: 2 })
expect(s.tabs[2].id).toBe(t1.id)
expect(s.tabs[0].id).toBe(t3.id === s.tabs[0].id ? t3.id : s.tabs[0].id)
})
})
describe('syncEditorContent(验收 P3 回归)', () => {
it('干净标签的规范化回写同步基线、不标脏;脏标签只更新正文', () => {
const t = createTab({ content: '| A |\n|---|\n| 1 |' })
let s: TabsState = { tabs: [t], activeId: t.id }
// 挂载序列化把对齐冒号规范化了:干净标签基线跟随,仍不脏
s = tabsReducer(s, { type: 'syncEditorContent', id: t.id, content: '| A |\n|:---|\n| 1 |' })
expect(isDirty(s.tabs[0])).toBe(false)
expect(s.tabs[0].content).toContain(':---')
// 脏标签:只更新正文,基线不动
s = tabsReducer(s, { type: 'updateContent', id: t.id, content: '改过' })
expect(isDirty(s.tabs[0])).toBe(true)
s = tabsReducer(s, { type: 'syncEditorContent', id: t.id, content: '改过又回写' })
expect(s.tabs[0].savedContent).toContain(':---')
expect(isDirty(s.tabs[0])).toBe(true)
})
})