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 €192")) #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?")) #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">
Is the case clean?"#)) #expect(html.contains(#"entry--text entry--theirs">
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("