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 createConnector, { parseItemPage, salesHistorySitemaps } from './index.js';89const dir = path.dirname(fileURLToPath(import.meta.url));10const meta = localMeta(JSON.parse(readFileSync(path.join(dir, 'meta.json'), 'utf8')));11const connector = createConnector(meta);1213/** Real prefetchedItemData captured live from https://www.fanaticscollect.com/weekly/1e389fce-6f23-11ed-a610-0a58a9feac02 (2026-09-08), trimmed. */14const SOLD_LISTING = {15 __typename: 'CollectListing',16 id: '1e389fce-6f23-11ed-a610-0a58a9feac02',17 title: '2003 Fleer Tradition LeBron James ROOKIE #261 PSA 9 MINT',18 listingType: 'WEEKLY',19 isManageable: false,20 certifiedSeller: 'Fanatics Collect',21 currentBid: { __typename: 'Money', amountInCents: 23000, currency: 'USD' },22 auction: { __typename: 'CollectWeeklyAuction', id: '8d27de96-6f1d-11ed-ab20-0a58a9feac02', auctionIntegerId: 30, integerId: 172, name: '2022 Weekly Auction #1', shortName: 'WA1', payoutDate: '2022-02-16', startsAt: '2022-01-14T03:00:00Z', endsAt: '2022-01-24T03:00:00Z', status: 'CLOSED' },23 integerId: 3093498,24 insertedAt: '2021-12-22T13:41:22Z',25 updatedAt: '2023-01-09T18:38:25Z',26 soldDate: null,27 lotString: 'WA1 Lot: 1100',28 imageSets: [{ __typename: 'CollectImageSet', large: 'https://dilxwvfkfup17.cloudfront.net/abc-large', medium: 'https://dilxwvfkfup17.cloudfront.net/abc-medium' }],29 slug: null,30 soldFor: null,31 startingPrice: { __typename: 'Money', amountInCents: 500, currency: 'USD' },32 description: 'Welcome to our first Weekly Sunday Auction! This is one of over 3,600 items spanning all sports and genres, closing on the evening of Sunday, January 23rd.',33 status: 'STATUS_SOLD',34 bidCount: 27,35 collectSales: [{ __typename: 'CollectSale', soldDate: '2022-01-24T04:10:06Z', soldFor: { __typename: 'Money', amountInCents: 27600, currency: 'USD' } }],36 vaultItem: { __typename: 'CollectVaultItem', id: '0bf62e5c-6f1f-11ed-a6c4-0a58a9feac02', integerId: 732786 },37};3839/** Real fixed-price (best offer) listing from /fixed/eb86cdda-a912-11f0-8fa5-0a58a9feac02/… — sold via offer, but no CollectSale exposed. */40const FIXED_LISTING = {41 __typename: 'CollectListing',42 id: '178984d1-fd50-473c-8e6d-3e96d63b5ba3',43 title: '2000 Pokemon Insert Card Ancient Mew CGC 5.5 EX+',44 listingType: 'BO',45 status: 'BO_STATUS_SOLD',46 soldDate: null,47 soldFor: null,48 startingPrice: { __typename: 'Money', amountInCents: 0, currency: 'USD' },49 collectSales: [],50 vaultItem: { __typename: 'CollectVaultItem', id: '60ecaa72-89d8-11f0-85e1-02f5d6e69f97' },51 imageSets: [{ __typename: 'CollectImageSet', large: 'https://cdn-vault.fanaticscollect.com/2025/9/4/rm2/large/v1503807_2020090306325159R_23.jpg' }],52 slug: '2000-pokemon-insert-card-ancient-mew-cgc-55-ex',53 description: '',54};5556/** Wrap an object in a Next.js flight chunk the way the live page does (JS-string escaped JSON). */57function flightHtml(listing: unknown): string {58 const line = `2f:["$","$L42",null,{"prefetchedItemData":${JSON.stringify(listing)},"foo":1}]\n`;59 return `<html><head><title>x</title></head><body><script>self.__next_f.push([1,"1:\\"$Sreact.fragment\\"\\n"])</script><script>self.__next_f.push([1,${JSON.stringify(line)}])</script></body></html>`;60}6162const SITEMAP_INDEX = `<?xml version="1.0" encoding="UTF-8"?><sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">63<sitemap><loc>https://www.fanaticscollect.com/sitemap/pages.xml.gz</loc></sitemap>64<sitemap><loc>https://www.fanaticscollect.com/sitemap/weekly-auction-1.xml.gz</loc></sitemap>65<sitemap><loc>https://www.fanaticscollect.com/sitemap/sales-history-weekly-auction-1.xml.gz</loc></sitemap>66<sitemap><loc>https://www.fanaticscollect.com/sitemap/sales-history-weekly-auction-2.xml.gz</loc></sitemap>67<sitemap><loc>https://www.fanaticscollect.com/sitemap/sales-history-fixed-price-1.xml.gz</loc></sitemap>68<sitemap><loc>https://www.fanaticscollect.com/sitemap/sales-history-fixed-price-7.xml.gz</loc></sitemap>69</sitemapindex>`;7071describe('fanatics-collect', () => {72 runFixtureSuite(connector, it, expect);7374 it('parses the RSC flight payload of a sold weekly lot', () => {75 const url = 'https://www.fanaticscollect.com/weekly/1e389fce-6f23-11ed-a610-0a58a9feac02';76 const p = parseItemPage(flightHtml(SOLD_LISTING), url, '2023-01-09')!;77 expect(p.kind).toBe('fc_item');78 expect(p.listing).toMatchObject({ id: '1e389fce-6f23-11ed-a610-0a58a9feac02', listingType: 'WEEKLY', status: 'STATUS_SOLD', bidCount: 27, lotString: 'WA1 Lot: 1100' });79 expect(p.listing.collectSales[0]).toEqual({ soldDate: '2022-01-24T04:10:06Z', soldFor: { amountInCents: 27600, currency: 'USD' } });80 expect(p.listing.images).toEqual(['https://dilxwvfkfup17.cloudfront.net/abc-large']);81 expect(p.listing.auction).toMatchObject({ shortName: 'WA1', endsAt: '2022-01-24T03:00:00Z' });82 expect((p.listing as Record<string, unknown>).__typename).toBeUndefined();83 });8485 it('normalises a sale with buyer premium included, hammer in metadata, PSA grade and lot number', async () => {86 const url = 'https://www.fanaticscollect.com/weekly/1e389fce-6f23-11ed-a610-0a58a9feac02';87 const p = parseItemPage(flightHtml(SOLD_LISTING), url, '2023-01-09')!;88 const out = await connector.normalize({ url, externalId: p.listing.id, kind: 'sale', engine: 'api', fetchedAt: new Date('2026-09-08T10:00:00Z'), payload: p });89 expect(out.length).toBe(1);90 const s = out[0]!;91 if (s.kind !== 'sale') throw new Error('expected sale');92 expect(s).toMatchObject({ price: 276, currency: 'USD', buyerPremiumIncluded: true, auctionHouse: 'Fanatics Collect', lotNumber: '1100', saleType: 'auction', location: 'US', externalId: '1e389fce-6f23-11ed-a610-0a58a9feac02' });93 expect(s.saleDate.toISOString()).toBe('2022-01-24T04:10:06.000Z');94 expect(s.attributes.categorySlug).toBe('basketball_cards');95 expect(s.attributes.year).toBe(2003);96 expect(s.attributes.metadata).toMatchObject({ hammer_price: 230, buyer_premium_pct: 20, auction_short_name: 'WA1', listing_type: 'WEEKLY' });97 expect(s.attributes.identifiers).toEqual({ fanatics_listing_id: '1e389fce-6f23-11ed-a610-0a58a9feac02', fanatics_vault_item_id: '0bf62e5c-6f1f-11ed-a6c4-0a58a9feac02' });98 expect(s.grade).toMatchObject({ grader: 'psa', grade: '9' });99 expect(s.confidence).toBe(0.9);100 });101102 it('skips fixed-price pages that expose no CollectSale', async () => {103 const url = 'https://www.fanaticscollect.com/fixed/eb86cdda-a912-11f0-8fa5-0a58a9feac02/2000-pokemon-insert-card-ancient-mew-cgc-55-ex';104 const p = parseItemPage(flightHtml(FIXED_LISTING), url)!;105 expect(p.listing.listingType).toBe('BO');106 expect(p.listing.collectSales).toEqual([]);107 expect(await connector.normalize({ url, externalId: p.listing.id, kind: 'sale', engine: 'api', fetchedAt: new Date(), payload: p })).toEqual([]);108 });109110 it('records fixed-price sales without a premium flag when a CollectSale exists', async () => {111 const url = 'https://www.fanaticscollect.com/fixed/178984d1-fd50-473c-8e6d-3e96d63b5ba3/x';112 const p = parseItemPage(flightHtml({ ...FIXED_LISTING, collectSales: [{ __typename: 'CollectSale', soldDate: '2025-10-22T09:59:16.000Z', soldFor: { __typename: 'Money', amountInCents: 500, currency: 'USD' } }] }), url)!;113 const out = await connector.normalize({ url, externalId: p.listing.id, kind: 'sale', engine: 'api', fetchedAt: new Date(), payload: p });114 expect(out.length).toBe(1);115 const s = out[0]!;116 if (s.kind !== 'sale') throw new Error('expected sale');117 expect(s).toMatchObject({ price: 5, saleType: 'fixed_price', buyerPremiumIncluded: null, lotNumber: null });118 expect(s.attributes.categorySlug).toBe('pokemon');119 expect(s.grade).toMatchObject({ grader: 'cgc', grade: '5.5' });120 });121122 it('returns null for pages without listing JSON and orders sales-history sitemaps newest first', () => {123 expect(parseItemPage('<html><body>nothing</body></html>', 'https://www.fanaticscollect.com/weekly/x')).toBeNull();124 expect(salesHistorySitemaps(SITEMAP_INDEX)).toEqual([125 'https://www.fanaticscollect.com/sitemap/sales-history-fixed-price-7.xml.gz',126 'https://www.fanaticscollect.com/sitemap/sales-history-weekly-auction-2.xml.gz',127 'https://www.fanaticscollect.com/sitemap/sales-history-fixed-price-1.xml.gz',128 'https://www.fanaticscollect.com/sitemap/sales-history-weekly-auction-1.xml.gz',129 ]);130 });131132 it('recognises item URLs for lookup', () => {133 expect(connector.urlPatterns![0]!.test('https://www.fanaticscollect.com/weekly/1e389fce-6f23-11ed-a610-0a58a9feac02')).toBe(true);134 expect(connector.urlPatterns![0]!.test('https://www.fanaticscollect.com/fixed/eb86cdda-a912-11f0-8fa5-0a58a9feac02/slug')).toBe(true);135 expect(connector.urlPatterns![0]!.test('https://www.fanaticscollect.com/marketplace?type=WEEKLY')).toBe(false);136 });137});138