/* 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. */ // Post media and post pages over real HTTP. The media IS the content of these // posts (screen recordings of the work), and it must come from our own // origin: the privacy notice states that everything the browser loads comes // from catcrafts.net, and a third-party embed would send every visitor's IP // to whichever instance hosted the file. import std; import Catcrafts.E2eHarness; using namespace Catcrafts::E2e; namespace { // Media loaded from a third party — `poster` is in the list because a video // poster is fetched on page load exactly like an src is. const std::regex kThirdPartyMedia( R"((src|srcset|href|poster)="https?://[^"]*\.(mp4|webm|webp|avif|png|jpe?g|gif))"); std::vector PostPathsFromSitemap(TestServer& srv, std::size_t limit) { const std::string sitemap = srv.Body("/sitemap.xml"); const std::regex slug(R"(/posts/[a-z0-9-]+)"); std::vector out; for (auto it = std::sregex_iterator(sitemap.begin(), sitemap.end(), slug); it != std::sregex_iterator() && out.size() < limit; ++it) { if (std::ranges::find(out, it->str()) == out.end()) out.push_back(it->str()); } return out; } } // namespace int main(int argc, char** argv) { TestServer srv(argv[1], 8214); const std::string posts = srv.Body("/posts"); // ── post media ──────────────────────────────────────────────────── Check(std::regex_search(posts, std::regex(R"(<(img|video) class="post-media__item")")), "/posts embeds its media"); Check(!std::regex_search(posts, kThirdPartyMedia), "/posts loads no media from a third party"); // Dimensions prevent layout shift as each file arrives. Needs ffprobe at // fetch time — a build host without it produces no dimensions at all, // which is what this catches. Check(std::regex_search(posts, std::regex( R"(]*width="[0-9]+" height="[0-9]+")")), "images carry width/height"); // Videos too. This assertion exists because they silently lost theirs: // ffprobe appends an empty CSV field for some files, so parsing // `width,height` as one joined string yielded a height of "480x" and the // guard discarded both. Check(std::regex_search(posts, std::regex( R"(