feat(P2): dedicated S&P theme + render pipeline + CI workflow (REQ-239..243)
Create nova-sp-theme.css — S&P Global Energy Marp theme (Red/Black/White palette applied to all slide chrome: backgrounds, headers/footers, pagination, tables, blockquotes, code blocks). Create render_slides.sh — end-to-end pipeline: mermaid PNGs + Marp HTML/PPTX. Create slides.yml CI workflow — auto-renders on docs/presentations/ changes. Create test_slides_pipeline.py — 12 tests (theme CSS, Marp frontmatter, script, workflow, .mmd/.png parity, README retired-deck cleanup). Update Marp frontmatter: theme: nova-sp + footer v1.20. Fix presentations/README.md directory layout (remove retired decks). Re-render HTML + PPTX with S&P theme. ---ci--- project: acdl phase: 2 milestone: v1.20 status: execute requirements: [REQ-239, REQ-240, REQ-241, REQ-242, REQ-243] ---/ci---
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
"""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"
|
||||
Reference in New Issue
Block a user