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%
3.1 KB · 66 lines typescript
Raw Blame History
1import { describe, expect, it } from 'vitest';2import { parsePrice, formatMoney } from './money.js';3import { parseSourceDate } from './dates.js';4import { median, trimmedMean, ewMean, maxDrawdown } from './stats.js';5import { normalizeForMatch, jaccard, slugify } from './text.js';67describe('parsePrice', () => {8  it('parses US formats', () => {9    expect(parsePrice('$1,234.56')).toMatchObject({ amount: 1234.56, currency: 'USD' });10    expect(parsePrice('US $2,500.00')).toMatchObject({ amount: 2500, currency: 'USD' });11    expect(parsePrice('Sold for $420,000')).toMatchObject({ amount: 420000, currency: 'USD' });12  });13  it('parses EU formats', () => {14    expect(parsePrice('1.234,56 €')).toMatchObject({ amount: 1234.56, currency: 'EUR' });15    expect(parsePrice('EUR 12,50')).toMatchObject({ amount: 12.5, currency: 'EUR' });16    expect(parsePrice('2.500 €')).toMatchObject({ amount: 2500, currency: 'EUR' });17  });18  it('parses GBP/JPY/CAD', () => {19    expect(parsePrice('£12.99')).toMatchObject({ amount: 12.99, currency: 'GBP' });20    expect(parsePrice('¥15,000')).toMatchObject({ amount: 15000, currency: 'JPY' });21    expect(parsePrice('CA$1 200,00')).toMatchObject({ amount: 1200, currency: 'CAD' });22    expect(parsePrice('C$99')).toMatchObject({ amount: 99, currency: 'CAD' });23  });24  it('does not invent a currency', () => {25    expect(parsePrice('1500')).toMatchObject({ amount: 1500, currency: null });26    expect(parsePrice('1500', 'GBP')).toMatchObject({ amount: 1500, currency: 'GBP' });27    expect(parsePrice('no price')).toBeNull();28  });29  it('formats', () => {30    expect(formatMoney(8450)).toBe('$8,450');31    expect(formatMoney(12.5)).toBe('$12.50');32    expect(formatMoney(null)).toBe('—');33  });34});3536describe('parseSourceDate', () => {37  it('handles common formats', () => {38    expect(parseSourceDate('2024-03-05')?.toISOString()).toBe('2024-03-05T00:00:00.000Z');39    expect(parseSourceDate('Mar 5, 2024')?.toISOString()).toBe('2024-03-05T00:00:00.000Z');40    expect(parseSourceDate('5 March 2024')?.toISOString()).toBe('2024-03-05T00:00:00.000Z');41    expect(parseSourceDate('03/05/2024')?.toISOString()).toBe('2024-03-05T00:00:00.000Z');42    expect(parseSourceDate('05.03.2024', { dayFirst: true })?.toISOString()).toBe('2024-03-05T00:00:00.000Z');43    expect(parseSourceDate('garbage')).toBeNull();44  });45});4647describe('stats', () => {48  it('median/trimmed/ew/drawdown', () => {49    expect(median([3, 1, 2])).toBe(2);50    expect(median([4, 1, 2, 3])).toBe(2.5);51    expect(trimmedMean([1, 2, 3, 4, 100], 0.2)).toBe(3);52    expect(ewMean([{ value: 100, ageDays: 0 }, { value: 50, ageDays: 1000 }])).toBeGreaterThan(90);53    expect(maxDrawdown([100, 120, 60, 90])).toBeCloseTo(0.5);54  });55});5657describe('text', () => {58  it('normalises and compares', () => {59    const a = '1999 Pokemon Base Charizard 1st PSA 10';60    const b = 'Pokémon 1999 First Edition Charizard Holo PSA GEM MT 10';61    expect(normalizeForMatch(a)).toBe('1999 pokemon base charizard 1st psa 10');62    expect(jaccard(a, b)).toBeGreaterThan(0.3);63    expect(slugify('1999 Base Set Charizard #4/102 — 1st Edition')).toBe('1999-base-set-charizard-4-102-1st-edition');64  });65});66