mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-05 09:06:08 +00:00
LibJS: Begin implementing Intl.NumberFormat.prototype.format
There is quite a lot to be done here so this is just a first pass at number formatting. Decimal and percent formatting are mostly working, but only for standard and compact notation (engineering and scientific notation are not implemented here). Currency formatting is parsed, but there is more work to be done to handle e.g. using symbols instead of currency codes ("$" instead of "USD"), and putting spaces around the currency symbol ("USD 2.00" instead of "USD2.00").
This commit is contained in:
parent
0469006263
commit
89523f70cf
Notes:
sideshowbarker
2024-07-18 01:15:02 +09:00
Author: https://github.com/trflynn89
Commit: 89523f70cf
Pull-request: https://github.com/SerenityOS/serenity/pull/10873
Reviewed-by: https://github.com/linusg
8 changed files with 1433 additions and 1 deletions
|
@ -0,0 +1,357 @@
|
|||
describe("errors", () => {
|
||||
test("called on non-NumberFormat object", () => {
|
||||
expect(() => {
|
||||
Intl.NumberFormat.prototype.format;
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Intl.NumberFormat");
|
||||
|
||||
expect(() => {
|
||||
Intl.NumberFormat.prototype.format(1);
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Intl.NumberFormat");
|
||||
});
|
||||
|
||||
test("called with value that cannot be converted to a number", () => {
|
||||
expect(() => {
|
||||
Intl.NumberFormat().format(Symbol.hasInstance);
|
||||
}).toThrowWithMessage(TypeError, "Cannot convert symbol to number");
|
||||
});
|
||||
});
|
||||
|
||||
describe("special values", () => {
|
||||
test("NaN", () => {
|
||||
const en = new Intl.NumberFormat("en");
|
||||
expect(en.format()).toBe("NaN");
|
||||
expect(en.format(NaN)).toBe("NaN");
|
||||
expect(en.format(undefined)).toBe("NaN");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar");
|
||||
expect(ar.format()).toBe("ليس رقم");
|
||||
expect(ar.format(NaN)).toBe("ليس رقم");
|
||||
expect(ar.format(undefined)).toBe("ليس رقم");
|
||||
});
|
||||
|
||||
test("Infinity", () => {
|
||||
const en = new Intl.NumberFormat("en");
|
||||
expect(en.format(Infinity)).toBe("∞");
|
||||
expect(en.format(-Infinity)).toBe("-∞");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar");
|
||||
expect(ar.format(Infinity)).toBe("∞");
|
||||
expect(ar.format(-Infinity)).toBe("\u061c-∞");
|
||||
});
|
||||
});
|
||||
|
||||
describe("style=decimal", () => {
|
||||
test("default", () => {
|
||||
const en = new Intl.NumberFormat("en");
|
||||
expect(en.format(1)).toBe("1");
|
||||
expect(en.format(12)).toBe("12");
|
||||
expect(en.format(123)).toBe("123");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar");
|
||||
expect(ar.format(1)).toBe("\u0661");
|
||||
expect(ar.format(12)).toBe("\u0661\u0662");
|
||||
expect(ar.format(123)).toBe("\u0661\u0662\u0663");
|
||||
});
|
||||
|
||||
test("integer digits", () => {
|
||||
const en = new Intl.NumberFormat("en", { minimumIntegerDigits: 2 });
|
||||
expect(en.format(1)).toBe("01");
|
||||
expect(en.format(12)).toBe("12");
|
||||
expect(en.format(123)).toBe("123");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { minimumIntegerDigits: 2 });
|
||||
expect(ar.format(1)).toBe("\u0660\u0661");
|
||||
expect(ar.format(12)).toBe("\u0661\u0662");
|
||||
expect(ar.format(123)).toBe("\u0661\u0662\u0663");
|
||||
});
|
||||
|
||||
test("significant digits", () => {
|
||||
const en = new Intl.NumberFormat("en", {
|
||||
minimumSignificantDigits: 4,
|
||||
maximumSignificantDigits: 6,
|
||||
});
|
||||
expect(en.format(1)).toBe("1.000");
|
||||
expect(en.format(12)).toBe("12.00");
|
||||
expect(en.format(12.3)).toBe("12.30");
|
||||
expect(en.format(12.34)).toBe("12.34");
|
||||
expect(en.format(12.345)).toBe("12.345");
|
||||
expect(en.format(12.3456)).toBe("12.3456");
|
||||
expect(en.format(12.34567)).toBe("12.3457");
|
||||
expect(en.format(12.34561)).toBe("12.3456");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", {
|
||||
minimumSignificantDigits: 4,
|
||||
maximumSignificantDigits: 6,
|
||||
});
|
||||
expect(ar.format(1)).toBe("\u0661\u066b\u0660\u0660\u0660");
|
||||
expect(ar.format(12)).toBe("\u0661\u0662\u066b\u0660\u0660");
|
||||
expect(ar.format(12.3)).toBe("\u0661\u0662\u066b\u0663\u0660");
|
||||
expect(ar.format(12.34)).toBe("\u0661\u0662\u066b\u0663\u0664");
|
||||
expect(ar.format(12.345)).toBe("\u0661\u0662\u066b\u0663\u0664\u0665");
|
||||
expect(ar.format(12.3456)).toBe("\u0661\u0662\u066b\u0663\u0664\u0665\u0666");
|
||||
expect(ar.format(12.34567)).toBe("\u0661\u0662\u066b\u0663\u0664\u0665\u0667");
|
||||
expect(ar.format(12.34561)).toBe("\u0661\u0662\u066b\u0663\u0664\u0665\u0666");
|
||||
});
|
||||
|
||||
test("fraction digits", () => {
|
||||
const en = new Intl.NumberFormat("en", {
|
||||
minimumFractionDigits: 3,
|
||||
maximumFractionDigits: 5,
|
||||
});
|
||||
expect(en.format(1)).toBe("1.000");
|
||||
expect(en.format(12)).toBe("12.000");
|
||||
expect(en.format(1.2)).toBe("1.200");
|
||||
expect(en.format(1.23)).toBe("1.230");
|
||||
expect(en.format(1.234)).toBe("1.234");
|
||||
expect(en.format(1.2345)).toBe("1.2345");
|
||||
expect(en.format(1.23456)).toBe("1.23456");
|
||||
expect(en.format(1.234567)).toBe("1.23457");
|
||||
expect(en.format(1.234561)).toBe("1.23456");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", {
|
||||
minimumFractionDigits: 3,
|
||||
maximumFractionDigits: 5,
|
||||
});
|
||||
expect(ar.format(1)).toBe("\u0661\u066b\u0660\u0660\u0660");
|
||||
expect(ar.format(12)).toBe("\u0661\u0662\u066b\u0660\u0660\u0660");
|
||||
expect(ar.format(1.2)).toBe("\u0661\u066b\u0662\u0660\u0660");
|
||||
expect(ar.format(1.23)).toBe("\u0661\u066b\u0662\u0663\u0660");
|
||||
expect(ar.format(1.234)).toBe("\u0661\u066b\u0662\u0663\u0664");
|
||||
expect(ar.format(1.2345)).toBe("\u0661\u066b\u0662\u0663\u0664\u0665");
|
||||
expect(ar.format(1.23456)).toBe("\u0661\u066b\u0662\u0663\u0664\u0665\u0666");
|
||||
expect(ar.format(1.234567)).toBe("\u0661\u066b\u0662\u0663\u0664\u0665\u0667");
|
||||
expect(ar.format(1.234561)).toBe("\u0661\u066b\u0662\u0663\u0664\u0665\u0666");
|
||||
});
|
||||
|
||||
test("notation=compact", () => {
|
||||
const en = new Intl.NumberFormat("en", { notation: "compact" });
|
||||
expect(en.format(1)).toBe("1");
|
||||
expect(en.format(1.2)).toBe("1.2");
|
||||
expect(en.format(1.23)).toBe("1.2");
|
||||
expect(en.format(1.29)).toBe("1.3");
|
||||
expect(en.format(12)).toBe("12");
|
||||
expect(en.format(12.3)).toBe("12");
|
||||
expect(en.format(12.34)).toBe("12");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { notation: "compact" });
|
||||
expect(ar.format(1)).toBe("\u0661");
|
||||
expect(ar.format(1.2)).toBe("\u0661\u066b\u0662");
|
||||
expect(ar.format(1.23)).toBe("\u0661\u066b\u0662");
|
||||
expect(ar.format(1.29)).toBe("\u0661\u066b\u0663");
|
||||
expect(ar.format(12)).toBe("\u0661\u0662");
|
||||
expect(ar.format(12.3)).toBe("\u0661\u0662");
|
||||
expect(ar.format(12.34)).toBe("\u0661\u0662");
|
||||
});
|
||||
|
||||
test("signDisplay=never", () => {
|
||||
const en = new Intl.NumberFormat("en", { signDisplay: "never" });
|
||||
expect(en.format(1)).toBe("1");
|
||||
expect(en.format(-1)).toBe("1");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { signDisplay: "never" });
|
||||
expect(ar.format(1)).toBe("\u0661");
|
||||
expect(ar.format(-1)).toBe("\u0661");
|
||||
});
|
||||
|
||||
test("signDisplay=auto", () => {
|
||||
const en = new Intl.NumberFormat("en", { signDisplay: "auto" });
|
||||
expect(en.format(0)).toBe("0");
|
||||
expect(en.format(1)).toBe("1");
|
||||
expect(en.format(-0)).toBe("-0");
|
||||
expect(en.format(-1)).toBe("-1");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { signDisplay: "auto" });
|
||||
expect(ar.format(0)).toBe("\u0660");
|
||||
expect(ar.format(1)).toBe("\u0661");
|
||||
expect(ar.format(-0)).toBe("\u061c-\u0660");
|
||||
expect(ar.format(-1)).toBe("\u061c-\u0661");
|
||||
});
|
||||
|
||||
test("signDisplay=always", () => {
|
||||
const en = new Intl.NumberFormat("en", { signDisplay: "always" });
|
||||
expect(en.format(0)).toBe("+0");
|
||||
expect(en.format(1)).toBe("+1");
|
||||
expect(en.format(-0)).toBe("-0");
|
||||
expect(en.format(-1)).toBe("-1");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { signDisplay: "always" });
|
||||
expect(ar.format(0)).toBe("\u061c+\u0660");
|
||||
expect(ar.format(1)).toBe("\u061c+\u0661");
|
||||
expect(ar.format(-0)).toBe("\u061c-\u0660");
|
||||
expect(ar.format(-1)).toBe("\u061c-\u0661");
|
||||
});
|
||||
|
||||
test("signDisplay=exceptZero", () => {
|
||||
const en = new Intl.NumberFormat("en", { signDisplay: "exceptZero" });
|
||||
expect(en.format(0)).toBe("0");
|
||||
expect(en.format(1)).toBe("+1");
|
||||
expect(en.format(-0)).toBe("0");
|
||||
expect(en.format(-1)).toBe("-1");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { signDisplay: "exceptZero" });
|
||||
expect(ar.format(0)).toBe("\u0660");
|
||||
expect(ar.format(1)).toBe("\u061c+\u0661");
|
||||
expect(ar.format(-0)).toBe("\u0660");
|
||||
expect(ar.format(-1)).toBe("\u061c-\u0661");
|
||||
});
|
||||
});
|
||||
|
||||
describe("style=percent", () => {
|
||||
test("default", () => {
|
||||
const en = new Intl.NumberFormat("en", { style: "percent" });
|
||||
expect(en.format(1)).toBe("100%");
|
||||
expect(en.format(1.2)).toBe("120%");
|
||||
expect(en.format(0.234)).toBe("23%");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { style: "percent" });
|
||||
expect(ar.format(1)).toBe("\u0661\u0660\u0660\u066a\u061c");
|
||||
expect(ar.format(1.2)).toBe("\u0661\u0662\u0660\u066a\u061c");
|
||||
expect(ar.format(0.234)).toBe("\u0662\u0663\u066a\u061c");
|
||||
});
|
||||
|
||||
test("integer digits", () => {
|
||||
const en = new Intl.NumberFormat("en", { style: "percent", minimumIntegerDigits: 2 });
|
||||
expect(en.format(0.01)).toBe("01%");
|
||||
expect(en.format(0.12)).toBe("12%");
|
||||
expect(en.format(1.23)).toBe("123%");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { style: "percent", minimumIntegerDigits: 2 });
|
||||
expect(ar.format(0.01)).toBe("\u0660\u0661\u066a\u061c");
|
||||
expect(ar.format(0.12)).toBe("\u0661\u0662\u066a\u061c");
|
||||
expect(ar.format(1.23)).toBe("\u0661\u0662\u0663\u066a\u061c");
|
||||
});
|
||||
|
||||
test("significant digits", () => {
|
||||
const en = new Intl.NumberFormat("en", {
|
||||
style: "percent",
|
||||
minimumSignificantDigits: 4,
|
||||
maximumSignificantDigits: 6,
|
||||
});
|
||||
expect(en.format(0.1)).toBe("10.00%");
|
||||
expect(en.format(1.2)).toBe("120.0%");
|
||||
expect(en.format(1.23)).toBe("123.0%");
|
||||
expect(en.format(1.234)).toBe("123.4%");
|
||||
expect(en.format(1.2345)).toBe("123.45%");
|
||||
expect(en.format(1.23456)).toBe("123.456%");
|
||||
expect(en.format(1.234567)).toBe("123.457%");
|
||||
expect(en.format(1.234561)).toBe("123.456%");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", {
|
||||
style: "percent",
|
||||
minimumSignificantDigits: 4,
|
||||
maximumSignificantDigits: 6,
|
||||
});
|
||||
expect(ar.format(0.1)).toBe("\u0661\u0660\u066b\u0660\u0660\u066a\u061c");
|
||||
expect(ar.format(1.2)).toBe("\u0661\u0662\u0660\u066b\u0660\u066a\u061c");
|
||||
expect(ar.format(1.23)).toBe("\u0661\u0662\u0663\u066b\u0660\u066a\u061c");
|
||||
expect(ar.format(1.234)).toBe("\u0661\u0662\u0663\u066b\u0664\u066a\u061c");
|
||||
expect(ar.format(1.2345)).toBe("\u0661\u0662\u0663\u066b\u0664\u0665\u066a\u061c");
|
||||
expect(ar.format(1.23456)).toBe("\u0661\u0662\u0663\u066b\u0664\u0665\u0666\u066a\u061c");
|
||||
expect(ar.format(1.234567)).toBe("\u0661\u0662\u0663\u066b\u0664\u0665\u0667\u066a\u061c");
|
||||
expect(ar.format(1.234561)).toBe("\u0661\u0662\u0663\u066b\u0664\u0665\u0666\u066a\u061c");
|
||||
});
|
||||
|
||||
test("fraction digits", () => {
|
||||
const en = new Intl.NumberFormat("en", {
|
||||
style: "percent",
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 4,
|
||||
});
|
||||
expect(en.format(0.01)).toBe("1.00%");
|
||||
expect(en.format(0.1)).toBe("10.00%");
|
||||
expect(en.format(0.12)).toBe("12.00%");
|
||||
expect(en.format(0.123)).toBe("12.30%");
|
||||
expect(en.format(0.1234)).toBe("12.34%");
|
||||
expect(en.format(0.12345)).toBe("12.345%");
|
||||
expect(en.format(0.123456)).toBe("12.3456%");
|
||||
expect(en.format(0.1234567)).toBe("12.3457%");
|
||||
expect(en.format(0.1234561)).toBe("12.3456%");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", {
|
||||
style: "percent",
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 4,
|
||||
});
|
||||
expect(ar.format(0.01)).toBe("\u0661\u066b\u0660\u0660\u066a\u061c");
|
||||
expect(ar.format(0.1)).toBe("\u0661\u0660\u066b\u0660\u0660\u066a\u061c");
|
||||
expect(ar.format(0.12)).toBe("\u0661\u0662\u066b\u0660\u0660\u066a\u061c");
|
||||
expect(ar.format(0.123)).toBe("\u0661\u0662\u066b\u0663\u0660\u066a\u061c");
|
||||
expect(ar.format(0.1234)).toBe("\u0661\u0662\u066b\u0663\u0664\u066a\u061c");
|
||||
expect(ar.format(0.12345)).toBe("\u0661\u0662\u066b\u0663\u0664\u0665\u066a\u061c");
|
||||
expect(ar.format(0.123456)).toBe("\u0661\u0662\u066b\u0663\u0664\u0665\u0666\u066a\u061c");
|
||||
expect(ar.format(0.1234567)).toBe("\u0661\u0662\u066b\u0663\u0664\u0665\u0667\u066a\u061c");
|
||||
expect(ar.format(0.1234561)).toBe("\u0661\u0662\u066b\u0663\u0664\u0665\u0666\u066a\u061c");
|
||||
});
|
||||
|
||||
test("notation=compact", () => {
|
||||
const en = new Intl.NumberFormat("en", { style: "percent", notation: "compact" });
|
||||
expect(en.format(0.01)).toBe("1%");
|
||||
expect(en.format(0.012)).toBe("1.2%");
|
||||
expect(en.format(0.0123)).toBe("1.2%");
|
||||
expect(en.format(0.0129)).toBe("1.3%");
|
||||
expect(en.format(0.12)).toBe("12%");
|
||||
expect(en.format(0.123)).toBe("12%");
|
||||
expect(en.format(0.1234)).toBe("12%");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { style: "percent", notation: "compact" });
|
||||
expect(ar.format(0.01)).toBe("\u0661\u066a\u061c");
|
||||
expect(ar.format(0.012)).toBe("\u0661\u066b\u0662\u066a\u061c");
|
||||
expect(ar.format(0.0123)).toBe("\u0661\u066b\u0662\u066a\u061c");
|
||||
expect(ar.format(0.0129)).toBe("\u0661\u066b\u0663\u066a\u061c");
|
||||
expect(ar.format(0.12)).toBe("\u0661\u0662\u066a\u061c");
|
||||
expect(ar.format(0.123)).toBe("\u0661\u0662\u066a\u061c");
|
||||
expect(ar.format(0.1234)).toBe("\u0661\u0662\u066a\u061c");
|
||||
});
|
||||
|
||||
test("signDisplay=never", () => {
|
||||
const en = new Intl.NumberFormat("en", { style: "percent", signDisplay: "never" });
|
||||
expect(en.format(0.01)).toBe("1%");
|
||||
expect(en.format(-0.01)).toBe("1%");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { style: "percent", signDisplay: "never" });
|
||||
expect(ar.format(0.01)).toBe("\u0661\u066a\u061c");
|
||||
expect(ar.format(-0.01)).toBe("\u0661\u066a\u061c");
|
||||
});
|
||||
|
||||
test("signDisplay=auto", () => {
|
||||
const en = new Intl.NumberFormat("en", { style: "percent", signDisplay: "auto" });
|
||||
expect(en.format(0.0)).toBe("0%");
|
||||
expect(en.format(0.01)).toBe("1%");
|
||||
expect(en.format(-0.0)).toBe("-0%");
|
||||
expect(en.format(-0.01)).toBe("-1%");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { style: "percent", signDisplay: "auto" });
|
||||
expect(ar.format(0.0)).toBe("\u0660\u066a\u061c");
|
||||
expect(ar.format(0.01)).toBe("\u0661\u066a\u061c");
|
||||
expect(ar.format(-0.0)).toBe("\u061c-\u0660\u066a\u061c");
|
||||
expect(ar.format(-0.01)).toBe("\u061c-\u0661\u066a\u061c");
|
||||
});
|
||||
|
||||
test("signDisplay=always", () => {
|
||||
const en = new Intl.NumberFormat("en", { style: "percent", signDisplay: "always" });
|
||||
expect(en.format(0.0)).toBe("+0%");
|
||||
expect(en.format(0.01)).toBe("+1%");
|
||||
expect(en.format(-0.0)).toBe("-0%");
|
||||
expect(en.format(-0.01)).toBe("-1%");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { style: "percent", signDisplay: "always" });
|
||||
expect(ar.format(0.0)).toBe("\u061c+\u0660\u066a\u061c");
|
||||
expect(ar.format(0.01)).toBe("\u061c+\u0661\u066a\u061c");
|
||||
expect(ar.format(-0.0)).toBe("\u061c-\u0660\u066a\u061c");
|
||||
expect(ar.format(-0.01)).toBe("\u061c-\u0661\u066a\u061c");
|
||||
});
|
||||
|
||||
test("signDisplay=exceptZero", () => {
|
||||
const en = new Intl.NumberFormat("en", { style: "percent", signDisplay: "exceptZero" });
|
||||
expect(en.format(0.0)).toBe("0%");
|
||||
expect(en.format(0.01)).toBe("+1%");
|
||||
expect(en.format(-0.0)).toBe("0%");
|
||||
expect(en.format(-0.01)).toBe("-1%");
|
||||
|
||||
const ar = new Intl.NumberFormat("ar", { style: "percent", signDisplay: "exceptZero" });
|
||||
expect(ar.format(0.0)).toBe("\u0660\u066a\u061c");
|
||||
expect(ar.format(0.01)).toBe("\u061c+\u0661\u066a\u061c");
|
||||
expect(ar.format(-0.0)).toBe("\u0660\u066a\u061c");
|
||||
expect(ar.format(-0.01)).toBe("\u061c-\u0661\u066a\u061c");
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue