This commit is contained in:
parent
728e48fc4f
commit
cc02023625
10 changed files with 341 additions and 39 deletions
75
tools/e2e.sh
75
tools/e2e.sh
|
|
@ -111,7 +111,7 @@ header_has() {
|
|||
}
|
||||
|
||||
echo "== status codes =="
|
||||
for p in / /shop /shop/fp6-pmos /projects /posts /demos /demos/raytracer \
|
||||
for p in / /about /shop /shop/fp6-pmos /projects /posts /demos /demos/raytracer \
|
||||
/legal/privacy /legal/terms /legal/imprint /feed.xml /sitemap.xml /api/healthz; do
|
||||
status "$p" 200
|
||||
done
|
||||
|
|
@ -145,6 +145,11 @@ else
|
|||
bad "/demo Location header" "not /demos"
|
||||
fi
|
||||
|
||||
# Open shop or coming-soon? The pricing blob (data-cc) exists only on the real
|
||||
# order form, so its presence is the probe. Used by the script-shape checks
|
||||
# below and by the checkout/order/invoice gating further down.
|
||||
if curl -s "$BASE/shop/fp6-pmos" | grep -q 'data-cc='; then SHOP_OPEN=1; else SHOP_OPEN=0; fi
|
||||
|
||||
echo "== 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.
|
||||
|
|
@ -160,16 +165,18 @@ for pg in /legal/privacy /legal/terms /legal/imprint /shop/fp6-pmos; do
|
|||
done
|
||||
|
||||
# Shop pages are the one exception to script-free: they carry exactly ONE
|
||||
# inline script — the timezone price hint. Pin its 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.
|
||||
# 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 pg in /shop /shop/fp6-pmos; do
|
||||
n=$(curl -s "$BASE$pg" | grep -c '<script' || true)
|
||||
n=$(curl -s "$BASE$pg" | grep -c '<script>' || true)
|
||||
if [ "$n" = 1 ]; then
|
||||
ok "$pg carries exactly one script (the price hint)"
|
||||
ok "$pg carries exactly one executable script (the price hint)"
|
||||
else
|
||||
bad "$pg script count" "expected 1, got $n"
|
||||
bad "$pg script count" "expected 1 bare <script>, got $n"
|
||||
fi
|
||||
if curl -s "$BASE$pg" | grep -qE '<script[^>]*src='; then
|
||||
bad "$pg script" "an external script crept in under the inline exception"
|
||||
|
|
@ -185,6 +192,46 @@ done
|
|||
body_has /shop/fp6-pmos 'cc-noneu' "price hint tags the non-EU outcome"
|
||||
body_has /shop/fp6-pmos 'cc-eu' "price hint tags the confirmed-EU outcome too"
|
||||
|
||||
# ── structured data ──
|
||||
# The JSON-LD blocks are the machine-readable identity and offer records,
|
||||
# born from a Google AI overview flatly asserting that nobody named Catcrafts
|
||||
# sells Fairphone hardware. Both must parse as JSON and carry the facts.
|
||||
extract_ld() {
|
||||
curl -s "$BASE$1" \
|
||||
| grep -o '<script type="application/ld+json">[^<]*' \
|
||||
| sed 's/^<script type="application\/ld+json">//'
|
||||
}
|
||||
if extract_ld / | jq -e '."@type" == "Organization" and .vatID == "NL003329281B38"' >/dev/null 2>&1; then
|
||||
ok "home Organization schema parses and carries the VAT identity"
|
||||
else
|
||||
bad "Organization schema" "missing, unparseable, or wrong identity"
|
||||
fi
|
||||
if extract_ld /shop/fp6-pmos | jq -e '."@type" == "Product" and (.offers | length) == 3' >/dev/null 2>&1; then
|
||||
ok "product schema parses with one offer per colour"
|
||||
else
|
||||
bad "Product schema" "missing, unparseable, or wrong offer count"
|
||||
fi
|
||||
body_has /shop/fp6-pmos '"price":"563.30"' "schema price is the checkout integer"
|
||||
if [ "$SHOP_OPEN" = 1 ]; then
|
||||
body_has /shop/fp6-pmos 'schema.org/InStock' "open shop maps to InStock availability"
|
||||
else
|
||||
body_has /shop/fp6-pmos 'schema.org/PreOrder' "coming-soon maps to PreOrder availability"
|
||||
fi
|
||||
# og: tags are the link-preview card on Mastodon and Lemmy — where the
|
||||
# traffic actually comes from.
|
||||
body_has / 'property="og:title"' "home page has og:title"
|
||||
body_has /shop/fp6-pmos 'og:image" content="https://catcrafts.net/fp6-pmos.jpg' "product og:image is the absolute photo URL"
|
||||
|
||||
# The about page is the person-company weld: the founder must be named in the
|
||||
# HTML, the Person schema must parse, and the home page byline must link it.
|
||||
body_has /about 'Jorijn van der Graaf' "about page names the founder"
|
||||
body_has / 'Jorijn van der Graaf' "home page carries the founder byline"
|
||||
if extract_ld /about | jq -e '.mainEntity.name == "Jorijn van der Graaf"' >/dev/null 2>&1; then
|
||||
ok "about Person schema parses and names the founder"
|
||||
else
|
||||
bad "Person schema" "missing, unparseable, or wrong name"
|
||||
fi
|
||||
|
||||
# The shop card: one euro number as the crawler/no-JS text, every supported
|
||||
# currency pre-formatted server-side as a data attribute for the script to
|
||||
# pick from. Converted amounts carry "~". CAD converts the ex-VAT price;
|
||||
|
|
@ -332,6 +379,7 @@ header_has /sitemap.xml 'content-type: *application/xml' "sitemap content-ty
|
|||
|
||||
echo "== sitemap and feed content =="
|
||||
body_has /sitemap.xml "/shop/fp6-pmos" "sitemap lists the product"
|
||||
body_has /sitemap.xml "/about" "sitemap lists the about page"
|
||||
body_has /sitemap.xml "/legal/privacy" "sitemap lists the privacy page"
|
||||
body_has /sitemap.xml "/demos" "sitemap lists the demos page"
|
||||
|
||||
|
|
@ -376,12 +424,11 @@ body_lacks /sitemap.xml "/blog" "sitemap does not advertise the redire
|
|||
body_lacks /sitemap.xml "/order" "sitemap does not advertise order pages"
|
||||
body_has /feed.xml "<feed xmlns=\"http://www.w3.org/2005/Atom\">" "feed is Atom"
|
||||
|
||||
# Open shop or coming-soon? The pricing blob (data-cc) exists only on the real
|
||||
# order form, so its presence is the probe. The checkout, order-lifecycle and
|
||||
# invoice suites below only run when the shop is open; the coming-soon branch
|
||||
# asserts the closed state instead. Launch day (status flip to "available" in
|
||||
# Catcrafts.Shared-Content.cppm) re-arms the full suite with no e2e edit.
|
||||
if curl -s "$BASE/shop/fp6-pmos" | grep -q 'data-cc='; then SHOP_OPEN=1; else SHOP_OPEN=0; fi
|
||||
# SHOP_OPEN was probed above (before the script-shape checks). The checkout,
|
||||
# order-lifecycle and invoice suites below only run when the shop is open; the
|
||||
# coming-soon branch asserts the closed state instead. Launch day (status flip
|
||||
# to "available" in Catcrafts.Shared-Content.cppm) re-arms the full suite with
|
||||
# no e2e edit.
|
||||
|
||||
echo "== the shop front =="
|
||||
# The price is rendered from the same integers the checkout charges, with the
|
||||
|
|
|
|||
Loading…
Reference in a new issue