Add Prettier and ESLint tooling (Refs #77)
This commit is contained in:
+115
-21
@@ -13,14 +13,57 @@ import GameFileHosts from '../../src/lib/ui/components/GameFileHosts.svelte';
|
||||
import type { Host } from '../../src/lib/hosts.js';
|
||||
import type { PlayerMatch } from '../../src/lib/leaderboard.js';
|
||||
|
||||
vi.mock('$app/stores', () => ({ page: writable({ url: new URL('http://localhost/hosts'), data: { user: { id: 'admin', role: 'admin' } } }) }));
|
||||
vi.mock('$app/navigation', () => ({ goto: vi.fn(), beforeNavigate: vi.fn(), afterNavigate: vi.fn(), invalidateAll: vi.fn() }));
|
||||
vi.mock('$app/stores', () => ({
|
||||
page: writable({
|
||||
url: new URL('http://localhost/hosts'),
|
||||
data: { user: { id: 'admin', role: 'admin' } }
|
||||
})
|
||||
}));
|
||||
vi.mock('$app/navigation', () => ({
|
||||
goto: vi.fn(),
|
||||
beforeNavigate: vi.fn(),
|
||||
afterNavigate: vi.fn(),
|
||||
invalidateAll: vi.fn()
|
||||
}));
|
||||
const now = Date.parse('2026-10-07T12:00:00Z');
|
||||
const host: Host = { id: 'dallas', name: 'Dallas 1', hostname: 'dallas.example', mode: 'active', portMin: 28960, portMax: 28980,
|
||||
bindAddress: '0.0.0.0', publicAddress: '', minimumFreeBytes: 100, createdAt: new Date(now).toISOString(),
|
||||
lastHeartbeat: new Date(now).toISOString(), connected: false, connectionState: 'offline', readiness: 'ready', readinessReasons: [],
|
||||
inventory: {}, enrolled: true, credentialActive: true, decommission: null };
|
||||
const match: PlayerMatch = { id: 17, game: 't5', server: 't5_zombie_theater_1', mapName: 'Kino Der Toten', startedAt: new Date(now).toISOString(), lastSeen: new Date(now).toISOString(), status: 'completed', completed: 1, highestRound: 30, roundsSurvived: 20, kills: 1200, headshots: 200, revives: 2, downs: 1, bestScore: 45000 };
|
||||
const host: Host = {
|
||||
id: 'dallas',
|
||||
name: 'Dallas 1',
|
||||
hostname: 'dallas.example',
|
||||
mode: 'active',
|
||||
portMin: 28960,
|
||||
portMax: 28980,
|
||||
bindAddress: '0.0.0.0',
|
||||
publicAddress: '',
|
||||
minimumFreeBytes: 100,
|
||||
createdAt: new Date(now).toISOString(),
|
||||
lastHeartbeat: new Date(now).toISOString(),
|
||||
connected: false,
|
||||
connectionState: 'offline',
|
||||
readiness: 'ready',
|
||||
readinessReasons: [],
|
||||
inventory: {},
|
||||
enrolled: true,
|
||||
credentialActive: true,
|
||||
decommission: null
|
||||
};
|
||||
const match: PlayerMatch = {
|
||||
id: 17,
|
||||
game: 't5',
|
||||
server: 't5_zombie_theater_1',
|
||||
mapName: 'Kino Der Toten',
|
||||
startedAt: new Date(now).toISOString(),
|
||||
lastSeen: new Date(now).toISOString(),
|
||||
status: 'completed',
|
||||
completed: 1,
|
||||
highestRound: 30,
|
||||
roundsSurvived: 20,
|
||||
kills: 1200,
|
||||
headshots: 200,
|
||||
revives: 2,
|
||||
downs: 1,
|
||||
bestScore: 45000
|
||||
};
|
||||
|
||||
it('pluralizes zero, one, multiple, fractional and irregular counts consistently', () => {
|
||||
expect(countLabel(0, 'server')).toBe('0 servers');
|
||||
@@ -34,8 +77,19 @@ it('pluralizes zero, one, multiple, fractional and irregular counts consistently
|
||||
});
|
||||
|
||||
it('recommends a sampling upgrade only for a known older version', () => {
|
||||
for (const version of ['1.0.0', '1.4.5', 'v1.4.0']) expect(hostCalibrationHint(version)).toContain('Update this host to agent 1.4.6');
|
||||
for (const version of ['1.4.6', '1.7.0', '1.10.0', '2.0.0', 'v2.0.0+build', null, undefined, 'unknown', '1.4']) {
|
||||
for (const version of ['1.0.0', '1.4.5', 'v1.4.0'])
|
||||
expect(hostCalibrationHint(version)).toContain('Update this host to agent 1.4.6');
|
||||
for (const version of [
|
||||
'1.4.6',
|
||||
'1.7.0',
|
||||
'1.10.0',
|
||||
'2.0.0',
|
||||
'v2.0.0+build',
|
||||
null,
|
||||
undefined,
|
||||
'unknown',
|
||||
'1.4'
|
||||
]) {
|
||||
expect(hostCalibrationHint(version)).toContain('still calibrating');
|
||||
expect(hostCalibrationHint(version)).not.toContain('Update');
|
||||
}
|
||||
@@ -44,29 +98,69 @@ it('recommends a sampling upgrade only for a known older version', () => {
|
||||
it('describes per-player completion as final-report evidence without inventing unfinished results', () => {
|
||||
expect(playerCompletionLabel(match)).toBe('Present in final report');
|
||||
expect(playerCompletionLabel({ ...match, completed: 0 })).toBe('Not present in final report');
|
||||
expect(playerCompletionLabel({ ...match, status: 'active', completed: 0 })).toBe('Match in progress');
|
||||
expect(playerCompletionLabel({ ...match, status: 'interrupted', completed: 0 })).toBe('Final report unavailable');
|
||||
const body = render(PlayerMatchCards, { props: { matches: [match, { ...match, id: 18, completed: 0 }] } }).body;
|
||||
expect(body).toContain('Present in final report'); expect(body).toContain('Not present in final report');
|
||||
expect(body).not.toContain('Completion credited'); expect(body).not.toContain('No completion credit');
|
||||
expect(playerCompletionLabel({ ...match, status: 'active', completed: 0 })).toBe(
|
||||
'Match in progress'
|
||||
);
|
||||
expect(playerCompletionLabel({ ...match, status: 'interrupted', completed: 0 })).toBe(
|
||||
'Final report unavailable'
|
||||
);
|
||||
const body = render(PlayerMatchCards, {
|
||||
props: { matches: [match, { ...match, id: 18, completed: 0 }] }
|
||||
}).body;
|
||||
expect(body).toContain('Present in final report');
|
||||
expect(body).toContain('Not present in final report');
|
||||
expect(body).not.toContain('Completion credited');
|
||||
expect(body).not.toContain('No completion credit');
|
||||
});
|
||||
|
||||
it('uses product game names while preserving unsupported game identifiers', () => {
|
||||
expect(gameTitle('t5')).toBe('Black Ops I'); expect(gameTitle('T6')).toBe('Black Ops II');
|
||||
expect(gameTitle('t5', true)).toBe('BO1'); expect(gameTitle('t6', true)).toBe('BO2');
|
||||
expect(gameTitle('custom')).toBe('CUSTOM'); expect(gameTitle(null)).toBe('Unknown game');
|
||||
expect(gameTitle('t5')).toBe('Black Ops I');
|
||||
expect(gameTitle('T6')).toBe('Black Ops II');
|
||||
expect(gameTitle('t5', true)).toBe('BO1');
|
||||
expect(gameTitle('t6', true)).toBe('BO2');
|
||||
expect(gameTitle('custom')).toBe('CUSTOM');
|
||||
expect(gameTitle(null)).toBe('Unknown game');
|
||||
expect(scopeLabel({ scope_type: 'game', scope_value: 't5' })).toBe('Black Ops I');
|
||||
});
|
||||
|
||||
it('does not offer an existing-account sign-in path on first administrator setup', () => {
|
||||
const body = render(Setup).body;
|
||||
expect(body).toContain('Create the first administrator'); expect(body).not.toContain('Already have an account');
|
||||
expect(body).toContain('Create the first administrator');
|
||||
expect(body).not.toContain('Already have an account');
|
||||
expect(body).not.toContain('href="/login"');
|
||||
});
|
||||
|
||||
it('renders singular host attention and explicit whitespace before an offline suffix', () => {
|
||||
const body = render(Hosts, { props: { data: { hosts: [{ ...host, serverCount: 0, playerCount: 0, unreportedInstances: 0 }], serverNow: now, user: null, setupRequired: false } } }).body;
|
||||
expect(body).toContain('1 host'); expect(body).toContain('1 needs attention'); expect(body).not.toContain('1 need attention');
|
||||
const files = render(GameFileHosts, { props: { hosts: [host], caches: [{ hostId: host.id, libraryVersionId: 'files', status: 'missing', bytesDownloaded: 0, bytesTotal: 0, manifestSha256: null, error: null, updatedAt: new Date(now).toISOString(), operationId: null }] } }).body;
|
||||
const body = render(Hosts, {
|
||||
props: {
|
||||
data: {
|
||||
hosts: [{ ...host, serverCount: 0, playerCount: 0, unreportedInstances: 0 }],
|
||||
serverNow: now,
|
||||
user: null,
|
||||
setupRequired: false
|
||||
}
|
||||
}
|
||||
}).body;
|
||||
expect(body).toContain('1 host');
|
||||
expect(body).toContain('1 needs attention');
|
||||
expect(body).not.toContain('1 need attention');
|
||||
const files = render(GameFileHosts, {
|
||||
props: {
|
||||
hosts: [host],
|
||||
caches: [
|
||||
{
|
||||
hostId: host.id,
|
||||
libraryVersionId: 'files',
|
||||
status: 'missing',
|
||||
bytesDownloaded: 0,
|
||||
bytesTotal: 0,
|
||||
manifestSha256: null,
|
||||
error: null,
|
||||
updatedAt: new Date(now).toISOString(),
|
||||
operationId: null
|
||||
}
|
||||
]
|
||||
}
|
||||
}).body;
|
||||
expect(files).toMatch(/Dallas 1<\/a>.*?> · Offline<\/span>/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user