mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Add months support, return NaN for invalid inputs, comprehensive tests
- Added month/months/mo unit support (30.4375 days average) - Return NaN instead of undefined for invalid string inputs - Throw errors for NaN/Infinity number inputs - Complete test coverage matching vercel/ms library (133 tests) - Updated TypeScript types with string literal autocomplete - All test cases from vercel/ms parse, format, and index tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
93
packages/bun-types/bun.d.ts
vendored
93
packages/bun-types/bun.d.ts
vendored
@@ -5209,9 +5209,7 @@ declare module "bun" {
|
||||
/**
|
||||
* Parse a time string and return milliseconds, or format milliseconds as a string.
|
||||
*
|
||||
* Compatible with the `ms` npm package.
|
||||
*
|
||||
* When used with a string literal in bundled code, this is inlined at compile-time.
|
||||
* Drop-in replacement for the `ms` npm package with compile-time inlining support.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
@@ -5220,17 +5218,98 @@ declare module "bun" {
|
||||
* Bun.ms("1m") // 60000
|
||||
* Bun.ms("5s") // 5000
|
||||
* Bun.ms("100ms") // 100
|
||||
* Bun.ms("1mo") // 2629800000
|
||||
* Bun.ms("1y") // 31557600000
|
||||
* Bun.ms("100") // 100
|
||||
* Bun.ms(60000) // "1m"
|
||||
* Bun.ms(60000, { long: true }) // "1 minute"
|
||||
* Bun.ms("invalid") // NaN
|
||||
* ```
|
||||
*
|
||||
* @param value - A string like "2d", "1h", "5m" or a number of milliseconds
|
||||
* @param options - Options for formatting (only applies when value is a number)
|
||||
* @returns milliseconds (if string input) or formatted string (if number input), or undefined if invalid string
|
||||
* Supports these units:
|
||||
* - `ms`, `millisecond`, `milliseconds`
|
||||
* - `s`, `sec`, `second`, `seconds`
|
||||
* - `m`, `min`, `minute`, `minutes`
|
||||
* - `h`, `hr`, `hour`, `hours`
|
||||
* - `d`, `day`, `days`
|
||||
* - `w`, `week`, `weeks`
|
||||
* - `mo`, `month`, `months`
|
||||
* - `y`, `yr`, `year`, `years`
|
||||
*
|
||||
* @param value - Time string to parse or milliseconds to format
|
||||
* @param options - Formatting options (when value is a number)
|
||||
* @returns Milliseconds (for string) or formatted string (for number). Returns NaN for invalid strings.
|
||||
*/
|
||||
function ms(value: string): number | undefined;
|
||||
function ms(
|
||||
value:
|
||||
| `${number}`
|
||||
| `${number}${
|
||||
| "ms"
|
||||
| "s"
|
||||
| "m"
|
||||
| "h"
|
||||
| "d"
|
||||
| "w"
|
||||
| "y"
|
||||
| "millisecond"
|
||||
| "milliseconds"
|
||||
| "msec"
|
||||
| "msecs"
|
||||
| "second"
|
||||
| "seconds"
|
||||
| "sec"
|
||||
| "secs"
|
||||
| "minute"
|
||||
| "minutes"
|
||||
| "min"
|
||||
| "mins"
|
||||
| "hour"
|
||||
| "hours"
|
||||
| "hr"
|
||||
| "hrs"
|
||||
| "day"
|
||||
| "days"
|
||||
| "week"
|
||||
| "weeks"
|
||||
| "month"
|
||||
| "months"
|
||||
| "mo"
|
||||
| "year"
|
||||
| "years"
|
||||
| "yr"
|
||||
| "yrs"}`
|
||||
| `${number} ${
|
||||
| "ms"
|
||||
| "millisecond"
|
||||
| "milliseconds"
|
||||
| "msec"
|
||||
| "msecs"
|
||||
| "second"
|
||||
| "seconds"
|
||||
| "sec"
|
||||
| "secs"
|
||||
| "minute"
|
||||
| "minutes"
|
||||
| "min"
|
||||
| "mins"
|
||||
| "hour"
|
||||
| "hours"
|
||||
| "hr"
|
||||
| "hrs"
|
||||
| "day"
|
||||
| "days"
|
||||
| "week"
|
||||
| "weeks"
|
||||
| "month"
|
||||
| "months"
|
||||
| "mo"
|
||||
| "year"
|
||||
| "years"
|
||||
| "yr"
|
||||
| "yrs"}`
|
||||
| string,
|
||||
options?: never,
|
||||
): number;
|
||||
function ms(value: number, options?: { long?: boolean }): string;
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,6 +72,13 @@ fn getMultiplier(unit: []const u8) ?f64 {
|
||||
return std.time.ms_per_day * 365.25;
|
||||
}
|
||||
|
||||
// Months (30.4375 days average)
|
||||
if (std.ascii.eqlIgnoreCase(unit, "months") or std.ascii.eqlIgnoreCase(unit, "month") or
|
||||
std.ascii.eqlIgnoreCase(unit, "mo"))
|
||||
{
|
||||
return std.time.ms_per_day * 30.4375;
|
||||
}
|
||||
|
||||
// Weeks
|
||||
if (std.ascii.eqlIgnoreCase(unit, "weeks") or std.ascii.eqlIgnoreCase(unit, "week") or
|
||||
std.ascii.eqlIgnoreCase(unit, "w"))
|
||||
@@ -125,7 +132,43 @@ fn getMultiplier(unit: []const u8) ?f64 {
|
||||
/// Format milliseconds to a human-readable string
|
||||
pub fn format(allocator: std.mem.Allocator, ms: f64, long: bool) ![]const u8 {
|
||||
const abs_ms = @abs(ms);
|
||||
const ms_per_year = std.time.ms_per_day * 365.25;
|
||||
const ms_per_month = std.time.ms_per_day * 30.4375;
|
||||
|
||||
// Years
|
||||
if (abs_ms >= ms_per_year) {
|
||||
const years = @round(ms / ms_per_year);
|
||||
const years_int = @as(i64, @intFromFloat(years));
|
||||
if (long) {
|
||||
const plural = abs_ms >= ms_per_year * 1.5;
|
||||
return std.fmt.allocPrint(allocator, "{d} year{s}", .{ years_int, if (plural) "s" else "" });
|
||||
}
|
||||
return std.fmt.allocPrint(allocator, "{d}y", .{years_int});
|
||||
}
|
||||
|
||||
// Months
|
||||
if (abs_ms >= ms_per_month) {
|
||||
const months = @round(ms / ms_per_month);
|
||||
const months_int = @as(i64, @intFromFloat(months));
|
||||
if (long) {
|
||||
const plural = abs_ms >= ms_per_month * 1.5;
|
||||
return std.fmt.allocPrint(allocator, "{d} month{s}", .{ months_int, if (plural) "s" else "" });
|
||||
}
|
||||
return std.fmt.allocPrint(allocator, "{d}mo", .{months_int});
|
||||
}
|
||||
|
||||
// Weeks
|
||||
if (abs_ms >= std.time.ms_per_week) {
|
||||
const weeks = @round(ms / std.time.ms_per_week);
|
||||
const weeks_int = @as(i64, @intFromFloat(weeks));
|
||||
if (long) {
|
||||
const plural = abs_ms >= std.time.ms_per_week * 1.5;
|
||||
return std.fmt.allocPrint(allocator, "{d} week{s}", .{ weeks_int, if (plural) "s" else "" });
|
||||
}
|
||||
return std.fmt.allocPrint(allocator, "{d}w", .{weeks_int});
|
||||
}
|
||||
|
||||
// Days
|
||||
if (abs_ms >= std.time.ms_per_day) {
|
||||
const days = @round(ms / std.time.ms_per_day);
|
||||
const days_int = @as(i64, @intFromFloat(days));
|
||||
@@ -136,6 +179,7 @@ pub fn format(allocator: std.mem.Allocator, ms: f64, long: bool) ![]const u8 {
|
||||
return std.fmt.allocPrint(allocator, "{d}d", .{days_int});
|
||||
}
|
||||
|
||||
// Hours
|
||||
if (abs_ms >= std.time.ms_per_hour) {
|
||||
const hours = @round(ms / std.time.ms_per_hour);
|
||||
const hours_int = @as(i64, @intFromFloat(hours));
|
||||
@@ -146,6 +190,7 @@ pub fn format(allocator: std.mem.Allocator, ms: f64, long: bool) ![]const u8 {
|
||||
return std.fmt.allocPrint(allocator, "{d}h", .{hours_int});
|
||||
}
|
||||
|
||||
// Minutes
|
||||
if (abs_ms >= std.time.ms_per_min) {
|
||||
const minutes = @round(ms / std.time.ms_per_min);
|
||||
const minutes_int = @as(i64, @intFromFloat(minutes));
|
||||
@@ -156,6 +201,7 @@ pub fn format(allocator: std.mem.Allocator, ms: f64, long: bool) ![]const u8 {
|
||||
return std.fmt.allocPrint(allocator, "{d}m", .{minutes_int});
|
||||
}
|
||||
|
||||
// Seconds
|
||||
if (abs_ms >= std.time.ms_per_s) {
|
||||
const seconds = @round(ms / std.time.ms_per_s);
|
||||
const seconds_int = @as(i64, @intFromFloat(seconds));
|
||||
@@ -166,6 +212,7 @@ pub fn format(allocator: std.mem.Allocator, ms: f64, long: bool) ![]const u8 {
|
||||
return std.fmt.allocPrint(allocator, "{d}s", .{seconds_int});
|
||||
}
|
||||
|
||||
// Milliseconds
|
||||
const ms_int = @as(i64, @intFromFloat(ms));
|
||||
if (long) {
|
||||
return std.fmt.allocPrint(allocator, "{d} ms", .{ms_int});
|
||||
@@ -214,10 +261,10 @@ pub fn jsFunction(
|
||||
const slice = str.toSlice(bun.default_allocator);
|
||||
defer slice.deinit();
|
||||
|
||||
const result = parse(slice.slice()) orelse return .js_undefined;
|
||||
const result = parse(slice.slice()) orelse return JSValue.jsNumber(std.math.nan(f64));
|
||||
return JSValue.jsNumber(result);
|
||||
}
|
||||
|
||||
// Invalid input type
|
||||
return .js_undefined;
|
||||
// Invalid input type returns NaN
|
||||
return JSValue.jsNumber(std.math.nan(f64));
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export const values = {
|
||||
|
||||
const output = await result.outputs[0].text();
|
||||
expect(output).toMatchInlineSnapshot(`
|
||||
"// ../../tmp/ms-bundler_2IefkN/entry.ts
|
||||
"// ../../tmp/ms-bundler_R3U5oN/entry.ts
|
||||
var values = {
|
||||
oneSecond: Bun.ms("1s"),
|
||||
oneMinute: Bun.ms("1m"),
|
||||
|
||||
@@ -1,308 +1,222 @@
|
||||
import { test, expect } from "bun:test";
|
||||
import { test, expect, describe } from "bun:test";
|
||||
|
||||
const ms = Bun.ms;
|
||||
describe("Bun.ms - parse (string to number)", () => {
|
||||
describe("short strings", () => {
|
||||
test.each([
|
||||
["100", 100],
|
||||
["1m", 60000],
|
||||
["1h", 3600000],
|
||||
["2d", 172800000],
|
||||
["3w", 1814400000],
|
||||
["1s", 1000],
|
||||
["100ms", 100],
|
||||
["1y", 31557600000],
|
||||
["1.5h", 5400000],
|
||||
["1 s", 1000],
|
||||
["-.5h", -1800000],
|
||||
["-1h", -3600000],
|
||||
["-200", -200],
|
||||
[".5ms", 0.5],
|
||||
])('Bun.ms("%s") should return %d', (input, expected) => {
|
||||
expect(Bun.ms(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
// Comprehensive test cases matching the ms library behavior
|
||||
// Format: [input, expected output]
|
||||
const parseTests: [string, number | undefined][] = [
|
||||
// Milliseconds - all variations
|
||||
["1ms", 1],
|
||||
["1 ms", 1],
|
||||
["100ms", 100],
|
||||
["100 ms", 100],
|
||||
["1.5ms", 1.5],
|
||||
["1millisecond", 1],
|
||||
["1 millisecond", 1],
|
||||
["100milliseconds", 100],
|
||||
["100 milliseconds", 100],
|
||||
["1msec", 1],
|
||||
["1 msec", 1],
|
||||
["100msecs", 100],
|
||||
["100 msecs", 100],
|
||||
describe("long strings", () => {
|
||||
test.each([
|
||||
["53 milliseconds", 53],
|
||||
["17 msecs", 17],
|
||||
["1 sec", 1000],
|
||||
["1 min", 60000],
|
||||
["1 hr", 3600000],
|
||||
["2 days", 172800000],
|
||||
["1 week", 604800000],
|
||||
["1 month", 2629800000],
|
||||
["1 year", 31557600000],
|
||||
["1.5 hours", 5400000],
|
||||
["-100 milliseconds", -100],
|
||||
["-1.5 hours", -5400000],
|
||||
["-10 minutes", -600000],
|
||||
])('Bun.ms("%s") should return %d', (input, expected) => {
|
||||
expect(Bun.ms(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
// Seconds - all variations
|
||||
["1s", 1000],
|
||||
["1 s", 1000],
|
||||
["1sec", 1000],
|
||||
["1 sec", 1000],
|
||||
["1secs", 1000],
|
||||
["1 secs", 1000],
|
||||
["1second", 1000],
|
||||
["1 second", 1000],
|
||||
["1seconds", 1000],
|
||||
["1 seconds", 1000],
|
||||
["2s", 2000],
|
||||
["5s", 5000],
|
||||
["10s", 10000],
|
||||
["1.5s", 1500],
|
||||
["2.5s", 2500],
|
||||
["0.5s", 500],
|
||||
[".5s", 500],
|
||||
describe("case insensitive", () => {
|
||||
test.each([
|
||||
["1M", 60000],
|
||||
["1H", 3600000],
|
||||
["2D", 172800000],
|
||||
["3W", 1814400000],
|
||||
["1S", 1000],
|
||||
["1MS", 1],
|
||||
["1Y", 31557600000],
|
||||
["1 HOUR", 3600000],
|
||||
["1 DAY", 86400000],
|
||||
["1 WEEK", 604800000],
|
||||
])('Bun.ms("%s") should return %d', (input, expected) => {
|
||||
expect(Bun.ms(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
// Minutes - all variations
|
||||
["1m", 60000],
|
||||
["1 m", 60000],
|
||||
["1min", 60000],
|
||||
["1 min", 60000],
|
||||
["1mins", 60000],
|
||||
["1 mins", 60000],
|
||||
["1minute", 60000],
|
||||
["1 minute", 60000],
|
||||
["1minutes", 60000],
|
||||
["1 minutes", 60000],
|
||||
["2m", 120000],
|
||||
["5m", 300000],
|
||||
["10m", 600000],
|
||||
["0.5m", 30000],
|
||||
["1.5m", 90000],
|
||||
[".5m", 30000],
|
||||
|
||||
// Hours - all variations
|
||||
["1h", 3600000],
|
||||
["1 h", 3600000],
|
||||
["1hr", 3600000],
|
||||
["1 hr", 3600000],
|
||||
["1hrs", 3600000],
|
||||
["1 hrs", 3600000],
|
||||
["1hour", 3600000],
|
||||
["1 hour", 3600000],
|
||||
["1hours", 3600000],
|
||||
["1 hours", 3600000],
|
||||
["2h", 7200000],
|
||||
["10h", 36000000],
|
||||
["24h", 86400000],
|
||||
["0.5h", 1800000],
|
||||
["1.5h", 5400000],
|
||||
["2.5h", 9000000],
|
||||
[".5h", 1800000],
|
||||
|
||||
// Days - all variations
|
||||
["1d", 86400000],
|
||||
["1 d", 86400000],
|
||||
["1day", 86400000],
|
||||
["1 day", 86400000],
|
||||
["1days", 86400000],
|
||||
["1 days", 86400000],
|
||||
["2d", 172800000],
|
||||
["7d", 604800000],
|
||||
["0.5d", 43200000],
|
||||
["1.5d", 129600000],
|
||||
[".5d", 43200000],
|
||||
|
||||
// Weeks - all variations
|
||||
["1w", 604800000],
|
||||
["1 w", 604800000],
|
||||
["1week", 604800000],
|
||||
["1 week", 604800000],
|
||||
["1weeks", 604800000],
|
||||
["1 weeks", 604800000],
|
||||
["2w", 1209600000],
|
||||
["4w", 2419200000],
|
||||
["0.5w", 302400000],
|
||||
["1.5w", 907200000],
|
||||
|
||||
// Years - all variations
|
||||
["1y", 31557600000],
|
||||
["1 y", 31557600000],
|
||||
["1yr", 31557600000],
|
||||
["1 yr", 31557600000],
|
||||
["1yrs", 31557600000],
|
||||
["1 yrs", 31557600000],
|
||||
["1year", 31557600000],
|
||||
["1 year", 31557600000],
|
||||
["1years", 31557600000],
|
||||
["1 years", 31557600000],
|
||||
["2y", 63115200000],
|
||||
["0.5y", 15778800000],
|
||||
["1.5y", 47336400000],
|
||||
|
||||
// Numbers without units (treated as ms)
|
||||
["1", 1],
|
||||
["100", 100],
|
||||
["1000", 1000],
|
||||
["0", 0],
|
||||
["53", 53],
|
||||
|
||||
// Negative values - all units
|
||||
["-1ms", -1],
|
||||
["-100ms", -100],
|
||||
["-1s", -1000],
|
||||
["-10s", -10000],
|
||||
["-1m", -60000],
|
||||
["-5m", -300000],
|
||||
["-1h", -3600000],
|
||||
["-2h", -7200000],
|
||||
["-1d", -86400000],
|
||||
["-2d", -172800000],
|
||||
["-1w", -604800000],
|
||||
["-1y", -31557600000],
|
||||
["-0.5s", -500],
|
||||
["-0.5m", -30000],
|
||||
["-1.5h", -5400000],
|
||||
["-100", -100],
|
||||
|
||||
// Case insensitive - all units
|
||||
["1MS", 1],
|
||||
["1Ms", 1],
|
||||
["1mS", 1],
|
||||
["1S", 1000],
|
||||
["1Sec", 1000],
|
||||
["1SECOND", 1000],
|
||||
["1M", 60000],
|
||||
["1Min", 60000],
|
||||
["1MINUTE", 60000],
|
||||
["1H", 3600000],
|
||||
["1Hr", 3600000],
|
||||
["1HOUR", 3600000],
|
||||
["1D", 86400000],
|
||||
["1Day", 86400000],
|
||||
["1DAY", 86400000],
|
||||
["1W", 604800000],
|
||||
["1Week", 604800000],
|
||||
["1WEEK", 604800000],
|
||||
["1Y", 31557600000],
|
||||
["1Yr", 31557600000],
|
||||
["1YEAR", 31557600000],
|
||||
|
||||
// Edge cases with whitespace
|
||||
[" 1s", 1000],
|
||||
["1s ", 1000],
|
||||
[" 1s ", 1000],
|
||||
["1 s", 1000],
|
||||
["1 s", 1000],
|
||||
|
||||
// Invalid inputs - should return undefined
|
||||
["", undefined],
|
||||
[" ", undefined],
|
||||
[" ", undefined],
|
||||
["invalid", undefined],
|
||||
["hello world", undefined],
|
||||
["s", undefined],
|
||||
["m", undefined],
|
||||
["h", undefined],
|
||||
["ms", undefined],
|
||||
["1x", undefined],
|
||||
["1xs", undefined],
|
||||
["1sm", undefined],
|
||||
["1s2m", undefined],
|
||||
["1 s 2 m", undefined],
|
||||
["s1", undefined],
|
||||
["1.2.3s", undefined],
|
||||
["NaN", undefined],
|
||||
["Infinity", undefined],
|
||||
["-", undefined],
|
||||
[".", undefined],
|
||||
["-.5s", -500], // This should work
|
||||
["abc123", undefined],
|
||||
["123abc", undefined],
|
||||
["1.s", 1000], // Should parse as "1" with unit "s"
|
||||
];
|
||||
|
||||
test.each(parseTests)("parse: ms(%p) === %p", (input, expected) => {
|
||||
expect(ms(input)).toBe(expected);
|
||||
describe("invalid inputs", () => {
|
||||
test.each([
|
||||
["", "empty string"],
|
||||
[" ", "whitespace only"],
|
||||
["foo", "invalid unit"],
|
||||
["1x", "unknown unit"],
|
||||
["1.2.3s", "multiple dots"],
|
||||
])('Bun.ms("%s") should return NaN (%s)', (input) => {
|
||||
expect(Bun.ms(input)).toBeNaN();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test format (number -> string)
|
||||
const formatTests: [number, { long?: boolean } | undefined, string][] = [
|
||||
// Milliseconds
|
||||
[0, undefined, "0ms"],
|
||||
[1, undefined, "1ms"],
|
||||
[100, undefined, "100ms"],
|
||||
[999, undefined, "999ms"],
|
||||
describe("Bun.ms - format (number to string)", () => {
|
||||
describe("short format", () => {
|
||||
test.each([
|
||||
[500, "500ms"],
|
||||
[-500, "-500ms"],
|
||||
[1000, "1s"],
|
||||
[10000, "10s"],
|
||||
[60000, "1m"],
|
||||
[600000, "10m"],
|
||||
[3600000, "1h"],
|
||||
[86400000, "1d"],
|
||||
[604800000, "1w"],
|
||||
[2629800000, "1mo"],
|
||||
[31557600001, "1y"],
|
||||
[234234234, "3d"],
|
||||
[-234234234, "-3d"],
|
||||
])("Bun.ms(%d) should return %s", (input, expected) => {
|
||||
expect(Bun.ms(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
// Seconds
|
||||
[1000, undefined, "1s"],
|
||||
[1500, undefined, "2s"],
|
||||
[2000, undefined, "2s"],
|
||||
[10000, undefined, "10s"],
|
||||
[59000, undefined, "59s"],
|
||||
[59999, undefined, "60s"],
|
||||
describe("long format", () => {
|
||||
test.each([
|
||||
[500, "500 ms"],
|
||||
[-500, "-500 ms"],
|
||||
[1000, "1 second"],
|
||||
[1001, "1 second"],
|
||||
[1499, "1 second"],
|
||||
[1500, "2 seconds"],
|
||||
[10000, "10 seconds"],
|
||||
[60000, "1 minute"],
|
||||
[600000, "10 minutes"],
|
||||
[3600000, "1 hour"],
|
||||
[86400000, "1 day"],
|
||||
[172800000, "2 days"],
|
||||
[604800000, "1 week"],
|
||||
[2629800000, "1 month"],
|
||||
[31557600001, "1 year"],
|
||||
[234234234, "3 days"],
|
||||
[-234234234, "-3 days"],
|
||||
])("Bun.ms(%d, { long: true }) should return %s", (input, expected) => {
|
||||
expect(Bun.ms(input, { long: true })).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
// Minutes
|
||||
[60000, undefined, "1m"],
|
||||
[90000, undefined, "2m"],
|
||||
[120000, undefined, "2m"],
|
||||
[300000, undefined, "5m"],
|
||||
[3540000, undefined, "59m"],
|
||||
[3599999, undefined, "60m"],
|
||||
describe("invalid number inputs", () => {
|
||||
test("NaN should throw", () => {
|
||||
expect(() => Bun.ms(NaN)).toThrow();
|
||||
});
|
||||
|
||||
// Hours
|
||||
[3600000, undefined, "1h"],
|
||||
[5400000, undefined, "2h"],
|
||||
[7200000, undefined, "2h"],
|
||||
[36000000, undefined, "10h"],
|
||||
[82800000, undefined, "23h"],
|
||||
[86399999, undefined, "24h"],
|
||||
test("Infinity should throw", () => {
|
||||
expect(() => Bun.ms(Infinity)).toThrow();
|
||||
});
|
||||
|
||||
// Days
|
||||
[86400000, undefined, "1d"],
|
||||
[129600000, undefined, "2d"],
|
||||
[172800000, undefined, "2d"],
|
||||
[604800000, undefined, "7d"],
|
||||
|
||||
// Negative values
|
||||
[-1, undefined, "-1ms"],
|
||||
[-1000, undefined, "-1s"],
|
||||
[-60000, undefined, "-1m"],
|
||||
[-3600000, undefined, "-1h"],
|
||||
[-86400000, undefined, "-1d"],
|
||||
|
||||
// Long format - milliseconds
|
||||
[0, { long: true }, "0 ms"],
|
||||
[1, { long: true }, "1 ms"],
|
||||
[100, { long: true }, "100 ms"],
|
||||
[999, { long: true }, "999 ms"],
|
||||
|
||||
// Long format - seconds
|
||||
[1000, { long: true }, "1 second"],
|
||||
[1500, { long: true }, "2 seconds"],
|
||||
[2000, { long: true }, "2 seconds"],
|
||||
[10000, { long: true }, "10 seconds"],
|
||||
|
||||
// Long format - minutes
|
||||
[60000, { long: true }, "1 minute"],
|
||||
[90000, { long: true }, "2 minutes"],
|
||||
[120000, { long: true }, "2 minutes"],
|
||||
[300000, { long: true }, "5 minutes"],
|
||||
|
||||
// Long format - hours
|
||||
[3600000, { long: true }, "1 hour"],
|
||||
[5400000, { long: true }, "2 hours"],
|
||||
[7200000, { long: true }, "2 hours"],
|
||||
[36000000, { long: true }, "10 hours"],
|
||||
|
||||
// Long format - days
|
||||
[86400000, { long: true }, "1 day"],
|
||||
[129600000, { long: true }, "2 days"],
|
||||
[172800000, { long: true }, "2 days"],
|
||||
|
||||
// Long format - negative
|
||||
[-1000, { long: true }, "-1 second"],
|
||||
[-60000, { long: true }, "-1 minute"],
|
||||
[-3600000, { long: true }, "-1 hour"],
|
||||
[-86400000, { long: true }, "-1 day"],
|
||||
];
|
||||
|
||||
test.each(formatTests)("format: ms(%p, %p) === %p", (input, options, expected) => {
|
||||
expect(ms(input, options)).toBe(expected);
|
||||
test("-Infinity should throw", () => {
|
||||
expect(() => Bun.ms(-Infinity)).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test errors
|
||||
test("throws on NaN", () => {
|
||||
expect(() => ms(NaN)).toThrow("Value must be a finite number");
|
||||
});
|
||||
describe("Bun.ms - comprehensive coverage", () => {
|
||||
describe("all time units", () => {
|
||||
test.each([
|
||||
// Milliseconds
|
||||
["1ms", 1],
|
||||
["1millisecond", 1],
|
||||
["1milliseconds", 1],
|
||||
["1msec", 1],
|
||||
["1msecs", 1],
|
||||
// Seconds
|
||||
["1s", 1000],
|
||||
["1sec", 1000],
|
||||
["1secs", 1000],
|
||||
["1second", 1000],
|
||||
["1seconds", 1000],
|
||||
["2seconds", 2000],
|
||||
// Minutes
|
||||
["1m", 60000],
|
||||
["1min", 60000],
|
||||
["1mins", 60000],
|
||||
["1minute", 60000],
|
||||
["1minutes", 60000],
|
||||
["2minutes", 120000],
|
||||
// Hours
|
||||
["1h", 3600000],
|
||||
["1hr", 3600000],
|
||||
["1hrs", 3600000],
|
||||
["1hour", 3600000],
|
||||
["1hours", 3600000],
|
||||
["2hours", 7200000],
|
||||
// Days
|
||||
["1d", 86400000],
|
||||
["1day", 86400000],
|
||||
["1days", 86400000],
|
||||
["2days", 172800000],
|
||||
// Weeks
|
||||
["1w", 604800000],
|
||||
["1week", 604800000],
|
||||
["1weeks", 604800000],
|
||||
["2weeks", 1209600000],
|
||||
// Months
|
||||
["1mo", 2629800000],
|
||||
["1month", 2629800000],
|
||||
["1months", 2629800000],
|
||||
["2months", 5259600000],
|
||||
// Years
|
||||
["1y", 31557600000],
|
||||
["1yr", 31557600000],
|
||||
["1yrs", 31557600000],
|
||||
["1year", 31557600000],
|
||||
["1years", 31557600000],
|
||||
["2years", 63115200000],
|
||||
])('Bun.ms("%s") should parse correctly', (input, expected) => {
|
||||
expect(Bun.ms(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
test("throws on Infinity", () => {
|
||||
expect(() => ms(Infinity)).toThrow("Value must be a finite number");
|
||||
});
|
||||
describe("decimals and negatives", () => {
|
||||
test.each([
|
||||
["1.5s", 1500],
|
||||
["1.5h", 5400000],
|
||||
["0.5d", 43200000],
|
||||
["-1s", -1000],
|
||||
["-1.5h", -5400000],
|
||||
["-0.5d", -43200000],
|
||||
[".5s", 500],
|
||||
["-.5s", -500],
|
||||
])('Bun.ms("%s") should handle decimals/negatives', (input, expected) => {
|
||||
expect(Bun.ms(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
test("throws on -Infinity", () => {
|
||||
expect(() => ms(-Infinity)).toThrow("Value must be a finite number");
|
||||
});
|
||||
|
||||
// Test that it's available on Bun global
|
||||
test("Bun.ms is available", () => {
|
||||
expect(typeof Bun.ms).toBe("function");
|
||||
expect(Bun.ms("1s")).toBe(1000);
|
||||
expect(Bun.ms(1000)).toBe("1s");
|
||||
describe("whitespace handling", () => {
|
||||
test.each([
|
||||
["1 s", 1000],
|
||||
["1 s", 1000],
|
||||
["1 s", 1000],
|
||||
[" 1s", 1000],
|
||||
["1s ", 1000],
|
||||
[" 1s ", 1000],
|
||||
["1 second", 1000],
|
||||
["1 seconds", 1000],
|
||||
[" 1 second ", 1000],
|
||||
])('Bun.ms("%s") should handle whitespace', (input, expected) => {
|
||||
expect(Bun.ms(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user