Files
dualfroz 93dd2b2f06 fix: pluralize unit word using the maxDecimalPoints-truncated count
When maxDecimalPoints truncates a unit count to exactly 1, the rendered
number showed 1 while the word was pluralized from the raw count (for
example humanizeDuration(1500, { maxDecimalPoints: 0 }) returned
"1 seconds"). Pluralize from the normalized count that is actually
displayed so the number and word agree.
2026-09-04 09:04:26 +02:00

351 lines
9.6 KiB
JavaScript

const humanizeDuration = require("..");
const humanizer = humanizeDuration.humanizer;
const { describe, it } = require("node:test");
const assert = require("node:assert/strict");
const ms = require("ms");
describe("humanizer", function () {
it("humanizes English when passed no arguments", function () {
const h = humanizer();
assert.strictEqual(h(1000), "1 second");
});
it("humanizes English when passed an empty object", function () {
const h = humanizer({});
assert.strictEqual(h(1000), "1 second");
});
it("can change the delimiter", function () {
const h = humanizer({ delimiter: "+" });
assert.strictEqual(h(0), "0 seconds");
assert.strictEqual(h(1000), "1 second");
assert.strictEqual(h(363000), "6 minutes+3 seconds");
});
it("can change the spacer", function () {
const h = humanizer({ spacer: " whole " });
assert.strictEqual(h(0), "0 whole seconds");
assert.strictEqual(h(1000), "1 whole second");
assert.strictEqual(h(260040000), "3 whole days, 14 whole minutes");
});
it("can use a conjunction", function () {
const h = humanizer({ conjunction: " and " });
assert.strictEqual(h(0), "0 seconds");
assert.strictEqual(h(1000), "1 second");
assert.strictEqual(h(260040000), "3 days and 14 minutes");
assert.strictEqual(h(10874000), "3 hours, 1 minute, and 14 seconds");
});
it("can use a conjunction without a serial comma", function () {
const h = humanizer({
conjunction: " & ",
serialComma: false,
});
assert.strictEqual(h(1000), "1 second");
assert.strictEqual(h(260040000), "3 days & 14 minutes");
assert.strictEqual(h(10874000), "3 hours, 1 minute & 14 seconds");
});
it("can change the units", function () {
const h = humanizer({ units: ["d"] });
assert.strictEqual(h(0), "0 days");
assert.strictEqual(h(ms("6h")), "0.25 days");
assert.strictEqual(h(ms("7d")), "7 days");
});
it("can overwrite the unit measures in the initializer", function () {
const h = humanizer({
unitMeasures: {
y: 10512000000,
mo: 864000000,
w: 144000000,
d: 28800000,
h: 3600000,
m: 60000,
s: 1000,
ms: 1,
},
});
assert.strictEqual(h(1000), "1 second");
assert.strictEqual(h(3600000), "1 hour");
assert.strictEqual(h(28800000), "1 day");
assert.strictEqual(h(144000000), "1 week");
});
it("can change the decimal", function () {
const h = humanizer({
units: ["s"],
decimal: "what",
});
assert.strictEqual(h(1234), "1what234 seconds");
assert.strictEqual(
h(1234, {
decimal: "!!",
}),
"1!!234 seconds"
);
});
it("can do simple rounding", function () {
const h = humanizer({ round: true });
assert.strictEqual(h(0), "0 seconds");
assert.strictEqual(h(499), "0 seconds");
assert.strictEqual(h(500), "1 second");
assert.strictEqual(h(1000), "1 second");
assert.strictEqual(h(1499), "1 second");
assert.strictEqual(h(1500), "2 seconds");
assert.strictEqual(h(1500), "2 seconds");
assert.strictEqual(h(121499), "2 minutes, 1 second");
assert.strictEqual(h(121500), "2 minutes, 2 seconds");
});
it('can do rounding with the "units" option', function () {
const h = humanizer({ round: true });
assert.strictEqual(
h(86364000, { units: ["y", "mo", "w", "d", "h"] }),
"1 day"
);
assert.strictEqual(
h(1209564000, { units: ["y", "mo", "w", "d", "h"] }),
"2 weeks"
);
assert.strictEqual(h(3692131200000, { units: ["y", "mo"] }), "117 years");
assert.strictEqual(
h(3692131200001, { units: ["y", "mo", "w", "d", "h", "m"] }),
"116 years, 11 months, 4 weeks, 1 day, 4 hours, 30 minutes"
);
});
it('can do rounding with the "largest" option', function () {
const h = humanizer({ round: true });
assert.strictEqual(h(3692131200000, { largest: 1 }), "117 years");
assert.strictEqual(h(3692131200000, { largest: 2 }), "117 years");
assert.strictEqual(
h(3692131200001, { largest: 100 }),
"116 years, 11 months, 4 weeks, 1 day, 4 hours, 30 minutes"
);
assert.strictEqual(h(2838550, { largest: 3 }), "47 minutes, 19 seconds");
});
it('can do rounding with the "maxDecimalPoints" option', function () {
var h = humanizer({ maxDecimalPoints: 2 });
assert.strictEqual(h(8123.456789), "8.12 seconds");
h.maxDecimalPoints = 3;
assert.strictEqual(h(8123.456789), "8.123 seconds");
assert.strictEqual(h(8000), "8 seconds");
h.maxDecimalPoints = 6;
assert.strictEqual(h(8123.45), "8.12345 seconds");
h.maxDecimalPoints = 6;
assert.strictEqual(h(8000), "8 seconds");
h.maxDecimalPoints = 0;
assert.strictEqual(h(7123.456), "7 seconds");
h.maxDecimalPoints = 2;
assert.strictEqual(h(7999), "7.99 seconds");
h.maxDecimalPoints = 3;
assert.strictEqual(h(7999), "7.999 seconds");
});
it('pluralizes based on the count rendered by "maxDecimalPoints"', function () {
// When `maxDecimalPoints` truncates the count down to exactly 1, the unit
// word must agree with the displayed "1", i.e. be singular.
assert.strictEqual(humanizer({ maxDecimalPoints: 0 })(1500), "1 second");
assert.strictEqual(humanizer({ maxDecimalPoints: 2 })(1005), "1 second");
// A truncated count that is not 1 stays plural.
assert.strictEqual(humanizer({ maxDecimalPoints: 0 })(2500), "2 seconds");
// German shares the same singular/plural mechanism.
assert.strictEqual(
humanizer({ language: "de", maxDecimalPoints: 0 })(1500),
"1 Sekunde"
);
});
it("can ask for the largest units", function () {
const h = humanizer({ largest: 2 });
assert.strictEqual(h(0), "0 seconds");
assert.strictEqual(h(1000), "1 second");
assert.strictEqual(h(2000), "2 seconds");
assert.strictEqual(h(540360012), "6 days, 6 hours");
assert.strictEqual(
h(540360012, { largest: 3 }),
"6 days, 6 hours, 6 minutes"
);
assert.strictEqual(
h(540360012, { largest: 100 }),
"6 days, 6 hours, 6 minutes, 0.012 seconds"
);
});
it("has properties which can be modified", function () {
const h = humanizer();
assert.strictEqual(h(363000), "6 minutes, 3 seconds");
h.delimiter = "+";
assert.strictEqual(h(363000), "6 minutes+3 seconds");
h.language = "es";
assert.strictEqual(h(363000), "6 minutos+3 segundos");
h.units = ["m"];
assert.strictEqual(h(363000), "6,05 minutos");
});
it("is a named function", function () {
assert.strictEqual(humanizer().name, "humanizer");
});
it("can add a new language", function () {
const h = humanizer({ language: "cool language" });
h.languages["cool language"] = {
y: () => "y",
mo: () => "mo",
w: () => "w",
d: () => "d",
h: () => "h",
m: () => "m",
s: () => "s",
ms: () => "ms",
delimiter: "--",
};
assert.strictEqual(h(1000), "1 s");
assert.strictEqual(h(61000), "1 m--1 s");
assert.strictEqual(h(61000, { delimiter: "&&" }), "1 m&&1 s");
assert.strictEqual(
h(1000, {
language: "es",
}),
"1 segundo"
);
const anotherH = humanizer({
language: "cool language",
});
assert.throws(() => {
anotherH(1000);
}, Error);
});
it("can overwrite an existing language", function () {
const h = humanizer({ language: "en" });
assert.strictEqual(h(1000), "1 second");
h.languages.en = {
y: () => "y",
mo: () => "mo",
w: () => "w",
d: () => "d",
h: () => "h",
m: () => "m",
s: () => "s",
ms: () => "ms",
};
assert.strictEqual(h(1000), "1 s");
assert.strictEqual(h(15600000), "4 h, 20 m");
const anotherH = humanizer({ language: "en" });
assert.strictEqual(anotherH(1000), "1 second");
});
it("can overwrite the languages property in the initializer", function () {
const h = humanizer({
languages: {
en: {
y: () => "y",
mo: () => "mo",
w: () => "w",
d: () => "d",
h: () => "h",
m: () => "m",
s: () => "s",
ms: () => "ms",
decimal: "!",
},
},
});
assert.strictEqual(h(1000), "1 s");
assert.strictEqual(h(15600000), "4 h, 20 m");
assert.strictEqual(h(1000, { language: "es" }), "1 segundo");
assert.strictEqual(h(71750), "1 m, 11!75 s");
});
it('uses "." as a fallback for a missing decimal', function () {
const h = humanizer({
languages: {
en: {
y: () => "y",
mo: () => "mo",
w: () => "w",
d: () => "d",
h: () => "h",
m: () => "m",
s: () => "s",
ms: () => "ms",
},
},
});
assert.strictEqual(h(71750), "1 m, 11.75 s");
assert.strictEqual(h(71750, { decimal: "!" }), "1 m, 11!75 s");
});
it("accepts fallback languages", function () {
const h = humanizer();
assert.strictEqual(
h(10000, { language: "es", fallbacks: ["en"] }),
"10 segundos"
);
assert.strictEqual(
h(10000, { language: "BAD", fallbacks: ["BAD", "es"] }),
"10 segundos"
);
assert.strictEqual(
h(10000, { language: "BAD", fallbacks: ["es", "fr"] }),
"10 segundos"
);
});
it("can replace digits", function () {
const h = humanizer({
digitReplacements: [
"Zero",
"One",
"Two",
"Three",
"UNUSED",
"UNUSED",
"UNUSED",
"UNUSED",
"UNUSED",
"UNUSED",
],
});
assert.strictEqual(h(123), "Zero.OneTwoThree seconds");
});
});