rea/scripts/test-website.py

131 lines
4.7 KiB
Python

"""Exercise automatic route discovery and mistakes that break search metadata."""
import contextlib
import importlib.util
import io
from pathlib import Path
import shutil
import tempfile
import unittest
import xml.etree.ElementTree as ET
ROOT = Path(__file__).resolve().parent.parent
def load_script(name):
"""Load a repository script without executing its command-line entrypoint."""
spec = importlib.util.spec_from_file_location(name, ROOT / "scripts" / f"{name}.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
PREPARE = load_script("prepare-website")
VERIFY = load_script("verify-website")
class WebsiteSearchChecks(unittest.TestCase):
"""Use authored pages and temporary fixtures to check the publishing boundary."""
def test_sitemap_tracks_added_moved_removed_and_nonindexable_pages(self):
with tempfile.TemporaryDirectory() as directory:
site = Path(directory)
def page(name, head="", body=""):
path = site / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(f"<html><head>{head}</head><body>{body}</body></html>")
return path
page("index.html")
nested = page("guides/new/index.html")
page(
"examples/fixture/index.html",
'<meta name="robots" content="NOINDEX, follow">',
)
page("draft.html", '<meta name="googlebot" content="none">')
page("code & notes.html")
page("visible/index.html", body='<meta name="robots" content="noindex">')
(site / "asset.json").write_text("{}")
def urls():
with contextlib.redirect_stdout(io.StringIO()):
PREPARE.prepare_sitemap(site)
sitemap = ET.parse(site / "sitemap.xml")
return {node.text for node in sitemap.findall(".//{*}loc")}
self.assertEqual(
urls(),
{
"https://rea.tools/",
"https://rea.tools/guides/new/",
"https://rea.tools/code%20%26%20notes.html",
"https://rea.tools/visible/",
},
)
nested.rename(site / "moved.html")
self.assertIn("https://rea.tools/moved.html", urls())
self.assertNotIn("https://rea.tools/guides/new/", urls())
(site / "moved.html").unlink()
self.assertNotIn("https://rea.tools/moved.html", urls())
def test_verifier_rejects_wrong_canonicals_stale_sitemaps_and_missing_previews(
self,
):
cases = (
(
"index.html",
'rel="canonical" href="https://rea.tools/"',
'rel="canonical" href="https://morluto.github.io/rea/"',
"head canonical",
),
(
"showcase/aegis/index.html",
'rel="canonical" href="https://rea.tools/showcase/aegis/"',
'rel="canonical" href="https://rea.tools/"',
"head canonical",
),
(
"examples/notes-electron/index.html",
'content="noindex"',
'content="index"',
"must have a head noindex",
),
(
"sitemap.xml",
"https://rea.tools/faq/",
"https://rea.tools/deleted/",
"each indexable page once",
),
(
"faq/index.html",
'name="twitter:card" content="summary_large_image"',
'name="twitter:card" content="summary"',
"twitter:card",
),
)
for name, old, new, message in cases:
with self.subTest(name=name), tempfile.TemporaryDirectory() as directory:
site = Path(directory) / "public"
shutil.copytree(ROOT / "website/public", site)
path = site / name
source = path.read_text()
self.assertIn(old, source)
path.write_text(source.replace(old, new, 1))
self.assertTrue(
any(message in error for error in VERIFY.check_site(site)[3])
)
with tempfile.TemporaryDirectory() as directory:
site = Path(directory) / "public"
shutil.copytree(ROOT / "website/public", site)
(site / "assets/social-card.png").unlink()
self.assertIn(
"Social preview PNG is missing; run scripts/prepare-website.py",
VERIFY.check_site(site)[3],
)
if __name__ == "__main__":
unittest.main()