Files
smartdiff/test/test.ts

259 lines
8.8 KiB
TypeScript

import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as smartdiff from '../ts/index.js';
// =============================================================================
// Backward Compatibility Tests
// =============================================================================
tap.test('createDiff and applyPatch should work (backward compatible)', async () => {
const text1 = 'hi there';
const text2 = 'hi Polly, there is a Sandwich.';
const patch = smartdiff.createDiff(text1, text2);
console.log('Compact diff:', patch);
const result = smartdiff.applyPatch(text1, patch);
console.log('Reconstructed:', result);
expect(result).toEqual(text2);
});
tap.test('createDiff should produce compact format', async () => {
const original = 'Hello World';
const revised = 'Hello smart World';
const diff = smartdiff.createDiff(original, revised);
const parsed = JSON.parse(diff);
// Should be array of tuples
expect(Array.isArray(parsed)).toBeTrue();
// Each tuple should have operation and value
for (const item of parsed) {
expect(Array.isArray(item)).toBeTrue();
expect(item.length).toEqual(2);
expect([-1, 0, 1]).toContain(item[0]);
}
});
// =============================================================================
// Character Diff Tests
// =============================================================================
tap.test('getCharDiff should return character-level diff segments', async () => {
const original = 'hello';
const revised = 'hallo';
const segments = smartdiff.getCharDiff(original, revised);
expect(Array.isArray(segments)).toBeTrue();
expect(segments.length).toBeGreaterThan(0);
// Each segment should have type and value
for (const segment of segments) {
expect(['equal', 'insert', 'delete']).toContain(segment.type);
expect(typeof segment.value).toEqual('string');
}
// Should detect the 'e' -> 'a' change
const deleteSegment = segments.find(s => s.type === 'delete');
const insertSegment = segments.find(s => s.type === 'insert');
expect(deleteSegment).toBeDefined();
expect(insertSegment).toBeDefined();
});
tap.test('formatCharDiffForConsole should produce ANSI colored output', async () => {
const original = 'hello world';
const revised = 'hello beautiful world';
const output = smartdiff.formatCharDiffForConsole(original, revised);
console.log('Char diff console output:', output);
// Should contain ANSI codes
expect(output).toInclude('\x1b[');
// Should contain the unchanged parts
expect(output).toInclude('hello');
expect(output).toInclude('world');
});
tap.test('formatCharDiffAsHtml should produce HTML spans', async () => {
const original = 'hello';
const revised = 'hallo';
const html = smartdiff.formatCharDiffAsHtml(original, revised);
console.log('Char diff HTML:', html);
expect(html).toInclude('<span class="smartdiff-');
expect(html).toInclude('smartdiff-delete');
expect(html).toInclude('smartdiff-insert');
});
// =============================================================================
// Word Diff Tests
// =============================================================================
tap.test('getWordDiff should return word-level diff segments', async () => {
const original = 'The quick brown fox';
const revised = 'The slow brown dog';
const segments = smartdiff.getWordDiff(original, revised);
console.log('Word diff segments:', segments);
expect(Array.isArray(segments)).toBeTrue();
// Should detect word changes
const hasDelete = segments.some(s => s.type === 'delete');
const hasInsert = segments.some(s => s.type === 'insert');
expect(hasDelete).toBeTrue();
expect(hasInsert).toBeTrue();
});
tap.test('formatWordDiffForConsole should produce readable output', async () => {
const original = 'Hello World';
const revised = 'Hello Beautiful World';
const output = smartdiff.formatWordDiffForConsole(original, revised);
console.log('Word diff console output:', output);
expect(output).toInclude('Hello');
expect(output).toInclude('World');
});
tap.test('formatWordDiffAsHtml should produce HTML spans', async () => {
const original = 'one two three';
const revised = 'one TWO three';
const html = smartdiff.formatWordDiffAsHtml(original, revised);
console.log('Word diff HTML:', html);
expect(html).toInclude('<span class="smartdiff-');
});
// =============================================================================
// Line Diff Tests
// =============================================================================
tap.test('getLineDiff should return line-level diff', async () => {
const original = 'line1\nline2\nline3';
const revised = 'line1\nmodified\nline3';
const lineDiff = smartdiff.getLineDiff(original, revised);
console.log('Line diff:', lineDiff);
expect(Array.isArray(lineDiff)).toBeTrue();
// Should have line numbers and types
for (const line of lineDiff) {
expect(typeof line.lineNumber).toEqual('number');
expect(['equal', 'insert', 'delete']).toContain(line.type);
expect(typeof line.value).toEqual('string');
}
});
tap.test('formatLineDiffForConsole should produce unified-style output', async () => {
const original = 'function hello() {\n console.log("hi");\n}';
const revised = 'function hello() {\n console.log("hello world");\n}';
const output = smartdiff.formatLineDiffForConsole(original, revised);
console.log('Line diff console output:\n' + output);
// Should have +/- prefixes
expect(output).toInclude('-');
expect(output).toInclude('+');
});
tap.test('formatLineDiffAsHtml should produce HTML divs', async () => {
const original = 'a\nb\nc';
const revised = 'a\nB\nc';
const html = smartdiff.formatLineDiffAsHtml(original, revised);
console.log('Line diff HTML:\n' + html);
expect(html).toInclude('<div class="smartdiff-lines">');
expect(html).toInclude('<div class="smartdiff-line');
expect(html).toInclude('smartdiff-prefix');
});
// =============================================================================
// Unified Diff Tests
// =============================================================================
tap.test('createUnifiedDiff should produce git-style unified diff', async () => {
const original = 'line1\nline2\nline3\nline4\nline5';
const revised = 'line1\nmodified\nline3\nline4\nline5';
const unifiedDiff = smartdiff.createUnifiedDiff(original, revised, {
originalFileName: 'file.txt',
revisedFileName: 'file.txt',
});
console.log('Unified diff:\n' + unifiedDiff);
expect(unifiedDiff).toInclude('---');
expect(unifiedDiff).toInclude('+++');
expect(unifiedDiff).toInclude('@@');
});
tap.test('formatUnifiedDiffForConsole should colorize unified diff', async () => {
const original = 'old content';
const revised = 'new content';
const output = smartdiff.formatUnifiedDiffForConsole(original, revised);
console.log('Unified diff console:\n' + output);
// Should contain ANSI codes
expect(output).toInclude('\x1b[');
});
// =============================================================================
// CSS Helper Test
// =============================================================================
tap.test('getDefaultDiffCss should return valid CSS', async () => {
const css = smartdiff.getDefaultDiffCss();
console.log('Default CSS:\n' + css);
expect(css).toInclude('.smartdiff-delete');
expect(css).toInclude('.smartdiff-insert');
expect(css).toInclude('.smartdiff-equal');
expect(css).toInclude('background-color');
});
// =============================================================================
// Edge Cases
// =============================================================================
tap.test('should handle empty strings', async () => {
const diff1 = smartdiff.createDiff('', 'new');
const diff2 = smartdiff.createDiff('old', '');
const diff3 = smartdiff.createDiff('', '');
expect(smartdiff.applyPatch('', diff1)).toEqual('new');
expect(smartdiff.applyPatch('old', diff2)).toEqual('');
expect(smartdiff.applyPatch('', diff3)).toEqual('');
});
tap.test('should handle identical strings', async () => {
const text = 'no changes here';
const diff = smartdiff.createDiff(text, text);
const result = smartdiff.applyPatch(text, diff);
expect(result).toEqual(text);
// Visualization should show all equal
const segments = smartdiff.getCharDiff(text, text);
expect(segments.every(s => s.type === 'equal')).toBeTrue();
});
tap.test('should escape HTML in output', async () => {
const original = '<div>test</div>';
const revised = '<span>test</span>';
const html = smartdiff.formatCharDiffAsHtml(original, revised);
console.log('HTML escaped output:', html);
// Should escape < and >
expect(html).toInclude('&lt;');
expect(html).toInclude('&gt;');
// Should not contain raw HTML tags from input
expect(html).not.toInclude('<div>');
expect(html).not.toInclude('<span>');
});
export default tap.start();