import { readFileSync } from 'node:fs'; import path from 'node:path'; import { describe, expect, it } from 'vitest'; import { getConnectorMeta } from '@rareindex/connectors'; import { fixtureDir, loadFixture, runFixtureSuite } from '@rareindex/connectors/testing'; import createConnector, { parseCategoryPage, parseProductPage } from './index.js'; const connector = createConnector(getConnectorMeta('chronext')); const sample = (name: string) => readFileSync(path.join(fixtureDir('chronext'), name), 'utf8'); describe('chronext', () => { runFixtureSuite(connector, it, expect); it('parses product tiles (brand, model, reference, price text, condition, image, V-id) and the next-page offset link', async () => { const p = parseCategoryPage(sample('category-tiles.sample.html'), 'https://www.chronext.com/rolex/datejust-pre-owned', '/rolex/datejust-pre-owned', 1); expect(p.tiles).toHaveLength(3); const t = p.tiles[0]!; expect(t.id).toBe('V01800358'); expect(t.brand).toBe('Rolex'); expect(t.model).toBe('Lady-Datejust'); expect(t.reference).toBe('79160'); expect(t.priceText).toBe('USD 4,380'); expect(t.condition).toBe('Like New'); expect(t.href).toBe('https://www.chronext.com/rolex/lady-datejust/79160/V01800358'); expect(t.image).toMatch(/^https:\/\/chronexttime\.imgix\.net\//); expect(p.nextUrl).toContain('offset%5D=24'); expect(p.title).toBe('Rolex Datejust Pre-Owned Watches'); const out = await connector.normalize({ url: p.url, externalId: null, kind: 'listing', engine: 'api', fetchedAt: new Date('2026-09-08T00:00:00Z'), payload: p }); const l = out[0]; if (l?.kind !== 'listing') throw new Error('expected listing'); expect(l.price).toBe(4380); expect(l.currency).toBe('USD'); expect(l.attributes.categorySlug).toBe('rolex'); expect(l.attributes.reference).toBe('79160'); expect(l.attributes.identifiers).toEqual({ chronext_id: 'V01800358', reference: '79160' }); expect(l.condition.conditionRaw).toBe('Excellent'); expect(l.seller).toBe('CHRONEXT'); }); it('parses the product page JSON-LD (sku, mpn, brand, USD offer)', async () => { const p = parseProductPage(sample('product-jsonld.sample.html'), 'https://www.chronext.com/rolex/datejust/116234/V66464'); if (p?.kind !== 'product_page') throw new Error('expected product page'); expect(p.product.id).toBe('V66464'); expect(p.product.sku).toBe('A20325'); expect(p.product.mpn).toBe('116234'); expect(p.product.brand).toBe('Rolex'); expect(p.product.price).toBe(8850); expect(p.product.currency).toBe('USD'); expect(p.product.condition).toBe('RefurbishedCondition'); const out = await connector.normalize({ url: p.url, externalId: null, kind: 'listing', engine: 'api', fetchedAt: new Date('2026-09-08T00:00:00Z'), payload: p }); const l = out[0]; if (l?.kind !== 'listing') throw new Error('expected listing'); expect(l.attributes.model).toBe('Datejust 36'); expect(l.attributes.identifiers.chronext_sku).toBe('A20325'); expect(l.attributes.reference).toBe('116234'); expect(l.availability).toBe('available'); }); it('category fixture normalises to Rolex listings with references; EUR price text is kept in EUR', async () => { const fx = loadFixture('chronext', 'rolex-datejust-p1'); const out = await connector.normalize(fx.raw); expect(out.length).toBeGreaterThan(5); expect(out.every((r) => r.kind === 'listing' && r.attributes.categorySlug === 'rolex' && r.currency === 'USD')).toBe(true); const eur = await connector.normalize({ ...fx.raw, payload: { ...(fx.raw.payload as object), tiles: [{ id: 'V1', href: 'https://www.chronext.com/omega/speedmaster/311.30.42.30.01.005/V1', brand: 'Omega', model: 'Speedmaster', reference: '311.30.42.30.01.005', priceText: 'EUR 5.850', condition: 'Very Good', image: null }] } }); const l = eur[0]; if (l?.kind !== 'listing') throw new Error('expected listing'); expect(l.currency).toBe('EUR'); expect(l.price).toBe(5850); expect(l.attributes.categorySlug).toBe('omega'); expect(l.condition.conditionRaw).toBe('Very good'); }); });