/** * Unit tests for filesize.js * Tests the main filesize function and partial function */ import assert from "node:assert"; import { describe, it } from "node:test"; import { filesize, partial } from "../../src/filesize.js"; describe("filesize", () => { describe("Basic functionality", () => { it("should convert bytes to human readable format", () => { assert.strictEqual(filesize(1000), "1 kB"); assert.strictEqual(filesize(1000000), "1 MB"); assert.strictEqual(filesize(1000000000), "1 GB"); }); it("should handle zero bytes", () => { assert.strictEqual(filesize(0), "0 B"); }); it("should handle small numbers", () => { assert.strictEqual(filesize(1), "1 B"); assert.strictEqual(filesize(512), "512 B"); }); it("should handle negative numbers", () => { assert.strictEqual(filesize(-1000), "-1 kB"); assert.strictEqual(filesize(-1000000), "-1 MB"); }); it("should throw TypeError for Infinity", () => { assert.throws(() => filesize(Infinity), TypeError); }); it("should throw TypeError for -Infinity", () => { assert.throws(() => filesize(-Infinity), TypeError); }); }); describe("Bits option", () => { it("should convert to bits when bits option is true", () => { assert.strictEqual(filesize(1000, { bits: true }), "8 kbit"); assert.strictEqual(filesize(1000000, { bits: true }), "8 Mbit"); }); it("should handle zero with bits option", () => { assert.strictEqual(filesize(0, { bits: true }), "0 bit"); }); it("should respect forced exponent with bits", () => { // Forced exponent should prevent auto-increment assert.strictEqual(filesize(1024, { exponent: 0, bits: true }), "8192 bit"); assert.strictEqual(filesize(1024, { exponent: 1, bits: true }), "8.19 kbit"); }); }); describe("Zero value edge cases", () => { it("should handle zero with exponent output", () => { assert.strictEqual(filesize(0, { output: "exponent" }), 0); }); it("should pad zero like any other value", () => { assert.strictEqual(filesize(0, { pad: true, round: 2 }), "0.00 B"); assert.strictEqual(filesize(0, { pad: true, round: 3 }), "0.000 B"); assert.strictEqual(filesize(0, { pad: true, round: 0 }), "0 B"); }); it("should prefer precision over pad for zero", () => { assert.strictEqual(filesize(0, { precision: 3, pad: true, round: 5 }), "0.00 B"); }); it("should handle zero with custom symbols", () => { assert.strictEqual(filesize(0, { symbols: { B: "Bytes" } }), "0 Bytes"); }); it("should handle zero with fullform", () => { assert.strictEqual(filesize(0, { fullform: true }), "0 byte"); }); it("should handle zero with fullform and bits", () => { assert.strictEqual(filesize(0, { fullform: true, bits: true }), "0 bit"); }); it("should handle zero with array output", () => { const result = filesize(0, { output: "array" }); assert.deepStrictEqual(result, [0, "B"]); }); it("should handle zero with object output", () => { const result = filesize(0, { output: "object" }); assert.deepStrictEqual(result, { value: 0, symbol: "B", exponent: 0, unit: "B" }); }); }); describe("Standards", () => { it("should use IEC standard", () => { assert.strictEqual(filesize(1024, { standard: "iec" }), "1 KiB"); assert.strictEqual(filesize(1048576, { standard: "iec" }), "1 MiB"); }); it("should use JEDEC standard", () => { assert.strictEqual(filesize(1024, { standard: "jedec" }), "1 KB"); assert.strictEqual(filesize(1048576, { standard: "jedec" }), "1 MB"); }); it("should use SI standard", () => { assert.strictEqual(filesize(1000, { standard: "si" }), "1 kB"); assert.strictEqual(filesize(1000000, { standard: "si" }), "1 MB"); }); }); describe("Output formats", () => { it("should return string by default", () => { const result = filesize(1000); assert.strictEqual(typeof result, "string"); assert.strictEqual(result, "1 kB"); }); it('should return array when output is "array"', () => { const result = filesize(1000, { output: "array" }); assert(Array.isArray(result)); assert.strictEqual(result[0], 1); assert.strictEqual(result[1], "kB"); }); it('should return object when output is "object"', () => { const result = filesize(1000, { output: "object" }); assert.strictEqual(typeof result, "object"); assert.strictEqual(result.value, 1); assert.strictEqual(result.symbol, "kB"); assert.strictEqual(result.exponent, 1); assert.strictEqual(result.unit, "kB"); }); it('should return exponent when output is "exponent"', () => { const result = filesize(1000, { output: "exponent" }); assert.strictEqual(typeof result, "number"); assert.strictEqual(result, 1); }); it("should match the object exponent when rounding overflows the unit", () => { // 999999 renders as "1 MB", so the exponent is 2, not 1 assert.strictEqual(filesize(999999, { output: "exponent" }), 2); assert.strictEqual( filesize(999999, { output: "exponent" }), filesize(999999, { output: "object" }).exponent, ); // IEC: 1048575 renders as "1 MiB" assert.strictEqual(filesize(1048575, { standard: "iec", output: "exponent" }), 2); }); it("should match the object exponent when bits auto-increment the unit", () => { // 125000 bytes = 1 Mbit, so the exponent is 2, not 1 assert.strictEqual(filesize(125000, { bits: true, output: "exponent" }), 2); assert.strictEqual( filesize(125000, { bits: true, output: "exponent" }), filesize(125000, { bits: true, output: "object" }).exponent, ); }); }); describe("Rounding", () => { it("should round to specified decimal places", () => { assert.strictEqual(filesize(1536, { round: 1 }), "1.5 kB"); assert.strictEqual(filesize(1536, { round: 0 }), "2 kB"); assert.strictEqual(filesize(1536, { round: 3 }), "1.536 kB"); }); it("should use different rounding methods", () => { assert.strictEqual(filesize(1536, { roundingMethod: "floor" }), "1.53 kB"); assert.strictEqual(filesize(1536, { roundingMethod: "ceil" }), "1.54 kB"); assert.strictEqual(filesize(1536, { roundingMethod: "round" }), "1.54 kB"); }); }); describe("Padding", () => { it("should pad decimal places when pad is true", () => { assert.strictEqual(filesize(1000, { pad: true, round: 2 }), "1.00 kB"); assert.strictEqual(filesize(1536, { pad: true, round: 1 }), "1.5 kB"); }); it("should pad decimal places for negative numbers", () => { assert.strictEqual(filesize(-1000, { pad: true, round: 2 }), "-1.00 kB"); assert.strictEqual(filesize(-1536, { pad: true, round: 2 }), "-1.54 kB"); assert.strictEqual(filesize(-1024, { pad: true, round: 3 }), "-1.024 kB"); }); it("should pad decimal places with locale formatting for negative numbers", () => { assert.strictEqual(filesize(-1536, { locale: "de", pad: true, round: 2 }), "-1,54 kB"); assert.strictEqual(filesize(-1000, { locale: "en-US", pad: true, round: 2 }), "-1.00 kB"); }); it("should pad decimal places with locale formatting and grouping separators", () => { // German locale: comma as decimal separator, dot as grouping separator // Verify comma is correctly detected as decimal separator (not grouping separator) assert.strictEqual(filesize(1234567, { locale: "de-DE", pad: true, round: 2 }), "1,23 MB"); // Padding with German locale (3 decimal places) assert.strictEqual(filesize(1536, { locale: "de-DE", pad: true, round: 3 }), "1,536 kB"); // German locale with padding that adds decimal places assert.strictEqual(filesize(1536, { locale: "de", pad: true, round: 4 }), "1,5360 kB"); // German locale with grouping separator in integer part (1.234.567,89 kB) assert.strictEqual( filesize(1234567890, { locale: "de", exponent: 1, pad: true, round: 2 }), "1.234.567,89 kB", ); // English locale with larger numbers assert.strictEqual(filesize(1234567, { locale: "en-US", pad: true, round: 2 }), "1.23 MB"); assert.strictEqual(filesize(1234567890, { locale: "en-US", pad: true, round: 2 }), "1.23 GB"); }); it("should keep every digit group and pad when an integer value has grouping separators", () => { // en: comma grouping with no decimal in the raw value. Previously the // trailing grouping comma was mistaken for the decimal separator, which // dropped digit groups and skipped padding. assert.strictEqual( filesize(1234500e24, { locale: "en", pad: true, round: 2 }), "1,234,500.00 YB", ); assert.strictEqual(filesize(1000e24, { locale: "en", pad: true, round: 2 }), "1,000.00 YB"); }); }); describe("Custom separators and spacers", () => { it("should use custom decimal separator", () => { assert.strictEqual(filesize(1536, { separator: "," }), "1,54 kB"); }); it("should use custom spacer between value and unit", () => { assert.strictEqual(filesize(1000, { spacer: "_" }), "1_kB"); assert.strictEqual(filesize(1000, { spacer: "" }), "1kB"); }); }); describe("Custom symbols", () => { it("should use custom symbols when provided", () => { const customSymbols = { kB: "kilobyte" }; assert.strictEqual(filesize(1000, { symbols: customSymbols }), "1 kilobyte"); }); }); describe("Locale formatting", () => { it("should format using system locale when locale is true", () => { const result = filesize(1000, { locale: true }); assert(typeof result === "string"); assert(result.includes("kB")); }); it("should format using specific locale", () => { const result = filesize(1536, { locale: "en-US" }); assert(typeof result === "string"); assert(result.includes("1.5")); }); }); describe("Full form names", () => { it("should use full form names when fullform is true", () => { assert.strictEqual(filesize(1000, { fullform: true }), "1 kilobyte"); assert.strictEqual(filesize(2000, { fullform: true }), "2 kilobytes"); assert.strictEqual(filesize(1, { fullform: true }), "1 byte"); }); it("should use custom fullforms when provided", () => { const customFullforms = ["", "thousand-byte"]; assert.strictEqual( filesize(1000, { fullform: true, fullforms: customFullforms }), "1 thousand-byte", ); }); it("should keep the singular fullform name for negative one-magnitude values", () => { assert.strictEqual(filesize(-1, { fullform: true }), "-1 byte"); assert.strictEqual(filesize(-0.125, { bits: true, fullform: true }), "-1 bit"); }); it("should keep the plural fullform name for negative values", () => { assert.strictEqual(filesize(-2, { fullform: true }), "-2 bytes"); assert.strictEqual(filesize(-0.25, { bits: true, fullform: true }), "-2 bits"); }); it("should handle bits fullform singular", () => { assert.strictEqual(filesize(0.125, { bits: true, fullform: true }), "1 bit"); }); it("should handle bits fullform plural", () => { assert.strictEqual(filesize(0.25, { bits: true, fullform: true }), "2 bits"); }); it("should handle fullform with precision (string value path)", () => { assert.strictEqual( filesize(1058223158, { standard: "iec", precision: 3, fullform: true }), "0.990 gibibytes", ); assert.strictEqual(filesize(1234567890, { precision: 2, fullform: true }), "1.2 gigabytes"); }); it("should keep the plural fullform when a comma decimal separator is used", () => { assert.strictEqual(filesize(1500000, { fullform: true, separator: "," }), "1,5 megabytes"); assert.strictEqual(filesize(1500000, { fullform: true, locale: "de-DE" }), "1,5 megabytes"); }); }); describe("Base and exponent options", () => { it("should use base 2 calculations", () => { assert.strictEqual(filesize(1024, { base: 2 }), "1 KiB"); }); it("should use base 10 calculations", () => { assert.strictEqual(filesize(1000, { base: 10 }), "1 kB"); }); it("should use forced exponent", () => { assert.strictEqual(filesize(1024, { exponent: 0 }), "1024 B"); assert.strictEqual(filesize(1024, { exponent: 2 }), "0 MB"); }); }); describe("Precision option", () => { it("should use specified precision", () => { assert.strictEqual(filesize(1536, { precision: 3 }), "1.54 kB"); assert.strictEqual(filesize(1536000, { precision: 2 }), "1.5 MB"); }); it("should remove scientific notation from precision results", () => { // Test cases where toPrecision would normally produce scientific notation // but our implementation removes it by splitting on "e" // Very large numbers that would produce scientific notation assert.strictEqual(filesize(1234567890123, { precision: 2 }), "1.2 TB"); assert.strictEqual(filesize(9876543210987, { precision: 3 }), "9.88 TB"); // Test with different precision values on large numbers assert.strictEqual(filesize(1e15, { precision: 1 }), "1 PB"); assert.strictEqual(filesize(1e15, { precision: 2 }), "1.0 PB"); assert.strictEqual(filesize(1e15, { precision: 3 }), "1.00 PB"); }); it("should handle precision with very small numbers", () => { // Very small numbers (less than 1 byte) assert.strictEqual(filesize(0.001, { precision: 1 }), "0 B"); assert.strictEqual(filesize(0.1, { precision: 2 }), "0.0 B"); assert.strictEqual(filesize(0.5, { precision: 1 }), "1 B"); }); it("should handle precision with numbers that would produce e+ notation", () => { // Numbers that produce e+ notation with toPrecision const largeNum = 1234567890; assert.strictEqual(filesize(largeNum, { precision: 2 }), "1.2 GB"); assert.strictEqual(filesize(largeNum, { precision: 3 }), "1.23 GB"); assert.strictEqual(filesize(largeNum, { precision: 1 }), "1 GB"); }); it("should handle precision with numbers that would produce e- notation", () => { // Very small decimal numbers that would produce e- notation assert.strictEqual(filesize(0.0001, { precision: 1 }), "0 B"); assert.strictEqual(filesize(0.00001, { precision: 2 }), "0.0 B"); }); it("should work with precision and different standards", () => { const largeNum = 1234567890; assert.strictEqual(filesize(largeNum, { precision: 2, standard: "iec" }), "1.1 GiB"); assert.strictEqual(filesize(largeNum, { precision: 2, standard: "jedec" }), "1.1 GB"); assert.strictEqual(filesize(largeNum, { precision: 2, standard: "si" }), "1.2 GB"); }); it("should work with precision and bits option", () => { const largeNum = 1234567890; assert.strictEqual(filesize(largeNum, { precision: 2, bits: true }), "9.9 Gbit"); assert.strictEqual(filesize(largeNum, { precision: 3, bits: true }), "9.88 Gbit"); }); it("should work with precision and array output", () => { const result = filesize(1234567890, { precision: 2, output: "array" }); assert(Array.isArray(result)); assert.strictEqual(result[0], "1.2"); assert.strictEqual(result[1], "GB"); }); it("should work with precision and object output", () => { const result = filesize(1234567890, { precision: 2, output: "object" }); assert.strictEqual(typeof result, "object"); assert.strictEqual(result.value, "1.2"); assert.strictEqual(result.symbol, "GB"); }); it("should handle precision with extremely large numbers", () => { // Test with numbers that exceed normal exponent range const extremeNumber = Math.pow(1024, 15); const result = filesize(extremeNumber, { precision: 2 }); assert(typeof result === "string"); // Note: For extremely large numbers that exceed JavaScript's normal number range, // the number itself may be in scientific notation, which is expected behavior assert(result.includes("YB") || result.includes("YiB")); }); it("should handle precision edge cases", () => { // Test precision with zero assert.strictEqual(filesize(0, { precision: 3 }), "0.00 B"); // Test precision with exactly 1 byte assert.strictEqual(filesize(1, { precision: 2 }), "1.0 B"); // Test precision with negative numbers assert.strictEqual(filesize(-1234567890, { precision: 2 }), "-1.2 GB"); }); it("should keep precision trailing zeros for negative values", () => { // A negative value must carry the same significant digits as its positive counterpart assert.strictEqual(filesize(1500, { precision: 3 }), "1.50 kB"); assert.strictEqual(filesize(-1500, { precision: 3 }), "-1.50 kB"); assert.strictEqual(filesize(-1000, { precision: 3 }), "-1.00 kB"); assert.strictEqual(filesize(-1, { precision: 3 }), "-1.00 B"); assert.deepStrictEqual(filesize(-1500, { precision: 3, output: "array" }), ["-1.50", "kB"]); assert.strictEqual(filesize(-1500, { precision: 3, output: "object" }).value, "-1.50"); }); it("should ensure no scientific notation in any precision result", () => { // Test a range of numbers that would normally produce scientific notation from toPrecision // but should have it removed by our implementation const testNumbers = [1e3, 1e6, 1e9, 1e12, 1234567890123456, Math.pow(10, 10)]; testNumbers.forEach((num) => { [1, 2, 3, 4].forEach((precision) => { const result = filesize(num, { precision }); // Result should not contain scientific notation markers when using toPrecision // Note: We exclude extremely large numbers that naturally contain 'e' in JavaScript if (!num.toString().includes("e")) { assert( !result.includes("e"), `Result "${result}" contains 'e' for number ${num} with precision ${precision}`, ); assert( !result.includes("E"), `Result "${result}" contains 'E' for number ${num} with precision ${precision}`, ); } }); }); }); it("should handle precision with base 2 when scientific notation is produced (regular number)", () => { // Test case that triggers base === 2 path in line 168 precision handling // We need a number that will produce scientific notation with toPrecision // and uses base 2 (IEC standard) const testNumber = 999999999999999; // Large number that triggers scientific notation const result = filesize(testNumber, { precision: 1, standard: "iec" }); assert(typeof result === "string"); assert(!result.includes("e")); assert(!result.includes("E")); // Should use IEC units (base 2) assert(result.includes("TiB") || result.includes("PiB")); }); it("should handle precision with base 2 when scientific notation is produced (BigInt)", () => { // Test case that triggers base === 2 path in line 168 precision handling using BigInt // This ensures both number types are tested for base 2 path const testNumber = BigInt("999999999999999"); // Large BigInt that triggers scientific notation const result = filesize(testNumber, { precision: 1, standard: "iec" }); assert(typeof result === "string"); assert(!result.includes("e")); assert(!result.includes("E")); // Should use IEC units (base 2) assert(result.includes("TiB") || result.includes("PiB")); }); it("should handle precision with base 10 when scientific notation is produced (regular number)", () => { // Test case that triggers base === 10 path in line 168 precision handling // We need a number that will produce scientific notation with toPrecision // and uses base 10 (SI/JEDEC standard) const testNumber = 999999999999999; // Large number that triggers scientific notation const result = filesize(testNumber, { precision: 1, standard: "jedec" }); assert(typeof result === "string"); assert(!result.includes("e")); assert(!result.includes("E")); // Should use JEDEC units (base 10 logic) assert(result.includes("TB") || result.includes("PB")); }); it("should handle precision with base 10 when scientific notation is produced (BigInt)", () => { // Test case that triggers base === 10 path in line 168 precision handling using BigInt // This ensures both number types are tested for base 10 path const testNumber = BigInt("999999999999999"); // Large BigInt that triggers scientific notation const result = filesize(testNumber, { precision: 1, standard: "jedec" }); assert(typeof result === "string"); assert(!result.includes("e")); assert(!result.includes("E")); // Should use JEDEC units (base 10 logic) assert(result.includes("TB") || result.includes("PB")); }); it("should handle precision with YiB-level numbers where e >= 8 (regular number)", () => { // Test case that covers line 166 when e >= 8 (YiB level) using regular number // This triggers result[0].includes(E) but e < 8 is false, so increment logic is skipped const yibNumber = Math.pow(1024, 8); // 1 YiB in bytes using regular number const result = filesize(yibNumber, { precision: 1, standard: "iec" }); assert(typeof result === "string"); // For extremely large regular numbers, some scientific notation may be expected assert(result.includes("YiB")); }); it("should handle precision with YiB-level numbers where e >= 8 (BigInt)", () => { // Test case that covers line 166 when e >= 8 (YiB level) using BigInt // This triggers result[0].includes(E) but e < 8 is false, so increment logic is skipped // YiB is at exponent 8, so we need a number large enough to reach this level const yibNumber = BigInt(1024) ** BigInt(8); // 1 YiB in bytes using BigInt const result = filesize(yibNumber, { precision: 1, standard: "iec" }); assert(typeof result === "string"); assert(!result.includes("e")); assert(!result.includes("E")); assert(result.includes("YiB")); }); it("should handle precision with extremely large numbers that exceed YiB (regular number)", () => { // Test case for numbers that are larger than YiB using regular number // This ensures we test the e >= 8 condition thoroughly with regular numbers const extremeNumber = Math.pow(1024, 8) * 1000; // 1000 YiB using regular number const result = filesize(extremeNumber, { precision: 2, standard: "iec" }); assert(typeof result === "string"); // For extremely large regular numbers, some scientific notation may be expected assert(result.includes("YiB")); }); it("should handle precision with extremely large numbers that exceed YiB (BigInt)", () => { // Test case for numbers that are larger than YiB using BigInt // This ensures we test the e >= 8 condition thoroughly with BigInt const extremeNumber = BigInt(1024) ** BigInt(8) * BigInt(1000); // 1000 YiB using BigInt const result = filesize(extremeNumber, { precision: 2, standard: "iec" }); assert(typeof result === "string"); // For extremely large BigInt numbers, the result should still use YiB units // Some scientific notation in the final result may be acceptable for such large numbers assert(result.includes("YiB")); }); it("should handle precision with specific test case for GiB values", () => { // Test specific precision case: filesize(1058223158, {base: 2, standard: 'iec', precision: 3}) // This should output '0.990 GiB' assert.strictEqual( filesize(1058223158, { base: 2, standard: "iec", precision: 3 }), "0.990 GiB", ); }); it("should return string value in object output when precision > 0", () => { const result = filesize(1234567890, { precision: 2, output: "object" }); assert.strictEqual(typeof result.value, "string"); assert.strictEqual(result.value, "1.2"); }); it("should skip padding when round is 0", () => { assert.strictEqual(filesize(1000, { pad: true, round: 0 }), "1 kB"); }); it("should fall back to JEDEC default for unknown standard", () => { assert.strictEqual(filesize(1024, { standard: "foobar" }), "1.02 kB"); }); }); describe("Edge cases", () => { it("should handle very large numbers", () => { const largeNumber = Math.pow(1024, 8); const result = filesize(largeNumber); assert(typeof result === "string"); assert(result.includes("YB") || result.includes("YiB")); }); it("should handle BigInt input", () => { const result = filesize(BigInt(1024), { standard: "jedec" }); assert.strictEqual(result, "1 KB"); }); it("should handle numbers exceeding maximum exponent", () => { const hugeNumber = Math.pow(1024, 10); const result = filesize(hugeNumber); assert(typeof result === "string"); }); it("should handle numbers exceeding lookup table size (decimal)", () => { // Test number requiring Math.pow fallback for decimal powers const hugeNumber = Math.pow(1000, 15); // Exceeds DECIMAL_POWERS array const result = filesize(hugeNumber, { base: 10 }); assert.ok(result.includes("YB")); }); it("should handle numbers exceeding lookup table size (binary)", () => { // Test number requiring Math.pow fallback for binary powers const hugeNumber = Math.pow(2, 100); // Exceeds BINARY_POWERS array const result = filesize(hugeNumber, { base: 2 }); assert.ok(result.includes("YiB")); }); it("should handle precision with numbers exceeding lookup table", () => { const hugeNumber = Math.pow(1000, 12); const result = filesize(hugeNumber, { precision: 2, base: 10 }); assert.ok(result.includes("YB")); }); it("should handle precision with round=0 and scientific notation", () => { // Test case where p === 1 in the precision recalculation branch const result = filesize(1000000000000000000, { precision: 1, round: 0 }); assert.ok(typeof result === "string"); }); it("should cover p===1 branch in precision recalculation", () => { // Try to force the p===1 branch in the precision recalculation // This needs a very specific edge case where scientific notation is produced // and after increment e=0 or round=0 makes p=1 const result = filesize(0.0001, { precision: 20, round: 0 }); assert.ok(typeof result === "string"); }); it("should trigger precision recalculation with bytes and round=0", () => { // Another attempt: very small number that might produce scientific notation in bytes const result = filesize(0.000000000000001, { precision: 30, round: 0 }); assert.ok(typeof result === "string"); }); it("should cover p===1 branch with input=1 and extreme precision", () => { // When input=1, e=0, so p=1. High precision might trigger scientific notation // and the recalculation with p===1 branch const result = filesize(1, { precision: 50 }); assert.ok(typeof result === "string"); }); it("should attempt to trigger the elusive p===1 branch in recalculation", () => { // Numbers that would produce scientific notation with precision=1 // When e=0 (bytes), p=1, and toPrecision(1) might produce "1e+1" etc. const result = filesize(10, { precision: 1, round: 0 }); assert.ok(typeof result === "string"); }); it("should handle extremely large numbers with precision adjustment", () => { const extremeNumber = Math.pow(1024, 15); // Much larger than supported exponent const result = filesize(extremeNumber, { precision: 3 }); assert(typeof result === "string"); assert(result.includes("YB") || result.includes("YiB")); }); it("should handle bit conversion auto-increment near boundary", () => { // Test edge case where bits conversion causes auto-increment to next unit const result = filesize(127.5, { bits: true }); // Should auto-increment assert(typeof result === "string"); }); it("should handle result equal to ceil boundary for auto-increment", () => { // Test case where result equals ceil and triggers increment const result = filesize(999.5, { round: 0 }); // Should round to 1000 and increment assert(typeof result === "string"); }); it("should handle padding with custom separator edge cases", () => { // Test padding when separator is found via regex const result = filesize(1536, { pad: true, round: 2, locale: "de-DE" }); assert(typeof result === "string"); }); it("should handle NaN exponent edge case", () => { const result = filesize(1000, { exponent: NaN }); assert.strictEqual(result, "1 kB"); }); it("should handle NaN exponent with precision", () => { // NaN exponent should behave like auto (-1) and allow scientific notation normalization const result = filesize(1e15, { exponent: NaN, precision: 2 }); assert(typeof result === "string"); assert(!result.includes("e"), "Should not contain scientific notation"); }); it("should handle NaN exponent with auto-increment", () => { // NaN exponent should allow auto-increment when result equals ceil (1024) const result = filesize(1024, { exponent: NaN, standard: "iec" }); assert.strictEqual(result, "1 KiB"); }); it("should clamp an out-of-range negative exponent to 0 instead of returning NaN", () => { // Only -1 is the documented "auto" sentinel. Any other negative exponent // indexed the power lookup tables out of bounds and produced "NaN undefined". assert.strictEqual(filesize(1024, { exponent: -2 }), "1024 B"); assert.strictEqual(filesize(1024, { exponent: -100 }), "1024 B"); }); it("should clamp a negative exponent to 0 for object output", () => { const result = filesize(1024, { exponent: -3, output: "object" }); assert.deepStrictEqual(result, { value: 1024, symbol: "B", exponent: 0, unit: "B" }); }); it("should clamp a negative exponent to 0 for array output", () => { const result = filesize(1024, { exponent: -3, output: "array" }); assert.deepStrictEqual(result, [1024, "B"]); }); }); describe("Error handling", () => { it("should throw TypeError for invalid number input", () => { assert.throws(() => filesize("invalid"), TypeError); assert.throws(() => filesize(NaN), TypeError); assert.throws(() => filesize({}), TypeError); }); it("should throw TypeError for invalid rounding method", () => { assert.throws(() => filesize(1024, { roundingMethod: "invalid" }), TypeError); assert.throws(() => filesize(1024, { roundingMethod: "nonexistent" }), TypeError); }); }); describe("Input type handling", () => { describe("Number input", () => { describe("filesize() with number input", () => { it("should handle integer numbers", () => { assert.strictEqual(filesize(1024), "1.02 kB"); assert.strictEqual(filesize(0), "0 B"); assert.strictEqual(filesize(-1024), "-1.02 kB"); }); it("should handle floating point numbers", () => { assert.strictEqual(filesize(1536.5), "1.54 kB"); assert.strictEqual(filesize(0.5), "1 B"); }); it("should handle very large numbers", () => { const largeNumber = Number.MAX_SAFE_INTEGER; const result = filesize(largeNumber); assert(typeof result === "string"); }); }); describe("partial() with number input", () => { it("should handle integer numbers", () => { const formatIEC = partial({ standard: "iec" }); assert.strictEqual(formatIEC(1024), "1 KiB"); assert.strictEqual(formatIEC(0), "0 B"); assert.strictEqual(formatIEC(-1024), "-1 KiB"); }); it("should handle floating point numbers", () => { const formatRounded = partial({ round: 1 }); assert.strictEqual(formatRounded(1536.7), "1.5 kB"); assert.strictEqual(formatRounded(0.5), "1 B"); }); it("should handle very large numbers", () => { const formatBits = partial({ bits: true }); const largeNumber = Number.MAX_SAFE_INTEGER; const result = formatBits(largeNumber); assert(typeof result === "string"); }); }); }); describe("String input", () => { describe("filesize() with string input", () => { it("should handle valid integer string", () => { assert.strictEqual(filesize("1024"), "1.02 kB"); assert.strictEqual(filesize("0"), "0 B"); assert.strictEqual(filesize("-1024"), "-1.02 kB"); }); it("should handle valid float string", () => { assert.strictEqual(filesize("1536.5"), "1.54 kB"); assert.strictEqual(filesize("0.5"), "1 B"); }); it("should handle scientific notation string", () => { assert.strictEqual(filesize("1e3"), "1 kB"); assert.strictEqual(filesize("1.024e3"), "1.02 kB"); }); it("should handle string with leading/trailing whitespace", () => { assert.strictEqual(filesize(" 1024 "), "1.02 kB"); assert.strictEqual(filesize("\t1024\n"), "1.02 kB"); }); it("should work with string input and different options", () => { assert.strictEqual(filesize("1024", { standard: "iec" }), "1 KiB"); assert.strictEqual(filesize("1000", { bits: true }), "8 kbit"); assert.strictEqual(filesize("1024", { output: "array" })[0], 1.02); }); }); describe("partial() with string input", () => { it("should handle valid integer string", () => { const formatJEDEC = partial({ standard: "jedec" }); assert.strictEqual(formatJEDEC("1024"), "1 KB"); assert.strictEqual(formatJEDEC("0"), "0 B"); assert.strictEqual(formatJEDEC("-1024"), "-1 KB"); }); it("should handle valid float string", () => { const formatPadded = partial({ pad: true, round: 2 }); assert.strictEqual(formatPadded("1536.5"), "1.54 kB"); assert.strictEqual(formatPadded("0.5"), "1.00 B"); }); it("should handle scientific notation string", () => { const formatBits = partial({ bits: true }); assert.strictEqual(formatBits("1e3"), "8 kbit"); assert.strictEqual(formatBits("1.024e3"), "8.19 kbit"); }); it("should handle string with leading/trailing whitespace", () => { const formatFullForm = partial({ fullform: true, standard: "iec" }); assert.strictEqual(formatFullForm(" 1024 "), "1 kibibyte"); assert.strictEqual(formatFullForm("\t2048\n"), "2 kibibytes"); }); it("should work with string input and complex options", () => { const formatComplex = partial({ standard: "si", round: 1, spacer: "_", output: "array", }); const result = formatComplex("1000"); assert(Array.isArray(result)); assert.strictEqual(result[0], 1); assert.strictEqual(result[1], "kB"); }); }); }); describe("BigInt input", () => { describe("filesize() with BigInt input", () => { it("should handle regular BigInt values", () => { assert.strictEqual(filesize(BigInt(1024)), "1.02 kB"); assert.strictEqual(filesize(BigInt(0)), "0 B"); assert.strictEqual(filesize(BigInt(-1024)), "-1.02 kB"); }); it("should handle large BigInt values", () => { const largeBigInt = BigInt("9007199254740992"); // 2^53 const result = filesize(largeBigInt); assert(typeof result === "string"); assert(result.includes("PB") || result.includes("PiB")); }); it("should handle very large BigInt values", () => { const veryLargeBigInt = BigInt("1208925819614629174706176"); // 1024^8 const result = filesize(veryLargeBigInt); assert(typeof result === "string"); assert(result.includes("YB") || result.includes("YiB")); }); it("should work with BigInt input and different options", () => { assert.strictEqual(filesize(BigInt(1024), { standard: "jedec" }), "1 KB"); assert.strictEqual(filesize(BigInt(1000), { bits: true }), "8 kbit"); const objectResult = filesize(BigInt(1024), { output: "object" }); assert.strictEqual(objectResult.value, 1.02); assert.strictEqual(objectResult.symbol, "kB"); }); }); describe("partial() with BigInt input", () => { it("should handle regular BigInt values", () => { const formatCustomSymbols = partial({ symbols: { KiB: "kibibyte" }, standard: "iec" }); assert.strictEqual(formatCustomSymbols(BigInt(1024)), "1 kibibyte"); assert.strictEqual(formatCustomSymbols(BigInt(0)), "0 B"); assert.strictEqual(formatCustomSymbols(BigInt(-1024)), "-1 kibibyte"); }); it("should handle large BigInt values", () => { const formatPrecision = partial({ precision: 3 }); const largeBigInt = BigInt("9007199254740992"); // 2^53 const result = formatPrecision(largeBigInt); assert(typeof result === "string"); assert(result.includes("PB") || result.includes("PiB")); }); it("should handle very large BigInt values", () => { const formatExponent = partial({ exponent: -1 }); // Auto-select exponent const veryLargeBigInt = BigInt("1208925819614629174706176"); // 1024^8 const result = formatExponent(veryLargeBigInt); assert(typeof result === "string"); assert(result.includes("YB") || result.includes("YiB")); }); it("should work with BigInt input and different output formats", () => { const formatObject = partial({ output: "object", standard: "iec" }); const objectResult = formatObject(BigInt(1024)); assert.strictEqual(typeof objectResult, "object"); assert.strictEqual(objectResult.value, 1); assert.strictEqual(objectResult.symbol, "KiB"); const formatExponentOnly = partial({ output: "exponent" }); const exponentResult = formatExponentOnly(BigInt(1024)); assert.strictEqual(typeof exponentResult, "number"); assert.strictEqual(exponentResult, 1); }); it("should work with BigInt input and bits option", () => { const formatBits = partial({ bits: true, round: 1 }); assert.strictEqual(formatBits(BigInt(1024)), "8.2 kbit"); assert.strictEqual(formatBits(BigInt(1000)), "8 kbit"); }); it("should work with BigInt input and locale formatting", () => { const formatLocale = partial({ locale: "en-US", round: 2 }); const result = formatLocale(BigInt(1536)); assert(typeof result === "string"); assert(result.includes("1.5") || result.includes("1.54")); }); }); }); }); }); describe("partial", () => { describe("Basic functionality", () => { it("should create a function with preset options", () => { const formatKiB = partial({ standard: "iec" }); assert.strictEqual(typeof formatKiB, "function"); assert.strictEqual(formatKiB(1024), "1 KiB"); }); it("should preserve all options in partial application", () => { const formatWithOptions = partial({ round: 1, standard: "iec", spacer: "_", }); assert.strictEqual(formatWithOptions(1536), "1.5_KiB"); }); }); describe("Options inheritance", () => { it("should apply bits option from partial", () => { const formatBits = partial({ bits: true }); assert.strictEqual(formatBits(1000), "8 kbit"); }); it("should apply pad option from partial", () => { const formatPadded = partial({ pad: true, round: 2 }); assert.strictEqual(formatPadded(1000), "1.00 kB"); }); it("should apply output format from partial", () => { const formatArray = partial({ output: "array" }); const result = formatArray(1000); assert(Array.isArray(result)); assert.strictEqual(result[0], 1); assert.strictEqual(result[1], "kB"); }); it("should apply custom symbols from partial", () => { const formatCustom = partial({ symbols: { kB: "kilobyte" } }); assert.strictEqual(formatCustom(1000), "1 kilobyte"); }); }); describe("Multiple partial functions", () => { it("should create independent partial functions", () => { const formatIEC = partial({ standard: "iec" }); const formatJEDEC = partial({ standard: "jedec" }); assert.strictEqual(formatIEC(1024), "1 KiB"); assert.strictEqual(formatJEDEC(1024), "1 KB"); }); it("should handle complex option combinations", () => { const formatComplex = partial({ bits: true, standard: "iec", round: 1, fullform: true, }); assert.strictEqual(formatComplex(1024), "8 kibibits"); }); }); describe("Precision with partial", () => { it("should apply precision option from partial", () => { const formatPrecision = partial({ precision: 2 }); assert.strictEqual(formatPrecision(1234567890), "1.2 GB"); assert.strictEqual(formatPrecision(9876543210), "9.9 GB"); }); it("should remove scientific notation in partial functions", () => { const formatWithPrecision = partial({ precision: 3 }); const largeNum = 1234567890123; const result = formatWithPrecision(largeNum); assert(!result.includes("e")); assert(!result.includes("E")); assert.strictEqual(result, "1.23 TB"); }); it("should work with precision and other options combined", () => { const formatComplex = partial({ precision: 2, standard: "iec", bits: true, }); const result = formatComplex(1234567890); assert(!result.includes("e")); assert(!result.includes("E")); assert.strictEqual(result, "9.2 Gibit"); }); }); describe("Default behavior", () => { it("should work with no options provided", () => { const defaultFormat = partial(); assert.strictEqual(typeof defaultFormat, "function"); assert.strictEqual(defaultFormat(1000), "1 kB"); }); it("should work with empty options object", () => { const emptyOptions = partial({}); assert.strictEqual(emptyOptions(1000), "1 kB"); }); }); describe("Immutability", () => { it("should not be affected by mutations to top-level primitive options", () => { const opts = { standard: "iec", round: 1 }; const format = partial(opts); // First call should work normally assert.strictEqual(format(1536), "1.5 KiB"); // Mutate the original options object opts.standard = "jedec"; opts.round = 0; // The partial function should still use the destructured primitive values assert.strictEqual(format(1536), "1.5 KiB"); }); it("should not affect other partial functions when modifying one", () => { const format1 = partial({ symbols: { kB: "first_kB" } }); const format2 = partial({ symbols: { kB: "second_kB" } }); // Both should work independently assert.strictEqual(format1(1000), "1 first_kB"); assert.strictEqual(format2(1000), "1 second_kB"); }); it("should not be affected by mutations to localeOptions reference", () => { const opts = { localeOptions: { useGrouping: true } }; const format = partial(opts); assert.strictEqual(typeof format(1234567), "string"); opts.localeOptions.useGrouping = false; assert.strictEqual(typeof format(1234567), "string"); }); it("should not be affected by mutations to symbols reference", () => { const opts = { symbols: { kB: "original_kB" } }; const format = partial(opts); assert.strictEqual(format(1000), "1 original_kB"); opts.symbols.kB = "mutated_kB"; assert.strictEqual(format(1000), "1 original_kB"); }); it("should not be affected by mutations to fullforms reference", () => { const opts = { fullforms: ["custom"], fullform: true }; const format = partial(opts); assert.strictEqual(format(1), "1 custom"); opts.fullforms[0] = "mutated"; assert.strictEqual(format(1), "1 custom"); }); }); }); describe("partial structuredClone", () => { it("should use structuredClone when available for localeOptions", () => { const opts = { localeOptions: { minimumFractionDigits: 2 } }; const format = partial(opts); const result = format(1234.5); assert(typeof result === "string"); // Mutate original - should not affect partial opts.localeOptions.minimumFractionDigits = 5; const result2 = format(1234.5); assert(typeof result2 === "string"); }); it("should use structuredClone when available for symbols", () => { const opts = { symbols: { kB: "custom_kB" } }; const format = partial(opts); assert.strictEqual(format(1000), "1 custom_kB"); opts.symbols.kB = "mutated_kB"; assert.strictEqual(format(1000), "1 custom_kB"); }); it("should use structuredClone when available for fullforms", () => { const opts = { fullforms: ["my_byte"], fullform: true }; const format = partial(opts); assert.strictEqual(format(1), "1 my_byte"); opts.fullforms[0] = "mutated"; assert.strictEqual(format(1), "1 my_byte"); }); it("should maintain deep independence between partial instances", () => { const opts1 = { symbols: { kB: "first" } }; const opts2 = { symbols: { kB: "second" } }; const format1 = partial(opts1); const format2 = partial(opts2); assert.strictEqual(format1(1000), "1 first"); assert.strictEqual(format2(1000), "1 second"); opts1.symbols.kB = "mutated_first"; opts2.symbols.kB = "mutated_second"; assert.strictEqual(format1(1000), "1 first"); assert.strictEqual(format2(1000), "1 second"); }); });