TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { describe, expect, it } from 'vitest';2import { getConnectorMeta } from '@rareindex/connectors';3import { loadFixture, runFixtureSuite } from '@rareindex/connectors/testing';4import createConnector, { categoryPageAuctionIds, classifyLot, parseAuctionPage, parseLotPage, type AuctionLotsPayload, type LotPayload } from './index.js';56const connector = createConnector(getConnectorMeta('catawiki'));78describe('catawiki connector', () => {9 runFixtureSuite(connector, it, expect);1011 it('turns a closed, sold lot into a hammer-price sale in EUR with the source end time', async () => {12 const fx = loadFixture('catawiki', 'seeded-1');13 const lot = fx.raw.payload as LotPayload;14 const out = await connector.normalize(fx.raw);15 expect(out).toHaveLength(1);16 const r = out[0]!;17 if (r.kind !== 'sale') throw new Error('expected sale');18 expect(r.price).toBe(lot.bidding.finalBidEur);19 expect(r.currency).toBe('EUR');20 expect(r.buyerPremiumIncluded).toBe(false);21 expect(r.saleDate.getTime()).toBe(lot.bidding.biddingEndTime);22 expect(r.auctionHouse).toBe('Catawiki');23 expect(r.location).toBe(lot.sellerCountry);24 expect(r.attributes.identifiers.catawiki_lot_id).toBe(String(lot.id));25 expect(r.attributes.metadata.estimate_min_eur).toBe(lot.estimateMinEur);26 expect(r.attributes.material).toBe('Wood');27 expect(r.attributes.categorySlug).toBe('antiques');28 });2930 it('drops closed-unsold lots and emits live lots as auction_lot', async () => {31 const fx = loadFixture('catawiki', 'seeded-1');32 const lot = structuredClone(fx.raw.payload) as LotPayload;33 const unsold = { ...lot, bidding: { ...lot.bidding, sold: false } };34 expect(await connector.normalize({ ...fx.raw, payload: unsold })).toHaveLength(0);35 const live = { ...lot, bidding: { ...lot.bidding, closed: false, sold: null, biddingEndTime: Date.now() + 3 * 86_400_000 } };36 const out = await connector.normalize({ ...fx.raw, payload: live });37 expect(out).toHaveLength(1);38 expect(out[0]!.kind).toBe('auction_lot');39 if (out[0]!.kind === 'auction_lot') {40 expect(out[0]!.status).toBe('live');41 expect(out[0]!.currentBid).toBe(lot.bidding.finalBidEur);42 expect(out[0]!.estimateLow).toBe(lot.estimateMinEur);43 }44 });4546 it('emits auction_lot records for a live themed auction and none for a stale one', async () => {47 const fx = loadFixture('catawiki', 'seeded-2');48 const payload = structuredClone(fx.raw.payload) as AuctionLotsPayload;49 const future = new Date(Date.now() + 2 * 86_400_000).toISOString();50 payload.auction = { ...payload.auction, closeAt: future, startAt: new Date(Date.now() - 86_400_000).toISOString() };51 const out = await connector.normalize({ ...fx.raw, payload });52 expect(out.length).toBeGreaterThan(payload.lots.length * 0.8);53 for (const r of out) {54 expect(r.kind).toBe('auction_lot');55 if (r.kind !== 'auction_lot') continue;56 expect(r.endsAt?.toISOString()).toBe(new Date(future).toISOString());57 expect(r.currency).toBe('EUR');58 expect(['rolex', 'patek_philippe', 'audemars_piguet', 'omega', 'other_watches', 'pens', 'lighters', 'clocks', 'jewelry']).toContain(r.attributes.categorySlug);59 }60 const stale = { ...payload, auction: { ...payload.auction, closeAt: '2020-01-01T00:00:00Z' } };61 expect(await connector.normalize({ ...fx.raw, payload: stale })).toHaveLength(0);62 });6364 it('classifies lots from category path, specs and titles', () => {65 const base: LotPayload = { kind: 'lot', seedHint: 'toys', id: 1, url: 'https://www.catawiki.com/en/l/1', title: 'LEGO - Star Wars - 10179 - Millennium Falcon UCS', subtitle: 'Sealed', description: null, images: [], categoryId: null, categoryUrl: null, auction: { id: 1, title: 'LEGO Auction', url: 'https://www.catawiki.com/en/a/1', status: 'closed', startAt: null, closeAt: null, closedAt: null, categories: ['Toys & Models', 'LEGO'], lotCount: null }, specs: [{ name: 'Set number', value: '10179' }], estimateMinEur: null, estimateMaxEur: null, sellerCountry: 'NL', sellerName: null, sellerIsPro: null, bidding: { closed: true, sold: true, finalBidEur: 2500, biddingStartTime: null, biddingEndTime: 1, bidCount: null, reservePriceMet: null } };66 expect(classifyLot(base).slug).toBe('lego_sets');67 expect(classifyLot({ ...base, seedHint: 'cards', title: 'Pokémon - Charizard Base Set 4/102 - PSA 9', auction: { ...base.auction!, categories: ['Trading Cards', 'Pokémon'] } }).slug).toBe('pokemon');68 expect(classifyLot({ ...base, seedHint: 'watches', title: 'Rolex - Submariner 16610 - Men - 2005', auction: { ...base.auction!, categories: ['Watches, Pens & Lighters', 'Watches'] } }).slug).toBe('rolex');69 expect(classifyLot({ ...base, seedHint: 'wine', title: 'Macallan 18 Sherry Oak 2020 release', auction: { ...base.auction!, categories: ['Wine, Whisky & Spirits', 'Whisky'] } }).slug).toBe('whisky');70 expect(classifyLot({ ...base, seedHint: 'coins', title: 'Netherlands 10 Gulden 1875 gold', auction: { ...base.auction!, categories: ['Coins & Stamps', 'Coins'] } }).slug).toBe('coins');71 expect(categoryPageAuctionIds('<a href="/en/a/1263062-vintage"></a><a href="https://www.catawiki.com/en/a/1263062-vintage"></a><a href="/en/a/99-x">')).toEqual([1263062, 99]);72 expect(parseAuctionPage('<html></html>')).toBeNull();73 expect(parseLotPage('<html></html>', 'https://www.catawiki.com/en/l/1', 'unknown')).toBeNull();74 });75});76