Replace instance metrics with map lobby counts (Refs #139)
CI / checks (push) Successful in 4m55s
CI / checks (push) Successful in 4m55s
This commit is contained in:
@@ -30,6 +30,7 @@ const matches = (changes: Partial<MatchMetrics> = {}): MatchMetrics => ({
|
||||
...changes
|
||||
});
|
||||
const map = (i: number, changes: Partial<MapMetrics> = {}): MapMetrics => ({
|
||||
running: { average: 2.5, peak: 3, capturedHours: 4 },
|
||||
game: 't5',
|
||||
map: `Map ${String(i).padStart(2, '0')}`,
|
||||
matches: matches({
|
||||
@@ -189,11 +190,11 @@ it('shows concurrent lobbies and empty spare share with unknown lobby-hours in t
|
||||
it('searches and sorts the complete map set before pagination and clamps invalid pages', () => {
|
||||
const data = report();
|
||||
scope('?mapPage=2');
|
||||
let body = render(MetricsTables, { props: { maps: data.maps, servers: data.servers } }).body;
|
||||
let body = render(MetricsTables, { props: { maps: data.maps } }).body;
|
||||
expect(body).toContain('Map 09');
|
||||
expect(body).not.toContain('Map 24');
|
||||
scope('?mapPage=2', '?mapQ=Map+24&mapPage=100&mapSort=playerHours');
|
||||
body = render(MetricsTables, { props: { maps: data.maps, servers: data.servers } }).body;
|
||||
body = render(MetricsTables, { props: { maps: data.maps } }).body;
|
||||
expect(body).toContain('Map 24');
|
||||
expect(body.replace(/\s+/g, ' ')).toContain('1–1 of 1 maps');
|
||||
expect(body).not.toContain('>Next<');
|
||||
@@ -245,7 +246,7 @@ it('sorts map match counts numerically, including maps without occupancy or reco
|
||||
map(4, { matches: { ...noRoster, started: 0 } })
|
||||
];
|
||||
scope('?mapSort=matches&mapDirection=asc');
|
||||
let body = render(MetricsTables, { props: { maps, servers: [] } }).body;
|
||||
let body = render(MetricsTables, { props: { maps } }).body;
|
||||
expect(body.indexOf('Map 04')).toBeLessThan(body.indexOf('Map 02'));
|
||||
expect(body.indexOf('Map 02')).toBeLessThan(body.indexOf('Map 03'));
|
||||
expect(body.indexOf('Map 03')).toBeLessThan(body.indexOf('Map 01'));
|
||||
@@ -255,7 +256,7 @@ it('sorts map match counts numerically, including maps without occupancy or reco
|
||||
expect(body.replace(/\s+/g, ' ')).toContain('0 of 100');
|
||||
|
||||
scope('?mapSort=matches&mapDirection=desc');
|
||||
body = render(MetricsTables, { props: { maps, servers: [] } }).body;
|
||||
body = render(MetricsTables, { props: { maps } }).body;
|
||||
expect(body.indexOf('Map 01')).toBeLessThan(body.indexOf('Map 03'));
|
||||
expect(body.indexOf('Map 03')).toBeLessThan(body.indexOf('Map 02'));
|
||||
expect(body.indexOf('Map 02')).toBeLessThan(body.indexOf('Map 04'));
|
||||
@@ -274,6 +275,7 @@ it('keeps match summaries and map rows visible without any retained occupancy sa
|
||||
data.maps = [
|
||||
map(1, {
|
||||
...data.summary,
|
||||
running: data.running,
|
||||
noEmpty: 0,
|
||||
availabilityObserved: 0,
|
||||
matches: data.matches
|
||||
@@ -302,7 +304,7 @@ it('keeps unknown availability last when sorting and distinguishes it from known
|
||||
map(2, { noEmpty: 0 }),
|
||||
map(3)
|
||||
];
|
||||
const body = render(MetricsTables, { props: { maps, servers: [] } }).body;
|
||||
const body = render(MetricsTables, { props: { maps } }).body;
|
||||
expect(body.indexOf('Map 02')).toBeLessThan(body.indexOf('Map 03'));
|
||||
expect(body.indexOf('Map 03')).toBeLessThan(body.indexOf('Map 01'));
|
||||
expect(body).toContain('0.0%');
|
||||
@@ -311,18 +313,56 @@ it('keeps unknown availability last when sorting and distinguishes it from known
|
||||
expect(body).not.toContain('All lobbies full');
|
||||
});
|
||||
|
||||
it('preserves instance return context and distinguishes historic names without current links', () => {
|
||||
scope('?view=instances&serverQ=theater&days=30');
|
||||
const body = render(MetricsTables, {
|
||||
props: { maps: [], servers: [server(1), server(2)], view: 'instances' }
|
||||
}).body;
|
||||
expect(body).toContain('Historical');
|
||||
expect(body).not.toContain('href="/servers/t5_zombie_theater_1');
|
||||
expect(body).toContain('href="/servers/t5_zombie_theater_2?returnTo=');
|
||||
expect(body).toContain('serverQ%3Dtheater');
|
||||
expect(body).not.toContain('>Idle</button>');
|
||||
expect(body).not.toContain('Open current server');
|
||||
});
|
||||
it.each(['', '?view=instances&serverQ=theater&days=30'])(
|
||||
'shows map lobby metrics without an Instances tab for %s',
|
||||
(search) => {
|
||||
scope(search);
|
||||
const body = render(MetricsPage, {
|
||||
props: { data: { ...report(), user: null, setupRequired: false } }
|
||||
}).body;
|
||||
expect(body).toContain('aria-label="Map metrics table"');
|
||||
expect(body).toContain('aria-labelledby="metrics-views-maps"');
|
||||
expect(body).toContain('Lobbies (avg · peak)');
|
||||
expect(body).not.toContain('Instances');
|
||||
expect(body).not.toContain('Instance utilization');
|
||||
expect(body).not.toContain('Historical');
|
||||
expect(body).not.toContain('grouped by name');
|
||||
}
|
||||
);
|
||||
|
||||
it.each(['asc', 'desc'])(
|
||||
'sorts average lobby counts numerically %s with missing evidence last and shows empty time',
|
||||
(direction) => {
|
||||
scope(`?mapSort=lobbies&mapDirection=${direction}`);
|
||||
const maps = [
|
||||
map(1, { running: { average: null, peak: 0, capturedHours: 0 }, observed: 0, idle: 0 }),
|
||||
map(2, { running: { average: 2.5, peak: 4, capturedHours: 2 }, idle: 0 }),
|
||||
map(3, { running: { average: 10, peak: 12, capturedHours: 1 }, idle: 1.2 }),
|
||||
map(4, {
|
||||
running: { average: 3, peak: 3, capturedHours: 1 },
|
||||
observed: 0,
|
||||
idle: 0,
|
||||
unknown: 3
|
||||
})
|
||||
];
|
||||
const body = render(MetricsTables, { props: { maps } }).body;
|
||||
const ordered = direction === 'asc' ? [2, 4, 3, 1] : [3, 4, 2, 1];
|
||||
for (let i = 1; i < ordered.length; i++) {
|
||||
expect(body.indexOf(`Map 0${ordered[i - 1]}`)).toBeLessThan(
|
||||
body.indexOf(`Map 0${ordered[i]}`)
|
||||
);
|
||||
}
|
||||
expect(body).toContain(`aria-sort="${direction === 'asc' ? 'ascending' : 'descending'}"`);
|
||||
expect(body).toContain('2.5 · 4');
|
||||
expect(body).toContain('10 · 12');
|
||||
expect(body).toContain('3 · 3');
|
||||
expect(body).toContain('0 h empty');
|
||||
expect(body).toContain('1.2 h empty');
|
||||
expect(body).toContain('Empty time unavailable');
|
||||
expect(body).toContain('Captured map time');
|
||||
expect(body).toMatch(/class="lobbies [^"]*">—\s*<small/);
|
||||
}
|
||||
);
|
||||
|
||||
it('highlights at most three supported policy changes and collapses tentative findings', () => {
|
||||
const data = report();
|
||||
@@ -475,13 +515,13 @@ it('renders whole-lobby axis ticks without rounding fractional averages or other
|
||||
});
|
||||
|
||||
it('uses shallow URL state for the selected breakdown while retaining the common summary', () => {
|
||||
scope('?view=maps', '?view=instances&serverQ=theater&serverPage=2');
|
||||
scope('?view=insights', '?view=maps&mapPage=2');
|
||||
let body = render(MetricsPage, {
|
||||
props: { data: { ...report(), user: null, setupRequired: false } }
|
||||
}).body;
|
||||
expect(body).toContain('aria-label="Instance metrics table"');
|
||||
expect(body).not.toContain('aria-label="Map metrics table"');
|
||||
expect(body.replace(/\s+/g, ' ')).toContain('16–24 of 24 instances');
|
||||
expect(body).toContain('aria-label="Map metrics table"');
|
||||
expect(body).not.toContain('id="metrics-explore-title"');
|
||||
expect(body.replace(/\s+/g, ' ')).toContain('16–24 of 24 maps');
|
||||
scope('?view=maps', '?view=insights');
|
||||
body = render(MetricsPage, {
|
||||
props: { data: { ...report(), user: null, setupRequired: false } }
|
||||
|
||||
Reference in New Issue
Block a user