// changestats.test.mjs — Unit tests for changestats display functions. // // Run with: node --test web/test/changestats.test.mjs import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { formatChangestats, renderChangestatsBadge } from '../app.js'; // ── Mock DOM ─────────────────────────────────────────────────────────────────── function makeDoc() { return { createElement(tag) { const el = { tag, className: '', textContent: '', children: [], appendChild(child) { this.children.push(child); return child; }, }; return el; }, }; } // ── formatChangestats ────────────────────────────────────────────────────────── describe('formatChangestats', () => { it('formats valid stats as "N files, +A -R"', () => { const result = formatChangestats({ files_changed: 5, lines_added: 127, lines_removed: 43 }); assert.equal(result, '5 files, +127 -43'); }); it('returns empty string for null', () => { const result = formatChangestats(null); assert.equal(result, ''); }); it('returns empty string for undefined', () => { const result = formatChangestats(undefined); assert.equal(result, ''); }); it('formats zero values correctly', () => { const result = formatChangestats({ files_changed: 0, lines_added: 0, lines_removed: 0 }); assert.equal(result, '0 files, +0 -0'); }); it('formats single file correctly', () => { const result = formatChangestats({ files_changed: 1, lines_added: 10, lines_removed: 2 }); assert.equal(result, '1 files, +10 -2'); }); }); // ── renderChangestatsBadge ───────────────────────────────────────────────────── describe('renderChangestatsBadge', () => { it('returns element with class changestats-badge for valid stats', () => { const doc = makeDoc(); const el = renderChangestatsBadge({ files_changed: 5, lines_added: 127, lines_removed: 43 }, doc); assert.ok(el, 'element should not be null'); assert.equal(el.className, 'changestats-badge'); }); it('returns element with correct text content', () => { const doc = makeDoc(); const el = renderChangestatsBadge({ files_changed: 5, lines_added: 127, lines_removed: 43 }, doc); assert.equal(el.textContent, '5 files, +127 -43'); }); it('returns null for null stats', () => { const doc = makeDoc(); const el = renderChangestatsBadge(null, doc); assert.equal(el, null); }); it('returns null for undefined stats', () => { const doc = makeDoc(); const el = renderChangestatsBadge(undefined, doc); assert.equal(el, null); }); }); // ── State-based visibility ──────────────────────────────────────────────────── // // Changestats badge should appear on COMPLETED (and READY) tasks that have // changestats data, and must not appear on QUEUED tasks. const CHANGESTATS_STATES = new Set(['COMPLETED', 'READY']); function shouldShowChangestats(task) { return CHANGESTATS_STATES.has(task.state) && task.changestats != null; } describe('changestats badge visibility by task state', () => { it('COMPLETED task with changestats shows badge', () => { const task = { state: 'COMPLETED', changestats: { files_changed: 3, lines_added: 50, lines_removed: 10 } }; assert.equal(shouldShowChangestats(task), true); }); it('READY task with changestats shows badge', () => { const task = { state: 'READY', changestats: { files_changed: 1, lines_added: 5, lines_removed: 2 } }; assert.equal(shouldShowChangestats(task), true); }); it('QUEUED task hides changestats', () => { const task = { state: 'QUEUED', changestats: { files_changed: 3, lines_added: 50, lines_removed: 10 } }; assert.equal(shouldShowChangestats(task), false); }); it('COMPLETED task without changestats hides badge', () => { const task = { state: 'COMPLETED', changestats: null }; assert.equal(shouldShowChangestats(task), false); }); it('RUNNING task hides changestats', () => { const task = { state: 'RUNNING', changestats: null }; assert.equal(shouldShowChangestats(task), false); }); it('PENDING task hides changestats', () => { const task = { state: 'PENDING', changestats: null }; assert.equal(shouldShowChangestats(task), false); }); });