import { describe, expect, it } from 'vitest';
import { readFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { loadFixture, runFixtureSuite } from '@rareindex/connectors/testing';
import { localMeta } from '../_lib/local-meta.js';
import { hibidCategory, isFirearm } from './categories.js';
import createConnector, { parseCatalog, parsePastAuctions, slugify } 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);
/** Trimmed REAL Apollo state captured from https://hibid.com/catalog/752746/auction-200-silver-and-paper-money on 2026-09-08 (3 lots, contact details removed). */
const STATE = readFileSync(path.join(dir, 'fixtures.state.json'), 'utf8');
const CATALOG_HTML = `
Auction 200 Silver and Paper Money | HiBid`;
function withPage(pageNumber: number, results: string): string {
return CATALOG_HTML.replace(/"pageNumber":1,"totalCount":3,"filteredCount":3,"results":\[[^\]]*\]/, `"pageNumber":${pageNumber},"totalCount":3,"filteredCount":3,"results":[${results}]`);
}
describe('hibid', () => {
runFixtureSuite(connector, it, expect);
it('parses the auction header and lots from the embedded Apollo state', () => {
const cat = parseCatalog(CATALOG_HTML, 1)!;
expect(cat.auction).toMatchObject({ id: '752746', eventName: 'Auction 200 Silver and Paper Money', currency: 'CAD', buyerPremium: 'Buyers Premium 18%', bidCloseDateTime: '2026-09-07T19:00:00' });
expect(cat.auction.auctioneer).toMatchObject({ name: 'Legacy Auctions', city: 'Leamington', state: 'ON', country: 'Canada' });
expect(cat.pageNumber).toBe(1);
expect(cat.totalCount).toBe(3);
expect(cat.lots.length).toBe(3);
const sold = cat.lots.find((l) => l.lotNumber === '93')!;
expect(sold).toMatchObject({ id: '308002776', title: '1-1954 DEVIL FACE 20 DOLLAR BILL C/E2791225', priceRealized: 48, quantitySold: 1, isClosed: true, categoryPath: 'Coins & Currency - Currency - World', mappable: true });
expect(sold.url).toBe('https://hibid.com/lot/308002776/1-1954-devil-face-20-dollar-bill-c-e2791225');
expect(cat.lots.find((l) => l.lotNumber === '91')!.priceRealized).toBeNull();
expect(cat.lots[0]!.image).toMatch(/^https:\/\/cdn\.hibid\.com\/img\.axd/);
});
it('detects the end of a catalog (page 2 with no results)', () => {
const cat = parseCatalog(withPage(2, ''), 2)!;
expect(cat.pageNumber).toBe(2);
expect(cat.lots.length).toBe(0);
});
it('maps HiBid categories to taxonomy slugs and filters non-collectibles and firearms', () => {
expect(hibidCategory('Coins & Currency - Currency - Canada', '1-1937 BANK OF CANADA 1 DOLLAR BILL S/N3599211')).toBe('banknotes');
expect(hibidCategory('Coins & Currency - Coins - Canada', '1967 Canada Silver Dollar')).toBe('coins');
expect(hibidCategory('Sports Memorabilia & Cards - Cards', '1986 Fleer Michael Jordan Rookie PSA 8')).toBe('basketball_cards');
expect(hibidCategory('Toys & Hobbies - Action Figures', 'Star Wars Kenner Luke Skywalker MOC')).toBe('action_figures');
expect(hibidCategory('Jewelry, Watches & Gemstones - Watches', 'Rolex Submariner 16610 wristwatch')).toBe('rolex');
expect(hibidCategory('Real Estate - Residential', '3 bedroom house')).toBeNull();
expect(hibidCategory('Construction & Farm - Tractors', 'John Deere 4020')).toBeNull();
expect(hibidCategory('Household - Kitchen', 'Kitchen aid mixer')).toBeNull();
expect(hibidCategory('Firearms & Military - Rifles', 'Winchester Model 70')).toBeNull();
// Liquidators file new merchandise under "Antiques & Collectibles" (seen live: ToyTexx wholesale sale) → filtered.
expect(hibidCategory('Antiques & Collectibles - Collectibles - Bakeware', '15PCS Nonstick Roasting Pan with Removable Flat Rack')).toBeNull();
expect(hibidCategory('Antiques & Collectibles - Collectibles - Decorative - Ornaments', '10PCS Christmas Tree Building Sets, 730PC')).toBeNull();
expect(hibidCategory('Antiques & Collectibles - Collectibles - Advertising', 'INFORMATION ONLY - DO NOT BID')).toBeNull();
expect(hibidCategory('Antiques & Collectibles - Collectibles - Advertising', 'Coca-Cola porcelain sign 1950s')).toBe('advertising');
expect(hibidCategory('Antiques & Collectibles - Collectibles - Kitchen', 'Vintage Pyrex Gooseberry bowl 1958')).toBe('antiques');
expect(hibidCategory('Antiques & Collectibles - Collectibles', 'Vintage 1960s Zippo lighter')).toBe('lighters');
expect(isFirearm('Antiques & Collectibles - Collectibles Remington .22 caliber rifle')).toBe(true);
expect(isFirearm('1-1954 DEVIL FACE 20 DOLLAR BILL')).toBe(false);
expect(slugify('Elite Auction SALE – Fine Jewelry, Rolex Watches, Art & Coll')).toBe('elite-auction-sale-fine-jewelry-rolex-watches-art-coll');
});
it('parses the past-auction list (archived auctions + paging)', () => {
// Real page shape reduced to one auction entry.
const past = ``;
const list = parsePastAuctions(past)!;
expect(list).toMatchObject({ pageNumber: 2, totalCount: 1324, pageLength: 25 });
expect(list.auctions).toEqual([{ id: '770300', eventName: '09/07/2026 @ 10am CT - Annual Labor Day: Fine Auction', url: 'https://hibid.com/catalog/770300/09-07-2026-10am-ct-annual-labor-day-fine-auction', bidCloseDateTime: '2026-09-07T23:00:00', eventDateEnd: '2026-09-07T00:00:00', lotCount: 455, status: 'ARCHIVED' }]);
});
it('normalises only closed lots with a published price realized (hammer, CAD, premium not included)', async () => {
const cat = parseCatalog(CATALOG_HTML, 1)!;
const payload = { kind: 'hibid_catalog_page', auction: cat.auction, page: 1, totalCount: cat.totalCount, lots: cat.lots.filter((l) => l.mappable).map(({ mappable: _m, ...rest }) => rest) };
const out = await connector.normalize({ url: 'https://hibid.com/catalog/752746/auction-200-silver-and-paper-money', externalId: 'catalog:752746:page:1', kind: 'sale', engine: 'api', fetchedAt: new Date('2026-09-08T08:00:00Z'), payload });
expect(out.length).toBe(1);
const s = out[0]!;
if (s.kind !== 'sale') throw new Error('expected sale');
expect(s).toMatchObject({ price: 48, currency: 'CAD', buyerPremiumIncluded: false, auctionHouse: 'Legacy Auctions (HiBid)', lotNumber: '93', location: 'Leamington, ON, Canada', quantity: 1, isBundle: false });
expect(s.saleDate.toISOString()).toBe('2026-09-07T19:00:00.000Z');
expect(s.attributes.categorySlug).toBe('banknotes');
expect(s.attributes.identifiers).toEqual({ hibid_lot_id: '308002776', hibid_auction_id: '752746' });
expect(s.attributes.metadata).toMatchObject({ buyer_premium_text: 'Buyers Premium 18%', category_path: 'Coins & Currency - Currency - World' });
expect(s.attributes.year).toBe(1954);
});
it('skips unsupported currencies and firearms at normalisation time', async () => {
const cat = parseCatalog(CATALOG_HTML, 1)!;
const lots = cat.lots.filter((l) => l.mappable).map(({ mappable: _m, ...rest }) => rest);
const gun = { ...lots[1]!, id: '1', title: 'Winchester Model 94 rifle .30-30', categoryPath: 'Antiques & Collectibles - Collectibles', priceRealized: 500 };
const mxn = await connector.normalize({ url: 'x', externalId: 'e', kind: 'sale', engine: 'api', fetchedAt: new Date(), payload: { kind: 'hibid_catalog_page', auction: { ...cat.auction, currency: 'MXN' }, page: 1, totalCount: 3, lots } });
expect(mxn.length).toBe(0);
const withGun = await connector.normalize({ url: 'x', externalId: 'e', kind: 'sale', engine: 'api', fetchedAt: new Date(), payload: { kind: 'hibid_catalog_page', auction: cat.auction, page: 1, totalCount: 3, lots: [...lots, gun] } });
expect(withGun.map((r) => ('externalId' in r ? r.externalId : null))).toEqual(['308002776']);
});
it('fixture sales are CAD/USD hammer prices dated by the auction close', async () => {
const out = await connector.normalize(loadFixture('hibid', 'coins-1').raw);
expect(out.length).toBeGreaterThan(0);
for (const r of out) {
if (r.kind !== 'sale') throw new Error('expected sale');
expect(['CAD', 'USD']).toContain(r.currency);
expect(r.buyerPremiumIncluded).toBe(false);
}
});
});