Some checks failed
Multi-MDX Regression (IMP-91) / multi-mdx-regression (push) Failing after 22s
u1 KNOWN_AXES tuple gains slide_css entry in src/user_overrides_io.py
(snake_case parity with image_overrides); round-trip test extends
to 6 axes.
u2 src/mdx_normalizer.py surfaces nested slide_overrides.css from the
MDX frontmatter into the normalize_mdx_content return dict; absent
key -> {}, non-string css drops. 4 unit cases in tests/test_mdx_normalizer.py
(present / absent / non-string / title-only).
u3 src/slide_css_injector.py NEW (88 lines) mirrors the
inject_image_overrides_style contract from src/image_id_stamper.py:
marker pair <!--IMP45-SLIDE-CSS:OPEN--> / <!--IMP45-SLIDE-CSS:CLOSE-->,
idempotent re-injection, </head> > <body> > document-start three-tier
fallback, empty/None -> unchanged. 8 fixtures in
tests/test_slide_css_injector.py mirror test_image_id_stamper.py.
u4 run_phase_z2_mvp1 accepts override_slide_css: Optional[str] = None;
None -> frontmatter slide_overrides.css fallback. Step 13 calls
inject_slide_css after image override injection and before the
final.html disk write, so CLI/CI/regression renders observe the same
backend artifact.
u5 argparse adds mutually-exclusive --override-slide-css TEXT (inline
CSS, <style> wrapper optional) and --slide-css-file PATH (UTF-8 read,
fail-closed sys.exit(2) on missing path / decode error / both flags
present). Resolved string is forwarded as override_slide_css kwarg.
6 cases in tests/test_phase_z2_cli_overrides.py (inline / file / both
/ missing / non-utf8 / neither).
u6 samples/mdx_batch/04.mdx frontmatter gains slide_overrides.css
block (verbatim of the former MDX04_DEFAULT_OVERRIDE_CSS constant,
no sample/frame gate). Subprocess smoke in
tests/test_phase_z2_slide_css_smoke.py verifies the marker pair and
CSS substring land in final.html.
u7 Front/client removes the sample/frame-gated frontend-only injection:
Home.tsx drops the MDX04_DEFAULT_OVERRIDE_CSS constant and the
sample==="04"+frame==="process_product_two_way" branch (-28 lines);
SlideCanvas.tsx drops the iframe contentDocument.head injection of
that prop (-14 lines). Live preview now reads backend final.html only.
u8 tests/regression/fixtures/89a_pre_baseline_sha.json 04.mdx entry
resyncs to the live SHA ddb6bf2f... / 28042 bytes (overwrites the
earlier 5-byte-drift d02c76fd... / 28047). Other entries untouched.
Note: 01.mdx baseline drift (ad6f16a3... / 29089 -> live f26a7fac...
/ 29084) predates this branch and is split to a follow-up issue per
the closed-issue fresh validation rule.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
108 lines
3.7 KiB
Python
108 lines
3.7 KiB
Python
"""IMP-45 (#74) u2 — frontmatter ``slide_overrides`` surfacing.
|
|
|
|
Covers ``src.mdx_normalizer.normalize_mdx_content`` and the helper
|
|
``_extract_slide_overrides``. The Stage 2 plan enumerates four cases:
|
|
|
|
1. Present — nested ``slide_overrides.css`` survives normalization verbatim.
|
|
2. Absent — return dict carries an empty ``slide_overrides`` mapping.
|
|
3. Non-string ``css`` — dropped (fail-closed against typo'd YAML shapes).
|
|
4. Title-only frontmatter — no ``slide_overrides`` key in metadata → ``{}``.
|
|
|
|
Scope-lock: this unit only adds the new key to the return dict. The four
|
|
pre-existing return keys (``clean_text``/``title``/``images``/``popups``/
|
|
``tables``/``sections``) are asserted unchanged at the structural level.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from src.mdx_normalizer import _extract_slide_overrides, normalize_mdx_content
|
|
|
|
|
|
_CSS_BLOCK = "<style>.f29b__col_right { width: 320px; }</style>"
|
|
|
|
|
|
def _mdx_with_frontmatter(fm_body: str, body: str = "# 제목\n\n본문 한 줄.\n") -> str:
|
|
return f"---\n{fm_body}---\n{body}"
|
|
|
|
|
|
# -- case 1: present (nested css string survives verbatim) ------------------
|
|
|
|
|
|
def test_normalize_surfaces_nested_slide_overrides_css():
|
|
raw = _mdx_with_frontmatter(
|
|
"title: 04 sample\n"
|
|
"slide_overrides:\n"
|
|
f" css: \"{_CSS_BLOCK}\"\n"
|
|
)
|
|
|
|
result = normalize_mdx_content(raw)
|
|
|
|
assert result["slide_overrides"] == {"css": _CSS_BLOCK}
|
|
# Other axes unaffected by the new key.
|
|
assert result["title"] == "04 sample"
|
|
assert "본문" in result["clean_text"]
|
|
|
|
|
|
# -- case 2: absent (no slide_overrides key in frontmatter) -----------------
|
|
|
|
|
|
def test_normalize_returns_empty_slide_overrides_when_key_absent():
|
|
raw = _mdx_with_frontmatter("title: 03 sample\n")
|
|
|
|
result = normalize_mdx_content(raw)
|
|
|
|
assert result["slide_overrides"] == {}
|
|
# Confirm key is always present (callers can rely on .get without default).
|
|
assert "slide_overrides" in result
|
|
|
|
|
|
# -- case 3: non-string css (fail-closed drop) ------------------------------
|
|
|
|
|
|
def test_normalize_drops_non_string_css_under_slide_overrides():
|
|
# YAML list under .css should be dropped; sibling unknown keys survive
|
|
# so the future generalization path (e.g., slide_overrides.js) stays
|
|
# forward-compatible per the Stage 2 plan.
|
|
raw = _mdx_with_frontmatter(
|
|
"title: typo case\n"
|
|
"slide_overrides:\n"
|
|
" css:\n"
|
|
" - .f29b__col_right { width: 320px; }\n"
|
|
" note: experimental sibling\n"
|
|
)
|
|
|
|
result = normalize_mdx_content(raw)
|
|
|
|
assert "css" not in result["slide_overrides"]
|
|
assert result["slide_overrides"].get("note") == "experimental sibling"
|
|
|
|
|
|
# -- case 4: title-only frontmatter (no slide_overrides at all) -------------
|
|
|
|
|
|
def test_normalize_title_only_frontmatter_yields_empty_slide_overrides():
|
|
raw = _mdx_with_frontmatter("title: title only\n")
|
|
|
|
result = normalize_mdx_content(raw)
|
|
|
|
assert result["title"] == "title only"
|
|
assert result["slide_overrides"] == {}
|
|
|
|
|
|
# -- direct helper coverage (defensive against future return-shape drift) ---
|
|
|
|
|
|
def test_extract_slide_overrides_non_mapping_returns_empty_dict():
|
|
# Frontmatter parsers can yield odd types if the user writes
|
|
# ``slide_overrides: 42`` or ``slide_overrides: ".x{}"``. The helper
|
|
# must coerce to ``{}`` rather than raise.
|
|
for bad in (None, 42, "literal string", ["css"]):
|
|
assert _extract_slide_overrides({"slide_overrides": bad}) == {}
|
|
|
|
|
|
def test_extract_slide_overrides_passes_through_unknown_siblings():
|
|
payload = {"slide_overrides": {"css": ".a{}", "js": "console.log(1)"}}
|
|
assert _extract_slide_overrides(payload) == {
|
|
"css": ".a{}",
|
|
"js": "console.log(1)",
|
|
}
|