chore: merge origin/main

This commit is contained in:
Abdessamad Derraz committed 2026-11-09 07:35:22 +01:00
commit 45cf1accfb
28 files changed
+1084 -600

No files matched your search

+6 -3
View File
@@ -56,8 +56,8 @@ class ReadmeRegressions(unittest.TestCase):
def _quick_install(self) -> str:
readme = (ROOT / "README.md").read_text(encoding="utf-8")
return readme.split("## Quick install", 1)[1].split(
"## Download BIOS packs", 1
return readme.split("## Install in one line", 1)[1].split(
"## Or download a ZIP", 1
)[0]
def test_advertised_bootstrap_urls_are_the_shipped_ones(self):
@@ -1121,7 +1121,10 @@ class CatalogRatioRegressions(unittest.TestCase):
match = re.search(
r"\*\*([\d,]+) of ([\d,]+) system files\*\* matched to", readme
)
self.assertIsNotNone(match, "README no longer states the catalog ratio")
if match is None:
# The README states no ratio since it leads to the site: a ratio
# it states must still be the database's.
return
matched, total = (int(g.replace(",", "")) for g in match.groups())
self.assertEqual(matched, count_catalog_matched(db))
self.assertEqual(total, compute_composition(db)["systems"]["files"])
+10 -13
View File
@@ -340,7 +340,7 @@ class FilesColumnParagraph(unittest.TestCase):
paragraph = next(p for p in readme.split("\n\n") if "The Files column" in p)
self.assertNotIn("\n", paragraph.strip())
after = readme.split(paragraph, 1)[1].lstrip("\n")
self.assertTrue(after.startswith("| Platform | Files |"))
self.assertTrue(after.startswith("| Setup | Files |"))
class PageOpenings(unittest.TestCase):
@@ -769,14 +769,12 @@ class ReadmeInstallerOptions(unittest.TestCase):
import generate_readme
source = inspect.getsource(generate_readme.generate_readme)
self.assertIn('install_command("powershell", "--check")', source)
self.assertIn("wiki/installer/#passing-options", source)
self.assertIn("## Passing options", _page("installer.md"))
readme = (ROOT / "README.md").read_text(encoding="utf-8")
quick = _section(readme, "Quick install")
from siteguides import INSTALL_PS1
self.assertIn(f"&([scriptblock]::Create((irm {INSTALL_PS1}))) --check", quick)
quick = _section(readme, "Install in one line")
# The README gives the bare line and sends options to the page that
# writes their PowerShell form: an option after `| iex` reaches iex.
self.assertIn("wiki/installer/#passing-options", quick)
self.assertNotRegex(readme, r"(?m)^irm \S+ \| iex \S")
@@ -791,7 +789,7 @@ class PublishedNumbersShareOneFormat(unittest.TestCase):
readme_source = inspect.getsource(generate_readme.generate_readme)
self.assertNotIn("size_mb", readme_source)
self.assertIn("format_size(total_size)", readme_source)
self.assertIn("format_size(size)", readme_source)
self.assertIn("{total_files:,}", inspect.getsource(generate_site))
self.assertEqual(generate_readme.format_size(16011 * 1024 * 1024), "15.6 GB")
@@ -1713,9 +1711,7 @@ class ReadmeKeepsItsPointers(unittest.TestCase):
"""The README sent a reader to 'build your own pack below' for a bare
minimum, and the block below never named --required-only."""
readme = (ROOT / "README.md").read_text(encoding="utf-8")
self.assertIn("For a bare minimum, build your own pack below.", readme)
block = readme[readme.index("## Build your own pack"):]
block = block[:block.index("\n## ", 1)]
block = readme[readme.index("<summary><strong>For developers"):readme.index("</details>")]
self.assertIn("--required-only", block)
def test_unsourceable_files_are_not_said_to_exist_nowhere(self):
@@ -1725,7 +1721,8 @@ class ReadmeKeepsItsPointers(unittest.TestCase):
readme = (ROOT / "README.md").read_text(encoding="utf-8")
self.assertNotIn("sourced at all", readme)
self.assertNotIn("nobody has made", readme)
self.assertRegex(readme, r"more the collection does not hold and does not serve by name")
# The README names no count: the gap analysis it links does, by reason.
self.assertIn("gaps/", readme)
class ArchitectureStatesTheArbitration(unittest.TestCase):
@@ -2041,8 +2038,8 @@ class BuildYourOwnPackSaysWhatTheCloneCosts(unittest.TestCase):
line = generate_readme.clone_line(db)
self.assertIn("git clone --depth 1", line)
self.assertRegex(line, r"over \d+(\.\d)? GB")
section = _section((ROOT / "README.md").read_text(encoding="utf-8"),
"Build your own pack")
readme = (ROOT / "README.md").read_text(encoding="utf-8")
section = readme[readme.index("<summary><strong>For developers"):readme.index("</details>")]
self.assertIn("git clone --depth 1", section)
def test_advanced_usage_names_the_shallow_clone(self):
+7 -6
View File
@@ -191,16 +191,17 @@ class DownloadLinksKeepTheirAddress(unittest.TestCase):
from common import list_registered_platforms, list_release_pack_platforms
readme = _read("README.md")
table = _section(readme, "## Download BIOS packs")
table = _section(readme, "## Or download a ZIP")
self.assertNotIn("/releases/download/", table)
released = set(list_release_pack_platforms("platforms"))
rows = [line for line in table.splitlines() if line.startswith("| ")]
for key in list_registered_platforms("platforms"):
with self.subTest(platform=key):
# Every setup's lasting page is linked: a ZIP's from its row,
# the others from the line that says they install by command.
self.assertIn(f"]({SITE_URL}get/{key}/)", table)
row = next(line for line in table.splitlines()
if f"get/{key}/)" in line)
if key not in released:
self.assertIn("[Use the command]", row)
in_rows = any(f"get/{key}/)" in row for row in rows)
self.assertEqual(in_rows, key in released)
def test_the_download_page_rows_link_get_pages(self):
page = _which_pack()
@@ -258,7 +259,7 @@ class TheReadmeLeadsBeginnersToTheirPages(unittest.TestCase):
def test_quick_install_links_the_start_pages(self):
# The code block holds `#` comment lines, so the section runs to
# the next level-two heading.
section = _read("README.md").split("## Quick install", 1)[1].split("\n## ", 1)[0]
section = _read("README.md").split("## Install in one line", 1)[1].split("\n## ", 1)[0]
for page in ("first-install", "troubleshooting", "faq"):
with self.subTest(page=page):
self.assertIn(f"wiki/{page}/)", section)
+3 -3
View File
@@ -284,9 +284,9 @@ class EverySurfaceSaysWhy(unittest.TestCase):
def test_the_readme_rows_read_use_the_command(self):
readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8")
row = next(line for line in readme.splitlines() if line.startswith("| KNULLI |"))
self.assertIn(f"[Use the command]({SITE}get/knulli/)", row)
self.assertIn("ZIPs are made for the setups most people download.", readme)
line = next(text for text in readme.split("\n\n") if "ZIPs are made for the setups most people download." in text)
self.assertIn(f"[KNULLI]({SITE}get/knulli/)", line)
self.assertIn("install with the line above", line)
def test_the_faq_answers_why(self):
faq = (REPO_ROOT / "wiki" / "faq.md").read_text(encoding="utf-8")
+1 -1
View File
@@ -271,7 +271,7 @@ class GeneratedSurfaces(unittest.TestCase):
def test_the_readme_shows_the_short_form(self):
readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8")
quick = readme.split("## Quick install", 1)[1].split("\n## ", 1)[0]
quick = readme.split("## Install in one line", 1)[1].split("\n## ", 1)[0]
self.assertIn(f"\n{install_command('sh')}\n", quick)
self.assertIn(f"\n{install_command('powershell')}\n", quick)
self.assertNotIn("raw.githubusercontent.com", quick)
+145 -40
View File
@@ -1,9 +1,12 @@
"""The installer demo of the README and the site (scripts/make_demo.py).
"""The installer demos of the README and the site (scripts/make_demo.py).
The home page and Get your files score 98 to 100 on a phone: the video must
The home page and Get your files score 98 to 100 on a phone: a video must
cost nothing before it is on screen, keep still under reduced motion, and say
in words what it shows. The files are committed, so their sizes are held
here, and the words come from what the recording measured.
here, and the words come from what each recording measured. Get your files
shows one recording per tab of its install box: the handheld's under "On the
device", none under Windows (no Windows recording is published), the
terminal recording under the others.
"""
from __future__ import annotations
@@ -29,43 +32,90 @@ COMPONENT = ROOT / "website" / "src" / "components" / "InstallDemo.astro"
DIST = ROOT / "website" / "dist"
def _facts() -> dict:
def _all_facts() -> dict:
return json.loads((DEMO / "demo.json").read_text(encoding="utf-8"))
def _facts(variant: str = "default") -> dict:
return _all_facts()[variant]
class TheFilesAreThereAndSmall(unittest.TestCase):
def test_each_output_is_under_its_target(self):
for name, limit in make_demo.LIMITS.items():
with self.subTest(name=name):
self.assertLessEqual((DEMO / name).stat().st_size, limit)
for variant in make_demo.VARIANTS.values():
for name, limit in make_demo.limits(variant).items():
with self.subTest(name=name):
self.assertLessEqual((DEMO / name).stat().st_size, limit)
def test_the_gif_and_the_poster_have_the_recorded_size(self):
def test_only_the_readme_variant_has_a_gif(self):
gifs = sorted(p.name for p in DEMO.glob("*.gif"))
self.assertEqual(gifs, [Path(generate_readme.DEMO_GIF).name])
self.assertEqual([v.name for v in make_demo.VARIANTS.values() if v.gif], ["default"])
def test_the_gif_and_the_posters_have_the_recorded_size(self):
facts = _facts()
head = (DEMO / "install.gif").read_bytes()[:10]
self.assertEqual(head[:6], b"GIF89a")
self.assertEqual(struct.unpack("<HH", head[6:10]), (facts["width"], facts["height"]))
png = (DEMO / "install.png").read_bytes()[:24]
self.assertEqual(png[:8], b"\x89PNG\r\n\x1a\n")
self.assertEqual(struct.unpack(">II", png[16:24]), (facts["width"], facts["height"]))
for variant in make_demo.VARIANTS.values():
facts = _facts(variant.name)
png = (DEMO / f"{variant.stem}.png").read_bytes()[:24]
with self.subTest(variant=variant.name):
self.assertEqual(png[:8], b"\x89PNG\r\n\x1a\n")
self.assertEqual(struct.unpack(">II", png[16:24]),
(facts["width"], facts["height"]))
def test_the_videos_are_webm_and_mp4(self):
self.assertEqual((DEMO / "install.webm").read_bytes()[:4], b"\x1a\x45\xdf\xa3")
self.assertEqual((DEMO / "install.mp4").read_bytes()[4:8], b"ftyp")
for variant in make_demo.VARIANTS.values():
with self.subTest(variant=variant.name):
self.assertEqual((DEMO / f"{variant.stem}.webm").read_bytes()[:4],
b"\x1a\x45\xdf\xa3")
self.assertEqual((DEMO / f"{variant.stem}.mp4").read_bytes()[4:8], b"ftyp")
def test_demo_json_holds_each_variant_and_its_alt_text(self):
facts = _all_facts()
self.assertEqual(list(facts), list(make_demo.VARIANTS))
for name, entry in facts.items():
with self.subTest(variant=name):
self.assertEqual(entry["alt"], make_demo.describe(name, entry))
# Written into an MDX attribute: no quote of either kind.
self.assertNotRegex(entry["alt"], "[\"']")
class TheTapeAndTheDriverAgree(unittest.TestCase):
def setUp(self):
self.tape = make_demo.TAPE.read_text(encoding="utf-8")
def test_the_tape_writes_what_the_driver_encodes(self):
self.assertIn(f"Output {make_demo.RECORDED.relative_to(ROOT).as_posix()}\n", self.tape)
self.assertIn(f"Screenshot {make_demo.POSTER_SHOT.relative_to(ROOT).as_posix()}\n",
self.tape)
def test_each_tape_writes_what_the_driver_encodes(self):
for variant in make_demo.VARIANTS.values():
tape = variant.tape.read_text(encoding="utf-8")
with self.subTest(variant=variant.name):
self.assertIn(f"Output {variant.recorded.relative_to(ROOT).as_posix()}\n", tape)
self.assertIn(
f"Screenshot {variant.poster_shot.relative_to(ROOT).as_posix()}\n", tape)
def test_the_size_in_demo_json_is_the_tapes(self):
facts = _facts()
self.assertIn(f"Set Width {facts['width']}\n", self.tape)
self.assertIn(f"Set Height {facts['height']}\n", self.tape)
def test_the_size_in_demo_json_is_each_tapes(self):
for variant in make_demo.VARIANTS.values():
facts, tape = _facts(variant.name), variant.tape.read_text(encoding="utf-8")
with self.subTest(variant=variant.name):
self.assertIn(f"Set Width {facts['width']}\n", tape)
self.assertIn(f"Set Height {facts['height']}\n", tape)
def test_the_handheld_is_an_r36s_screen_driven_by_its_buttons(self):
variant = make_demo.VARIANTS["handheld"]
tape = variant.tape.read_text(encoding="utf-8")
self.assertIn("Set Width 640\n", tape)
self.assertIn("Set Height 480\n", tape)
# Nothing typed but the hidden start of the session: the buttons
# gptokeyb maps (ports/shared/retrobios.gptk: a = enter) do the rest.
self.assertEqual(re.findall(r"^Type (.*)$", tape, re.M),
["'exec \"$RETROBIOS_DEMO_SESSION\"'"])
gptk = (ROOT / "ports" / "shared" / "retrobios.gptk").read_text(encoding="utf-8")
self.assertRegex(gptk, r"(?m)^a = enter$")
env = make_demo.session_env("http://127.0.0.1:1", ROOT / "tmp", ROOT / "tmp", variant)
self.assertEqual(env["RETROBIOS_PAD"], "gptokeyb")
self.assertEqual(env["RETROBIOS_OS"], "linux")
self.assertEqual(variant.mount, "/roms")
def test_the_command_typed_is_the_one_the_pages_give(self):
from siteguides import install_command # noqa: PLC0415
@@ -84,8 +134,8 @@ class TheReadmeShowsTheGif(unittest.TestCase):
readme = (ROOT / "README.md").read_text(encoding="utf-8")
line = f"![{generate_readme.demo_description()}]({generate_readme.DEMO_GIF})"
self.assertIn(line, readme)
self.assertLess(readme.index("## Quick install"), readme.index(line))
self.assertLess(readme.index(line), readme.index("## Download BIOS packs"))
self.assertLess(readme.index("## Install in one line"), readme.index(line))
self.assertLess(readme.index(line), readme.index("## Or download a ZIP"))
def test_the_description_names_what_the_recording_measured(self):
facts = _facts()
@@ -112,7 +162,13 @@ class TheVideoLoadsOnlyOnScreen(unittest.TestCase):
self.assertRegex(self.markup, rf"\n\t\t{attribute}\n")
def test_webm_comes_before_mp4(self):
self.assertLess(self.markup.index("install.webm"), self.markup.index("install.mp4"))
self.assertLess(self.markup.index("asset('webm')"), self.markup.index("asset('mp4')"))
def test_each_variant_has_its_files_and_its_own_ids(self):
self.assertIn("const stem = variant === 'default' ? 'install' : `install-${variant}`;",
self.source)
self.assertIn("const textId = name ? `rb-demo-text-${name}` : 'rb-demo-text';",
self.source)
def test_reduced_motion_keeps_the_poster_with_a_button(self):
self.assertIn("matchMedia('(prefers-reduced-motion: reduce)')", self.source)
@@ -121,44 +177,93 @@ class TheVideoLoadsOnlyOnScreen(unittest.TestCase):
self.markup)
def test_screen_readers_get_the_description(self):
self.assertIn('aria-describedby="rb-demo-text"', self.markup)
self.assertIn('<figcaption id="rb-demo-text" class="sr-only">{description}</figcaption>',
self.assertIn("aria-describedby={textId}", self.markup)
self.assertIn('<figcaption id={textId} class="sr-only">{description}</figcaption>',
self.markup)
def test_the_component_is_registered(self):
self.assertEqual(sitestarlight.COMPONENTS["InstallDemo"], "components/InstallDemo.astro")
def _tab_bodies(markdown: str) -> dict[str, str]:
"""Each tab of the quick install box of Get your files, by label."""
box = markdown[markdown.index("{ #quick-install }"):]
box = box[:box.index("</Tabs>")]
return dict(re.findall(r'<TabItem label="([^"]+)">(.*?)</TabItem>', box, re.S))
class TheTwoPagesShowIt(unittest.TestCase):
def test_home_and_get_your_files(self):
def test_home_shows_the_terminal_recording(self):
import generate_site # noqa: PLC0415
tag = generate_site.install_demo()[0]
self.assertIn(f'description="{generate_readme.demo_description()}"', tag)
source = Path(generate_site.__file__).read_text(encoding="utf-8")
for function in ("def generate_home(", "def generate_which_pack("):
body = source[source.index(function):]
body = body[:body.index("\ndef ", 1)]
with self.subTest(function=function):
self.assertIn("*install_demo(),", body)
body = source[source.index("def generate_home("):]
body = body[:body.index("\ndef ", 1)]
self.assertIn("*install_demo(),", body)
def test_the_site_serves_the_video_not_the_gif(self):
def test_each_tab_of_get_your_files_shows_its_variant(self):
import generate_site # noqa: PLC0415
self.assertEqual(generate_site.DEMO_ASSETS, ("install.webm", "install.mp4", "install.png"))
tabs = _tab_bodies(generate_site.generate_which_pack())
expected = {"Windows": None, "macOS": "default", "Linux": "default",
"Steam Deck": "default", "Android (Termux)": "default",
"On the device": "handheld"}
self.assertEqual(set(tabs), set(expected))
for label, variant in expected.items():
demos = re.findall(r"<InstallDemo [^>]*/>", tabs[label])
with self.subTest(tab=label):
if variant is None:
self.assertEqual(demos, [])
continue
self.assertEqual(len(demos), 1)
self.assertIn(f'description="{_facts(variant)["alt"]}"', demos[0])
self.assertIn(f'width="{_facts(variant)["width"]}"', demos[0])
self.assertIn(f'variant="{variant}"', demos[0])
# The demo below the box moved into its tabs: one per tab, no other.
page = generate_site.generate_which_pack()
self.assertEqual(page.count("<InstallDemo "), len(expected) - 1)
def test_the_site_serves_the_videos_not_the_gif(self):
import generate_site # noqa: PLC0415
expected = tuple(name for variant in make_demo.VARIANTS.values()
for name in variant.outputs if not name.endswith(".gif"))
self.assertEqual(sorted(generate_site.DEMO_ASSETS), sorted(expected))
def test_the_built_pages_fetch_nothing_of_it(self):
if not (DIST / "index.html").is_file():
self.skipTest("no built site")
for page in ("index.html", "which-pack/index.html"):
html = (DIST / page).read_text(encoding="utf-8")
figure = re.search(r'<figure class="rb-demo[^"]*".*?</figure>', html, re.S)
figures = re.findall(r'<figure class="rb-demo[^"]*".*?</figure>', html, re.S)
with self.subTest(page=page):
self.assertIsNotNone(figure)
self.assertIn('preload="none"', figure.group(0))
self.assertNotRegex(figure.group(0), r"\s(src|poster)=")
for name in ("install.webm", "install.mp4", "install.png"):
self.assertTrue((DIST / "assets" / "demo" / name).is_file())
self.assertTrue(figures)
for figure in figures:
self.assertIn('preload="none"', figure)
self.assertNotRegex(figure, r"\s(src|poster)=")
for name in generate_site_assets():
self.assertTrue((DIST / "assets" / "demo" / name).is_file())
def test_the_built_tabs_hold_their_variant(self):
page = DIST / "which-pack" / "index.html"
if not page.is_file():
self.skipTest("no built site")
html = page.read_text(encoding="utf-8")
panel = r'<div[^>]*role="tabpanel"'
panels = re.findall(rf"{panel}[^>]*>(.*?)(?={panel}|</starlight-tabs>)", html, re.S)
variants = [re.findall(r'data-variant="([a-z]+)"', panel) for panel in panels]
# The quick install box: Windows, macOS, Linux, Steam Deck, Android, device.
self.assertIn([[], ["default"], ["default"], ["default"], ["default"], ["handheld"]],
[variants[i:i + 6] for i in range(len(variants) - 5)])
self.assertIn("assets/demo/install-handheld.png", html)
def generate_site_assets() -> tuple[str, ...]:
import generate_site # noqa: PLC0415
return generate_site.DEMO_ASSETS
if __name__ == "__main__":
+1 -1
View File
@@ -237,7 +237,7 @@ class FaqInstallsFromWhatYouHold(unittest.TestCase):
word = rule.name.split()[0]
with self.subTest(share=rule.name):
self.assertIn(word, section)
self.assertRegex(readme, rf"# SD card mounted on a PC: .*\b{word}\b")
self.assertRegex(readme, rf"\b{word}\b[^.]*SD card plugged into the computer is found by itself")
# The label the first screen gives a card found: "Onion SD card".
self.assertEqual(install._card_word(("retroarch", Path("/x/BIOS"), "Onion")), "SD card")
self.assertEqual(install._card_word(("batocera", Path("/x/bios"), "Batocera")),
+94
View File
@@ -0,0 +1,94 @@
"""The drawn steps of the install box (scripts/sitesteps.py).
Each drawing says in words what it shows, to a screen reader and to a
reader whose images do not load: a title and a desc the SVG names. It tells
the same steps as the text above it, the keys included, so the picture and
the words never disagree. And it draws in the theme's colours.
"""
from __future__ import annotations
import tests # noqa: F401 offline, scratch under tmp/, the suite's own cache
import re
import sys
import unittest
import xml.etree.ElementTree as ET
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "scripts"))
import sitesteps # noqa: E402
from siteguides import TERMINALS # noqa: E402
SVG = "{http://www.w3.org/2000/svg}"
DIST = ROOT / "website" / "dist"
class EachDrawingNamesItsSteps(unittest.TestCase):
def test_title_and_desc_are_what_aria_points_at(self):
for tab in sitesteps.STEPS:
for layout in ("wide", "narrow"):
root = ET.fromstring(sitesteps._svg(tab, layout))
with self.subTest(tab=tab, layout=layout):
self.assertEqual(root.get("role"), "img")
title, desc = root.find(f"{SVG}title"), root.find(f"{SVG}desc")
self.assertIsNotNone(title)
self.assertIsNotNone(desc)
self.assertEqual(root.get("aria-labelledby").split(),
[title.get("id"), desc.get("id")])
self.assertEqual(desc.text, sitesteps.description(tab))
def test_every_caption_drawn_is_in_the_desc(self):
for tab, (_title, steps) in sitesteps.STEPS.items():
desc = sitesteps.description(tab)
for step in steps:
with self.subTest(tab=tab, step=step.caption):
self.assertIn(step.caption, desc)
def test_the_drawing_and_the_text_name_the_same_keys(self):
for tab, (_title, steps) in sitesteps.STEPS.items():
text = TERMINALS[tab][1].replace("**", "")
keys = {step.word for step in steps if step.icon == "keys"}
with self.subTest(tab=tab):
for word in keys:
self.assertIn(word, text)
drawn = set(re.findall(r"\b(?:Ctrl|Cmd|Shift|Alt)\+\w+(?:\+\w+)?", text))
self.assertLessEqual(drawn, keys)
self.assertIn("Click Copy", text)
def test_one_drawing_per_computer_tab_none_elsewhere(self):
self.assertEqual(set(sitesteps.STEPS), {"windows", "macos", "linux", "steamdeck"})
self.assertEqual(sitesteps.steps_figure("device"), [])
def test_colours_come_from_the_theme(self):
for tab in sitesteps.STEPS:
root = ET.fromstring(sitesteps._svg(tab, "wide"))
for node in root.iter():
tag = node.tag.removeprefix(SVG)
if tag in ("rect", "path", "circle"):
with self.subTest(tab=tab, node=ET.tostring(node)[:60]):
self.assertTrue(node.get("class", "").startswith("rb-d-"))
def test_ids_are_unique_on_the_page(self):
ids = re.findall(r'id="([^"]+)"', "".join(
sitesteps._svg(tab, layout) for tab in sitesteps.STEPS for layout in ("wide", "narrow")))
self.assertEqual(len(ids), len(set(ids)))
class TheBuiltPageHoldsThem(unittest.TestCase):
def test_under_each_tab_of_get_your_files(self):
page = DIST / "which-pack" / "index.html"
if not page.is_file():
self.skipTest("no built site")
html = page.read_text(encoding="utf-8")
for tab in sitesteps.STEPS:
with self.subTest(tab=tab):
self.assertIn(f'aria-labelledby="rb-steps-{tab}-wide-t rb-steps-{tab}-wide-d"',
html)
self.assertIn(f'<desc id="rb-steps-{tab}-narrow-d">', html)
if __name__ == "__main__":
unittest.main()