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 createConnector, { parseItemPage, salesHistorySitemaps } from './index.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);
/** Real prefetchedItemData captured live from https://www.fanaticscollect.com/weekly/1e389fce-6f23-11ed-a610-0a58a9feac02 (2026-09-08), trimmed. */
const SOLD_LISTING = {
__typename: 'CollectListing',
id: '1e389fce-6f23-11ed-a610-0a58a9feac02',
title: '2003 Fleer Tradition LeBron James ROOKIE #261 PSA 9 MINT',
listingType: 'WEEKLY',
isManageable: false,
certifiedSeller: 'Fanatics Collect',
currentBid: { __typename: 'Money', amountInCents: 23000, currency: 'USD' },
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' },
integerId: 3093498,
insertedAt: '2021-12-22T13:41:22Z',
updatedAt: '2023-01-09T18:38:25Z',
soldDate: null,
lotString: 'WA1 Lot: 1100',
imageSets: [{ __typename: 'CollectImageSet', large: 'https://dilxwvfkfup17.cloudfront.net/abc-large', medium: 'https://dilxwvfkfup17.cloudfront.net/abc-medium' }],
slug: null,
soldFor: null,
startingPrice: { __typename: 'Money', amountInCents: 500, currency: 'USD' },
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.',
status: 'STATUS_SOLD',
bidCount: 27,
collectSales: [{ __typename: 'CollectSale', soldDate: '2022-01-24T04:10:06Z', soldFor: { __typename: 'Money', amountInCents: 27600, currency: 'USD' } }],
vaultItem: { __typename: 'CollectVaultItem', id: '0bf62e5c-6f1f-11ed-a6c4-0a58a9feac02', integerId: 732786 },
};
/** Real fixed-price (best offer) listing from /fixed/eb86cdda-a912-11f0-8fa5-0a58a9feac02/… — sold via offer, but no CollectSale exposed. */
const FIXED_LISTING = {
__typename: 'CollectListing',
id: '178984d1-fd50-473c-8e6d-3e96d63b5ba3',
title: '2000 Pokemon Insert Card Ancient Mew CGC 5.5 EX+',
listingType: 'BO',
status: 'BO_STATUS_SOLD',
soldDate: null,
soldFor: null,
startingPrice: { __typename: 'Money', amountInCents: 0, currency: 'USD' },
collectSales: [],
vaultItem: { __typename: 'CollectVaultItem', id: '60ecaa72-89d8-11f0-85e1-02f5d6e69f97' },
imageSets: [{ __typename: 'CollectImageSet', large: 'https://cdn-vault.fanaticscollect.com/2025/9/4/rm2/large/v1503807_2020090306325159R_23.jpg' }],
slug: '2000-pokemon-insert-card-ancient-mew-cgc-55-ex',
description: '',
};
/** Wrap an object in a Next.js flight chunk the way the live page does (JS-string escaped JSON). */
function flightHtml(listing: unknown): string {
const line = `2f:["$","$L42",null,{"prefetchedItemData":${JSON.stringify(listing)},"foo":1}]\n`;
return `
x`;
}
const SITEMAP_INDEX = `
https://www.fanaticscollect.com/sitemap/pages.xml.gz
https://www.fanaticscollect.com/sitemap/weekly-auction-1.xml.gz
https://www.fanaticscollect.com/sitemap/sales-history-weekly-auction-1.xml.gz
https://www.fanaticscollect.com/sitemap/sales-history-weekly-auction-2.xml.gz
https://www.fanaticscollect.com/sitemap/sales-history-fixed-price-1.xml.gz
https://www.fanaticscollect.com/sitemap/sales-history-fixed-price-7.xml.gz
`;
describe('fanatics-collect', () => {
runFixtureSuite(connector, it, expect);
it('parses the RSC flight payload of a sold weekly lot', () => {
const url = 'https://www.fanaticscollect.com/weekly/1e389fce-6f23-11ed-a610-0a58a9feac02';
const p = parseItemPage(flightHtml(SOLD_LISTING), url, '2023-01-09')!;
expect(p.kind).toBe('fc_item');
expect(p.listing).toMatchObject({ id: '1e389fce-6f23-11ed-a610-0a58a9feac02', listingType: 'WEEKLY', status: 'STATUS_SOLD', bidCount: 27, lotString: 'WA1 Lot: 1100' });
expect(p.listing.collectSales[0]).toEqual({ soldDate: '2022-01-24T04:10:06Z', soldFor: { amountInCents: 27600, currency: 'USD' } });
expect(p.listing.images).toEqual(['https://dilxwvfkfup17.cloudfront.net/abc-large']);
expect(p.listing.auction).toMatchObject({ shortName: 'WA1', endsAt: '2022-01-24T03:00:00Z' });
expect((p.listing as Record).__typename).toBeUndefined();
});
it('normalises a sale with buyer premium included, hammer in metadata, PSA grade and lot number', async () => {
const url = 'https://www.fanaticscollect.com/weekly/1e389fce-6f23-11ed-a610-0a58a9feac02';
const p = parseItemPage(flightHtml(SOLD_LISTING), url, '2023-01-09')!;
const out = await connector.normalize({ url, externalId: p.listing.id, kind: 'sale', engine: 'api', fetchedAt: new Date('2026-09-08T10:00:00Z'), payload: p });
expect(out.length).toBe(1);
const s = out[0]!;
if (s.kind !== 'sale') throw new Error('expected sale');
expect(s).toMatchObject({ price: 276, currency: 'USD', buyerPremiumIncluded: true, auctionHouse: 'Fanatics Collect', lotNumber: '1100', saleType: 'auction', location: 'US', externalId: '1e389fce-6f23-11ed-a610-0a58a9feac02' });
expect(s.saleDate.toISOString()).toBe('2022-01-24T04:10:06.000Z');
expect(s.attributes.categorySlug).toBe('basketball_cards');
expect(s.attributes.year).toBe(2003);
expect(s.attributes.metadata).toMatchObject({ hammer_price: 230, buyer_premium_pct: 20, auction_short_name: 'WA1', listing_type: 'WEEKLY' });
expect(s.attributes.identifiers).toEqual({ fanatics_listing_id: '1e389fce-6f23-11ed-a610-0a58a9feac02', fanatics_vault_item_id: '0bf62e5c-6f1f-11ed-a6c4-0a58a9feac02' });
expect(s.grade).toMatchObject({ grader: 'psa', grade: '9' });
expect(s.confidence).toBe(0.9);
});
it('skips fixed-price pages that expose no CollectSale', async () => {
const url = 'https://www.fanaticscollect.com/fixed/eb86cdda-a912-11f0-8fa5-0a58a9feac02/2000-pokemon-insert-card-ancient-mew-cgc-55-ex';
const p = parseItemPage(flightHtml(FIXED_LISTING), url)!;
expect(p.listing.listingType).toBe('BO');
expect(p.listing.collectSales).toEqual([]);
expect(await connector.normalize({ url, externalId: p.listing.id, kind: 'sale', engine: 'api', fetchedAt: new Date(), payload: p })).toEqual([]);
});
it('records fixed-price sales without a premium flag when a CollectSale exists', async () => {
const url = 'https://www.fanaticscollect.com/fixed/178984d1-fd50-473c-8e6d-3e96d63b5ba3/x';
const p = parseItemPage(flightHtml({ ...FIXED_LISTING, collectSales: [{ __typename: 'CollectSale', soldDate: '2025-10-22T09:59:16.000Z', soldFor: { __typename: 'Money', amountInCents: 500, currency: 'USD' } }] }), url)!;
const out = await connector.normalize({ url, externalId: p.listing.id, kind: 'sale', engine: 'api', fetchedAt: new Date(), payload: p });
expect(out.length).toBe(1);
const s = out[0]!;
if (s.kind !== 'sale') throw new Error('expected sale');
expect(s).toMatchObject({ price: 5, saleType: 'fixed_price', buyerPremiumIncluded: null, lotNumber: null });
expect(s.attributes.categorySlug).toBe('pokemon');
expect(s.grade).toMatchObject({ grader: 'cgc', grade: '5.5' });
});
it('returns null for pages without listing JSON and orders sales-history sitemaps newest first', () => {
expect(parseItemPage('nothing', 'https://www.fanaticscollect.com/weekly/x')).toBeNull();
expect(salesHistorySitemaps(SITEMAP_INDEX)).toEqual([
'https://www.fanaticscollect.com/sitemap/sales-history-fixed-price-7.xml.gz',
'https://www.fanaticscollect.com/sitemap/sales-history-weekly-auction-2.xml.gz',
'https://www.fanaticscollect.com/sitemap/sales-history-fixed-price-1.xml.gz',
'https://www.fanaticscollect.com/sitemap/sales-history-weekly-auction-1.xml.gz',
]);
});
it('recognises item URLs for lookup', () => {
expect(connector.urlPatterns![0]!.test('https://www.fanaticscollect.com/weekly/1e389fce-6f23-11ed-a610-0a58a9feac02')).toBe(true);
expect(connector.urlPatterns![0]!.test('https://www.fanaticscollect.com/fixed/eb86cdda-a912-11f0-8fa5-0a58a9feac02/slug')).toBe(true);
expect(connector.urlPatterns![0]!.test('https://www.fanaticscollect.com/marketplace?type=WEEKLY')).toBe(false);
});
});