diff --git a/site/src/modules/workspaces/WorkspaceMoreActions/DownloadLogsDialog.test.ts b/site/src/modules/workspaces/WorkspaceMoreActions/DownloadLogsDialog.test.ts new file mode 100644 index 0000000000..fa57d60038 --- /dev/null +++ b/site/src/modules/workspaces/WorkspaceMoreActions/DownloadLogsDialog.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "vitest"; +import { humanBlobSize } from "./DownloadLogsDialog"; + +describe("humanBlobSize", () => { + it("formats bytes without decimals", () => { + expect(humanBlobSize(200)).toBe("200 B"); + }); + + it("promotes 1024 bytes to 1 KB", () => { + expect(humanBlobSize(1024)).toBe("1 KB"); + }); + + it("formats fractional kilobytes with up to 2 decimals", () => { + expect(humanBlobSize(1491)).toBe("1.46 KB"); + expect(humanBlobSize(4472)).toBe("4.37 KB"); + }); + + it("formats larger units", () => { + expect(humanBlobSize(1024 * 1024)).toBe("1 MB"); + expect(humanBlobSize(1024 * 1024 * 1.5)).toBe("1.5 MB"); + }); +}); diff --git a/site/src/modules/workspaces/WorkspaceMoreActions/DownloadLogsDialog.tsx b/site/src/modules/workspaces/WorkspaceMoreActions/DownloadLogsDialog.tsx index 2e5719e06a..044134e321 100644 --- a/site/src/modules/workspaces/WorkspaceMoreActions/DownloadLogsDialog.tsx +++ b/site/src/modules/workspaces/WorkspaceMoreActions/DownloadLogsDialog.tsx @@ -227,21 +227,22 @@ const DownloadingItem: FC = ({ file, giveUpTimeMs }) => { ); }; -function humanBlobSize(size: number) { +export function humanBlobSize(size: number) { const BLOB_SIZE_UNITS = ["B", "KB", "MB", "GB", "TB"] as const; let i = 0; - let sizeIterator = size; - while (sizeIterator > 1024 && i < BLOB_SIZE_UNITS.length) { - sizeIterator /= 1024; + let sizeInUnits = size; + while (sizeInUnits >= 1024 && i < BLOB_SIZE_UNITS.length - 1) { + sizeInUnits /= 1024; i++; } - // The condition for the while loop above means that over time, we could break - // out of the loop because we accidentally shot past the array bounds and i - // is at index (BLOB_SIZE_UNITS.length). Adding a lot of redundant checks to - // make sure we always have a usable unit - const finalUnit = BLOB_SIZE_UNITS[i] ?? BLOB_SIZE_UNITS.at(-1) ?? "TB"; - return `${size.toFixed(2)} ${finalUnit}`; + const finalUnit = BLOB_SIZE_UNITS[i]; + + // Round to 2 decimals and omit trailing zeros for whole numbers. + const formattedSize = new Intl.NumberFormat("en-US", { + maximumFractionDigits: 2, + }).format(sizeInUnits); + return `${formattedSize} ${finalUnit}`; } type FileNameInfo = Readonly<{