"""REQ-239..243 (v1.20): S&P theme + slide render pipeline tests. Validates: - The Marp deck frontmatter references nova-sp-theme.css - The CSS file contains the S&P colors (#D6002A, #1B1B1B) - The mermaid theme JSON contains the S&P colors - Every .mmd has a corresponding .png - The render_slides.sh script exists and is executable - The CI workflow file exists """ import re from pathlib import Path import pytest ROOT = Path(__file__).resolve().parent.parent PRESENTATIONS = ROOT / "docs" / "presentations" ASSETS = PRESENTATIONS / "assets" THEME_CSS = ASSETS / "nova-sp-theme.css" THEME_JSON = ASSETS / "mmd" / "sp-theme.json" MARP_DECK = PRESENTATIONS / "nova-no-humans-platform-marp.md" RENDER_SCRIPT = ROOT / "scripts" / "render_slides.sh" SLIDES_WORKFLOW = ROOT / ".github" / "workflows" / "slides.yml" def test_sp_theme_css_exists(): """REQ-239: nova-sp-theme.css exists.""" assert THEME_CSS.is_file(), f"theme CSS not found: {THEME_CSS}" def test_sp_theme_css_has_snp_colors(): """REQ-239: CSS contains S&P Red and Black.""" css = THEME_CSS.read_text() assert "#D6002A" in css, "S&P Red (#D6002A) missing from theme CSS" assert "#1B1B1B" in css, "S&P Black (#1B1B1B) missing from theme CSS" def test_sp_theme_json_has_snp_colors(): """The mermaid theme JSON also has S&P colors.""" json_text = THEME_JSON.read_text() assert "#D6002A" in json_text, "S&P Red missing from mermaid theme" assert "#1B1B1B" in json_text, "S&P Black missing from mermaid theme" def test_marp_deck_uses_sp_theme(): """REQ-239: Marp deck frontmatter references nova-sp-theme.css.""" text = MARP_DECK.read_text() # The frontmatter is between the first two --- fm_match = re.match(r'^---\n(.*?)\n---', text, re.DOTALL) assert fm_match, "Marp frontmatter not found" frontmatter = fm_match.group(1) assert "nova-sp" in frontmatter, \ "Marp deck does not reference nova-sp theme" def test_marp_deck_not_using_default_theme(): """The Marp deck must not use 'theme: default'.""" text = MARP_DECK.read_text() fm_match = re.match(r'^---\n(.*?)\n---', text, re.DOTALL) assert fm_match, "Marp frontmatter not found" frontmatter = fm_match.group(1) assert "theme: default" not in frontmatter, \ "Marp deck still uses 'theme: default' — should use nova-sp-theme.css" def test_render_slides_script_exists(): """REQ-240: render_slides.sh exists and is executable.""" assert RENDER_SCRIPT.is_file(), "render_slides.sh not found" import os assert os.access(RENDER_SCRIPT, os.X_OK), "render_slides.sh not executable" def test_render_slides_script_renders_mermaid(): """REQ-240: render_slides.sh renders mermaid diagrams.""" text = RENDER_SCRIPT.read_text() assert "mermaid-cli" in text or "mmdc" in text, \ "render_slides.sh does not invoke mermaid-cli" assert "sp-theme.json" in text, \ "render_slides.sh does not reference sp-theme.json" def test_render_slides_script_renders_marp(): """REQ-240: render_slides.sh renders Marp HTML + PPTX.""" text = RENDER_SCRIPT.read_text() assert "marp-cli" in text, "render_slides.sh does not invoke marp-cli" assert ".html" in text, "render_slides.sh does not produce HTML" assert ".pptx" in text, "render_slides.sh does not produce PPTX" def test_slides_ci_workflow_exists(): """REQ-241: CI workflow for slides exists.""" assert SLIDES_WORKFLOW.is_file(), "slides.yml workflow not found" def test_slides_ci_workflow_triggers_on_presentations(): """REQ-241: CI workflow triggers on docs/presentations/ changes.""" text = SLIDES_WORKFLOW.read_text() assert "docs/presentations" in text, \ "slides.yml does not trigger on docs/presentations/" assert "render_slides.sh" in text, \ "slides.yml does not invoke render_slides.sh" def test_every_mmd_has_png(): """REQ-240: every .mmd file has a corresponding .png.""" mmd_dir = ASSETS / "mmd" png_dir = ASSETS / "png" if not mmd_dir.is_dir(): pytest.skip("no .mmd directory") mmd_files = sorted(mmd_dir.glob("*.mmd")) assert len(mmd_files) > 0, "no .mmd files found" missing = [] for mmd in mmd_files: png = png_dir / f"{mmd.stem}.png" if not png.is_file(): missing.append(mmd.name) assert not missing, f"PNGs missing for: {missing}" def test_readme_no_retired_decks(): """REQ-243: presentations README does not list retired decks.""" readme = (PRESENTATIONS / "README.md").read_text() assert "how-the-platform-works" not in readme, \ "README still references retired 'how-the-platform-works' deck" assert "the-developer-experience" not in readme, \ "README still references retired 'the-developer-experience' deck"