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.7 KB · 71 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, { parseLotPageDate, parseMillonIndex, parseMillonSalePage, parsePagerMax } 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 = `<input type="number" class="pagerer-page" max="49"><article class="card no-full"> <div class="card-heading"> <div class="location"> <span class="selling-location">Online</span> </div> <div class="time"> <span class="day-of-sale">lun 06 Juil - lun 07 Sep</span> </div> </div> <a class="card-image" href="/catalogue/vente3964-bd-online-3"></a> <div class="card-content"> <h5 class="card-title"><a href="/catalogue/vente3964-bd-online-3" title="BD online #3">BD online #3</a></h5> </div></article>16<article class="card"><h5 class="card-title"><a href="/catalogue/vente4187-horlogerie" title="Horlogerie">Horlogerie</a></h5><span class="selling-location">Paris</span></article>`;1718const SALE = `<h1 class="title-large">BD online #3</h1> <p class="type">Vente en ligne</p> <p class="time"> <span class="day-of-sale">lun 06 Juil - lun 07 Sep</span></p><span class="sale-status">Online</span>19<input type="number" class="pagerer-page" value="1" min="1" max="6">20<article class="card no-full"> <div class="card-heading"> <span class="lot"> Lot 1 </span> </div> <a href="/catalogue/vente3964-bd-online-3/lot1-anonyme-xx" class="card-image"><picture><source srcset="/sites/millon/files/styles/x/public/auction-items/958005-3964-img1.jpg.webp?itok=a 1x"></picture></a> <div class="card-content"> <h5 class="card-title"><a href="/catalogue/vente3964-bd-online-3/lot1-anonyme-xx">ANONYME (XX)</a></h5> <p class="sub-title">Pifou poche n°49, gag de la page 49.</p> <div class="description"> Encre de Chine et crayon bleu sur papier. </div> <div class="footer"> <p class="estimation"> <span class="label">Estimation |&nbsp;</span> 30 € - 50 € </p> <p class="estimation"> <span class="label">Adjugé à |&nbsp;</span> 1 160 € </p> </div></div></article>21<article class="card no-full"> <span class="lot"> Lot 2 </span> <h5 class="card-title"><a href="/catalogue/vente3964-bd-online-3/lot2-uderzo">UDERZO Albert</a></h5> <p class="sub-title">Astérix, planche originale</p> <p class="estimation"> <span class="label">Estimation |&nbsp;</span> 3 000 € - 5 000 € </p></article>`;2223const LOT = `<p class="sale--info"> Vente <a href="/catalogue/vente3964-bd-online-3" target="_blank" hreflang="fr">BD online #3</a> du 6 juillet 2026 </p>`;2425describe('millon', () => {26  runFixtureSuite(connector, it, expect);2728  it('fixtures: EUR results with unknown premium basis', async () => {29    let sales = 0;30    for (const name of listFixtures('millon')) {31      for (const r of await connector.normalize(loadFixture('millon', name).raw)) {32        if (!('attributes' in r)) continue;33        expect(r.attributes.identifiers.millon_lot).toMatch(/^\d+\/\S+$/);34        expect(r.sourceUrl).toMatch(/^https:\/\/www\.millon\.com\/catalogue\/vente\d+/);35        if (r.kind === 'sale') {36          sales++;37          expect(r.currency).toBe('EUR');38          expect(r.buyerPremiumIncluded).toBeNull();39          expect(r.auctionHouse).toBe('Millon');40        }41      }42    }43    expect(sales).toBeGreaterThan(10);44  });4546  it('parses the past-sales index, pager and lot-page date', () => {47    const sales = parseMillonIndex(INDEX);48    expect(sales.map((s) => s.id)).toEqual(['3964', '4187']);49    expect(sales[0]).toMatchObject({ title: 'BD online #3', url: 'https://www.millon.com/catalogue/vente3964-bd-online-3', location: 'Online' });50    expect(parsePagerMax(INDEX)).toBe(49);51    expect(parseLotPageDate(LOT)).toBe('2026-07-06T00:00:00.000Z');52    expect(parseLotPageDate('<p>rien</p>')).toBeNull();53  });5455  it('parses a sale page (French prices, estimates, sold/unsold, 0-based pagination)', async () => {56    const sale = { id: '3964', title: 'BD online #3', url: 'https://www.millon.com/catalogue/vente3964-bd-online-3', date: '2026-07-06T00:00:00.000Z', location: null, extra: {} };57    const p = parseMillonSalePage(SALE, sale, 1)!;58    expect(p.hasMore).toBe(true);59    expect(parseMillonSalePage(SALE, sale, 6)!.hasMore).toBe(false);60    expect(p.lots).toHaveLength(2);61    expect(p.lots[0]).toMatchObject({ lotNo: '1', title: 'ANONYME (XX)', subtitle: 'Pifou poche n°49, gag de la page 49.', price: 1160, currency: 'EUR', premiumIncluded: null, estimateLow: 30, estimateHigh: 50, sold: true, url: 'https://www.millon.com/catalogue/vente3964-bd-online-3/lot1-anonyme-xx', image: 'https://www.millon.com/sites/millon/files/styles/x/public/auction-items/958005-3964-img1.jpg.webp?itok=a' });62    expect(p.lots[1]).toMatchObject({ lotNo: '2', price: null, sold: false, estimateLow: 3000, estimateHigh: 5000 });63    expect(p.sale?.title).toBe('BD online #3');64    const out = await connector.normalize({ url: sale.url, kind: 'sale', engine: 'api', fetchedAt: new Date(), payload: { kind: 'sale_results', url: sale.url, sale, page: 1, totalLots: null, lots: p.lots } });65    expect(out.map((r) => r.kind)).toEqual(['sale', 'auction_lot']);66    expect(attrsOf(out[0]!).categorySlug).toBe('independent_comics');67    expect(connector.salePageUrl(sale, 2)).toBe('https://www.millon.com/catalogue/vente3964-bd-online-3?page=1');68    expect(parseMillonSalePage('<html></html>', sale, 1)).toBeNull();69  });70});71