feat: publish unlisted GTIN tool

This commit is contained in:
desfoto automation
2026-09-25 19:01:51 +02:00
parent 68613788b8
commit 160d72f144
35 changed files with 5632 additions and 31 deletions

View File

@@ -26,7 +26,8 @@ import pages # noqa: E402
from content import CONTACT, LEGAL, SITE as META # noqa: E402
ROUTE_PATHS = [route[0] for route in pages.ROUTES]
HTML_FILES = sorted(SITE.rglob("*.html"))
HTML_FILES = [SITE / route[1] for route in pages.ROUTES] + [SITE / "404.html"]
GTIN_HTML = SITE / "gtin" / "index.html"
def read(path: Path) -> str:
@@ -48,8 +49,8 @@ class BuildLayout(unittest.TestCase):
self.assertTrue((SITE / rel).is_file(), f"missing {rel}")
def test_no_stray_html_files(self):
expected = {route[1] for route in pages.ROUTES} | {"404.html"}
actual = {str(f.relative_to(SITE)) for f in HTML_FILES}
expected = {route[1] for route in pages.ROUTES} | {"404.html", "gtin/index.html"}
actual = {str(f.relative_to(SITE)) for f in SITE.rglob("*.html")}
self.assertEqual(expected, actual)
def test_site_files_are_world_readable(self):
@@ -214,6 +215,9 @@ class PrivacyAndLegal(unittest.TestCase):
for needle in (
"keine Zugriffsprotokolle",
"keine Cookies",
"GTIN-Tool",
"bis zu 20",
"nicht an einen Server übertragen",
"youtube-nocookie.com",
"Art. 6 Abs. 1 lit. f DSGVO",
LEGAL["authority"],
@@ -314,6 +318,43 @@ class Content(unittest.TestCase):
self.assertIn("User-agent: *", read(SITE / "robots.txt"))
self.assertIn(f"Sitemap: {META['url']}/sitemap.xml", read(SITE / "robots.txt"))
def test_gtin_tool_is_public_but_unlisted(self):
text = read(GTIN_HTML)
self.assertIn("GTIN Generator", text)
self.assertIn('<link rel="canonical" href="https://desfoto.de/gtin/" />', text)
self.assertIn('<meta name="robots" content="noindex, nofollow" />', text)
self.assertIn('href="/impressum/"', text)
self.assertIn('href="/datenschutz/"', text)
self.assertNotIn('href="/"', text)
self.assertNotIn("/gtin/", read(SITE / "sitemap.xml"))
for file in HTML_FILES:
with self.subTest(file=str(file.relative_to(SITE))):
self.assertNotIn("/gtin", read(file))
def test_gtin_assets_and_local_data_storage_are_scoped(self):
html_text = read(GTIN_HTML)
for asset in ("./css/app.css", "./js/app.js", "./manifest.webmanifest"):
with self.subTest(asset=asset):
self.assertTrue((SITE / "gtin" / asset.removeprefix("./")).is_file())
self.assertIn(asset, html_text)
self.assertTrue((SITE / "gtin" / "sw.js").is_file())
self.assertFalse((SITE / "gtin" / "health").exists())
app_js = read(SITE / "gtin" / "js" / "app.js")
self.assertIn("desfoto.gtin.verlauf.v1", app_js)
self.assertIn("localStorage", app_js)
self.assertIn(".register('./sw.js', { scope: './' })", app_js)
self.assertNotIn("fetch(", app_js)
self.assertNotIn("XMLHttpRequest", app_js)
service_worker = read(SITE / "gtin" / "sw.js")
self.assertIn("name.startsWith(CACHE_PREFIX)", service_worker)
self.assertIn("if (!ziel.pathname.startsWith(SCOPE_PFAD)) return;", service_worker)
self.assertNotIn("/impressum/", service_worker)
def test_gtin_manifest_uses_the_web_app_manifest_mime_type(self):
conf = read(ROOT / "nginx.conf")
self.assertIn("location = /gtin/manifest.webmanifest", conf)
self.assertIn("default_type application/manifest+json;", conf)
class Redesign(unittest.TestCase):
"""The 2026 redesign: Arbeitsbereiche, real portfolio, logo, motion layer."""