74 lines
2.8 KiB
TypeScript
74 lines
2.8 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { computeEditScript } from '../src/myers.js';
|
|
|
|
describe('computeEditScript', () => {
|
|
it('returns empty for two empty arrays', () => {
|
|
expect(computeEditScript([], [])).toEqual([]);
|
|
});
|
|
|
|
it('returns all-insert for empty before', () => {
|
|
const ops = computeEditScript([], ['a', 'b']);
|
|
expect(ops).toEqual([{ type: 'insert', lines: ['a', 'b'] }]);
|
|
});
|
|
|
|
it('returns all-delete for empty after', () => {
|
|
const ops = computeEditScript(['a', 'b'], []);
|
|
expect(ops).toEqual([{ type: 'delete', lines: ['a', 'b'] }]);
|
|
});
|
|
|
|
it('returns all-equal for identical arrays', () => {
|
|
const ops = computeEditScript(['a', 'b', 'c'], ['a', 'b', 'c']);
|
|
expect(ops).toEqual([{ type: 'equal', lines: ['a', 'b', 'c'] }]);
|
|
});
|
|
|
|
it('detects a single changed line', () => {
|
|
const ops = computeEditScript(['a', 'b', 'c'], ['a', 'X', 'c']);
|
|
const types = ops.map(o => o.type);
|
|
expect(types).toContain('delete');
|
|
expect(types).toContain('insert');
|
|
const deleted = ops.filter(o => o.type === 'delete').flatMap(o => o.lines);
|
|
const inserted = ops.filter(o => o.type === 'insert').flatMap(o => o.lines);
|
|
expect(deleted).toContain('b');
|
|
expect(inserted).toContain('X');
|
|
});
|
|
|
|
it('detects inserted lines at end', () => {
|
|
const ops = computeEditScript(['a'], ['a', 'b', 'c']);
|
|
const inserted = ops.filter(o => o.type === 'insert').flatMap(o => o.lines);
|
|
expect(inserted).toEqual(['b', 'c']);
|
|
});
|
|
|
|
it('detects deleted lines at start', () => {
|
|
const ops = computeEditScript(['x', 'y', 'a'], ['a']);
|
|
const deleted = ops.filter(o => o.type === 'delete').flatMap(o => o.lines);
|
|
expect(deleted).toEqual(['x', 'y']);
|
|
});
|
|
|
|
it('merges consecutive ops of same type', () => {
|
|
const ops = computeEditScript(['a', 'b'], ['c', 'd']);
|
|
// Should have merged deletes and inserts, not one-op-per-line
|
|
const delOp = ops.find(o => o.type === 'delete');
|
|
const insOp = ops.find(o => o.type === 'insert');
|
|
expect(delOp?.lines.length).toBeGreaterThanOrEqual(1);
|
|
expect(insOp?.lines.length).toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
it('round-trip: applying ops to before yields after', () => {
|
|
const before = ['prop: 1', 'style: red', 'size: md'];
|
|
const after = ['prop: 1', 'style: blue', 'size: lg', 'weight: 600'];
|
|
const ops = computeEditScript(before, after);
|
|
|
|
const result: string[] = [];
|
|
for (const op of ops) {
|
|
if (op.type === 'equal' || op.type === 'insert') result.push(...op.lines);
|
|
}
|
|
expect(result).toEqual(after);
|
|
});
|
|
|
|
it('handles single-element change', () => {
|
|
const ops = computeEditScript(['x'], ['y']);
|
|
expect(ops.some(o => o.type === 'delete' && o.lines.includes('x'))).toBe(true);
|
|
expect(ops.some(o => o.type === 'insert' && o.lines.includes('y'))).toBe(true);
|
|
});
|
|
});
|