import { describe, it, beforeEach, afterEach } from 'node:test'; import assert from 'node:assert'; import fs from 'node:fs'; import path from 'node:path'; import os from 'node:os'; import { writeBuffer, readBuffer } from '../skill/scripts/live/manual-edits-buffer.mjs'; import { scrubManualEditsAgainstOriginalBlock } from '../skill/scripts/live-accept.mjs'; let tmpDir; beforeEach(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'scrub-test-')); }); afterEach(() => { fs.rmSync(tmpDir, { recursive: true, force: true }); }); function entry({ id = 'e1', pageUrl = '/', ops = [] } = {}) { return { id, pageUrl, element: { tagName: 'p' }, ops, stagedAt: new Date().toISOString() }; } function op({ ref = 'div>p.1', originalText = 'A', newText = 'B' } = {}) { return { ref, tag: 'p', classes: ['x'], originalText, newText }; } describe('scrubManualEditsAgainstOriginalBlock', () => { it('drops only ops whose original or edited text appeared in the accepted original block', () => { writeBuffer(tmpDir, { entries: [ entry({ ops: [ op({ ref: 'r1', originalText: 'Accepted original', newText: 'Accepted edited' }), op({ ref: 'r2', originalText: 'Accepted second', newText: 'Accepted second edited' }), op({ ref: 'r3', originalText: 'Outside text', newText: 'Outside edited' }), ], }), ], }); scrubManualEditsAgainstOriginalBlock('

Accepted edited

Accepted second

', tmpDir, '/'); const buf = readBuffer(tmpDir); assert.equal(buf.entries.length, 1); assert.equal(buf.entries[0].ops.length, 1); assert.equal(buf.entries[0].ops[0].ref, 'r3'); }); it('preserves cross-file staged edits even when their original text is absent from the accepted file', () => { writeBuffer(tmpDir, { entries: [ entry({ id: 'hero', ops: [op({ ref: 'hero-h1', originalText: 'Hero title', newText: 'Hero title edited' })], }), entry({ id: 'header', ops: [op({ ref: 'header-nav', originalText: 'Docs', newText: 'Docs edited' })], }), ], }); scrubManualEditsAgainstOriginalBlock('

Hero title edited

', tmpDir, '/'); const buf = readBuffer(tmpDir); assert.equal(buf.entries.length, 1); assert.equal(buf.entries[0].id, 'header'); assert.equal(buf.entries[0].ops[0].ref, 'header-nav'); }); it('prunes entries whose ops all belonged to the accepted block', () => { writeBuffer(tmpDir, { entries: [ entry({ id: 'doomed', ops: [op({ originalText: 'Gone A', newText: 'New A' }), op({ originalText: 'Gone B', newText: 'New B' })] }), entry({ id: 'survivor', ops: [op({ originalText: 'Other source', newText: 'Other source edited' })] }), ], }); scrubManualEditsAgainstOriginalBlock('

New A

Gone B

', tmpDir, '/'); const buf = readBuffer(tmpDir); assert.equal(buf.entries.length, 1); assert.equal(buf.entries[0].id, 'survivor'); }); it('is a no-op when the buffer is empty', () => { scrubManualEditsAgainstOriginalBlock('
', tmpDir, '/'); assert.equal(readBuffer(tmpDir).entries.length, 0); }); it('only scrubs the accepted page and preserves matching text on other pages', () => { writeBuffer(tmpDir, { entries: [ entry({ id: 'home', pageUrl: '/', ops: [op({ ref: 'home-title', originalText: 'Shared label', newText: 'Shared edited' })], }), entry({ id: 'docs', pageUrl: '/docs', ops: [op({ ref: 'docs-title', originalText: 'Shared label', newText: 'Shared edited' })], }), ], }); scrubManualEditsAgainstOriginalBlock('

Shared edited

', tmpDir, '/'); const buf = readBuffer(tmpDir); assert.equal(buf.entries.length, 1); assert.equal(buf.entries[0].id, 'docs'); assert.equal(buf.entries[0].pageUrl, '/docs'); }); it('does not scrub anything without a page URL', () => { writeBuffer(tmpDir, { entries: [ entry({ id: 'home', pageUrl: '/', ops: [op({ ref: 'home-title', originalText: 'Accepted original', newText: 'Accepted edited' })], }), ], }); scrubManualEditsAgainstOriginalBlock('

Accepted edited

', tmpDir); const buf = readBuffer(tmpDir); assert.equal(buf.entries.length, 1); assert.equal(buf.entries[0].ops.length, 1); }); it('does not scrub ops whose copy is only a substring of the accepted block', () => { writeBuffer(tmpDir, { entries: [ entry({ id: 'home', ops: [ op({ ref: 'short', originalText: 'Edit', newText: 'Save' }), op({ ref: 'exact', originalText: 'Cancel', newText: 'Done' }), ], }), ], }); scrubManualEditsAgainstOriginalBlock('
', tmpDir, '/'); const buf = readBuffer(tmpDir); assert.equal(buf.entries.length, 1); assert.deepEqual(buf.entries[0].ops.map((item) => item.ref), ['short']); }); });