diff options
author | Timothy Flynn <trflynn89@pm.me> | 2021-11-16 19:35:40 -0500 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-11-17 09:01:32 +0000 |
commit | c19c3205ffe0747fa299950a725a57ebd6667e95 (patch) | |
tree | c2f28a77357ecd7a43b604d5383e17e89d9b63bc /Userland/Libraries/LibJS | |
parent | 8fe1c1f78876c8ef189a12ac9174488ed9576384 (diff) | |
download | serenity-c19c3205ffe0747fa299950a725a57ebd6667e95.zip |
LibJS: Implement ECMA-402 Number.prototype.toLocaleString
Diffstat (limited to 'Userland/Libraries/LibJS')
3 files changed, 106 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp b/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp index 1213b26e9b..07ddaf2f40 100644 --- a/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp @@ -7,9 +7,12 @@ #include <AK/Function.h> #include <AK/TypeCasts.h> +#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/Completion.h> #include <LibJS/Runtime/Error.h> #include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/Intl/NumberFormat.h> +#include <LibJS/Runtime/Intl/NumberFormatConstructor.h> #include <LibJS/Runtime/NumberObject.h> #include <LibJS/Runtime/NumberPrototype.h> @@ -37,6 +40,7 @@ void NumberPrototype::initialize(GlobalObject& object) Object::initialize(object); u8 attr = Attribute::Configurable | Attribute::Writable; define_native_function(vm.names.toFixed, to_fixed, 1, attr); + define_native_function(vm.names.toLocaleString, to_locale_string, 0, attr); define_native_function(vm.names.toString, to_string, 1, attr); define_native_function(vm.names.valueOf, value_of, 0, attr); } @@ -77,6 +81,29 @@ JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_fixed) return js_string(vm, String::formatted("{:0.{1}}", number, static_cast<size_t>(fraction_digits))); } +// 18.2.1 Number.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sup-number.prototype.tolocalestring +JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_locale_string) +{ + auto locales = vm.argument(0); + auto options = vm.argument(1); + + // 1. Let x be ? thisNumberValue(this value). + auto number_value = TRY(this_number_value(global_object, vm.this_value(global_object))); + + MarkedValueList arguments { vm.heap() }; + arguments.append(locales); + arguments.append(options); + + // 2. Let numberFormat be ? Construct(%NumberFormat%, ยซ locales, options ยป). + auto* number_format = static_cast<Intl::NumberFormat*>(TRY(construct(global_object, *global_object.intl_number_format_constructor(), move(arguments)))); + + // 3. Return ? FormatNumeric(numberFormat, x). + // Note: Our implementation of FormatNumeric does not throw. + auto formatted = Intl::format_numeric(*number_format, number_value.as_double()); + + return js_string(vm, move(formatted)); +} + // 21.1.3.6 Number.prototype.toString ( [ radix ] ), https://tc39.es/ecma262/#sec-number.prototype.tostring JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_string) { diff --git a/Userland/Libraries/LibJS/Runtime/NumberPrototype.h b/Userland/Libraries/LibJS/Runtime/NumberPrototype.h index ed36778376..bf35eb8332 100644 --- a/Userland/Libraries/LibJS/Runtime/NumberPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/NumberPrototype.h @@ -19,6 +19,7 @@ public: virtual ~NumberPrototype() override; JS_DECLARE_NATIVE_FUNCTION(to_fixed); + JS_DECLARE_NATIVE_FUNCTION(to_locale_string); JS_DECLARE_NATIVE_FUNCTION(to_string); JS_DECLARE_NATIVE_FUNCTION(value_of); }; diff --git a/Userland/Libraries/LibJS/Tests/builtins/Number/Number.prototype.toLocaleString.js b/Userland/Libraries/LibJS/Tests/builtins/Number/Number.prototype.toLocaleString.js new file mode 100644 index 0000000000..72ffb24573 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Number/Number.prototype.toLocaleString.js @@ -0,0 +1,78 @@ +describe("errors", () => { + test("must be called with numeric |this|", () => { + [true, [], {}, Symbol("foo"), "bar", 1n].forEach(value => { + expect(() => Number.prototype.toLocaleString.call(value)).toThrowWithMessage( + TypeError, + "Not an object of type Number" + ); + }); + }); +}); + +describe("correct behavior", () => { + test("length", () => { + expect(Number.prototype.toLocaleString).toHaveLength(0); + }); +}); + +describe("special values", () => { + test("NaN", () => { + expect(NaN.toLocaleString()).toBe("NaN"); + expect(NaN.toLocaleString("en")).toBe("NaN"); + expect(NaN.toLocaleString("ar")).toBe("ููุณย ุฑูู
"); + }); + + test("Infinity", () => { + expect(Infinity.toLocaleString()).toBe("โ"); + expect(Infinity.toLocaleString("en")).toBe("โ"); + expect(Infinity.toLocaleString("ar")).toBe("โ"); + }); +}); + +describe("styles", () => { + test("decimal", () => { + expect((12).toLocaleString("en")).toBe("12"); + expect((12).toLocaleString("ar")).toBe("\u0661\u0662"); + }); + + test("percent", () => { + expect((0.234).toLocaleString("en", { style: "percent" })).toBe("23%"); + expect((0.234).toLocaleString("ar", { style: "percent" })).toBe("\u0662\u0663\u066a\u061c"); + }); + + test("currency", () => { + expect( + (1.23).toLocaleString("en", { + style: "currency", + currency: "USD", + currencyDisplay: "name", + }) + ).toBe("1.23 US dollars"); + + expect( + (1.23).toLocaleString("ar", { + style: "currency", + currency: "USD", + currencyDisplay: "name", + }) + ).toBe("\u0661\u066b\u0662\u0663 ุฏููุงุฑ ุฃู
ุฑููู"); + }); + + test("unit", () => { + expect( + (1.23).toLocaleString("en", { + style: "unit", + unit: "kilometer-per-hour", + unitDisplay: "long", + }) + ).toBe("1.23 kilometers per hour"); + + expect( + (1.23).toLocaleString("ar", { + style: "unit", + unit: "kilometer-per-hour", + unitDisplay: "long", + }) + ).toBe("\u0661\u066b\u0662\u0663 ููููู
ุชุฑ ูู ุงูุณุงุนุฉ"); + }); +}); |