SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
5.2 KB · 64 lines typescript
Raw Blame History
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, { parseCataloguePage, parseNzDate, parsePreviousAuctions } 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 = `<div class="card auction-card mb-3"> <div class="card-body"> <span class="badge bg-secondary">Timed Auction</span> <h2 class="card-title">Vintage Toy Auction</h2> <h3 class="card-subtitle text-muted mb-2">Thursday, 2 - Wednesday, 15 October 2025</h3> <a class="btn btn-dark" href="/1858">Auction Details</a> </div></div>16<div class="card auction-card mb-3"><div class="card-body"><h2 class="card-title">Sterling Silver &amp; Accoutrements</h2><h3 class="card-subtitle text-muted mb-2">Wednesday, 21 June 2023</h3><a class="btn btn-dark" href="/1745">Auction Details</a></div></div>`;1718const CAT = `<h1 class="page-header">Sterling Silver &amp; Accoutrements <small class="text-muted">Wednesday, 21 June 2023 / 10:00 am start</small></h1><button type="button" class="btn dropdown-toggle"> Page 1 of 6 </button>19<div class="lot-grid row"> <div class="col mb-4"> <div id="0001" class="card h-100" data-ln="0001"> <a href="/1745/catalogue/0001"> <img class="card-img-top" src="/img/loading600.gif" data-src="https://d1v8q4.tandemauctions.com/1745/0001/sq/875763a3.webp" alt="Geo II S/S Sugar Caster"> </a> <div class="card-body"> <p class="card-subtitle mb-2 text-muted"> <a href="/1745/catalogue/0001"> 0001 </a> </p> <p class="card-text"> Geo II S/S Sugar Caster<br /> <small class="estimate">Estimate: $200 - $350</small> </p> </div> <div class="card-footer realised"> Realised: $140 <small>&#x2B; premium</small> </div> </div> </div>20<div class="col mb-4"> <div id="0002" class="card h-100" data-ln="0002"> <a href="/1745/catalogue/0002"><img data-src="https://d1v8q4.tandemauctions.com/1745/0002/sq/x.webp" alt="Rolex Oyster Perpetual wristwatch"></a> <div class="card-body"> <p class="card-text"> Rolex Oyster Perpetual wristwatch<br /> <small class="estimate">Estimate: $3,000 - $5,000</small> </p> </div> </div> </div></div>`;2122describe('dunbar-sloane', () => {23  runFixtureSuite(connector, it, expect);2425  it('fixtures: NZD hammer ("plus premium") results', async () => {26    let sales = 0;27    for (const name of listFixtures('dunbar-sloane')) {28      for (const r of await connector.normalize(loadFixture('dunbar-sloane', name).raw)) {29        if (!('attributes' in r)) continue;30        expect(r.attributes.identifiers.dunbar_sloane_lot).toMatch(/^\d+\/\S+$/);31        expect(r.sourceUrl).toMatch(/^https:\/\/auctions\.dunbarsloane\.co\.nz\/\d+\/catalogue\//);32        if (r.kind === 'sale') {33          sales++;34          expect(r.currency).toBe('NZD');35          expect(r.buyerPremiumIncluded).toBe(false);36          expect(r.auctionHouse).toBe('Dunbar Sloane');37        }38      }39    }40    expect(sales).toBeGreaterThan(10);41  });4243  it('parses NZ dates, the previous-auctions list and a catalogue page', async () => {44    expect(parseNzDate('Thursday, 2 - Wednesday, 15 October 2025')).toBe('2025-10-02T00:00:00.000Z');45    expect(parseNzDate('Wednesday, 21 June 2023 / 10:00 am start')).toBe('2023-06-21T00:00:00.000Z');46    expect(parseNzDate('Friday, 17 - Tuesday, 28 October 2025')).toBe('2025-10-17T00:00:00.000Z');47    const sales = parsePreviousAuctions(INDEX);48    expect(sales.map((s) => s.id)).toEqual(['1858', '1745']);49    expect(sales[0]).toMatchObject({ title: 'Vintage Toy Auction', url: 'https://auctions.dunbarsloane.co.nz/1858/catalogue', date: '2025-10-02T00:00:00.000Z' });50    const p = parseCataloguePage(CAT, sales[1]!, 1)!;51    expect(p.hasMore).toBe(true);52    expect(parseCataloguePage(CAT, sales[1]!, 6)!.hasMore).toBe(false);53    expect(p.sale).toMatchObject({ title: 'Sterling Silver & Accoutrements', date: '2023-06-21T00:00:00.000Z' });54    expect(p.lots[0]).toMatchObject({ lotNo: '0001', title: 'Geo II S/S Sugar Caster', price: 140, currency: 'NZD', premiumIncluded: false, estimateLow: 200, estimateHigh: 350, sold: true, url: 'https://auctions.dunbarsloane.co.nz/1745/catalogue/0001', image: 'https://d1v8q4.tandemauctions.com/1745/0001/sq/875763a3.webp' });55    expect(p.lots[1]).toMatchObject({ lotNo: '0002', price: null, sold: false, estimateLow: 3000, estimateHigh: 5000 });56    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 } });57    expect(out.map((r) => r.kind)).toEqual(['sale', 'auction_lot']);58    expect(attrsOf(out[0]!).categorySlug).toBe('silver');59    expect(attrsOf(out[1]!).categorySlug).toBe('rolex');60    expect(connector.salePageUrl(sales[1]!, 2)).toBe('https://auctions.dunbarsloane.co.nz/1745/catalogue?page=2');61    expect(parseCataloguePage('<html>x</html>', sales[1]!, 1)).toBeNull();62  });63});64