- JournalPostCard: the title link is the card's only anchor; the image is no longer a link and the separate read-more link is gone; the whole card stays clickable through a CSS stretched link with the focus ring on the card; prop headingLevel (2 | 3, default 2) - related posts keep their h2 and render their cards at h3; JournalPostList passes headingLevel through - smoke checks headings=strict (one h1, no skipped level) and cardlinks=N on every fixture page; expectation files for postTitleTag forced to h1 and p (the documented two-h1 and no-h1 consequences) BREAKING CHANGE: .journal-post-card__more and the image link (.journal-post-card__image is now a div) are removed from post cards.
106 lines
6.4 KiB
Plaintext
106 lines
6.4 KiB
Plaintext
# SSR expectations against the acme fixtures (testing/fixtures/acme).
|
|
# Grammar: STATUS PATH [contains=TEXT] [notcontains=TEXT] [count=TEXT:N]
|
|
# [type=PREFIX] [header=NAME: VALUE] [noheader=NAME]; resolves PATH N
|
|
# The consumer enables en (default, no prefix) and pl (/pl prefix). The
|
|
# fixture SEO API (fixtures/acme/seo) answers the site URL https://acme.test.
|
|
|
|
# The post page on the shared SEO layer: exactly one H1 in both Journal H1
|
|
# modes, one canonical and one JSON-LD script (UI-SPEC L6, D-97).
|
|
200 /blog/heading-on count=<h1:1 contains=<h1 class="journal-post__title" count=application/ld+json:1 count=rel="canonical":1
|
|
200 /blog/heading-on contains=<h2>Body heading one</h2> notcontains=<p class="journal-post__title"
|
|
200 /blog/heading-off count=<h1:1 contains=<p class="journal-post__title" count=application/ld+json:1 count=rel="canonical":1
|
|
200 /blog/heading-off contains=<h1>Body heading one</h1>
|
|
# The CMS head wins: its title, no second title.
|
|
200 /blog/heading-on contains=<title>Heading On Sample | Acme Blog</title> count=<title>:1
|
|
# Without a CMS answer the post's own title and description are the fallback.
|
|
200 /blog/uncategorised-note contains=<title>Uncategorised Note</title> contains=Uncategorised Note: a short summary.
|
|
|
|
# Single post
|
|
200 /blog/hello-acme contains=Hello Acme
|
|
200 /blog/hello-acme contains=https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ
|
|
404 /blog/missing-post
|
|
200 /blog/unsafe-sample contains=<p>Before</p><p>After</p> notcontains=alert(
|
|
404 /blog/draft-note
|
|
|
|
# Post index and pagination (9 per page, 13 published posts: 2 pages)
|
|
200 /blog contains=Hello Acme
|
|
200 /blog?page=2
|
|
404 /blog?page=3
|
|
404 /blog?page=abc
|
|
404 /blog?page=0
|
|
200 /pl/blog contains=Najnowsze wpisy
|
|
|
|
# Categories (include_empty=1; content renders above the list)
|
|
200 /blog/category/news contains=Acme news marker
|
|
200 /blog/category/quiet contains=There are no posts in this category yet.
|
|
404 /blog/category/nope
|
|
|
|
# Related posts fall back to the latest posts for a post without a category
|
|
200 /blog/uncategorised-note contains=Hello Acme
|
|
|
|
# Author (slug and login resolve) and tag pages
|
|
200 /blog/author/jane-doe contains=Jane Doe
|
|
200 /blog/author/jdoe contains=<link rel="canonical" href="https://acme.test/blog/author/jane-doe"
|
|
200 /blog/hello-acme contains=href="/blog/author/jane-doe" notcontains=/blog/author/jdoe
|
|
404 /blog/author/nobody
|
|
200 /blog/tag/release contains=Hello Acme
|
|
404 /blog/tag/nope
|
|
|
|
# RSS, canonical and meta (sitemaps come from the CMS, not the layer)
|
|
200 /rss.xml type=application/rss+xml
|
|
200 /blog contains=href="/rss.xml"
|
|
200 /blog?page=2 contains=?page=2
|
|
200 /blog?page=2 contains=<link rel="canonical" href="https://acme.test/blog?page=2">
|
|
200 /blog/hello-acme contains=og:title
|
|
200 /blog/hello-acme contains=Acme says hello and introduces its blog.
|
|
|
|
# Search (the playground sets journal.routes.search: '/search'). 11 posts
|
|
# match "acme": 2 pages of 9. Not route-cached (the playground caches /**)
|
|
# and noindex, follow.
|
|
200 /search?q=acme contains=Hello Acme
|
|
200 /search?q=acme contains=11 results
|
|
200 /search?q=acme contains=noindex, follow header=x-robots-tag: noindex, follow
|
|
200 /search?q=acme&page=2 contains=Acme Note 12
|
|
404 /search?q=acme&page=3
|
|
404 /search?q=acme&page=abc
|
|
200 /search?q=unsafe contains=Unsafe Sample notcontains=Hello Acme
|
|
200 /search?q=unsafe contains=1 result
|
|
200 /search?q=zzzzzz contains=Nothing found for “zzzzzz”.
|
|
200 /search?q=ac contains=Type at least 3 characters.
|
|
200 /search contains=Type what you are looking for.
|
|
# The term is text everywhere it is rendered (attributes may keep a bare <).
|
|
200 /search?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E contains=Search results: <script>alert(1) notcontains=>Search results: <script>
|
|
200 /pl/search?q=acme contains=11 wyników
|
|
200 /pl/search?q=unsafe contains=1 wynik
|
|
# The header box renders its no-JS form to the search route on every page.
|
|
200 /blog contains=action="/search"
|
|
200 /pl/blog contains=action="/pl/search"
|
|
|
|
# Body links, outbound rules and images (UI-SPEC L7). hello-acme: new tab on
|
|
# with the default hint; auto-links in document order up to autolink_max 2,
|
|
# never in a heading, never to the page itself.
|
|
200 /blog/hello-acme contains=<a href="/blog/acme-note-07">anvils</a> contains=<a href="/blog/acme-note-05">rockets</a> notcontains=<a href="/blog/acme-note-03"> notcontains=<a href="/blog/acme-note-06"> notcontains=<a href="https://acme.test/blog/hello-acme"
|
|
200 /blog/hello-acme contains=<a href="https://partner.test/review" rel="ugc nofollow noopener" target="_blank">the review<span class="seo-visually-hidden"> (opens in a new tab)</span></a>
|
|
200 /blog/hello-acme contains=<a href="https://shop.ads.test/deal" target="_blank" rel="sponsored noopener">a deal<span class="seo-visually-hidden"> (opens in a new tab)</span></a>
|
|
200 /blog/hello-acme contains=<a href="/blog/acme-note-04">an older note</a>
|
|
200 /blog/hello-acme contains=<img src="/storage/app/uploads/public/acme/anvil.png" alt="" width="640" height="480" loading="lazy" decoding="async" />
|
|
# heading-off: new tab off keeps the editor rel and the domain-list nofollow,
|
|
# without target, noopener or the hint.
|
|
200 /blog/heading-off contains=<a href="https://partner.test/x" rel="ugc nofollow">the partner</a> contains=<a href="https://other.test/y">another site</a> notcontains=target="_blank" notcontains=noopener notcontains=(opens in a new tab)
|
|
|
|
# Heading convention (UI-SPEC L6) and one link per post card (L5) on every
|
|
# fixture page: exactly one <h1, no skipped level, each card one <a, post
|
|
# cards h2 in listings and h3 under the related posts' h2, no read-more link.
|
|
200 /blog headings=strict cardlinks=1 count=<h1:1 notcontains=journal-post-card__more
|
|
200 /blog?page=2 headings=strict cardlinks=1 count=<h1:1 contains=· Page 2
|
|
200 /pl/blog?page=2 headings=strict cardlinks=1 count=<h1:1 contains=· Strona 2
|
|
200 /blog/category/news headings=strict cardlinks=1 count=<h1:1
|
|
200 /blog/category/quiet headings=strict count=<h1:1
|
|
200 /blog/tag/release headings=strict cardlinks=1 count=<h1:1
|
|
200 /blog/author/jane-doe headings=strict cardlinks=1 count=<h1:1
|
|
200 /search?q=acme headings=strict cardlinks=1 count=<h1:1
|
|
200 /blog/hello-acme headings=strict cardlinks=1 count=<h1:1 contains=<h2 class="journal-related__title" contains=<h3 class="journal-post-card__title" notcontains=<h2 class="journal-post-card__title" notcontains=journal-post-card__more
|
|
200 /blog/heading-on headings=strict cardlinks=1 count=<h1:1
|
|
200 /blog/heading-off headings=strict cardlinks=1 count=<h1:1
|
|
200 /blog/uncategorised-note headings=strict cardlinks=1 count=<h1:1
|