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
89 lines
3.0 KiB
JavaScript
89 lines
3.0 KiB
JavaScript
import { describe, it, expect, beforeEach } from 'vitest';
|
|
import {
|
|
selection,
|
|
normalizeSelection,
|
|
entry,
|
|
gameEntry,
|
|
toggle,
|
|
stats,
|
|
statsNational,
|
|
importFlags,
|
|
} from '../src/store/selection.js';
|
|
|
|
beforeEach(() => {
|
|
localStorage.clear();
|
|
selection.replace({ version: 3, pokemon: {}, games: {} });
|
|
});
|
|
|
|
describe('normalizeSelection', () => {
|
|
it('migrates a flat v2 store to per-game', () => {
|
|
const v2 = {
|
|
version: 2,
|
|
pokemon: {
|
|
1: { seen: true, caught: true, favorite: true, note: '' },
|
|
4: { seen: true, caught: false },
|
|
150: { seen: true, caught: true, favorite: false, note: 'box 1' },
|
|
25: { favorite: true }, // favourite only, never seen
|
|
},
|
|
};
|
|
const out = normalizeSelection(v2);
|
|
expect(out.version).toBe(3);
|
|
// favourite / note lift to the global record
|
|
expect(out.pokemon['1']).toMatchObject({ favorite: true });
|
|
expect(out.pokemon['150']).toMatchObject({ note: 'box 1' });
|
|
expect(out.pokemon['25']).toMatchObject({ favorite: true });
|
|
expect(out.pokemon['4']).toBeUndefined(); // nothing global to keep
|
|
// seen / caught drop into the 'all' bucket, not a real game
|
|
expect(out.games.all['1']).toMatchObject({ seen: true, caught: true });
|
|
expect(out.games.all['4']).toMatchObject({ seen: true, caught: false });
|
|
expect(out.games.all['25']).toBeUndefined();
|
|
});
|
|
|
|
it('passes a v3 store through untouched', () => {
|
|
const v3 = { version: 3, pokemon: { 7: { favorite: true } }, games: { crystal: {} } };
|
|
expect(normalizeSelection(v3)).toEqual(v3);
|
|
});
|
|
|
|
it('handles junk input', () => {
|
|
expect(normalizeSelection(null)).toEqual({ version: 3, pokemon: {}, games: {} });
|
|
});
|
|
});
|
|
|
|
describe('per-game seen/caught', () => {
|
|
it('a catch in one game does not appear in another', () => {
|
|
toggle(3, 'caught', 'crystal');
|
|
expect(entry(3, 'crystal').caught).toBe(true);
|
|
expect(entry(3, 'red-blue').caught).toBe(false);
|
|
// catching implies seen
|
|
expect(gameEntry(3, 'crystal').seen).toBe(true);
|
|
});
|
|
|
|
it("'all' reads as the union across games", () => {
|
|
toggle(1, 'caught', 'crystal');
|
|
toggle(4, 'caught', 'red-blue');
|
|
expect(gameEntry(1, 'all').caught).toBe(true);
|
|
expect(gameEntry(4, 'all').caught).toBe(true);
|
|
expect(gameEntry(7, 'all').caught).toBe(false);
|
|
});
|
|
|
|
it('favorite is global, not per game', () => {
|
|
toggle(25, 'favorite', 'crystal');
|
|
expect(entry(25, 'red-blue').favorite).toBe(true);
|
|
expect(entry(25, 'emerald').favorite).toBe(true);
|
|
});
|
|
|
|
it('stats counts one game; statsNational counts the union', () => {
|
|
importFlags({ seen: [1, 2, 3], caught: [1, 2], game: 'crystal' });
|
|
importFlags({ seen: [4], caught: [4], game: 'red-blue' });
|
|
|
|
const cr = stats([1, 2, 3, 4], 'crystal');
|
|
expect(cr).toEqual({ seen: 3, caught: 2, total: 4 });
|
|
|
|
const rb = stats([1, 2, 3, 4], 'red-blue');
|
|
expect(rb).toEqual({ seen: 1, caught: 1, total: 4 });
|
|
|
|
const nat = statsNational([1, 2, 3, 4]);
|
|
expect(nat).toEqual({ seen: 4, caught: 3, total: 4 });
|
|
});
|
|
});
|