import { describe, expect, it } from 'vitest'; import metaJson from './meta.json' with { type: 'json' }; import { loadFixture, runFixtureSuite } from '@rareindex/connectors/testing'; import { missingRequirements } from '@rareindex/connectors'; import { localMeta } from '../_lib/local-meta.js'; import createConnector, { parseSearchResponse, priceOf, resolveTaxonomyPath, searchUrl } from './index.js'; const meta = localMeta(metaJson); const connector = createConnector(meta); describe('etsy (gated)', () => { runFixtureSuite(connector, it, expect); it('is gated on ETSY_API_KEY', () => { expect(meta.requires).toEqual(['ETSY_API_KEY']); expect(missingRequirements(meta, {})).toEqual(['ETSY_API_KEY']); expect(missingRequirements(meta, { ETSY_API_KEY: 'k' })).toEqual([]); }); it('builds findAllListingsActive URLs and resolves taxonomy paths', () => { const url = new URL(searchUrl({ keywords: 'vintage fountain pen', categorySlug: 'pens', minPrice: 50 }, { limit: 100, offset: 200, sortOn: 'created', sortOrder: 'desc' }, 1234)); expect(url.origin + url.pathname).toBe('https://openapi.etsy.com/v3/application/listings/active'); expect(url.searchParams.get('keywords')).toBe('vintage fountain pen'); expect(url.searchParams.get('taxonomy_id')).toBe('1234'); expect(url.searchParams.get('min_price')).toBe('50'); expect(url.searchParams.get('limit')).toBe('100'); expect(url.searchParams.get('offset')).toBe('200'); const tree = { count: 1, results: [{ id: 1, name: 'Toys & Games', children: [{ id: 10, name: 'Games & Puzzles', children: [{ id: 100, name: 'Card Games', children: [] }] }] }] }; expect(resolveTaxonomyPath(tree, ['Toys & Games', 'Games & Puzzles', 'Card Games'])).toBe(100); expect(resolveTaxonomyPath(tree, ['toys & games'])).toBe(1); expect(resolveTaxonomyPath(tree, ['Nope'])).toBeNull(); }); it('parses Money prices (amount/divisor) and normalises native-currency listings', async () => { expect(priceOf({ amount: 12999, divisor: 100, currency_code: 'USD' })).toBe(129.99); expect(priceOf({ amount: 0, divisor: 100, currency_code: 'USD' })).toBeNull(); const fx = loadFixture('etsy', 'docs-listings-active'); const payload = fx.raw.payload as { listings: unknown[] }; const parsed = parseSearchResponse({ count: 3, results: payload.listings }); expect(parsed?.listings.length).toBe(payload.listings.length); expect(parseSearchResponse({ error: 'Invalid API key' })).toBeNull(); const out = await connector.normalize(fx.raw); // one made_to_order listing is skipped expect(out.length).toBe(payload.listings.length - 1); const first = out[0]!; if (first.kind !== 'listing') throw new Error('expected listing'); expect(first.currency).toBe('USD'); expect(first.price).toBe(129.99); expect(first.attributes.identifiers.etsy_listing_id).toMatch(/^\d+$/); expect(first.imageUrls[0]).toMatch(/etsystatic/); expect(first.seller).toBeTruthy(); expect(first.listedAt?.getUTCFullYear()).toBe(2026); const gbp = out.find((r) => r.kind === 'listing' && r.currency === 'GBP'); expect(gbp).toBeTruthy(); }); });