import { describe, expect, it } from 'vitest'; import { readFileSync } from 'node:fs'; import path from 'node:path'; import meta from './meta.json' with { type: 'json' }; import { localMeta } from '../_lib/local-meta.js'; import { fixtureDir, listFixtures, loadFixture, runFixtureSuite } from '@rareindex/connectors/testing'; import createConnector, { LANGUAGES, apiUrl, parseCardsets, parseProductName } from './index.js'; const connector = createConnector(localMeta(meta)); describe('hareruya', () => { runFixtureSuite(connector, it, expect); it('parses product names and builds the JSON endpoint URL the page uses', () => { expect(parseProductName('《Black Lotus》[2ED]')).toEqual({ name: 'Black Lotus', setCode: '2ED', number: null }); expect(parseProductName('《Black Lotus》[2ED] 茶R')).toEqual({ name: 'Black Lotus', setCode: '2ED', number: null }); expect(parseProductName('(001)《Ugin, Eye of the Storms》[TDM]')).toEqual({ name: 'Ugin, Eye of the Storms', setCode: 'TDM', number: '001' }); expect(parseProductName(null)).toEqual({ name: null, setCode: null, number: null }); expect(apiUrl('429', 2, 60)).toBe('https://www.hareruyamtg.com/en/products/search/unisearch_api?fq.category_id=1&fq.cardset=429&fq.price=1%7E%2A&rows=60&page=2'); expect(LANGUAGES['1']).toBe('Japanese'); expect(LANGUAGES['2']).toBe('English'); }); it('reads the card-set options from the saved public search form', () => { const sets = parseCardsets(readFileSync(path.join(fixtureDir('hareruya'), 'search-form.html'), 'utf8')); expect(sets.length).toBeGreaterThan(100); expect(sets.every((s) => /^\d+$/.test(s.id))).toBe(true); expect(sets[0]!.name.length).toBeGreaterThan(2); }); it('emits one JPY listing per offer with language, foil variant and set', async () => { for (const name of listFixtures('hareruya')) { const fx = loadFixture('hareruya', name); const out = await connector.normalize(fx.raw); const docs = (fx.raw.payload as { docs: unknown[] }).docs; if (!docs.length) { // empty result page (a set announced but not yet stocked) → no records, no crash expect(out).toEqual([]); continue; } expect(out.length).toBeGreaterThan(0); const ids = new Set(); for (const r of out) { if (r.kind !== 'listing') continue; expect(r.currency).toBe('JPY'); expect(r.price).toBeGreaterThan(0); expect(Number.isInteger(r.price)).toBe(true); expect(r.seller).toBe('Hareruya'); expect(r.attributes.set).toBe((fx.raw.payload as { cardset: { name: string } }).cardset.name); expect(r.sourceUrl).toMatch(/^https:\/\/www\.hareruyamtg\.com\/en\/products\/detail\/\d+\?lang=[A-Z]{2}/); expect(r.condition.condition).toBeNull(); // condition codes are never guessed expect(ids.has(r.externalId!)).toBe(false); ids.add(r.externalId!); if (r.attributes.variant) expect(r.attributes.variant).toBe('Foil'); } } }); });