From a10dbc2a0c7866f01bf61678e0e57b81592bb7ad Mon Sep 17 00:00:00 2001 From: Wojciech Maj Date: Wed, 18 Feb 2026 12:27:06 +0100 Subject: [PATCH] Fix kB/KiB confusion - kB / MB means decimal units: 1000, 1,000,000. - KiB / MiB means binary units: 1024, 1,048,576. We were calculating binary units while labeling them as decimal ones. --- app/composables/useNumberFormatter.ts | 10 ++-- .../composables/use-number-formatter.spec.ts | 46 +++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 test/unit/app/composables/use-number-formatter.spec.ts diff --git a/app/composables/useNumberFormatter.ts b/app/composables/useNumberFormatter.ts index 0c9b46f6d..a375bc043 100644 --- a/app/composables/useNumberFormatter.ts +++ b/app/composables/useNumberFormatter.ts @@ -16,19 +16,21 @@ export const useBytesFormatter = () => { const decimalNumberFormatter = useNumberFormatter({ maximumFractionDigits: 1, }) + const KB = 1000 + const MB = 1000 * 1000 return { format: (bytes: number) => { - if (bytes < 1024) + if (bytes < KB) return t('package.size.b', { size: decimalNumberFormatter.value.format(bytes), }) - if (bytes < 1024 * 1024) + if (bytes < MB) return t('package.size.kb', { - size: decimalNumberFormatter.value.format(bytes / 1024), + size: decimalNumberFormatter.value.format(bytes / KB), }) return t('package.size.mb', { - size: decimalNumberFormatter.value.format(bytes / (1024 * 1024)), + size: decimalNumberFormatter.value.format(bytes / MB), }) }, } diff --git a/test/unit/app/composables/use-number-formatter.spec.ts b/test/unit/app/composables/use-number-formatter.spec.ts new file mode 100644 index 000000000..eef5d0f02 --- /dev/null +++ b/test/unit/app/composables/use-number-formatter.spec.ts @@ -0,0 +1,46 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { computed, ref } from 'vue' +import { useBytesFormatter } from '../../../../app/composables/useNumberFormatter' + +describe('useBytesFormatter', () => { + beforeEach(() => { + vi.stubGlobal('computed', computed) + vi.stubGlobal('useI18n', () => ({ + locale: ref('en-US'), + t: (key: string, params?: { size?: string }) => { + const size = params?.size ?? '' + + if (key === 'package.size.b') return `${size} B` + if (key === 'package.size.kb') return `${size} kB` + if (key === 'package.size.mb') return `${size} MB` + + return key + }, + })) + }) + + afterEach(() => { + vi.unstubAllGlobals() + }) + + it('formats values below 1 kB in bytes', () => { + const { format } = useBytesFormatter() + + expect(format(0)).toBe('0 B') + expect(format(999)).toBe('999 B') + }) + + it('formats kB values using decimal base (1000)', () => { + const { format } = useBytesFormatter() + + expect(format(1000)).toBe('1 kB') + expect(format(8414)).toBe('8.4 kB') + }) + + it('formats MB values using decimal base (1000 * 1000)', () => { + const { format } = useBytesFormatter() + + expect(format(1_000_000)).toBe('1 MB') + expect(format(1_500_000)).toBe('1.5 MB') + }) +})