// filter.test.mjs — TDD contract tests for filterTasks // // filterTasks is defined inline here to establish the expected behaviour. // Once filterTasks is added to web/app.js (or a shared module), remove the // inline definition and import it instead. // // Run with: node --test web/test/filter.test.mjs import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; // ── Implementation under contract ───────────────────────────────────────────── // Remove this block once filterTasks is exported from app.js / a shared module. const HIDE_STATES = new Set(['COMPLETED', 'FAILED']); function filterTasks(tasks, hideCompletedFailed = false) { if (!hideCompletedFailed) return tasks; return tasks.filter(t => !HIDE_STATES.has(t.state)); } // ── Helpers ──────────────────────────────────────────────────────────────────── function makeTask(state) { return { id: state, name: `task-${state}`, state }; } // ── Tests ────────────────────────────────────────────────────────────────────── describe('filterTasks', () => { it('removes COMPLETED tasks when hideCompletedFailed=true', () => { const tasks = [makeTask('COMPLETED'), makeTask('PENDING')]; const result = filterTasks(tasks, true); assert.ok(!result.some(t => t.state === 'COMPLETED'), 'COMPLETED should be excluded'); assert.equal(result.length, 1); }); it('removes FAILED tasks when hideCompletedFailed=true', () => { const tasks = [makeTask('FAILED'), makeTask('RUNNING')]; const result = filterTasks(tasks, true); assert.ok(!result.some(t => t.state === 'FAILED'), 'FAILED should be excluded'); assert.equal(result.length, 1); }); it('returns all tasks when hideCompletedFailed=false', () => { const tasks = [ makeTask('COMPLETED'), makeTask('FAILED'), makeTask('PENDING'), makeTask('RUNNING'), ]; const result = filterTasks(tasks, false); assert.equal(result.length, 4, 'all tasks should be returned'); assert.deepEqual(result, tasks); }); it('returns all tasks when hideCompletedFailed is omitted (default false)', () => { const tasks = [makeTask('COMPLETED'), makeTask('FAILED'), makeTask('QUEUED')]; const result = filterTasks(tasks); assert.equal(result.length, 3); }); it('includes PENDING, QUEUED, RUNNING, TIMED_OUT, CANCELLED, BUDGET_EXCEEDED when hiding', () => { const activeStates = ['PENDING', 'QUEUED', 'RUNNING', 'TIMED_OUT', 'CANCELLED', 'BUDGET_EXCEEDED']; const tasks = activeStates.map(makeTask); const result = filterTasks(tasks, true); assert.equal(result.length, activeStates.length, 'all non-terminal active states should be kept'); for (const state of activeStates) { assert.ok(result.some(t => t.state === state), `${state} should be included`); } }); it('returns an empty array when given an empty array', () => { assert.deepEqual(filterTasks([], true), []); assert.deepEqual(filterTasks([], false), []); }); });