"""Tests for sprint 49 timestamp footer polish — v0.50.97. Covers: - #680: assistant messages now render footer timestamps, not just user messages - messages from prior days render a fuller date+time string in the footer - timestamp/action footer stays attached to visible response segments only - user and assistant footer chrome is hover-only by default - last assistant turn keeps cumulative usage visible and reveals time/actions on hover - unchanged historical messages preserve their original timestamps across turns """ import pathlib import re from api.streaming import _restore_reasoning_metadata REPO = pathlib.Path(__file__).parent.parent UI_JS = (REPO / "static" / "ui.js").read_text(encoding="utf-8") UI_CSS = (REPO / "static" / "style.css").read_text(encoding="utf-8") STREAMING_PY = (REPO / "api" / "streaming.py").read_text(encoding="utf-8") def test_footer_timestamp_is_not_limited_to_user_messages(): assert "const timeHtml = tsTime ?" in UI_JS assert "isUser && tsTime" not in UI_JS, ( "Timestamp footer should no longer be gated to user messages only" ) def test_footer_timestamp_uses_richer_format_for_older_messages(): assert "function _formatMessageFooterTimestamp(tsVal)" in UI_JS assert "month:'short'" in UI_JS or 'month: "short"' in UI_JS assert "day:'numeric'" in UI_JS or 'day: "numeric"' in UI_JS assert "hour:'numeric'" in UI_JS or 'hour: "numeric"' in UI_JS assert "minute:'2-digit'" in UI_JS or 'minute: "2-digit"' in UI_JS def test_timestamp_footer_stays_on_visible_response_segments(): assert "if(hasVisibleBody){" in UI_JS assert 'seg.insertAdjacentHTML(\'beforeend\', `${filesHtml}
${bodyHtml}
${footHtml}`);' in UI_JS, ( "Footer timestamp should stay attached to visible response segments" ) assert "else if(!thinkingText){" in UI_JS, ( "Thinking-only assistant segments should still avoid rendering a footer" ) def test_footer_chrome_is_hover_only_for_user_and_assistant_messages(): assert ".msg-row[data-role=\"user\"] .msg-foot {\n opacity: 0;" in UI_CSS assert ".msg-row[data-role=\"user\"]:hover .msg-foot," in UI_CSS assert ".msg-row[data-role=\"assistant\"] .msg-foot," in UI_CSS assert ".assistant-turn .msg-foot {" in UI_CSS assert ".assistant-turn:hover .msg-foot," in UI_CSS def test_last_assistant_keeps_usage_visible_and_reveals_time_and_actions_on_hover(): assert "usage.className='msg-usage-inline';" in UI_JS assert "targetFoot.classList.add('msg-foot-with-usage');" in UI_JS assert "targetFoot.insertBefore(usage, targetFoot.firstChild);" in UI_JS assert ".assistant-turn .msg-foot-with-usage," in UI_CSS assert ".msg-row[data-role=\"assistant\"] .msg-foot-with-usage {\n opacity: 1;" in UI_CSS assert ".msg-foot-with-usage .msg-time,\n.msg-foot-with-usage .msg-actions {\n opacity: 0;" in UI_CSS assert ".assistant-turn:hover .msg-foot-with-usage .msg-time," in UI_CSS def test_restore_reasoning_metadata_preserves_existing_timestamps(): assert "def _restore_reasoning_metadata(previous_messages, updated_messages):" in STREAMING_PY assert "if prev_msg.get('timestamp') and not cur_msg.get('timestamp'):" in STREAMING_PY assert "cur_msg['timestamp'] = prev_msg['timestamp']" in STREAMING_PY assert "elif prev_msg.get('_ts') and not cur_msg.get('_ts') and not cur_msg.get('timestamp'):" in STREAMING_PY assert "cur_msg['_ts'] = prev_msg['_ts']" in STREAMING_PY def test_restore_reasoning_metadata_preserves_timestamp_on_reload_for_unchanged_messages(): previous_messages = [ {"role": "user", "content": "hello", "timestamp": 1713500000}, {"role": "assistant", "content": "world", "timestamp": 1713500060}, ] updated_messages = [ {"role": "user", "content": "hello"}, {"role": "assistant", "content": "world"}, ] restored = _restore_reasoning_metadata(previous_messages, updated_messages) assert restored[0]["timestamp"] == 1713500000 assert restored[1]["timestamp"] == 1713500060 def test_restore_reasoning_metadata_does_not_preserve_timestamp_for_changed_messages(): previous_messages = [ {"role": "user", "content": "hello", "timestamp": 1713500000}, {"role": "assistant", "content": "old answer", "timestamp": 1713500060}, ] updated_messages = [ {"role": "user", "content": "hello"}, {"role": "assistant", "content": "new answer"}, ] restored = _restore_reasoning_metadata(previous_messages, updated_messages) assert restored[0]["timestamp"] == 1713500000 assert "timestamp" not in restored[1]