| 1 | import re |
| 2 | from pathlib import Path |
| 3 | |
| 4 | |
| 5 | ROOT = Path(__file__).resolve().parents[1] |
| 6 | READMES = { |
| 7 | "English": ROOT / "README.md", |
| 8 | "Français": ROOT / "README.fr.md", |
| 9 | "Deutsch": ROOT / "README.de.md", |
| 10 | "Español": ROOT / "README.es.md", |
| 11 | "Português (Brasil)": ROOT / "README.pt-BR.md", |
| 12 | "日本語": ROOT / "README.ja.md", |
| 13 | "简体中文": ROOT / "README.zh-CN.md", |
| 14 | } |
| 15 | |
| 16 | |
| 17 | def _navigation(current: str) -> str: |
| 18 | return " | ".join( |
| 19 | label if label == current else f"[{label}]({path.name})" |
| 20 | for label, path in READMES.items() |
| 21 | ) |
| 22 | |
| 23 | |
| 24 | def _code_blocks(text: str) -> list[str]: |
| 25 | return re.findall(r"```[^\n]*\n(.*?)```", text, flags=re.DOTALL) |
| 26 | |
| 27 | |
| 28 | def _code_commands(text: str) -> list[str]: |
| 29 | return [ |
| 30 | line |
| 31 | for block in _code_blocks(text) |
| 32 | for line in block.splitlines() |
| 33 | if line and not line.lstrip().startswith("#") |
| 34 | ] |
| 35 | |
| 36 | |
| 37 | def _relative_link_targets(text: str) -> set[str]: |
| 38 | destinations = re.findall(r"\]\(([^)]+)\)", text) |
| 39 | return { |
| 40 | destination.split("#", 1)[0] |
| 41 | for destination in destinations |
| 42 | if destination |
| 43 | and not destination.startswith(("#", "http://", "https://", "mailto:")) |
| 44 | } |
| 45 | |
| 46 | |
| 47 | def _external_link_targets(text: str) -> set[str]: |
| 48 | return { |
| 49 | destination |
| 50 | for destination in re.findall(r"\]\(([^)]+)\)", text) |
| 51 | if destination.startswith(("http://", "https://")) |
| 52 | } |
| 53 | |
| 54 | |
| 55 | def test_readme_translations_preserve_structure_and_commands() -> None: |
| 56 | english = READMES["English"].read_text(encoding="utf-8") |
| 57 | expected_code_commands = _code_commands(english) |
| 58 | expected_code_fences = english.count("```") |
| 59 | expected_external_links = _external_link_targets(english) |
| 60 | expected_table_structure = [ |
| 61 | line.count("|") for line in english.splitlines() if line.startswith("|") |
| 62 | ] |
| 63 | expected_ordered_items = len(re.findall(r"^\d+\. ", english, flags=re.MULTILINE)) |
| 64 | |
| 65 | for label, path in READMES.items(): |
| 66 | text = path.read_text(encoding="utf-8") |
| 67 | lines = text.splitlines() |
| 68 | assert lines[0] == "# /last30days" |
| 69 | assert lines[2] == _navigation(label) |
| 70 | assert text.count("```") == expected_code_fences |
| 71 | assert _code_commands(text) == expected_code_commands |
| 72 | assert _external_link_targets(text) == expected_external_links |
| 73 | assert [line.count("|") for line in lines if line.startswith("|")] == ( |
| 74 | expected_table_structure |
| 75 | ) |
| 76 | assert len(re.findall(r"^\d+\. ", text, flags=re.MULTILINE)) == expected_ordered_items |
| 77 | assert not re.search(r"(?:ZXQ|XXQ|ZZQ|ZZZ)\d", text) |
| 78 | assert not re.search(r"^#", text, flags=re.MULTILINE) |
| 79 | assert not re.search(r"\]\([^)]+\)\]", text) |
| 80 | |
| 81 | |
| 82 | def test_readme_translation_relative_links_exist() -> None: |
| 83 | for path in READMES.values(): |
| 84 | for target in _relative_link_targets(path.read_text(encoding="utf-8")): |
| 85 | assert (ROOT / target).exists(), f"{path.name}: missing relative link target {target}" |
| 86 |