31 tests in test/, run in a plain Node env with a tiny localStorage shim (no jsdom — its Node-22.4 transitive-dep breakage isn't worth working around for logic tests): - type-chart: modern effectiveness, dual-type stacking, and the era rules (no Fairy pre-6, Steel resists Ghost/Dark pre-6, the Gen 1 Ghost→Psychic and Bug↔Poison quirks). - damage-calc: nature multipliers, the Gen 3+ stat formula (Garchomp reference values), STAB/effectiveness/immunity, pre/post-Gen-6 crit. - savedex: a synthetic Gen 1 SRAM (bitfields + checksum) round-trips; checksum-mismatch and size guards. - selection: normalizeSelection v2→v3 migration, per-game isolation, global favourite, stats vs statsNational. - pokedex-resolver: prettify, dex resolution against a mini snapshot. `npm test` wired into CI after format:check. Also: docker-compose host port 8080 → 4753 (README updated). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Ve7HLspzeG2xDPtJQ8vmu
66 lines
2.6 KiB
JavaScript
66 lines
2.6 KiB
JavaScript
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');
|
|
});
|
|
});
|