This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { flushSync, mount, unmount, type ComponentProps } from 'svelte';
|
||||
import { fromStore, writable } from 'svelte/store';
|
||||
import ServerCard from '../../src/lib/ui/components/ServerCard.svelte';
|
||||
import ServerIdentity from '../../src/lib/ui/components/ServerIdentity.svelte';
|
||||
import PlayerScoreboard from '../../src/lib/ui/components/PlayerScoreboard.svelte';
|
||||
import { fleetIdentity } from '../../src/lib/fleet-overview.js';
|
||||
import { gameEvent } from '../helpers/telemetry.js';
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(components.splice(0).map((component) => unmount(component)));
|
||||
document.body.replaceChildren();
|
||||
});
|
||||
vi.mock('$app/stores', () => ({
|
||||
page: writable({ url: new URL('http://localhost/') })
|
||||
}));
|
||||
const components: ReturnType<typeof mount>[] = [];
|
||||
type MapServer = Parameters<typeof fleetIdentity>[0];
|
||||
const town: MapServer = {
|
||||
name: 't6_zombie_town_1',
|
||||
game: 't6',
|
||||
map: 'zm_transit',
|
||||
last_map: 'zm_transit'
|
||||
};
|
||||
|
||||
function show(
|
||||
initial: MapServer,
|
||||
options: Partial<ComponentProps<typeof ServerCard>> = {},
|
||||
fleetHeading = true
|
||||
) {
|
||||
const store = writable(initial);
|
||||
const state = fromStore(store);
|
||||
const identity = () => fleetIdentity(state.current);
|
||||
components.push(
|
||||
mount(ServerCard, {
|
||||
target: document.body,
|
||||
props: {
|
||||
get name() {
|
||||
return state.current.name;
|
||||
},
|
||||
get game() {
|
||||
return state.current.game;
|
||||
},
|
||||
get map() {
|
||||
return state.current.last_map ?? state.current.map;
|
||||
},
|
||||
get mapSlug() {
|
||||
return state.current.map_slug;
|
||||
},
|
||||
get gameMode() {
|
||||
return state.current.game_mode;
|
||||
},
|
||||
get mapLabelOverride() {
|
||||
return fleetHeading ? identity().map : undefined;
|
||||
},
|
||||
health: 'online',
|
||||
players: 4,
|
||||
...options
|
||||
}
|
||||
}),
|
||||
mount(ServerIdentity, {
|
||||
target: document.body,
|
||||
props: {
|
||||
get name() {
|
||||
return state.current.name;
|
||||
},
|
||||
get mapLabel() {
|
||||
return identity().map;
|
||||
}
|
||||
}
|
||||
}),
|
||||
mount(PlayerScoreboard, {
|
||||
target: document.body,
|
||||
props: {
|
||||
get report() {
|
||||
return {
|
||||
...gameEvent(
|
||||
'heartbeat',
|
||||
12,
|
||||
4,
|
||||
state.current.name,
|
||||
state.current.last_map ?? state.current.map ?? ''
|
||||
),
|
||||
receivedAt: new Date().toISOString()
|
||||
};
|
||||
},
|
||||
get mapLabel() {
|
||||
return identity().map;
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
flushSync();
|
||||
return (next: Partial<MapServer>) => {
|
||||
store.update((server) => ({ ...server, ...next }));
|
||||
flushSync();
|
||||
};
|
||||
}
|
||||
function expectLabels(label: string) {
|
||||
expect(document.querySelector('h3')?.textContent).toBe(label);
|
||||
expect(document.querySelector('.identity .map')?.textContent).toBe(label);
|
||||
expect(document.querySelector('.match-title h2')?.textContent).toBe(label);
|
||||
expect(document.querySelector('.map-note')).toBeNull();
|
||||
}
|
||||
|
||||
const variants = [
|
||||
['town', 'Town Survival'],
|
||||
['farm', 'Farm Survival'],
|
||||
['busdepot', 'Bus Depot Survival'],
|
||||
['transit', 'TranZit']
|
||||
];
|
||||
describe('variant-aware rendered map labels', () => {
|
||||
it.each(variants)('keeps %s consistent across card, list, and detail', (slug, label) => {
|
||||
const update = show({ ...town, name: `t6_zombie_${slug}_1` });
|
||||
expectLabels(label);
|
||||
update({ name: 'Friday lobby', map_slug: slug, game_mode: 'zombie' });
|
||||
expectLabels(label);
|
||||
});
|
||||
|
||||
it.each([undefined, null, 'unknown'])('does not infer TranZit from variant %s', (map_slug) => {
|
||||
const server = { ...town, name: 'Custom lobby', map_slug };
|
||||
show(server);
|
||||
expectLabels('zm_transit (variant unknown)');
|
||||
expect(fleetIdentity(server).launch).toBeNull();
|
||||
});
|
||||
|
||||
it('lets unknown stored metadata override a recognized name', () => {
|
||||
show({ ...town, map_slug: 'unknown' });
|
||||
expectLabels('zm_transit (variant unknown)');
|
||||
});
|
||||
|
||||
it('updates map and variant metadata reactively without retaining an obsolete launch shortcut', () => {
|
||||
const update = show(town);
|
||||
expectLabels('Town Survival');
|
||||
update({ last_map: 'zm_tomb' });
|
||||
expectLabels('Origins');
|
||||
expect(fleetIdentity({ ...town, last_map: 'zm_tomb' }).launch).toBeNull();
|
||||
update({ last_map: 'zm_transit', map_slug: 'farm' });
|
||||
expectLabels('Farm Survival');
|
||||
expect(fleetIdentity({ ...town, map_slug: 'farm' }).launch).toEqual({
|
||||
game: 't6',
|
||||
slug: 'farm'
|
||||
});
|
||||
update({ map_slug: 'unknown' });
|
||||
expectLabels('zm_transit (variant unknown)');
|
||||
update({ last_map: 'zm_buried' });
|
||||
expectLabels('Buried');
|
||||
});
|
||||
|
||||
it.each(['stale', 'offline'] as const)(
|
||||
'keeps the selected variant for %s observations',
|
||||
(health) => {
|
||||
show(town, { health, lastSeenAt: '2026-01-01T00:00:00Z' });
|
||||
expectLabels('Town Survival');
|
||||
}
|
||||
);
|
||||
|
||||
it.each([true, false])('preserves empty-card compact=%s behavior', (compactEmpty) => {
|
||||
show(town, { players: 0, compactEmpty });
|
||||
expectLabels('Town Survival');
|
||||
expect(document.querySelector('article')?.classList.contains('compact')).toBe(compactEmpty);
|
||||
expect(document.querySelector('article .metrics') === null).toBe(compactEmpty);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['online', 'Current map'],
|
||||
['stale', 'Last map'],
|
||||
['offline', 'Last map']
|
||||
] as const)('retains useful %s map context with a name-based heading', (health, prefix) => {
|
||||
const update = show(town, { health }, false);
|
||||
expect(document.querySelector('.map-note')).toBeNull();
|
||||
update({ last_map: 'zm_tomb' });
|
||||
expect(document.querySelector('h3')?.textContent).toBe('Town Survival');
|
||||
expect(document.querySelector('.map-note')?.textContent?.trim()).toBe(`${prefix}: Origins`);
|
||||
});
|
||||
|
||||
it('hides additional map context in compact cards', () => {
|
||||
show({ ...town, last_map: 'zm_tomb' }, { players: 0 }, false);
|
||||
expect(document.querySelector('.map-note')).toBeNull();
|
||||
});
|
||||
|
||||
it('uses stored variant metadata for a custom heading and does not invent a missing observation', () => {
|
||||
const update = show({ ...town, name: 'Friday lobby', map_slug: 'town' }, {}, false);
|
||||
expect(document.querySelector('.map-note')?.textContent?.trim()).toBe(
|
||||
'Current map: Town Survival'
|
||||
);
|
||||
update({ map: null, last_map: null });
|
||||
expect(document.querySelector('.map-note')).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user