catcrafts.net/tests/ShouldStayScriptFree/main.cpp

151 lines
7.7 KiB
C++
Raw Normal View History

2026-08-15 00:54:05 +02:00
/*
catcrafts.net
Copyright (C) 2026 Catcrafts
The source code of this website is made available for viewing purposes only.
No permission is granted to copy, modify, distribute, or create derivative works.
*/
// The no-JavaScript guarantee. The site must be complete without the wasm
// module — if these fail, the SSR work has regressed and crawlers see an
// empty page again. Shop pages carry exactly ONE pinned-down inline script
// (the timezone price hint); everything else ships none at all.
import std;
import Catcrafts.E2eHarness;
using namespace Catcrafts::E2e;
namespace {
// Visible prose only: strip tags, so an instance name inside an href (a
// post's own permalink necessarily contains one) does not trip the check.
std::string StripTags(std::string_view html) {
std::string out;
out.reserve(html.size());
bool inTag = false;
for (char c : html) {
if (c == '<') inTag = true;
else if (c == '>') { inTag = false; out.push_back(' '); }
else if (!inTag) out.push_back(c);
}
return out;
}
std::string Lower(std::string s) {
for (char& c : s) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
return s;
}
} // namespace
int main(int argc, char** argv) {
TestServer srv(argv[1], 8211);
// ── the no-JavaScript guarantee ───────────────────────────────────
srv.BodyHas("/projects", "imsd", "/projects has content in the HTML");
srv.BodyHas("/projects", "<title>Projects", "/projects has a real title");
srv.BodyLacks("/projects", "<script", "/projects ships no script at all");
srv.BodyLacks("/legal/privacy", "<script", "/legal/privacy ships no script");
srv.BodyHas("/financials", "<title>Financials", "/financials has a real title");
srv.BodyLacks("/financials", "<script", "/financials ships no script");
// Placeholders are dev-only markers; one reaching production is a content
// bug (an imprint that says PLACEHOLDER once shipped exactly that way).
for (std::string_view pg : { "/legal/privacy", "/legal/terms", "/legal/imprint",
"/shop/fp6-pmos", "/financials" }) {
srv.BodyLacks(std::string(pg), "PLACEHOLDER",
std::format("{} ships no placeholder markers", pg));
}
// Shop pages are the one exception to script-free: they carry exactly ONE
// EXECUTABLE inline script — the timezone price hint, whose tag is the
// bare <script>. JSON-LD blocks (<script type="application/ld+json">) are
// inert data the browser never executes, so they don't count against the
// rule. Pin the shape hard: inline only (no src=, so nothing external can
// ever ride in under this exception), no network APIs, and the page must
// remain complete without it — both prices in the markup regardless.
for (std::string_view pg : { "/shop", "/shop/fp6-pmos" }) {
const std::string body = srv.Body(std::string(pg));
const std::size_t n = CountOccurrences(body, "<script>");
Check(n == 1,
std::format("{} carries exactly one executable script (the price hint)", pg),
std::format("expected 1 bare <script>, got {}", n));
const std::regex srcTag(R"(<script[^>]*src=)");
Check(!std::regex_search(body, srcTag),
std::format("{} script is inline, not external", pg));
const std::size_t open = body.find("<script>");
const std::size_t close = body.find("</script>", open);
std::string_view inlineScript;
if (open != std::string::npos && close != std::string::npos) {
inlineScript = std::string_view(body).substr(open, close - open);
}
bool network = false;
for (std::string_view api : { "fetch", "XMLHttpRequest", "WebSocket",
"navigator.sendBeacon" }) {
network = network || inlineScript.find(api) != std::string_view::npos;
}
Check(!network, std::format("{} script makes no network calls", pg));
}
srv.BodyHas("/shop/fp6-pmos", "cc-noneu", "price hint tags the non-EU outcome");
srv.BodyHas("/shop/fp6-pmos", "cc-eu", "price hint tags the confirmed-EU outcome too");
// The renderer loads only where a demo entry declares needsWasm — the
// demo LIST is a content page and must stay free of it.
srv.BodyLacks("/demos", "<script", "/demos itself ships no script");
srv.BodyHas("/demos/raytracer", "id=\"webgpu-demo\"", "raytracer page has the mount element");
// Exactly one chrome root: the wasm adopts the server's, never builds a second.
{
const std::size_t roots =
CountOccurrences(srv.Body("/demos/raytracer"), "id=\"catcrafts-root\"");
Check(roots == 1, "raytracer page has exactly one chrome root",
std::format("expected 1, got {}", roots));
}
// ── SSR / wasm head interaction ───────────────────────────────────
// catcrafts-head.js used to set document.title unconditionally, which
// replaced the server's per-route title with the generic site name and
// appended a second stylesheet, favicon and viewport tag. The
// <meta name="cc-ssr"> marker is what it now checks; if that marker stops
// being emitted the guard silently stops working, so assert it is present
// and that the head is not duplicated.
srv.BodyHas("/demos/raytracer", "name=\"cc-ssr\"", "SSR marker present for head.js to detect");
srv.BodyHas("/demos/raytracer", "<title>Real-time ray tracer",
"demo page keeps its route-specific title");
{
const std::string body = srv.Body("/demos/raytracer");
for (std::string_view probe : { "rel=\"stylesheet\"", "rel=\"icon\"",
"name=\"viewport\"" }) {
const std::size_t n = CountOccurrences(body, probe);
Check(n == 1, std::format("demo page has exactly one {}", probe),
std::format("expected 1, got {}", n));
}
}
// The base tag belongs only where the runtime needs it. On a content page
// it is dead weight and one more thing that could retarget a future
// relative link.
srv.BodyLacks("/posts", "<base", "/posts has no base tag");
srv.BodyLacks("/shop/fp6-pmos", "<base", "/shop/<slug> has no base tag");
// ── home page actions ─────────────────────────────────────────────
srv.BodyHas("/", "Browse projects", "home links to projects");
srv.BodyHas("/", "Browse shop", "home links to the shop");
srv.BodyLacks("/", "ray tracer", "home no longer pushes the ray tracer");
// ── instance-agnostic copy ────────────────────────────────────────
// The account lives on one instance but posts go into communities on
// others, so no page should name a specific instance as though it were
// the home of the discussion. In visible text, not in href values.
for (std::string_view pg : { "/", "/posts", "/shop" }) {
const std::string prose = Lower(StripTags(srv.Body(std::string(pg))));
Check(prose.find("ani.social") == std::string::npos,
std::format("{} names no specific instance in visible text", pg));
}
srv.BodyHas("/posts", "fediverse", "/posts refers to the fediverse generally");
// The fediverse account is not advertised at all — only individual posts are.
srv.BodyLacks("/", "/u/", "footer does not link a fediverse profile");
srv.BodyLacks("/posts", "/u/", "/posts links no account profile, only threads");
return Finish();
}