Files
helder/test/diff.test.ts
Jonathan van Rij a2d1c5df83
Some checks failed
CI / check (push) Has been cancelled
shows relative images in markdown
2026-09-14 11:27:10 +02:00

102 lines
3.7 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { buildDiff, makeDiff } from '../src/renderer/src/diff'
describe('buildDiff', () => {
it('reports no changes for identical text', () => {
const d = buildDiff('a\nb\nc\n', 'a\nb\nc\n')
expect(d.add).toBe(0)
expect(d.del).toBe(0)
expect(d.rows.every((r) => r.sign === ' ')).toBe(true)
})
it('counts a single changed line as one add + one del', () => {
const d = buildDiff('a\nb\nc', 'a\nB\nc')
expect(d.add).toBe(1)
expect(d.del).toBe(1)
const signs = d.rows.map((r) => r.sign).join('')
expect(signs).toContain('-')
expect(signs).toContain('+')
})
it('treats an empty original as all additions (new file)', () => {
const d = buildDiff('', 'x\ny')
expect(d.add).toBe(2)
expect(d.del).toBe(0)
expect(d.left).toHaveLength(0)
expect(d.right).toHaveLength(2)
})
it('treats an empty updated as all deletions (deleted file)', () => {
const d = buildDiff('x\ny\nz', '')
expect(d.del).toBe(3)
expect(d.add).toBe(0)
expect(d.right).toHaveLength(0)
})
it('marks the changed line on both sides', () => {
const d = buildDiff('keep\nold\nkeep', 'keep\nnew\nkeep')
expect(d.left.find((l) => l.text === 'old')?.mark).toBe('del')
expect(d.right.find((l) => l.text === 'new')?.mark).toBe('add')
expect(d.left.find((l) => l.text === 'keep')?.mark).toBeNull()
})
it('aligns split rows: same lines pair, changes stack into the gap', () => {
const d = buildDiff('a\nold\nb', 'a\nnew\nb')
// every split row has at least one side
expect(d.split.every((r) => r.l || r.r)).toBe(true)
// the matched 'a' and 'b' lines pair on both sides
const paired = d.split.filter((r) => r.l && r.r && r.l.text === r.r.text)
expect(paired.map((r) => r.l!.text)).toEqual(['a', 'b'])
})
it('ignores a single trailing newline difference', () => {
const d = buildDiff('a\nb', 'a\nb\n')
expect(d.add).toBe(0)
expect(d.del).toBe(0)
})
})
describe('makeDiff', () => {
it('flags added / deleted and carries the text pair', () => {
const added = makeDiff('A', '', 'hi')
expect(added.added).toBe(true)
expect(added.deleted).toBe(false)
expect(added.original).toBe('')
expect(added.updated).toBe('hi')
const deleted = makeDiff('D', 'bye', '')
expect(deleted.deleted).toBe(true)
expect(deleted.added).toBe(false)
})
})
describe('buildDiff on large files', () => {
it('diffs a 60k-line file without exhausting the heap', () => {
const base = Array.from({ length: 60_000 }, (_, i) => `line ${i}`)
const changed = base.slice()
changed[30_000] = 'line 30000 edited'
const d = buildDiff(base.join('\n'), changed.join('\n'))
expect(d.add).toBe(1)
expect(d.del).toBe(1)
expect(d.right[30_000].mark).toBe('add')
})
it('survives a full rewrite of a large file with no shared lines', () => {
const a = Array.from({ length: 40_000 }, (_, i) => `old ${i}`).join('\n')
const b = Array.from({ length: 40_000 }, (_, i) => `new ${i}`).join('\n')
const d = buildDiff(a, b)
expect(d.del).toBe(40_000)
expect(d.add).toBe(40_000)
})
it('keeps the unchanged head and tail of a large, heavily edited file', () => {
const head = Array.from({ length: 5_000 }, (_, i) => `head ${i}`)
const tail = Array.from({ length: 5_000 }, (_, i) => `tail ${i}`)
const mid = (p: string): string[] => Array.from({ length: 30_000 }, (_, i) => `${p} ${i % 7}`)
const d = buildDiff([...head, ...mid('x'), ...tail].join('\n'), [...head, ...mid('y'), ...tail].join('\n'))
expect(d.left.slice(0, 5_000).every((l) => l.mark === null)).toBe(true)
expect(d.left.slice(-5_000).every((l) => l.mark === null)).toBe(true)
expect(d.add).toBe(30_000)
})
})