epub2md/tests/test_qa2_open.py

123 lines
6.4 KiB
Python
Raw Permalink Normal View History

"""Regression tests for the remaining findings of the independent QA (QA2-09 … QA2-19)."""
import re
from conftest import PNG, SVG, Book, Doc
M = 'xmlns="http://www.w3.org/1998/Math/MathML"'
def html_table(text: str) -> str:
return text[text.index("<table"): text.index("</table>")]
def test_qa2_09_math_and_notes_in_html_tables_are_html(convert):
body = ('<h1>T</h1><table><tr><td colspan="2">Kopf</td></tr>'
f'<tr><td><math {M}><mfrac><mi>a</mi><mi>b</mi></mfrac></math></td>'
'<td><span class="InlineEquation"><img src="f.png" alt="$$x^2$$"/></span>'
'<a epub:type="noteref" href="#n1">1</a></td></tr></table>'
'<aside epub:type="footnote" id="n1"><p>Notiz <em>wichtig</em>.</p></aside>')
for mode in ("auto", "tex"):
run = convert(Book(docs=[Doc("c.xhtml", body)], media={"f.png": PNG}), "--math", mode, "--footnotes", "inline")
table = html_table(run.chapter("001"))
visible = re.sub(r'"[^"]*"', '""', table) # ignore attribute values (alt texts)
assert "<math" in table and "$" not in visible and "\\(" not in visible # renderable MathML
assert '<img src="../media/images/' in table and 'alt="$$x^2$$"' in table # formula image kept
assert "(Anmerkung:" not in table and '<sup><a href="#fn-001">1</a></sup>' in table
assert '<a id="fn-001"></a>' in run.chapter("001")
assert run.report["output_link_check"]["broken_count"] == 0
def test_qa2_10_book_readme_recommends_pdf_via_html(convert):
"""Pandoc drops raw HTML (tables, <a id>) when going Markdown -> LaTeX; the HTML route keeps both."""
run = convert(Book(docs=[Doc("a.xhtml", "<h1>A</h1><p>x</p>")]))
readme = (run.out / "README.md").read_text(encoding="utf-8")
book = r"--lua-filter \S+/pandoc/book\.lua" # table of contents after the front matter (both steps)
assert re.search(rf"pandoc \S+\.md -s {book} -o \S+\.html && pandoc \S+\.html {book} --pdf-engine=xelatex -o \S+\.pdf",
readme)
def test_qa2_11_code_language_hint(convert):
py = "def f(x):\n return x\n\nclass A:\n pass"
body = "<h1>C</h1>" + "".join(f"<pre>{py}\n# {i}</pre><pre>Ergebnis: {i}</pre>" for i in range(10))
run = convert(Book(docs=[Doc("c.xhtml", body)]))
hints = [i for i in run.report["infos"] if i["code"] == "code-language-hint"]
assert hints and "--code-language python" in hints[0]["message"]
def test_qa2_12_strict_aborts_on_undecodable_xhtml(convert, tmp_path):
import zipfile
from conftest import build_epub
path = build_epub(tmp_path / "g.epub", Book(docs=[Doc("a.xhtml", "<h1>A</h1><p>x</p>")]))
with zipfile.ZipFile(path) as z:
entries = {n: z.read(n) for n in z.namelist()}
entries["OEBPS/a.xhtml"] = b'<?xml version="1.0" encoding="utf-8"?><html><body><p>\xff\xfe\x00\x81 kaputt</p></body></html>'
with zipfile.ZipFile(path, "w") as z:
for n, d in entries.items():
z.writestr(n, d)
assert convert(path).rc == 0
assert convert(path, "--strict").rc == 3
def test_qa2_13_suspicious_media_is_skipped_not_fatal(convert, tmp_path):
import zipfile
from conftest import build_epub
path = build_epub(tmp_path / "b.epub", Book(docs=[Doc("a.xhtml", '<h1>A</h1><p><img src="big.png" alt="Riesig"/> Text</p>')],
media={"big.png": PNG}))
with zipfile.ZipFile(path) as z:
entries = [(i, z.read(i)) for i in z.infolist()]
with zipfile.ZipFile(path, "w", compression=zipfile.ZIP_DEFLATED) as z:
for info, data in entries:
z.writestr(info.filename, b"\0" * 60_000_000 if info.filename == "OEBPS/big.png" else data)
run = convert(path)
assert run.rc == 0
assert "Riesig" in run.chapter("001") and "big.png" not in run.chapter("001")
assert any(e["code"] == "media-skipped" for e in run.report["errors"])
assert run.report["output_link_check"]["broken_count"] == 0
def test_qa2_14_formula_class_on_img_itself(convert):
body = '<h1>F</h1><p>Wert <img class="inlineequation" src="f.png" alt="Summe a plus b"/> ist.</p>'
run = convert(Book(docs=[Doc("c.xhtml", body)], media={"f.png": PNG}))
assert [f["origin"] for f in run.report["formulas"]["items"]] == ["image"]
assert any(w["code"] == "formula-image" for w in run.report["warnings"])
def test_qa2_15_diagnostics_count_repeats_and_html_table_formulas(convert):
img = '<span class="equation"><img src="f.png" alt="Summe"/></span>'
body = (f"<h1>F</h1><p>{img} und {img}</p>"
f'<table><tr><td colspan="2"><math {M}><mi>x</mi></math></td></tr><tr><td>a</td><td>b</td></tr></table>')
run = convert(Book(docs=[Doc("c.xhtml", body)], media={"f.png": PNG}))
w = [w for w in run.report["warnings"] if w["code"] == "formula-image"]
assert len(w) == 1 and w[0]["count"] == 2
assert run.report["formulas"]["count"] == 3
def test_qa2_16_non_ascii_ids_are_kept(convert):
body = '<h1 id="über-uns">Über</h1><p id="été">Sommer</p><p><a href="#été">S</a> <a href="#über-uns">Ü</a></p>'
text = convert(Book(docs=[Doc("c.xhtml", body)])).chapter("001")
assert '<a id="été"></a>' in text and '<a id="über-uns"></a>' in text
assert "[S](#été)" in text
def test_qa2_17_no_empty_fragment_links_and_no_epub_type_attr(convert):
body = ('<h1>K</h1><p>Text<a epub:type="noteref" href="#n">1</a></p>'
'<table><tr><td colspan="2"><a epub:type="noteref" href="#n">1</a></td></tr><tr><td>a</td><td>b</td></tr></table>'
'<aside epub:type="footnote" id="n"><p><a href="c.xhtml">↩</a> Note.</p></aside>')
text = convert(Book(docs=[Doc("c.xhtml", body)]), "--footnotes", "keep-links").chapter("001")
assert "](#)" not in text
assert ' type="noteref"' not in text
def test_qa2_18_undefined_entities_are_reported(convert):
body = "<h1>E</h1><p>A &unbekannt; B &auch;</p>"
run = convert(Book(docs=[Doc("c.xhtml", body)]))
w = [w for w in run.report["warnings"] if w["code"] == "undefined-entity"]
assert w and "unbekannt" in w[0]["message"] and "auch" in w[0]["message"]
def test_qa2_19_adjacent_placeholders_without_media(convert):
body = '<h1>B</h1><p><img src="a.png" alt="Eins"/><img src="b.svg" alt="Zwei"/></p>'
text = convert(Book(docs=[Doc("c.xhtml", body)], media={"a.png": PNG, "b.svg": SVG}), "--no-extract-media").chapter("001")
assert "**" not in text
assert re.search(r"Bild: Eins.*Bild: Zwei", text)