summaryrefslogtreecommitdiffstats
path: root/js/src/tests/non262/Intl/NumberFormat
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/tests/non262/Intl/NumberFormat')
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/StringBuffer.js37
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/bigint-int64.js40
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/browser.js0
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/call.js182
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/construct-newtarget.js81
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/cross-compartment.js70
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/currency-narrow-symbol.js40
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/currency-sign-accounting.js192
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/duplicate-singleton-variant.js49
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/format-as-code-or-name.js75
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/format.js55
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/formatToParts.js357
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/formatting-NaN.js35
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/negativeZeroFractionDigits.js21
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/notation-compact-long.js134
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/notation-compact-short.js136
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/notation-engineering.js136
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/notation-scientific.js136
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/numberingSystem-format.js18
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/numberingSystem-option.js60
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/options-emulate-undefined.js13
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/remove-unicode-extensions.js25
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/shell.js48
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/sign-display.js187
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/significantDigitsOfZero.js38
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/supportedLocalesOf.js371
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/toStringTag.js29
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/unit-compound-combinations.js63
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/unit-formatToParts-has-unit-field.js88
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/unit-well-formed.js250
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/unit.js104
-rw-r--r--js/src/tests/non262/Intl/NumberFormat/unwrapping.js247
32 files changed, 3317 insertions, 0 deletions
diff --git a/js/src/tests/non262/Intl/NumberFormat/StringBuffer.js b/js/src/tests/non262/Intl/NumberFormat/StringBuffer.js
new file mode 100644
index 0000000000..70aaa7bd75
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/StringBuffer.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// The implementation of the format function uses the C++ StringBuffer class,
+// which changes its storage model at 32 characters, and uses it in a way which
+// also means that there's no room for null-termination at this limit.
+// This test makes sure that none of this affects the output.
+
+var format = new Intl.NumberFormat("it-IT", {minimumFractionDigits: 1});
+
+assertEq(format.format(1123123123123123123123.1), "1.123.123.123.123.123.100.000,0");
+assertEq(format.format(12123123123123123123123.1), "12.123.123.123.123.122.000.000,0");
+assertEq(format.format(123123123123123123123123.1), "123.123.123.123.123.120.000.000,0");
+
+// Ensure the ICU output matches Number.prototype.toFixed.
+function formatToFixed(x) {
+ var mfd = format.resolvedOptions().maximumFractionDigits;
+ var s = x.toFixed(mfd);
+
+ // To keep it simple we assume |s| is always in exponential form.
+ var m = s.match(/^(\d)\.(\d+)e\+(\d+)$/);
+ assertEq(m !== null, true);
+ s = m[1] + m[2].padEnd(m[3], "0");
+
+ // Group digits and append fractional part.
+ m = s.match(/\d{1,3}(?=(?:\d{3})*$)/g);
+ assertEq(m !== null, true);
+ return m.join(".") + ",0";
+}
+
+assertEq(formatToFixed(1123123123123123123123.1), "1.123.123.123.123.123.100.000,0");
+assertEq(formatToFixed(12123123123123123123123.1), "12.123.123.123.123.122.000.000,0");
+assertEq(formatToFixed(123123123123123123123123.1), "123.123.123.123.123.120.000.000,0");
+
+reportCompare(0, 0, "ok");
diff --git a/js/src/tests/non262/Intl/NumberFormat/bigint-int64.js b/js/src/tests/non262/Intl/NumberFormat/bigint-int64.js
new file mode 100644
index 0000000000..7b670d0873
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/bigint-int64.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+// Ensure the int64_t optimization when formatting a BigInt value works correctly by testing with
+// various integers around the (u)int[32,64] limits.
+
+const limits = {
+ int32: {
+ min: -0x80000000n,
+ max: 0x7FFFFFFFn,
+ },
+ uint32: {
+ min: 0n,
+ max: 0xFFFFFFFFn
+ },
+ int64: {
+ min: -0x8000000000000000n,
+ max: 0x7FFFFFFFFFFFFFFFn,
+ },
+ uint64: {
+ min: 0n,
+ max: 0xFFFFFFFFFFFFFFFFn
+ },
+};
+
+const nf = new Intl.NumberFormat("en", {useGrouping: false});
+
+const diff = 10n;
+
+for (const int of Object.values(limits)) {
+ for (let i = -diff; i <= diff; ++i) {
+ let n = int.min + i;
+ assertEq(nf.format(n), n.toString());
+
+ let m = int.max + i;
+ assertEq(nf.format(m), m.toString());
+ }
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/browser.js b/js/src/tests/non262/Intl/NumberFormat/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/browser.js
diff --git a/js/src/tests/non262/Intl/NumberFormat/call.js b/js/src/tests/non262/Intl/NumberFormat/call.js
new file mode 100644
index 0000000000..9711a4d6a9
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/call.js
@@ -0,0 +1,182 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+function IsIntlService(c) {
+ return typeof c === "function" &&
+ c.hasOwnProperty("prototype") &&
+ c.prototype.hasOwnProperty("resolvedOptions");
+}
+
+function IsObject(o) {
+ return Object(o) === o;
+}
+
+function IsPrimitive(o) {
+ return Object(o) !== o;
+}
+
+function thisValues() {
+ const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService);
+
+ return [
+ // Primitive values.
+ ...[undefined, null, true, "abc", Symbol(), 123],
+
+ // Object values.
+ ...[{}, [], /(?:)/, function(){}, new Proxy({}, {})],
+
+ // Intl objects.
+ ...[].concat(...intlConstructors.map(ctor => {
+ let args = [];
+ if (ctor === Intl.DisplayNames) {
+ // Intl.DisplayNames can't be constructed without any arguments.
+ args = [undefined, {type: "language"}];
+ }
+
+ return [
+ // Instance of an Intl constructor.
+ new ctor(...args),
+
+ // Instance of a subclassed Intl constructor.
+ new class extends ctor {}(...args),
+
+ // Object inheriting from an Intl constructor prototype.
+ Object.create(ctor.prototype),
+
+ // Intl object not inheriting from its default prototype.
+ Object.setPrototypeOf(new ctor(...args), Object.prototype),
+ ];
+ })),
+ ];
+}
+
+const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.NumberFormat.call(Object.create(Intl.NumberFormat.prototype)))[0];
+
+// Invoking [[Call]] for Intl.NumberFormat returns a new instance unless called
+// with an instance inheriting from Intl.NumberFormat.prototype.
+for (let thisValue of thisValues()) {
+ let obj = Intl.NumberFormat.call(thisValue);
+
+ if (!Intl.NumberFormat.prototype.isPrototypeOf(thisValue)) {
+ assertEq(Object.is(obj, thisValue), false);
+ assertEq(obj instanceof Intl.NumberFormat, true);
+ if (IsObject(thisValue))
+ assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
+ } else {
+ assertEq(Object.is(obj, thisValue), true);
+ assertEq(obj instanceof Intl.NumberFormat, true);
+ assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
+ }
+}
+
+// Intl.NumberFormat uses the legacy Intl constructor compromise semantics.
+// - Test when InstanceofOperator(thisValue, %NumberFormat%) returns true.
+for (let thisValue of thisValues().filter(IsObject)) {
+ let hasInstanceCalled = false;
+ Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, {
+ value() {
+ assertEq(hasInstanceCalled, false);
+ hasInstanceCalled = true;
+ return true;
+ }, configurable: true
+ });
+ let obj = Intl.NumberFormat.call(thisValue);
+ delete Intl.NumberFormat[Symbol.hasInstance];
+
+ assertEq(Object.is(obj, thisValue), true);
+ assertEq(hasInstanceCalled, true);
+ assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
+}
+// - Test when InstanceofOperator(thisValue, %NumberFormat%) returns false.
+for (let thisValue of thisValues().filter(IsObject)) {
+ let hasInstanceCalled = false;
+ Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, {
+ value() {
+ assertEq(hasInstanceCalled, false);
+ hasInstanceCalled = true;
+ return false;
+ }, configurable: true
+ });
+ let obj = Intl.NumberFormat.call(thisValue);
+ delete Intl.NumberFormat[Symbol.hasInstance];
+
+ assertEq(Object.is(obj, thisValue), false);
+ assertEq(obj instanceof Intl.NumberFormat, true);
+ assertEq(hasInstanceCalled, true);
+ assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
+}
+// - Test with primitive values.
+for (let thisValue of thisValues().filter(IsPrimitive)) {
+ // Ensure @@hasInstance is not called.
+ Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, {
+ value() { assertEq(true, false); }, configurable: true
+ });
+ let obj = Intl.NumberFormat.call(thisValue);
+ delete Intl.NumberFormat[Symbol.hasInstance];
+
+ assertEq(Object.is(obj, thisValue), false);
+ assertEq(obj instanceof Intl.NumberFormat, true);
+}
+
+// Throws an error when attempting to install [[FallbackSymbol]] twice.
+{
+ let thisValue = Object.create(Intl.NumberFormat.prototype);
+ assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
+
+ assertEq(Intl.NumberFormat.call(thisValue), thisValue);
+ assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
+
+ assertThrowsInstanceOf(() => Intl.NumberFormat.call(thisValue), TypeError);
+ assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
+}
+
+// Throws an error when the thisValue is non-extensible.
+{
+ let thisValue = Object.create(Intl.NumberFormat.prototype);
+ Object.preventExtensions(thisValue);
+
+ assertThrowsInstanceOf(() => Intl.NumberFormat.call(thisValue), TypeError);
+ assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
+}
+
+// [[FallbackSymbol]] is installed as a frozen property holding an Intl.NumberFormat instance.
+{
+ let thisValue = Object.create(Intl.NumberFormat.prototype);
+ Intl.NumberFormat.call(thisValue);
+
+ let desc = Object.getOwnPropertyDescriptor(thisValue, intlFallbackSymbol);
+ assertEq(desc !== undefined, true);
+ assertEq(desc.writable, false);
+ assertEq(desc.enumerable, false);
+ assertEq(desc.configurable, false);
+ assertEq(desc.value instanceof Intl.NumberFormat, true);
+}
+
+// Ensure [[FallbackSymbol]] is installed last by changing the [[Prototype]]
+// during initialization.
+{
+ let thisValue = {};
+ let options = {
+ get useGrouping() {
+ Object.setPrototypeOf(thisValue, Intl.NumberFormat.prototype);
+ return false;
+ }
+ };
+ let obj = Intl.NumberFormat.call(thisValue, undefined, options);
+ assertEq(Object.is(obj, thisValue), true);
+ assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]);
+}
+{
+ let thisValue = Object.create(Intl.NumberFormat.prototype);
+ let options = {
+ get useGrouping() {
+ Object.setPrototypeOf(thisValue, Object.prototype);
+ return false;
+ }
+ };
+ let obj = Intl.NumberFormat.call(thisValue, undefined, options);
+ assertEq(Object.is(obj, thisValue), false);
+ assertEqArray(Object.getOwnPropertySymbols(thisValue), []);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/construct-newtarget.js b/js/src/tests/non262/Intl/NumberFormat/construct-newtarget.js
new file mode 100644
index 0000000000..0053b2737e
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/construct-newtarget.js
@@ -0,0 +1,81 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+// Test subclassing %Intl.NumberFormat% works correctly.
+class MyNumberFormat extends Intl.NumberFormat {}
+
+var obj = new MyNumberFormat();
+assertEq(obj instanceof MyNumberFormat, true);
+assertEq(obj instanceof Intl.NumberFormat, true);
+assertEq(Object.getPrototypeOf(obj), MyNumberFormat.prototype);
+
+obj = Reflect.construct(MyNumberFormat, []);
+assertEq(obj instanceof MyNumberFormat, true);
+assertEq(obj instanceof Intl.NumberFormat, true);
+assertEq(Object.getPrototypeOf(obj), MyNumberFormat.prototype);
+
+obj = Reflect.construct(MyNumberFormat, [], MyNumberFormat);
+assertEq(obj instanceof MyNumberFormat, true);
+assertEq(obj instanceof Intl.NumberFormat, true);
+assertEq(Object.getPrototypeOf(obj), MyNumberFormat.prototype);
+
+obj = Reflect.construct(MyNumberFormat, [], Intl.NumberFormat);
+assertEq(obj instanceof MyNumberFormat, false);
+assertEq(obj instanceof Intl.NumberFormat, true);
+assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype);
+
+
+// Set a different constructor as NewTarget.
+obj = Reflect.construct(MyNumberFormat, [], Array);
+assertEq(obj instanceof MyNumberFormat, false);
+assertEq(obj instanceof Intl.NumberFormat, false);
+assertEq(obj instanceof Array, true);
+assertEq(Object.getPrototypeOf(obj), Array.prototype);
+
+obj = Reflect.construct(Intl.NumberFormat, [], Array);
+assertEq(obj instanceof Intl.NumberFormat, false);
+assertEq(obj instanceof Array, true);
+assertEq(Object.getPrototypeOf(obj), Array.prototype);
+
+
+// The prototype defaults to %NumberFormatPrototype% if null.
+function NewTargetNullPrototype() {}
+NewTargetNullPrototype.prototype = null;
+
+obj = Reflect.construct(Intl.NumberFormat, [], NewTargetNullPrototype);
+assertEq(obj instanceof Intl.NumberFormat, true);
+assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype);
+
+obj = Reflect.construct(MyNumberFormat, [], NewTargetNullPrototype);
+assertEq(obj instanceof MyNumberFormat, false);
+assertEq(obj instanceof Intl.NumberFormat, true);
+assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype);
+
+
+// "prototype" property is retrieved exactly once.
+var trapLog = [], getLog = [];
+var ProxiedConstructor = new Proxy(Intl.NumberFormat, new Proxy({
+ get(target, propertyKey, receiver) {
+ getLog.push(propertyKey);
+ return Reflect.get(target, propertyKey, receiver);
+ }
+}, {
+ get(target, propertyKey, receiver) {
+ trapLog.push(propertyKey);
+ return Reflect.get(target, propertyKey, receiver);
+ }
+}));
+
+obj = Reflect.construct(Intl.NumberFormat, [], ProxiedConstructor);
+assertEqArray(trapLog, ["get"]);
+assertEqArray(getLog, ["prototype"]);
+assertEq(obj instanceof Intl.NumberFormat, true);
+assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype);
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/non262/Intl/NumberFormat/cross-compartment.js b/js/src/tests/non262/Intl/NumberFormat/cross-compartment.js
new file mode 100644
index 0000000000..806f889d6f
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/cross-compartment.js
@@ -0,0 +1,70 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+var otherGlobal = newGlobal();
+
+var numberFormat = new Intl.NumberFormat();
+var ccwNumberFormat = new otherGlobal.Intl.NumberFormat();
+
+// Test Intl.NumberFormat.prototype.format with a CCW object.
+var Intl_NumberFormat_format_get = Object.getOwnPropertyDescriptor(Intl.NumberFormat.prototype, "format").get;
+
+assertEq(Intl_NumberFormat_format_get.call(ccwNumberFormat)(0),
+ Intl_NumberFormat_format_get.call(numberFormat)(0));
+
+// Test Intl.NumberFormat.prototype.formatToParts with a CCW object.
+var Intl_NumberFormat_formatToParts = Intl.NumberFormat.prototype.formatToParts;
+
+assertEq(deepEqual(Intl_NumberFormat_formatToParts.call(ccwNumberFormat, 0),
+ Intl_NumberFormat_formatToParts.call(numberFormat, 0)),
+ true);
+
+// Test Intl.NumberFormat.prototype.resolvedOptions with a CCW object.
+var Intl_NumberFormat_resolvedOptions = Intl.NumberFormat.prototype.resolvedOptions;
+
+assertEq(deepEqual(Intl_NumberFormat_resolvedOptions.call(ccwNumberFormat),
+ Intl_NumberFormat_resolvedOptions.call(numberFormat)),
+ true);
+
+// Special case for Intl.NumberFormat: The Intl fallback symbol.
+
+function fallbackSymbol(global) {
+ var NF = global.Intl.NumberFormat;
+ return Object.getOwnPropertySymbols(NF.call(Object.create(NF.prototype)))[0];
+}
+
+const intlFallbackSymbol = fallbackSymbol(this);
+const otherIntlFallbackSymbol = fallbackSymbol(otherGlobal);
+assertEq(intlFallbackSymbol === otherIntlFallbackSymbol, false);
+
+// Test when the fallback symbol points to a CCW NumberFormat object.
+var objWithFallbackCCWNumberFormat = {
+ __proto__: Intl.NumberFormat.prototype,
+ [intlFallbackSymbol]: ccwNumberFormat,
+};
+
+assertEq(Intl_NumberFormat_format_get.call(objWithFallbackCCWNumberFormat)(0),
+ Intl_NumberFormat_format_get.call(numberFormat)(0));
+
+assertEq(deepEqual(Intl_NumberFormat_resolvedOptions.call(objWithFallbackCCWNumberFormat),
+ Intl_NumberFormat_resolvedOptions.call(numberFormat)),
+ true);
+
+// Ensure the fallback symbol(s) are not accessed for CCW NumberFormat objects.
+var ccwNumberFormatWithPoisonedFallback = new otherGlobal.Intl.NumberFormat();
+Object.setPrototypeOf(ccwNumberFormatWithPoisonedFallback, Intl.NumberFormat.prototype);
+Object.defineProperty(ccwNumberFormatWithPoisonedFallback, intlFallbackSymbol, {
+ get() { throw new Error(); }
+});
+Object.defineProperty(ccwNumberFormatWithPoisonedFallback, otherIntlFallbackSymbol, {
+ get() { throw new Error(); }
+});
+
+assertEq(Intl_NumberFormat_format_get.call(ccwNumberFormatWithPoisonedFallback)(0),
+ Intl_NumberFormat_format_get.call(numberFormat)(0));
+
+assertEq(deepEqual(Intl_NumberFormat_resolvedOptions.call(ccwNumberFormatWithPoisonedFallback),
+ Intl_NumberFormat_resolvedOptions.call(numberFormat)),
+ true);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/currency-narrow-symbol.js b/js/src/tests/non262/Intl/NumberFormat/currency-narrow-symbol.js
new file mode 100644
index 0000000000..4873045685
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/currency-narrow-symbol.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const {
+ Integer, Decimal, Fraction, Currency, Literal,
+} = NumberFormatParts;
+
+const testcases = [
+ {
+ locale: "en-CA",
+ options: {
+ style: "currency",
+ currency: "USD",
+ currencyDisplay: "narrowSymbol",
+ },
+ values: [
+ {value: 123, string: "$123.00",
+ parts: [Currency("$"), Integer("123"), Decimal("."), Fraction("00")]},
+ ],
+ },
+
+ // And for comparison "symbol" currency-display.
+
+ {
+ locale: "en-CA",
+ options: {
+ style: "currency",
+ currency: "USD",
+ currencyDisplay: "symbol",
+ },
+ values: [
+ {value: 123, string: "US$123.00",
+ parts: [Currency("US$"), Integer("123"), Decimal("."), Fraction("00")]},
+ ],
+ },
+];
+
+runNumberFormattingTestcases(testcases);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/currency-sign-accounting.js b/js/src/tests/non262/Intl/NumberFormat/currency-sign-accounting.js
new file mode 100644
index 0000000000..6f91d6edd4
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/currency-sign-accounting.js
@@ -0,0 +1,192 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const {
+ Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction,
+ Currency, Literal,
+} = NumberFormatParts;
+
+const testcases = [
+ // "auto": Show the sign on negative numbers only.
+ {
+ locale: "en",
+ options: {
+ style: "currency",
+ currency: "USD",
+ currencySign: "accounting",
+ signDisplay: "auto",
+ },
+ values: [
+ {value: +0, string: "$0.00",
+ parts: [Currency("$"), Integer("0"), Decimal("."), Fraction("00")]},
+ {value: -0, string: "($0.00)",
+ parts: [Literal("("), Currency("$"), Integer("0"), Decimal("."), Fraction("00"), Literal(")")]},
+
+ {value: 1, string: "$1.00",
+ parts: [Currency("$"), Integer("1"), Decimal("."), Fraction("00")]},
+ {value: -1, string: "($1.00)",
+ parts: [Literal("("), Currency("$"), Integer("1"), Decimal("."), Fraction("00"), Literal(")")]},
+
+ {value: Infinity, string: "$∞", parts: [Currency("$"), Inf("∞")]},
+ {value: -Infinity, string: "($∞)", parts: [Literal("("), Currency("$"), Inf("∞"), Literal(")")]},
+
+ {value: NaN, string: "$NaN", parts: [Currency("$"), Nan("NaN")]},
+ {value: -NaN, string: "$NaN", parts: [Currency("$"), Nan("NaN")]},
+ ],
+ },
+
+ // "never": Show the sign on neither positive nor negative numbers.
+ {
+ locale: "en",
+ options: {
+ style: "currency",
+ currency: "USD",
+ currencySign: "accounting",
+ signDisplay: "never",
+ },
+ values: [
+ {value: +0, string: "$0.00", parts: [Currency("$"), Integer("0"), Decimal("."), Fraction("00")]},
+ {value: -0, string: "$0.00", parts: [Currency("$"), Integer("0"), Decimal("."), Fraction("00")]},
+
+ {value: 1, string: "$1.00", parts: [Currency("$"), Integer("1"), Decimal("."), Fraction("00")]},
+ {value: -1, string: "$1.00", parts: [Currency("$"), Integer("1"), Decimal("."), Fraction("00")]},
+
+ {value: Infinity, string: "$∞", parts: [Currency("$"), Inf("∞")]},
+ {value: -Infinity, string: "$∞", parts: [Currency("$"), Inf("∞")]},
+
+ {value: NaN, string: "$NaN", parts: [Currency("$"), Nan("NaN")]},
+ {value: -NaN, string: "$NaN", parts: [Currency("$"), Nan("NaN")]},
+ ],
+ },
+
+ // "always": Show the sign on positive and negative numbers including zero.
+ {
+ locale: "en",
+ options: {
+ style: "currency",
+ currency: "USD",
+ currencySign: "accounting",
+ signDisplay: "always",
+ },
+ values: [
+ {value: +0, string: "+$0.00",
+ parts: [PlusSign("+"), Currency("$"), Integer("0"), Decimal("."), Fraction("00")]},
+ {value: -0, string: "($0.00)",
+ parts: [Literal("("), Currency("$"), Integer("0"), Decimal("."), Fraction("00"), Literal(")")]},
+
+ {value: 1, string: "+$1.00",
+ parts: [PlusSign("+"), Currency("$"), Integer("1"), Decimal("."), Fraction("00")]},
+ {value: -1, string: "($1.00)",
+ parts: [Literal("("), Currency("$"), Integer("1"), Decimal("."), Fraction("00"), Literal(")")]},
+
+ {value: Infinity, string: "+$∞", parts: [PlusSign("+"), Currency("$"), Inf("∞")]},
+ {value: -Infinity, string: "($∞)", parts: [Literal("("), Currency("$"), Inf("∞"), Literal(")")]},
+
+ {value: NaN, string: "+$NaN", parts: [PlusSign("+"), Currency("$"), Nan("NaN")]},
+ {value: -NaN, string: "+$NaN", parts: [PlusSign("+"), Currency("$"), Nan("NaN")]},
+ ],
+ },
+
+ // "exceptZero": Show the sign on positive and negative numbers but not zero.
+ {
+ locale: "en",
+ options: {
+ style: "currency",
+ currency: "USD",
+ currencySign: "accounting",
+ signDisplay: "exceptZero",
+ },
+ values: [
+ {value: +0, string: "$0.00",
+ parts: [Currency("$"), Integer("0"), Decimal("."), Fraction("00")]},
+ {value: -0, string: "$0.00",
+ parts: [Currency("$"), Integer("0"), Decimal("."), Fraction("00")]},
+
+ {value: 1, string: "+$1.00",
+ parts: [PlusSign("+"), Currency("$"), Integer("1"), Decimal("."), Fraction("00")]},
+ {value: -1, string: "($1.00)",
+ parts: [Literal("("), Currency("$"), Integer("1"), Decimal("."), Fraction("00"), Literal(")")]},
+
+ {value: Infinity, string: "+$∞", parts: [PlusSign("+"), Currency("$"), Inf("∞")]},
+ {value: -Infinity, string: "($∞)", parts: [Literal("("), Currency("$"), Inf("∞"), Literal(")")]},
+
+ {value: NaN, string: "$NaN", parts: [Currency("$"), Nan("NaN")]},
+ {value: -NaN, string: "$NaN", parts: [Currency("$"), Nan("NaN")]},
+ ],
+ },
+
+ // Tests with suppressed fractional digits.
+
+ // "auto": Show the sign on negative numbers only.
+ {
+ locale: "en",
+ options: {
+ style: "currency",
+ currency: "USD",
+ currencySign: "accounting",
+ signDisplay: "auto",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ },
+ values: [
+ {value: +0.1, string: "$0", parts: [Currency("$"), Integer("0")]},
+ {value: -0.1, string: "($0)", parts: [Literal("("), Currency("$"), Integer("0"), Literal(")")]},
+ ],
+ },
+
+ // "never": Show the sign on neither positive nor negative numbers.
+ {
+ locale: "en",
+ options: {
+ style: "currency",
+ currency: "USD",
+ currencySign: "accounting",
+ signDisplay: "never",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ },
+ values: [
+ {value: +0.1, string: "$0", parts: [Currency("$"), Integer("0")]},
+ {value: -0.1, string: "$0", parts: [Currency("$"), Integer("0")]},
+ ],
+ },
+
+ // "always": Show the sign on positive and negative numbers including zero.
+ {
+ locale: "en",
+ options: {
+ style: "currency",
+ currency: "USD",
+ currencySign: "accounting",
+ signDisplay: "always",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ },
+ values: [
+ {value: +0.1, string: "+$0", parts: [PlusSign("+"), Currency("$"), Integer("0")]},
+ {value: -0.1, string: "($0)", parts: [Literal("("), Currency("$"), Integer("0"), Literal(")")]},
+ ],
+ },
+
+ // "exceptZero": Show the sign on positive and negative numbers but not zero.
+ {
+ locale: "en",
+ options: {
+ style: "currency",
+ currency: "USD",
+ currencySign: "accounting",
+ signDisplay: "exceptZero",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ },
+
+ values: [
+ {value: +0.1, string: "$0", parts: [Currency("$"), Integer("0")]},
+ {value: -0.1, string: "$0", parts: [Currency("$"), Integer("0")]},
+ ],
+ }
+];
+
+runNumberFormattingTestcases(testcases);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/duplicate-singleton-variant.js b/js/src/tests/non262/Intl/NumberFormat/duplicate-singleton-variant.js
new file mode 100644
index 0000000000..42bf78c3fe
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/duplicate-singleton-variant.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Check for duplicate variants and singletons case-insensitively, but don't
+// check in privateuse components.
+
+function checkInvalidLocale(locale)
+{
+ try
+ {
+ new Intl.NumberFormat(locale);
+ throw new Error("didn't throw");
+ }
+ catch (e)
+ {
+ assertEq(e instanceof RangeError, true,
+ "expected RangeError for locale '" + locale + "', got " + e);
+ }
+}
+
+var badLocales =
+ [
+ "en-u-foo-U-foo",
+ "en-tester-Tester",
+ "en-tesTER-TESter",
+ "de-DE-u-kn-true-U-kn-true",
+ "ar-u-foo-q-bar-u-baz",
+ "ar-z-moo-u-foo-q-bar-z-eit-u-baz",
+ ];
+
+for (var locale of badLocales)
+ checkInvalidLocale(locale);
+
+// Fully-privateuse locales are rejected.
+for (var locale of badLocales)
+ assertThrowsInstanceOf(() => new Intl.NumberFormat("x-" + locale), RangeError);
+
+// Locales with trailing privateuse also okay.
+for (var locale of badLocales)
+{
+ new Intl.NumberFormat("en-x-" + locale).format(5);
+ new Intl.NumberFormat("en-u-foo-x-u-" + locale).format(5);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/format-as-code-or-name.js b/js/src/tests/non262/Intl/NumberFormat/format-as-code-or-name.js
new file mode 100644
index 0000000000..ba257ecd71
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/format-as-code-or-name.js
@@ -0,0 +1,75 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1093421;
+var summary =
+ "new Intl.NumberFormat(..., { style: 'currency', currency: '...', " +
+ "currencyDisplay: 'name' or 'code' }) should have behavior other than " +
+ "throwing";
+
+print(BUGNUMBER + ": " + summary);
+
+//-----------------------------------------------------------------------------
+
+// Test that currencyDisplay: "code" behaves correctly and doesn't throw.
+
+var usdCodeOptions =
+ {
+ style: "currency",
+ currency: "USD",
+ currencyDisplay: "code",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var usDollarsCode = new Intl.NumberFormat("en-US", usdCodeOptions);
+assertEq(/USD/.test(usDollarsCode.format(25)), true);
+
+// ISO 4217 currency codes are formed from an ISO 3166-1 alpha-2 country code
+// followed by a third letter. ISO 3166 guarantees that no country code
+// starting with "X" will ever be assigned. Stepping carefully around a few
+// 4217-designated special "currencies", XQQ will never have a representation.
+// Thus, yes: this really is specified to work, as unrecognized or unsupported
+// codes pass into the string unmodified.
+var xqqCodeOptions =
+ {
+ style: "currency",
+ currency: "XQQ",
+ currencyDisplay: "code",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var xqqMoneyCode = new Intl.NumberFormat("en-US", xqqCodeOptions);
+assertEq(/XQQ/.test(xqqMoneyCode.format(25)), true);
+
+// Test that currencyDisplay: "name" behaves without throwing. (Unlike the two
+// above tests, the results here aren't guaranteed as the name is
+// implementation-defined.)
+var usdNameOptions =
+ {
+ style: "currency",
+ currency: "USD",
+ currencyDisplay: "name",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var usDollarsName = new Intl.NumberFormat("en-US", usdNameOptions);
+assertEq(usDollarsName.format(25), "25 US dollars");
+
+// But if the implementation doesn't recognize the currency, the provided code
+// is used in place of a proper name, unmolested.
+var xqqNameOptions =
+ {
+ style: "currency",
+ currency: "XQQ",
+ currencyDisplay: "name",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var xqqMoneyName = new Intl.NumberFormat("en-US", xqqNameOptions);
+assertEq(/XQQ/.test(xqqMoneyName.format(25)), true);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/format.js b/js/src/tests/non262/Intl/NumberFormat/format.js
new file mode 100644
index 0000000000..a71a877566
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/format.js
@@ -0,0 +1,55 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Tests the format function with a diverse set of locales and options.
+
+var format;
+
+// Locale en-US; default options.
+format = new Intl.NumberFormat("en-us");
+assertEq(format.format(0), "0");
+assertEq(format.format(-1), "-1");
+assertEq(format.format(123456789.123456789), "123,456,789.123");
+
+// Locale en-US; currency USD.
+// The US dollar uses two fractional digits, and negative values are commonly
+// parenthesized.
+format = new Intl.NumberFormat("en-us", {style: "currency", currency: "USD"});
+assertEq(format.format(0), "$0.00");
+assertEq(format.format(-1), "-$1.00");
+assertEq(format.format(123456789.123456789), "$123,456,789.12");
+
+// Locale ja-JP; currency JPY.
+// The Japanese yen has no subunit in real life.
+format = new Intl.NumberFormat("ja-jp", {style: "currency", currency: "JPY"});
+assertEq(format.format(0), "¥0");
+assertEq(format.format(-1), "-¥1");
+assertEq(format.format(123456789.123456789), "¥123,456,789");
+
+// Locale ar-JO; currency JOD.
+// The Jordanian Dinar divides into 1000 fils. Jordan uses (real) Arabic digits.
+format = new Intl.NumberFormat("ar-jo", {style: "currency", currency: "JOD"});
+assertEq(format.format(0), "٠٫٠٠٠ د.أ.‏");
+assertEq(format.format(-1), "؜-١٫٠٠٠ د.أ.‏");
+assertEq(format.format(123456789.123456789), "١٢٣٬٤٥٦٬٧٨٩٫١٢٣ د.أ.‏");
+
+// Locale th-TH; Thai digits, percent, two significant digits.
+format = new Intl.NumberFormat("th-th-u-nu-thai",
+ {style: "percent",
+ minimumSignificantDigits: 2,
+ maximumSignificantDigits: 2});
+assertEq(format.format(0), "๐.๐%");
+assertEq(format.format(-0.01), "-๑.๐%");
+assertEq(format.format(1.10), "๑๑๐%");
+
+
+// Test the .name property of the "format" getter.
+var desc = Object.getOwnPropertyDescriptor(Intl.NumberFormat.prototype, "format");
+assertEq(desc !== undefined, true);
+assertEq(typeof desc.get, "function");
+assertEq(desc.get.name, "get format");
+
+
+reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/non262/Intl/NumberFormat/formatToParts.js b/js/src/tests/non262/Intl/NumberFormat/formatToParts.js
new file mode 100644
index 0000000000..3ac7c3f4d3
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/formatToParts.js
@@ -0,0 +1,357 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1289882;
+var summary = "Implement Intl.NumberFormat.prototype.formatToParts";
+
+print(BUGNUMBER + ": " + summary);
+
+//-----------------------------------------------------------------------------
+
+assertEq("formatToParts" in Intl.NumberFormat(), true);
+
+// NOTE: Some of these tests exercise standard behavior (e.g. that format and
+// formatToParts expose the same formatted string). But much of this,
+// like the exact-formatted-string expectations, is technically
+// implementation-dependent. This is necessary as a practical matter to
+// properly test the conversion from ICU's nested-field exposure to
+// ECMA-402's sequential-parts exposure.
+
+var {
+ Nan, Inf, Integer, Group, Decimal, Fraction,
+ MinusSign, PlusSign, PercentSign, Currency, Literal,
+} = NumberFormatParts;
+
+//-----------------------------------------------------------------------------
+
+// Test -0's partitioning now that it's not treated like +0.
+// https://github.com/tc39/ecma402/pull/232
+
+var deadSimpleFormatter = new Intl.NumberFormat("en-US");
+
+assertParts(deadSimpleFormatter, -0,
+ [MinusSign("-"), Integer("0")]);
+
+// Test behavior of a currency with code formatting.
+var usdCodeOptions =
+ {
+ style: "currency",
+ currency: "USD",
+ currencyDisplay: "code",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var usDollarsCode = new Intl.NumberFormat("en-US", usdCodeOptions);
+
+assertParts(usDollarsCode, 25,
+ [Currency("USD"), Literal("\xA0"), Integer("25")]);
+
+// ISO 4217 currency codes are formed from an ISO 3166-1 alpha-2 country code
+// followed by a third letter. ISO 3166 guarantees that no country code
+// starting with "X" will ever be assigned. Stepping carefully around a few
+// 4217-designated special "currencies", XQQ will never have a representation.
+// Thus, yes: this really is specified to work, as unrecognized or unsupported
+// codes pass into the string unmodified.
+var xqqCodeOptions =
+ {
+ style: "currency",
+ currency: "XQQ",
+ currencyDisplay: "code",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var xqqMoneyCode = new Intl.NumberFormat("en-US", xqqCodeOptions);
+
+assertParts(xqqMoneyCode, 25,
+ [Currency("XQQ"), Literal("\xA0"), Integer("25")]);
+
+// Test currencyDisplay: "name".
+var usdNameOptions =
+ {
+ style: "currency",
+ currency: "USD",
+ currencyDisplay: "name",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var usDollarsName = new Intl.NumberFormat("en-US", usdNameOptions);
+
+assertParts(usDollarsName, 25,
+ [Integer("25"), Literal(" "), Currency("US dollars")]);
+
+var usdNameGroupingOptions =
+ {
+ style: "currency",
+ currency: "USD",
+ currencyDisplay: "name",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var usDollarsNameGrouping =
+ new Intl.NumberFormat("en-US", usdNameGroupingOptions);
+
+assertParts(usDollarsNameGrouping, 12345678,
+ [Integer("12"),
+ Group(","),
+ Integer("345"),
+ Group(","),
+ Integer("678"),
+ Literal(" "),
+ Currency("US dollars")]);
+
+// But if the implementation doesn't recognize the currency, the provided code
+// is used in place of a proper name, unmolested.
+var xqqNameOptions =
+ {
+ style: "currency",
+ currency: "XQQ",
+ currencyDisplay: "name",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var xqqMoneyName = new Intl.NumberFormat("en-US", xqqNameOptions);
+
+assertParts(xqqMoneyName, 25,
+ [Integer("25"), Literal(" "), Currency("XQQ")]);
+
+// Test some currencies with fractional components.
+
+var usdNameFractionOptions =
+ {
+ style: "currency",
+ currency: "USD",
+ currencyDisplay: "name",
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ };
+var usdNameFractionFormatter =
+ new Intl.NumberFormat("en-US", usdNameFractionOptions);
+
+// The US national surplus (i.e. debt) as of October 18, 2016. (Replicating
+// data from a comment in builtin/Intl/NumberFormat.cpp.)
+var usNationalSurplus = -19766580028249.41;
+
+assertParts(usdNameFractionFormatter, usNationalSurplus,
+ [MinusSign("-"),
+ Integer("19"),
+ Group(","),
+ Integer("766"),
+ Group(","),
+ Integer("580"),
+ Group(","),
+ Integer("028"),
+ Group(","),
+ Integer("249"),
+ Decimal("."),
+ Fraction("41"),
+ Literal(" "),
+ Currency("US dollars")]);
+
+// Percents in various forms.
+
+var usPercentOptions =
+ {
+ style: "percent",
+ minimumFractionDigits: 1,
+ maximumFractionDigits: 1,
+ };
+var usPercentFormatter =
+ new Intl.NumberFormat("en-US", usPercentOptions);
+
+assertParts(usPercentFormatter, 0.375,
+ [Integer("37"), Decimal("."), Fraction("5"), PercentSign("%")]);
+
+assertParts(usPercentFormatter, -1284.375,
+ [MinusSign("-"),
+ Integer("128"),
+ Group(","),
+ Integer("437"),
+ Decimal("."),
+ Fraction("5"),
+ PercentSign("%")]);
+
+assertParts(usPercentFormatter, NaN,
+ [Nan("NaN"), PercentSign("%")]);
+
+assertParts(usPercentFormatter, Infinity,
+ [Inf("∞"), PercentSign("%")]);
+
+assertParts(usPercentFormatter, -Infinity,
+ [MinusSign("-"), Inf("∞"), PercentSign("%")]);
+
+var dePercentOptions =
+ {
+ style: "percent",
+ minimumFractionDigits: 1,
+ maximumFractionDigits: 1,
+ };
+var dePercentFormatter =
+ new Intl.NumberFormat("de", dePercentOptions);
+
+assertParts(dePercentFormatter, 0.375,
+ [Integer("37"), Decimal(","), Fraction("5"), Literal("\xA0"), PercentSign("%")]);
+
+assertParts(dePercentFormatter, -1284.375,
+ [MinusSign("-"),
+ Integer("128"),
+ Group("."),
+ Integer("437"),
+ Decimal(","),
+ Fraction("5"),
+ Literal("\xA0"),
+ PercentSign("%")]);
+
+assertParts(dePercentFormatter, NaN,
+ [Nan("NaN"), Literal("\xA0"), PercentSign("%")]);
+
+assertParts(dePercentFormatter, Infinity,
+ [Inf("∞"), Literal("\xA0"), PercentSign("%")]);
+
+assertParts(dePercentFormatter, -Infinity,
+ [MinusSign("-"), Inf("∞"), Literal("\xA0"), PercentSign("%")]);
+
+var arPercentOptions =
+ {
+ style: "percent",
+ minimumFractionDigits: 2,
+ };
+var arPercentFormatter =
+ new Intl.NumberFormat("ar-IQ", arPercentOptions);
+
+assertParts(arPercentFormatter, -135.32,
+ [Literal("\u{061C}"),
+ MinusSign("-"),
+ Integer("١٣"),
+ Group("٬"),
+ Integer("٥٣٢"),
+ Decimal("٫"),
+ Fraction("٠٠"),
+ PercentSign("٪"),
+ Literal("\u{061C}")]);
+
+// Decimals.
+
+var usDecimalOptions =
+ {
+ style: "decimal",
+ maximumFractionDigits: 7 // minimum defaults to 0
+ };
+var usDecimalFormatter =
+ new Intl.NumberFormat("en-US", usDecimalOptions);
+
+assertParts(usDecimalFormatter, 42,
+ [Integer("42")]);
+
+assertParts(usDecimalFormatter, 1337,
+ [Integer("1"), Group(","), Integer("337")]);
+
+assertParts(usDecimalFormatter, -6.25,
+ [MinusSign("-"), Integer("6"), Decimal("."), Fraction("25")]);
+
+assertParts(usDecimalFormatter, -1376.25,
+ [MinusSign("-"),
+ Integer("1"),
+ Group(","),
+ Integer("376"),
+ Decimal("."),
+ Fraction("25")]);
+
+assertParts(usDecimalFormatter, 124816.8359375,
+ [Integer("124"),
+ Group(","),
+ Integer("816"),
+ Decimal("."),
+ Fraction("8359375")]);
+
+var usNoGroupingDecimalOptions =
+ {
+ style: "decimal",
+ useGrouping: false,
+ maximumFractionDigits: 7 // minimum defaults to 0
+ };
+var usNoGroupingDecimalFormatter =
+ new Intl.NumberFormat("en-US", usNoGroupingDecimalOptions);
+
+assertParts(usNoGroupingDecimalFormatter, 1337,
+ [Integer("1337")]);
+
+assertParts(usNoGroupingDecimalFormatter, -6.25,
+ [MinusSign("-"), Integer("6"), Decimal("."), Fraction("25")]);
+
+assertParts(usNoGroupingDecimalFormatter, -1376.25,
+ [MinusSign("-"),
+ Integer("1376"),
+ Decimal("."),
+ Fraction("25")]);
+
+assertParts(usNoGroupingDecimalFormatter, 124816.8359375,
+ [Integer("124816"),
+ Decimal("."),
+ Fraction("8359375")]);
+
+var deDecimalOptions =
+ {
+ style: "decimal",
+ maximumFractionDigits: 7 // minimum defaults to 0
+ };
+var deDecimalFormatter =
+ new Intl.NumberFormat("de-DE", deDecimalOptions);
+
+assertParts(deDecimalFormatter, 42,
+ [Integer("42")]);
+
+assertParts(deDecimalFormatter, 1337,
+ [Integer("1"), Group("."), Integer("337")]);
+
+assertParts(deDecimalFormatter, -6.25,
+ [MinusSign("-"), Integer("6"), Decimal(","), Fraction("25")]);
+
+assertParts(deDecimalFormatter, -1376.25,
+ [MinusSign("-"),
+ Integer("1"),
+ Group("."),
+ Integer("376"),
+ Decimal(","),
+ Fraction("25")]);
+
+assertParts(deDecimalFormatter, 124816.8359375,
+ [Integer("124"),
+ Group("."),
+ Integer("816"),
+ Decimal(","),
+ Fraction("8359375")]);
+
+var deNoGroupingDecimalOptions =
+ {
+ style: "decimal",
+ useGrouping: false,
+ maximumFractionDigits: 7 // minimum defaults to 0
+ };
+var deNoGroupingDecimalFormatter =
+ new Intl.NumberFormat("de-DE", deNoGroupingDecimalOptions);
+
+assertParts(deNoGroupingDecimalFormatter, 1337,
+ [Integer("1337")]);
+
+assertParts(deNoGroupingDecimalFormatter, -6.25,
+ [MinusSign("-"), Integer("6"), Decimal(","), Fraction("25")]);
+
+assertParts(deNoGroupingDecimalFormatter, -1376.25,
+ [MinusSign("-"),
+ Integer("1376"),
+ Decimal(","),
+ Fraction("25")]);
+
+assertParts(deNoGroupingDecimalFormatter, 124816.8359375,
+ [Integer("124816"),
+ Decimal(","),
+ Fraction("8359375")]);
+
+//-----------------------------------------------------------------------------
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, 'ok');
+
+print("Tests complete");
diff --git a/js/src/tests/non262/Intl/NumberFormat/formatting-NaN.js b/js/src/tests/non262/Intl/NumberFormat/formatting-NaN.js
new file mode 100644
index 0000000000..430d74222e
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/formatting-NaN.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1484943;
+var summary = "Don't crash doing format/formatToParts on -NaN";
+
+print(BUGNUMBER + ": " + summary);
+
+//-----------------------------------------------------------------------------
+
+assertEq("formatToParts" in Intl.NumberFormat(), true);
+
+var nf = new Intl.NumberFormat("en-US");
+var parts;
+
+var values = [NaN, -NaN];
+
+for (var v of values)
+{
+ assertEq(nf.format(v), "NaN");
+
+ parts = nf.formatToParts(v);
+ assertEq(parts.length, 1);
+ assertEq(parts[0].type, "nan");
+ assertEq(parts[0].value, "NaN");
+}
+
+//-----------------------------------------------------------------------------
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, 'ok');
+
+print("Tests complete");
diff --git a/js/src/tests/non262/Intl/NumberFormat/negativeZeroFractionDigits.js b/js/src/tests/non262/Intl/NumberFormat/negativeZeroFractionDigits.js
new file mode 100644
index 0000000000..0db461f50e
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/negativeZeroFractionDigits.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const optionsList = [
+ {minimumFractionDigits: -0, maximumFractionDigits: -0},
+ {minimumFractionDigits: -0, maximumFractionDigits: +0},
+ {minimumFractionDigits: +0, maximumFractionDigits: -0},
+ {minimumFractionDigits: +0, maximumFractionDigits: +0},
+];
+
+for (let options of optionsList) {
+ let numberFormat = new Intl.NumberFormat("en-US", options);
+
+ let {minimumFractionDigits, maximumFractionDigits} = numberFormat.resolvedOptions();
+ assertEq(minimumFractionDigits, +0);
+ assertEq(maximumFractionDigits, +0);
+
+ assertEq(numberFormat.format(123), "123");
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/notation-compact-long.js b/js/src/tests/non262/Intl/NumberFormat/notation-compact-long.js
new file mode 100644
index 0000000000..576c45858c
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/notation-compact-long.js
@@ -0,0 +1,134 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const {
+ Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, Group, Literal,
+ Compact,
+} = NumberFormatParts;
+
+const testcases = [
+ {
+ locale: "en",
+ options: {
+ notation: "compact",
+ compactDisplay: "long",
+ },
+ values: [
+ {value: +0, string: "0", parts: [Integer("0")]},
+ {value: -0, string: "-0", parts: [MinusSign("-"), Integer("0")]},
+ {value: 0n, string: "0", parts: [Integer("0")]},
+
+ {value: 1, string: "1", parts: [Integer("1")]},
+ {value: 10, string: "10", parts: [Integer("10")]},
+ {value: 100, string: "100", parts: [Integer("100")]},
+ {value: 1000, string: "1 thousand", parts: [Integer("1"), Literal(" "), Compact("thousand")]},
+ {value: 10000, string: "10 thousand", parts: [Integer("10"), Literal(" "), Compact("thousand")]},
+ {value: 100000, string: "100 thousand", parts: [Integer("100"), Literal(" "), Compact("thousand")]},
+ {value: 1000000, string: "1 million", parts: [Integer("1"), Literal(" "), Compact("million")]},
+ {value: 10000000, string: "10 million", parts: [Integer("10"), Literal(" "), Compact("million")]},
+ {value: 100000000, string: "100 million", parts: [Integer("100"), Literal(" "), Compact("million")]},
+ {value: 1000000000, string: "1 billion", parts: [Integer("1"), Literal(" "), Compact("billion")]},
+ {value: 10000000000, string: "10 billion", parts: [Integer("10"), Literal(" "), Compact("billion")]},
+ {value: 100000000000, string: "100 billion", parts: [Integer("100"), Literal(" "), Compact("billion")]},
+ {value: 1000000000000, string: "1 trillion", parts: [Integer("1"), Literal(" "), Compact("trillion")]},
+ {value: 10000000000000, string: "10 trillion", parts: [Integer("10"), Literal(" "), Compact("trillion")]},
+ {value: 100000000000000, string: "100 trillion", parts: [Integer("100"), Literal(" "), Compact("trillion")]},
+ {value: 1000000000000000, string: "1000 trillion", parts: [Integer("1000"), Literal(" "), Compact("trillion")]},
+ {value: 10000000000000000, string: "10,000 trillion", parts: [Integer("10"), Group(","), Integer("000"), Literal(" "), Compact("trillion")]},
+ {value: 100000000000000000, string: "100,000 trillion", parts: [Integer("100"), Group(","), Integer("000"), Literal(" "), Compact("trillion")]},
+
+ {value: 1n, string: "1", parts: [Integer("1")]},
+ {value: 10n, string: "10", parts: [Integer("10")]},
+ {value: 100n, string: "100", parts: [Integer("100")]},
+ {value: 1000n, string: "1 thousand", parts: [Integer("1"), Literal(" "), Compact("thousand")]},
+ {value: 10000n, string: "10 thousand", parts: [Integer("10"), Literal(" "), Compact("thousand")]},
+ {value: 100000n, string: "100 thousand", parts: [Integer("100"), Literal(" "), Compact("thousand")]},
+ {value: 1000000n, string: "1 million", parts: [Integer("1"), Literal(" "), Compact("million")]},
+ {value: 10000000n, string: "10 million", parts: [Integer("10"), Literal(" "), Compact("million")]},
+ {value: 100000000n, string: "100 million", parts: [Integer("100"), Literal(" "), Compact("million")]},
+ {value: 1000000000n, string: "1 billion", parts: [Integer("1"), Literal(" "), Compact("billion")]},
+ {value: 10000000000n, string: "10 billion", parts: [Integer("10"), Literal(" "), Compact("billion")]},
+ {value: 100000000000n, string: "100 billion", parts: [Integer("100"), Literal(" "), Compact("billion")]},
+ {value: 1000000000000n, string: "1 trillion", parts: [Integer("1"), Literal(" "), Compact("trillion")]},
+ {value: 10000000000000n, string: "10 trillion", parts: [Integer("10"), Literal(" "), Compact("trillion")]},
+ {value: 100000000000000n, string: "100 trillion", parts: [Integer("100"), Literal(" "), Compact("trillion")]},
+ {value: 1000000000000000n, string: "1000 trillion", parts: [Integer("1000"), Literal(" "), Compact("trillion")]},
+ {value: 10000000000000000n, string: "10,000 trillion", parts: [Integer("10"), Group(","), Integer("000"), Literal(" "), Compact("trillion")]},
+ {value: 100000000000000000n, string: "100,000 trillion", parts: [Integer("100"), Group(","), Integer("000"), Literal(" "), Compact("trillion")]},
+
+ {value: 0.1, string: "0.1", parts: [Integer("0"), Decimal("."), Fraction("1")]},
+ {value: 0.01, string: "0.01", parts: [Integer("0"), Decimal("."), Fraction("01")]},
+ {value: 0.001, string: "0.001", parts: [Integer("0"), Decimal("."), Fraction("001")]},
+ {value: 0.0001, string: "0.0001", parts: [Integer("0"), Decimal("."), Fraction("0001")]},
+ {value: 0.00001, string: "0.00001", parts: [Integer("0"), Decimal("."), Fraction("00001")]},
+ {value: 0.000001, string: "0.000001", parts: [Integer("0"), Decimal("."), Fraction("000001")]},
+ {value: 0.0000001, string: "0.0000001", parts: [Integer("0"), Decimal("."), Fraction("0000001")]},
+
+ {value: 12, string: "12", parts: [Integer("12")]},
+ {value: 123, string: "123", parts: [Integer("123")]},
+ {value: 1234, string: "1.2 thousand", parts: [Integer("1"), Decimal("."), Fraction("2"), Literal(" "), Compact("thousand")]},
+ {value: 12345, string: "12 thousand", parts: [Integer("12"), Literal(" "), Compact("thousand")]},
+ {value: 123456, string: "123 thousand", parts: [Integer("123"), Literal(" "), Compact("thousand")]},
+ {value: 1234567, string: "1.2 million", parts: [Integer("1"), Decimal("."), Fraction("2"), Literal(" "), Compact("million")]},
+ {value: 12345678, string: "12 million", parts: [Integer("12"), Literal(" "), Compact("million")]},
+ {value: 123456789, string: "123 million", parts: [Integer("123"), Literal(" "), Compact("million")]},
+
+ {value: Infinity, string: "∞", parts: [Inf("∞")]},
+ {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]},
+
+ {value: NaN, string: "NaN", parts: [Nan("NaN")]},
+ {value: -NaN, string: "NaN", parts: [Nan("NaN")]},
+ ],
+ },
+
+ // The "{compactName}" placeholder may have different plural forms.
+ {
+ locale: "de",
+ options: {
+ notation: "compact",
+ compactDisplay: "long",
+ },
+ values: [
+ {value: 1e6, string: "1 Million", parts: [Integer("1"), Literal(" "), Compact("Million")]},
+ {value: 2e6, string: "2 Millionen", parts: [Integer("2"), Literal(" "), Compact("Millionen")]},
+ {value: 1e9, string: "1 Milliarde", parts: [Integer("1"), Literal(" "), Compact("Milliarde")]},
+ {value: 2e9, string: "2 Milliarden", parts: [Integer("2"), Literal(" "), Compact("Milliarden")]},
+ ],
+ },
+
+ // Digits are grouped in myriads (every 10,000) in Japanese.
+ {
+ locale: "ja",
+ options: {
+ notation: "compact",
+ compactDisplay: "long",
+ },
+ values: [
+ {value: 1, string: "1", parts: [Integer("1")]},
+ {value: 10, string: "10", parts: [Integer("10")]},
+ {value: 100, string: "100", parts: [Integer("100")]},
+ {value: 1000, string: "1000", parts: [Integer("1000")]},
+
+ {value: 10e3, string: "1\u4E07", parts: [Integer("1"), Compact("\u4E07")]},
+ {value: 100e3, string: "10\u4E07", parts: [Integer("10"), Compact("\u4E07")]},
+ {value: 1000e3, string: "100\u4E07", parts: [Integer("100"), Compact("\u4E07")]},
+ {value: 10000e3, string: "1000\u4E07", parts: [Integer("1000"), Compact("\u4E07")]},
+
+ {value: 10e7, string: "1\u5104", parts: [Integer("1"), Compact("\u5104")]},
+ {value: 100e7, string: "10\u5104", parts: [Integer("10"), Compact("\u5104")]},
+ {value: 1000e7, string: "100\u5104", parts: [Integer("100"), Compact("\u5104")]},
+ {value: 10000e7, string: "1000\u5104", parts: [Integer("1000"), Compact("\u5104")]},
+
+ {value: 10e11, string: "1\u5146", parts: [Integer("1"), Compact("\u5146")]},
+ {value: 100e11, string: "10\u5146", parts: [Integer("10"), Compact("\u5146")]},
+ {value: 1000e11, string: "100\u5146", parts: [Integer("100"), Compact("\u5146")]},
+ {value: 10000e11, string: "1000\u5146", parts: [Integer("1000"), Compact("\u5146")]},
+
+ {value: 100000e11, string: "10,000\u5146", parts: [Integer("10"), Group(","), Integer("000"), Compact("\u5146")]},
+ ],
+ },
+];
+
+runNumberFormattingTestcases(testcases);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/notation-compact-short.js b/js/src/tests/non262/Intl/NumberFormat/notation-compact-short.js
new file mode 100644
index 0000000000..f3a546294d
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/notation-compact-short.js
@@ -0,0 +1,136 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const {
+ Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, Group, Literal,
+ Compact,
+} = NumberFormatParts;
+
+const testcases = [
+ {
+ locale: "en",
+ options: {
+ notation: "compact",
+ compactDisplay: "short",
+ },
+ values: [
+ {value: +0, string: "0", parts: [Integer("0")]},
+ {value: -0, string: "-0", parts: [MinusSign("-"), Integer("0")]},
+ {value: 0n, string: "0", parts: [Integer("0")]},
+
+ {value: 1, string: "1", parts: [Integer("1")]},
+ {value: 10, string: "10", parts: [Integer("10")]},
+ {value: 100, string: "100", parts: [Integer("100")]},
+ {value: 1000, string: "1K", parts: [Integer("1"), Compact("K")]},
+ {value: 10000, string: "10K", parts: [Integer("10"), Compact("K")]},
+ {value: 100000, string: "100K", parts: [Integer("100"), Compact("K")]},
+ {value: 1000000, string: "1M", parts: [Integer("1"), Compact("M")]},
+ {value: 10000000, string: "10M", parts: [Integer("10"), Compact("M")]},
+ {value: 100000000, string: "100M", parts: [Integer("100"), Compact("M")]},
+ {value: 1000000000, string: "1B", parts: [Integer("1"), Compact("B")]},
+ {value: 10000000000, string: "10B", parts: [Integer("10"), Compact("B")]},
+ {value: 100000000000, string: "100B", parts: [Integer("100"), Compact("B")]},
+ {value: 1000000000000, string: "1T", parts: [Integer("1"), Compact("T")]},
+ {value: 10000000000000, string: "10T", parts: [Integer("10"), Compact("T")]},
+ {value: 100000000000000, string: "100T", parts: [Integer("100"), Compact("T")]},
+ {value: 1000000000000000, string: "1000T", parts: [Integer("1000"), Compact("T")]},
+ {value: 10000000000000000, string: "10,000T", parts: [Integer("10"), Group(","), Integer("000"), Compact("T")]},
+ {value: 100000000000000000, string: "100,000T", parts: [Integer("100"), Group(","), Integer("000"), Compact("T")]},
+
+ {value: 1n, string: "1", parts: [Integer("1")]},
+ {value: 10n, string: "10", parts: [Integer("10")]},
+ {value: 100n, string: "100", parts: [Integer("100")]},
+ {value: 1000n, string: "1K", parts: [Integer("1"), Compact("K")]},
+ {value: 10000n, string: "10K", parts: [Integer("10"), Compact("K")]},
+ {value: 100000n, string: "100K", parts: [Integer("100"), Compact("K")]},
+ {value: 1000000n, string: "1M", parts: [Integer("1"), Compact("M")]},
+ {value: 10000000n, string: "10M", parts: [Integer("10"), Compact("M")]},
+ {value: 100000000n, string: "100M", parts: [Integer("100"), Compact("M")]},
+ {value: 1000000000n, string: "1B", parts: [Integer("1"), Compact("B")]},
+ {value: 10000000000n, string: "10B", parts: [Integer("10"), Compact("B")]},
+ {value: 100000000000n, string: "100B", parts: [Integer("100"), Compact("B")]},
+ {value: 1000000000000n, string: "1T", parts: [Integer("1"), Compact("T")]},
+ {value: 10000000000000n, string: "10T", parts: [Integer("10"), Compact("T")]},
+ {value: 100000000000000n, string: "100T", parts: [Integer("100"), Compact("T")]},
+ {value: 1000000000000000n, string: "1000T", parts: [Integer("1000"), Compact("T")]},
+ {value: 10000000000000000n, string: "10,000T", parts: [Integer("10"), Group(","), Integer("000"), Compact("T")]},
+ {value: 100000000000000000n, string: "100,000T", parts: [Integer("100"), Group(","), Integer("000"), Compact("T")]},
+
+ {value: 0.1, string: "0.1", parts: [Integer("0"), Decimal("."), Fraction("1")]},
+ {value: 0.01, string: "0.01", parts: [Integer("0"), Decimal("."), Fraction("01")]},
+ {value: 0.001, string: "0.001", parts: [Integer("0"), Decimal("."), Fraction("001")]},
+ {value: 0.0001, string: "0.0001", parts: [Integer("0"), Decimal("."), Fraction("0001")]},
+ {value: 0.00001, string: "0.00001", parts: [Integer("0"), Decimal("."), Fraction("00001")]},
+ {value: 0.000001, string: "0.000001", parts: [Integer("0"), Decimal("."), Fraction("000001")]},
+ {value: 0.0000001, string: "0.0000001", parts: [Integer("0"), Decimal("."), Fraction("0000001")]},
+
+ {value: 12, string: "12", parts: [Integer("12")]},
+ {value: 123, string: "123", parts: [Integer("123")]},
+ {value: 1234, string: "1.2K", parts: [Integer("1"), Decimal("."), Fraction("2"), Compact("K")]},
+ {value: 12345, string: "12K", parts: [Integer("12"), Compact("K")]},
+ {value: 123456, string: "123K", parts: [Integer("123"), Compact("K")]},
+ {value: 1234567, string: "1.2M", parts: [Integer("1"), Decimal("."), Fraction("2"), Compact("M")]},
+ {value: 12345678, string: "12M", parts: [Integer("12"), Compact("M")]},
+ {value: 123456789, string: "123M", parts: [Integer("123"), Compact("M")]},
+
+ {value: Infinity, string: "∞", parts: [Inf("∞")]},
+ {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]},
+
+ {value: NaN, string: "NaN", parts: [Nan("NaN")]},
+ {value: -NaN, string: "NaN", parts: [Nan("NaN")]},
+ ],
+ },
+
+ // Compact symbol can consist of multiple characters, e.g. when it's an abbreviation.
+ {
+ locale: "de",
+ options: {
+ notation: "compact",
+ compactDisplay: "short",
+ },
+ values: [
+ {value: 1e6, string: "1\u00A0Mio.", parts: [Integer("1"), Literal("\u00A0"), Compact("Mio.")]},
+ {value: 1e9, string: "1\u00A0Mrd.", parts: [Integer("1"), Literal("\u00A0"), Compact("Mrd.")]},
+ {value: 1e12, string: "1\u00A0Bio.", parts: [Integer("1"), Literal("\u00A0"), Compact("Bio.")]},
+
+ // CLDR doesn't support "Billiarde" (Brd.), Trillion (Trill.), etc.
+ {value: 1e15, string: "1000\u00A0Bio.", parts: [Integer("1000"), Literal("\u00A0"), Compact("Bio.")]},
+ ],
+ },
+
+ // Digits are grouped in myriads (every 10,000) in Chinese.
+ {
+ locale: "zh",
+ options: {
+ notation: "compact",
+ compactDisplay: "short",
+ },
+ values: [
+ {value: 1, string: "1", parts: [Integer("1")]},
+ {value: 10, string: "10", parts: [Integer("10")]},
+ {value: 100, string: "100", parts: [Integer("100")]},
+ {value: 1000, string: "1000", parts: [Integer("1000")]},
+
+ {value: 10e3, string: "1\u4E07", parts: [Integer("1"), Compact("\u4E07")]},
+ {value: 100e3, string: "10\u4E07", parts: [Integer("10"), Compact("\u4E07")]},
+ {value: 1000e3, string: "100\u4E07", parts: [Integer("100"), Compact("\u4E07")]},
+ {value: 10000e3, string: "1000\u4E07", parts: [Integer("1000"), Compact("\u4E07")]},
+
+ {value: 10e7, string: "1\u4EBF", parts: [Integer("1"), Compact("\u4EBF")]},
+ {value: 100e7, string: "10\u4EBF", parts: [Integer("10"), Compact("\u4EBF")]},
+ {value: 1000e7, string: "100\u4EBF", parts: [Integer("100"), Compact("\u4EBF")]},
+ {value: 10000e7, string: "1000\u4EBF", parts: [Integer("1000"), Compact("\u4EBF")]},
+
+ {value: 10e11, string: "1\u4E07\u4EBF", parts: [Integer("1"), Compact("\u4E07\u4EBF")]},
+ {value: 100e11, string: "10\u4E07\u4EBF", parts: [Integer("10"), Compact("\u4E07\u4EBF")]},
+ {value: 1000e11, string: "100\u4E07\u4EBF", parts: [Integer("100"), Compact("\u4E07\u4EBF")]},
+ {value: 10000e11, string: "1000\u4E07\u4EBF", parts: [Integer("1000"), Compact("\u4E07\u4EBF")]},
+
+ {value: 100000e11, string: "10,000\u4E07\u4EBF", parts: [Integer("10"), Group(","), Integer("000"), Compact("\u4E07\u4EBF")]},
+ ],
+ },
+];
+
+runNumberFormattingTestcases(testcases);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/notation-engineering.js b/js/src/tests/non262/Intl/NumberFormat/notation-engineering.js
new file mode 100644
index 0000000000..b4c0e19e53
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/notation-engineering.js
@@ -0,0 +1,136 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const {
+ Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, Group,
+ ExponentSeparator, ExponentInteger, ExponentMinusSign,
+} = NumberFormatParts;
+
+const testcases = [
+ {
+ locale: "en",
+ options: {
+ notation: "engineering",
+ },
+ values: [
+ {value: +0, string: "0E0", parts: [Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]},
+ {value: -0, string: "-0E0", parts: [MinusSign("-"), Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]},
+ {value: 0n, string: "0E0", parts: [Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]},
+
+ {value: 1, string: "1E0", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("0")]},
+ {value: 10, string: "10E0", parts: [Integer("10"), ExponentSeparator("E"), ExponentInteger("0")]},
+ {value: 100, string: "100E0", parts: [Integer("100"), ExponentSeparator("E"), ExponentInteger("0")]},
+ {value: 1000, string: "1E3", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("3")]},
+ {value: 10000, string: "10E3", parts: [Integer("10"), ExponentSeparator("E"), ExponentInteger("3")]},
+ {value: 100000, string: "100E3", parts: [Integer("100"), ExponentSeparator("E"), ExponentInteger("3")]},
+ {value: 1000000, string: "1E6", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("6")]},
+
+ {value: 1n, string: "1E0", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("0")]},
+ {value: 10n, string: "10E0", parts: [Integer("10"), ExponentSeparator("E"), ExponentInteger("0")]},
+ {value: 100n, string: "100E0", parts: [Integer("100"), ExponentSeparator("E"), ExponentInteger("0")]},
+ {value: 1000n, string: "1E3", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("3")]},
+ {value: 10000n, string: "10E3", parts: [Integer("10"), ExponentSeparator("E"), ExponentInteger("3")]},
+ {value: 100000n, string: "100E3", parts: [Integer("100"), ExponentSeparator("E"), ExponentInteger("3")]},
+ {value: 1000000n, string: "1E6", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("6")]},
+
+ {value: 0.1, string: "100E-3", parts: [Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")]},
+ {value: 0.01, string: "10E-3", parts: [Integer("10"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")]},
+ {value: 0.001, string: "1E-3", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")]},
+ {value: 0.0001, string: "100E-6", parts: [Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("6")]},
+ {value: 0.00001, string: "10E-6", parts: [Integer("10"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("6")]},
+ {value: 0.000001, string: "1E-6", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("6")]},
+ {value: 0.0000001, string: "100E-9", parts: [Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("9")]},
+
+ {value: Infinity, string: "∞", parts: [Inf("∞")]},
+ {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]},
+
+ {value: NaN, string: "NaN", parts: [Nan("NaN")]},
+ {value: -NaN, string: "NaN", parts: [Nan("NaN")]},
+ ],
+ },
+
+ // Exponent modifications take place early, so while in the "standard" notation
+ // `Intl.NumberFormat("en", {maximumFractionDigits: 0}).format(0.1)` returns "0", for
+ // "engineering" notation the result string is not "0", but instead "100E-3".
+
+ {
+ locale: "en",
+ options: {
+ notation: "engineering",
+ maximumFractionDigits: 0,
+ },
+ values: [
+ {value: 0.1, string: "100E-3", parts: [
+ Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")
+ ]},
+ ],
+ },
+
+ {
+ locale: "en",
+ options: {
+ notation: "engineering",
+ minimumFractionDigits: 4,
+ },
+ values: [
+ {value: 10, string: "10.0000E0", parts: [
+ Integer("10"), Decimal("."), Fraction("0000"), ExponentSeparator("E"), ExponentInteger("0")
+ ]},
+ {value: 0.1, string: "100.0000E-3", parts: [
+ Integer("100"), Decimal("."), Fraction("0000"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")
+ ]},
+ ],
+ },
+
+ {
+ locale: "en",
+ options: {
+ notation: "engineering",
+ minimumIntegerDigits: 4,
+ },
+ values: [
+ {value: 10, string: "0,010E0", parts: [
+ Integer("0"), Group(","), Integer("010"), ExponentSeparator("E"), ExponentInteger("0")
+ ]},
+ {value: 0.1, string: "0,100E-3", parts: [
+ Integer("0"), Group(","), Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")
+ ]},
+ ],
+ },
+
+ {
+ locale: "en",
+ options: {
+ notation: "engineering",
+ minimumSignificantDigits: 4,
+ },
+ values: [
+ {value: 10, string: "10.00E0", parts: [
+ Integer("10"), Decimal("."), Fraction("00"), ExponentSeparator("E"), ExponentInteger("0")
+ ]},
+ {value: 0.1, string: "100.0E-3", parts: [
+ Integer("100"), Decimal("."), Fraction("0"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")
+ ]},
+ ],
+ },
+
+ {
+ locale: "en",
+ options: {
+ notation: "engineering",
+ maximumSignificantDigits: 1,
+ },
+ values: [
+ {value: 12, string: "10E0", parts: [
+ Integer("10"), ExponentSeparator("E"), ExponentInteger("0")
+ ]},
+ {value: 0.12, string: "100E-3", parts: [
+ Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")
+ ]},
+ ],
+ },
+];
+
+runNumberFormattingTestcases(testcases);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/notation-scientific.js b/js/src/tests/non262/Intl/NumberFormat/notation-scientific.js
new file mode 100644
index 0000000000..f14a660129
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/notation-scientific.js
@@ -0,0 +1,136 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const {
+ Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, Group,
+ ExponentSeparator, ExponentInteger, ExponentMinusSign,
+} = NumberFormatParts;
+
+const testcases = [
+ {
+ locale: "en",
+ options: {
+ notation: "scientific",
+ },
+ values: [
+ {value: +0, string: "0E0", parts: [Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]},
+ {value: -0, string: "-0E0", parts: [MinusSign("-"), Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]},
+ {value: 0n, string: "0E0", parts: [Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]},
+
+ {value: 1, string: "1E0", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("0")]},
+ {value: 10, string: "1E1", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("1")]},
+ {value: 100, string: "1E2", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("2")]},
+ {value: 1000, string: "1E3", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("3")]},
+ {value: 10000, string: "1E4", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("4")]},
+ {value: 100000, string: "1E5", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("5")]},
+ {value: 1000000, string: "1E6", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("6")]},
+
+ {value: 1n, string: "1E0", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("0")]},
+ {value: 10n, string: "1E1", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("1")]},
+ {value: 100n, string: "1E2", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("2")]},
+ {value: 1000n, string: "1E3", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("3")]},
+ {value: 10000n, string: "1E4", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("4")]},
+ {value: 100000n, string: "1E5", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("5")]},
+ {value: 1000000n, string: "1E6", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("6")]},
+
+ {value: 0.1, string: "1E-1", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")]},
+ {value: 0.01, string: "1E-2", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("2")]},
+ {value: 0.001, string: "1E-3", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")]},
+ {value: 0.0001, string: "1E-4", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("4")]},
+ {value: 0.00001, string: "1E-5", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("5")]},
+ {value: 0.000001, string: "1E-6", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("6")]},
+ {value: 0.0000001, string: "1E-7", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("7")]},
+
+ {value: Infinity, string: "∞", parts: [Inf("∞")]},
+ {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]},
+
+ {value: NaN, string: "NaN", parts: [Nan("NaN")]},
+ {value: -NaN, string: "NaN", parts: [Nan("NaN")]},
+ ],
+ },
+
+ // Exponent modifications take place early, so while in the "standard" notation
+ // `Intl.NumberFormat("en", {maximumFractionDigits: 0}).format(0.1)` returns "0", for
+ // "scientific" notation the result string is not "0", but instead "1E-1".
+
+ {
+ locale: "en",
+ options: {
+ notation: "scientific",
+ maximumFractionDigits: 0,
+ },
+ values: [
+ {value: 0.1, string: "1E-1", parts: [
+ Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")
+ ]},
+ ],
+ },
+
+ {
+ locale: "en",
+ options: {
+ notation: "scientific",
+ minimumFractionDigits: 4,
+ },
+ values: [
+ {value: 10, string: "1.0000E1", parts: [
+ Integer("1"), Decimal("."), Fraction("0000"), ExponentSeparator("E"), ExponentInteger("1")
+ ]},
+ {value: 0.1, string: "1.0000E-1", parts: [
+ Integer("1"), Decimal("."), Fraction("0000"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")
+ ]},
+ ],
+ },
+
+ {
+ locale: "en",
+ options: {
+ notation: "scientific",
+ minimumIntegerDigits: 4,
+ },
+ values: [
+ {value: 10, string: "0,001E1", parts: [
+ Integer("0"), Group(","), Integer("001"), ExponentSeparator("E"), ExponentInteger("1")
+ ]},
+ {value: 0.1, string: "0,001E-1", parts: [
+ Integer("0"), Group(","), Integer("001"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")
+ ]},
+ ],
+ },
+
+ {
+ locale: "en",
+ options: {
+ notation: "scientific",
+ minimumSignificantDigits: 4,
+ },
+ values: [
+ {value: 10, string: "1.000E1", parts: [
+ Integer("1"), Decimal("."), Fraction("000"), ExponentSeparator("E"), ExponentInteger("1")
+ ]},
+ {value: 0.1, string: "1.000E-1", parts: [
+ Integer("1"), Decimal("."), Fraction("000"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")
+ ]},
+ ],
+ },
+
+ {
+ locale: "en",
+ options: {
+ notation: "scientific",
+ maximumSignificantDigits: 1,
+ },
+ values: [
+ {value: 12, string: "1E1", parts: [
+ Integer("1"), ExponentSeparator("E"), ExponentInteger("1")
+ ]},
+ {value: 0.12, string: "1E-1", parts: [
+ Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")
+ ]},
+ ],
+ },
+];
+
+runNumberFormattingTestcases(testcases);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/numberingSystem-format.js b/js/src/tests/non262/Intl/NumberFormat/numberingSystem-format.js
new file mode 100644
index 0000000000..c0b9ba78ed
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/numberingSystem-format.js
@@ -0,0 +1,18 @@
+for (let [numberingSystem, {digits, algorithmic}] of Object.entries(numberingSystems)) {
+ if (algorithmic) {
+ // We don't yet support algorithmic numbering systems.
+ continue;
+ }
+
+ let nf = new Intl.NumberFormat("en", {numberingSystem});
+
+ assertEq([...digits].length, 10, "expect exactly ten digits for each numbering system");
+
+ let i = 0;
+ for (let digit of digits) {
+ assertEq(nf.format(i++), digit);
+ }
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/numberingSystem-option.js b/js/src/tests/non262/Intl/NumberFormat/numberingSystem-option.js
new file mode 100644
index 0000000000..4624ee5192
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/numberingSystem-option.js
@@ -0,0 +1,60 @@
+const defaultLocale = "en";
+const defaultNumberingSystem = new Intl.NumberFormat(defaultLocale).resolvedOptions().numberingSystem;
+
+function createWithLocale(locale, numberingSystem) {
+ return new Intl.NumberFormat(locale, {numberingSystem});
+}
+
+function create(numberingSystem) {
+ return createWithLocale(defaultLocale, numberingSystem);
+}
+
+// Empty string should throw.
+assertThrowsInstanceOf(() => create(""), RangeError);
+
+// Trailing \0 should throw.
+assertThrowsInstanceOf(() => create("latn\0"), RangeError);
+
+// Too short or too long strings should throw.
+assertThrowsInstanceOf(() => create("a"), RangeError);
+assertThrowsInstanceOf(() => create("toolongstring"), RangeError);
+
+// Throw even when prefix is valid.
+assertThrowsInstanceOf(() => create("latn-toolongstring"), RangeError);
+
+// |numberingSystem| can be set to |undefined|.
+let nf = create(undefined);
+assertEq(nf.resolvedOptions().numberingSystem, defaultNumberingSystem);
+
+// Unsupported numbering systems are ignored.
+nf = create("xxxxxxxx");
+assertEq(nf.resolvedOptions().numberingSystem, defaultNumberingSystem);
+
+// Numbering system in options overwrite Unicode extension keyword.
+nf = createWithLocale(`${defaultLocale}-u-nu-thai`, "arab");
+assertEq(nf.resolvedOptions().locale, defaultLocale);
+assertEq(nf.resolvedOptions().numberingSystem, "arab");
+
+// |numberingSystem| option ignores case.
+nf = create("ARAB");
+assertEq(nf.resolvedOptions().locale, defaultLocale);
+assertEq(nf.resolvedOptions().numberingSystem, "arab");
+
+for (let [numberingSystem, {algorithmic}] of Object.entries(numberingSystems)) {
+ let nf1 = new Intl.NumberFormat(`${defaultLocale}-u-nu-${numberingSystem}`);
+ let nf2 = new Intl.NumberFormat(defaultLocale, {numberingSystem});
+
+ if (!algorithmic) {
+ assertEq(nf1.resolvedOptions().numberingSystem, numberingSystem);
+ assertEq(nf2.resolvedOptions().numberingSystem, numberingSystem);
+ } else {
+ // We don't yet support algorithmic numbering systems.
+ assertEq(nf1.resolvedOptions().numberingSystem, defaultNumberingSystem);
+ assertEq(nf2.resolvedOptions().numberingSystem, defaultNumberingSystem);
+ }
+
+ assertEq(nf2.format(0), nf1.format(0));
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/options-emulate-undefined.js b/js/src/tests/non262/Intl/NumberFormat/options-emulate-undefined.js
new file mode 100644
index 0000000000..ddb2d61350
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/options-emulate-undefined.js
@@ -0,0 +1,13 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// createIsHTMLDDA is only available when running tests in the shell,
+// not the browser
+if (typeof createIsHTMLDDA === "function") {
+ let nf = new Intl.NumberFormat('en-US', createIsHTMLDDA());
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/remove-unicode-extensions.js b/js/src/tests/non262/Intl/NumberFormat/remove-unicode-extensions.js
new file mode 100644
index 0000000000..87cfc317cb
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/remove-unicode-extensions.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Locale processing is supposed to internally remove any Unicode extension
+// sequences in the locale. Test that various weird testcases invoking
+// algorithmic edge cases don't assert or throw exceptions.
+
+var weirdCases =
+ [
+ "en-x-u-foo",
+ "en-a-bar-x-u-foo",
+ "en-x-u-foo-a-bar",
+ "en-a-bar-u-baz-x-u-foo",
+ ];
+
+for (var locale of weirdCases)
+ Intl.NumberFormat(locale).format(5);
+
+assertThrowsInstanceOf(() => Intl.NumberFormat("x-u-foo"), RangeError);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/shell.js b/js/src/tests/non262/Intl/NumberFormat/shell.js
new file mode 100644
index 0000000000..da786bbcc2
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/shell.js
@@ -0,0 +1,48 @@
+function GenericPartCreator(type) {
+ return str => ({ type, value: str });
+}
+
+const NumberFormatParts = {
+ Nan: GenericPartCreator("nan"),
+ Inf: GenericPartCreator("infinity"),
+ Integer: GenericPartCreator("integer"),
+ Group: GenericPartCreator("group"),
+ Decimal: GenericPartCreator("decimal"),
+ Fraction: GenericPartCreator("fraction"),
+ MinusSign: GenericPartCreator("minusSign"),
+ PlusSign: GenericPartCreator("plusSign"),
+ PercentSign: GenericPartCreator("percentSign"),
+ Currency: GenericPartCreator("currency"),
+ Literal: GenericPartCreator("literal"),
+ ExponentSeparator: GenericPartCreator("exponentSeparator"),
+ ExponentMinusSign: GenericPartCreator("exponentMinusSign"),
+ ExponentInteger: GenericPartCreator("exponentInteger"),
+ Compact: GenericPartCreator("compact"),
+ Unit: GenericPartCreator("unit"),
+};
+
+function assertParts(nf, x, expected) {
+ var parts = nf.formatToParts(x);
+ assertEq(parts.map(part => part.value).join(""), nf.format(x),
+ "formatToParts and format must agree");
+
+ var len = parts.length;
+ assertEq(len, expected.length, "parts count mismatch");
+ for (var i = 0; i < len; i++) {
+ assertEq(parts[i].type, expected[i].type, "type mismatch at " + i);
+ assertEq(parts[i].value, expected[i].value, "value mismatch at " + i);
+ }
+}
+
+function runNumberFormattingTestcases(testcases) {
+ for (let {locale, options, values} of testcases) {
+ let nf = new Intl.NumberFormat(locale, options);
+
+ for (let {value, string, parts} of values) {
+ assertEq(nf.format(value), string,
+ `locale=${locale}, options=${JSON.stringify(options)}, value=${value}`);
+
+ assertParts(nf, value, parts);
+ }
+ }
+}
diff --git a/js/src/tests/non262/Intl/NumberFormat/sign-display.js b/js/src/tests/non262/Intl/NumberFormat/sign-display.js
new file mode 100644
index 0000000000..b18517d847
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/sign-display.js
@@ -0,0 +1,187 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const {
+ Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction
+} = NumberFormatParts;
+
+const testcases = [
+ // "auto": Show the sign on negative numbers only.
+ {
+ locale: "en",
+ options: {
+ signDisplay: "auto",
+ },
+ values: [
+ {value: +0, string: "0", parts: [Integer("0")]},
+ {value: -0, string: "-0", parts: [MinusSign("-"), Integer("0")]},
+ {value: 0n, string: "0", parts: [Integer("0")]},
+
+ {value: 1, string: "1", parts: [Integer("1")]},
+ {value: -1, string: "-1", parts: [MinusSign("-"), Integer("1")]},
+ {value: 1n, string: "1", parts: [Integer("1")]},
+ {value: -1n, string: "-1", parts: [MinusSign("-"), Integer("1")]},
+
+ {value: 0.1, string: "0.1", parts: [Integer("0"), Decimal("."), Fraction("1")]},
+ {value: -0.1, string: "-0.1", parts: [MinusSign("-"), Integer("0"), Decimal("."), Fraction("1")]},
+
+ {value: 0.9, string: "0.9", parts: [Integer("0"), Decimal("."), Fraction("9")]},
+ {value: -0.9, string: "-0.9", parts: [MinusSign("-"), Integer("0"), Decimal("."), Fraction("9")]},
+
+ {value: Infinity, string: "∞", parts: [Inf("∞")]},
+ {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]},
+
+ {value: NaN, string: "NaN", parts: [Nan("NaN")]},
+ {value: -NaN, string: "NaN", parts: [Nan("NaN")]},
+ ],
+ },
+
+ // "never": Show the sign on neither positive nor negative numbers.
+ {
+ locale: "en",
+ options: {
+ signDisplay: "never",
+ },
+ values: [
+ {value: +0, string: "0", parts: [Integer("0")]},
+ {value: -0, string: "0", parts: [Integer("0")]},
+ {value: 0n, string: "0", parts: [Integer("0")]},
+
+ {value: 1, string: "1", parts: [Integer("1")]},
+ {value: -1, string: "1", parts: [Integer("1")]},
+ {value: 1n, string: "1", parts: [Integer("1")]},
+ {value: -1n, string: "1", parts: [Integer("1")]},
+
+ {value: 0.1, string: "0.1", parts: [Integer("0"), Decimal("."), Fraction("1")]},
+ {value: -0.1, string: "0.1", parts: [Integer("0"), Decimal("."), Fraction("1")]},
+
+ {value: 0.9, string: "0.9", parts: [Integer("0"), Decimal("."), Fraction("9")]},
+ {value: -0.9, string: "0.9", parts: [Integer("0"), Decimal("."), Fraction("9")]},
+
+ {value: Infinity, string: "∞", parts: [Inf("∞")]},
+ {value: -Infinity, string: "∞", parts: [Inf("∞")]},
+
+ {value: NaN, string: "NaN", parts: [Nan("NaN")]},
+ {value: -NaN, string: "NaN", parts: [Nan("NaN")]},
+ ],
+ },
+
+ // "always": Show the sign on positive and negative numbers including zero.
+ {
+ locale: "en",
+ options: {
+ signDisplay: "always",
+ },
+ values: [
+ {value: +0, string: "+0", parts: [PlusSign("+"), Integer("0")]},
+ {value: -0, string: "-0", parts: [MinusSign("-"), Integer("0")]},
+ {value: 0n, string: "+0", parts: [PlusSign("+"), Integer("0")]},
+
+ {value: 1, string: "+1", parts: [PlusSign("+"), Integer("1")]},
+ {value: -1, string: "-1", parts: [MinusSign("-"), Integer("1")]},
+ {value: 1n, string: "+1", parts: [PlusSign("+"), Integer("1")]},
+ {value: -1n, string: "-1", parts: [MinusSign("-"), Integer("1")]},
+
+ {value: 0.1, string: "+0.1", parts: [PlusSign("+"), Integer("0"), Decimal("."), Fraction("1")]},
+ {value: -0.1, string: "-0.1", parts: [MinusSign("-"), Integer("0"), Decimal("."), Fraction("1")]},
+
+ {value: 0.9, string: "+0.9", parts: [PlusSign("+"), Integer("0"), Decimal("."), Fraction("9")]},
+ {value: -0.9, string: "-0.9", parts: [MinusSign("-"), Integer("0"), Decimal("."), Fraction("9")]},
+
+ {value: Infinity, string: "+∞", parts: [PlusSign("+"), Inf("∞")]},
+ {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]},
+
+ {value: NaN, string: "+NaN", parts: [PlusSign("+"), Nan("NaN")]},
+ {value: -NaN, string: "+NaN", parts: [PlusSign("+"), Nan("NaN")]},
+ ],
+ },
+
+ // "exceptZero": Show the sign on positive and negative numbers but not zero.
+ {
+ locale: "en",
+ options: {
+ signDisplay: "exceptZero",
+ },
+ values: [
+ {value: +0, string: "0", parts: [Integer("0")]},
+ {value: -0, string: "0", parts: [Integer("0")]},
+ {value: 0n, string: "0", parts: [Integer("0")]},
+
+ {value: 1, string: "+1", parts: [PlusSign("+"), Integer("1")]},
+ {value: -1, string: "-1", parts: [MinusSign("-"), Integer("1")]},
+ {value: 1n, string: "+1", parts: [PlusSign("+"), Integer("1")]},
+ {value: -1n, string: "-1", parts: [MinusSign("-"), Integer("1")]},
+
+ {value: 0.1, string: "+0.1", parts: [PlusSign("+"), Integer("0"), Decimal("."), Fraction("1")]},
+ {value: -0.1, string: "-0.1", parts: [MinusSign("-"), Integer("0"), Decimal("."), Fraction("1")]},
+
+ {value: 0.9, string: "+0.9", parts: [PlusSign("+"), Integer("0"), Decimal("."), Fraction("9")]},
+ {value: -0.9, string: "-0.9", parts: [MinusSign("-"), Integer("0"), Decimal("."), Fraction("9")]},
+
+ {value: Infinity, string: "+∞", parts: [PlusSign("+"), Inf("∞")]},
+ {value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]},
+
+ {value: NaN, string: "NaN", parts: [Nan("NaN")]},
+ {value: -NaN, string: "NaN", parts: [Nan("NaN")]},
+ ],
+ },
+
+ // Tests with suppressed fractional digits.
+
+ // "auto": Show the sign on negative numbers only.
+ {
+ locale: "en",
+ options: {
+ signDisplay: "auto",
+ maximumFractionDigits: 0,
+ },
+ values: [
+ {value: +0.1, string: "0", parts: [Integer("0")]},
+ {value: -0.1, string: "-0", parts: [MinusSign("-"), Integer("0")]},
+ ],
+ },
+
+ // "never": Show the sign on neither positive nor negative numbers.
+ {
+ locale: "en",
+ options: {
+ signDisplay: "never",
+ maximumFractionDigits: 0,
+ },
+ values: [
+ {value: +0.1, string: "0", parts: [Integer("0")]},
+ {value: -0.1, string: "0", parts: [Integer("0")]},
+ ],
+ },
+
+ // "always": Show the sign on positive and negative numbers including zero.
+ {
+ locale: "en",
+ options: {
+ signDisplay: "always",
+ maximumFractionDigits: 0,
+ },
+ values: [
+ {value: +0.1, string: "+0", parts: [PlusSign("+"), Integer("0")]},
+ {value: -0.1, string: "-0", parts: [MinusSign("-"), Integer("0")]},
+ ],
+ },
+
+ // "exceptZero": Show the sign on positive and negative numbers but not zero.
+ {
+ locale: "en",
+ options: {
+ signDisplay: "exceptZero",
+ maximumFractionDigits: 0,
+ },
+
+ values: [
+ {value: +0.1, string: "0", parts: [Integer("0")]},
+ {value: -0.1, string: "0", parts: [Integer("0")]},
+ ],
+ }
+];
+
+runNumberFormattingTestcases(testcases);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/significantDigitsOfZero.js b/js/src/tests/non262/Intl/NumberFormat/significantDigitsOfZero.js
new file mode 100644
index 0000000000..c569313266
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/significantDigitsOfZero.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+// -- test that NumberFormat correctly formats 0 with various numbers of significant digits
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var testData = [
+ {minimumSignificantDigits: 1, maximumSignificantDigits: 1, expected: "0"},
+ {minimumSignificantDigits: 1, maximumSignificantDigits: 2, expected: "0"},
+ {minimumSignificantDigits: 1, maximumSignificantDigits: 3, expected: "0"},
+ {minimumSignificantDigits: 1, maximumSignificantDigits: 4, expected: "0"},
+ {minimumSignificantDigits: 1, maximumSignificantDigits: 5, expected: "0"},
+ {minimumSignificantDigits: 2, maximumSignificantDigits: 2, expected: "0.0"},
+ {minimumSignificantDigits: 2, maximumSignificantDigits: 3, expected: "0.0"},
+ {minimumSignificantDigits: 2, maximumSignificantDigits: 4, expected: "0.0"},
+ {minimumSignificantDigits: 2, maximumSignificantDigits: 5, expected: "0.0"},
+ {minimumSignificantDigits: 3, maximumSignificantDigits: 3, expected: "0.00"},
+ {minimumSignificantDigits: 3, maximumSignificantDigits: 4, expected: "0.00"},
+ {minimumSignificantDigits: 3, maximumSignificantDigits: 5, expected: "0.00"},
+];
+
+for (var i = 0; i < testData.length; i++) {
+ var min = testData[i].minimumSignificantDigits;
+ var max = testData[i].maximumSignificantDigits;
+ var options = {minimumSignificantDigits: min, maximumSignificantDigits: max};
+ var format = new Intl.NumberFormat("en-US", options);
+ var actual = format.format(0);
+ var expected = testData[i].expected;
+ assertEq(actual, expected,
+ "Wrong formatted string for 0 with " +
+ "minimumSignificantDigits " + min +
+ ", maximumSignificantDigits " + max +
+ ": expected \"" + expected +
+ "\", actual \"" + actual + "\"");
+}
+
+reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/supportedLocalesOf.js b/js/src/tests/non262/Intl/NumberFormat/supportedLocalesOf.js
new file mode 100644
index 0000000000..5ba4470a98
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/supportedLocalesOf.js
@@ -0,0 +1,371 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl")||xulRuntime.shell)
+// -- test in browser only that ICU has locale data for all Mozilla languages
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This array contains the locales that ICU supports in
+// number formatting whose languages Mozilla localizes Firefox into.
+// Current as of ICU 50.1.2 and Firefox March 2013.
+var locales = [
+ "af",
+ "af-NA",
+ "af-ZA",
+ "ar",
+ "ar-001",
+ "ar-AE",
+ "ar-BH",
+ "ar-DJ",
+ "ar-DZ",
+ "ar-EG",
+ "ar-EH",
+ "ar-ER",
+ "ar-IL",
+ "ar-IQ",
+ "ar-JO",
+ "ar-KM",
+ "ar-KW",
+ "ar-LB",
+ "ar-LY",
+ "ar-MA",
+ "ar-MR",
+ "ar-OM",
+ "ar-PS",
+ "ar-QA",
+ "ar-SA",
+ "ar-SD",
+ "ar-SO",
+ "ar-SY",
+ "ar-TD",
+ "ar-TN",
+ "ar-YE",
+ "as",
+ "as-IN",
+ "be",
+ "be-BY",
+ "bg",
+ "bg-BG",
+ "bn",
+ "bn-BD",
+ "bn-IN",
+ "br",
+ "br-FR",
+ "bs",
+ "bs-Cyrl",
+ "bs-Cyrl-BA",
+ "bs-Latn",
+ "bs-Latn-BA",
+ "ca",
+ "ca-AD",
+ "ca-ES",
+ "cs",
+ "cs-CZ",
+ "cy",
+ "cy-GB",
+ "da",
+ "da-DK",
+ "de",
+ "de-AT",
+ "de-BE",
+ "de-CH",
+ "de-DE",
+ "de-LI",
+ "de-LU",
+ "el",
+ "el-CY",
+ "el-GR",
+ "en",
+ "en-150",
+ "en-AG",
+ "en-AS",
+ "en-AU",
+ "en-BB",
+ "en-BE",
+ "en-BM",
+ "en-BS",
+ "en-BW",
+ "en-BZ",
+ "en-CA",
+ "en-CM",
+ "en-DM",
+ "en-FJ",
+ "en-FM",
+ "en-GB",
+ "en-GD",
+ "en-GG",
+ "en-GH",
+ "en-GI",
+ "en-GM",
+ "en-GU",
+ "en-GY",
+ "en-HK",
+ "en-IE",
+ "en-IM",
+ "en-IN",
+ "en-JE",
+ "en-JM",
+ "en-KE",
+ "en-KI",
+ "en-KN",
+ "en-KY",
+ "en-LC",
+ "en-LR",
+ "en-LS",
+ "en-MG",
+ "en-MH",
+ "en-MP",
+ "en-MT",
+ "en-MU",
+ "en-MW",
+ "en-NA",
+ "en-NG",
+ "en-NZ",
+ "en-PG",
+ "en-PH",
+ "en-PK",
+ "en-PR",
+ "en-PW",
+ "en-SB",
+ "en-SC",
+ "en-SG",
+ "en-SL",
+ "en-SS",
+ "en-SZ",
+ "en-TC",
+ "en-TO",
+ "en-TT",
+ "en-TZ",
+ "en-UG",
+ "en-UM",
+ "en-US",
+ "en-US-POSIX",
+ "en-VC",
+ "en-VG",
+ "en-VI",
+ "en-VU",
+ "en-WS",
+ "en-ZA",
+ "en-ZM",
+ "en-ZW",
+ "eo",
+ "es",
+ "es-419",
+ "es-AR",
+ "es-BO",
+ "es-CL",
+ "es-CO",
+ "es-CR",
+ "es-CU",
+ "es-DO",
+ "es-EA",
+ "es-EC",
+ "es-ES",
+ "es-GQ",
+ "es-GT",
+ "es-HN",
+ "es-IC",
+ "es-MX",
+ "es-NI",
+ "es-PA",
+ "es-PE",
+ "es-PH",
+ "es-PR",
+ "es-PY",
+ "es-SV",
+ "es-US",
+ "es-UY",
+ "es-VE",
+ "et",
+ "et-EE",
+ "eu",
+ "eu-ES",
+ "fa",
+ "fa-AF",
+ "fa-IR",
+ "ff",
+ "ff-SN",
+ "fi",
+ "fi-FI",
+ "fr",
+ "fr-BE",
+ "fr-BF",
+ "fr-BI",
+ "fr-BJ",
+ "fr-BL",
+ "fr-CA",
+ "fr-CD",
+ "fr-CF",
+ "fr-CG",
+ "fr-CH",
+ "fr-CI",
+ "fr-CM",
+ "fr-DJ",
+ "fr-DZ",
+ "fr-FR",
+ "fr-GA",
+ "fr-GF",
+ "fr-GN",
+ "fr-GP",
+ "fr-GQ",
+ "fr-HT",
+ "fr-KM",
+ "fr-LU",
+ "fr-MA",
+ "fr-MC",
+ "fr-MF",
+ "fr-MG",
+ "fr-ML",
+ "fr-MQ",
+ "fr-MR",
+ "fr-MU",
+ "fr-NC",
+ "fr-NE",
+ "fr-PF",
+ "fr-RE",
+ "fr-RW",
+ "fr-SC",
+ "fr-SN",
+ "fr-SY",
+ "fr-TD",
+ "fr-TG",
+ "fr-TN",
+ "fr-VU",
+ "fr-YT",
+ "ga",
+ "ga-IE",
+ "gl",
+ "gl-ES",
+ "gu",
+ "gu-IN",
+ "he",
+ "he-IL",
+ "hi",
+ "hi-IN",
+ "hr",
+ "hr-BA",
+ "hr-HR",
+ "hu",
+ "hu-HU",
+ "hy",
+ "hy-AM",
+ "id",
+ "id-ID",
+ "is",
+ "is-IS",
+ "it",
+ "it-CH",
+ "it-IT",
+ "it-SM",
+ "ja",
+ "ja-JP",
+ "kk",
+ "kk-Cyrl",
+ "kk-Cyrl-KZ",
+ "km",
+ "km-KH",
+ "kn",
+ "kn-IN",
+ "ko",
+ "ko-KP",
+ "ko-KR",
+ "lt",
+ "lt-LT",
+ "lv",
+ "lv-LV",
+ "mk",
+ "mk-MK",
+ "ml",
+ "ml-IN",
+ "mr",
+ "mr-IN",
+ "nb",
+ "nb-NO",
+ "nl",
+ "nl-AW",
+ "nl-BE",
+ "nl-CW",
+ "nl-NL",
+ "nl-SR",
+ "nl-SX",
+ "nn",
+ "nn-NO",
+ "or",
+ "or-IN",
+ "pa",
+ "pa-Arab",
+ "pa-Arab-PK",
+ "pa-Guru",
+ "pa-Guru-IN",
+ "pl",
+ "pl-PL",
+ "pt",
+ "pt-AO",
+ "pt-BR",
+ "pt-CV",
+ "pt-GW",
+ "pt-MO",
+ "pt-MZ",
+ "pt-PT",
+ "pt-ST",
+ "pt-TL",
+ "rm",
+ "rm-CH",
+ "ro",
+ "ro-MD",
+ "ro-RO",
+ "ru",
+ "ru-BY",
+ "ru-KG",
+ "ru-KZ",
+ "ru-MD",
+ "ru-RU",
+ "ru-UA",
+ "si",
+ "si-LK",
+ "sk",
+ "sk-SK",
+ "sl",
+ "sl-SI",
+ "sq",
+ "sq-AL",
+ "sq-MK",
+ "sr",
+ "sr-Cyrl",
+ "sr-Cyrl-BA",
+ "sr-Cyrl-ME",
+ "sr-Cyrl-RS",
+ "sr-Latn",
+ "sr-Latn-BA",
+ "sr-Latn-ME",
+ "sr-Latn-RS",
+ "sv",
+ "sv-AX",
+ "sv-FI",
+ "sv-SE",
+ "te",
+ "te-IN",
+ "th",
+ "th-TH",
+ "tr",
+ "tr-CY",
+ "tr-TR",
+ "uk",
+ "uk-UA",
+ "vi",
+ "vi-VN",
+ "zh",
+ "zh-Hans",
+ "zh-Hans-CN",
+ "zh-Hans-HK",
+ "zh-Hans-MO",
+ "zh-Hans-SG",
+ "zh-Hant",
+ "zh-Hant-HK",
+ "zh-Hant-MO",
+ "zh-Hant-TW",
+];
+
+var count = Intl.NumberFormat.supportedLocalesOf(locales).length;
+
+reportCompare(locales.length, count, "Number of supported locales in Intl.NumberFormat");
diff --git a/js/src/tests/non262/Intl/NumberFormat/toStringTag.js b/js/src/tests/non262/Intl/NumberFormat/toStringTag.js
new file mode 100644
index 0000000000..9ad0901a0b
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/toStringTag.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var desc = Object.getOwnPropertyDescriptor(Intl.NumberFormat.prototype, Symbol.toStringTag);
+
+assertEq(desc !== undefined, true);
+assertEq(desc.value, "Intl.NumberFormat");
+assertEq(desc.writable, false);
+assertEq(desc.enumerable, false);
+assertEq(desc.configurable, true);
+
+assertEq(Object.prototype.toString.call(Intl.NumberFormat.prototype), "[object Intl.NumberFormat]");
+assertEq(Object.prototype.toString.call(new Intl.NumberFormat), "[object Intl.NumberFormat]");
+
+Object.defineProperty(Intl.NumberFormat.prototype, Symbol.toStringTag, {value: "NumberFormat"});
+
+assertEq(Object.prototype.toString.call(Intl.NumberFormat.prototype), "[object NumberFormat]");
+assertEq(Object.prototype.toString.call(new Intl.NumberFormat), "[object NumberFormat]");
+
+delete Intl.NumberFormat.prototype[Symbol.toStringTag];
+
+assertEq(Object.prototype.toString.call(Intl.NumberFormat.prototype), "[object Object]");
+assertEq(Object.prototype.toString.call(new Intl.NumberFormat), "[object Object]");
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/unit-compound-combinations.js b/js/src/tests/non262/Intl/NumberFormat/unit-compound-combinations.js
new file mode 100644
index 0000000000..5160d08d74
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/unit-compound-combinations.js
@@ -0,0 +1,63 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+// Generated by make_intl_data.py. DO NOT EDIT.
+
+const sanctionedSimpleUnitIdentifiers = [
+ "acre",
+ "bit",
+ "byte",
+ "celsius",
+ "centimeter",
+ "day",
+ "degree",
+ "fahrenheit",
+ "fluid-ounce",
+ "foot",
+ "gallon",
+ "gigabit",
+ "gigabyte",
+ "gram",
+ "hectare",
+ "hour",
+ "inch",
+ "kilobit",
+ "kilobyte",
+ "kilogram",
+ "kilometer",
+ "liter",
+ "megabit",
+ "megabyte",
+ "meter",
+ "mile",
+ "mile-scandinavian",
+ "milliliter",
+ "millimeter",
+ "millisecond",
+ "minute",
+ "month",
+ "ounce",
+ "percent",
+ "petabyte",
+ "pound",
+ "second",
+ "stone",
+ "terabit",
+ "terabyte",
+ "week",
+ "yard",
+ "year"
+];
+
+// Test all simple unit identifier combinations are allowed.
+
+for (const numerator of sanctionedSimpleUnitIdentifiers) {
+ for (const denominator of sanctionedSimpleUnitIdentifiers) {
+ const unit = `${numerator}-per-${denominator}`;
+ const nf = new Intl.NumberFormat("en", {style: "unit", unit});
+
+ assertEq(nf.format(1), nf.formatToParts(1).map(p => p.value).join(""));
+ }
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/unit-formatToParts-has-unit-field.js b/js/src/tests/non262/Intl/NumberFormat/unit-formatToParts-has-unit-field.js
new file mode 100644
index 0000000000..fc433711e7
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/unit-formatToParts-has-unit-field.js
@@ -0,0 +1,88 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+// Generated by make_intl_data.py. DO NOT EDIT.
+
+const sanctionedSimpleUnitIdentifiers = [
+ "acre",
+ "bit",
+ "byte",
+ "celsius",
+ "centimeter",
+ "day",
+ "degree",
+ "fahrenheit",
+ "fluid-ounce",
+ "foot",
+ "gallon",
+ "gigabit",
+ "gigabyte",
+ "gram",
+ "hectare",
+ "hour",
+ "inch",
+ "kilobit",
+ "kilobyte",
+ "kilogram",
+ "kilometer",
+ "liter",
+ "megabit",
+ "megabyte",
+ "meter",
+ "mile",
+ "mile-scandinavian",
+ "milliliter",
+ "millimeter",
+ "millisecond",
+ "minute",
+ "month",
+ "ounce",
+ "percent",
+ "petabyte",
+ "pound",
+ "second",
+ "stone",
+ "terabit",
+ "terabyte",
+ "week",
+ "yard",
+ "year"
+];
+
+// Test only English and Chinese to keep the overall runtime reasonable.
+//
+// Chinese is included because it contains more than one "unit" element for
+// certain unit combinations.
+const locales = ["en", "zh"];
+
+// Plural rules for English only differentiate between "one" and "other". Plural
+// rules for Chinese only use "other". That means we only need to test two values
+// per unit.
+const values = [0, 1];
+
+// Ensure unit formatters contain at least one "unit" element.
+
+for (const locale of locales) {
+ for (const unit of sanctionedSimpleUnitIdentifiers) {
+ const nf = new Intl.NumberFormat(locale, {style: "unit", unit});
+
+ for (const value of values) {
+ assertEq(nf.formatToParts(value).some(e => e.type === "unit"), true,
+ `locale=${locale}, unit=${unit}`);
+ }
+ }
+
+ for (const numerator of sanctionedSimpleUnitIdentifiers) {
+ for (const denominator of sanctionedSimpleUnitIdentifiers) {
+ const unit = `${numerator}-per-${denominator}`;
+ const nf = new Intl.NumberFormat(locale, {style: "unit", unit});
+
+ for (const value of values) {
+ assertEq(nf.formatToParts(value).some(e => e.type === "unit"), true,
+ `locale=${locale}, unit=${unit}`);
+ }
+ }
+ }
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/unit-well-formed.js b/js/src/tests/non262/Intl/NumberFormat/unit-well-formed.js
new file mode 100644
index 0000000000..ec1ab4beda
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/unit-well-formed.js
@@ -0,0 +1,250 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+// Generated by make_intl_data.py. DO NOT EDIT.
+
+const sanctionedSimpleUnitIdentifiers = [
+ "acre",
+ "bit",
+ "byte",
+ "celsius",
+ "centimeter",
+ "day",
+ "degree",
+ "fahrenheit",
+ "fluid-ounce",
+ "foot",
+ "gallon",
+ "gigabit",
+ "gigabyte",
+ "gram",
+ "hectare",
+ "hour",
+ "inch",
+ "kilobit",
+ "kilobyte",
+ "kilogram",
+ "kilometer",
+ "liter",
+ "megabit",
+ "megabyte",
+ "meter",
+ "mile",
+ "mile-scandinavian",
+ "milliliter",
+ "millimeter",
+ "millisecond",
+ "minute",
+ "month",
+ "ounce",
+ "percent",
+ "petabyte",
+ "pound",
+ "second",
+ "stone",
+ "terabit",
+ "terabyte",
+ "week",
+ "yard",
+ "year"
+];
+
+const allUnits = [
+ "acceleration-g-force",
+ "acceleration-meter-per-square-second",
+ "angle-arc-minute",
+ "angle-arc-second",
+ "angle-degree",
+ "angle-radian",
+ "angle-revolution",
+ "area-acre",
+ "area-dunam",
+ "area-hectare",
+ "area-square-centimeter",
+ "area-square-foot",
+ "area-square-inch",
+ "area-square-kilometer",
+ "area-square-meter",
+ "area-square-mile",
+ "area-square-yard",
+ "concentr-karat",
+ "concentr-milligram-per-deciliter",
+ "concentr-millimole-per-liter",
+ "concentr-mole",
+ "concentr-percent",
+ "concentr-permille",
+ "concentr-permillion",
+ "concentr-permyriad",
+ "consumption-liter-per-100-kilometer",
+ "consumption-liter-per-kilometer",
+ "consumption-mile-per-gallon",
+ "consumption-mile-per-gallon-imperial",
+ "digital-bit",
+ "digital-byte",
+ "digital-gigabit",
+ "digital-gigabyte",
+ "digital-kilobit",
+ "digital-kilobyte",
+ "digital-megabit",
+ "digital-megabyte",
+ "digital-petabyte",
+ "digital-terabit",
+ "digital-terabyte",
+ "duration-century",
+ "duration-day",
+ "duration-day-person",
+ "duration-decade",
+ "duration-hour",
+ "duration-microsecond",
+ "duration-millisecond",
+ "duration-minute",
+ "duration-month",
+ "duration-month-person",
+ "duration-nanosecond",
+ "duration-second",
+ "duration-week",
+ "duration-week-person",
+ "duration-year",
+ "duration-year-person",
+ "electric-ampere",
+ "electric-milliampere",
+ "electric-ohm",
+ "electric-volt",
+ "energy-british-thermal-unit",
+ "energy-calorie",
+ "energy-electronvolt",
+ "energy-foodcalorie",
+ "energy-joule",
+ "energy-kilocalorie",
+ "energy-kilojoule",
+ "energy-kilowatt-hour",
+ "energy-therm-us",
+ "force-newton",
+ "force-pound-force",
+ "frequency-gigahertz",
+ "frequency-hertz",
+ "frequency-kilohertz",
+ "frequency-megahertz",
+ "graphics-dot-per-centimeter",
+ "graphics-dot-per-inch",
+ "graphics-em",
+ "graphics-megapixel",
+ "graphics-pixel",
+ "graphics-pixel-per-centimeter",
+ "graphics-pixel-per-inch",
+ "length-astronomical-unit",
+ "length-centimeter",
+ "length-decimeter",
+ "length-fathom",
+ "length-foot",
+ "length-furlong",
+ "length-inch",
+ "length-kilometer",
+ "length-light-year",
+ "length-meter",
+ "length-micrometer",
+ "length-mile",
+ "length-mile-scandinavian",
+ "length-millimeter",
+ "length-nanometer",
+ "length-nautical-mile",
+ "length-parsec",
+ "length-picometer",
+ "length-point",
+ "length-solar-radius",
+ "length-yard",
+ "light-lux",
+ "light-solar-luminosity",
+ "mass-carat",
+ "mass-dalton",
+ "mass-earth-mass",
+ "mass-gram",
+ "mass-kilogram",
+ "mass-metric-ton",
+ "mass-microgram",
+ "mass-milligram",
+ "mass-ounce",
+ "mass-ounce-troy",
+ "mass-pound",
+ "mass-solar-mass",
+ "mass-stone",
+ "mass-ton",
+ "power-gigawatt",
+ "power-horsepower",
+ "power-kilowatt",
+ "power-megawatt",
+ "power-milliwatt",
+ "power-watt",
+ "pressure-atmosphere",
+ "pressure-bar",
+ "pressure-hectopascal",
+ "pressure-inch-ofhg",
+ "pressure-kilopascal",
+ "pressure-megapascal",
+ "pressure-millibar",
+ "pressure-millimeter-ofhg",
+ "pressure-pascal",
+ "pressure-pound-force-per-square-inch",
+ "speed-kilometer-per-hour",
+ "speed-knot",
+ "speed-meter-per-second",
+ "speed-mile-per-hour",
+ "temperature-celsius",
+ "temperature-fahrenheit",
+ "temperature-generic",
+ "temperature-kelvin",
+ "torque-newton-meter",
+ "torque-pound-force-foot",
+ "volume-acre-foot",
+ "volume-barrel",
+ "volume-bushel",
+ "volume-centiliter",
+ "volume-cubic-centimeter",
+ "volume-cubic-foot",
+ "volume-cubic-inch",
+ "volume-cubic-kilometer",
+ "volume-cubic-meter",
+ "volume-cubic-mile",
+ "volume-cubic-yard",
+ "volume-cup",
+ "volume-cup-metric",
+ "volume-deciliter",
+ "volume-fluid-ounce",
+ "volume-fluid-ounce-imperial",
+ "volume-gallon",
+ "volume-gallon-imperial",
+ "volume-hectoliter",
+ "volume-liter",
+ "volume-megaliter",
+ "volume-milliliter",
+ "volume-pint",
+ "volume-pint-metric",
+ "volume-quart",
+ "volume-tablespoon",
+ "volume-teaspoon"
+];
+
+// Test only sanctioned unit identifiers are allowed.
+
+for (const typeAndUnit of allUnits) {
+ const [_, type, unit] = typeAndUnit.match(/(\w+)-(.+)/);
+
+ let allowed;
+ if (unit.includes("-per-")) {
+ const [numerator, denominator] = unit.split("-per-");
+ allowed = sanctionedSimpleUnitIdentifiers.includes(numerator) &&
+ sanctionedSimpleUnitIdentifiers.includes(denominator);
+ } else {
+ allowed = sanctionedSimpleUnitIdentifiers.includes(unit);
+ }
+
+ if (allowed) {
+ const nf = new Intl.NumberFormat("en", {style: "unit", unit});
+ assertEq(nf.format(1), nf.formatToParts(1).map(p => p.value).join(""));
+ } else {
+ assertThrowsInstanceOf(() => new Intl.NumberFormat("en", {style: "unit", unit}),
+ RangeError, `Missing error for "${typeAndUnit}"`);
+ }
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/unit.js b/js/src/tests/non262/Intl/NumberFormat/unit.js
new file mode 100644
index 0000000000..7abc5142e0
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/unit.js
@@ -0,0 +1,104 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const {
+ Nan, Inf, Integer, MinusSign, PlusSign, Literal,
+ Unit
+} = NumberFormatParts;
+
+const testcases = [
+ {
+ locale: "en",
+ options: {
+ style: "unit",
+ unit: "meter",
+ unitDisplay: "short",
+ },
+ values: [
+ {value: +0, string: "0 m", parts: [Integer("0"), Literal(" "), Unit("m")]},
+ {value: -0, string: "-0 m", parts: [MinusSign("-"), Integer("0"), Literal(" "), Unit("m")]},
+ {value: 0n, string: "0 m", parts: [Integer("0"), Literal(" "), Unit("m")]},
+
+ {value: 1, string: "1 m", parts: [Integer("1"), Literal(" "), Unit("m")]},
+ {value: -1, string: "-1 m", parts: [MinusSign("-"), Integer("1"), Literal(" "), Unit("m")]},
+ {value: 1n, string: "1 m", parts: [Integer("1"), Literal(" "), Unit("m")]},
+ {value: -1n, string: "-1 m", parts: [MinusSign("-"), Integer("1"), Literal(" "), Unit("m")]},
+
+ {value: Infinity, string: "∞ m", parts: [Inf("∞"), Literal(" "), Unit("m")]},
+ {value: -Infinity, string: "-∞ m", parts: [MinusSign("-"), Inf("∞"), Literal(" "), Unit("m")]},
+
+ {value: NaN, string: "NaN m", parts: [Nan("NaN"), Literal(" "), Unit("m")]},
+ {value: -NaN, string: "NaN m", parts: [Nan("NaN"), Literal(" "), Unit("m")]},
+ ],
+ },
+
+ {
+ locale: "en",
+ options: {
+ style: "unit",
+ unit: "meter",
+ unitDisplay: "narrow",
+ },
+ values: [
+ {value: +0, string: "0m", parts: [Integer("0"), Unit("m")]},
+ {value: -0, string: "-0m", parts: [MinusSign("-"), Integer("0"), Unit("m")]},
+ {value: 0n, string: "0m", parts: [Integer("0"), Unit("m")]},
+
+ {value: 1, string: "1m", parts: [Integer("1"), Unit("m")]},
+ {value: -1, string: "-1m", parts: [MinusSign("-"), Integer("1"), Unit("m")]},
+ {value: 1n, string: "1m", parts: [Integer("1"), Unit("m")]},
+ {value: -1n, string: "-1m", parts: [MinusSign("-"), Integer("1"), Unit("m")]},
+
+ {value: Infinity, string: "∞m", parts: [Inf("∞"), Unit("m")]},
+ {value: -Infinity, string: "-∞m", parts: [MinusSign("-"), Inf("∞"), Unit("m")]},
+
+ {value: NaN, string: "NaNm", parts: [Nan("NaN"), Unit("m")]},
+ {value: -NaN, string: "NaNm", parts: [Nan("NaN"), Unit("m")]},
+ ],
+ },
+
+ {
+ locale: "en",
+ options: {
+ style: "unit",
+ unit: "meter",
+ unitDisplay: "long",
+ },
+ values: [
+ {value: +0, string: "0 meters", parts: [Integer("0"), Literal(" "), Unit("meters")]},
+ {value: -0, string: "-0 meters", parts: [MinusSign("-"), Integer("0"), Literal(" "), Unit("meters")]},
+ {value: 0n, string: "0 meters", parts: [Integer("0"), Literal(" "), Unit("meters")]},
+
+ {value: 1, string: "1 meter", parts: [Integer("1"), Literal(" "), Unit("meter")]},
+ {value: -1, string: "-1 meter", parts: [MinusSign("-"), Integer("1"), Literal(" "), Unit("meter")]},
+ {value: 1n, string: "1 meter", parts: [Integer("1"), Literal(" "), Unit("meter")]},
+ {value: -1n, string: "-1 meter", parts: [MinusSign("-"), Integer("1"), Literal(" "), Unit("meter")]},
+
+ {value: Infinity, string: "∞ meters", parts: [Inf("∞"), Literal(" "), Unit("meters")]},
+ {value: -Infinity, string: "-∞ meters", parts: [MinusSign("-"), Inf("∞"), Literal(" "), Unit("meters")]},
+
+ {value: NaN, string: "NaN meters", parts: [Nan("NaN"), Literal(" "), Unit("meters")]},
+ {value: -NaN, string: "NaN meters", parts: [Nan("NaN"), Literal(" "), Unit("meters")]},
+ ],
+ },
+
+ // Ensure the correct compound unit is automatically selected by ICU. For
+ // example instead of "50 chilometri al orari", 50 km/h should return
+ // "50 chilometri orari" in Italian.
+
+ {
+ locale: "it",
+ options: {
+ style: "unit",
+ unit: "kilometer-per-hour",
+ unitDisplay: "long",
+ },
+ values: [
+ {value: 50, string: "50 chilometri orari", parts: [Integer("50"), Literal(" "), Unit("chilometri orari")]},
+ ],
+ },
+];
+
+runNumberFormattingTestcases(testcases);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Intl/NumberFormat/unwrapping.js b/js/src/tests/non262/Intl/NumberFormat/unwrapping.js
new file mode 100644
index 0000000000..7e66c43916
--- /dev/null
+++ b/js/src/tests/non262/Intl/NumberFormat/unwrapping.js
@@ -0,0 +1,247 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+// Test UnwrapNumberFormat operation.
+
+const numberFormatFunctions = [];
+numberFormatFunctions.push({
+ function: Intl.NumberFormat.prototype.resolvedOptions,
+ unwrap: true,
+});
+numberFormatFunctions.push({
+ function: Object.getOwnPropertyDescriptor(Intl.NumberFormat.prototype, "format").get,
+ unwrap: true,
+});
+numberFormatFunctions.push({
+ function: Intl.NumberFormat.prototype.formatToParts,
+ unwrap: false,
+});
+
+function IsIntlService(c) {
+ return typeof c === "function" &&
+ c.hasOwnProperty("prototype") &&
+ c.prototype.hasOwnProperty("resolvedOptions");
+}
+
+function IsObject(o) {
+ return Object(o) === o;
+}
+
+function IsPrimitive(o) {
+ return Object(o) !== o;
+}
+
+function intlObjects(ctor) {
+ let args = [];
+ if (ctor === Intl.DisplayNames) {
+ // Intl.DisplayNames can't be constructed without any arguments.
+ args = [undefined, {type: "language"}];
+ }
+
+ return [
+ // Instance of an Intl constructor.
+ new ctor(...args),
+
+ // Instance of a subclassed Intl constructor.
+ new class extends ctor {}(...args),
+
+ // Intl object not inheriting from its default prototype.
+ Object.setPrototypeOf(new ctor(...args), Object.prototype),
+ ];
+}
+
+function thisValues(C) {
+ const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService);
+
+ return [
+ // Primitive values.
+ ...[undefined, null, true, "abc", Symbol(), 123],
+
+ // Object values.
+ ...[{}, [], /(?:)/, function(){}, new Proxy({}, {})],
+
+ // Intl objects.
+ ...[].concat(...intlConstructors.filter(ctor => ctor !== C).map(intlObjects)),
+
+ // Object inheriting from an Intl constructor prototype.
+ ...intlConstructors.map(ctor => Object.create(ctor.prototype)),
+ ];
+}
+
+const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.NumberFormat.call(Object.create(Intl.NumberFormat.prototype)))[0];
+
+// Test Intl.NumberFormat.prototype methods.
+for (let {function: numberFormatFunction, unwrap} of numberFormatFunctions) {
+ // Test a TypeError is thrown when the this-value isn't an initialized
+ // Intl.NumberFormat instance.
+ for (let thisValue of thisValues(Intl.NumberFormat)) {
+ assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError);
+ }
+
+ // And test no error is thrown for initialized Intl.NumberFormat instances.
+ for (let thisValue of intlObjects(Intl.NumberFormat)) {
+ numberFormatFunction.call(thisValue);
+ }
+
+ // Manually add [[FallbackSymbol]] to objects and then repeat the tests from above.
+ for (let thisValue of thisValues(Intl.NumberFormat)) {
+ assertThrowsInstanceOf(() => numberFormatFunction.call({
+ __proto__: Intl.NumberFormat.prototype,
+ [intlFallbackSymbol]: thisValue,
+ }), TypeError);
+ }
+
+ for (let thisValue of intlObjects(Intl.NumberFormat)) {
+ let obj = {
+ __proto__: Intl.NumberFormat.prototype,
+ [intlFallbackSymbol]: thisValue,
+ };
+ if (unwrap) {
+ numberFormatFunction.call(obj);
+ } else {
+ assertThrowsInstanceOf(() => numberFormatFunction.call(obj), TypeError);
+ }
+ }
+
+ // Ensure [[FallbackSymbol]] isn't retrieved for Intl.NumberFormat instances.
+ for (let thisValue of intlObjects(Intl.NumberFormat)) {
+ Object.defineProperty(thisValue, intlFallbackSymbol, {
+ get() { assertEq(false, true); }
+ });
+ numberFormatFunction.call(thisValue);
+ }
+
+ // Ensure [[FallbackSymbol]] is only retrieved for objects inheriting from Intl.NumberFormat.prototype.
+ for (let thisValue of thisValues(Intl.NumberFormat).filter(IsObject)) {
+ if (Intl.NumberFormat.prototype.isPrototypeOf(thisValue))
+ continue;
+ Object.defineProperty(thisValue, intlFallbackSymbol, {
+ get() { assertEq(false, true); }
+ });
+ assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError);
+ }
+
+ // Repeat the test from above, but also change Intl.NumberFormat[@@hasInstance]
+ // so it always returns |null|.
+ for (let thisValue of thisValues(Intl.NumberFormat).filter(IsObject)) {
+ let hasInstanceCalled = false, symbolGetterCalled = false;
+ Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, {
+ value() {
+ assertEq(hasInstanceCalled, false);
+ hasInstanceCalled = true;
+ return true;
+ }, configurable: true
+ });
+ Object.defineProperty(thisValue, intlFallbackSymbol, {
+ get() {
+ assertEq(symbolGetterCalled, false);
+ symbolGetterCalled = true;
+ return null;
+ }, configurable: true
+ });
+
+ assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError);
+
+ delete Intl.NumberFormat[Symbol.hasInstance];
+
+ assertEq(hasInstanceCalled, unwrap);
+ assertEq(symbolGetterCalled, unwrap);
+ }
+
+ // Test with primitive values.
+ for (let thisValue of thisValues(Intl.NumberFormat).filter(IsPrimitive)) {
+ // Ensure @@hasInstance is not called.
+ Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, {
+ value() { assertEq(true, false); }, configurable: true
+ });
+ let isUndefinedOrNull = thisValue === undefined || thisValue === null;
+ let symbolHolder;
+ if (!isUndefinedOrNull) {
+ // Ensure the fallback symbol isn't retrieved from the primitive wrapper prototype.
+ symbolHolder = Object.getPrototypeOf(thisValue);
+ Object.defineProperty(symbolHolder, intlFallbackSymbol, {
+ get() { assertEq(true, false); }, configurable: true
+ });
+ }
+
+ assertThrowsInstanceOf(() => numberFormatFunction.call(thisValue), TypeError);
+
+ delete Intl.NumberFormat[Symbol.hasInstance];
+ if (!isUndefinedOrNull)
+ delete symbolHolder[intlFallbackSymbol];
+ }
+}
+
+// Test format() returns the correct result for objects initialized as Intl.NumberFormat instances.
+{
+ // An actual Intl.NumberFormat instance.
+ let numberFormat = new Intl.NumberFormat();
+
+ // An object initialized as a NumberFormat instance.
+ let thisValue = Object.create(Intl.NumberFormat.prototype);
+ Intl.NumberFormat.call(thisValue);
+
+ // Object with [[FallbackSymbol]] set to NumberFormat instance.
+ let fakeObj = {
+ __proto__: Intl.NumberFormat.prototype,
+ [intlFallbackSymbol]: numberFormat,
+ };
+
+ for (let number of [0, 1, 1.5, Infinity, NaN]) {
+ let expected = numberFormat.format(number);
+ assertEq(thisValue.format(number), expected);
+ assertEq(thisValue[intlFallbackSymbol].format(number), expected);
+ assertEq(fakeObj.format(number), expected);
+ }
+}
+
+// Ensure formatToParts() doesn't use the fallback semantics.
+{
+ let formatToParts = Intl.NumberFormat.prototype.formatToParts;
+
+ // An object initialized as a NumberFormat instance.
+ let thisValue = Object.create(Intl.NumberFormat.prototype);
+ Intl.NumberFormat.call(thisValue);
+ assertThrowsInstanceOf(() => formatToParts.call(thisValue), TypeError);
+
+ // Object with [[FallbackSymbol]] set to NumberFormat instance.
+ let fakeObj = {
+ __proto__: Intl.NumberFormat.prototype,
+ [intlFallbackSymbol]: new Intl.NumberFormat(),
+ };
+ assertThrowsInstanceOf(() => formatToParts.call(fakeObj), TypeError);
+}
+
+// Test resolvedOptions() returns the same results.
+{
+ // An actual Intl.NumberFormat instance.
+ let numberFormat = new Intl.NumberFormat();
+
+ // An object initialized as a NumberFormat instance.
+ let thisValue = Object.create(Intl.NumberFormat.prototype);
+ Intl.NumberFormat.call(thisValue);
+
+ // Object with [[FallbackSymbol]] set to NumberFormat instance.
+ let fakeObj = {
+ __proto__: Intl.NumberFormat.prototype,
+ [intlFallbackSymbol]: numberFormat,
+ };
+
+ function assertEqOptions(actual, expected) {
+ actual = Object.entries(actual);
+ expected = Object.entries(expected);
+
+ assertEq(actual.length, expected.length, "options count mismatch");
+ for (var i = 0; i < expected.length; i++) {
+ assertEq(actual[i][0], expected[i][0], "key mismatch at " + i);
+ assertEq(actual[i][1], expected[i][1], "value mismatch at " + i);
+ }
+ }
+
+ let expected = numberFormat.resolvedOptions();
+ assertEqOptions(thisValue.resolvedOptions(), expected);
+ assertEqOptions(thisValue[intlFallbackSymbol].resolvedOptions(), expected);
+ assertEqOptions(fakeObj.resolvedOptions(), expected);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);