Capture team chat and expose moderation match archives
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { afterEach, expect, it, vi } from 'vitest';
|
||||
import { flushSync, mount, unmount } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import MatchHistory from '../../src/lib/ui/components/MatchHistory.svelte';
|
||||
import type { MatchHistoryResponse, ServerMatch } from '../../src/lib/match-history.js';
|
||||
import type { UserRole } from '../../src/lib/auth.js';
|
||||
|
||||
const currentPage = writable({
|
||||
url: new URL('http://localhost/matches'),
|
||||
data: { user: { role: 'moderator' as UserRole } }
|
||||
});
|
||||
const navigation = vi.hoisted(() => ({ goto: vi.fn().mockResolvedValue(undefined) }));
|
||||
vi.mock('$app/stores', () => ({
|
||||
page: {
|
||||
subscribe: (run: Parameters<typeof currentPage.subscribe>[0]) => currentPage.subscribe(run)
|
||||
}
|
||||
}));
|
||||
vi.mock('$app/navigation', () => ({ goto: navigation.goto }));
|
||||
vi.mock('../../src/lib/ui/poll.js', () => ({ pollVisible: () => () => {} }));
|
||||
|
||||
let component: ReturnType<typeof mount> | undefined;
|
||||
afterEach(async () => {
|
||||
if (component) await unmount(component);
|
||||
component = undefined;
|
||||
document.body.replaceChildren();
|
||||
navigation.goto.mockClear();
|
||||
});
|
||||
const statuses: ServerMatch['status'][] = [
|
||||
'completed',
|
||||
'interrupted',
|
||||
'end-report-missing',
|
||||
'active'
|
||||
];
|
||||
const report: MatchHistoryResponse = {
|
||||
total: 4,
|
||||
page: 1,
|
||||
limit: 25,
|
||||
maps: [],
|
||||
matches: statuses.map((status, index) => ({
|
||||
id: index + 1,
|
||||
game: 't5',
|
||||
server: 't5_zombie_theater_1',
|
||||
map: 'Kino Der Toten',
|
||||
observedRound: 1,
|
||||
finalRound: status === 'completed' ? 1 : null,
|
||||
status,
|
||||
reporterMatchKey: null,
|
||||
missingReports: null,
|
||||
startedAt: '2026-10-17T10:00:00Z',
|
||||
lastReportedAt: '2026-10-17T10:01:00Z',
|
||||
endedAt: status === 'completed' ? '2026-10-17T10:01:00Z' : null,
|
||||
durationMs: null,
|
||||
startPlayers: 1,
|
||||
uniquePlayers: 1,
|
||||
endPlayers: status === 'completed' ? 1 : null
|
||||
}))
|
||||
};
|
||||
function show(search = '', role: UserRole = 'moderator') {
|
||||
currentPage.set({
|
||||
url: new URL(`http://localhost/matches${search}`),
|
||||
data: { user: { role } }
|
||||
});
|
||||
component = mount(MatchHistory, { target: document.body, props: { initial: report } });
|
||||
flushSync();
|
||||
}
|
||||
|
||||
it('enables the moderation archive through a namespaced URL while preserving filters', async () => {
|
||||
show('?matchGame=t5&matchPage=2');
|
||||
document.querySelector<HTMLInputElement>('input[type="checkbox"]')!.click();
|
||||
await vi.waitFor(() => expect(navigation.goto).toHaveBeenCalledOnce());
|
||||
expect(navigation.goto.mock.calls[0][0]).toBe('/matches?matchGame=t5&matchArchive=1');
|
||||
});
|
||||
|
||||
it('links round-one and unfinished archive rows to retained match detail with return context', () => {
|
||||
show('?matchArchive=1&matchGame=t5');
|
||||
expect(document.querySelector<HTMLInputElement>('input[type="checkbox"]')?.checked).toBe(true);
|
||||
const links = [...document.querySelectorAll<HTMLAnchorElement>('tbody a.match-link')];
|
||||
expect(links).toHaveLength(4);
|
||||
for (const [index, link] of links.entries()) {
|
||||
const url = new URL(link.href);
|
||||
expect(url.pathname).toBe(`/matches/${index + 1}`);
|
||||
expect(url.searchParams.get('matchArchive')).toBe('1');
|
||||
expect(url.searchParams.get('returnTo')).toBe('/matches?matchArchive=1&matchGame=t5');
|
||||
}
|
||||
expect(document.body.textContent).toContain('Highest observed');
|
||||
expect(document.body.textContent).toContain('Last report');
|
||||
});
|
||||
|
||||
it('keeps ordinary history navigation and hides archive controls from viewers', () => {
|
||||
show('', 'viewer');
|
||||
expect(document.querySelector('input[type="checkbox"]')).toBeNull();
|
||||
const links = [...document.querySelectorAll<HTMLAnchorElement>('tbody a.match-link')];
|
||||
expect(links).toHaveLength(1);
|
||||
expect(new URL(links[0].href).searchParams.has('matchArchive')).toBe(false);
|
||||
});
|
||||
Reference in New Issue
Block a user