|
|
@@ -5,6 +5,7 @@ import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'
|
|
|
import type { ComponentProps } from 'react'
|
|
|
import { DEFAULT_DIFF_MAX_LINES, DiffBlock as LocalizedDiffBlock, type DiffHunk } from '../src/index.ts'
|
|
|
import { diffBlockLabels } from './labels.client.ts'
|
|
|
+import { diffTotals } from '../src/DiffBlock.tsx'
|
|
|
|
|
|
function DiffBlock(props: Omit<ComponentProps<typeof LocalizedDiffBlock>, 'labels'>) {
|
|
|
return <LocalizedDiffBlock {...props} labels={diffBlockLabels} />
|
|
|
@@ -94,6 +95,51 @@ describe('DiffBlock structure', () => {
|
|
|
})
|
|
|
})
|
|
|
|
|
|
+describe('DiffBlock local changes', () => {
|
|
|
+ it('copies shared context once and counts only a changed line', async () => {
|
|
|
+ const writeText = vi.fn().mockResolvedValue(undefined)
|
|
|
+ Object.defineProperty(navigator, 'clipboard', { configurable: true, value: { writeText } })
|
|
|
+ const diffs = [{ path: 'settings.ts', oldText: 'start\nmode = 1\nend', newText: 'start\nmode = 2\nend' }]
|
|
|
+ render(<DiffBlock diffs={diffs} />)
|
|
|
+ expect(screen.getAllByText('start')).toHaveLength(1)
|
|
|
+ expect(screen.getAllByText('end')).toHaveLength(1)
|
|
|
+ expect(screen.getByText('└ +1 -1 · 1 file')).toBeTruthy()
|
|
|
+ expect(diffTotals(diffs)).toEqual({ added: 1, removed: 1 })
|
|
|
+ await act(async () => { fireEvent.click(screen.getByRole('button', { name: '复制' })) })
|
|
|
+ expect(writeText).toHaveBeenCalledWith('settings.ts\n start\n- mode = 1\n+ mode = 2\n end')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('keeps three neutral context lines around distant changes and separates hunks', () => {
|
|
|
+ const before = Array.from({ length: 50 }, (_, i) => `item ${i}`)
|
|
|
+ const after = before.map((line, i) => i === 10 || i === 40 ? `changed ${i}` : line)
|
|
|
+ const { container } = render(<DiffBlock diffs={[{
|
|
|
+ path: 'items.txt', oldText: before.join('\n'), newText: after.join('\n'),
|
|
|
+ }]} maxLines={100} />)
|
|
|
+ expect(bodyRows(container)).toEqual([
|
|
|
+ 'items.txt', 'item 7', 'item 8', 'item 9', 'item 10', 'changed 10',
|
|
|
+ 'item 11', 'item 12', 'item 13', '⋯', 'item 37', 'item 38', 'item 39',
|
|
|
+ 'item 40', 'changed 40', 'item 41', 'item 42', 'item 43',
|
|
|
+ ])
|
|
|
+ expect(changeRows(container)).toEqual(['item 10', 'changed 10', 'item 40', 'changed 40'])
|
|
|
+ expect(screen.getByText('└ +2 -2 · 1 file')).toBeTruthy()
|
|
|
+ })
|
|
|
+
|
|
|
+ it.each([
|
|
|
+ ['a\nx\na\ny\na', 'a\nx\na\nz\na', 1, 1],
|
|
|
+ ['a\nb', 'a\nx\nb', 1, 0],
|
|
|
+ ['a\nx\nb', 'a\nb', 0, 1],
|
|
|
+ ['same\n', 'same', 0, 0],
|
|
|
+ ['same', 'same', 0, 0],
|
|
|
+ ['', '', 0, 0],
|
|
|
+ ['a\n\nb', 'a\nb', 0, 1],
|
|
|
+ ])('counts ordered changes in %j → %j', (oldText, newText, added, removed) => {
|
|
|
+ const diffs = [{ path: 'a.txt', oldText, newText }]
|
|
|
+ render(<DiffBlock diffs={diffs} />)
|
|
|
+ expect(diffTotals(diffs)).toEqual({ added, removed })
|
|
|
+ expect(screen.getByText(`└ +${added} -${removed} · 1 file`)).toBeTruthy()
|
|
|
+ })
|
|
|
+})
|
|
|
+
|
|
|
describe('DiffBlock footer', () => {
|
|
|
it('counts added and removed lines and one file', () => {
|
|
|
const diffs: DiffHunk[] = [{ path: 'a.ts', oldText: 'a\nb', newText: 'c' }]
|