Files
vue-journal-layer/testing/expect-acme.txt
Jakub Zych 51e5d67576 feat!: one link per post card and the heading convention
- 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.
2026-11-04 13:49:36 +01:00

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: &lt;script&gt;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