"""The t7log bundle (goal 41 T7, ux-notes structural bet 3): the task page <-> event log unification. The two surfaces are ONE investigation flow: the task page mounts the log as its "Event log" tab (the same task-log-feed partial the standalone /tasks/{id}/log route renders), the entrypoint publishes both slots, and no link to the log opens a new tab any more. Fixture-driven like the pages bundle: a task + session are inserted as rows, events land through the push wire (/api/saddle/chunks), and the assertions read page HTML, entrypoint slot state and the SSE stream. """ from harness import * # noqa: F401,F403 — the shared wire helpers # ============================================================================= # Bundle: t7log — task page <-> event log unification. # ============================================================================= def run(ctx): auth, fake, fake_id, fake_token = ctx.auth, ctx.fake, ctx.fake_id, ctx.fake_token print("== B1: the task page embeds the log as its second tab ==") # A working task with one session, then a real event through the # push wire (the same path the daemon writes). conn = db() conn.execute( "insert into tasks (project_id, title, status, priority, created_at)" " values (1, 'T7 unified log task', 'working', 5," " strftime('%s','now'))") task_id = conn.execute("select max(id) from tasks").fetchone()[0] conn.execute( "insert into sessions (task_id, saddle_id, started_at)" " values (?, ?, strftime('%s','now'))", (task_id, fake_id)) conn.commit() session_id = conn.execute("select max(id) from sessions").fetchone()[0] conn.close() status, body = saddle_api("POST", "/api/saddle/chunks", fake_token, { "session_id": session_id, "events": [{"seq": 101, "kind": "comment", "actor": "user:admin", "payload": {"body": "T7 probe comment", "author": "user:admin"}}]}) check(status == 200 and int(body.get("stored") or 0) == 1, "the fixture event stores through the push wire") # The tab markup: Spry's CSS tabs (radios first, then .tab-list # labels, then .tab-panels), Overview checked by default. task_page = http_get_page(auth, "/tasks/%d" % task_id) check('class="tabs task-tabs"' in task_page, "the task page carries the tab strip") check(task_page.index('id="task-view-overview"') < task_page.index('class="tab-list"') < task_page.index('class="tab-panels"'), "the radios lead the tab markup (Spry's nth-of-type contract)") check('id="task-view-overview" checked' in task_page, "Overview is the default (checked) tab") check('' in task_page and '' in task_page, "the tab list names Overview and Event log") check('class="task-overview-panel"' in task_page and 'class="task-log-panel"' in task_page, "the panels carry the overview and the log") print("== B2: one shared shell, both surfaces ==") # The SAME partial ships both pages: the interactive hooks the # client engine binds appear exactly once on each, and the # compiled shell markup (the partial's) rides both. task_page = http_get_page(auth, "/tasks/%d" % task_id) log_page = http_get_page(auth, "/tasks/%d/log" % task_id) for surface, name in ((task_page, "the task page"), (log_page, "the log page")): check('class="log-shell"' in surface and "data-log-filter" in surface, "%s renders the shared log shell" % name) # Hook multiplicity: each interactive hook appears exactly as # many times as the ONE shared shell carries it (5 filter # buttons, 1 feed/more/search/jump) — proving neither surface # ships a second, hand-copied shell. for hook, times in (("data-log-feed", 1), ("data-log-more", 1), ("data-log-filter", 5), ("data-log-search", 1), ("data-log-jump", 1)): check(task_page.count(hook) == times and log_page.count(hook) == times, "the %s hook appears exactly %d time(s) on both surfaces" % (hook, times)) print("== B3: no log link opens a new tab any more ==") # The Progress cluster's control is a