mirror of
https://github.com/jcreek/LivingDexTracker.git
synced 2026-09-14 17:42:17 +00:00
test(pokedex): cover shared dex loading and preview badges
Adds loadSharedPokedex unit tests and a share preview case for every badge, restoring the branch coverage gate. Gives the offline sync BDD poll 30s so a full Living Dex snapshot can finish caching on CI.
This commit is contained in:
@@ -67,16 +67,19 @@ When('I go offline and reload the sign-in page', async ({ page }) => {
|
||||
|
||||
Given('my offline copy is synchronized', async ({ page, state }) => {
|
||||
await waitForServiceWorker(page);
|
||||
// A full Living Dex snapshot plus its artwork can take longer than the default poll window on CI.
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(async (userId) => {
|
||||
const meta = await (
|
||||
await caches.open('livingdex-offline-meta-v1')
|
||||
).match('/__offline/current');
|
||||
if (!meta) return false;
|
||||
const value = await meta.json();
|
||||
return value.userId === userId;
|
||||
}, state.userId)
|
||||
.poll(
|
||||
() =>
|
||||
page.evaluate(async (userId) => {
|
||||
const meta = await (
|
||||
await caches.open('livingdex-offline-meta-v1')
|
||||
).match('/__offline/current');
|
||||
if (!meta) return false;
|
||||
const value = await meta.json();
|
||||
return value.userId === userId;
|
||||
}, state.userId),
|
||||
{ timeout: 30_000 }
|
||||
)
|
||||
.toBe(true);
|
||||
const serializedSnapshot = await page.evaluate(async () => {
|
||||
|
||||
@@ -42,6 +42,22 @@ describe('share preview rendering', () => {
|
||||
expect(svg).toContain('42%');
|
||||
});
|
||||
|
||||
it('lists every enabled dex badge and omits an empty description', () => {
|
||||
const svg = buildSharePreviewSvg({
|
||||
...shared,
|
||||
description: '',
|
||||
isShinyDex: true,
|
||||
isOriginDex: true,
|
||||
isFormDex: true,
|
||||
gameScope: 'Scarlet'
|
||||
});
|
||||
for (const badge of ['Living', 'Shiny', 'Origin', 'Form', 'Scarlet']) {
|
||||
expect(svg).toContain(`class="badge">${badge}</text>`);
|
||||
}
|
||||
expect(svg).not.toContain('All Games');
|
||||
expect(svg).not.toContain('class="description"');
|
||||
});
|
||||
|
||||
it('renders a valid 1200 by 630 PNG', async () => {
|
||||
const png = await renderSharePreview(shared);
|
||||
const metadata = await sharp(png).metadata();
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { calculateSharedProgress, isShareToken } from '$lib/services/SharedPokedexService';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||
import type { PokedexEntry } from '$lib/models/PokedexEntry';
|
||||
import type { SharedPokedexRpcData } from '$lib/models/SharedPokedex';
|
||||
|
||||
const { findAllCombinedData } = vi.hoisted(() => ({ findAllCombinedData: vi.fn() }));
|
||||
vi.mock('$lib/repositories/CombinedDataRepository', () => ({
|
||||
default: vi.fn().mockImplementation(() => ({ findAllCombinedData }))
|
||||
}));
|
||||
|
||||
import {
|
||||
calculateSharedProgress,
|
||||
isShareToken,
|
||||
loadSharedPokedex
|
||||
} from '$lib/services/SharedPokedexService';
|
||||
|
||||
const TOKEN = '123e4567-e89b-42d3-a456-426614174000';
|
||||
|
||||
function supabaseWithRpc(result: { data: unknown; error: unknown }) {
|
||||
const rpc = vi.fn().mockResolvedValue(result);
|
||||
return { supabase: { rpc } as unknown as SupabaseClient, rpc };
|
||||
}
|
||||
|
||||
describe('shared Pokédex helpers', () => {
|
||||
it('accepts UUID capability tokens and rejects malformed route values', () => {
|
||||
@@ -32,3 +52,75 @@ describe('shared Pokédex helpers', () => {
|
||||
expect(calculateSharedProgress([], 0)).toEqual({ caught: 0, completionPercentage: 0 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadSharedPokedex', () => {
|
||||
beforeEach(() => {
|
||||
findAllCombinedData.mockReset();
|
||||
});
|
||||
|
||||
it('rejects malformed tokens without querying', async () => {
|
||||
const { supabase, rpc } = supabaseWithRpc({ data: null, error: null });
|
||||
expect(await loadSharedPokedex(supabase, 'not-a-token')).toBeNull();
|
||||
expect(rpc).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns null when the token matches nothing or the RPC fails', async () => {
|
||||
expect(
|
||||
await loadSharedPokedex(supabaseWithRpc({ data: null, error: null }).supabase, TOKEN)
|
||||
).toBeNull();
|
||||
expect(
|
||||
await loadSharedPokedex(
|
||||
supabaseWithRpc({ data: null, error: { message: 'boom' } }).supabase,
|
||||
TOKEN
|
||||
)
|
||||
).toBeNull();
|
||||
expect(findAllCombinedData).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('maps shared catch statuses onto the scoped dex entries', async () => {
|
||||
const rpcData: SharedPokedexRpcData = {
|
||||
name: 'Kanto',
|
||||
description: 'Gen 1',
|
||||
isLivingDex: true,
|
||||
isShinyDex: false,
|
||||
isOriginDex: false,
|
||||
isFormDex: true,
|
||||
gameScope: null,
|
||||
dexScopes: ['kanto'],
|
||||
catchStatuses: [
|
||||
{ pokemonId: '1', caught: true, haveToEvolve: false, inHome: true, hasGigantamaxed: false },
|
||||
{
|
||||
pokemonId: '99',
|
||||
caught: true,
|
||||
haveToEvolve: false,
|
||||
inHome: false,
|
||||
hasGigantamaxed: false
|
||||
}
|
||||
]
|
||||
};
|
||||
const entries = ['1', '2', '3', '4'].map((id) => ({
|
||||
pokedexEntry: { _id: id } as PokedexEntry,
|
||||
catchRecord: null
|
||||
}));
|
||||
findAllCombinedData.mockResolvedValue(entries);
|
||||
const { supabase, rpc } = supabaseWithRpc({ data: rpcData, error: null });
|
||||
|
||||
const shared = await loadSharedPokedex(supabase, TOKEN);
|
||||
|
||||
expect(rpc).toHaveBeenCalledWith('get_shared_pokedex', { p_share_token: TOKEN });
|
||||
expect(findAllCombinedData).toHaveBeenCalledWith('', true, '', '', ['kanto']);
|
||||
expect(shared?.combinedData.map((entry) => entry.catchRecord?.pokemonId ?? null)).toEqual([
|
||||
'1',
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]);
|
||||
// Statuses for Pokémon outside the dex scope must not inflate progress.
|
||||
expect(shared).toMatchObject({
|
||||
name: 'Kanto',
|
||||
total: 4,
|
||||
caught: 1,
|
||||
completionPercentage: 25
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user