summaryrefslogtreecommitdiff
path: root/web/test/event-timeline.test.mjs
blob: 20ef61a1b19de11beeca6a3ea7023470cbb5db78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// event-timeline.test.mjs — Unit tests for the event timeline component.
//
// Run with: node --test web/test/event-timeline.test.mjs

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { formatEventText, renderEventTimeline, fetchTaskEvents } from '../app.js';

function makeDoc() {
  return {
    createElement(tag) {
      return {
        tag,
        className: '',
        textContent: '',
        children: [],
        appendChild(child) { this.children.push(child); return child; },
      };
    },
  };
}

describe('formatEventText', () => {
  it('formats state_change with from/to', () => {
    assert.equal(
      formatEventText({ kind: 'state_change', payload: { from: 'QUEUED', to: 'RUNNING' } }),
      'State: QUEUED → RUNNING',
    );
  });

  it('formats summary text', () => {
    assert.equal(
      formatEventText({ kind: 'summary', payload: { text: 'did the thing' } }),
      'Summary: did the thing',
    );
  });

  it('formats clarification_request and answer', () => {
    assert.equal(
      formatEventText({ kind: 'clarification_request', payload: { text: 'which branch?' } }),
      'Question: which branch?',
    );
    assert.equal(
      formatEventText({ kind: 'clarification_answer', payload: { answer: 'main' } }),
      'Answer: main',
    );
  });

  it('formats subtask_spawned by name', () => {
    assert.equal(
      formatEventText({ kind: 'subtask_spawned', payload: { name: 'write tests', subtask_id: 'x' } }),
      'Spawned subtask: write tests',
    );
  });

  it('falls back to kind for unknown events', () => {
    assert.equal(formatEventText({ kind: 'mystery', payload: {} }), 'mystery');
  });

  it('handles null/empty defensively', () => {
    assert.equal(formatEventText(null), '');
    assert.equal(formatEventText({ kind: 'agent_message', payload: { message: 'hi' } }), 'hi');
  });
});

describe('renderEventTimeline', () => {
  it('renders one item per event with actor and text', () => {
    const events = [
      { kind: 'state_change', actor: 'system', payload: { from: 'PENDING', to: 'QUEUED' } },
      { kind: 'summary', actor: 'agent', payload: { text: 'done' } },
    ];
    const ul = renderEventTimeline(events, makeDoc());
    assert.equal(ul.className, 'event-timeline');
    assert.equal(ul.children.length, 2);
    // each item has [actor, text]
    assert.equal(ul.children[0].children[0].textContent, 'system');
    assert.equal(ul.children[0].children[1].textContent, 'State: PENDING → QUEUED');
    assert.equal(ul.children[1].children[1].textContent, 'Summary: done');
  });

  it('renders an empty-state item for no events', () => {
    const ul = renderEventTimeline([], makeDoc());
    assert.equal(ul.children.length, 1);
    assert.equal(ul.children[0].className, 'event-timeline__empty');
  });

  it('returns null when doc is null', () => {
    assert.equal(renderEventTimeline([], null), null);
  });
});

describe('fetchTaskEvents', () => {
  it('returns parsed array on success', async () => {
    const stub = async () => ({ ok: true, json: async () => [{ kind: 'summary', payload: {} }] });
    const events = await fetchTaskEvents('t1', stub);
    assert.equal(events.length, 1);
  });

  it('returns [] on non-ok response', async () => {
    const stub = async () => ({ ok: false, json: async () => ({}) });
    assert.deepEqual(await fetchTaskEvents('t1', stub), []);
  });

  it('returns [] when fetch throws', async () => {
    const stub = async () => { throw new Error('network'); };
    assert.deepEqual(await fetchTaskEvents('t1', stub), []);
  });

  it('returns [] when no fetch implementation is available', async () => {
    assert.deepEqual(await fetchTaskEvents('t1', null), []);
  });
});