import base64 import hashlib import re from pathlib import Path ROOT = Path(__file__).resolve().parents[2] WEB = ROOT / "apps" / "web-console" def source(name: str) -> str: content = (WEB / name).read_text(encoding="utf-8") if name == "index.html": content += "\n" + (WEB / "src" / "App.vue").read_text(encoding="utf-8") return content def test_shadcn_semantic_theme_tokens_drive_the_console() -> None: styles = source("styles.css") console = source("console-v2.css") speech = source("speech-input.css") expected_tokens = { "--background: oklch(0.968 0.019 244)", "--foreground: oklch(0.29 0.042 252)", "--card-foreground: oklch(0.34 0.032 250)", "--primary: oklch(0.57 0.19 258)", "--primary-foreground: oklch(0.99 0.004 245)", "--destructive: oklch(0.61 0.19 24)", "--sidebar-primary: oklch(0.57 0.19 258)", "--background: oklch(0.2 0.035 252)", "--foreground: oklch(0.94 0.02 245)", "--primary: oklch(0.7 0.15 252)", "--sidebar-primary: oklch(0.7 0.15 252)", } token_lines = {line.strip() for line in styles.replace(";", "").splitlines()} assert expected_tokens <= token_lines assert ".dark {" in styles assert "--v2-accent: var(--primary)" in console assert "--v2-sidebar: var(--sidebar)" in console assert "color: var(--destructive)" in speech def test_theme_asset_versions_are_consistent_with_the_pwa_shell() -> None: main = source("src/main.js") service_worker = source("service-worker.js") for source_asset, cached_asset in ( ('import "../styles.css"', "/styles.css?v=dual-seat-24-dialog-size"), ('import "../console-v2.css"', "/console-v2.css?v=delivery-state-69"), ('import "../self-extension.css"', "/self-extension.css?v=pet-settings-54-dialog-size"), ('import "../speech-input.css"', "/speech-input.css?v=sensevoice-7"), ('import "../codepen-glass-theme.css"', "/codepen-glass-theme.css?v=creative-cloud-6-performance"), ): assert source_asset in main assert f'"{cached_asset}"' in service_worker assert 'import "./styles/design-system.css"' in main assert 'const CACHE_VERSION = "cognitive-agent-pad-v329-mode-preview"' in service_worker def test_reference_glass_theme_covers_the_complete_workspace() -> None: html = source("index.html") theme = source("codepen-glass-theme.css") app = source("app.js") assert '' in html assert 'const key = "cognitive-agent.glass-theme"' in html assert 'id="glassThemeToggle"' in html assert 'data-theme-icon="sun"' in html assert 'data-theme-icon="moon"' in html assert 'const GLASS_THEME_STORAGE_KEY = "cognitive-agent.glass-theme"' in app assert "localStorage.setItem(GLASS_THEME_STORAGE_KEY, normalized)" in app assert "document.cookie = `${GLASS_THEME_STORAGE_KEY}=${normalized}" in app assert 'new CustomEvent("cognitive-agent:theme-change"' in app for contract in ( "--glass-shell:", "--glass-panel:", "--glass-card:", "backdrop-filter: blur(22px) saturate(1.25)", ".main-panel > .v2-mode-rail", "#assistantDrawer", ".joint-twin-shell > .twin-band", "#v2ExtensionPanel .v2-extension-sync-mount", ".v2-pet-card", ".v2-pet-presence-card", ".v2-pet-settings-dialog", ".mode-gallery", ".v2-nfc-dialog", ".energy-monitor-page", ): assert contract in theme assert 'html[data-glass-theme="light"] body.console-v2' in theme assert ".glass-theme-toggle" in theme assert "width: 100vw !important" in theme assert "height: 100dvh !important" in theme assert "margin: 0 !important" in theme assert "border: 0 !important" in theme assert "border-radius: 0 !important" in theme def test_theme_bootstrap_is_allowed_by_csp_without_unsafe_inline() -> None: html = (WEB / "index.html").read_text(encoding="utf-8") inline_script = re.search(r"", html, flags=re.DOTALL) assert inline_script is not None digest = base64.b64encode( hashlib.sha256(inline_script.group(1).encode("utf-8")).digest() ).decode("ascii") server = (ROOT / "src" / "cognitive_agent" / "server.py").read_text( encoding="utf-8" ) assert f"'sha256-{digest}'" in server assert "'unsafe-inline'" not in server def test_reference_theme_uses_one_transform_only_floating_card_contract() -> None: theme = source("codepen-glass-theme.css") for contract in ( "--glass-card-radius:", "--glass-card-lift:", "--glass-card-rest-shadow:", "--glass-card-float-shadow:", ".assistant-dispatched-controls", ".assistant-mode-draft", ".mode-gallery-card", ".mode-editor-command-card", ".v2-device-card", ".v2-pet-card", ".energy-kpi", "transform: translateY(var(--glass-card-lift))", "@media (prefers-reduced-motion: reduce)", "transition: none !important", ): assert contract in theme def test_dark_theme_uses_one_glass_scrim_for_every_modal_backdrop() -> None: theme = source("codepen-glass-theme.css") for contract in ( "--glass-dialog-scrim:", "--glass-dialog-scrim-filter:", ".mode-gallery-backdrop", ".mode-editor-backdrop", ".energy-monitor-layer", ".device-power-backdrop", ".v2-extension-dialog::backdrop", "background: var(--glass-dialog-scrim) !important", "backdrop-filter: var(--glass-dialog-scrim-filter) !important", ): assert contract in theme def test_vehicle_twin_icons_and_nested_overlays_use_semantic_tokens() -> None: styles = source("styles.css") console = source("console-v2.css") extension = source("self-extension.css") expected_console_rules = { ".console-v2 .v2-twin-hotspot-label", ".console-v2 .v2-twin-device-popover", ".console-v2 .v2-reasoning-field", ".console-v2 .device-power-drawer", ".console-v2 .v2-sync-log li[data-log-status=\"error\"] > span", "background: var(--primary)", "background: var(--popover)", } expected_overlay_rules = { ".mode-gallery-backdrop", ".mode-editor-backdrop", ".gesture-drawer", "background: var(--card)", "border-color: var(--border)", } expected_extension_rules = { ".v2-extension-dialog::backdrop", ".v2-gscu-tcp-dialog", ".v2-nfc-dialog", ".v2-extension-toast.error", ".console-v2 #v2ExtensionPanel .v2-extension-gateway > button", "background: var(--destructive)", } assert all(rule in console for rule in expected_console_rules) assert all(rule in styles for rule in expected_overlay_rules) assert all(rule in extension for rule in expected_extension_rules) def test_operator_controls_share_status_and_typography_tokens() -> None: html = source("index.html") styles = source("styles.css") console = source("console-v2.css") script = source("console-v2.js") for token in ( "--status-online:", "--status-offline:", "--status-info:", "--status-success:", "--status-warning:", "--status-error:", "--heading-foreground:", "--body-foreground:", "--subtle-foreground:", "--text-caption:", "--text-control:", "--text-input:", "--text-label:", ): assert token in styles assert '
' in html assert 'id="modeRailToggle"' in html assert 'aria-controls="modeRailContent"' in html assert 'id="modeRailContent"' in html assert 'role="region"' in html assert 'aria-label="功能模式列表"' in html assert 'AI 新建' in html assert "模拟下发" in html assert "真实下发" in html assert 'id="simulationMode"' not in html assert 'class="v2-sync-link-stack"' not in script assert '.mode-option[data-connection-state="online"]::after' in styles assert '.mode-option[data-connection-state="offline"]::after' in styles assert "font-size: var(--text-control) !important" in console def test_wired_can_link_requires_real_device_connection_evidence() -> None: app = source("app.js") assert 'runtime?.state_sync?.state === "online"' in app assert '["online", "ready"].includes(runtime?.state_sync?.state)' not in app assert 'wiredCanProbe: { passenger: false, vehicle: false }' in app assert 'updateWiredCanProbe("passenger", true, requestedTransport)' in app assert 'updateWiredCanProbe("vehicle", true, requestedTransport)' in app assert 'updateWiredCanProbe("passenger", false, requestedTransport)' in app assert 'updateWiredCanProbe("vehicle", false, requestedTransport)' in app assert "resetTransportConnectionEvidence();" in app assert 'label.textContent = `${transportLabel} · 信号链路断开`' in app def test_command_fields_use_distinct_semantic_colors() -> None: styles = source("styles.css") console = source("console-v2.css") for token in ( "--command-channel:", "--command-source:", "--command-delivered:", "--command-pending:", "--command-category:", "--command-control:", "--command-frame:", ): assert token in styles for mapping in ( "color: var(--command-channel)", "color: var(--command-source)", "color: var(--command-pending)", "color: var(--command-delivered)", "color: var(--command-category)", "color: var(--command-control)", "color: var(--command-frame)", ): assert mapping in console def test_sidebar_command_and_transport_status_use_theme_semantics() -> None: console = source("console-v2.js") design_system = source("src/styles/design-system.css") assert '"v2-sidebar-footer v2-control-link-card"' in console assert 'transport === "wireless_mqtt" ? "无线 CAN" : "有线 CAN"' in console assert 'MQTT ${transportOnline ? "已连接" : "未连接"}' in console assert 'footer.dataset.state = presentationState' in console assert 'attributeFilter: ["aria-pressed", "class", "data-connection-state", "title"]' in console assert ".v2-control-link-card" in design_system assert 'data-state="online"' in design_system assert 'data-state="offline"' in design_system assert ".v2-can-command-actual code" in design_system assert "color: var(--card-foreground) !important" in design_system def test_function_modes_use_an_inline_accessible_collapsible() -> None: html = source("index.html") app = source("app.js") console = source("console-v2.css") assert '
' in html assert 'id="modeRailSelection">静态模式' in html assert 'id="modeGalleryToggle"' in html assert 'class="mode-rail-identity"' in html assert 'AI 新建' in html assert 'aria-expanded="true"' in html assert '
None: console = source("console-v2.js") assert "function normalizeDeviceReadout(value)" in console assert "function deviceFeedbackAvailable(deviceId)" in console assert 'return rows.map(([label]) => [label, "-"])' in console assert 'const waiting = rows.some(([, value]) => value === "-")' in console assert '"cognitive-agent:vehicle-feedback-state"' in console def test_final_theme_layers_do_not_reintroduce_raw_ui_colors() -> None: sections = ( source("styles.css").split("/* Final semantic theme layer", 1)[1], source("console-v2.css").split("/* Final semantic theme cascade", 1)[1], source("self-extension.css").split( "/* Final semantic theme layer for extension surfaces", 1 )[1], ) raw_color = re.compile(r"#[0-9a-fA-F]{3,8}\b|rgba?\(") for section in sections: assert not raw_color.search(section) def test_all_five_output_modules_are_covered_by_the_final_theme_layer() -> None: console = source("console-v2.css") final_layer = console.split("/* Final semantic theme cascade", 1)[1] for selector in ( ".joint-twin-shell.v2-output-page", ".decision-list li", ".result-workspace #panel-config.config-panel", ".plan-panel .frame-summary", ".execution-summary", ): assert selector in final_layer assert "--config-accent: var(--primary)" in final_layer assert "background: var(--card)" in final_layer assert "border-color: var(--border)" in final_layer def test_ai_light_blue_theme_closes_after_legacy_color_layers() -> None: console = source("console-v2.css") assistant = source("assistant-chat.css") extension = source("self-extension.css") assert console.rfind("/* AI assistant light-blue final override") > console.rfind( "/* Final semantic theme cascade" ) assert assistant.rfind("/* Shared AI light-blue palette") > assistant.rfind( "@media (prefers-reduced-motion: reduce)" ) assert extension.rfind("/* AI light-blue closure") > extension.rfind( "/* Final semantic theme layer for extension surfaces" ) assert "color: var(--body-foreground)" in console assert "color: var(--subtle-foreground)" in console assert "background: var(--status-success-soft)" in assistant assert "background: var(--primary)" in assistant