TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { describe, expect, it } from 'vitest';2import { getConnectorMeta } from '@rareindex/connectors';3import { loadFixture, runFixtureSuite } from '@rareindex/connectors/testing';4import createConnector from './index.js';56const connector = createConnector(getConnectorMeta('scryfall'));78describe('scryfall', () => {9 runFixtureSuite(connector, it, expect);1011 it('maps Black Lotus (Alpha) to a catalog item + EUR observation', async () => {12 const out = await connector.normalize(loadFixture('scryfall', 'black-lotus-alpha').raw);13 const cat = out.find((r) => r.kind === 'catalog_item');14 const obs = out.filter((r) => r.kind === 'price_observation');15 expect(cat && 'attributes' in cat && cat.attributes).toMatchObject({ categorySlug: 'magic_the_gathering', set: 'Limited Edition Alpha', setCode: 'LEA', number: '232', year: 1993, rarity: 'rare', language: 'en', variant: null });16 expect(cat && 'attributes' in cat ? cat.attributes.identifiers : {}).toMatchObject({ scryfall_id: 'b0faa7f2-b547-42c4-a810-839da50dadfe', tcgplayer_id: '1042', cardmarket_id: '5465' });17 expect(cat && 'rawTitle' in cat ? cat.rawTitle : '').toBe('Black Lotus · Limited Edition Alpha #232 (1993)');18 expect(obs).toHaveLength(1);19 expect(obs[0]).toMatchObject({ currency: 'EUR', priceKind: 'market' });20 if (obs[0]?.kind === 'price_observation') {21 expect(obs[0].price).toBeGreaterThan(1000);22 expect(obs[0].observationDate.toISOString()).toBe('2026-09-06T21:05:43.673Z');23 }24 });2526 it('splits foil / nonfoil finishes into distinct variants with matching prices', async () => {27 const out = await connector.normalize(loadFixture('scryfall', 'secret-lair-foil-nonfoil').raw);28 const cats = out.filter((r) => r.kind === 'catalog_item');29 expect(cats.map((c) => ('attributes' in c ? c.attributes.variant : undefined)).sort()).toEqual([null, 'Foil'].sort());30 const obs = out.filter((r) => r.kind === 'price_observation');31 const foilUsd = obs.find((o) => 'attributes' in o && o.attributes.variant === 'Foil' && 'currency' in o && o.currency === 'USD');32 const nonfoilUsd = obs.find((o) => 'attributes' in o && o.attributes.variant === null && 'currency' in o && o.currency === 'USD');33 expect(foilUsd && nonfoilUsd).toBeTruthy();34 for (const o of obs) expect(o.kind === 'price_observation' && o.attributes.identifiers.finish).toMatch(/foil|nonfoil/);35 });3637 it('labels showcase treatments and foil-only printings', async () => {38 const out = await connector.normalize(loadFixture('scryfall', 'neo-showcase-foil-only').raw);39 const cats = out.filter((r) => r.kind === 'catalog_item');40 expect(cats).toHaveLength(1);41 expect(cats[0] && 'attributes' in cats[0] ? cats[0].attributes.variant : '').toMatch(/Showcase.*Foil/);42 expect(out.filter((r) => r.kind === 'price_observation').every((o) => 'attributes' in o && /Foil/.test(o.attributes.variant ?? ''))).toBe(true);43 });4445 it('uses card face images for double-faced cards', async () => {46 const out = await connector.normalize(loadFixture('scryfall', 'clb-double-faced').raw);47 const cat = out.find((r) => r.kind === 'catalog_item');48 expect(cat && 'imageUrls' in cat ? cat.imageUrls.length : 0).toBeGreaterThanOrEqual(1);49 });5051 it('recognises scryfall card URLs for lookup', () => {52 expect(connector.urlPatterns?.some((re) => re.test('https://scryfall.com/card/lea/232/black-lotus'))).toBe(true);53 expect(connector.urlPatterns?.some((re) => re.test('https://scryfall.com/sets/lea'))).toBe(false);54 });55});56