TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { describe, expect, it } from 'vitest';2import { readFileSync } from 'node:fs';3import path from 'node:path';4import { fileURLToPath } from 'node:url';5import { runFixtureSuite } from '@rareindex/connectors/testing';6import { localMeta } from '../_lib/local-meta.js';7import { decodeEntities } from '../_g7-auctions-na-lib/index.js';8import createConnector, { parseInfoPage, parseSessionPage, ragoCategory, sessionUrlFromSitemap, WRIGHT } from './index.js';9import { asInertiaHtml, INFO_JSON, SESSION_JSON } from './test-data.js';1011const dir = path.dirname(fileURLToPath(import.meta.url));12const meta = localMeta(JSON.parse(readFileSync(path.join(dir, 'meta.json'), 'utf8')));13const connector = createConnector(meta);1415describe('wright', () => {16 runFixtureSuite(connector, it, expect);1718 it('derives session URLs from the sitemap index names', () => {19 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');20 expect(sessionUrlFromSitemap(WRIGHT.site, 'https://www.wright20.com/sitemaps/sitemap-artists.xml')).toBeNull();21 });2223 it('parses the Inertia data-page (entities decoded) into session + items', () => {24 const html = asInertiaHtml(SESSION_JSON);25 expect(decodeEntities('"a&b"')).toBe('"a&b"');26 const p = parseSessionPage(html, WRIGHT, 'https://www.wright20.com/auctions/2026/08/design')!;27 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 });28 expect(p.items.length).toBe(3);29 const calder = p.items.find((i) => i.id === '413032')!;30 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' });31 const miro = p.items.find((i) => i.id === '412274')!;32 expect(miro.result).toBeNull(); // `result: false` = unsold / not shown33 expect(miro.artist).toBe('After Joan Miró');34 expect(parseInfoPage(asInertiaHtml(INFO_JSON))).toEqual({ endedAt: '2026-08-13T18:58:44.000000Z', resultsIncludePremium: true });35 expect(parseSessionPage('<html><body>no app</body></html>', WRIGHT, 'https://www.wright20.com/x')).toBeNull();36 });3738 it('normalises sold lots only, dated by the close time, premium flag from the info page', async () => {39 const p = parseSessionPage(asInertiaHtml(SESSION_JSON), WRIGHT, 'https://www.wright20.com/auctions/2026/08/design')!;40 const info = parseInfoPage(asInertiaHtml(INFO_JSON));41 p.session.endedAt = info.endedAt;42 p.session.resultsIncludePremium = info.resultsIncludePremium;43 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 });44 expect(out.length).toBe(2);45 const s = out.find((r) => 'externalId' in r && r.externalId === '413032')!;46 if (s.kind !== 'sale') throw new Error('expected sale');47 expect(s).toMatchObject({ price: 57600, currency: 'USD', buyerPremiumIncluded: true, auctionHouse: 'Wright', lotNumber: '100', rawTitle: 'After Alexander Calder, Turquoise tapestry', location: 'Los Angeles' });48 expect(s.saleDate.toISOString()).toBe('2026-08-13T18:58:44.000Z');49 expect(s.sourceUrl).toBe('https://www.wright20.com/auctions/2026/08/design/100');50 expect(s.attributes).toMatchObject({ categorySlug: 'design_furniture', name: 'Turquoise tapestry', brand: null });51 expect(s.attributes.identifiers).toEqual({ wright_item_id: '413032' });52 expect(s.attributes.metadata).toMatchObject({ artist: 'After Alexander Calder', estimate_low: 15000, estimate_high: 20000, bid_count: 14 });53 // without the info page the premium basis is unknown and the auction date is used54 p.session.endedAt = null;55 p.session.resultsIncludePremium = null;56 const out2 = await connector.normalize({ url: 'x', externalId: 'e', kind: 'sale', engine: 'api', fetchedAt: new Date('2026-09-08T00:00:00Z'), payload: p });57 const s2 = out2.find((r) => 'externalId' in r && r.externalId === '413032')!;58 if (s2.kind !== 'sale') throw new Error('expected sale');59 expect(s2.buyerPremiumIncluded).toBeNull();60 expect(s2.saleDate.toISOString()).toBe('2026-08-13T16:00:00.000Z');61 });6263 it('emits auction lots for future sessions and lowers confidence on preliminary results', async () => {64 const future = JSON.parse(JSON.stringify(SESSION_JSON)) as typeof SESSION_JSON;65 const nextYear = Math.floor(Date.now() / 1000) + 30 * 86_400;66 future.props.sessions['2884']!.start_date = nextYear;67 future.props.sessions['2884']!.archive_auction = 0;68 future.props.sessions['2884']!.sale_status = 'PRESALE';69 for (const it of Object.values(future.props.items)) (it as { result: number | boolean }).result = false;70 const p = parseSessionPage(asInertiaHtml(future), WRIGHT, 'https://www.wright20.com/auctions/2026/08/design')!;71 const out = await connector.normalize({ url: 'x', externalId: 'e', kind: 'auction_lot', engine: 'api', fetchedAt: new Date(), payload: p });72 expect(out.length).toBe(3);73 expect(out.every((r) => r.kind === 'auction_lot')).toBe(true);74 const lot = out.find((r) => 'externalId' in r && r.externalId === '413032')!;75 if (lot.kind !== 'auction_lot') throw new Error('expected lot');76 expect(lot).toMatchObject({ auctionHouse: 'Wright', auctionName: 'Design', estimateLow: 15000, estimateHigh: 20000, status: 'upcoming', currency: 'USD' });7778 const prelim = parseSessionPage(asInertiaHtml(SESSION_JSON), WRIGHT, 'https://www.wright20.com/auctions/2026/08/design')!;79 prelim.session.preliminary = true;80 const out2 = await connector.normalize({ url: 'x', externalId: 'e', kind: 'sale', engine: 'api', fetchedAt: new Date(), payload: prelim });81 expect((out2[0] as { confidence: number }).confidence).toBe(0.75);82 expect((out2[0] as { attributes: { metadata: Record<string, unknown> } }).attributes.metadata.preliminary).toBe(true);83 });8485 it('maps sale titles to taxonomy slugs', () => {86 expect(ragoCategory('Design', 'Sam Maloof, Coffee table')).toBe('design_furniture');87 expect(ragoCategory('Art + Design', 'Takashi Murakami, Flowers screenprint')).toBe('contemporary_art');88 expect(ragoCategory('Photographs', 'Irving Penn, gelatin silver print')).toBe('photography');89 expect(ragoCategory('Jewelry', 'Rolex, Submariner wristwatch')).toBe('rolex');90 expect(ragoCategory('Art of Africa, Oceania and the Americas', 'Yoruba, mask')).toBe('antiques');91 expect(ragoCategory('Scandinavian Design', 'Hans Wegner, Papa Bear chair')).toBe('design_furniture');92 });93});94