import { describe, it, expect } from 'vitest'; import { multiplier, defensiveMatchups } from '../src/data/type-chart.js'; describe('multiplier — modern (Gen 9)', () => { it('single-type effectiveness', () => { expect(multiplier('fire', ['grass'])).toBe(2); expect(multiplier('fire', ['water'])).toBe(0.5); expect(multiplier('normal', ['ghost'])).toBe(0); expect(multiplier('ghost', ['normal'])).toBe(0); expect(multiplier('dragon', ['fairy'])).toBe(0); }); it('stacks across a dual type', () => { expect(multiplier('water', ['ground', 'rock'])).toBe(4); expect(multiplier('grass', ['water', 'ground'])).toBe(4); expect(multiplier('fighting', ['normal', 'flying'])).toBe(1); // 2 * 0.5 expect(multiplier('ground', ['flying', 'steel'])).toBe(0); // immunity wins }); it('ghost hits steel neutrally from Gen 6 on', () => { expect(multiplier('ghost', ['steel'], 9)).toBe(1); }); }); describe('multiplier — era rules', () => { it('Fairy does not exist before Gen 6 (defender ignored)', () => { expect(multiplier('dragon', ['fairy'], 5)).toBe(1); expect(multiplier('poison', ['fairy'], 5)).toBe(1); // still applies in Gen 6+ expect(multiplier('poison', ['fairy'], 6)).toBe(2); }); it('Steel resisted Ghost and Dark before Gen 6', () => { expect(multiplier('ghost', ['steel'], 5)).toBe(0.5); expect(multiplier('dark', ['steel'], 5)).toBe(0.5); expect(multiplier('ghost', ['steel'], 6)).toBe(1); }); it('Gen 1 quirks', () => { expect(multiplier('ghost', ['psychic'], 1)).toBe(0); // the famous bug expect(multiplier('ghost', ['psychic'], 2)).toBe(2); // fixed in Gen 2 expect(multiplier('bug', ['poison'], 1)).toBe(2); expect(multiplier('poison', ['bug'], 1)).toBe(2); expect(multiplier('bug', ['poison'], 2)).toBe(0.5); // modern value }); }); describe('defensiveMatchups', () => { it('buckets a dual type by multiplier and omits absent attackers', () => { // Sableye (Dark/Ghost) modern: immune to Normal/Fighting/Psychic, weak // only to Fairy (added in Gen 6). const m = defensiveMatchups(['dark', 'ghost'], 9); expect(m['0'].sort()).toEqual(['fighting', 'normal', 'psychic'].sort()); expect(m['2']).toEqual(['fairy']); expect(m['4']).toEqual([]); expect(m['0.5']).toContain('poison'); // Pre-Gen-2 there is no Dark/Steel attacker, so Fairy is absent too const g1 = defensiveMatchups(['ghost'], 1); const flat = Object.values(g1).flat(); expect(flat).not.toContain('dark'); expect(flat).not.toContain('steel'); expect(flat).not.toContain('fairy'); }); });