diff --git a/scripts/generate_site.py b/scripts/generate_site.py
index 4fc19c664..a94048f90 100644
--- a/scripts/generate_site.py
+++ b/scripts/generate_site.py
@@ -3426,8 +3426,6 @@ def _launcher_refs(platform: str, config: dict) -> dict[str, list[str]]:
return found
-# The characters of a hash a row shows, as many as the served copy's sha1.
-SHORT_HASH = 8
# The hash a row shows first, when the list does not say which it checks.
_ROW_HASHES = ("sha1", "md5", "sha256", "crc32")
# An untested entry whose reason is a hash the bytes do not match.
@@ -5856,7 +5854,8 @@ _CARD_HASHES = ("SHA1", "MD5", "CRC32", "SHA256")
# The words a card's fold names its other facts by.
_CARD_MORE_WORDS = {"Romset": "romset", "Collection path": "collection path",
"Catalogued": "catalogues"}
-# A SHA1 shown short: as long as the card's own address (sha1-<12 hex>).
+# A hash shown short, in a card, a platform row or an emulator table: as long
+# as the card's own address (sha1-<12 hex>), one length across the site.
SHORT_HASH = 12
diff --git a/tests/test_site_density_cards.py b/tests/test_site_density_cards.py
index adf141516..afe64f11d 100644
--- a/tests/test_site_density_cards.py
+++ b/tests/test_site_density_cards.py
@@ -260,7 +260,9 @@ class TheScriptWiresBoth(unittest.TestCase):
self.assertIn("\ninstallPickers();\n", source)
self.assertIn("\ncopyButtons();\n", source)
self.assertIn("const SETUP_KEY = 'rb-setup';", source)
- self.assertIn("const COPYABLE = '[data-copy], .rb-sys-file dd.rb-hash';", source)
+ self.assertIn("const COPYABLE = '[data-copy]:not(button), .rb-sys-file dd.rb-hash';", source)
+ # A platform row's button carries its own data-copy: never given a second button.
+ self.assertIn("const own = button.dataset.copy;", source)
# Visible words of each built page: measured before the round and after it,
diff --git a/tests/test_site_entry_tables.py b/tests/test_site_entry_tables.py
index cd72b7c38..ab8a733aa 100644
--- a/tests/test_site_entry_tables.py
+++ b/tests/test_site_entry_tables.py
@@ -131,7 +131,7 @@ class ALongEmulatorPageIsOneTable(unittest.TestCase):
# and in the card.
md5 = f"{9:032x}"
self.assertIn(f'MD5 {md5[:12]}', optional)
- self.assertIn(f'MD5: {md5}', optional)
+ self.assertRegex(optional, rf'MD5: ]*>{md5}')
def test_a_shorter_page_keeps_its_cards(self):
files = [_archive(i) for i in range(gs.EMULATOR_TABLE_MIN - 1)]
diff --git a/tests/test_site_platform_density.py b/tests/test_site_platform_density.py
index da917c295..dae52b5ec 100644
--- a/tests/test_site_platform_density.py
+++ b/tests/test_site_platform_density.py
@@ -102,7 +102,7 @@ class ARowPerFile(unittest.TestCase):
details, button = row.split("")
self.assertNotIn("')
+ f'aria-label="Copy MD5 {md5[:generate_site.SHORT_HASH]}">{md5[:generate_site.SHORT_HASH]}')
# Every hash whole in the details, with the size.
for value in (md5, sha1, "0badf00d"):
self.assertIn(value, details)
@@ -140,7 +140,7 @@ class ARowPerFile(unittest.TestCase):
values = ["1" * 32, "2" * 32, "3" * 32]
row = self._row({"name": "x", "status": "ok"}, {"md5": ",".join(values)})
self.assertIn(f'data-copy="{values[0]}"', row)
- self.assertIn(f">{values[0][:8]} +2", row)
+ self.assertIn(f">{values[0][:generate_site.SHORT_HASH]} +2", row)
for value in values:
self.assertIn(f'{value}', row)