TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { describe, expect, it } from 'vitest';2import { getConnectorMeta } from '@rareindex/connectors';3import { listFixtures, loadFixture, runFixtureSuite } from '@rareindex/connectors/testing';4import createConnector, { auctionLinks, classifyTitle, hintsForAuction, lotUrl, parseAuctionPage, parseGraphqlLots, type PagePayload } from './index.js';56const connector = createConnector(getConnectorMeta('sothebys'));78describe('sothebys connector', () => {9 runFixtureSuite(connector, it, expect);1011 it('closed sales: price includes premium, hammer kept in metadata, date from the lot closing time', async () => {12 const closed = listFixtures('sothebys').filter((n) => n.startsWith('closed'));13 expect(closed.length).toBeGreaterThan(0);14 const fx = loadFixture('sothebys', closed[0]!);15 const payload = fx.raw.payload as PagePayload;16 expect(payload.auction.state).toBe('Closed');17 const out = await connector.normalize(fx.raw);18 expect(out.length).toBeGreaterThan(0);19 for (const r of out) {20 expect(r.kind).toBe('sale');21 if (r.kind !== 'sale') continue;22 const lot = payload.lots.find((l) => l.lotId === r.externalId)!;23 expect(lot.isSold).toBe(true);24 expect(r.price).toBe(lot.finalPrice);25 expect(r.price).toBeGreaterThanOrEqual(lot.currentBid ?? 0);26 expect(r.buyerPremiumIncluded).toBe(true);27 expect(r.attributes.metadata.hammer_price).toBe(lot.currentBid);28 expect(r.saleDate.toISOString()).toBe(new Date(lot.closingTime ?? payload.auction.closedAt!).toISOString());29 expect(r.auctionHouse).toBe("Sotheby's");30 expect(r.sourceUrl).toBe(lotUrl(payload.auction.url, lot.slug, lot.lotId));31 }32 });3334 it('open sales: lots become auction_lot records with estimates', async () => {35 const live = listFixtures('sothebys').filter((n) => n.startsWith('live'));36 expect(live.length).toBeGreaterThan(0);37 const fx = loadFixture('sothebys', live[0]!);38 const payload = fx.raw.payload as PagePayload;39 expect(payload.auction.state).not.toBe('Closed');40 const out = await connector.normalize(fx.raw);41 expect(out.length).toBeGreaterThan(0);42 for (const r of out) {43 expect(r.kind).toBe('auction_lot');44 if (r.kind !== 'auction_lot') continue;45 expect(['upcoming', 'live']).toContain(r.status);46 expect(r.currency).toBe(payload.auction.currency);47 expect(r.auctionName).toBe(payload.auction.title);48 }49 });5051 it('mixed multi-department sales only trust title keywords', () => {52 const mixed = { auctionId: 'x', url: 'u', title: 'Arcade | New York', saleNumber: null, state: 'Closed', type: 'Timed', departments: ['American Furniture', 'Decorative Art', 'American Art', 'Books & Manuscripts', 'Contemporary Art'], currency: 'USD', location: 'New York', startsAt: null, endsAt: null, closedAt: null, totalLots: null };53 const hints = hintsForAuction(mixed);54 expect(hints[0]).toBe('unknown');55 expect(classifyTitle('Tom Baril — Lilies', hints)).toBeNull();56 expect(classifyTitle('Andy Warhol — Marilyn, screenprint', hints)).toBe('contemporary_art');57 const watches = { ...mixed, title: 'Important Watches', departments: ['Watches'] };58 expect(classifyTitle('Patek Philippe — Nautilus Ref. 5711/1A', hintsForAuction(watches))).toBe('patek_philippe');59 });6061 it('parses GraphQL pages and discovery links defensively', () => {62 expect(parseAuctionPage('<html></html>', 'u')).toBeNull();63 expect(parseGraphqlLots({ errors: [{ message: 'x' }] })).toBeNull();64 const gl = parseGraphqlLots({ data: { auction: { lotCards: { totalCount: 1, hasNextPage: false, lots: [{ lotId: 'l1', title: 'T', lotNumber: { lotDisplayNumber: '7' }, slug: { lotSlug: 't' }, estimateV2: { lowEstimate: { amount: '10' }, highEstimate: { amount: '20' } }, bidState: { isClosed: true, closingTime: '2026-08-26T16:01Z', numberOfBids: 3, currentBidV2: { amount: '900', currency: 'USD' }, sold: { __typename: 'ResultVisible', isSold: true, premiums: { finalPriceV2: { amount: '1152', currency: 'USD' } } } } }] } } } });65 expect(gl?.lots[0]).toMatchObject({ lotId: 'l1', lotNumber: '7', estimateLow: 10, currentBid: 900, finalPrice: 1152, finalCurrency: 'USD', isSold: true });66 expect(auctionLinks('<a href="https://www.sothebys.com/en/buy/auction/2026/arcade-new-york">x</a> https://www.sothebys.com/en/buy/auction/2026/arcade-new-york/lilies')).toEqual(['https://www.sothebys.com/en/buy/auction/2026/arcade-new-york']);67 });68});69