TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { describe, expect, it } from 'vitest';2import type { NormalizedRecord } from '@rareindex/shared';3import { ConnectorMetaSchema } from '@rareindex/connectors';4import { listFixtures, loadFixture, runFixtureSuite } from '@rareindex/connectors/testing';5import metaJson from './meta.json' with { type: 'json' };6import createConnector, { parseAuDate, parseResultsIndex, parseResultsPage } from './index.js';78const meta = ConnectorMetaSchema.parse(metaJson);9const connector = createConnector(meta);10const attrsOf = (r: NormalizedRecord) => {11 if (!('attributes' in r)) throw new Error(`record kind ${r.kind} has no attributes`);12 return r.attributes;13};1415const INDEX = `<section class="flexible-auction-calendar-item col-2"> <div class="flexible-auction-calendar-item__date"> <p>7 September</p> </div> <div class="flexible-auction-calendar-item__image" onclick="location.href='/custom_asp/searchresults.asp?type=result&st=D&pg=1&ps=100&sale_no=IT127++';"></div> <div class="flexible-auction-calendar-item__lot"> <p>Sale IT127</p> </div> <div class="flexible-auction-calendar-item__heading"> <h3>Modern Design</h3> </div> <div class="flexible-auction-calendar-item__auction-details"> <p class="flexible-auction-calendar-item__sub-title">AUCTION</p> <p>Monday 7 Sep 2026, 10:00am</p> <p class="online">Timed Online Auction (Melbourne)</p> </div></section>16<section class="flexible-auction-calendar-item col-2"><h3>Fine Jewels Sydney</h3><a href="/custom_asp/searchresults.asp?type=result&st=D&pg=1&ps=100&sale_no=LJ8818+">Browse</a><div class="flexible-auction-calendar-item__auction-details"><p>Monday 17 Aug 2026, 6:30pm</p><p class="online">Live Auction (Sydney)</p></div></section>`;1718const PAGE = `<h1>Fine Jewels Sydney</h1><p> Sale: LJ8818<br> 17 August 2026, 6:30pm <em>36-40 Queen Street, Woollahra</em> </p>19<section class="flexible-auction-item auction-collection-item col-2 col-md-1"> <a id="40936777"></a> <a href="https://auctions.leonardjoel.com.au/asp/fullCatalogue.asp?salelot=LJ8818+++201+&refno=40936777&saletype= "><div class="flexible-auction-item__image" style="background-image: url(https://auctions.leonardjoel.com.au/Thumb//777/40936777tn.jpg);"></div></a> <div class="flexible-auction-item__heading"> <h6>Lot 201</h6> <h4>ROBERT CLERC EGYPTIAN COLLECTION LAPIS LAZULI, EMERALD, QUARTZ AND DIAMOND NECKLACE, CIRCA 1993 Property of a Private New South Wa...</h4> </div> <div class="flexible-auction-item__lot"> <p>Estimate:<br/> $4,600 - 5,500<p><b>Sold for $8,500</b></p></i> </div> </section>20<section class="flexible-auction-item auction-collection-item col-2 col-md-1"> <a href="https://auctions.leonardjoel.com.au/asp/fullCatalogue.asp?salelot=LJ8818+++202+&refno=40936778&saletype= "><div class="flexible-auction-item__image"></div></a> <div class="flexible-auction-item__heading"> <h6>Lot 202</h6> <h4>ROLEX DATEJUST WRISTWATCH REF 16233</h4> </div> <div class="flexible-auction-item__lot"> <p>Estimate:<br/> $8,000 - 12,000</p> </div> </section>21<div class="pagination"><ul><li><a href='/custom_asp/searchresults.asp?type=result&st=D&pg=2&ps=100&sale_no=LJ8818'>2</a><a href='/custom_asp/searchresults.asp?type=result&st=D&ps=100&pg=2&sale_no=LJ8818'>→</a></li></ul></div>`;2223describe('leonard-joel', () => {24 runFixtureSuite(connector, it, expect);2526 it('fixtures: AUD "Sold for" results', async () => {27 let sales = 0;28 for (const name of listFixtures('leonard-joel')) {29 for (const r of await connector.normalize(loadFixture('leonard-joel', name).raw)) {30 if (!('attributes' in r)) continue;31 expect(r.attributes.identifiers.leonard_joel_lot).toMatch(/^[A-Z]{1,3}\d+\/\S+$/);32 expect(r.sourceUrl).toMatch(/^https:\/\/auctions\.leonardjoel\.com\.au\//);33 if (r.kind === 'sale') {34 sales++;35 expect(r.currency).toBe('AUD');36 expect(r.buyerPremiumIncluded).toBeNull();37 expect(r.auctionHouse).toBe('Leonard Joel');38 }39 }40 }41 expect(sales).toBeGreaterThan(10);42 });4344 it('parses the results index and a results page (AUD, estimates, pagination)', async () => {45 const sales = parseResultsIndex(INDEX);46 expect(sales.map((s) => s.id)).toEqual(['IT127', 'LJ8818']);47 expect(sales[0]).toMatchObject({ title: 'Modern Design', date: '2026-09-07T00:00:00.000Z', location: 'Melbourne' });48 expect(parseAuDate('17 August 2026, 6:30pm')).toBe('2026-08-17T00:00:00.000Z');49 const p = parseResultsPage(PAGE, sales[1]!, 1)!;50 expect(p.hasMore).toBe(false); // only 2 lots (< page size) even though a page-2 link exists51 expect(p.sale).toMatchObject({ title: 'Fine Jewels Sydney', date: '2026-08-17T00:00:00.000Z', location: '36-40 Queen Street, Woollahra' });52 expect(p.lots[0]).toMatchObject({ lotNo: '201', price: 8500, currency: 'AUD', premiumIncluded: null, estimateLow: 4600, estimateHigh: 5500, sold: true, image: 'https://auctions.leonardjoel.com.au/Thumb//777/40936777tn.jpg' });53 expect(p.lots[0]!.url).toBe('https://auctions.leonardjoel.com.au/asp/fullCatalogue.asp?salelot=LJ8818+++201+&refno=40936777&saletype=');54 expect(p.lots[1]).toMatchObject({ lotNo: '202', price: null, sold: false, estimateLow: 8000, estimateHigh: 12000 });55 const out = await connector.normalize({ url: sales[1]!.url, kind: 'sale', engine: 'api', fetchedAt: new Date(), payload: { kind: 'sale_results', url: sales[1]!.url, sale: { ...sales[1]!, ...p.sale, extra: sales[1]!.extra }, page: 1, totalLots: null, lots: p.lots } });56 expect(out.map((r) => r.kind)).toEqual(['sale', 'auction_lot']);57 expect(attrsOf(out[0]!).categorySlug).toBe('jewelry');58 expect(attrsOf(out[1]!).categorySlug).toBe('rolex');59 expect(parseResultsPage('<html>x</html>', sales[1]!, 1)).toBeNull();60 });61});62