import { describe, expect, it } from 'vitest'; import metaJson from './meta.json' with { type: 'json' }; import { loadFixture, runFixtureSuite } from '@rareindex/connectors/testing'; import { localMeta } from '../_lib/local-meta.js'; import createConnector, { isSold, pageUrl, parseAuctionList, parseEndDate, parseLotPage } from './index.js'; const connector = createConnector(localMeta(metaJson)); const snapshot = (name: string): string => String((loadFixture('whisky-auction', name).raw.payload as { snapshot?: string }).snapshot ?? ''); describe('whisky-auction', () => { runFixtureSuite(connector, it, expect); it('parses the lot grid HTML (winning bid, state, end date, product id)', () => { const html = snapshot('auction-201-whisky-p0'); const p = parseLotPage(html); expect(p.total).toBeGreaterThan(100); expect(p.lots.length).toBeGreaterThanOrEqual(2); const first = p.lots[0]!; expect(first.lotId).toMatch(/^\d+$/); expect(first.url).toMatch(/^https:\/\/whisky\.auction\/auctions\/lot\/\d+\//); expect(first.winningBid).toBeGreaterThan(0); expect(['met', 'na', 'aftersale']).toContain(first.status); expect(first.endDate).toMatch(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}/); expect(first.productId).toMatch(/^\d+$/); expect(first.image).toMatch(/media\.whisky\.auction/); expect(p.auctionTitle).toMatch(/2026/); }); it('parses the past-auction list', () => { const entries = parseAuctionList(snapshot('auctions-list')); expect(entries.length).toBeGreaterThan(1); expect(Number(entries[0]!.id)).toBeGreaterThan(Number(entries[1]!.id)); expect(entries[0]!.endedOn).toMatch(/\d{1,2} [A-Z][a-z]{2} \d{4}/); }); it('treats NotMet / negative bids as unsold and builds page URLs', () => { const base = { lotId: '1', productId: null, url: 'https://whisky.auction/auctions/lot/1/x', title: 'x', sub1: null, sub2: null, image: null, endDate: null, endedOn: null, vatScheme: null } as const; expect(isSold({ ...base, winningBid: -1, status: 'notmet' })).toBe(false); expect(isSold({ ...base, winningBid: 0, status: 'na' })).toBe(false); expect(isSold({ ...base, winningBid: 120, status: 'na' })).toBe(true); expect(isSold({ ...base, winningBid: 120, status: 'aftersale' })).toBe(true); const url = pageUrl('201', { category: 'Rum', slug: 'rum' }, 2, 90); expect(url).toBe('https://whisky.auction/auctions/auction/201?category=Rum&pageIndex=2&pageSize=90&sort=Price&dir=desc'); expect(parseEndDate('2026-05-19 21:20:00')?.toISOString()).toBe('2026-05-19T21:20:00.000Z'); expect(parseEndDate('garbage')).toBeNull(); }); it('normalises sold lots into GBP hammer sales dated by the lot end', async () => { const fx = loadFixture('whisky-auction', 'auction-201-whisky-p0'); const out = await connector.normalize(fx.raw); expect(out.length).toBeGreaterThan(0); for (const r of out) { if (r.kind !== 'sale') throw new Error('expected sale'); expect(r.currency).toBe('GBP'); expect(r.buyerPremiumIncluded).toBe(false); expect(r.attributes.categorySlug).toBe('whisky'); expect(r.attributes.identifiers.whisky_auction_lot).toMatch(/^\d+$/); expect(r.saleDate.toISOString().slice(0, 7)).toBe('2026-05'); expect(r.auctionHouse).toBe('Whisky.Auction'); } const rum = loadFixture('whisky-auction', 'auction-201-rum-p0'); const rumOut = await connector.normalize(rum.raw); const payload = rum.raw.payload as { lots: Array<{ status: string; winningBid: number | null }> }; const unsold = payload.lots.filter((l) => l.status === 'notmet').length; expect(rumOut.length).toBe(payload.lots.length - unsold); for (const r of rumOut) expect((r as { attributes: { categorySlug: string } }).attributes.categorySlug).toBe('rum'); }); });