Files
libretro/tests/test_site_nav.py
T

643 lines
30 KiB
Python

"""Navigation and search.
The 404 page's sidebar read "0 1 2 3 4". A sidebar group label was a link
to its first page ("Handhelds and TV boxes" opened AmberELEC), and a crumb
linked a sibling page. Emulators sat three levels deep under "D to F / D".
Previous and next ran from Sony to Sord and from one tab into another,
and disagreed with the page's own "Next" list. The Reference tab was a flat
list of fifteen. These tests read the generator and, where it is built,
the site.
"playstation bios" found five setups' lists for the console before its own
page: the pages a setup's list moves a console to weighed 1.5, three times a
reference page. The file names a system page holds were the text of a hidden
span, so the search showed them as the excerpt. Start here looped: First
install's "Next steps" pointed back to Get your files, Is it legal? to Common
questions, and both pages had the same page as previous and next. A part
page had no crumb to the page it belongs to and nothing marked in the
sidebar. "psx" or "dc" found nothing, though every platform files the
console under that name. These tests read the generator and, where they
are built, the pages and the site.
"""
from __future__ import annotations
import json
import re
import struct
import sys
import tempfile
import unittest
from html.parser import HTMLParser
from pathlib import Path
import ast
from html import unescape
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
import generate_site # noqa: E402
import sitestarlight # noqa: E402
from common import ( # noqa: E402
list_registered_platforms,
load_platform_config,
load_platform_registry,
)
DIST = REPO_ROOT / "website" / "dist"
ROUTE = REPO_ROOT / "website" / "src" / "routeData.ts"
TAB_ROOTS = {"/retrobios/", "/retrobios/platforms/", "/retrobios/explore/",
"/retrobios/wiki/", "/retrobios/contributing/"}
def _nav() -> list:
platforms = REPO_ROOT / "platforms"
registry = load_platform_registry(str(platforms))
coverages = {
key: {"platform": load_platform_config(key, str(platforms))["platform"]}
for key in list_registered_platforms(str(platforms), include_archived=True)
}
manufacturers = {"Sony": {}, "Sega": {}, "ScummVM": {}}
profiles = {
"duckstation": {"emulator": "DuckStation", "systems": ["sony-playstation"]},
"dolphin": {"emulator": "Dolphin", "systems": ["nintendo-gamecube"]},
"mame": {"emulator": "MAME", "systems": ["arcade"]},
}
return generate_site.generate_nav(coverages, manufacturers, profiles, registry)
def _targets(value: object) -> set[str]:
if isinstance(value, str):
return {value}
if isinstance(value, list):
return set().union(*(_targets(v) for v in value)) if value else set()
return set().union(*(_targets(v) for v in value.values()))
def _present(nav: list) -> set[str]:
return {t for t in _targets(nav) if not t.startswith("http")}
from siteguides import DEVICE_GROUPS # noqa: E402
def _index_anchors(markdown: str) -> set[str]:
"""The heading ids a generated page carries once written."""
_title, title_id, body = sitestarlight.split_title(markdown)
return sitestarlight.heading_ids(sitestarlight.pin_heading_ids(body, title_id))
ANCHORS = {
"platforms/index.md": {sitestarlight.toc_slug(label) for label, _ in DEVICE_GROUPS.values()},
"systems/index.md": {"consoles-and-computers"},
"emulators/index.md": _index_anchors(generate_site.generate_emulators_index({
"duckstation": {"emulator": "DuckStation", "systems": ["sony-playstation"]},
"dolphin": {"emulator": "Dolphin", "systems": ["nintendo-gamecube"]},
"mame": {"emulator": "MAME", "systems": ["arcade"]},
})),
}
class NotFoundPage(unittest.TestCase):
def test_the_404_opens_on_the_first_tab(self):
side = sitestarlight.sidebar(_nav(), _present(_nav()), ANCHORS)
self.assertNotIn("/404", side["options"]["exclude"])
self.assertIn("/404", side["options"]["topics"]["start-here"])
def test_the_404_has_no_contents_no_index_and_no_structured_data(self):
_target, text = sitestarlight.finalize_page(
"404.md", "# Page not found\n\nGone.\n", level="", weight=None,
browser_title="Page not found", description="d",
structured={"@type": "TechArticle"}, terms={})
front = generate_site._front_matter(text)
self.assertIs(front["tableOfContents"], False)
self.assertIs(front["prev"], False)
self.assertIs(front["next"], False)
self.assertIn({"tag": "meta", "attrs": {"name": "robots", "content": "noindex"}}, front["head"])
self.assertNotIn("ld+json", text)
class GroupsAreGroups(unittest.TestCase):
def setUp(self):
nav = _nav()
self.side = sitestarlight.sidebar(nav, _present(nav), ANCHORS)
self.groups = self.side["groups"]
def test_setup_groups_stay_groups_and_point_at_their_section(self):
setup = {key: value for key, value in self.groups.items() if key.startswith("Your setup/")}
self.assertTrue(setup)
for key, group in setup.items():
with self.subTest(group=key):
self.assertFalse(group["prune"])
self.assertTrue(group["href"].startswith("/retrobios/platforms/#"), group["href"])
def test_explore_groups_collapse_to_their_overview(self):
explore = {key: value for key, value in self.groups.items() if key.startswith("Explore/")}
self.assertEqual(explore["Explore/Systems"],
{"href": "/retrobios/systems/", "prune": True, "crumb": True})
self.assertEqual(explore["Explore/Systems/Consoles and computers"]["href"],
"/retrobios/systems/#consoles-and-computers")
self.assertEqual(explore["Explore/Emulators/D"],
{"href": "/retrobios/emulators/#d", "prune": True, "crumb": False})
def test_no_group_points_at_one_of_its_own_pages(self):
"""The overview a group opens on is its own index page or a section of
the page above it, never the first page it lists."""
overview_pages = {sitestarlight.page_url(rel) for rel in _present(_nav())
if rel == "index.md" or rel.endswith("/index.md") or rel == "explore.md"}
for key, group in self.groups.items():
if not group["href"]:
continue
with self.subTest(group=key):
self.assertTrue("#" in group["href"] or group["href"] in overview_pages, group["href"])
def test_the_middleware_reads_the_groups_not_the_first_link(self):
route = ROUTE.read_text(encoding="utf-8")
self.assertNotIn("firstLink", route)
self.assertIn("sidebar.groups", route)
self.assertIn("sidebar.reading", route)
class EmulatorsOneLevelDeep(unittest.TestCase):
def test_initials_hold_pages_not_groups(self):
nav = generate_site._emulator_nav({
"duckstation": {"emulator": "DuckStation", "systems": ["s"]},
"dolphin": {"emulator": "Dolphin", "systems": ["s"]},
})
self.assertEqual(nav, ["emulators/index.md", {"D": [
{"Dolphin": "emulators/dolphin.md"}, {"DuckStation": "emulators/duckstation.md"}]}])
def test_every_initial_has_its_section_on_the_index(self):
"""A letter in the sidebar lands on the section of that letter."""
from common import load_emulator_profiles
profiles = load_emulator_profiles(str(REPO_ROOT / "emulators"))
anchors = _index_anchors(generate_site.generate_emulators_index(profiles))
for group in generate_site._emulator_nav(profiles)[1:]:
(label, _pages), = group.items()
with self.subTest(initial=label):
self.assertIn(sitestarlight.toc_slug(label), anchors)
def test_every_initial_holds_pages(self):
from common import load_emulator_profiles
nav = generate_site._emulator_nav(load_emulator_profiles(str(REPO_ROOT / "emulators")))
for group in nav[1:]:
(label, pages), = group.items()
with self.subTest(initial=label):
self.assertTrue(all(isinstance(next(iter(p.values())), str) for p in pages))
class OneNextPerPage(unittest.TestCase):
def setUp(self):
nav = _nav()
self.runs = sitestarlight.sidebar(nav, _present(nav), ANCHORS)["reading"]
self.links = [[entry["link"] for entry in run] for run in self.runs]
def test_start_here_runs_into_your_setup_and_stops(self):
first = self.links[0]
self.assertEqual(first[0], "/retrobios/")
self.assertIn("/retrobios/platforms/", first)
self.assertNotIn("/retrobios/explore/", first)
def test_each_other_tab_is_read_on_its_own(self):
starts = [run[0] for run in self.links]
self.assertEqual(starts, ["/retrobios/", "/retrobios/explore/", "/retrobios/wiki/",
"/retrobios/contributing/"])
def test_system_emulator_and_platform_pages_are_not_in_the_reading(self):
for link in (link for run in self.links for link in run):
with self.subTest(link=link):
self.assertNotRegex(link, r"^/retrobios/(systems|emulators|platforms)/[^/]+/$")
def test_an_overview_is_named_after_what_it_opens(self):
labels = {entry["link"]: entry["label"] for run in self.runs for entry in run}
self.assertEqual(labels["/retrobios/platforms/"], "Your setup")
self.assertEqual(labels["/retrobios/systems/"], "Systems")
self.assertNotIn("Overview", labels.values())
def test_card_pages_turn_pagination_off(self):
for rel in ("systems/sony.md", "emulators/duckstation.md", "platforms/batocera.md"):
_target, text = sitestarlight.finalize_page(
rel, "# T\n\nBody.\n", level="explore", weight=None, browser_title="T",
description="d", structured={}, terms={})
front = generate_site._front_matter(text)
with self.subTest(page=rel):
self.assertIs(front["prev"], False)
self.assertIs(front["next"], False)
def test_the_next_link_is_the_first_of_next_steps(self):
body = ("# Troubleshooting\n\nText.\n\n## Next steps\n\n"
"- [Common questions](faq.md)\n- [Glossary](glossary.md)\n")
_target, text = sitestarlight.finalize_page(
"wiki/troubleshooting.md", body, level="start", weight=None,
browser_title="Troubleshooting", description="d", structured={}, terms={})
front = generate_site._front_matter(text)
self.assertEqual(front["next"], {"link": "/retrobios/wiki/faq/", "label": "Common questions"})
def test_a_page_without_next_steps_keeps_the_reading_order(self):
_target, text = sitestarlight.finalize_page(
"wiki/faq.md", "# FAQ\n\nText.\n", level="start", weight=None,
browser_title="FAQ", description="d", structured={}, terms={})
self.assertNotIn("next", generate_site._front_matter(text))
class ReferenceGrouped(unittest.TestCase):
def _reference(self, wiki: Path | None = None) -> list:
nav = generate_site.generate_nav({}, {}, {}, {}, wiki)
return next(tab["Reference"] for tab in nav if "Reference" in tab)
def test_checking_files_opens_on_find_a_file(self):
reference = self._reference()
groups = {next(iter(item)): next(iter(item.values())) for item in reference
if isinstance(item, dict) and isinstance(next(iter(item.values())), list)}
self.assertEqual(groups["Checking files"][0], {"Find or check a file": "files/index.md"})
self.assertIn({"What the packs do not hold": "gaps.md"}, groups["Checking files"])
self.assertEqual(groups["Data"][:3], [{"Data and API": "data.md"},
{"Data model": "wiki/data-model.md"},
{"Updated platform files": "exports.md"}])
self.assertEqual(reference[0], "wiki/index.md")
def test_a_wiki_page_joins_a_group_from_its_front_matter(self):
(REPO_ROOT / "tmp").mkdir(exist_ok=True)
with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp") as tmp:
wiki = Path(tmp)
(wiki / "index.md").write_text("---\nlevel: reference\norder: 0\n---\n# Reference\n")
(wiki / "modes.md").write_text(
"---\nlevel: reference\norder: 10\ngroup: Checking files\n---\n# How each platform checks files\n")
reference = self._reference(wiki)
group = next(item["Checking files"] for item in reference
if isinstance(item, dict) and "Checking files" in item)
self.assertEqual(group[:2], [{"Find or check a file": "files/index.md"},
{"How each platform checks files": "wiki/modes.md"}])
class Redirects(unittest.TestCase):
def test_core_names_lead_to_their_profile(self):
profiles = {
"beetle_psx": {"cores": ["mednafen_psx", "beetle_psx", "Beetle PSX"]},
"a": {"cores": ["shared"]},
"b": {"cores": ["shared", "dolphin"]},
"dolphin": {"cores": []},
}
present = {"emulators/beetle_psx.md", "emulators/a.md", "emulators/b.md",
"emulators/dolphin.md", "which-pack.md"}
self.assertEqual(sitestarlight.redirects(profiles, present), {
"/emulators/mednafen_psx/": "/emulators/beetle_psx/",
"/wiki/getting-started/": "/which-pack/",
})
def test_a_moved_page_redirects_only_once_its_source_is_gone(self):
present = {"wiki/getting-started.md", "which-pack.md"}
self.assertEqual(sitestarlight.redirects({}, present), {})
def test_the_generator_writes_the_file(self):
source = Path(generate_site.__file__).read_text(encoding="utf-8")
self.assertIn('generated / "redirects.json"', source)
def test_a_list_part_gone_with_its_rows_redirects_to_its_page(self):
"""Only platform parts were recorded: gaps-files-to-source-7, gone
once 29 files were collected, ended on the 404 page."""
with tempfile.TemporaryDirectory() as tmp:
docs = Path(tmp)
part = '---\nlevel: reference\nparent:\n label: "Gaps"\n link: "/gaps/"\n---\n\n# P\n'
(docs / "gaps-files-to-source-7.md").write_text(part, encoding="utf-8")
(docs / "gaps.md").write_text("# Gaps\n", encoding="utf-8")
kept = {"platforms/b-atari-st.md": "platforms/b.md#atari-st"}
published = generate_site.record_parts(
docs, {"gaps-files-to-source-7.md", "gaps.md"}, kept)
self.assertEqual(published, {"gaps-files-to-source-7.md": "gaps.md", **kept})
# An emulator part names its parent with the site's base.
self.assertEqual(generate_site._parent_page("/retrobios/emulators/amiberry/"),
"emulators/amiberry.md")
self.assertEqual(generate_site._parent_page("/platforms/b/#atari-st"),
"platforms/b.md#atari-st")
# The next run made six parts.
self.assertEqual(
sitestarlight.redirects({}, {"gaps.md", "platforms/b.md"}, published),
{"/gaps-files-to-source-7/": "/gaps/", "/platforms/b-atari-st/": "/platforms/b/#atari-st"})
class PlatformShareCards(unittest.TestCase):
def test_every_platform_page_shares_its_own_card(self):
for key in list_registered_platforms(str(REPO_ROOT / "platforms"), include_archived=True):
with self.subTest(platform=key):
card = REPO_ROOT / "docs_assets" / "og" / "platforms" / f"{key}.png"
data = card.read_bytes()
self.assertEqual(struct.unpack(">II", data[16:24]), (1200, 630))
self.assertEqual(sitestarlight.og_image(f"platforms/{key}.md", "setup"),
f"{sitestarlight.SITE_URL}assets/og/platforms/{key}.png")
def test_other_pages_keep_their_section_card(self):
self.assertEqual(sitestarlight.og_image("platforms/index.md", "setup"),
f"{sitestarlight.SITE_URL}assets/og/setup.png")
# A manufacturer page has its own card. One
# without a rendered card keeps its section's.
self.assertEqual(sitestarlight.og_image("systems/sony.md", "explore"),
f"{sitestarlight.SITE_URL}assets/og/systems/sony.png")
self.assertEqual(sitestarlight.og_image("systems/no-such-maker.md", "explore"),
f"{sitestarlight.SITE_URL}assets/og/explore.png")
# The built site
class _Page(HTMLParser):
def __init__(self) -> None:
super().__init__()
self.where: list[str] = []
self.sidebar: list[str] = []
self.crumbs: list[str | None] = []
self.pagination = False
self._sidebar = 0
self._crumbs = 0
self._text: list[str] | None = None
self._pagination = False
def handle_starttag(self, tag: str, attrs: list) -> None:
a = dict(attrs)
if tag == "nav" and a.get("aria-label") == "Breadcrumb":
self._crumbs = 1
elif self._crumbs and tag == "a":
self.crumbs.append(a.get("href"))
if "sidebar-content" in (a.get("class") or ""):
self._sidebar = 1
elif self._sidebar and tag in ("a", "summary"):
self._text = []
if "pagination-links" in (a.get("class") or ""):
self._pagination = True
elif self._pagination and tag == "a":
self.pagination = True
def handle_endtag(self, tag: str) -> None:
if tag == "nav" and self._crumbs:
self._crumbs = 0
if tag == "div" and self._pagination and not self.pagination:
self._pagination = False
if self._text is not None and tag in ("a", "summary"):
self.sidebar.append(" ".join("".join(self._text).split()))
self._text = None
def handle_data(self, data: str) -> None:
if self._text is not None:
self._text.append(data)
def _read(path: Path) -> _Page:
page = _Page()
page.feed(path.read_text(encoding="utf-8"))
return page
@unittest.skipUnless((DIST / "index.html").is_file(), "site not built")
class BuiltNavigation(unittest.TestCase):
def test_the_404_sidebar_names_pages(self):
page = _read(DIST / "404.html")
self.assertTrue(page.sidebar)
self.assertFalse([label for label in page.sidebar if re.fullmatch(r"\d+", label)],
page.sidebar)
def test_crumbs_link_a_tab_an_overview_or_a_section(self):
overviews = TAB_ROOTS | {"/retrobios/systems/", "/retrobios/emulators/"}
pages = [DIST / rel for rel in (
"platforms/batocera/index.html", "platforms/retroarch/index.html",
"systems/sony/index.html", "emulators/duckstation/index.html",
"wiki/verification-modes/index.html", "files/index.html")]
for path in pages:
if not path.is_file():
continue
for href in _read(path).crumbs:
with self.subTest(page=str(path.relative_to(DIST)), href=href):
self.assertTrue(href in overviews or "#" in (href or ""), href)
def test_a_setup_page_names_its_group(self):
page = _read(DIST / "platforms" / "batocera" / "index.html")
self.assertEqual(len(page.crumbs), 2, page.crumbs)
def test_card_pages_have_no_previous_or_next(self):
for rel in ("systems/sony/index.html", "emulators/duckstation/index.html",
"platforms/batocera/index.html"):
with self.subTest(page=rel):
self.assertFalse(_read(DIST / rel).pagination)
self.assertTrue(_read(DIST / "wiki" / "faq" / "index.html").pagination)
def test_the_redirects_file_is_written(self):
data = json.loads((REPO_ROOT / "website" / "src" / "generated" / "redirects.json")
.read_text(encoding="utf-8"))
self.assertEqual(data.get("/emulators/mednafen_psx/"), "/emulators/beetle_psx/")
SEARCH_GATE = REPO_ROOT / "website" / "tools" / "search_gate.py"
DOCS = REPO_ROOT / "website" / "src" / "content" / "docs"
CONTENT_CONFIG = REPO_ROOT / "website" / "src" / "content.config.ts"
START_HERE = [
"/retrobios/", "/retrobios/wiki/what-are-bios-files/", "/retrobios/which-pack/",
"/retrobios/wiki/first-install/", "/retrobios/wiki/troubleshooting/",
"/retrobios/wiki/faq/", "/retrobios/wiki/legal/", "/retrobios/wiki/glossary/",
"/retrobios/platforms/",
]
def _front(text: str) -> str:
end = text.find("\n---\n", 4)
return text[4:end] if text.startswith("---\n") and end != -1 else ""
def _moved_platform_pages() -> list[Path]:
platforms = set(list_registered_platforms(str(REPO_ROOT / "platforms"), include_archived=True))
folder = DOCS / "platforms"
if not folder.is_dir():
return []
# A guide page sits beside the setups (platforms/linux-handhelds) without
# being a list moved off one of them.
from siteguides import HANDHELD_GUIDE # noqa: PLC0415
guides = {Path(HANDHELD_GUIDE.path).stem} if HANDHELD_GUIDE else set()
return [path for path in sorted(folder.glob("*.md*"))
if path.stem not in platforms and path.stem != "index" and path.stem not in guides]
class MetaFields(unittest.TestCase):
def test_file_names_are_an_attribute_not_text(self) -> None:
body = ('<h3 class="rb-file-title"><code>scph5501.bin</code></h3>'
'<h3 class="rb-file-title"><code>a,b.bin</code></h3>')
meta = sitestarlight.file_names_meta(body)
# Each name as its parts, each part once.
self.assertIn('data-pagefind-meta="files:a b bin scph5501"', meta)
self.assertRegex(meta, r"<span [^>]*hidden></span>")
def test_a_field_escapes_its_value(self) -> None:
meta = sitestarlight.search_field("aliases", ['a"b', "c&d"])
self.assertIn('data-pagefind-meta="aliases:a&quot;b c&amp;d"', meta)
def test_an_empty_field_writes_nothing(self) -> None:
self.assertEqual(sitestarlight.search_field("aliases", []), "")
self.assertEqual(sitestarlight.file_names_meta("<p>no card</p>"), "")
def test_system_pages_carry_the_platforms_names(self) -> None:
page = DOCS / "systems" / "sony-playstation.md"
if not page.is_file():
self.skipTest("site pages not generated")
text = page.read_text(encoding="utf-8")
match = re.search(r'data-pagefind-meta="aliases:([^"]*)"', text)
self.assertIsNotNone(match, "no aliases field on the PlayStation page")
self.assertIn("psx", unescape(match[1]).split())
# The Dreamcast section's page: its own, or Sega's when it fits there.
own = DOCS / "systems" / "sega-dreamcast.md"
dreamcast = (own if own.is_file() else DOCS / "systems" / "sega.md").read_text(
encoding="utf-8")
self.assertRegex(dreamcast, r'data-pagefind-meta="aliases:[^"]*\bdc\b')
class SystemPartsSitUnderTheirMaker(unittest.TestCase):
"""The crumbs say where a system part sits, read from its parent: field.
The "Part of" line under them said it again."""
def test_every_part_names_its_maker_as_parent(self) -> None:
pages = sorted((DOCS / "systems").glob("*.md"))
if not pages:
self.skipTest("site pages not generated")
makers = {page.stem for page in pages}
orphans = []
for page in pages:
text = page.read_text(encoding="utf-8")
front = text.split("\n---\n", 1)[0]
parent = re.search(r"^parent:\n label: .+\n link: /retrobios/systems/([a-z0-9-]+)/$",
front, re.M)
if parent is None:
continue
if parent[1] not in makers:
orphans.append(page.name)
self.assertNotIn("\nPart of [", text, page.name)
self.assertEqual(orphans, [])
class MovedPlatformPages(unittest.TestCase):
def test_a_setups_list_for_one_console_is_a_reference(self) -> None:
source = Path(generate_site.__file__).read_text(encoding="utf-8")
block = source[source.index("for stem, page in moved.items():"):]
block = block[: block.index("PAGE_DATA[f\"platforms/{name}.md\"]")]
self.assertIn('SEARCH_DEMOTED.add(f"platforms/{stem}.md")', block)
self.assertIn('level="reference"', block)
self.assertIn('search="exclude"', block)
self.assertIn("parent=", block)
def test_generated_pages_weigh_as_references_and_name_their_setup(self) -> None:
pages = _moved_platform_pages()
if not pages:
self.skipTest("site pages not generated")
for path in pages:
front = _front(path.read_text(encoding="utf-8"))
with self.subTest(page=path.name):
self.assertIn("\nlevel: reference\n", f"\n{front}\n")
self.assertIn("\nsearchWeight: 0.5\n", f"\n{front}\n")
# The title alone was indexed, and it outranked the console.
self.assertIn("\npagefind: false\n", f"\n{front}\n")
self.assertRegex(front, r"parent:\n label: .+\n link: /retrobios/platforms/[a-z0-9_-]+/\n")
def test_parent_link_resolves_against_the_page(self) -> None:
_target, text = sitestarlight.finalize_page(
"platforms/retroarch-sony-playstation.md", "# RetroArch: Sony PlayStation\n\nBody.\n",
level="reference", weight=0.5, browser_title="x", description="d",
structured={}, terms={}, parent={"label": "RetroArch", "link": "retroarch.md"},
)
self.assertIn("parent:\n label: RetroArch\n link: /retrobios/platforms/retroarch/\n", text)
def test_the_schema_accepts_parent(self) -> None:
self.assertRegex(CONTENT_CONFIG.read_text(encoding="utf-8"),
r"parent: z\.object\(\{ label: z\.string\(\), link: z\.string\(\) \}\)")
class ArcadeGroup(unittest.TestCase):
def test_arcade_is_not_a_console_or_computer(self) -> None:
nav = generate_site._system_nav({"Sony": {}, "Arcade": {}, "Sega": {}})
groups = {next(iter(entry)): entry[next(iter(entry))] for entry in nav if isinstance(entry, dict)}
self.assertEqual(groups["Arcade"], [{"Arcade": "systems/arcade.md"}])
self.assertNotIn({"Arcade": "systems/arcade.md"}, groups["Consoles and computers"])
class RouteMiddleware(unittest.TestCase):
def test_next_steps_link_is_kept_only_forward(self) -> None:
source = ROUTE.read_text(encoding="utf-8")
self.assertIn("target <= position", source)
def test_parent_is_a_crumb_and_marked(self) -> None:
source = ROUTE.read_text(encoding="utf-8")
self.assertIn("route.entry.data.parent", source)
self.assertIn("markCurrent(route.sidebar", source)
def _gate_queries() -> dict:
"""The search gate's QUERIES, read from its source: the tool is run
with node beside it, not imported as a package."""
tree = ast.parse(SEARCH_GATE.read_text(encoding="utf-8"))
for node in tree.body:
if isinstance(node, ast.AnnAssign) and getattr(node.target, "id", "") == "QUERIES":
return ast.literal_eval(node.value)
raise AssertionError("no QUERIES in search_gate.py")
class SearchGateQueries(unittest.TestCase):
def test_the_audits_queries_are_gated(self) -> None:
for query in ("playstation bios", "dreamcast bios", "neo geo bios", "bios not found",
"offline install", "r36s", "wonderswan color", "dc bios missing",
"which mame version", "psx bios"):
self.assertIn(query, _gate_queries())
def test_prefixes_match_a_consoles_own_page(self) -> None:
pages, _rank = _gate_queries()["playstation bios"]
self.assertTrue(any("systems/sony-playstation/".startswith(p) for p in pages))
def _html(url: str) -> str:
rel = url[len("/retrobios/"):]
path = DIST / rel / "index.html"
return path.read_text(encoding="utf-8") if path.is_file() else ""
def _pagination(text: str) -> tuple[str | None, str | None]:
start = text.find('<div class="pagination-links')
if start == -1:
return None, None
links = text[start:text.find("</footer>", start)]
prev = re.search(r'<a href="([^"]+)"[^>]*rel="prev"', links)
nxt = re.search(r'<a href="([^"]+)"[^>]*rel="next"', links)
return (prev[1] if prev else None), (nxt[1] if nxt else None)
@unittest.skipUnless((DIST / "index.html").is_file(), "site not built")
class BuiltSearchAndCrumbs(unittest.TestCase):
def test_next_from_home_reads_start_here_once(self) -> None:
seen, url = [], "/retrobios/"
while url and url not in seen and len(seen) < 20:
seen.append(url)
url = _pagination(_html(url))[1]
self.assertEqual(seen[: len(START_HERE)], START_HERE)
self.assertEqual(len(seen), len(set(seen)))
def test_no_page_has_the_same_previous_and_next(self) -> None:
same = []
for path in DIST.rglob("index.html"):
prev, nxt = _pagination(path.read_text(encoding="utf-8"))
if prev and prev == nxt:
same.append(str(path.relative_to(DIST)))
self.assertEqual(same, [])
def test_search_fields_hold_no_text(self) -> None:
texts = []
for path in (DIST / "systems").rglob("index.html"):
# The search fields are attributes on empty spans. The title
# meta reads the visible h1.
for match in re.finditer(r'<(\w+)[^>]*data-pagefind-meta="(?:files|aliases|devices|consoles|terms):'
r'[^>]*>([^<]*)</\1>',
path.read_text(encoding="utf-8")):
if match[2].strip():
texts.append(str(path.relative_to(DIST)))
self.assertEqual(texts, [])
def test_a_setups_console_page_names_its_setup(self) -> None:
text = _html("/retrobios/platforms/retroarch-sony-playstation/")
if not text:
self.skipTest("no RetroArch PlayStation page")
crumbs = re.search(r'<nav[^>]*class="[^"]*rb-breadcrumbs[^"]*"[^>]*>(.*?)</nav>', text, re.S)
self.assertIsNotNone(crumbs)
self.assertIn('href="/retrobios/platforms/retroarch/"', crumbs[1])
self.assertRegex(text, r'<a href="/retrobios/platforms/retroarch/"[^>]*aria-current="page"')
if __name__ == "__main__":
unittest.main()