$ node --test --test-reporter=spec "test/*.test.mjs"   (Node v22.22.3, Windows 11, 1 October 2026)

✔ default profile (0.8127ms)
✔ good (0.157ms)
✔ js-shell (0.0725ms)
✔ canonical-wrong (0.0633ms)
✔ canonical-conflict (0.1177ms)
✔ canonical-in-body (0.0568ms)
✔ noindex-meta (0.0488ms)
✔ noindex-header (0.0498ms)
✔ jsonld-malformed (0.2671ms)
✔ redirect-loop (0.4549ms)
✔ blocked (0.1343ms)
✔ not-in-sitemap (0.0502ms)
✔ temporary-redirect (0.0413ms)
✔ server-error (0.0326ms)
✔ timeout (0.0286ms)
✔ oversized (0.0301ms)
✔ redirect-to-private (0.0303ms)
✔ edge-drift profile (0.0265ms)
✔ edge-drift (0.0366ms)
✔ robots-503 profile (0.0378ms)
✔ robots-503 (0.0304ms)
✔ no-robots profile (0.0333ms)
✔ no-robots (0.0279ms)
✔ robots-html profile (0.0265ms)
✔ robots-html (0.037ms)
▶ path matching: Google's URL-matching table
  ✔ / (1.131ms)
  ✔ /* (0.2369ms)
  ✔ /$ (0.2013ms)
  ✔ /fish (0.2736ms)
  ✔ /fish* (0.1446ms)
  ✔ /fish/ (1.011ms)
  ✔ /*.php (0.4062ms)
  ✔ /*.php$ (0.3127ms)
  ✔ /fish*.php (0.2976ms)
✔ path matching: Google's URL-matching table (5.3966ms)
▶ rule precedence: Google's sample situations
  ✔ /page with allow: /p + disallow: / (0.3129ms)
  ✔ /folder/page with allow: /folder + disallow: /folder (0.1206ms)
  ✔ /page.htm with allow: /page + disallow: /*.htm (0.1111ms)
  ✔ /page.php5 with allow: /page + disallow: /*.ph (0.0881ms)
  ✔ / with allow: /$ + disallow: / (0.0772ms)
  ✔ /page.htm with allow: /$ + disallow: / (0.0724ms)
✔ rule precedence: Google's sample situations (1.1384ms)
▶ groups: RFC 9309
  ✔ a crawler named in a group obeys only that group, not * (0.2284ms)
  ✔ a crawler with no group of its own falls back to * (0.1231ms)
  ✔ user-agent matching is case-insensitive (0.0898ms)
  ✔ consecutive User-agent lines share a group; comments and blank lines do not end it (0.0933ms)
  ✔ groups naming the same crawler are combined (0.0862ms)
  ✔ paths are case-sensitive (0.0601ms)
  ✔ /robots.txt is always allowed (0.0441ms)
  ✔ Sitemap lines are read (0.9081ms)
  ✔ with no group for the crawler and no * group, everything is allowed (0.152ms)
  ✔ an empty Disallow matches nothing (0.3113ms)
  ✔ a product token with a version still names the crawler (0.2201ms)
✔ groups: RFC 9309 (2.686ms)
▶ encoding
  ✔ non-ASCII paths match their percent-encoded form (0.3923ms)
  ✔ escaped unreserved characters match their plain form (0.7445ms)
✔ encoding (1.2595ms)
▶ comparing the live file with the source file
  ✔ identical files (0.6156ms)
  ✔ comment-only differences keep the same rules (0.1674ms)
  ✔ lines added at the edge are reported (0.1855ms)
✔ comparing the live file with the source file (1.232ms)
▶ request guard
  ✔ addresses are classified (6.4998ms)
  ✔ localhost is refused unless allowed, and answers when it is (22.3931ms)
  ✔ private addresses are refused even with --allow-localhost (0.3449ms)
  ✔ a name that resolves to loopback is refused like the address (4.1821ms)
  ✔ credentials in the URL and other schemes are refused (0.2452ms)
✔ request guard (34.8923ms)
▶ sitemap reader
  ✔ a sitemap over the byte limit is reported as over the limit, not as a broken file (28.7021ms)
✔ sitemap reader (28.9263ms)
▶ HTML reader
  ✔ an element that cannot be in <head> moves what follows it into <body> (7.9584ms)
  ✔ JSON-LD scripts are not counted as scripts that build the page (0.7872ms)
  ✔ text inside script, style and template is not page text (1.2437ms)
✔ HTML reader (10.4975ms)
▶ JSON-LD checks
  ✔ a block without a schema.org @context is a finding (1.2018ms)
  ✔ a page node naming another URL of the same site is a finding (0.3138ms)
  ✔ list items pointing at other pages are not mistaken for the page itself (0.9327ms)
  ✔ a FAQ question missing from the page text is a finding (0.6329ms)
  ✔ a reference to an entity described elsewhere is left for review (0.3196ms)
✔ JSON-LD checks (3.5846ms)
ℹ tests 70
ℹ suites 9
ℹ pass 70
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 4270.9501
