Files
new-markdown/tests/git-sync.test.tsx

124 lines
4.9 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.
// Git 同步:设置解析、状态文案、定时租约互斥、侧栏同步条渲染。
import { describe, it, expect, vi } from 'vitest'
import { createRoot } from 'react-dom/client'
import { act } from 'react-dom/test-utils'
import {
parseGitSyncSettings, gitStatusLabel, gitSyncCommitMessage,
acquireGitSyncLease, releaseGitSyncLease,
} from '../src/services/gitSync'
import type { GitStatusInfo } from '../src/services/platform'
vi.mock('../src/services/platform', async (importOriginal) => {
const orig = await importOriginal<typeof import('../src/services/platform')>()
return { ...orig, listDir: async () => [] }
})
import Sidebar from '../src/components/Sidebar'
const base: GitStatusInfo = {
isRepo: true, branch: 'main', remoteUrl: 'https://example.com/n.git',
dirtyCount: 0, ahead: 0, behind: 0, hasUpstream: true,
}
describe('parseGitSyncSettings', () => {
it('空/坏数据回默认(手动)', () => {
expect(parseGitSyncSettings(null).intervalMin).toBe(0)
expect(parseGitSyncSettings('{bad').intervalMin).toBe(0)
expect(parseGitSyncSettings('{"intervalMin":-3}').intervalMin).toBe(0)
})
it('正常解析间隔', () => {
expect(parseGitSyncSettings('{"intervalMin":15}').intervalMin).toBe(15)
})
})
describe('gitStatusLabel', () => {
it('未启用 / 同步中 / 已同步', () => {
expect(gitStatusLabel({ ...base, isRepo: false }, false)).toBe('未启用 Git 同步')
expect(gitStatusLabel(base, true)).toBe('正在同步…')
expect(gitStatusLabel(base, false)).toBe('main · 已同步')
})
it('待提交/待推送/待拉取计数', () => {
expect(gitStatusLabel({ ...base, dirtyCount: 2, ahead: 1, behind: 3 }, false))
.toBe('main · 待提交 2 处 · 待推送 1 · 待拉取 3')
})
it('无远程时提示未设置远程', () => {
expect(gitStatusLabel({ ...base, remoteUrl: null, hasUpstream: false }, false))
.toBe('main · 未设置远程 · 已同步')
})
})
describe('gitSyncCommitMessage', () => {
it('带本地时间戳与手动/自动区分', () => {
const d = new Date(2026, 10, 14, 9, 5)
expect(gitSyncCommitMessage(true, d)).toBe('自动同步 2026-11-14 09:05(new-markdown)')
expect(gitSyncCommitMessage(false, d)).toContain('手动同步 2026-11-14 09:05')
})
})
describe('定时同步租约', () => {
it('别的窗口新鲜占用时取不到,过期/释放后可取', () => {
localStorage.clear()
const t0 = 1_000_000
expect(acquireGitSyncLease('C:/ws', 'win-a', t0)).toBe(true)
expect(acquireGitSyncLease('C:/ws', 'win-b', t0 + 5_000)).toBe(false)
expect(acquireGitSyncLease('C:/ws', 'win-a', t0 + 6_000)).toBe(true) // 自己续租
expect(acquireGitSyncLease('C:/ws', 'win-b', t0 + 40_000)).toBe(true) // 已过期
releaseGitSyncLease('C:/ws', 'win-a') // 不是持有者,无效
expect(acquireGitSyncLease('C:/ws', 'win-c', t0 + 41_000)).toBe(false)
releaseGitSyncLease('C:/ws', 'win-b')
expect(acquireGitSyncLease('C:/ws', 'win-c', t0 + 42_000)).toBe(true)
})
})
describe('侧栏 Git 同步条', () => {
async function renderBar(gitSync: Record<string, unknown>) {
const container = document.createElement('div')
document.body.appendChild(container)
const root = createRoot(container)
await act(async () => {
root.render(
<Sidebar
mode="files"
onModeChange={() => undefined}
workspaceRoot="C:/ws"
activePath={null}
onOpenFile={() => undefined}
onOpenFolder={() => undefined}
outline={[]}
onJumpHeading={() => undefined}
tauriMode
gitSync={gitSync as never}
/>,
)
})
await act(async () => { await new Promise((r) => setTimeout(r, 30)) })
return container
}
it('未启用时显示状态与设置按钮,无立即同步', async () => {
const c = await renderBar({
isRepo: false, label: '未启用 Git 同步', syncing: false,
lastMessage: null, lastIsError: false,
onSyncNow: () => undefined, onOpenSettings: () => undefined,
})
expect(c.textContent).toContain('Git 同步')
expect(c.textContent).toContain('未启用 Git 同步')
expect(c.querySelector('[aria-label="Git 同步设置"]')).not.toBeNull()
expect(c.querySelector('[aria-label="立即同步"]')).toBeNull()
})
it('仓库状态下显示立即同步且可点,结果行可见', async () => {
const onSyncNow = vi.fn()
const c = await renderBar({
isRepo: true, label: 'main · 待提交 2 处', syncing: false,
lastMessage: '已提交本地更改', lastIsError: false,
onSyncNow, onOpenSettings: () => undefined,
})
expect(c.textContent).toContain('待提交 2 处')
expect(c.textContent).toContain('已提交本地更改')
const btn = c.querySelector('[aria-label="立即同步"]') as HTMLButtonElement
await act(async () => { btn.click() })
expect(onSyncNow).toHaveBeenCalledTimes(1)
})
})