const { describe, test } = require('node:test'); const assert = require('node:assert'); const { BB } = require('./setup'); describe('BB.utils.escapeHtml', () => { test('escapes angle brackets', () => { const r = BB.utils.escapeHtml('hi'); assert.ok(r.includes('<') && r.includes('>')); }); test('escapes ampersand', () => { assert.ok(BB.utils.escapeHtml('A & B').includes('&')); }); test('returns empty for falsy', () => { assert.strictEqual(BB.utils.escapeHtml(''), ''); assert.strictEqual(BB.utils.escapeHtml(null), ''); assert.strictEqual(BB.utils.escapeHtml(undefined), ''); }); test('passes safe strings through', () => { assert.strictEqual(BB.utils.escapeHtml('hello'), 'hello'); }); }); describe('BB.utils.escapeAttr', () => { test('escapes double quotes', () => { assert.ok(BB.utils.escapeAttr('a"b').includes('"')); }); test('escapes single quotes', () => { assert.ok(BB.utils.escapeAttr("a'b").includes(''')); }); test('escapes < and >', () => { const r = BB.utils.escapeAttr('<>'); assert.ok(r.includes('<') && r.includes('>')); }); test('escapes ampersand', () => { assert.strictEqual(BB.utils.escapeAttr('a&b'), 'a&b'); }); test('returns empty for falsy', () => { assert.strictEqual(BB.utils.escapeAttr(''), ''); assert.strictEqual(BB.utils.escapeAttr(null), ''); }); test('handles all special chars together', () => { const r = BB.utils.escapeAttr(`<"&'>`); assert.ok(!r.includes('<') || r.includes('<')); }); test('converts non-string to string', () => { assert.strictEqual(BB.utils.escapeAttr(123), '123'); }); }); describe('BB.utils.debounce', () => { test('does not fire immediately', () => { let called = false; const fn = BB.utils.debounce(() => { called = true; }, 10); fn(); assert.ok(!called, 'Should not fire immediately'); }); // Swaps the global timer functions for the length of one synchronous test // body, so nothing else can schedule against them mid-swap. test('rapid calls only execute last one', () => { let callCount = 0, lastArg = null; const origST = globalThis.setTimeout; const origCT = globalThis.clearTimeout; let pendingCb = null; globalThis.setTimeout = (cb) => { pendingCb = cb; return 1; }; globalThis.clearTimeout = () => { pendingCb = null; }; try { const fn = BB.utils.debounce((arg) => { callCount++; lastArg = arg; }, 100); fn('a'); fn('b'); fn('c'); if (pendingCb) pendingCb(); } finally { globalThis.setTimeout = origST; globalThis.clearTimeout = origCT; } assert.strictEqual(callCount, 1); assert.strictEqual(lastArg, 'c'); }); });