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 { decodeEntities } from '../_g7-auctions-na-lib/index.js'; import createConnector, { parseInfoPage, parseSessionPage, ragoCategory, sessionUrlFromSitemap, WRIGHT } from './index.js'; import { asInertiaHtml, INFO_JSON, SESSION_JSON } from './test-data.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); describe('wright', () => { runFixtureSuite(connector, it, expect); it('derives session URLs from the sitemap index names', () => { expect(sessionUrlFromSitemap(WRIGHT.site, 'https://www.wright20.com/sitemaps/sessions/sitemap-auctions_2000_12_the-american-scene-art-of-the-1930s-and-1940s.xml')).toBe('https://www.wright20.com/auctions/2000/12/the-american-scene-art-of-the-1930s-and-1940s'); expect(sessionUrlFromSitemap(WRIGHT.site, 'https://www.wright20.com/sitemaps/sitemap-artists.xml')).toBeNull(); }); it('parses the Inertia data-page (entities decoded) into session + items', () => { const html = asInertiaHtml(SESSION_JSON); expect(decodeEntities('"a&b"')).toBe('"a&b"'); const p = parseSessionPage(html, WRIGHT, 'https://www.wright20.com/auctions/2026/08/design')!; expect(p.session).toMatchObject({ id: '2884', title: 'Design', date: '2026-08-13T16:00:00.000000Z', startDate: 1786636800, showResults: true, archive: true, preliminary: false, resultsIncludePremium: null }); expect(p.items.length).toBe(3); const calder = p.items.find((i) => i.id === '413032')!; expect(calder).toMatchObject({ id: '413032', lotNumber: '100', artist: 'After Alexander Calder', name: 'Turquoise tapestry', estimateLow: 15000, estimateHigh: 20000, result: 57600, bidCount: 14, location: 'Los Angeles' }); const miro = p.items.find((i) => i.id === '412274')!; expect(miro.result).toBeNull(); // `result: false` = unsold / not shown expect(miro.artist).toBe('After Joan MirĂ³'); expect(parseInfoPage(asInertiaHtml(INFO_JSON))).toEqual({ endedAt: '2026-08-13T18:58:44.000000Z', resultsIncludePremium: true }); expect(parseSessionPage('
no app', WRIGHT, 'https://www.wright20.com/x')).toBeNull(); }); it('normalises sold lots only, dated by the close time, premium flag from the info page', async () => { const p = parseSessionPage(asInertiaHtml(SESSION_JSON), WRIGHT, 'https://www.wright20.com/auctions/2026/08/design')!; const info = parseInfoPage(asInertiaHtml(INFO_JSON)); p.session.endedAt = info.endedAt; p.session.resultsIncludePremium = info.resultsIncludePremium; const out = await connector.normalize({ url: 'https://www.wright20.com/auctions/2026/08/design', externalId: 'session:2884', kind: 'sale', engine: 'api', fetchedAt: new Date('2026-09-08T00:00:00Z'), payload: p }); expect(out.length).toBe(2); const s = out.find((r) => 'externalId' in r && r.externalId === '413032')!; if (s.kind !== 'sale') throw new Error('expected sale'); expect(s).toMatchObject({ price: 57600, currency: 'USD', buyerPremiumIncluded: true, auctionHouse: 'Wright', lotNumber: '100', rawTitle: 'After Alexander Calder, Turquoise tapestry', location: 'Los Angeles' }); expect(s.saleDate.toISOString()).toBe('2026-08-13T18:58:44.000Z'); expect(s.sourceUrl).toBe('https://www.wright20.com/auctions/2026/08/design/100'); expect(s.attributes).toMatchObject({ categorySlug: 'design_furniture', name: 'Turquoise tapestry', brand: null }); expect(s.attributes.identifiers).toEqual({ wright_item_id: '413032' }); expect(s.attributes.metadata).toMatchObject({ artist: 'After Alexander Calder', estimate_low: 15000, estimate_high: 20000, bid_count: 14 }); // without the info page the premium basis is unknown and the auction date is used p.session.endedAt = null; p.session.resultsIncludePremium = null; const out2 = await connector.normalize({ url: 'x', externalId: 'e', kind: 'sale', engine: 'api', fetchedAt: new Date('2026-09-08T00:00:00Z'), payload: p }); const s2 = out2.find((r) => 'externalId' in r && r.externalId === '413032')!; if (s2.kind !== 'sale') throw new Error('expected sale'); expect(s2.buyerPremiumIncluded).toBeNull(); expect(s2.saleDate.toISOString()).toBe('2026-08-13T16:00:00.000Z'); }); it('emits auction lots for future sessions and lowers confidence on preliminary results', async () => { const future = JSON.parse(JSON.stringify(SESSION_JSON)) as typeof SESSION_JSON; const nextYear = Math.floor(Date.now() / 1000) + 30 * 86_400; future.props.sessions['2884']!.start_date = nextYear; future.props.sessions['2884']!.archive_auction = 0; future.props.sessions['2884']!.sale_status = 'PRESALE'; for (const it of Object.values(future.props.items)) (it as { result: number | boolean }).result = false; const p = parseSessionPage(asInertiaHtml(future), WRIGHT, 'https://www.wright20.com/auctions/2026/08/design')!; const out = await connector.normalize({ url: 'x', externalId: 'e', kind: 'auction_lot', engine: 'api', fetchedAt: new Date(), payload: p }); expect(out.length).toBe(3); expect(out.every((r) => r.kind === 'auction_lot')).toBe(true); const lot = out.find((r) => 'externalId' in r && r.externalId === '413032')!; if (lot.kind !== 'auction_lot') throw new Error('expected lot'); expect(lot).toMatchObject({ auctionHouse: 'Wright', auctionName: 'Design', estimateLow: 15000, estimateHigh: 20000, status: 'upcoming', currency: 'USD' }); const prelim = parseSessionPage(asInertiaHtml(SESSION_JSON), WRIGHT, 'https://www.wright20.com/auctions/2026/08/design')!; prelim.session.preliminary = true; const out2 = await connector.normalize({ url: 'x', externalId: 'e', kind: 'sale', engine: 'api', fetchedAt: new Date(), payload: prelim }); expect((out2[0] as { confidence: number }).confidence).toBe(0.75); expect((out2[0] as { attributes: { metadata: Record