"""meta-ribbon.js DOM 渲染契约(静态 / 基于读文件 + 简易 string assert)。 完整 DOM 行为由 E2E browser 测试覆盖;本测试锁结构与字符串。 """ from pathlib import Path RIBBON_JS = Path("src/opensquilla/gateway/static/js/views/chat/meta-ribbon.js") PREFLIGHT_JS = Path("src/opensquilla/gateway/static/js/views/chat/meta-preflight.js") ARTIFACT_CARD_JS = Path("src/opensquilla/gateway/static/js/views/chat/artifact-card.js") META_RUN_HISTORY_JS = Path("src/opensquilla/gateway/static/js/views/chat/meta-run-history.js") RIBBON_CSS = Path("src/opensquilla/gateway/static/css/views/chat-meta-ribbon.css") CHAT_JS = Path("src/opensquilla/gateway/static/js/views/chat.js") INDEX_HTML = Path("src/opensquilla/gateway/templates/index.html") def _read_text(path: Path) -> str: return path.read_text(encoding="utf-8") def test_ribbon_module_exists(): assert RIBBON_JS.exists() text = _read_text(RIBBON_JS) for name in ("createRibbon", "updateStep", "completeRun", "renderRibbon"): assert f"function {name}" in text, f"missing function {name}" def test_ribbon_exposes_window_global(): text = _read_text(RIBBON_JS) assert "root.MetaRibbon" in text or "window.MetaRibbon" in text def test_ribbon_state_classes_are_normalized(): text = _read_text(RIBBON_JS) assert "step.state = normalizeStateClass(stepStateEvent.state);" in text assert "state.runOutcome = normalizeRunOutcome(completedEvent.outcome);" in text assert "function normalizeRunOutcome" in text assert "outcome === 'ok'" in text assert "const safeStepState = normalizeStateClass(s.state);" in text assert 'class="chip ${safeStepState}"' in text assert "STATE_GLYPH[state]" in text assert "'substituted'," in text assert "paused: 'Ⅱ'" in text assert "cancelled: '−'" in text for name in ("createRibbon", "updateStep", "completeRun", "renderRibbon"): assert name in text, f"window.MetaRibbon missing {name}" def test_ribbon_glyph_table_covers_all_states(): text = _read_text(RIBBON_JS) for state in ( "pending", "running", "succeeded", "failed", "skipped", "substituted", "paused", "cancelled", ): assert f"{state}:" in text, f"STATE_GLYPH missing {state}" def test_ribbon_css_has_chip_state_classes(): text = _read_text(RIBBON_CSS) for cls in ( "chip.pending", "chip.running", "chip.succeeded", "chip.failed", "chip.skipped", "chip.substituted", "chip.paused", "chip.cancelled", ): assert cls in text, f"CSS missing {cls}" def test_ribbon_renders_accessible_compact_run_bar(): js = _read_text(RIBBON_JS) css = _read_text(RIBBON_CSS) for token in ( "meta-ribbon-shell", "meta-ribbon-icon", "meta-ribbon-current", "meta-ribbon-track", "meta-ribbon-fill", "progressPercent", "role=\"progressbar\"", "aria-valuenow", "aria-valuemin=\"0\"", "aria-valuemax=\"100\"", "aria-live=\"polite\"", "aria-expanded", ): assert token in js, f"ribbon render missing {token}" assert "MetaSkill" not in js assert "const counterText = copy.counter(headerIndex, state.total);" in js for token in ( "max-width: min(760px, 100%)", "margin: 10px auto", ".meta-ribbon-track", ".meta-ribbon-fill", "height: 2px", "box-shadow: 0 1px 2px", "prefers-reduced-motion: reduce", ): assert token in css, f"ribbon CSS missing polished progress treatment {token}" assert "min-width: 6px" not in css, "0% progress should not render a fake leading fill" def test_preflight_uses_checkpoint_language_not_generic_confirmation(): text = _read_text(PREFLIGHT_JS) assert "我准备运行" in text assert "开始运行" in text assert "Confirmation" not in text def test_preflight_chrome_follows_request_language(): text = _read_text(PREFLIGHT_JS) for token in ( "detectLanguage", "preflightCopy", "state.language", "Before running", "I understood", "Start", "Cancel", "Use defaults", "Required", "Please fill this in.", ): assert token in text, f"preflight missing localized chrome token {token}" def test_ribbon_chrome_follows_request_language(): text = _read_text(RIBBON_JS) for token in ( "language: detectLanguage(announce.language", "ribbonCopy(state.language)", "Collapse/expand steps", "Step ${index} of ${total}", "第 ${index} / ${total} 步", "Running…", "Retry whole run", "Switch meta-skill…", "View error details", ): assert token in text, f"ribbon missing localized chrome token {token}" def test_preflight_collects_missing_fields_inline_instead_of_editing_composer(): preflight = _read_text(PREFLIGHT_JS) chat = _read_text(CHAT_JS) for token in ( "renderMissingFields", "collectFieldValues", "validateRequiredFields", "renderCollapsed", "setSubmitting", "setError", "meta-preflight-field", "data-field-name", "使用默认值运行", "取消", "知道了", "Dismiss", ): assert token in preflight, f"preflight missing inline field behavior {token}" assert 'data-action="edit"' not in preflight assert "补充到输入框" not in preflight assert "补充:" not in chat assert "renderCollapsed(card, detail, 'running')" in chat assert "setSubmitting(card, true)" in chat assert "setError(card, err" in chat # The Vue 3 frontend (opensquilla-webui) is the active control UI; its # index.html is a Vite entry that bundles modules rather than listing #