Implemented phase 1 for the POC prototype in the Website service.

This commit is contained in:
javier committed 2026-09-21 11:16:52 +02:00
1 parent 6d7ab4bcba
commit ea89ea892a
37 files changed
+4173 -43

No files matched your search

@@ -0,0 +1,167 @@
import Elementary
import Persistence
import Testing
@testable import WebsiteLibrary
@Suite(
"Catalogue pages",
.tags(.page)
)
struct CataloguePagesTests {
// MARK: IndexPage tests
@Test
func `landing page fills its rows and fans its covers from the listings`() {
let singles = (1...6).map { Listing.stub(slug: "single-\($0)", title: "Single \($0)") }
let manga = Listing.stub(slug: "manga-1", title: "Manga 1", format: "manga")
let html = IndexPage(
locale: .init(identifier: "en"),
listings: .init(from: singles + [manga])
).render()
#expect(html.contains("New in single issues"))
#expect(html.contains("Single 5"))
#expect(!html.contains("Single 6"))
#expect(html.contains("Manga 1"))
#expect(html.contains("hero__covers"))
#expect(html.contains(#"href="/search?format=manga&format=trade-paperbacks&format=hardcovers""#))
}
@Test
func `landing page drops its rows without listings`() {
let html = IndexPage(locale: .init(identifier: "en")).render()
#expect(!html.contains("New in single issues"))
#expect(!html.contains("hero__covers"))
#expect(html.contains("Passkeys, not passwords"))
}
// MARK: SearchPage tests
@Test
func `search page heads the results with the query and ticks the applied filters`() {
let html = SearchPage(
parameters: .init(query: "q=nightward+1&format=single-issues&publisher=Meridian+Comics"),
matches: [.stub(), .stub(slug: "b", shop: .stub(slug: "other", name: "Other"))],
publishers: ["Blackline", "Meridian Comics"]
).render()
#expect(html.contains("<title>“nightward 1” — CCN</title>"))
#expect(html.contains("2 listings"))
#expect(html.contains("from 2 shops"))
#expect(html.contains(#"name="q" value="nightward 1""#))
#expect(html.contains(#"value="Meridian Comics" checked"#))
#expect(!html.contains(#"value="Blackline" checked"#))
#expect(html.contains(#"aria-label="Remove filter: Meridian Comics""#))
#expect(html.contains("/css/search.css"))
}
@Test
func `search page pages through more matches than fit`() {
let matches = (1...13).map { Listing.stub(slug: "l-\($0)", title: "Listing \($0).") }
let html = SearchPage(
parameters: .init(query: "page=2"),
matches: matches,
publishers: []
).render()
#expect(html.contains("13 listings"))
#expect(html.contains("Listing 13."))
#expect(!html.contains("Listing 12."))
#expect(html.contains(#"href="/search?page=2" aria-current="page""#))
}
@Test
func `search page offers a way out of an empty result`() {
let html = SearchPage(
parameters: .init(query: "q=nothing"),
matches: [],
publishers: []
).render()
#expect(html.contains("No listings match"))
#expect(html.contains(#"href="/search?ships-from=anywhere""#))
}
// MARK: ListingPage tests
@Test
func `listing page states the copy and gates every action behind the sign-in`() {
let html = ListingPage(
listing: .stub(keyNotes: "First appearance"),
related: [.stub(slug: "nightward-2", title: "Nightward #2")]
).render()
#expect(html.contains("<title>Nightward #1 · CGC 9.8 — CCN</title>"))
#expect(html.contains("+ €6 tracked shipping to NL · accepts offers"))
#expect(html.contains("CGC 9.8 · cert 4312087-002"))
#expect(html.contains("First appearance"))
#expect(html.contains("More from Kestrel Row Comics"))
#expect(html.contains("Nightward #2"))
for action in ["Buy now", "Make an offer", "Message seller", "Save"] {
#expect(html.contains(action))
}
#expect(html.components(separatedBy: #"href="/sign-in?return=/listing/nightward-1""#).count - 1 >= 5)
}
@Test
func `listing page leaves out what the listing does not offer or state`() {
let html = ListingPage(
listing: .stub(grader: nil, grade: nil, condition: "Sealed", acceptsOffers: false),
related: []
).render()
#expect(!html.contains("Make an offer"))
#expect(!html.contains("Key notes"))
#expect(!html.contains("More from"))
#expect(html.contains("<dt>Condition</dt>"))
}
// MARK: ShopPage tests
@Test
func `shop page counts its tabs from the whole inventory and lists the matches`() {
let inventory = [
Listing.stub(),
.stub(slug: "b", title: "Second"),
.stub(slug: "c", title: "A manga", format: "manga"),
]
let html = ShopPage(
shop: .stub(),
inventory: inventory,
matches: [inventory[2]],
parameters: .init(query: "format=manga&keys=1")
).render()
#expect(html.contains("<title>Kestrel Row Comics — CCN</title>"))
#expect(html.contains("All · 3"))
#expect(html.contains("Single issues · 2"))
#expect(html.contains(#"href="/shop/kestrel-row-comics?keys=1&amp;format=manga" aria-current="page""#))
#expect(!html.contains("Hardcovers ·"))
#expect(html.contains("A manga"))
#expect(!html.contains("Second"))
#expect(html.contains(#"href="/shop/kestrel-row-comics?format=manga" aria-pressed="true""#))
#expect(html.contains(#"type="hidden" name="format" value="manga""#))
}
// MARK: Shared tests
@Test
func `no catalogue page renders an inline style`() {
let pages = [
SearchPage(parameters: .init(), matches: [.stub()], publishers: ["Blackline"]).render(),
ListingPage(listing: .stub(), related: [.stub()]).render(),
ShopPage(shop: .stub(), inventory: [.stub()], matches: [.stub()], parameters: .init()).render(),
]
for html in pages {
#expect(!html.contains("style="))
#expect(!html.contains("<style"))
}
}
}