import { describe, it, afterEach, mock } from 'node:test'; import assert from 'node:assert/strict'; import { section, step, line, input, output, result, ok, warn, error } from '../log.js'; afterEach(() => mock.restoreAll()); const TS = '\\[\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2}\\]'; describe('log helpers', () => { it('formats section and step messages', () => { const calls = []; mock.method(console, 'log', (...args) => { calls.push(args.join(' ')); }); section('Pipeline'); step('Step1', 'Start'); assert.match(calls[0], new RegExp(`^\\n${TS}\\[INF\\]: === Pipeline ===$`)); assert.match(calls[1], new RegExp(`^\\n${TS}\\[INF\\]: \\[Step1\\] Start$`)); }); it('formats line and ok messages with console.log', () => { const calls = []; mock.method(console, 'log', (...args) => { calls.push(args.join(' ')); }); line('hello'); ok('done'); assert.match(calls[0], new RegExp(`^${TS}\\[INF\\]: - hello$`)); assert.match(calls[1], new RegExp(`^${TS}\\[INF\\]: ✓ done$`)); }); it('formats input/output and pass/fail result messages', () => { const calls = []; mock.method(console, 'log', (...args) => { calls.push(args.join(' ')); }); input('5 筆'); output('3 筆'); result(true, '通過'); result(false, '未通過'); assert.match(calls[0], new RegExp(`^${TS}\\[INF\\]: ← 輸入:5 筆$`)); assert.match(calls[1], new RegExp(`^${TS}\\[INF\\]: → 輸出:3 筆$`)); assert.match(calls[2], new RegExp(`^${TS}\\[INF\\]: ✅ 成功:通過$`)); assert.match(calls[3], new RegExp(`^${TS}\\[ERR\\]: ❌ 失敗:未通過$`)); }); it('formats warn messages with console.warn', () => { const calls = []; mock.method(console, 'warn', (...args) => { calls.push(args.join(' ')); }); warn('careful'); assert.match(calls[0], new RegExp(`^${TS}\\[WRN\\]: ! careful$`)); }); it('formats error messages with console.error', () => { const calls = []; mock.method(console, 'error', (...args) => { calls.push(args.join(' ')); }); error('boom'); assert.match(calls[0], new RegExp(`^${TS}\\[ERR\\]: x boom$`)); }); });