diff --git a/bundle/apps/runtime.html b/bundle/apps/runtime.html
new file mode 100644
index 0000000..e810bae
--- /dev/null
+++ b/bundle/apps/runtime.html
@@ -0,0 +1,202 @@
+
+
+
+ ' in html_content or "
" 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