Files
ccn/Services/Website/Tests/Library/Cases/Internal/Pages/CollectorPagesTests.swift
T

200 lines
8.6 KiB
Swift
Raw Normal View History

import Elementary
import Foundation
import Persistence
import Testing
@testable import WebsiteLibrary
@Suite(
"Account and collector pages",
.tags(.page)
)
struct CollectorPagesTests {
// MARK: Constants
private static let nightward = Series(slug: "nightward", name: "Nightward", publisher: "Meridian Comics", years: "2019–2021", issueCount: 4, blurb: "A short run.")
// MARK: OpenShopPage tests
@Test
func `open-a-shop form starts as a private seller, and gives back what was typed with its problems`() {
let empty = OpenShopPage().render()
let sent = OpenShopPage(
form: ShopFormTests.form(["kind": "business", "about": "Spare <copies>"]),
problems: [.kvk: "Enter your 8-digit KvK number."]
).render()
#expect(empty.contains(#"action="/sell/open-a-shop" method="post""#))
#expect(empty.contains(#"name="kind" value="private" checked"#))
#expect(!empty.contains(#"role="alert""#))
#expect(sent.contains("One thing to fix before opening."))
#expect(sent.contains(#"name="kind" value="business" checked"#))
#expect(sent.contains(#"name="name" value="Ana's Longbox""#) || sent.contains(#"name="name" value="Ana&#39;s Longbox""#))
#expect(sent.contains(#"id="kvk-error">Enter your 8-digit KvK number."#))
#expect(sent.contains("Spare &lt;copies&gt;"))
#expect(!sent.contains("style="))
}
// MARK: WelcomePage and AccountPage tests
@Test
func `welcome screen is bare, asks for a name, and carries the intent on`() {
let html = WelcomePage(displayName: "ana.devries", returnPath: "/listing/nightward-1").render()
#expect(html.contains("You're in.") || html.contains("You&#39;re in."))
#expect(html.contains(#"action="/welcome" method="post""#))
#expect(html.contains(#"name="name" value="ana.devries""#))
#expect(html.contains(#"name="return" value="/listing/nightward-1""#))
#expect(html.contains(#"content="noindex""#))
#expect(!html.contains("site-header"))
}
@Test
func `account screen lists the passkeys with a way to revoke each, and to add one`() {
let passkey = Passkey(credentialID: "cred-1", publicKey: [], signCount: 0, user: .mara)
let html = AccountPage(user: .mara, passkeys: [(passkey, .stub)], viewer: .init(.mara), now: .stub).render()
let bare = AccountPage(user: .mara, passkeys: [], now: .stub).render()
#expect(html.contains(#"action="/account/name" method="post""#))
#expect(html.contains(#"name="name" value="Mara Visser""#))
#expect(html.contains("Passkey 1"))
#expect(html.contains("created today, 09:14"))
#expect(html.contains(#"name="credential" value="cred-1""#))
#expect(html.contains(#"hidden data-passkey="add""#))
#expect(html.contains("/js/passkey.js"))
#expect(html.contains(#"href="/sell/open-a-shop">Open a shop"#))
#expect(bare.contains("No passkey yet"))
}
// MARK: WantsPage tests
@Test
func `want list shows each want's limits, its cheapest match, and what is new`() {
let want = Want(
id: .init(uuidString: "00000000-0000-0000-0000-0000000000EE") ?? .init(),
title: "Nightward #1", minGrade: 8, maxPriceCents: 30_000, slabbedOnly: true, alerts: true,
matches: [.stub(slug: "a", priceCents: 24_000), .stub(slug: "b", priceCents: 21_000)],
newMatchCount: 1
)
let html = WantsPage(wants: [want]).render()
#expect(html.contains("1 want · 1 new matches"))
#expect(html.contains("slabbed only · grade ≥ 8.0 · max €300 · ships from EU only"))
#expect(html.contains(#"href="/listing/b">From €210"#))
#expect(html.contains(#"class="wants__new">1 new"#))
#expect(html.contains(#"role="switch" aria-checked="true""#))
#expect(html.contains(#"action="/wants/00000000-0000-0000-0000-0000000000ee/remove""#))
#expect(html.contains(#"href="/search?q=Nightward%20%231&amp;grade-min=8.0&amp;price-max=300&amp;grading=slabbed""#))
#expect(html.contains(#"href="/wants/new""#))
}
@Test
func `want list fills its form from what is being saved`() {
let html = WantsPage(wants: [], draft: .init(title: "Nightward #2", minGrade: 9.6, maxPriceCents: 8_500)).render()
#expect(html.contains(#"name="title" value="Nightward #2""#))
#expect(html.contains(#"<option value="9.6" selected>"#))
#expect(html.contains(#"name="max-price" value="85""#))
#expect(!html.contains("No wants yet"))
}
// MARK: SeriesPage tests
@Test
func `series shows every issue of the run, the gaps one tap from an alert`() {
let html = SeriesPage(
series: Self.nightward,
listings: [.stub(keyNotes: "First appearance"), .stub(slug: "cheap", priceCents: 9_000)]
).render()
#expect(html.contains("All 4 issues"))
#expect(html.contains("2 copies listed across CCN right now."))
#expect(html.contains(#"href="/series/nightward?issue=1#copies">2 listed · from €90"#))
#expect(html.components(separatedBy: "series__issue--gap").count - 1 == 3)
// Signed out, an alert leads through the sign-in and back.
#expect(html.contains(#"href="/sign-in?return=/series/nightward">None listed · alert me"#))
#expect(html.contains("#1 · First appearance"))
#expect(!html.contains(#"id="copies""#))
}
@Test
func `series lists an issue's copies cheapest delivered first, ready to compare`() {
let html = SeriesPage(
series: Self.nightward,
listings: [.stub(), .stub(slug: "cheap", priceCents: 9_000, defects: ["Foxing / staining"])],
issue: 1,
viewer: .init(displayName: "Mara")
).render()
let cheap = try? #require(html.range(of: #"value="cheap""#))
let dear = try? #require(html.range(of: #"value="nightward-1""#))
#expect(html.contains("Copies of #1 — 2 listed"))
#expect(html.contains(#"action="/compare" method="get""#))
#expect(html.contains("Foxing / staining"))
#expect(html.contains(#"href="/wants/new?title=Nightward%20%232">None listed · alert me"#))
if let cheap, let dear {
#expect(cheap.lowerBound < dear.lowerBound)
}
}
@Test
func `series known only from its listings shows the issues listed, without gaps`() {
let html = SeriesPage(
series: .init(slug: "nightward", name: "Nightward", publisher: "Meridian Comics", years: "2019", issueCount: 0, blurb: ""),
listings: [.stub()]
).render()
#expect(html.contains("Issues listed"))
#expect(!html.contains("series__issue--gap"))
}
// MARK: ComparePage tests
@Test
func `comparison orders copies by what they cost delivered, and flags a customs border`() {
let abroad = Shop(
slug: "longbox", name: "Longbox Atlantic", city: "Bristol", country: "GB", sinceYear: 2024, isVerified: false,
rating: 4.6, ratedSales: 58, soldCount: 71, handlingDays: 4, shipsNote: "", returnsNote: "returns by agreement", about: ""
)
let html = ComparePage(copies: [.stub(), .stub(slug: "abroad", shop: abroad, priceCents: 15_000)]).render()
let first = try? #require(html.range(of: "€156"))
let second = try? #require(html.range(of: "€246"))
#expect(html.contains("customs may apply"))
#expect(html.components(separatedBy: "customs may apply").count - 1 == 1)
#expect(html.contains(#"href="/sign-in?return=/listing/abroad">Buy now"#))
#expect(html.contains("58 · 4.6 · returns by agreement"))
if let first, let second {
#expect(first.lowerBound < second.lowerBound)
}
}
@Test
func `comparison of nothing says where to pick copies`() {
#expect(ComparePage(copies: []).render().contains("Nothing to compare"))
}
// MARK: Shared tests
@Test
func `no account or collector page renders an inline style`() {
let pages = [
OpenShopPage().render(),
WelcomePage(displayName: "ana", returnPath: "/").render(),
AccountPage(user: .mara, passkeys: [], now: .stub).render(),
WantsPage(wants: [], draft: .init()).render(),
SeriesPage(series: Self.nightward, listings: [.stub()], issue: 1).render(),
SeriesIndexPage(entries: [(Self.nightward, [.stub()])]).render(),
ComparePage(copies: [.stub()]).render(),
]
for html in pages {
#expect(!html.contains("style="))
#expect(!html.contains("<style"))
}
}
}