"""REQ-274: tests for `scripts/render_pptx.py` — the structured, editable python-pptx deck produced alongside the MARP-rendered PPTX. The python-pptx deck is a native OOXML presentation: real text boxes, native tables, embedded pictures, and italic benefit callouts. These tests are offline (no AWS, no network) and assert the structural properties of the committed `*-python.pptx` artifact. """ from pathlib import Path from typing import cast import pytest from pptx import Presentation from pptx.enum.shapes import MSO_SHAPE_TYPE from pptx.presentation import Presentation as PresentationT from pptx.shapes.autoshape import Shape ROOT = Path(__file__).resolve().parent.parent PRESENTATIONS = ROOT / "docs" / "presentations" MARPT_DECK = PRESENTATIONS / "nova-autonomous-cloud-delivery-marp.md" PYTHON_PPTX = PRESENTATIONS / "nova-autonomous-cloud-delivery-python.pptx" # Title slide + 20 main slides + 1 appendix slide. EXPECTED_SLIDE_COUNT = 22 @pytest.fixture(scope="module") def prs() -> PresentationT: """Load the committed python-pptx deck once for the whole module.""" assert PYTHON_PPTX.is_file(), f"python-pptx PPTX not found: {PYTHON_PPTX}" return Presentation(str(PYTHON_PPTX)) def _slide_titles(prs: PresentationT) -> list[str]: """Return the first non-empty text-frame line per slide (the title).""" titles: list[str] = [] for slide in prs.slides: for shape in slide.shapes: if not shape.has_text_frame: continue text = cast(Shape, shape).text_frame.text.strip() if not text: continue # The title is the first non-empty line of the first non-empty # text frame we find on the slide. first_line = text.split("\n")[0].strip() if first_line: titles.append(first_line) break else: titles.append("") return titles def test_slide_count(prs: PresentationT): """REQ-269/274: the python-pptx deck has 22 slides (title + 20 main + 1 appendix).""" assert len(prs.slides) == EXPECTED_SLIDE_COUNT, \ f"expected {EXPECTED_SLIDE_COUNT} slides, got {len(prs.slides)}" def test_title_slide_colors(prs: PresentationT): """REQ-269: the title slide (slide 0) has a solid-filled background shape carrying the S&P Red (#D6002A) brand color (the title slide is a red-bar-on-black layout).""" title_slide = prs.slides[0] red_found = False black_found = False for shape in title_slide.shapes: fill = getattr(shape, "fill", None) if fill is None: continue try: if fill.type != 1: # MSO_FILL.SOLID continue except Exception: continue rgb = str(fill.fore_color.rgb).upper() if rgb == "D6002A": red_found = True if rgb == "1B1B1B": black_found = True assert red_found, \ "title slide has no solid-fill shape with S&P Red (#D6002A)" def test_expected_slide_titles(prs: PresentationT): """REQ-269/274: spot-check that key slide titles match the markdown deck (The Problem, Nova's Vision, Recap + Ask).""" titles = _slide_titles(prs) # Build a flat lowercase concatenation for substring checks. flat = " | ".join(titles).lower() expected = [ "the problem", "nova's vision", "recap + ask", ] missing = [t for t in expected if t not in flat] assert not missing, \ f"missing expected slide titles in python-pptx deck: {missing}; " \ f"found titles: {titles}" def test_table_rendering(prs: PresentationT): """REQ-269: a slide with a table (the RACI slide) has a native PPTX table shape (GraphicFrame with has_table=True).""" table_slides = [] for idx, slide in enumerate(prs.slides): for shape in slide.shapes: if shape.shape_type == MSO_SHAPE_TYPE.TABLE or getattr( shape, "has_table", False ): table_slides.append(idx) break assert table_slides, \ "no slide in the python-pptx deck has a native PPTX table shape" def test_image_embedding(prs: PresentationT): """REQ-269: a slide with an image (the Platform Pipeline slide) has a native PPTX picture shape.""" picture_slides = [] for idx, slide in enumerate(prs.slides): for shape in slide.shapes: if shape.shape_type == MSO_SHAPE_TYPE.PICTURE: picture_slides.append(idx) break assert picture_slides, \ "no slide in the python-pptx deck has a native PPTX picture shape" def test_benefit_callout_present(prs: PresentationT): """REQ-269/274: at least one slide has an italic text run (the benefit callout, rendered as italic body text by render_pptx.py).""" italic_runs = 0 for slide in prs.slides: for shape in slide.shapes: if not shape.has_text_frame: continue for paragraph in cast(Shape, shape).text_frame.paragraphs: for run in paragraph.runs: if run.font.italic and run.text.strip(): italic_runs += 1 assert italic_runs > 0, \ "no italic text runs found in the python-pptx deck (benefit callout)"