Files
chrome-use/src/diff.test.ts
T
Chris Tate d5a667ea2d diff (#510)
* diff

* fixes

* fixes

* fixes

* fixes

* fixes

* better docs
2026-02-19 23:51:09 -06:00

190 lines
6.7 KiB
TypeScript

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { diffSnapshots, diffScreenshots } from './diff.js';
import { chromium, type Browser, type BrowserContext, type Page } from 'playwright-core';
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
describe('diffSnapshots', () => {
it('should report no changes for identical inputs', () => {
const text = 'heading "Hello"\nbutton "Submit" [ref=e1]';
const result = diffSnapshots(text, text);
expect(result.changed).toBe(false);
expect(result.additions).toBe(0);
expect(result.removals).toBe(0);
expect(result.unchanged).toBe(2);
});
it('should report no changes for empty inputs', () => {
const result = diffSnapshots('', '');
expect(result.changed).toBe(false);
expect(result.additions).toBe(0);
expect(result.removals).toBe(0);
expect(result.unchanged).toBe(1);
});
it('should detect a single-line addition', () => {
const before = 'heading "Hello"';
const after = 'heading "Hello"\nbutton "New"';
const result = diffSnapshots(before, after);
expect(result.changed).toBe(true);
expect(result.additions).toBe(1);
expect(result.removals).toBe(0);
expect(result.unchanged).toBe(1);
expect(result.diff).toContain('+ button "New"');
});
it('should detect a single-line removal', () => {
const before = 'heading "Hello"\nbutton "Gone"';
const after = 'heading "Hello"';
const result = diffSnapshots(before, after);
expect(result.changed).toBe(true);
expect(result.additions).toBe(0);
expect(result.removals).toBe(1);
expect(result.unchanged).toBe(1);
expect(result.diff).toContain('- button "Gone"');
});
it('should detect completely different inputs', () => {
const before = 'line A\nline B';
const after = 'line C\nline D';
const result = diffSnapshots(before, after);
expect(result.changed).toBe(true);
expect(result.additions).toBe(2);
expect(result.removals).toBe(2);
expect(result.unchanged).toBe(0);
});
it('should handle mixed additions, removals, and unchanged lines', () => {
const before = [
'heading "Title"',
'button "Submit" [ref=e2]',
'text "old value"',
'footer "Copyright"',
].join('\n');
const after = [
'heading "Title"',
'button "Submit" [ref=e2] [disabled]',
'text "new value"',
'link "Help" [ref=e5]',
'footer "Copyright"',
].join('\n');
const result = diffSnapshots(before, after);
expect(result.changed).toBe(true);
expect(result.additions).toBeGreaterThan(0);
expect(result.removals).toBeGreaterThan(0);
expect(result.unchanged).toBeGreaterThan(0);
expect(result.diff).toContain('+ ');
expect(result.diff).toContain('- ');
});
it('should use + prefix for insertions and - prefix for deletions', () => {
const before = 'alpha';
const after = 'beta';
const result = diffSnapshots(before, after);
const lines = result.diff.split('\n');
const deletions = lines.filter((l) => l.startsWith('- '));
const insertions = lines.filter((l) => l.startsWith('+ '));
expect(deletions.length).toBe(1);
expect(insertions.length).toBe(1);
expect(deletions[0]).toBe('- alpha');
expect(insertions[0]).toBe('+ beta');
});
it('should use two-space prefix for unchanged lines', () => {
const text = 'unchanged line';
const result = diffSnapshots(text, text);
expect(result.diff).toBe(' unchanged line');
});
it('should handle multiline to empty', () => {
const before = 'line 1\nline 2\nline 3';
const after = '';
const result = diffSnapshots(before, after);
expect(result.changed).toBe(true);
expect(result.removals).toBeGreaterThanOrEqual(3);
});
it('should handle empty to multiline', () => {
const before = '';
const after = 'line 1\nline 2\nline 3';
const result = diffSnapshots(before, after);
expect(result.changed).toBe(true);
expect(result.additions).toBeGreaterThanOrEqual(3);
});
});
const canLaunchBrowser = await (async () => {
try {
const b = await chromium.launch({ headless: true });
await b.close();
return true;
} catch {
return false;
}
})();
describe.skipIf(!canLaunchBrowser)('diffScreenshots', () => {
let browser: Browser;
let context: BrowserContext;
let page: Page;
beforeAll(async () => {
browser = await chromium.launch({ headless: true });
context = await browser.newContext({ viewport: { width: 200, height: 200 } });
page = await context.newPage();
});
afterAll(async () => {
await browser.close();
});
async function screenshotOfColor(color: string): Promise<Buffer> {
await page.setContent(`<div style="width:200px;height:200px;background:${color}"></div>`);
return await page.screenshot({ type: 'png' });
}
it('should report match for identical images', async () => {
const img = await screenshotOfColor('red');
const result = await diffScreenshots(context, img, img, {});
expect(result.match).toBe(true);
expect(result.differentPixels).toBe(0);
expect(result.mismatchPercentage).toBe(0);
expect(result.dimensionMismatch).toBeUndefined();
if (result.diffPath) fs.unlinkSync(result.diffPath);
});
it('should detect differences between distinct images', async () => {
const imgA = await screenshotOfColor('red');
const imgB = await screenshotOfColor('blue');
const result = await diffScreenshots(context, imgA, imgB, {});
expect(result.match).toBe(false);
expect(result.differentPixels).toBeGreaterThan(0);
expect(result.mismatchPercentage).toBeGreaterThan(0);
if (result.diffPath) fs.unlinkSync(result.diffPath);
});
it('should detect dimension mismatch', async () => {
const imgA = await screenshotOfColor('white');
await page.setViewportSize({ width: 100, height: 100 });
const imgB = await screenshotOfColor('white');
await page.setViewportSize({ width: 200, height: 200 });
const result = await diffScreenshots(context, imgA, imgB, {});
expect(result.dimensionMismatch).toBe(true);
expect(result.mismatchPercentage).toBe(100);
if (result.diffPath) fs.unlinkSync(result.diffPath);
});
it('should write diff image to custom outputPath', async () => {
const imgA = await screenshotOfColor('green');
const imgB = await screenshotOfColor('yellow');
const outputPath = path.join(os.tmpdir(), `diff-test-${Date.now()}.png`);
const result = await diffScreenshots(context, imgA, imgB, { outputPath });
expect(result.diffPath).toBe(outputPath);
expect(fs.existsSync(outputPath)).toBe(true);
const stat = fs.statSync(outputPath);
expect(stat.size).toBeGreaterThan(0);
fs.unlinkSync(outputPath);
});
});