mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-12 21:13:49 -06:00
feat: show the installer demo on readme and site
The README shows the GIF under the commands. The home page and Get your files show the video through InstallDemo.astro: nothing is fetched until it is on screen, reduced motion and Save-Data keep the poster with a play button, and a description is read to screen readers.
This commit is contained in:
1 parent
e310723893
commit
65a5dfecc7
7 files changed
+325
-2
No files matched your search
@@ -36,6 +36,8 @@ curl -fsSL https://abdess.github.io/retrobios/i | sh -s -- --platform retroarch
|
||||
&([scriptblock]::Create((irm https://abdess.github.io/retrobios/i.ps1))) --check
|
||||
```
|
||||
|
||||

|
||||
|
||||
The script opens a full-screen installer, driven with the arrow keys and Enter, or with a controller on a handheld. It finds your setup and its BIOS folder, downloads only the missing files and checks each one. It can also copy the BIOS files from your old SD card.
|
||||
Every option, and what the installer detects and writes, is on the [installer page](https://abdess.github.io/retrobios/wiki/installer/#passing-options).
|
||||
|
||||
@@ -206,4 +208,4 @@ The scripts and tooling are released under the [MIT License](LICENSE).
|
||||
The BIOS and firmware files are not covered by that license: they are third-party system software, preserved and provided for personal backup, archival, and interoperability with emulation software. [NOTICE](NOTICE) sets out their status and how to ask for a file to be removed.
|
||||
The reasoning, and where it is weakest, is in [the project's legal position](https://abdess.github.io/retrobios/wiki/legal/).
|
||||
|
||||
*Auto-generated on 2026-11-06T10:21:06Z*
|
||||
*Auto-generated on 2026-11-06T19:12:52Z*
|
||||
@@ -440,6 +440,9 @@ def format_size(size: int) -> str:
|
||||
|
||||
|
||||
SITE_URL = "https://abdess.github.io/retrobios/"
|
||||
# The installer demo (scripts/make_demo.py): the recording and what it shows.
|
||||
DEMO_DIR = Path(__file__).resolve().parent.parent / "docs_assets" / "demo"
|
||||
DEMO_GIF = "docs_assets/demo/install.gif"
|
||||
REPO_PATH = "../.."
|
||||
REPO = "Abdess/retrobios"
|
||||
# GitHub's "Add file > Upload files" page for bios/: a browser upload by
|
||||
@@ -447,6 +450,19 @@ REPO = "Abdess/retrobios"
|
||||
UPLOAD_URL = f"https://github.com/{REPO}/upload/main/bios"
|
||||
|
||||
|
||||
def demo_facts() -> dict:
|
||||
"""What the installer demo shows, as make_demo.py measured it."""
|
||||
return json.loads((DEMO_DIR / "demo.json").read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def demo_description(facts: dict | None = None) -> str:
|
||||
"""The demo in words, for a reader who cannot see it."""
|
||||
facts = facts or demo_facts()
|
||||
return (f"Recording of the one-line command in a terminal. The installer finds an "
|
||||
f"{facts['setup']} SD card with {facts['games']} games, plans {facts['files']} "
|
||||
f"BIOS files ({facts['size']}) for them, installs them and shows the summary.")
|
||||
|
||||
|
||||
def _existing_contributor_block(readme_path: str = "README.md") -> list[str]:
|
||||
"""The contributors already published, read back from the file.
|
||||
|
||||
@@ -593,6 +609,8 @@ def generate_readme(db: dict, platforms_dir: str, db_path: str = "database.json"
|
||||
install_command("powershell", "--check"),
|
||||
"```",
|
||||
"",
|
||||
f"",
|
||||
"",
|
||||
"The script opens a full-screen installer, driven with the arrow keys and Enter, or with a"
|
||||
" controller on a handheld. It finds your setup and its BIOS folder, downloads only the missing"
|
||||
" files and checks each one. It can also copy the BIOS files from your old SD card.",
|
||||
|
||||
@@ -82,6 +82,8 @@ from generate_readme import (
|
||||
NO_PACK,
|
||||
asset_links,
|
||||
contributing_text,
|
||||
demo_description,
|
||||
demo_facts,
|
||||
download_links,
|
||||
extract_notes,
|
||||
extract_targets,
|
||||
@@ -555,6 +557,7 @@ def generate_home(
|
||||
"**Run one command.** Some setups have a ZIP.",
|
||||
"**Start a game.**",
|
||||
]),
|
||||
*install_demo(),
|
||||
"## Pick your device",
|
||||
"",
|
||||
*setup_picker(registry, display),
|
||||
@@ -4188,6 +4191,20 @@ def first_screen_words() -> list[str]:
|
||||
f"and **{installer.TUI_FOLDER_TITLE}**), then offers **{installer.TUI_INSTALL_ROW}**."]
|
||||
|
||||
|
||||
# The demo files the site serves (docs_assets/demo/, scripts/make_demo.py):
|
||||
# the GIF is the README's, the tape and demo.json are inputs.
|
||||
DEMO_ASSETS = ("install.webm", "install.mp4", "install.png")
|
||||
|
||||
|
||||
def install_demo() -> list[str]:
|
||||
"""The installer demo: a video that loads once it is on screen, its
|
||||
description for screen readers."""
|
||||
facts = demo_facts()
|
||||
text = html.escape(demo_description(facts), quote=True)
|
||||
return [f'<InstallDemo description="{text}" width="{facts["width"]}" '
|
||||
f'height="{facts["height"]}" />', ""]
|
||||
|
||||
|
||||
def check_pass_line() -> str:
|
||||
"""The pass line of "How to check": the words `--check` prints only when
|
||||
no file is missing or wrong (install.py EVERYTHING_IN_PLACE), read from
|
||||
@@ -12117,6 +12134,7 @@ def generate_which_pack() -> str:
|
||||
"",
|
||||
*first_screen_words(),
|
||||
"",
|
||||
*install_demo(),
|
||||
*trust_box(""),
|
||||
"**Check your folder later.** Add `--check`. It changes nothing.",
|
||||
"",
|
||||
@@ -12476,6 +12494,10 @@ def _generate(args: argparse.Namespace) -> None:
|
||||
source = Path("docs_assets") / folder
|
||||
if source.is_dir():
|
||||
shutil.copytree(source, public / "assets" / dest, dirs_exist_ok=True)
|
||||
demo_dest = public / "assets" / "demo"
|
||||
demo_dest.mkdir(parents=True, exist_ok=True)
|
||||
for name in DEMO_ASSETS:
|
||||
shutil.copy2(Path("docs_assets") / "demo" / name, demo_dest / name)
|
||||
assets_src = Path(".github") / "assets"
|
||||
for name, dest_name in [("logo.png", "logo.png"), ("favicon.png", "favicon.png")]:
|
||||
src = assets_src / name
|
||||
|
||||
@@ -29,6 +29,7 @@ COMPONENTS = {
|
||||
"TabItem": "@astrojs/starlight/components",
|
||||
"FilePicker": "components/FilePicker.astro",
|
||||
"MeasurementsExplorer": "components/MeasurementsExplorer.astro",
|
||||
"InstallDemo": "components/InstallDemo.astro",
|
||||
}
|
||||
|
||||
# Pages of file cards: level 3 headings by the hundred, so the table of
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
"""The installer demo 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
|
||||
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.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import tests # noqa: F401 offline, scratch under tmp/, the suite's own cache
|
||||
|
||||
import json
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
|
||||
import generate_readme # noqa: E402
|
||||
import make_demo # noqa: E402
|
||||
import sitestarlight # noqa: E402
|
||||
|
||||
DEMO = ROOT / "docs_assets" / "demo"
|
||||
COMPONENT = ROOT / "website" / "src" / "components" / "InstallDemo.astro"
|
||||
DIST = ROOT / "website" / "dist"
|
||||
|
||||
|
||||
def _facts() -> dict:
|
||||
return json.loads((DEMO / "demo.json").read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
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)
|
||||
|
||||
def test_the_gif_and_the_poster_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"]))
|
||||
|
||||
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")
|
||||
|
||||
|
||||
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_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_command_typed_is_the_one_the_pages_give(self):
|
||||
from siteguides import install_command # noqa: PLC0415
|
||||
|
||||
self.assertIn(f'Type "{install_command("sh", "--for-games")}"\n', self.tape)
|
||||
|
||||
def test_the_shell_answers_only_the_site_address(self):
|
||||
typed = re.search(r'Type "curl -fsSL (\S+) \|', self.tape)
|
||||
self.assertIsNotNone(typed)
|
||||
driver = Path(make_demo.__file__).read_text(encoding="utf-8")
|
||||
self.assertIn(f'"$*" = "-fsSL {typed.group(1)}"', driver)
|
||||
|
||||
|
||||
class TheReadmeShowsTheGif(unittest.TestCase):
|
||||
def test_the_gif_sits_under_the_commands_with_its_description(self):
|
||||
readme = (ROOT / "README.md").read_text(encoding="utf-8")
|
||||
line = f""
|
||||
self.assertIn(line, readme)
|
||||
self.assertLess(readme.index("## Quick install"), readme.index(line))
|
||||
self.assertLess(readme.index(line), readme.index("## Download BIOS packs"))
|
||||
|
||||
def test_the_description_names_what_the_recording_measured(self):
|
||||
facts = _facts()
|
||||
words = generate_readme.demo_description(facts)
|
||||
for value in (facts["setup"], f"{facts['games']} games", f"{facts['files']} BIOS files",
|
||||
facts["size"]):
|
||||
self.assertIn(str(value), words)
|
||||
|
||||
|
||||
class TheVideoLoadsOnlyOnScreen(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.source = COMPONENT.read_text(encoding="utf-8")
|
||||
self.markup = self.source.split("<script>", 1)[0]
|
||||
|
||||
def test_nothing_is_fetched_from_the_html(self):
|
||||
self.assertIn('preload="none"', self.markup)
|
||||
self.assertNotRegex(self.markup, r"\s(src|poster)=")
|
||||
self.assertEqual(len(re.findall(r"<source data-src=", self.markup)), 2)
|
||||
self.assertIn("data-poster=", self.markup)
|
||||
self.assertIn("IntersectionObserver", self.source)
|
||||
|
||||
def test_it_is_muted_looped_and_inline(self):
|
||||
for attribute in ("muted", "loop", "playsinline"):
|
||||
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"))
|
||||
|
||||
def test_reduced_motion_keeps_the_poster_with_a_button(self):
|
||||
self.assertIn("matchMedia('(prefers-reduced-motion: reduce)')", self.source)
|
||||
self.assertIn("let wanted = !still();", self.source)
|
||||
self.assertIn('<button type="button" class="rb-demo-toggle" hidden>Play</button>',
|
||||
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.markup)
|
||||
|
||||
def test_the_component_is_registered(self):
|
||||
self.assertEqual(sitestarlight.COMPONENTS["InstallDemo"], "components/InstallDemo.astro")
|
||||
|
||||
|
||||
class TheTwoPagesShowIt(unittest.TestCase):
|
||||
def test_home_and_get_your_files(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)
|
||||
|
||||
def test_the_site_serves_the_video_not_the_gif(self):
|
||||
import generate_site # noqa: PLC0415
|
||||
|
||||
self.assertEqual(generate_site.DEMO_ASSETS, ("install.webm", "install.mp4", "install.png"))
|
||||
|
||||
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)
|
||||
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())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,115 @@
|
||||
---
|
||||
// The installer demo, recorded by scripts/make_demo.py and served from
|
||||
// public/assets/demo/. Nothing of it loads before it is on screen: the
|
||||
// poster and the sources are set by the script below once the video is in
|
||||
// view (preload="none", no src in the HTML). With reduced motion or Save-Data
|
||||
// the poster stays, and the button plays it. The button pauses it otherwise.
|
||||
interface Props {
|
||||
description: string;
|
||||
width: string;
|
||||
height: string;
|
||||
}
|
||||
|
||||
const { description, width, height } = Astro.props;
|
||||
const base = import.meta.env.BASE_URL.replace(/\/?$/, '/');
|
||||
const asset = (name: string) => `${base}assets/demo/${name}`;
|
||||
---
|
||||
|
||||
<figure class="rb-demo" data-demo data-pagefind-ignore>
|
||||
<video
|
||||
width={width}
|
||||
height={height}
|
||||
muted
|
||||
loop
|
||||
playsinline
|
||||
preload="none"
|
||||
disablepictureinpicture
|
||||
data-poster={asset('install.png')}
|
||||
aria-describedby="rb-demo-text"
|
||||
>
|
||||
<source data-src={asset('install.webm')} type="video/webm" />
|
||||
<source data-src={asset('install.mp4')} type="video/mp4" />
|
||||
</video>
|
||||
<button type="button" class="rb-demo-toggle" hidden>Play</button>
|
||||
<figcaption id="rb-demo-text" class="sr-only">{description}</figcaption>
|
||||
</figure>
|
||||
|
||||
<script>
|
||||
const still = () =>
|
||||
matchMedia('(prefers-reduced-motion: reduce)').matches ||
|
||||
Boolean((navigator as Navigator & { connection?: { saveData?: boolean } }).connection?.saveData);
|
||||
|
||||
for (const figure of document.querySelectorAll<HTMLElement>('[data-demo]')) {
|
||||
const video = figure.querySelector('video');
|
||||
const toggle = figure.querySelector<HTMLButtonElement>('.rb-demo-toggle');
|
||||
if (!video || !toggle) continue;
|
||||
let wanted = !still();
|
||||
let visible = false;
|
||||
let loaded = false;
|
||||
const sync = () => {
|
||||
if (wanted && visible) {
|
||||
if (!loaded) {
|
||||
loaded = true;
|
||||
for (const source of video.querySelectorAll('source')) source.src = source.dataset.src ?? '';
|
||||
video.load();
|
||||
}
|
||||
video.play().catch(() => {
|
||||
wanted = false;
|
||||
toggle.textContent = 'Play';
|
||||
});
|
||||
} else {
|
||||
video.pause();
|
||||
}
|
||||
toggle.textContent = wanted ? 'Pause' : 'Play';
|
||||
};
|
||||
toggle.hidden = false;
|
||||
toggle.addEventListener('click', () => {
|
||||
wanted = !wanted;
|
||||
sync();
|
||||
});
|
||||
new IntersectionObserver(
|
||||
(entries) => {
|
||||
visible = entries.some((entry) => entry.isIntersecting);
|
||||
if (visible && !video.poster) video.poster = video.dataset.poster ?? '';
|
||||
sync();
|
||||
},
|
||||
{ threshold: 0.25 },
|
||||
).observe(video);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.rb-demo {
|
||||
position: relative;
|
||||
margin-inline: 0;
|
||||
}
|
||||
.rb-demo video {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
background: #12131c;
|
||||
border: 1px solid var(--rb-border);
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
.rb-demo-toggle {
|
||||
position: absolute;
|
||||
inset-inline-end: 0.5rem;
|
||||
inset-block-end: 0.5rem;
|
||||
min-height: 2.75rem;
|
||||
padding: 0.4rem 1rem;
|
||||
font: inherit;
|
||||
font-size: var(--sl-text-sm);
|
||||
color: var(--sl-color-white);
|
||||
background: var(--rb-surface);
|
||||
border: 1px solid var(--rb-border-strong);
|
||||
border-radius: 0.4rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.rb-demo-toggle:hover {
|
||||
background: var(--sl-color-accent-low);
|
||||
}
|
||||
.rb-demo-toggle:focus-visible {
|
||||
outline: 2px solid var(--sl-color-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
</style>
|
||||
@@ -67,7 +67,7 @@ Every module in `tests/`, by the area it guards.
|
||||
| Pack probe | the cores a platform runs for a system, staging grown to a fixed point, runs mapped to pack and manifest entries, the line and its schema, the probe lock per run, that no page shows its lines. One class builds a real pack narrowed to PlayStation and lays it out both ways, from the archive and with `install.py --dest`, and skips without the collection | `test_pack_probe` |
|
||||
| Automation runner, disk cleanup, asset sync and bit-flip repair | fake jobs, git repositories with a bare origin and session worktrees, a committed file with one bit flipped, canned CI runs in place of `gh` | `test_autorun` |
|
||||
| Truth and native export | synthetic + cached originals | `test_export_counts`, `test_export_native_layer`, `test_export_report`, `test_exporters`, `test_truth_data_dir_readers`, `test_truth_diff`, `test_truth_layout` |
|
||||
| Scrapers, upstream and provenance | inline sources, synthetic | `test_batocera_md5_list`, `test_check_freshness`, `test_cited_lines`, `test_contributing_text`, `test_coverage_cache`, `test_data_dir_refs`, `test_dataclass_defaults`, `test_dedup`, `test_doc_pages`, `test_doc_surfaces`, `test_elec_scrapers`, `test_fbneo_parser`, `test_find_pinned_revision`, `test_hash_merge`, `test_launchers`, `test_launchers_emudeck`, `test_launchers_recalbox`, `test_launchers_retrobat`, `test_launchers_retrodeck`, `test_launchers_retropie`, `test_launchers_rocknix`, `test_mame_devices`, `test_mame_parser`, `test_mame_ref_audit`, `test_optional_dependencies`, `test_pin_build`, `test_profile_sync`, `test_prose_style`, `test_provenance`, `test_refresh_data_dirs`, `test_refresh_failures`, `test_refresh_stale`, `test_retrodeck_scraper_errors`, `test_retired_files`, `test_romset_recipes`, `test_schema_semantic_gate`, `test_scraper_contract`, `test_site_agents`, `test_site_arcade_boards`, `test_site_availability`, `test_site_bootstrap`, `test_site_built_gate`, `test_site_components`, `test_site_console_folders`, `test_site_content_data`, `test_site_copy`, `test_site_cores`, `test_site_css`, `test_site_density_cards`, `test_site_emulator_page`, `test_site_emulators`, `test_site_entry`, `test_site_entry_tables`, `test_site_exports`, `test_site_funnel`, `test_site_images`, `test_site_inputs`, `test_site_insights`, `test_site_integration`, `test_site_nav`, `test_site_picker`, `test_site_platform_density`, `test_site_platform_hashes`, `test_site_privacy`, `test_site_reference`, `test_site_render`, `test_site_search`, `test_site_system_links`, `test_site_system_names`, `test_site_system_sections`, `test_site_systems`, `test_site_validation`, `test_site_weight`, `test_site_wiki`, `test_site_write`, `test_source_ref_targets`, `test_upstream`, `test_upstream_index`, `test_validate_pr`, `test_validate_pr_flow`, `test_validate_pr_hashes`, `test_version_basis`, `test_workflow_paths` |
|
||||
| Scrapers, upstream and provenance | inline sources, synthetic | `test_batocera_md5_list`, `test_check_freshness`, `test_cited_lines`, `test_contributing_text`, `test_coverage_cache`, `test_data_dir_refs`, `test_dataclass_defaults`, `test_dedup`, `test_doc_pages`, `test_doc_surfaces`, `test_elec_scrapers`, `test_fbneo_parser`, `test_find_pinned_revision`, `test_hash_merge`, `test_launchers`, `test_launchers_emudeck`, `test_launchers_recalbox`, `test_launchers_retrobat`, `test_launchers_retrodeck`, `test_launchers_retropie`, `test_launchers_rocknix`, `test_mame_devices`, `test_mame_parser`, `test_mame_ref_audit`, `test_optional_dependencies`, `test_pin_build`, `test_profile_sync`, `test_prose_style`, `test_provenance`, `test_refresh_data_dirs`, `test_refresh_failures`, `test_refresh_stale`, `test_retrodeck_scraper_errors`, `test_retired_files`, `test_romset_recipes`, `test_schema_semantic_gate`, `test_scraper_contract`, `test_site_agents`, `test_site_arcade_boards`, `test_site_availability`, `test_site_bootstrap`, `test_site_built_gate`, `test_site_components`, `test_site_console_folders`, `test_site_content_data`, `test_site_copy`, `test_site_cores`, `test_site_css`, `test_site_demo`, `test_site_density_cards`, `test_site_emulator_page`, `test_site_emulators`, `test_site_entry`, `test_site_entry_tables`, `test_site_exports`, `test_site_funnel`, `test_site_images`, `test_site_inputs`, `test_site_insights`, `test_site_integration`, `test_site_nav`, `test_site_picker`, `test_site_platform_density`, `test_site_platform_hashes`, `test_site_privacy`, `test_site_reference`, `test_site_render`, `test_site_search`, `test_site_system_links`, `test_site_system_names`, `test_site_system_sections`, `test_site_systems`, `test_site_validation`, `test_site_weight`, `test_site_wiki`, `test_site_write`, `test_source_ref_targets`, `test_upstream`, `test_upstream_index`, `test_validate_pr`, `test_validate_pr_flow`, `test_validate_pr_hashes`, `test_version_basis`, `test_workflow_paths` |
|
||||
| Games on the card | small DATs, listxml and setup sources, a fake fetcher, fixture install lists, and the committed data against its schemas | `test_arcade_parents`, `test_game_catalogs`, `test_game_headers`, `test_game_regions`, `test_games_index`, `test_romfolders_card`, `test_romfolders_es` |
|
||||
|
||||
## Test architecture
|
||||
|
||||
Reference in new issue
Block a user