106 lines
4.3 KiB
TypeScript
106 lines
4.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
compareMetricValues,
|
|
metricAverageLobbies,
|
|
metricDuration,
|
|
metricLobbyAxis,
|
|
metricNumber,
|
|
metricPercent,
|
|
metricPeriodLabels,
|
|
metricPlayerPaths
|
|
} from '../../src/lib/ui/metrics.js';
|
|
|
|
describe('metric presentation', () => {
|
|
it('breaks player lines at missing history while retaining real zeroes and isolated points', () => {
|
|
const paths = metricPlayerPaths([null, 2, 0, null, 1], 2);
|
|
expect(paths.line).toBe('M30,0 L50,100 M90,50');
|
|
expect(paths.area).toBe('M30,0 L50,100 L50,100 L30,100 Z M90,50 L90,100 L90,100 Z');
|
|
expect(metricPlayerPaths([null, null], 1)).toEqual({ line: '', area: '' });
|
|
});
|
|
it('normalizes partial buckets by captured wall time and preserves unknown lobby counts', () => {
|
|
expect(metricAverageLobbies(14 * 24, 24)).toBe(14);
|
|
expect(metricAverageLobbies(14 * 0.5, 0.5)).toBe(14);
|
|
expect(metricAverageLobbies(12 + 2, 1)).toBe(14);
|
|
expect(metricAverageLobbies(0, 1)).toBe(0);
|
|
expect(metricAverageLobbies(0, 0)).toBeNull();
|
|
expect(metricAverageLobbies(4, Number.NaN)).toBeNull();
|
|
});
|
|
it.each<[number, number[]]>([
|
|
[0, [1, 0]],
|
|
[0.4, [1, 0]],
|
|
[1, [1, 0]],
|
|
[2.1, [3, 2, 1, 0]],
|
|
[3.75, [4, 3, 2, 1, 0]],
|
|
[5, [6, 4, 2, 0]],
|
|
[14, [15, 10, 5, 0]],
|
|
[41.2, [60, 40, 20, 0]],
|
|
[999, [1000, 500, 0]],
|
|
[2500, [3000, 2000, 1000, 0]]
|
|
])('uses whole-lobby ticks that cover an average of %s', (maximum, expected) => {
|
|
const axis = metricLobbyAxis(maximum);
|
|
expect(axis.ticks).toEqual(expected);
|
|
expect(axis.ceiling).toBe(axis.ticks[0]);
|
|
expect(axis.ceiling).toBeGreaterThanOrEqual(maximum);
|
|
expect(axis.ticks.every(Number.isInteger)).toBe(true);
|
|
});
|
|
it('distinguishes unavailable evidence, zero, and tiny nonzero values', () => {
|
|
expect(metricPercent(0, 0)).toBe('—');
|
|
expect(metricPercent(0, 1)).toBe('0.0%');
|
|
expect(metricPercent(0.0001, 1)).toBe('<0.1%');
|
|
expect(metricDuration(0)).toBe('0 h');
|
|
expect(metricDuration(0.1 / 3600)).toBe('<1 s');
|
|
expect(metricDuration(45 / 3600)).toBe('45 s');
|
|
expect(metricDuration(90 / 3600)).toBe('1.5 min');
|
|
expect(metricNumber(0.0125)).toBe('<0.1');
|
|
});
|
|
|
|
it('sorts numerically while putting unavailable observations last in both directions', () => {
|
|
const values = [2, null, 100, 0, 10];
|
|
expect([...values].sort((a, b) => compareMetricValues(a, b, 'desc'))).toEqual([
|
|
100,
|
|
10,
|
|
2,
|
|
0,
|
|
null
|
|
]);
|
|
expect([...values].sort((a, b) => compareMetricValues(a, b, 'asc'))).toEqual([
|
|
0,
|
|
2,
|
|
10,
|
|
100,
|
|
null
|
|
]);
|
|
});
|
|
|
|
it('labels daily buckets by their actual starting weekday in the report timezone', () => {
|
|
const points = Array.from({ length: 8 }, (_, day) => ({
|
|
startAt: Date.parse('2026-10-01T00:00:00Z') + day * 86_400_000
|
|
}));
|
|
expect(metricPeriodLabels(points, 7, 'America/New_York').map((item) => item.label)).toEqual(
|
|
['Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Mon', 'Tue', 'Wed']
|
|
);
|
|
expect(metricPeriodLabels(points, 7, 'UTC')[0].label).toBe('Thu');
|
|
expect(metricPeriodLabels(points, 7, 'UTC')[0].position).toBe(6.25);
|
|
expect(metricPeriodLabels([], 7, 'UTC')).toEqual([]);
|
|
});
|
|
|
|
it('keeps hourly and long-range labels sparse without relabelling UTC buckets as local days', () => {
|
|
const points = Array.from({ length: 31 }, (_, day) => ({
|
|
startAt: Date.parse('2026-10-01T00:00:00Z') + day * 86_400_000
|
|
}));
|
|
const labels = metricPeriodLabels(points, 30, 'UTC');
|
|
expect(labels.length).toBeLessThanOrEqual(6);
|
|
expect(labels[0]).toMatchObject({ label: 'Thu', date: '1' });
|
|
const hourly = metricPeriodLabels(
|
|
Array.from({ length: 25 }, (_, hour) => ({
|
|
startAt: Date.parse('2026-11-01T04:00:00Z') + hour * 3_600_000
|
|
})),
|
|
1,
|
|
'America/New_York'
|
|
);
|
|
expect(hourly[0].label).toBe('12 AM');
|
|
expect(hourly[1].label).toBe('4 AM');
|
|
expect(hourly).toHaveLength(5);
|
|
});
|
|
});
|