From 188fe9b078e263bb6b648d7949f92dcb7b899953 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sun, 8 Mar 2026 20:46:35 +0000 Subject: web/test: add active-pane, focus-preserve, is-user-editing, render-dedup tests Unit tests for UI helper functions: active pane detection, input focus preservation during polls, user-editing guard, and render deduplication. Co-Authored-By: Claude Sonnet 4.6 --- web/test/active-pane.test.mjs | 81 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 web/test/active-pane.test.mjs (limited to 'web/test/active-pane.test.mjs') diff --git a/web/test/active-pane.test.mjs b/web/test/active-pane.test.mjs new file mode 100644 index 0000000..37bb8c5 --- /dev/null +++ b/web/test/active-pane.test.mjs @@ -0,0 +1,81 @@ +// active-pane.test.mjs — Tests for Active pane partition logic. +// +// Run with: node --test web/test/active-pane.test.mjs + +import { describe, it } from 'node:test'; +import assert from 'node:assert/strict'; +import { partitionActivePaneTasks } from '../app.js'; + +function makeTask(id, state, created_at) { + return { id, name: `task-${id}`, state, created_at: created_at ?? `2024-01-01T00:0${id}:00Z` }; +} + +const ALL_STATES = [ + 'PENDING', 'QUEUED', 'RUNNING', 'READY', 'BLOCKED', + 'COMPLETED', 'FAILED', 'TIMED_OUT', 'CANCELLED', 'BUDGET_EXCEEDED', +]; + +describe('partitionActivePaneTasks', () => { + it('running contains only RUNNING tasks', () => { + const tasks = ALL_STATES.map((s, i) => makeTask(String(i), s)); + const { running } = partitionActivePaneTasks(tasks); + assert.equal(running.length, 1); + assert.equal(running[0].state, 'RUNNING'); + }); + + it('ready contains only READY tasks', () => { + const tasks = ALL_STATES.map((s, i) => makeTask(String(i), s)); + const { ready } = partitionActivePaneTasks(tasks); + assert.equal(ready.length, 1); + assert.equal(ready[0].state, 'READY'); + }); + + it('excludes QUEUED, BLOCKED, PENDING, COMPLETED, FAILED and all other states', () => { + const tasks = ALL_STATES.map((s, i) => makeTask(String(i), s)); + const { running, ready } = partitionActivePaneTasks(tasks); + const allReturned = [...running, ...ready]; + assert.equal(allReturned.length, 2); + assert.ok(allReturned.every(t => t.state === 'RUNNING' || t.state === 'READY')); + }); + + it('returns empty arrays for empty input', () => { + const { running, ready } = partitionActivePaneTasks([]); + assert.deepEqual(running, []); + assert.deepEqual(ready, []); + }); + + it('handles multiple RUNNING tasks sorted by created_at ascending', () => { + const tasks = [ + makeTask('b', 'RUNNING', '2024-01-01T00:02:00Z'), + makeTask('a', 'RUNNING', '2024-01-01T00:01:00Z'), + makeTask('c', 'RUNNING', '2024-01-01T00:03:00Z'), + ]; + const { running } = partitionActivePaneTasks(tasks); + assert.equal(running.length, 3); + assert.equal(running[0].id, 'a'); + assert.equal(running[1].id, 'b'); + assert.equal(running[2].id, 'c'); + }); + + it('handles multiple READY tasks sorted by created_at ascending', () => { + const tasks = [ + makeTask('y', 'READY', '2024-01-01T00:02:00Z'), + makeTask('x', 'READY', '2024-01-01T00:01:00Z'), + ]; + const { ready } = partitionActivePaneTasks(tasks); + assert.equal(ready.length, 2); + assert.equal(ready[0].id, 'x'); + assert.equal(ready[1].id, 'y'); + }); + + it('returns both sections independently when both states present', () => { + const tasks = [ + makeTask('r1', 'RUNNING', '2024-01-01T00:01:00Z'), + makeTask('d1', 'READY', '2024-01-01T00:02:00Z'), + makeTask('r2', 'RUNNING', '2024-01-01T00:03:00Z'), + ]; + const { running, ready } = partitionActivePaneTasks(tasks); + assert.equal(running.length, 2); + assert.equal(ready.length, 1); + }); +}); -- cgit v1.2.3