Files
Ken 389a8fb655 refactor: extract parseA2UIBlocks to lib/a2ui.ts; remove unused import re
- Extract parseA2UIBlocks() from A2UIRenderer.tsx into its own
  frontend/src/lib/a2ui.ts module to satisfy react-refresh/only-export-
  components ESLint rule (A2UIRenderer should only export React components)
- Update ChatPanel.tsx and test files to import from lib/a2ui
- Split ChatPanel.test.tsx mock: separate vi.mock for lib/a2ui
  (parseA2UIBlocks) and A2UIRenderer (component only)
- Remove unused 'import re' from tests/test_runtime_html.py (ruff F401)

All 216 frontend tests and 242 bash tests still pass. TypeScript and
Vite builds are clean. Remaining ESLint warnings (react-hooks/set-state-
in-effect in MCPAppRenderer and useSessions) are pre-existing patterns
not introduced by Phase 3.
2026-05-26 21:14:10 +00:00

308 lines
9.9 KiB
Python

"""
Tests for bundle/apps/runtime.html
Verifies the visual artifact runtime HTML file exists with all required
CDN references, script structures, and message protocol implementations
as specified in task-5.
"""
import os
import pytest
HTML_FILE = os.path.join(
os.path.dirname(__file__),
"..",
"bundle",
"apps",
"runtime.html",
)
@pytest.fixture(scope="module")
def html_content():
assert os.path.exists(HTML_FILE), f"runtime.html not found at {HTML_FILE}"
with open(HTML_FILE) as f:
return f.read()
class TestFileExists:
def test_runtime_html_exists(self):
assert os.path.exists(HTML_FILE), f"runtime.html not found at {HTML_FILE}"
def test_is_complete_html_document(self, html_content):
assert (
"<!DOCTYPE html>" in html_content
or "<!doctype html>" in html_content.lower()
)
assert "<html" in html_content
assert "</html>" in html_content
class TestCDNLibraries:
"""Verify all required CDN libraries are present in the head section."""
def test_tabler_css_cdn(self, html_content):
assert (
"https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta20/dist/css/tabler.min.css"
in html_content
)
def test_leaflet_css_cdn(self, html_content):
assert (
"https://cdn.jsdelivr.net/npm/leaflet@1.9/dist/leaflet.css" in html_content
)
def test_chartjs_cdn(self, html_content):
assert "https://cdn.jsdelivr.net/npm/chart.js@4" in html_content
def test_leaflet_js_cdn(self, html_content):
assert (
"https://cdn.jsdelivr.net/npm/leaflet@1.9/dist/leaflet.js" in html_content
)
def test_react_19_umd(self, html_content):
# React 19 production UMD
assert "react@19" in html_content
assert (
"umd" in html_content.lower()
or "unpkg.com" in html_content
or "cdn.jsdelivr.net" in html_content
)
def test_react_dom_19_umd(self, html_content):
# ReactDOM 19 production UMD
assert "react-dom@19" in html_content
def test_babel_standalone_7(self, html_content):
assert "@babel/standalone@7" in html_content
assert "babel.min.js" in html_content
class TestStyles:
"""Verify required CSS styles."""
def test_transparent_body_background(self, html_content):
# transparent bg
assert "transparent" in html_content
def test_dark_color_scheme(self, html_content):
assert (
"color-scheme: dark" in html_content or "color-scheme:dark" in html_content
)
def test_body_padding_16px(self, html_content):
assert "16px" in html_content
def test_visual_error_class(self, html_content):
assert ".visual-error" in html_content
def test_visual_error_red_color(self, html_content):
# red text color for error class
assert (
"#ff" in html_content.lower()
or "red" in html_content
or "color: #" in html_content
)
def test_visual_error_monospace(self, html_content):
assert "monospace" in html_content
def test_visual_error_pre_wrap(self, html_content):
assert "pre-wrap" in html_content
class TestBodyStructure:
"""Verify body has root div."""
def test_root_div_exists(self, html_content):
assert '<div id="root">' in html_content or "<div id='root'>" in html_content
class TestMcpBridge:
"""Verify mcpBridge object implementation."""
def test_mcp_bridge_declared(self, html_content):
assert "mcpBridge" in html_content
def test_pending_map(self, html_content):
assert "_pending" in html_content
assert "Map" in html_content
def test_send_to_host_method(self, html_content):
assert "sendToHost" in html_content
assert "window.parent.postMessage" in html_content
def test_call_tool_method(self, html_content):
assert "callTool" in html_content
def test_call_tool_jsonrpc_20(self, html_content):
# jsonrpc key may appear as unquoted JS property (jsonrpc:) or quoted ("jsonrpc" / 'jsonrpc')
assert (
'"jsonrpc"' in html_content
or "'jsonrpc'" in html_content
or "jsonrpc:" in html_content
)
assert '"2.0"' in html_content or "'2.0'" in html_content
def test_call_tool_method_name(self, html_content):
assert "tools/call" in html_content
def test_call_tool_returns_promise(self, html_content):
assert "Promise" in html_content
def test_call_tool_uses_uuid(self, html_content):
# Random UUID for call id
assert (
"crypto" in html_content
or "Math.random" in html_content
or "uuid" in html_content.lower()
)
def test_send_message_method(self, html_content):
assert "sendMessage" in html_content
assert "ui/sendMessage" in html_content
def test_send_message_role_user(self, html_content):
assert "role" in html_content
assert "user" in html_content
def test_resize_method(self, html_content):
assert "resize" in html_content
assert "ui/resize" in html_content
def test_resize_capped_at_800(self, html_content):
assert "800" in html_content
assert "Math.min" in html_content
class TestExecuteVisual:
"""Verify executeVisual function implementation."""
def test_execute_visual_function(self, html_content):
assert "executeVisual" in html_content
def test_sets_visual_data_global(self, html_content):
assert "__VISUAL_DATA__" in html_content
def test_sets_mcp_global(self, html_content):
assert "__MCP__" in html_content
def test_jsx_detection_capital_tag_regex(self, html_content):
# Regex for JSX detection: /<[A-Z]/
assert "<[A-Z]" in html_content
def test_jsx_detection_react_create_element(self, html_content):
assert "React.createElement" in html_content
def test_jsx_detection_type_text_babel(self, html_content):
# The regex pattern in JS source: /type="text\/babel"/ contains text\/babel
assert "text/babel" in html_content or r"text\/babel" in html_content
def test_jsx_path_uses_babel_transform(self, html_content):
assert "Babel.transform" in html_content
assert "react" in html_content
def test_jsx_path_auto_render_app(self, html_content):
assert "App" in html_content
assert "ReactDOM.createRoot" in html_content
def test_html_path_detection(self, html_content):
# HTML path: code contains <[a-z]
assert "<[a-z]" in html_content
def test_html_path_sets_inner_html(self, html_content):
assert "innerHTML" in html_content
def test_html_path_clones_script_tags(self, html_content):
# Clone script elements for HTML path
assert "createElement" in html_content
assert "script" in html_content.lower()
def test_html_path_inline_scripts_wrapped(self, html_content):
# Inline scripts wrapped with data and mcp params
assert "__VISUAL_DATA__" in html_content
assert "__MCP__" in html_content
def test_plain_js_uses_new_function(self, html_content):
assert "new Function" in html_content
def test_plain_js_passes_chart_arg(self, html_content):
assert (
'"Chart"' in html_content
or "'Chart'" in html_content
or "Chart" in html_content
)
def test_plain_js_passes_leaflet_arg(self, html_content):
assert '"L"' in html_content or "'L'" in html_content
def test_auto_resize_via_raf(self, html_content):
assert "requestAnimationFrame" in html_content
def test_auto_resize_scroll_height(self, html_content):
assert "scrollHeight" in html_content
def test_auto_resize_adds_32(self, html_content):
assert "32" in html_content
def test_auto_resize_min_100(self, html_content):
assert "100" in html_content
assert "Math.max" in html_content
def test_error_renders_visual_error_div(self, html_content):
assert "visual-error" in html_content
def test_error_resize_120(self, html_content):
assert "120" in html_content
class TestMessageListener:
"""Verify window message event listener."""
def test_message_event_listener(self, html_content):
assert "addEventListener" in html_content
assert '"message"' in html_content or "'message'" in html_content
def test_pending_response_handler(self, html_content):
# If msg.id in _pending, resolve and delete
assert "_pending" in html_content
assert "resolve" in html_content
assert "delete" in html_content
def test_render_method_handler(self, html_content):
assert '"render"' in html_content or "'render'" in html_content
def test_render_calls_execute_visual(self, html_content):
assert "executeVisual" in html_content
def test_render_params_code(self, html_content):
assert "params" in html_content
assert "code" in html_content
def test_legacy_tool_input_method(self, html_content):
assert "tool/input" in html_content
def test_legacy_tool_input_triggers_render(self, html_content):
# Legacy tool/input with code param should call executeVisual
assert "tool/input" in html_content
assert "executeVisual" in html_content
class TestInitialHandshake:
"""Verify initial ui/initialize handshake is sent on load."""
def test_handshake_sent_on_load(self, html_content):
assert "ui/initialize" in html_content
def test_handshake_runtime_visual_artifact(self, html_content):
assert "visual-artifact" in html_content
def test_handshake_version_1_0_0(self, html_content):
assert "1.0.0" in html_content
def test_handshake_uses_send_to_host(self, html_content):
assert (
"sendToHost" in html_content or "window.parent.postMessage" in html_content
)