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

201 lines
8.6 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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("Nothing on your list 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"))
}
}
}