/** * Tests for shared/diff.js — the dependency-free text diff used by the * browse tool's markdown version-history viewer. * * Runs against the same shim as zddc.spec.js (shared/zddc-test.html, * which loads shared/diff.js and exposes window.zddc.diff). */ import { test, expect } from '@playwright/test'; import * as path from 'path'; const SHIM_PATH = 'file://' + path.resolve('shared/zddc-test.html'); async function diff(page, fn, ...args) { return page.evaluate( ([fn, args]) => window.zddc.diff[fn](...args), [fn, args] ); } test.beforeEach(async ({ page }) => { await page.goto(SHIM_PATH, { waitUntil: 'load' }); }); test('diff module is attached to window.zddc', async ({ page }) => { const present = await page.evaluate(() => !!(window.zddc && window.zddc.diff && typeof window.zddc.diff.lines === 'function' && typeof window.zddc.diff.words === 'function')); expect(present).toBe(true); }); test('identical text produces only eq ops', async ({ page }) => { const ops = await diff(page, 'lines', 'a\nb\nc', 'a\nb\nc'); expect(ops.every(o => o.type === 'eq')).toBe(true); }); test('a changed middle line shows del then add', async ({ page }) => { const ops = await diff(page, 'lines', 'a\nb\nc', 'a\nB\nc'); const compact = ops.map(o => `${o.type}:${o.text}`).join('|'); expect(compact).toContain('eq:a'); expect(compact).toContain('del:b'); expect(compact).toContain('add:B'); expect(compact).toContain('eq:c'); }); test('stats count added and removed lines', async ({ page }) => { const addStats = await diff(page, 'stats', await diff(page, 'lines', 'a\nb', 'a\nx\nb')); expect(addStats).toEqual({ added: 1, removed: 0 }); const delStats = await diff(page, 'stats', await diff(page, 'lines', 'a\nb\nc', 'a\nc')); expect(delStats).toEqual({ added: 0, removed: 1 }); }); test('pure insertion at end', async ({ page }) => { const ops = await diff(page, 'lines', 'one\ntwo', 'one\ntwo\nthree'); const added = ops.filter(o => o.type === 'add').map(o => o.text); expect(added).toEqual(['three']); expect(ops.filter(o => o.type === 'del')).toHaveLength(0); }); test('word diff aligns on word boundaries, preserving spaces', async ({ page }) => { const ops = await diff(page, 'words', 'the quick fox', 'the slow fox'); const changed = ops.filter(o => o.type !== 'eq').map(o => `${o.type}:${o.text}`); expect(changed).toEqual(['del:quick', 'add:slow']); }); test('empty inputs do not throw', async ({ page }) => { const ops = await diff(page, 'lines', '', ''); expect(Array.isArray(ops)).toBe(true); });