epub2md/tests/test_links.py
dschlueter 0dad1f7b2d fix: doppelte Tabellen-Anker und Bild-Links im Linkcheck
- Komplexe Tabellen mit verlinkter id erzeugten den Anker zweimal
  (<a id> und <table id>), also ungültiges HTML.
- Der Linkcheck übersah das äußere Ziel von Bild-Links
  [![alt](img)](ziel) und startete Links an escapten \[-Klammern.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:24:47 +02:00

83 lines
4.2 KiB
Python

from conftest import PNG, SVG, Book, Doc
from epub2md.linkcheck import check_links
def link_book() -> Book:
a = ('<h1 id="start">A</h1><p>Siehe <a href="b.xhtml">Kapitel B</a>, <a href="b.xhtml#ziel">Ziel in B</a>, '
'<a href="#unten">unten</a> und <a href="gibtsnicht.xhtml#x">kaputt</a>.</p>'
'<h1>Zweites H1</h1><p id="unten">Unten.</p>')
b = '<h1>B</h1><p>Text</p><p id="ziel">Ziel</p><p><a href="a.xhtml#start">zurück</a></p>'
return Book(docs=[Doc("a.xhtml", a), Doc("b.xhtml", b)])
def test_internal_links_are_rewritten(convert):
run = convert(link_book())
names = [p.name for p in run.chapters()]
assert names == ["001-a.md", "002-zweites-h1.md", "003-b.md"]
a = run.chapter("001")
assert "[Kapitel B](003-b.md)" in a
assert "[Ziel in B](003-b.md#ziel)" in a
assert "[unten](002-zweites-h1.md#unten)" in a # anchor ended up in the split-off chapter
assert "kaputt" in a and "gibtsnicht" not in a # unresolved: text kept, no broken link
assert '<a id="ziel"></a>Ziel' in run.chapter("003")
assert "[zurück](001-a.md#start)" in run.chapter("003")
assert run.report["unresolved_links"][0]["href"] == "gibtsnicht.xhtml#x"
assert any(w["code"] == "unresolved-link" for w in run.report["warnings"])
assert run.report["output_link_check"]["broken_count"] == 0
readme = (run.out / "README.md").read_text(encoding="utf-8")
assert "[A](chapters/001-a.md)" in readme
def test_images_are_extracted_once_with_relative_links(convert):
body = ('<h1>Bilder</h1><figure id="abb1"><img src="img/foto%20eins.png" alt="Foto" title="Titel"/>'
'<figcaption>Abbildung 1: Ein Foto.</figcaption></figure>'
'<p><img src="img/foto%20eins.png" alt="Nochmal"/> <img src="img/grafik.svg" alt=""/></p>'
'<figure><img src="img/grafik.svg"/><figcaption>Nur Unterschrift</figcaption></figure>')
book = Book(docs=[Doc("c.xhtml", body)], media={"img/foto eins.png": PNG, "img/grafik.svg": SVG})
run = convert(book)
text = run.chapter("001")
assert '![Foto](../media/images/0001-foto.png "Titel")' in text
assert "*Abbildung 1: Ein Foto.*" in text
assert "![Nochmal](../media/images/0001-foto.png)" in text
assert "![Nur Unterschrift](../media/images/0002-grafik.svg)" in text
images = sorted(p.name for p in (run.out / "media" / "images").iterdir())
assert images == ["0001-foto.png", "0002-grafik.svg"]
assert (run.out / "media/images/0002-grafik.svg").read_bytes() == SVG
assert run.report["output_link_check"]["broken_count"] == 0
def test_image_strategies(convert):
book = Book(docs=[Doc("c.xhtml", '<h1>B</h1><p><img src="img/a.png" alt="a"/></p>')], media={"img/a.png": PNG})
flat = convert(book, "--images", "flat")
assert "![a](../media/a.png)" in flat.chapter("001")
orig = convert(book, "--images", "original-path")
assert "![a](../media/img/a.png)" in orig.chapter("001")
assert (orig.out / "media/img/a.png").exists()
none = convert(book, "--no-extract-media")
assert not (none.out / "media").exists()
assert "Bild: a" in none.chapter("001")
def test_link_checker_detects_problems():
files = {
"chapters/001-a.md": '# A\n\n<a id="x"></a>Text [ok](002-b.md#y) [kaputt](002-b.md#nein) [fehlt](003.md) '
"![bild](../media/images/a.png) [ext](https://example.org)\n\n```\n[nicht](geprüft.md)\n```\n",
"chapters/002-b.md": '# B\n\n<a id="y"></a>Text [rück](001-a.md#x) [heading](#b)\n',
}
broken = check_links(files, {"media/images/a.png"})
assert {b["link"] for b in broken} == {"002-b.md#nein", "003.md"}
def test_link_checker_ignores_escaped_brackets():
"""An escaped \\[ must not start a link that swallows the following paragraphs."""
files = {
"a.md": '\\[»\\] Hinweis\n\n<a id="x"></a>Text [kaputt](fehlt.md) und \\[1\\]\n\n[ok](#x)\n',
}
broken = check_links(files, set())
assert [b["link"] for b in broken] == ["fehlt.md"]
def test_link_checker_handles_image_links():
files = {"a.md": "[![Abb.](bild.png)](fehlt.md) [![ok](bild.png)](#x) <a id=\"x\"></a>\n"}
broken = check_links(files, {"bild.png"})
assert [b["link"] for b in broken] == ["fehlt.md"]