feat: frontend turn_html_replace SSE handler for regenerate live-swap (T86)
This commit is contained in:
@@ -174,3 +174,74 @@ def test_chat_html_includes_stop_streaming_script(client, tmp_path):
|
||||
assert "stop-streaming" in body or "isStreaming" in body
|
||||
# Cancel route reference must be wired so the Stop button can call it.
|
||||
assert "/turns/cancel" in body
|
||||
|
||||
|
||||
def test_chat_html_has_turn_html_replace_listener(client, tmp_path):
|
||||
"""T86: the chat shell wires a JS handler for the ``turn_html_replace``
|
||||
SSE event so regenerate-driven swaps land in connected tabs without a
|
||||
page refresh.
|
||||
|
||||
This is a presence / string-check test: it verifies the handler is
|
||||
embedded in the rendered template but does NOT drive a real browser
|
||||
(no headless runner is wired into this test environment). The end-to-
|
||||
end behaviour — receiving the event over SSE and replacing the prior
|
||||
turn's DOM node — is therefore not exercised here; a manual smoke
|
||||
check or future browser-driven test would close that gap.
|
||||
"""
|
||||
_seed_chat(tmp_path / "test.db")
|
||||
response = client.get("/chats/chat_bot_a")
|
||||
assert response.status_code == 200
|
||||
body = response.text
|
||||
# The handler must be wired against the SSE event name the backend
|
||||
# publishes (chat.services.regenerate -> "turn_html_replace").
|
||||
assert "turn_html_replace" in body
|
||||
# Confirm the handler reads the JSON payload's ``supersedes_id`` so
|
||||
# it can locate the prior turn node. The exact lookup mechanism may
|
||||
# vary, but the field name is part of the contract with the backend.
|
||||
assert "supersedes_id" in body
|
||||
|
||||
|
||||
def test_rendered_turn_html_includes_event_id(client, tmp_path):
|
||||
"""T86 follow-up: the chat-detail Jinja loop stamps
|
||||
``id="turn-<event_id>"`` on every rendered turn DIV. Without this id
|
||||
the ``turn_html_replace`` SSE handler's ``getElementById`` lookup
|
||||
misses, falls through to ``insertAdjacentHTML('beforeend', …)``, and
|
||||
the regenerated turn appears APPENDED instead of swapped in-place
|
||||
(rendering the primary handler path dead code — exactly the gap the
|
||||
T86 reviewer flagged).
|
||||
|
||||
Seed a user_turn + assistant_turn, GET the chat page, and assert the
|
||||
response body carries both turns' event ids on the wrapper DIVs.
|
||||
"""
|
||||
db_path = tmp_path / "test.db"
|
||||
_seed_chat(db_path)
|
||||
with open_db(db_path) as conn:
|
||||
ut_id = append_event(
|
||||
conn,
|
||||
kind="user_turn",
|
||||
payload={
|
||||
"chat_id": "chat_bot_a",
|
||||
"prose": "hello bot",
|
||||
"segments": [],
|
||||
},
|
||||
)
|
||||
at_id = append_event(
|
||||
conn,
|
||||
kind="assistant_turn",
|
||||
payload={
|
||||
"chat_id": "chat_bot_a",
|
||||
"speaker_id": "bot_a",
|
||||
"text": "Hi there.",
|
||||
"truncated": False,
|
||||
"user_turn_id": ut_id,
|
||||
},
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
response = client.get("/chats/chat_bot_a")
|
||||
assert response.status_code == 200
|
||||
body = response.text
|
||||
# Both seeded turns must carry ``id="turn-<event_id>"`` so the SSE
|
||||
# in-place swap can find them.
|
||||
assert f'id="turn-{ut_id}"' in body
|
||||
assert f'id="turn-{at_id}"' in body
|
||||
|
||||
Reference in New Issue
Block a user