from __future__ import annotations import importlib.util import json from pathlib import Path import pytest from deerflow.skills.parser import parse_skill_file from deerflow.skills.storage.local_skill_storage import LocalSkillStorage from deerflow.skills.types import SkillCategory from deerflow.skills.validation import _validate_skill_frontmatter BACKEND_ROOT = Path(__file__).resolve().parents[1] SKILL_ROOT = BACKEND_ROOT / "skills" / "public" / "bbc-page-generator" SCRIPT_PATH = SKILL_ROOT / "scripts" / "render_bbc_page.py" LEAD_PROMPT_PATH = ( BACKEND_ROOT / "packages" / "harness" / "deerflow" / "agents" / "lead_agent" / "prompt.py" ) def _load_renderer(): spec = importlib.util.spec_from_file_location( "bbc_page_renderer", SCRIPT_PATH, ) assert spec and spec.loader module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def test_skill_is_discoverable_and_valid() -> None: valid, error, _metadata = _validate_skill_frontmatter(SKILL_ROOT) assert valid, error skill = parse_skill_file(SKILL_ROOT / "SKILL.md", SkillCategory.PUBLIC) assert skill is not None assert skill.name == "bbc-page-generator" assert "BBC" in skill.description discovered = LocalSkillStorage( host_path=str(SKILL_ROOT.parent.parent) ).load_skills(enabled_only=True) discovered_by_name = {item.name: item for item in discovered} assert ( discovered_by_name["bbc-page-generator"].category == SkillCategory.PUBLIC ) assert discovered_by_name["bbc-page-generator"].enabled is True lead_prompt_source = LEAD_PROMPT_PATH.read_text(encoding="utf-8") assert "/mnt/skills/public/bbc-page-generator/SKILL.md" in lead_prompt_source assert "专用 BBC 路由" in lead_prompt_source def test_default_json_renders_editable_bbc_html(tmp_path: Path) -> None: renderer = _load_renderer() data = json.loads( (SKILL_ROOT / "assets" / "default-data.json").read_text( encoding="utf-8" ) ) summary = renderer.validate_document(data) template = (SKILL_ROOT / "assets" / "bbc-template.html").read_text( encoding="utf-8" ) runtime = (SKILL_ROOT / "assets" / "bbc-runtime.js").read_text( encoding="utf-8" ) html = renderer.render_document(data, template, runtime) assert summary["modules"] == list(renderer.MODULES) assert summary["counts"]["focus"] >= 1 assert renderer.DATA_MARKER not in html assert renderer.RUNTIME_MARKER not in html assert 'id="bbcPageData"' in html assert 'id="leadLayout"' in html assert 'id="mediaGrid"' in html assert 'id="audioPlay"' in html assert 'id="liveModal"' in html assert 'id="openSettings"' in html assert 'id="moduleTabs"' in html assert 'id="applyJson"' in html assert "uploadJsonMedia" in html assert "installInlineMediaReplace" in html assert "dfInlineMediaInput" in html assert 'input.accept = "image/*,video/*"' in html assert 'button.title = "上传图片或视频并替换"' in html assert "active.expected" not in html assert "previous.posterUrl" not in html assert "decorateBuiltInVisuals" in html assert 'data-video-id="' in html assert "replaceBuiltInVisuals" not in html assert "readAsDataURL" in html assert '"brandLetters": [' in html assert "BBC 新闻 — 看见正在发生的世界" in html assert '"bbc-page-data-v1:" + pageFingerprint' in html assert "const readStorage = (key)" in html assert "window.localStorage.getItem" in html assert "const cachedData = localStorage.getItem" not in html assert 'BBC · 智能体公共新闻" in titled_html assert "BBC 新闻 — 看见正在发生的世界" not in titled_html data["lead"]["title"] = "" escaped_html = renderer.render_document(data, template, runtime) assert "