import { describe, expect, it } from 'vitest'; import { readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { runFixtureSuite } from '@rareindex/connectors/testing'; import { localMeta } from '../_lib/local-meta.js'; import { parseUsDate } from '../_g7-auctions-na-lib/index.js'; import createConnector, { doyleCategory, parseAuctionHeaderDate, parsePastAuctions, parseResultsPage } from './index.js'; const dir = path.dirname(fileURLToPath(import.meta.url)); const meta = localMeta(JSON.parse(readFileSync(path.join(dir, 'meta.json'), 'utf8'))); const connector = createConnector(meta); // Real fragments of https://www.doyle.com/past-auctions/ (captured 2026-09-08): a live sale ("Date:") and an online sale ("Ends:"). const CALENDAR = `
Rare Books, Autographs & Maps

Rare Books, Autographs & Maps

Date: Apr 16, 2026 10:00 EST
x

'80s New York Featuring the Keith Haring Pop Shop Collection of Julia Markus

Ends: Aug 20, 2026 10:00 EST

Section without an auction id

`; // Real fragment of https://www.doyle.com/auction/search/?au=9180 (page 1 of 5): a sold lot and an unsold lot, plus the pager. const RESULTS = `

Lot 1
Clutch of five sauropod eggs

Sold for $2,048
Estimated at $1,500 - $3,000

Lot 6
A complete set of the first Aldine edition of Livy

Property sold to benefit Albertus Magnus High School

Estimated at $6,000 - $9,000

`; describe('doyle', () => { runFixtureSuite(connector, it, expect); it('parses the past-auctions calendar (Date: and Ends: labels, skips entries without an auction id)', () => { const list = parsePastAuctions(CALENDAR); expect(list.length).toBe(2); expect(list[0]).toEqual({ auId: '9180', code: '26BP01', name: 'Rare Books, Autographs & Maps', dateText: 'Apr 16, 2026 10:00 EST', url: 'https://www.doyle.com/auction/26bp01-rare-books-autographs--maps?au=9180' }); expect(list[1]).toMatchObject({ auId: '9241', code: '26KH01', dateText: 'Aug 20, 2026 10:00 EST' }); expect(list[1]!.name.startsWith("'80s New York")).toBe(true); // 10:00 US Eastern (EST label) → 15:00 UTC expect(parseUsDate(list[0]!.dateText)?.toISOString()).toBe('2026-04-16T15:00:00.000Z'); expect(parseUsDate('Apr 16, 2026')?.toISOString()).toBe('2026-04-16T00:00:00.000Z'); }); it('parses result cards: lot number/title split, price, estimate, lot id, canonical url, pagination', () => { const auction = parsePastAuctions(CALENDAR)[0]!; const p = parseResultsPage(RESULTS, auction, 1); expect(p.kind).toBe('doyle_results_page'); expect(p.hasNext).toBe(true); expect(p.lots.length).toBe(2); expect(p.lots[0]).toMatchObject({ lotId: '1461680', lotNumber: '1', title: 'Clutch of five sauropod eggs', soldPrice: 2048, estimateLow: 1500, estimateHigh: 3000, categoryId: '146', url: 'https://www.doyle.com/auction/lot/lot-1---clutch-of-five-sauropod-eggs/?lot=1461680&au=9180' }); expect(p.lots[0]!.image).toContain('/stock/2079710-1-small.jpg'); expect(p.lots[1]).toMatchObject({ lotId: '1468947', lotNumber: '6', soldPrice: null, soldText: null, estimateLow: 6000, estimateHigh: 9000, categoryId: '4' }); // last page: no pn=2 link expect(parseResultsPage(RESULTS.replace(/pn=2/g, 'pn=9'), auction, 1).hasNext).toBe(false); expect(parseAuctionHeaderDate('
Request an Auction Estimate Apr 16, 2026 10:00 EST Rare Books
')).toBe('Apr 16, 2026 10:00 EST'); }); it('normalises only sold lots, dated by the calendar, USD with buyer premium included', async () => { const auction = parsePastAuctions(CALENDAR)[0]!; const payload = parseResultsPage(RESULTS, auction, 1); const out = await connector.normalize({ url: 'https://www.doyle.com/auction/search/?au=9180&pp=96&g=1', externalId: 'auction:9180:page:1', kind: 'sale', engine: 'api', fetchedAt: new Date('2026-09-08T00:00:00Z'), payload }); expect(out.length).toBe(1); const s = out[0]!; if (s.kind !== 'sale') throw new Error('expected sale'); expect(s).toMatchObject({ price: 2048, currency: 'USD', buyerPremiumIncluded: true, auctionHouse: 'Doyle', lotNumber: '1', location: 'US', externalId: '1461680' }); expect(s.saleDate.toISOString()).toBe('2026-04-16T15:00:00.000Z'); expect(s.attributes.categorySlug).toBe('fossils'); expect(s.attributes.identifiers).toEqual({ doyle_lot_id: '1461680' }); expect(s.attributes.metadata).toMatchObject({ auction_id: '9180', auction_code: '26BP01', estimate_low: 1500, estimate_high: 3000, doyle_category_id: '146' }); // no calendar date → nothing is emitted (never guess a sale date) expect(await connector.normalize({ url: 'x', externalId: 'y', kind: 'sale', engine: 'api', fetchedAt: new Date(), payload: { ...payload, auction: { ...auction, dateText: null } } })).toEqual([]); }); it('maps sale departments and titles to taxonomy slugs', () => { expect(doyleCategory('Rare Books, Autographs & Maps', 'A complete set of the first Aldine edition of Livy')).toBe('books'); expect(doyleCategory('Rare Books, Autographs & Maps', 'Map of the Americas, engraved, 1650')).toBe('maps'); expect(doyleCategory('Rare Books, Autographs & Maps', 'Thomas Jefferson autograph letter signed to Charles Willson Peale')).toBe('historical_documents'); expect(doyleCategory('Fine Jewelry', 'Platinum, diamond and sapphire ring')).toBe('jewelry'); expect(doyleCategory('Important Watches', 'Rolex stainless steel Submariner wristwatch Ref. 5513')).toBe('rolex'); expect(doyleCategory('Coins, Bank Notes, Stamps & Collectibles', '1921 Morgan silver dollar MS65')).toBe('coins'); expect(doyleCategory('Coins, Bank Notes, Stamps & Collectibles', 'United States $20 legal tender banknote 1880')).toBe('banknotes'); expect(doyleCategory('English & Continental Furniture, Old Master Paintings, Silver', 'George III mahogany chest of drawers')).toBe('antiques'); expect(doyleCategory('English & Continental Furniture, Old Master Paintings, Silver', 'Sterling silver tea service, Tiffany & Co.')).toBe('silver'); expect(doyleCategory('Post-War & Contemporary Art', 'Keith Haring, Untitled, screenprint, edition of 100')).toBe('contemporary_art'); expect(doyleCategory('Doyle at Home', 'Meissen porcelain figure group')).toBe('porcelain'); expect(doyleCategory('The Collection of a Florida Bibliophile', 'Hemingway, first edition, dust jacket')).toBe('books'); expect(doyleCategory('Entertaining with Style', 'Untitled mixed lot')).toBeNull(); }); });