test: 补充组件交互用例,覆盖率提升至 80%+

This commit is contained in:
2026-08-17 17:58:50 +08:00
parent dedc61bc35
commit 9003e4b494
4 changed files with 207 additions and 2 deletions
+40 -2
View File
@@ -1,7 +1,27 @@
import { describe, it, expect } from 'vitest'
import { render, screen } from '@testing-library/react'
import { describe, it, expect, beforeEach } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import App from '../App'
function mockApi(overrides?: Partial<typeof window.api>): typeof window.api {
const base = {
openFile: async () => ({
path: '/tmp/a.txt',
name: 'a.txt',
text: 'line1\nline2',
encoding: 'UTF-8'
}),
decodeBuffer: async () => ({ text: 'line1\nline2', encoding: 'UTF-8' }),
saveReport: async () => ({ ok: false, path: null }),
showInFolder: async () => true,
setClipboard: async () => true
}
return { ...base, ...overrides } as typeof window.api
}
beforeEach(() => {
window.api = mockApi()
})
describe('App - 初始空态', () => {
it('渲染应用名称 DiffLens', () => {
render(<App />)
@@ -18,4 +38,22 @@ describe('App - 初始空态', () => {
render(<App />)
expect(screen.queryByText('导出报告')).not.toBeInTheDocument()
})
})
describe('App - 加载文件后的非空态', () => {
it('打开文件后渲染对比视图与导出入口,并显示文件内容', async () => {
render(<App />)
fireEvent.click(screen.getByText('打开左侧'))
expect(await screen.findByText('导出报告')).toBeInTheDocument()
// 被对比的内容渲染(此处与空文件对比,内容出现在面板中)
expect((await screen.findAllByText(/line1/)).length).toBeGreaterThan(0)
expect(screen.getAllByText('a.txt').length).toBeGreaterThan(0)
})
it('右侧打开文件后可导出报告', async () => {
render(<App />)
fireEvent.click(screen.getByText('打开右侧'))
expect(await screen.findByText('导出报告')).toBeInTheDocument()
})
})
@@ -0,0 +1,42 @@
import { describe, it, expect, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import ContextMenu, { type ContextMenuItem } from './ContextMenu'
describe('ContextMenu', () => {
it('渲染菜单项并触行动作后关闭', () => {
const action = vi.fn()
const onClose = vi.fn()
const items: ContextMenuItem[] = [{ label: '复制此行内容', action }]
const { container } = render(
<ContextMenu x={10} y={10} items={items} onClose={onClose} />
)
fireEvent.click(screen.getByText('复制此行内容'))
expect(action).toHaveBeenCalledTimes(1)
expect(onClose).toHaveBeenCalled()
expect(container.firstChild).toBeTruthy()
})
it('禁用项不触行动作', () => {
const action = vi.fn()
const onClose = vi.fn()
const items: ContextMenuItem[] = [
{ label: '在文件夹中显示', disabled: true, action },
{ label: '复制文件名', action }
]
render(<ContextMenu x={10} y={10} items={items} onClose={onClose} />)
fireEvent.click(screen.getByText('在文件夹中显示'))
expect(action).not.toHaveBeenCalled()
expect(onClose).not.toHaveBeenCalled()
})
it('点击遮罩层触发关闭', () => {
const onClose = vi.fn()
const { container } = render(
<ContextMenu x={10} y={10} items={[]} onClose={onClose} />
)
const backdrop = container.querySelector('.ctx-backdrop')
expect(backdrop).not.toBeNull()
fireEvent.mouseDown(backdrop as Element)
expect(onClose).toHaveBeenCalled()
})
})
@@ -0,0 +1,60 @@
import { describe, it, expect, vi, beforeAll } from 'vitest'
import { render, screen } from '@testing-library/react'
import DiffView from './DiffView'
import type { DiffRow } from '../diff/diffEngine'
beforeAll(() => {
// jsdom 未实现 scrollIntoView,测试里打桩
Element.prototype.scrollIntoView = vi.fn()
})
function makeDiff(activeRowId: string | null): void {
const rows: DiffRow[] = [
{
id: 'r0',
rowKind: 'unchanged',
isChanged: false,
left: { lineNo: 1, text: 'hello', segs: null },
right: { lineNo: 1, text: 'hello', segs: null }
},
{
id: 'r1',
rowKind: 'added',
isChanged: true,
left: { lineNo: null, text: null, segs: null },
right: { lineNo: 2, text: 'world', segs: null }
}
]
render(
<DiffView
diff={{ rows, summary: { changedLines: 1, inserted: 1, deleted: 0, modified: 0 } }}
leftMeta={{ name: 'a.txt', encoding: 'UTF-8' }}
rightMeta={{ name: 'b.txt', encoding: 'GBK' }}
activeRowId={activeRowId}
onOpen={vi.fn()}
onRowContext={vi.fn()}
/>
)
}
describe('DiffView', () => {
it('渲染左右文件名与同/新增行内容', () => {
makeDiff(null)
expect(screen.getByText('a.txt')).toBeInTheDocument()
expect(screen.getByText('b.txt')).toBeInTheDocument()
expect(screen.getAllByText('hello').length).toBeGreaterThanOrEqual(2)
expect(screen.getAllByText('world').length).toBeGreaterThan(0)
})
it('渲染行号', () => {
makeDiff(null)
// 两栏各自渲染行号 1,总共应有多个
expect(screen.getAllByText('1').length).toBeGreaterThan(0)
expect(screen.getAllByText('2').length).toBeGreaterThan(0)
})
it('设置活动行时调用 scrollIntoView 定位', () => {
makeDiff('r1')
expect(Element.prototype.scrollIntoView).toHaveBeenCalled()
})
})
@@ -0,0 +1,65 @@
import { describe, it, expect, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import Toolbar from './Toolbar'
import { DiffSummary } from '../diff/diffEngine'
const summary: DiffSummary = { changedLines: 3, inserted: 1, deleted: 1, modified: 1 }
function setup(extra?: { canExport?: boolean }) {
const onOptionsChange = vi.fn()
const onNav = vi.fn()
const onExport = vi.fn()
const utils = render(
<Toolbar
options={{ trimWhitespace: false, ignoreCase: false }}
onOptionsChange={onOptionsChange}
summary={summary}
navIndex={0}
navCount={3}
onNav={onNav}
onExport={onExport}
canExport={extra?.canExport ?? true}
/>
)
return { onOptionsChange, onNav, onExport, ...utils }
}
describe('Toolbar', () => {
it('渲染差异统计徽章', () => {
setup()
expect(screen.getByText('+1')).toBeInTheDocument()
expect(screen.getByText('1')).toBeInTheDocument()
expect(screen.getByText('~1')).toBeInTheDocument()
})
it('切换忽略大小写触发 onOptionsChange', () => {
const { onOptionsChange } = setup()
fireEvent.click(screen.getByLabelText('忽略大小写'))
expect(onOptionsChange).toHaveBeenCalledWith({ trimWhitespace: false, ignoreCase: true })
})
it('切换忽略行首尾空白触发 onOptionsChange', () => {
const { onOptionsChange } = setup()
fireEvent.click(screen.getByLabelText('忽略行首尾空白'))
expect(onOptionsChange).toHaveBeenCalledWith({ trimWhitespace: true, ignoreCase: false })
})
it('点击下一处差异调用 onNav(1)', () => {
const { onNav } = setup()
fireEvent.click(screen.getByTitle('下一处差异'))
expect(onNav).toHaveBeenCalledWith(1)
})
it('点击上一处差异调用 onNav(-1)', () => {
const { onNav } = setup()
fireEvent.click(screen.getByTitle('上一处差异'))
expect(onNav).toHaveBeenCalledWith(-1)
})
it('导出菜单选择 HTML 触发 onExport("html")', () => {
const { onExport } = setup()
fireEvent.click(screen.getByText('导出报告'))
fireEvent.click(screen.getByText('HTML 报告'))
expect(onExport).toHaveBeenCalledWith('html')
})
})