diff --git a/internal/site/src/lib/utils.ts b/internal/site/src/lib/utils.ts index 69c6a1ebc..f9050c4c5 100644 --- a/internal/site/src/lib/utils.ts +++ b/internal/site/src/lib/utils.ts @@ -199,7 +199,7 @@ export function decimalString(num: number, digits = 2) { return formatter.format(num) } -export function formatMicroseconds(microseconds: number, showDigits = true): string { +export function formatMicroseconds(microseconds: number, fixedDigits = true): string { if (!Number.isFinite(microseconds)) { return "-" } @@ -208,15 +208,17 @@ export function formatMicroseconds(microseconds: number, showDigits = true): str return `${microseconds}μs` } + const digitFormatter = fixedDigits ? decimalString : toFixedFloat + if (microseconds < 1_000_000) { const milliseconds = microseconds / 1000 const digits = milliseconds >= 10 ? 1 : 2 - return `${decimalString(milliseconds, showDigits ? digits : 0)}ms` + return `${digitFormatter(milliseconds, digits)}ms` } const seconds = microseconds / 1_000_000 const digits = seconds >= 10 ? 1 : 2 - return `${decimalString(seconds, showDigits ? digits : 0)}s` + return `${digitFormatter(seconds, digits)}s` } /** Get value from local or session storage */ diff --git a/internal/site/tests/utils.test.ts b/internal/site/tests/utils.test.ts new file mode 100644 index 000000000..d8fcbcbf4 --- /dev/null +++ b/internal/site/tests/utils.test.ts @@ -0,0 +1,51 @@ +import { describe, expect, mock, test } from "bun:test" + +mock.module("@lingui/core/macro", () => ({ + t: (strings: any) => (typeof strings === "string" ? strings : strings?.[0] ?? ""), + plural: (count: number, forms: any) => forms.other ?? "", +})) + +const { formatMicroseconds } = await import("../src/lib/utils") + +describe("formatMicroseconds", () => { + test("formats with fixedDigits = true (default)", () => { + expect(formatMicroseconds(500)).toBe("500μs") + expect(formatMicroseconds(6000)).toBe("6.00ms") + expect(formatMicroseconds(6500)).toBe("6.50ms") + expect(formatMicroseconds(12000)).toBe("12.0ms") + expect(formatMicroseconds(12500)).toBe("12.5ms") + expect(formatMicroseconds(1_000_000)).toBe("1.00s") + expect(formatMicroseconds(1_500_000)).toBe("1.50s") + expect(formatMicroseconds(12_000_000)).toBe("12.0s") + }) + + test("formats with fixedDigits = false (used for chart y-axis ticks)", () => { + expect(formatMicroseconds(500, false)).toBe("500μs") + expect(formatMicroseconds(500.5, false)).toBe("500.5μs") + expect(formatMicroseconds(6000, false)).toBe("6ms") + expect(formatMicroseconds(6200, false)).toBe("6.2ms") + expect(formatMicroseconds(6500, false)).toBe("6.5ms") + expect(formatMicroseconds(6750, false)).toBe("6.75ms") + expect(formatMicroseconds(7000, false)).toBe("7ms") + expect(formatMicroseconds(12000, false)).toBe("12ms") + expect(formatMicroseconds(12500, false)).toBe("12.5ms") + expect(formatMicroseconds(1_000_000, false)).toBe("1s") + expect(formatMicroseconds(1_500_000, false)).toBe("1.5s") + expect(formatMicroseconds(12_000_000, false)).toBe("12s") + }) + + test("prevents duplicate tick values for intermediate intervals (#2373)", () => { + const tickInputs = [6000, 6200, 6400, 6600, 6800, 7000] + const formattedTicks = tickInputs.map((val) => formatMicroseconds(val, false)) + expect(formattedTicks).toEqual(["6ms", "6.2ms", "6.4ms", "6.6ms", "6.8ms", "7ms"]) + // Verify all formatted ticks are unique + const uniqueTicks = new Set(formattedTicks) + expect(uniqueTicks.size).toBe(tickInputs.length) + }) + + test("handles invalid / non-finite values", () => { + expect(formatMicroseconds(Number.NaN)).toBe("-") + expect(formatMicroseconds(Number.POSITIVE_INFINITY)).toBe("-") + expect(formatMicroseconds(Number.NEGATIVE_INFINITY)).toBe("-") + }) +})