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

257 lines
11 KiB
Swift
Raw Normal View History

import Elementary
import Foundation
import Persistence
import Testing
@testable import WebsiteLibrary
@Suite(
"Trade pages",
.tags(.page)
)
struct TradePagesTests {
// MARK: OfferPage tests
@Test
func `offer sheet fills in a tenth under asking and offers the picks the seller would hear`() {
let html = OfferPage(listing: .stub(), mode: .offer).render()
#expect(html.contains(#"action="/listing/nightward-1/offer" method="post""#))
#expect(html.contains(#"name="amount" value="216""#))
#expect(html.contains(#"href="/listing/nightward-1/offer?amount=216" aria-current="true">€216 · −10%"#))
#expect(html.contains("€192 · −20%"))
#expect(html.contains("auto-declines under <strong>€192</strong>"))
#expect(html.contains("+ €6 shipping to NL"))
#expect(html.contains(#"content="noindex""#))
}
@Test
func `offer sheet leaves out a pick under the seller's floor`() {
// €88 asking: the floor rounds up to €71, over the −20% pick of €70.
let html = OfferPage(listing: .stub(priceCents: 8_800), mode: .offer).render()
#expect(html.contains("€75 · −15%"))
#expect(!html.contains("−20%"))
}
@Test
func `offer sheet gives back what was typed, saying what went wrong`() {
let html = OfferPage(
listing: .stub(),
mode: .offer,
amountCents: 15_000,
note: "Bundle?",
error: TradeError.offerTooLow(floorCents: 19_200).explanation
).render()
#expect(html.contains(#"value="150""#))
#expect(html.contains(">Bundle?</textarea>"))
#expect(html.contains(#"role="alert">The seller auto-declines offers under €192."#))
}
@Test
func `buy sheet sums the asking price and the shipping`() {
let html = OfferPage(listing: .stub(), mode: .buy).render()
#expect(html.contains(#"action="/listing/nightward-1/buy" method="post""#))
#expect(html.contains("Agree to buy for €246"))
#expect(html.contains("CCN handles no money"))
#expect(!html.contains(#"name="amount""#))
}
// MARK: SalePage tests
@Test
func `sale record hands each party the step that is theirs`() {
func page(_ sale: Sale, _ role: Sale.Role) -> String {
SalePage(sale: sale, role: role, now: .stub).render()
}
#expect(page(.stub(), .buyer).contains(#"name="step" value="paid""#))
#expect(page(.stub(), .seller).contains("Waiting for the buyer to pay"))
#expect(page(.stub(paid: true), .seller).contains(#"name="step" value="sent""#))
#expect(page(.stub(paid: true), .buyer).contains("Waiting for the seller to dispatch"))
#expect(page(.stub(paid: true, sent: true), .buyer).contains(#"name="step" value="received""#))
#expect(page(.stub(paid: true, sent: true, received: true), .seller).contains(#"name="stars" value="5""#))
#expect(page(.stub(paid: true, sent: true, received: true, buyerRating: 5), .buyer).contains("Waiting for the other side"))
#expect(!page(.stub(paid: true, sent: true, received: true, buyerRating: 5, sellerRating: 4), .buyer).contains(#"name="step""#))
}
@Test
func `sale record states what was agreed, frozen, and where the handshake stands`() {
let html = SalePage(sale: .stub(paid: true), role: .buyer, now: .stub).render()
#expect(html.contains("Sale record · CCN-48213 · you are the buyer"))
#expect(html.contains("Agreed today, 09:14 with Kestrel Row Comics · €215 + €6 tracked shipping"))
#expect(html.contains("Awaiting dispatch"))
#expect(html.contains("CGC 9.8 · cert 4312087-002"))
#expect(html.contains("€215 · offer accepted (asked €240)"))
#expect(html.contains("marked this as paid by iDEAL today, 09:20"))
#expect(html.contains("stepper__step--done"))
#expect(html.components(separatedBy: "stepper__step--current").count - 1 == 1)
#expect(html.contains(#"href="/messages/00000000-0000-0000-0000-0000000000cc""#))
}
@Test
func `sale record shows the seller their buyer, and both the ratings once given`() {
let html = SalePage(
sale: .stub(priceCents: 24_000, paid: true, sent: true, received: true, buyerRating: 5, sellerRating: 4),
role: .seller,
now: .stub
).render()
#expect(html.contains("you are the seller"))
#expect(html.contains("Mara Visser"))
#expect(html.contains("bought at the asking price"))
#expect(html.contains("Complete"))
#expect(html.contains("★★★★★"))
#expect(html.contains("★★★★☆"))
#expect(!html.contains("Visit shop"))
}
// MARK: MessagesPage tests
@Test
func `messages tell the viewer's words from the other side's`() {
let html = MessagesPage(
conversations: [],
open: .stub(entries: [
.text("Is the case clean?", from: .mara),
.text("Clean all round.", from: .joost),
]),
now: .stub
).render()
#expect(html.contains(#"entry--text entry--own"><p class="entry__bubble">Is the case clean?"#))
#expect(html.contains(#"entry--text entry--theirs"><p class="entry__bubble">Clean all round."#))
#expect(html.contains("You · today, 09:14"))
#expect(html.contains("Kestrel Row Comics · today, 09:14"))
#expect(html.contains(#"action="/messages/00000000-0000-0000-0000-0000000000cc" method="post""#))
}
@Test
func `messages let the seller alone answer an open offer`() {
let entries = [Conversation.Entry(kind: .offer, sender: .mara, body: "Would you take €215?", offer: .stub(), createdAt: .stub)]
let selling = MessagesPage(conversations: [], open: .stub(side: .selling, entries: entries), now: .stub).render()
let buying = MessagesPage(conversations: [], open: .stub(side: .buying, entries: entries), now: .stub).render()
#expect(selling.contains("Their offer"))
#expect(selling.contains(#"action="/sell/offers/00000000-0000-0000-0000-0000000000aa""#))
#expect(selling.contains("47 h left"))
#expect(selling.contains("“Would you take €215?”"))
#expect(buying.contains("Your offer"))
#expect(buying.contains("Offer open · €215"))
#expect(!buying.contains("/sell/offers/"))
// One open offer per listing: no way to a second while it waits.
#expect(!buying.contains(#"href="/listing/nightward-1/offer""#))
}
@Test
func `messages lead to the sale record from the event that opened it, once`() {
let html = MessagesPage(
conversations: [],
open: .stub(
entries: [
.init(kind: .offer, sender: .mara, body: "", offer: .stub(status: .accepted), createdAt: .stub),
.init(kind: .event, sender: .joost, body: "accepted the offer", saleNumber: 48_213, createdAt: .stub),
.init(kind: .event, sender: .mara, body: "marked the sale paid", saleNumber: 48_213, createdAt: .stub),
],
saleNumber: 48_213
),
now: .stub
).render()
#expect(html.contains("Kestrel Row Comics accepted the offer · today, 09:14"))
#expect(html.contains("You marked the sale paid"))
#expect(html.components(separatedBy: "Sale record CCN-48213 created").count - 1 == 1)
#expect(html.contains("Agreed · CCN-48213"))
#expect(html.contains(">Accepted<"))
}
@Test
func `messages list the side asked for, counting what is unread on each`() {
let conversations: [Conversation] = [
.stub(side: .buying, entries: [.text("Shipping Thursday.", from: .joost)], unreadCount: 2),
.stub(side: .selling, entries: [.text("Still available?", from: .mara)], unreadCount: 1),
]
let buying = MessagesPage(conversations: conversations, now: .stub).render()
let selling = MessagesPage(conversations: conversations, box: .selling, now: .stub).render()
#expect(buying.contains("2 unread"))
#expect(buying.contains(#"href="/messages?box=buying" aria-current="true">Buying · 1"#))
#expect(buying.contains("Shipping Thursday."))
#expect(!buying.contains("Still available?"))
#expect(selling.contains("Still available?"))
#expect(selling.contains(">MV<"))
#expect(buying.contains("Pick a conversation."))
}
// MARK: SellerDashboardPage tests
@Test
func `seller dashboard lists what waits for the seller`() {
let html = SellerDashboardPage(
shop: .stub(),
offers: [.stub()],
sales: [.stub(paid: true), .stub(paid: true, sent: true)],
listings: [.stub()],
viewer: .init(displayName: "Joost de Wit", shopSlug: "kestrel-row-comics", unreadMessages: 2),
now: .stub
).render()
#expect(html.contains("Hello, Joost"))
#expect(html.contains("1 offer waiting · 1 sale to dispatch · 2 unread"))
#expect(html.contains("from Mara Visser"))
#expect(html.contains(#"name="return" value="/sell""#))
#expect(html.contains(#"href="/sales/48213""#))
#expect(html.contains("1 awaiting your dispatch"))
#expect(html.contains(#"handshake__step handshake__step--done">Paid"#))
}
@Test
func `seller dashboard says so when nothing waits`() {
let html = SellerDashboardPage(shop: .stub(), offers: [], sales: [], listings: [], now: .stub).render()
#expect(html.contains("No offers waiting."))
#expect(html.contains("No sales yet."))
}
// MARK: ListingPage tests
@Test
func `listing page closes its actions once sold, and to its own seller`() {
let sold = ListingPage(listing: .stub(soldAt: .stub), related: []).render()
let own = ListingPage(
listing: .stub(),
related: [],
viewer: .init(displayName: "Joost", shopSlug: "kestrel-row-comics")
).render()
#expect(sold.contains("This copy sold on 23 Sep."))
#expect(sold.contains("Kestrel Row Comics has 0 more copies of Nightward #1, and 0 are listed across CCN."))
#expect(sold.contains(#"href="/wants/new?title=Nightward%20%231""#))
#expect(!sold.contains("Buy now"))
#expect(own.contains("This is your listing."))
#expect(!own.contains("Make an offer"))
}
// MARK: Shared tests
@Test
func `no trade page renders an inline style`() {
let pages = [
OfferPage(listing: .stub(), mode: .offer).render(),
SalePage(sale: .stub(), role: .buyer, now: .stub).render(),
MessagesPage(conversations: [.stub()], open: .stub(), now: .stub).render(),
SellerDashboardPage(shop: .stub(), offers: [.stub()], sales: [.stub()], listings: [.stub()], now: .stub).render(),
]
for html in pages {
#expect(!html.contains("style="))
#expect(!html.contains("<style"))
#expect(html.contains("/css/trade.css"))
}
}
}