import { expect, it, vi } from 'vitest'; import { render } from 'svelte/server'; import { writable } from 'svelte/store'; import { get } from 'svelte/store'; import type { ComponentProps } from 'svelte'; import type { GameTelemetry } from '../../src/lib/telemetry.js'; import ServerPage from '../../src/routes/servers/[name]/+page.svelte'; import PlayerScoreboard from '../../src/lib/ui/components/PlayerScoreboard.svelte'; import ConfigInspector from '../../src/lib/ui/components/ConfigInspector.svelte'; import { uiNow } from '../../src/lib/ui/stores/servers.js'; import { gameEvent } from '../helpers/telemetry.js'; import { page } from '$app/stores'; vi.mock('$app/state', () => ({ page: { url: new URL('http://localhost/servers/t5_zombie_theater_1') } })); vi.mock('$app/stores', () => ({ page: writable({ params: { name: 't5_zombie_theater_1' }, url: new URL('http://localhost/servers/t5_zombie_theater_1') }) })); vi.mock('$app/navigation', () => ({ beforeNavigate: vi.fn(), afterNavigate: vi.fn(), goto: vi.fn() })); const report = (current = true): GameTelemetry => { const receivedAt = new Date(get(uiNow) - (current ? 10_000 : 300_000)).toISOString(); const joined = new Date(Date.parse(receivedAt) - 60_000).toISOString(); return { ...gameEvent('heartbeat', 4, 1), receivedAt, timing: { matchId: 1, players: { '12345678901234560': { serverJoinedAt: joined, matchJoinedAt: joined, roundStarted: 1 } } } }; }; it('keeps scoreboard columns focused and collapses player identifiers and timings', () => { const body = render(PlayerScoreboard, { props: { report: report(), matchElapsed: '4m 19s' } }).body; expect(body).toContain('Live scoreboard'); expect(body).toContain('Round 4'); expect(body).toContain('Match 4m 19s'); expect(body).toContain('aria-expanded="false"'); expect(body).toMatch(/]*hidden/); expect(body).toContain('Time on instance: 1m 10s'); expect(body).toContain('aria-label="Copy GUID for Player 1"'); expect(body).not.toContain('>Time on server'); expect(body).not.toContain('>Round started'); expect(body).toContain('aria-sort="descending"'); expect(body).toContain('About these stats'); }); it('freezes historical timing and keeps unreported scores distinct from zero', () => { const body = render(PlayerScoreboard, { props: { report: report(false) } }).body; expect(body).toContain('Last reported scoreboard'); expect(body).toContain('in last report'); expect(body).toContain('Time on instance: 1m 00s'); expect(body).not.toContain('Time on instance: 6m'); const start = { ...gameEvent('game_start', 0, 1), receivedAt: report().receivedAt }; expect(render(PlayerScoreboard, { props: { report: start } }).body).toContain('aria-label="Not yet reported"'); }); it('omits the fixed instance selector while keeping promoted/live distinction', () => { const body = render(ConfigInspector, { props: { fixedServer: 't5_zombie_theater_1' } }).body; expect(body).not.toContain(').set({ data: { user: { role: 'admin' } }, params: { name: 't5_zombie_theater_1' }, url: new URL(`http://localhost/servers/t5_zombie_theater_1?tab=${tab}&returnTo=%2F%3Ffilter%3Doccupied`) }); const telemetry = report(); const server = { name: 't5_zombie_theater_1', game: 't5', map: 'zombie_theater', last_map: 'zombie_theater', health: 'online', last_players: 1, last_seen_at: telemetry.receivedAt, last_error: null, held: [], telemetry, status_source: 'telemetry', host_id: 'host-1', native_instance_id: 'instance-1', host: { name: 'ec-1', connected: true, lastHeartbeat: telemetry.receivedAt }, rcon_status: { last_seen_at: null, last_error: 'timeout: no reply' }, game_started_at: null, game_ended_at: null, active_since: null, inactive_since: null }; const data = { serverSnapshot: { servers: [server], operations: [], serverNow: get(uiNow), integrations: {}, reservedNames: [] } } as unknown as ComponentProps['data']; return render(ServerPage, { props: { data } }).body; } it('keeps match content out of the shared header and preserves the fleet return filter', () => { const body = pageBody('configuration'); expect(body).toContain('More actions'); expect(body).toContain('href="/?filter=occupied"'); expect(body).toContain('ec-1'); expect(body).not.toContain('Active for'); expect(body).not.toContain('Round 4'); expect(body).not.toContain('aria-label="Configuration sections"'); }); it('reports RCON failures independently of fresh telemetry and defers the request inspector', () => { const body = pageBody('diagnostics'); expect(body).toContain('Game connection ยท RCON'); expect(body).toContain('Issue'); expect(body).toContain('Reporting'); expect(body).toContain('Player count source: game telemetry'); expect(body).toContain('Connection details'); expect(body).not.toContain('Telemetry API requests'); expect(body).not.toContain('>Healthy<'); }); it('uses the shared Activity view for server history', () => { const body = pageBody('history'); expect(body).toContain('Open in Activity'); expect(body).not.toContain('Loading campaign history'); expect(body).not.toContain('>Provisioning history'); });