diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-03-03 21:15:55 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-03-03 21:15:55 +0000 |
| commit | e8d1b80bd504088a7535e6045ab77f1ddd3b3d43 (patch) | |
| tree | b4eca65fc1306be5b56698941018969f90d716f7 /web/test | |
| parent | 74cc740398cf2d90804ab19db728c844c2e056b7 (diff) | |
Web UI: tabs, new task modal with AI draft, templates panel
- Tab bar to switch between Tasks and Templates views
- New Task modal with elaborate section ("Draft with AI") that calls
POST /api/tasks/elaborate and pre-fills form fields
- Templates panel listing saved configs with create/edit/delete
- base-path meta tag for sub-path deployments
- filter.test.mjs: contract tests for filterTasks function
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'web/test')
| -rw-r--r-- | web/test/filter.test.mjs | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/web/test/filter.test.mjs b/web/test/filter.test.mjs new file mode 100644 index 0000000..947934b --- /dev/null +++ b/web/test/filter.test.mjs @@ -0,0 +1,77 @@ +// 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), []); + }); +}); |
