106 lines
3.6 KiB
TypeScript
106 lines
3.6 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { hostRouteContextResolver, playerRouteContextResolver, resolveSessionCode } from './session-route-context';
|
|
|
|
type RouteLike = {
|
|
paramMap: { get: (key: string) => string | null };
|
|
queryParamMap: { get: (key: string) => string | null };
|
|
};
|
|
|
|
function route(params: Record<string, string | null>, query: Record<string, string | null> = {}): RouteLike {
|
|
return {
|
|
paramMap: { get: (key: string) => params[key] ?? null },
|
|
queryParamMap: { get: (key: string) => query[key] ?? null },
|
|
};
|
|
}
|
|
|
|
function storageMock(initial: Record<string, string> = {}): Storage {
|
|
const data = new Map<string, string>(Object.entries(initial));
|
|
|
|
return {
|
|
getItem: vi.fn((key: string) => data.get(key) ?? null),
|
|
setItem: vi.fn((key: string, value: string) => {
|
|
data.set(key, value);
|
|
}),
|
|
removeItem: vi.fn((key: string) => {
|
|
data.delete(key);
|
|
}),
|
|
clear: vi.fn(() => {
|
|
data.clear();
|
|
}),
|
|
key: vi.fn((index: number) => Array.from(data.keys())[index] ?? null),
|
|
get length() {
|
|
return data.size;
|
|
},
|
|
} as unknown as Storage;
|
|
}
|
|
|
|
function setWindow(localStorage: Storage, sessionStorage: Storage): void {
|
|
vi.stubGlobal('window', { localStorage, sessionStorage });
|
|
}
|
|
|
|
describe('session route context', () => {
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('resolves player code from persisted session context when route has no code', () => {
|
|
setWindow(
|
|
storageMock({ 'wpp.session-context': JSON.stringify({ sessionCode: 'ab12', playerId: 7, token: 'tok' }) }),
|
|
storageMock()
|
|
);
|
|
|
|
expect(resolveSessionCode(route({}, {}) as never, 'player')).toBe('AB12');
|
|
});
|
|
|
|
it('resolves host code from session query string', () => {
|
|
setWindow(storageMock(), storageMock());
|
|
|
|
expect(resolveSessionCode(route({}, { session: 'qwe9' }) as never, 'host')).toBe('QWE9');
|
|
});
|
|
|
|
it('player resolver emits player id/token when context matches route session', () => {
|
|
setWindow(
|
|
storageMock({ 'wpp.session-context': JSON.stringify({ sessionCode: 'AB12', playerId: 5, token: 'tok-5' }) }),
|
|
storageMock()
|
|
);
|
|
|
|
expect(playerRouteContextResolver(route({ context: 'ab12' }) as never, {} as never)).toEqual({
|
|
sessionCode: 'AB12',
|
|
playerId: 5,
|
|
token: 'tok-5',
|
|
locale: 'en',
|
|
});
|
|
});
|
|
|
|
it('host resolver stores normalized host session code for refresh bootstrap', () => {
|
|
const sessionStorage = storageMock();
|
|
setWindow(storageMock(), sessionStorage);
|
|
|
|
expect(hostRouteContextResolver(route({ context: 'ab12' }) as never, {} as never)).toEqual({
|
|
sessionCode: 'AB12',
|
|
playerId: null,
|
|
token: null,
|
|
locale: 'en',
|
|
});
|
|
expect(sessionStorage.setItem).toHaveBeenCalledWith('wpp.host-session-code', 'AB12');
|
|
});
|
|
|
|
it('resolvers normalize and expose locale from lang query param', () => {
|
|
setWindow(storageMock(), storageMock());
|
|
|
|
expect(hostRouteContextResolver(route({}, { lang: 'da-DK' }) as never, {} as never).locale).toBe('da');
|
|
expect(playerRouteContextResolver(route({}, { lang: 'EN' }) as never, {} as never).locale).toBe('en');
|
|
});
|
|
|
|
it('does not reset persisted preferred locale when lang query param is absent', () => {
|
|
const localStorage = storageMock({ 'wpp.locale': 'da' });
|
|
setWindow(localStorage, storageMock());
|
|
|
|
expect(hostRouteContextResolver(route({}, { lang: 'da' }) as never, {} as never).locale).toBe('da');
|
|
expect(hostRouteContextResolver(route({}, {}) as never, {} as never).locale).toBe('da');
|
|
expect(localStorage.setItem).toHaveBeenCalledTimes(1);
|
|
expect(localStorage.setItem).toHaveBeenCalledWith('wpp.locale', 'da');
|
|
});
|
|
});
|