Added new tests for chapters 10 to 13 of the ECMAScript Internationalization API Specification.

This commit is contained in:
Norbert Lindenberg 2012-08-26 20:50:24 -07:00
parent 1af2425075
commit d71ffa59d5
79 changed files with 3885 additions and 2 deletions

View File

@ -656,3 +656,474 @@ function isCanonicalizedStructurallyValidLanguageTag(locale) {
canonicalizeLanguageTag(locale) === locale;
}
/**
* Tests whether the named options property is correctly handled by the given constructor.
* @param {object} Constructor the constructor to test.
* @param {string} property the name of the options property to test.
* @param {string} type the type that values of the property are expected to have
* @param {Array} [values] an array of allowed values for the property. Not needed for boolean.
* @param {any} fallback the fallback value that the property assumes if not provided.
* @param {object} testOptions additional options:
* @param {boolean} isOptional whether support for this property is optional for implementations.
* @param {boolean} noReturn whether the resulting value of the property is not returned.
* @param {boolean} isILD whether the resulting value of the property is implementation and locale dependent.
* @param {object} extra additional option to pass along, properties are value -> {option: value}.
* @return {boolean} whether the test succeeded.
*/
function testOption(Constructor, property, type, values, fallback, testOptions) {
var isOptional = testOptions !== undefined && testOptions.isOptional === true;
var noReturn = testOptions !== undefined && testOptions.noReturn === true;
var isILD = testOptions !== undefined && testOptions.isILD === true;
function addExtraOptions(options, value, testOptions) {
if (testOptions !== undefined && testOptions.extra !== undefined) {
var extra;
if (value !== undefined && testOptions.extra[value] !== undefined) {
extra = testOptions.extra[value];
} else if (testOptions.extra.any !== undefined) {
extra = testOptions.extra.any;
}
if (extra !== undefined) {
Object.getOwnPropertyNames(extra).forEach(function (prop) {
options[prop] = extra[prop];
});
}
}
}
var testValues, options, obj, expected, actual, error;
// test that the specified values are accepted. Also add values that convert to specified values.
if (type === "boolean") {
if (values === undefined) {
values = [true, false];
}
testValues = values.slice(0);
testValues.push(888);
testValues.push(0);
} else if (type === "string") {
testValues = values.slice(0);
testValues.push({toString: function () { return values[0]; }});
}
testValues.forEach(function (value) {
options = {};
options[property] = value;
addExtraOptions(options, value, testOptions);
obj = new Constructor(undefined, options);
if (noReturn) {
if (obj.resolvedOptions().hasOwnProperty(property)) {
$ERROR("Option property " + property + " is returned, but shouldn't be.");
}
} else {
actual = obj.resolvedOptions()[property];
if (isILD) {
if (actual !== undefined && values.indexOf(actual) === -1) {
$ERROR("Invalid value " + actual + " returned for property " + property + ".");
}
} else {
if (type === "boolean") {
expected = Boolean(value);
} else if (type === "string") {
expected = String(value);
}
if (actual !== expected && !(isOptional && actual === undefined)) {
$ERROR("Option value " + value + " for property " + property +
" was not accepted; got " + actual + " instead.");
}
}
}
});
// test that invalid values are rejected
if (type === "string") {
var invalidValues = ["invalidValue", -1, null];
// assume that we won't have values in caseless scripts
if (values[0].toUpperCase() !== values[0]) {
invalidValues.push(values[0].toUpperCase());
} else {
invalidValues.push(values[0].toLowerCase());
}
invalidValues.forEach(function (value) {
options = {};
options[property] = value;
addExtraOptions(options, value, testOptions);
error = undefined;
try {
obj = new Constructor(undefined, options);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Invalid option value " + value + " for property " + property + " was not rejected.");
} else if (error.name !== "RangeError") {
$ERROR("Invalid option value " + value + " for property " + property + " was rejected with wrong error " + error.name + ".");
}
});
}
// test that fallback value or another valid value is used if no options value is provided
if (!noReturn) {
options = {};
addExtraOptions(options, undefined, testOptions);
obj = new Constructor(undefined, options);
actual = obj.resolvedOptions()[property];
if (!(isOptional && actual === undefined)) {
if (fallback !== undefined) {
if (actual !== fallback) {
$ERROR("Option fallback value " + fallback + " for property " + property +
" was not used; got " + actual + " instead.");
}
} else {
if (values.indexOf(actual) === -1 && !(isILD && actual === undefined)) {
$ERROR("Invalid value " + actual + " returned for property " + property + ".");
}
}
}
}
return true;
}
/**
* Tests whether the named property of the given object has a valid value
* and the default attributes of the properties of an object literal.
* @param {Object} obj the object to be tested.
* @param {string} property the name of the property
* @param {Function|Array} valid either a function that tests value for validity and returns a boolean,
* an array of valid values.
* @exception if the property has an invalid value.
*/
function testProperty(obj, property, valid) {
var desc = Object.getOwnPropertyDescriptor(obj, property);
if (!desc.writable) {
$ERROR("Property " + property + " must be writable.");
}
if (!desc.enumerable) {
$ERROR("Property " + property + " must be enumerable.");
}
if (!desc.configurable) {
$ERROR("Property " + property + " must be configurable.");
}
var value = desc.value;
var isValid = (typeof valid === "function") ? valid(value) : (valid.indexOf(value) !== -1);
if (!isValid) {
$ERROR("Property value " + value + " is not allowed for property " + property + ".");
}
}
/**
* Tests whether the named property of the given object, if present at all, has a valid value
* and the default attributes of the properties of an object literal.
* @param {Object} obj the object to be tested.
* @param {string} property the name of the property
* @param {Function|Array} valid either a function that tests value for validity and returns a boolean,
* an array of valid values.
* @exception if the property is present and has an invalid value.
*/
function mayHaveProperty(obj, property, valid) {
if (obj.hasOwnProperty(property)) {
testProperty(obj, property, valid);
}
}
/**
* Tests whether the given object has the named property with a valid value
* and the default attributes of the properties of an object literal.
* @param {Object} obj the object to be tested.
* @param {string} property the name of the property
* @param {Function|Array} valid either a function that tests value for validity and returns a boolean,
* an array of valid values.
* @exception if the property is missing or has an invalid value.
*/
function mustHaveProperty(obj, property, valid) {
if (!obj.hasOwnProperty(property)) {
$ERROR("Object is missing property " + property + ".");
}
testProperty(obj, property, valid);
}
/**
* Tests whether the given object does not have the named property.
* @param {Object} obj the object to be tested.
* @param {string} property the name of the property
* @exception if the property is present.
*/
function mustNotHaveProperty(obj, property) {
if (obj.hasOwnProperty(property)) {
$ERROR("Object has property it mustn't have: " + property + ".");
}
}
/**
* Tests whether name is a valid BCP 47 numbering system name
* and not excluded from use in the ECMAScript Internationalization API.
* @param {string} name the name to be tested.
* @return {boolean} whether name is a valid BCP 47 numbering system name and
* allowed for use in the ECMAScript Internationalization API.
*/
function isValidNumberingSystem(name) {
// source: CLDR file common/bcp47/number.xml; version CLDR 21.
var numberingSystems = [
"arab",
"arabext",
"armn",
"armnlow",
"bali",
"beng",
"brah",
"cakm",
"cham",
"deva",
"ethi",
"finance",
"fullwide",
"geor",
"grek",
"greklow",
"gujr",
"guru",
"hanidec",
"hans",
"hansfin",
"hant",
"hantfin",
"hebr",
"java",
"jpan",
"jpanfin",
"kali",
"khmr",
"knda",
"osma",
"lana",
"lanatham",
"laoo",
"latn",
"lepc",
"limb",
"mlym",
"mong",
"mtei",
"mymr",
"mymrshan",
"native",
"nkoo",
"olck",
"orya",
"roman",
"romanlow",
"saur",
"shrd",
"sora",
"sund",
"talu",
"takr",
"taml",
"tamldec",
"telu",
"thai",
"tibt",
"traditio",
"vaii"
];
var excluded = [
"finance",
"native",
"traditio"
];
return numberingSystems.indexOf(name) !== -1 && excluded.indexOf(name) === -1;
}
/**
* Provides the digits of numbering systems with simple digit mappings,
* as specified in 11.3.2.
*/
var numberingSystemDigits = {
arab: "٠١٢٣٤٥٦٧٨٩",
arabext: "۰۱۲۳۴۵۶۷۸۹",
beng: "০১২৩৪৫৬৭৮৯",
deva: "०१२३४५६७८९",
fullwide: "",
gujr: "૦૧૨૩૪૫૬૭૮૯",
guru: "੦੧੨੩੪੫੬੭੮੯",
hanidec: "〇一二三四五六七八九",
khmr: "០១២៣៤៥៦៧៨៩",
knda: "೦೧೨೩೪೫೬೭೮೯",
laoo: "໐໑໒໓໔໕໖໗໘໙",
latn: "0123456789",
mlym: "൦൧൨൩൪൫൬൭൮൯",
mong: "᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙",
mymr: "၀၁၂၃၄၅၆၇၈၉",
orya: "୦୧୨୩୪୫୬୭୮୯",
tamldec: "௦௧௨௩௪௫௬௭௮௯",
telu: "౦౧౨౩౪౫౬౭౮౯",
thai: "๐๑๒๓๔๕๖๗๘๙",
tibt: "༠༡༢༣༤༥༦༧༨༩"
};
/**
* Tests that number formatting is handled correctly. The function checks that the
* digit sequences in formatted output are as specified, converted to the
* selected numbering system, and embedded in consistent localized patterns.
* @param {Array} locales the locales to be tested.
* @param {Array} numberingSystems the numbering systems to be tested.
* @param {Object} options the options to pass to Intl.NumberFormat. Options
* must include {useGrouping: false}, and must cause 1.1 to be formatted
* pre- and post-decimal digits.
* @param {Object} testData maps input data (in ES5 9.3.1 format) to expected output strings
* in unlocalized format with Western digits.
*/
function testNumberFormat(locales, numberingSystems, options, testData) {
locales.forEach(function (locale) {
numberingSystems.forEach(function (numbering) {
var digits = numberingSystemDigits[numbering];
var format = new Intl.NumberFormat([locale + "-u-nu-" + numbering], options);
function getPatternParts(positive) {
var n = positive ? 1.1 : -1.1;
var formatted = format.format(n);
var oneoneRE = "([^" + digits + "]*)[" + digits + "]+([^" + digits + "]+)[" + digits + "]+([^" + digits + "]*)";
var match = formatted.match(new RegExp(oneoneRE));
if (match === null) {
$ERROR("Unexpected formatted " + n + " for " +
format.resolvedOptions().locale + " and options " +
JSON.stringify(options) + ": " + formatted);
}
return match;
}
function toNumbering(raw) {
return raw.replace(/[0-9]/g, function (digit) {
return digits[digit.charCodeAt(0) - "0".charCodeAt(0)];
});
}
function buildExpected(raw, patternParts) {
var period = raw.indexOf(".");
if (period === -1) {
return patternParts[1] + toNumbering(raw) + patternParts[3];
} else {
return patternParts[1] +
toNumbering(raw.substring(0, period)) +
patternParts[2] +
toNumbering(raw.substring(period + 1)) +
patternParts[3];
}
}
if (format.resolvedOptions().numberingSystem === numbering) {
// figure out prefixes, infixes, suffixes for positive and negative values
var posPatternParts = getPatternParts(true);
var negPatternParts = getPatternParts(false);
Object.getOwnPropertyNames(testData).forEach(function (input) {
var rawExpected = testData[input];
var patternParts;
if (rawExpected[0] === "-") {
patternParts = negPatternParts;
rawExpected = rawExpected.substring(1);
} else {
patternParts = posPatternParts;
}
var expected = buildExpected(rawExpected, patternParts);
var actual = format.format(input);
if (actual !== expected) {
$ERROR("Formatted value for " + input + ", " +
format.resolvedOptions().locale + " and options " +
JSON.stringify(options) + " is " + actual + "; expected " + expected + ".");
}
});
}
});
});
}
/**
* Return the components of date-time formats.
* @return {Array} an array with all date-time components.
*/
function getDateTimeComponents() {
return ["weekday", "era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"];
}
/**
* Return the valid values for the given date-time component, as specified
* by the table in section 12.1.1.
* @param {string} component a date-time component.
* @return {Array} an array with the valid values for the component.
*/
function getDateTimeComponentValues(component) {
var components = {
weekday: ["narrow", "short", "long"],
era: ["narrow", "short", "long"],
year: ["2-digit", "numeric"],
month: ["2-digit", "numeric", "narrow", "short", "long"],
day: ["2-digit", "numeric"],
hour: ["2-digit", "numeric"],
minute: ["2-digit", "numeric"],
second: ["2-digit", "numeric"],
timeZoneName: ["short", "long"]
};
var result = components[component];
if (result === undefined) {
$ERROR("Internal error: No values defined for date-time component " + component + ".");
}
return result;
}
/**
* Tests that the given value is valid for the given date-time component.
* @param {string} component a date-time component.
* @param {string} value the value to be tested.
* @return {boolean} true if the test succeeds.
* @exception if the test fails.
*/
function testValidDateTimeComponentValue(component, value) {
if (getDateTimeComponentValues(component).indexOf(value) === -1) {
$ERROR("Invalid value " + value + " for date-time component " + component + ".");
}
return true;
}
/**
* Verifies that the actual array matches the expected one in length, elements,
* and element order.
* @param {Array} expected the expected array.
* @param {Array} actual the actual array.
* @return {boolean} true if the test succeeds.
* @exception if the test fails.
*/
function testArraysAreSame(expected, actual) {
for (i = 0; i < Math.max(actual.length, expected.length); i++) {
if (actual[i] !== expected[i]) {
$ERROR("Result array element at index " + i + " should be \"" +
expected[i] + "\" but is \"" + actual[i] + "\".");
}
}
return true;
}

View File

@ -656,3 +656,474 @@ function isCanonicalizedStructurallyValidLanguageTag(locale) {
canonicalizeLanguageTag(locale) === locale;
}
/**
* Tests whether the named options property is correctly handled by the given constructor.
* @param {object} Constructor the constructor to test.
* @param {string} property the name of the options property to test.
* @param {string} type the type that values of the property are expected to have
* @param {Array} [values] an array of allowed values for the property. Not needed for boolean.
* @param {any} fallback the fallback value that the property assumes if not provided.
* @param {object} testOptions additional options:
* @param {boolean} isOptional whether support for this property is optional for implementations.
* @param {boolean} noReturn whether the resulting value of the property is not returned.
* @param {boolean} isILD whether the resulting value of the property is implementation and locale dependent.
* @param {object} extra additional option to pass along, properties are value -> {option: value}.
* @return {boolean} whether the test succeeded.
*/
function testOption(Constructor, property, type, values, fallback, testOptions) {
var isOptional = testOptions !== undefined && testOptions.isOptional === true;
var noReturn = testOptions !== undefined && testOptions.noReturn === true;
var isILD = testOptions !== undefined && testOptions.isILD === true;
function addExtraOptions(options, value, testOptions) {
if (testOptions !== undefined && testOptions.extra !== undefined) {
var extra;
if (value !== undefined && testOptions.extra[value] !== undefined) {
extra = testOptions.extra[value];
} else if (testOptions.extra.any !== undefined) {
extra = testOptions.extra.any;
}
if (extra !== undefined) {
Object.getOwnPropertyNames(extra).forEach(function (prop) {
options[prop] = extra[prop];
});
}
}
}
var testValues, options, obj, expected, actual, error;
// test that the specified values are accepted. Also add values that convert to specified values.
if (type === "boolean") {
if (values === undefined) {
values = [true, false];
}
testValues = values.slice(0);
testValues.push(888);
testValues.push(0);
} else if (type === "string") {
testValues = values.slice(0);
testValues.push({toString: function () { return values[0]; }});
}
testValues.forEach(function (value) {
options = {};
options[property] = value;
addExtraOptions(options, value, testOptions);
obj = new Constructor(undefined, options);
if (noReturn) {
if (obj.resolvedOptions().hasOwnProperty(property)) {
$ERROR("Option property " + property + " is returned, but shouldn't be.");
}
} else {
actual = obj.resolvedOptions()[property];
if (isILD) {
if (actual !== undefined && values.indexOf(actual) === -1) {
$ERROR("Invalid value " + actual + " returned for property " + property + ".");
}
} else {
if (type === "boolean") {
expected = Boolean(value);
} else if (type === "string") {
expected = String(value);
}
if (actual !== expected && !(isOptional && actual === undefined)) {
$ERROR("Option value " + value + " for property " + property +
" was not accepted; got " + actual + " instead.");
}
}
}
});
// test that invalid values are rejected
if (type === "string") {
var invalidValues = ["invalidValue", -1, null];
// assume that we won't have values in caseless scripts
if (values[0].toUpperCase() !== values[0]) {
invalidValues.push(values[0].toUpperCase());
} else {
invalidValues.push(values[0].toLowerCase());
}
invalidValues.forEach(function (value) {
options = {};
options[property] = value;
addExtraOptions(options, value, testOptions);
error = undefined;
try {
obj = new Constructor(undefined, options);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Invalid option value " + value + " for property " + property + " was not rejected.");
} else if (error.name !== "RangeError") {
$ERROR("Invalid option value " + value + " for property " + property + " was rejected with wrong error " + error.name + ".");
}
});
}
// test that fallback value or another valid value is used if no options value is provided
if (!noReturn) {
options = {};
addExtraOptions(options, undefined, testOptions);
obj = new Constructor(undefined, options);
actual = obj.resolvedOptions()[property];
if (!(isOptional && actual === undefined)) {
if (fallback !== undefined) {
if (actual !== fallback) {
$ERROR("Option fallback value " + fallback + " for property " + property +
" was not used; got " + actual + " instead.");
}
} else {
if (values.indexOf(actual) === -1 && !(isILD && actual === undefined)) {
$ERROR("Invalid value " + actual + " returned for property " + property + ".");
}
}
}
}
return true;
}
/**
* Tests whether the named property of the given object has a valid value
* and the default attributes of the properties of an object literal.
* @param {Object} obj the object to be tested.
* @param {string} property the name of the property
* @param {Function|Array} valid either a function that tests value for validity and returns a boolean,
* an array of valid values.
* @exception if the property has an invalid value.
*/
function testProperty(obj, property, valid) {
var desc = Object.getOwnPropertyDescriptor(obj, property);
if (!desc.writable) {
$ERROR("Property " + property + " must be writable.");
}
if (!desc.enumerable) {
$ERROR("Property " + property + " must be enumerable.");
}
if (!desc.configurable) {
$ERROR("Property " + property + " must be configurable.");
}
var value = desc.value;
var isValid = (typeof valid === "function") ? valid(value) : (valid.indexOf(value) !== -1);
if (!isValid) {
$ERROR("Property value " + value + " is not allowed for property " + property + ".");
}
}
/**
* Tests whether the named property of the given object, if present at all, has a valid value
* and the default attributes of the properties of an object literal.
* @param {Object} obj the object to be tested.
* @param {string} property the name of the property
* @param {Function|Array} valid either a function that tests value for validity and returns a boolean,
* an array of valid values.
* @exception if the property is present and has an invalid value.
*/
function mayHaveProperty(obj, property, valid) {
if (obj.hasOwnProperty(property)) {
testProperty(obj, property, valid);
}
}
/**
* Tests whether the given object has the named property with a valid value
* and the default attributes of the properties of an object literal.
* @param {Object} obj the object to be tested.
* @param {string} property the name of the property
* @param {Function|Array} valid either a function that tests value for validity and returns a boolean,
* an array of valid values.
* @exception if the property is missing or has an invalid value.
*/
function mustHaveProperty(obj, property, valid) {
if (!obj.hasOwnProperty(property)) {
$ERROR("Object is missing property " + property + ".");
}
testProperty(obj, property, valid);
}
/**
* Tests whether the given object does not have the named property.
* @param {Object} obj the object to be tested.
* @param {string} property the name of the property
* @exception if the property is present.
*/
function mustNotHaveProperty(obj, property) {
if (obj.hasOwnProperty(property)) {
$ERROR("Object has property it mustn't have: " + property + ".");
}
}
/**
* Tests whether name is a valid BCP 47 numbering system name
* and not excluded from use in the ECMAScript Internationalization API.
* @param {string} name the name to be tested.
* @return {boolean} whether name is a valid BCP 47 numbering system name and
* allowed for use in the ECMAScript Internationalization API.
*/
function isValidNumberingSystem(name) {
// source: CLDR file common/bcp47/number.xml; version CLDR 21.
var numberingSystems = [
"arab",
"arabext",
"armn",
"armnlow",
"bali",
"beng",
"brah",
"cakm",
"cham",
"deva",
"ethi",
"finance",
"fullwide",
"geor",
"grek",
"greklow",
"gujr",
"guru",
"hanidec",
"hans",
"hansfin",
"hant",
"hantfin",
"hebr",
"java",
"jpan",
"jpanfin",
"kali",
"khmr",
"knda",
"osma",
"lana",
"lanatham",
"laoo",
"latn",
"lepc",
"limb",
"mlym",
"mong",
"mtei",
"mymr",
"mymrshan",
"native",
"nkoo",
"olck",
"orya",
"roman",
"romanlow",
"saur",
"shrd",
"sora",
"sund",
"talu",
"takr",
"taml",
"tamldec",
"telu",
"thai",
"tibt",
"traditio",
"vaii"
];
var excluded = [
"finance",
"native",
"traditio"
];
return numberingSystems.indexOf(name) !== -1 && excluded.indexOf(name) === -1;
}
/**
* Provides the digits of numbering systems with simple digit mappings,
* as specified in 11.3.2.
*/
var numberingSystemDigits = {
arab: "٠١٢٣٤٥٦٧٨٩",
arabext: "۰۱۲۳۴۵۶۷۸۹",
beng: "০১২৩৪৫৬৭৮৯",
deva: "०१२३४५६७८९",
fullwide: "",
gujr: "૦૧૨૩૪૫૬૭૮૯",
guru: "੦੧੨੩੪੫੬੭੮੯",
hanidec: "〇一二三四五六七八九",
khmr: "០១២៣៤៥៦៧៨៩",
knda: "೦೧೨೩೪೫೬೭೮೯",
laoo: "໐໑໒໓໔໕໖໗໘໙",
latn: "0123456789",
mlym: "൦൧൨൩൪൫൬൭൮൯",
mong: "᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙",
mymr: "၀၁၂၃၄၅၆၇၈၉",
orya: "୦୧୨୩୪୫୬୭୮୯",
tamldec: "௦௧௨௩௪௫௬௭௮௯",
telu: "౦౧౨౩౪౫౬౭౮౯",
thai: "๐๑๒๓๔๕๖๗๘๙",
tibt: "༠༡༢༣༤༥༦༧༨༩"
};
/**
* Tests that number formatting is handled correctly. The function checks that the
* digit sequences in formatted output are as specified, converted to the
* selected numbering system, and embedded in consistent localized patterns.
* @param {Array} locales the locales to be tested.
* @param {Array} numberingSystems the numbering systems to be tested.
* @param {Object} options the options to pass to Intl.NumberFormat. Options
* must include {useGrouping: false}, and must cause 1.1 to be formatted
* pre- and post-decimal digits.
* @param {Object} testData maps input data (in ES5 9.3.1 format) to expected output strings
* in unlocalized format with Western digits.
*/
function testNumberFormat(locales, numberingSystems, options, testData) {
locales.forEach(function (locale) {
numberingSystems.forEach(function (numbering) {
var digits = numberingSystemDigits[numbering];
var format = new Intl.NumberFormat([locale + "-u-nu-" + numbering], options);
function getPatternParts(positive) {
var n = positive ? 1.1 : -1.1;
var formatted = format.format(n);
var oneoneRE = "([^" + digits + "]*)[" + digits + "]+([^" + digits + "]+)[" + digits + "]+([^" + digits + "]*)";
var match = formatted.match(new RegExp(oneoneRE));
if (match === null) {
$ERROR("Unexpected formatted " + n + " for " +
format.resolvedOptions().locale + " and options " +
JSON.stringify(options) + ": " + formatted);
}
return match;
}
function toNumbering(raw) {
return raw.replace(/[0-9]/g, function (digit) {
return digits[digit.charCodeAt(0) - "0".charCodeAt(0)];
});
}
function buildExpected(raw, patternParts) {
var period = raw.indexOf(".");
if (period === -1) {
return patternParts[1] + toNumbering(raw) + patternParts[3];
} else {
return patternParts[1] +
toNumbering(raw.substring(0, period)) +
patternParts[2] +
toNumbering(raw.substring(period + 1)) +
patternParts[3];
}
}
if (format.resolvedOptions().numberingSystem === numbering) {
// figure out prefixes, infixes, suffixes for positive and negative values
var posPatternParts = getPatternParts(true);
var negPatternParts = getPatternParts(false);
Object.getOwnPropertyNames(testData).forEach(function (input) {
var rawExpected = testData[input];
var patternParts;
if (rawExpected[0] === "-") {
patternParts = negPatternParts;
rawExpected = rawExpected.substring(1);
} else {
patternParts = posPatternParts;
}
var expected = buildExpected(rawExpected, patternParts);
var actual = format.format(input);
if (actual !== expected) {
$ERROR("Formatted value for " + input + ", " +
format.resolvedOptions().locale + " and options " +
JSON.stringify(options) + " is " + actual + "; expected " + expected + ".");
}
});
}
});
});
}
/**
* Return the components of date-time formats.
* @return {Array} an array with all date-time components.
*/
function getDateTimeComponents() {
return ["weekday", "era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"];
}
/**
* Return the valid values for the given date-time component, as specified
* by the table in section 12.1.1.
* @param {string} component a date-time component.
* @return {Array} an array with the valid values for the component.
*/
function getDateTimeComponentValues(component) {
var components = {
weekday: ["narrow", "short", "long"],
era: ["narrow", "short", "long"],
year: ["2-digit", "numeric"],
month: ["2-digit", "numeric", "narrow", "short", "long"],
day: ["2-digit", "numeric"],
hour: ["2-digit", "numeric"],
minute: ["2-digit", "numeric"],
second: ["2-digit", "numeric"],
timeZoneName: ["short", "long"]
};
var result = components[component];
if (result === undefined) {
$ERROR("Internal error: No values defined for date-time component " + component + ".");
}
return result;
}
/**
* Tests that the given value is valid for the given date-time component.
* @param {string} component a date-time component.
* @param {string} value the value to be tested.
* @return {boolean} true if the test succeeds.
* @exception if the test fails.
*/
function testValidDateTimeComponentValue(component, value) {
if (getDateTimeComponentValues(component).indexOf(value) === -1) {
$ERROR("Invalid value " + value + " for date-time component " + component + ".");
}
return true;
}
/**
* Verifies that the actual array matches the expected one in length, elements,
* and element order.
* @param {Array} expected the expected array.
* @param {Array} actual the actual array.
* @return {boolean} true if the test succeeds.
* @exception if the test fails.
*/
function testArraysAreSame(expected, actual) {
for (i = 0; i < Math.max(actual.length, expected.length); i++) {
if (actual[i] !== expected[i]) {
$ERROR("Result array element at index " + i + " should be \"" +
expected[i] + "\" but is \"" + actual[i] + "\".");
}
}
return true;
}

View File

@ -0,0 +1,43 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that an object can't be re-initialized as a Collator.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testWithIntlConstructors(function (Constructor) {
var obj, error;
// variant 1: use constructor in a "new" expression
obj = new Constructor();
try {
Intl.Collator.call(obj);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Re-initializing object created with \"new\" as Collator was not rejected.");
} else if (error.name !== "TypeError") {
$ERROR("Re-initializing object created with \"new\" as Collator was rejected with wrong error " + error.name + ".");
}
// variant 2: use constructor as a function
obj = Constructor.call({});
error = undefined;
try {
Intl.Collator.call(obj);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Re-initializing object created with constructor as function as Collator was not rejected.");
} else if (error.name !== "TypeError") {
$ERROR("Re-initializing object created with constructor as function as Collator was rejected with wrong error " + error.name + ".");
}
return true;
});

View File

@ -0,0 +1,18 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the behavior of a Record is not affected by adversarial
* changes to Object.prototype.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
taintProperties(["localeMatcher", "kn", "kk", "kf"]);
var locale = new Intl.Collator(undefined, {localeMatcher: "lookup"}).resolvedOptions().locale;
if (!isCanonicalizedStructurallyValidLanguageTag(locale)) {
$ERROR("Collator returns invalid locale " + locale + ".");
}

View File

@ -0,0 +1,12 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the option localeMatcher is processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testOption(Intl.Collator, "localeMatcher", "string", ["lookup", "best fit"], "best fit", {noReturn: true});

View File

@ -0,0 +1,14 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the options numeric, normalization, and caseFirst are processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testOption(Intl.Collator, "numeric", "boolean", undefined, undefined, {isOptional: true});
testOption(Intl.Collator, "normalization", "boolean", undefined, undefined, {isOptional: true});
testOption(Intl.Collator, "caseFirst", "string", ["upper", "lower", "false"], undefined, {isOptional: true});

View File

@ -0,0 +1,44 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests the special handling of the "co" key in Intl.Collator.
* @author Norbert Lindenberg
*/
function checkCollation(extensionCoValue, usageValue, expectedCollations, expectedUsage) {
var requestLocale = extensionCoValue !== undefined ? "de-DE-u-co-" + extensionCoValue : "de-DE";
var options = usageValue !== undefined ? { usage: usageValue } : undefined;
var collator = new Intl.Collator([requestLocale], options);
var collation = collator.resolvedOptions().collation;
if (expectedCollations.indexOf(collation) === -1) {
$ERROR((extensionCoValue === undefined ? "Default collation" : "Collation for \"" + extensionCoValue) +
"\" should be " + expectedCollations.join(" or ") + ", but is " + collation + ".");
}
var usage = collator.resolvedOptions().usage;
if (expectedUsage !== usage) {
$ERROR((usageValue === undefined ? "Default usage" : "Usage") +
" should be " + expectedUsage + ", but is " + usage + ".");
}
}
checkCollation(undefined, undefined, ["default"], "sort");
checkCollation("phonebk", undefined, ["phonebk", "default"], "sort");
checkCollation("invalid", undefined, ["default"], "sort");
checkCollation("standard", undefined, ["default"], "sort");
checkCollation("standard", "search", ["default"], "search");
checkCollation("standard", "sort", ["default"], "sort");
checkCollation("search", undefined, ["default"], "sort");
checkCollation("search", "search", ["default"], "search");
checkCollation("search", "sort", ["default"], "sort");

View File

@ -0,0 +1,64 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the options numeric, normalization, and caseFirst can be
* set through either the locale or the options.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var options = [
{key: "kn", property: "numeric", type: "boolean", values: [true, false]},
{key: "kk", property: "normalization", type: "boolean", values: [true, false]},
{key: "kf", property: "caseFirst", type: "string", values: ["upper", "lower", "false"]}
];
options.forEach(function (option) {
var defaultLocale = new Intl.Collator().resolvedOptions().locale;
var collator, opt, result;
// find out which values are supported for a property in the default locale
var supportedValues = [];
option.values.forEach(function (value) {
opt = {};
opt[option.property] = value;
collator = new Intl.Collator([defaultLocale], opt);
result = collator.resolvedOptions()[option.property];
if (result !== undefined && supportedValues.indexOf(result) === -1) {
supportedValues.push(result);
}
});
// verify that the supported values can also be set through the locale
supportedValues.forEach(function (value) {
collator = new Intl.Collator([defaultLocale + "-u-" + option.key + "-" + value]);
result = collator.resolvedOptions()[option.property];
if (result !== value) {
$ERROR("Property " + option.property + " couldn't be set through locale extension key " +
option.key + "; requested value: " + value + "; actual value: " + result + ".");
}
});
// verify that the options setting overrides the locale setting
supportedValues.forEach(function (value) {
var otherValue;
option.values.forEach(function (possibleValue) {
if (possibleValue !== value) {
otherValue = possibleValue;
}
});
if (otherValue !== undefined) {
opt = {};
opt[option.property] = value;
collator = new Intl.Collator([defaultLocale + "-u-" + option.key + "-" + otherValue], opt);
result = collator.resolvedOptions()[option.property];
if (result !== value) {
$ERROR("Options value for property " + option.property + " doesn't override locale extension key " +
option.key + "; requested value: " + value + "; actual value: " + result + ".");
}
}
});
});

View File

@ -0,0 +1,13 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the option sensitivity is processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
// the fallback is variant only for usage === sort, but that happens to be the fallback for usage
testOption(Intl.Collator, "sensitivity", "string", ["base", "accent", "case", "variant"], "variant");

View File

@ -0,0 +1,13 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the option ignorePunctuation is processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
// the fallback is variant only for usage === sort, but that happens to be the fallback for usage
testOption(Intl.Collator, "ignorePunctuation", "boolean", undefined, false);

View File

@ -0,0 +1,12 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the option usage is processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testOption(Intl.Collator, "usage", "string", ["sort", "search"], "sort");

View File

@ -0,0 +1,30 @@
// Copyright 2011-2012 Norbert Lindenberg. All rights reserved.
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Intl.Collator can be subclassed.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
// get a collator and have it sort an array for comparison with the subclass
var locales = ["tlh", "id", "en"];
var a = ["A", "C", "E", "B", "D", "F"];
var referenceCollator = new Intl.Collator(locales);
var referenceSorted = a.slice().sort(referenceCollator.compare);
function MyCollator(locales, options) {
Intl.Collator.call(this, locales, options);
// could initialize MyCollator properties
}
MyCollator.prototype = Object.create(Intl.Collator.prototype);
MyCollator.prototype.constructor = MyCollator;
// could add methods to MyCollator.prototype
var collator = new MyCollator(locales);
a.sort(collator.compare);
testArraysAreSame(referenceSorted, a);

View File

@ -0,0 +1,19 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that objects constructed by Intl.Collator have the specified internal properties.
* @author Norbert Lindenberg
*/
var obj = new Intl.Collator();
var actualPrototype = Object.getPrototypeOf(obj);
if (actualPrototype !== Intl.Collator.prototype) {
$ERROR("Prototype of object constructed by Intl.Collator isn't Intl.Collator.prototype; got " + actualPrototype);
}
if (!Object.isExtensible(obj)) {
$ERROR("Object constructed by Intl.Collator must be extensible.");
}

View File

@ -0,0 +1,22 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/**
* @description Tests that Intl.Collator.prototype has the required attributes.
* @author Norbert Lindenberg
*/
var desc = Object.getOwnPropertyDescriptor(Intl.Collator, "prototype");
if (desc === undefined) {
$ERROR("Intl.Collator.prototype is not defined.");
}
if (desc.writable) {
$ERROR("Intl.Collator.prototype must not be writable.");
}
if (desc.enumerable) {
$ERROR("Intl.Collator.prototype must not be enumerable.");
}
if (desc.configurable) {
$ERROR("Intl.Collator.prototype must not be configurable.");
}

View File

@ -0,0 +1,47 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/**
* @description Tests that Intl.Collator does not accept Unicode locale
* extension keys and values that are not allowed.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var defaultCollator = new Intl.Collator();
var defaultOptions = defaultCollator.resolvedOptions();
var defaultOptionsJSON = JSON.stringify(defaultOptions);
var defaultLocale = defaultOptions.locale;
var defaultSortedArray = ["hello", "你好", "こんにちは", "pêche", "peché", "9", "10"].sort(defaultCollator.compare);
var keyValues = {
"co": ["standard", "search", "invalid"],
"ka": ["noignore", "shifted", "invalid"],
"kb": ["true", "false", "invalid"],
"kc": ["true", "false", "invalid"],
"kh": ["true", "false", "invalid"],
"kr": ["latn-hira-hani", "hani-hira-latn", "invalid"],
"ks": ["level1", "level2", "level3", "level4", "identic", "invalid"],
"vt": ["1234-5678-9abc-edf0", "invalid"]
};
var testArray = defaultSortedArray.slice(0);
Object.getOwnPropertyNames(keyValues).forEach(function (key) {
keyValues[key].forEach(function (value) {
var collator = new Intl.Collator([defaultLocale + "-u-" + key + "-" + value]);
var options = collator.resolvedOptions();
if (options.locale !== defaultLocale) {
$ERROR("Locale " + options.locale + " is affected by key " +
key + "; value " + value + ".");
}
if (JSON.stringify(options) !== defaultOptionsJSON) {
$ERROR("Resolved options " + JSON.stringify(options) + " are affected by key " +
key + "; value " + value + ".");
}
testArraysAreSame(defaultSortedArray, testArray.sort(collator.compare));
});
});

View File

@ -0,0 +1,36 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that compare function is bound to its Intl.Collator.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var strings = ["d", "O", "od", "oe", "of", "ö", "o\u0308", "X", "y", "Z", "Z.", "\uD842\uDFB7野家", "吉野家", "!A", "A", "b", "C"];
var locales = [undefined, ["de"], ["de-u-co-phonebk"], ["en"], ["ja"], ["sv"]];
var options = [
undefined,
{usage: "search"},
{sensitivity: "base", ignorePunctuation: true}
];
locales.forEach(function (locales) {
options.forEach(function (options) {
var collatorObj = new Intl.Collator(locales, options);
var compareFunc = collatorObj.compare;
var referenceSorted = strings.slice();
referenceSorted.sort(function (a, b) { return collatorObj.compare(a, b); });
var sorted = strings;
sorted.sort(compareFunc);
try {
testArraysAreSame(referenceSorted, sorted);
} catch (e) {
e.message += " (Testing with locales " + locales + "; options " +
(options ? JSON.stringify(options) : options) + ".)";
throw e;
}
});
});

View File

@ -0,0 +1,56 @@
// Copyright 2012 Norbert Lindenberg. All rights reserved.
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/**
* @description Tests that the function returned by Intl.Collator.prototype.compare
* returns 0 when comparing Strings that are considered canonically equivalent
* by the Unicode standard.
* @author Norbert Lindenberg
*/
var collator = new Intl.Collator();
var pairs = [
// example from Unicode 5.0, section 3.7, definition D70
["o\u0308", "ö"],
// examples from Unicode 5.0, chapter 3.11
["ä\u0323", "a\u0323\u0308"],
["a\u0308\u0323", "a\u0323\u0308"],
["ạ\u0308", "a\u0323\u0308"],
["ä\u0306", "a\u0308\u0306"],
["ă\u0308", "a\u0306\u0308"],
// example from Unicode 5.0, chapter 3.12
["\u1111\u1171\u11B6", "퓛"],
// examples from UTS 10, Unicode Collation Algorithm
["Å", "Å"],
["Å", "A\u030A"],
["x\u031B\u0323", "x\u0323\u031B"],
["ự", "ụ\u031B"],
["ự", "u\u031B\u0323"],
["ự", "ư\u0323"],
["ự", "u\u0323\u031B"],
// examples from UAX 15, Unicode Normalization Forms
["Ç", "C\u0327"],
["q\u0307\u0323", "q\u0323\u0307"],
["가", "\u1100\u1161"],
["Å", "A\u030A"],
["Ω", "Ω"],
["Å", "A\u030A"],
["ô", "o\u0302"],
["ṩ", "s\u0323\u0307"],
["ḋ\u0323", "d\u0323\u0307"],
["ḋ\u0323", "ḍ\u0307"],
["q\u0307\u0323", "q\u0323\u0307"],
// examples involving supplementary characters from UCD NormalizationTest.txt
["\uD834\uDD5E", "\uD834\uDD57\uD834\uDD65"],
["\uD87E\uDC2B", "北"]
];
var i;
for (i = 0; i < pairs.length; i++) {
var pair = pairs[i];
if (collator.compare(pair[0], pair[1]) !== 0) {
$ERROR("Collator.compare considers " + pair[0] + " ≠ " + pair[1] + ".");
}
}

View File

@ -0,0 +1,22 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the compare function isn't entirely unreasonable.
* This test is not normative.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
// this test should be valid at least for the following locales
var locales = ["de", "en", "es", "fr", "it"];
locales = Intl.Collator.supportedLocalesOf(locales, {localeMatcher: "lookup"});
locales.forEach(function (locale) {
var collator = new Intl.Collator([locale], {sensitivity: "variant", localeMatcher: "lookup"});
var a = ["L", "X", "C", "k", "Z", "H", "d", "m", "w", "A", "i", "f", "y", "E", "N", "V", "g", "J", "b"];
a.sort(collator.compare);
var expected = ["A", "b", "C", "d", "E", "f", "g", "H", "i", "J", "k", "L", "m", "N", "V", "w", "X", "y", "Z"];
testArraysAreSame(expected, a);
});

View File

@ -0,0 +1,22 @@
// Copyright 2011-2012 Norbert Lindenberg. All rights reserved.
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the compare function supports phonebook sorting if it says it does.
* This test is not normative.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
// this test should be valid at least for the following locales
var locales = ["de-DE-u-co-phonebk", "de-u-co-phonebk"];
var collator = new Intl.Collator(locales, {localeMatcher: "lookup"});
if (locales.indexOf(collator.resolvedOptions().locale) !== -1) {
var a = ["A", "b", "Af", "Ab", "od", "off", "Ä", "ö"];
a.sort(collator.compare);
var expected = ["A", "Ab", "Ä", "Af", "b", "od", "ö", "off"];
testArraysAreSame(expected, a);
}

View File

@ -0,0 +1,34 @@
// Copyright 2011-2012 Norbert Lindenberg. All rights reserved.
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the compare function supports different sensitivity settings.
* This test is not normative.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
// this test should be valid at least for the following locales
var locales = ["de", "en", "es", "it"];
locales = Intl.Collator.supportedLocalesOf(locales, {localeMatcher: "lookup"});
locales.forEach(function (locale) {
var target = "Aa";
var input = ["Aa", "bA", "E", "b", "aA", "fC", "áÁ", "Aã"];
var expected = {
"base": ["Aa", "aA", "áÁ", "Aã"],
"accent": ["Aa", "aA"],
"case": ["Aa", "Aã"],
"variant": ["Aa"]
};
Object.getOwnPropertyNames(expected).forEach(function (sensitivity) {
var collator = new Intl.Collator([locale], {usage: "search",
sensitivity: sensitivity, localeMatcher: "lookup"});
var matches = input.filter(function (v) {
return collator.compare(v, target) === 0;
});
testArraysAreSame(expected[sensitivity], matches);
});
});

View File

@ -0,0 +1,48 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/**
* @description Tests that the object returned by Intl.Collator.prototype.resolvedOptions
* has the right properties.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var actual = new Intl.Collator().resolvedOptions();
var actual2 = new Intl.Collator().resolvedOptions();
if (actual2 === actual) {
$ERROR("resolvedOptions returned the same object twice.");
}
// source: CLDR file common/bcp47/collation.xml; version CLDR 21.
var collations = [
"default", // added
"big5han",
"dict",
"direct",
"ducet",
"gb2312",
"phonebk",
"phonetic",
"pinyin",
"reformed",
// "search", // excluded
"searchjl",
// "standard", // excluded
"stroke",
"trad",
"unihan"
];
// this assumes the default values where the specification provides them
mustHaveProperty(actual, "locale", isCanonicalizedStructurallyValidLanguageTag);
mustHaveProperty(actual, "usage", ["sort"]);
mustHaveProperty(actual, "sensitivity", ["variant"]);
mustHaveProperty(actual, "ignorePunctuation", [false]);
mustHaveProperty(actual, "collation", collations);
mayHaveProperty(actual, "numeric", [true, false]);
mayHaveProperty(actual, "normalization", [true, false]);
mayHaveProperty(actual, "caseFirst", ["upper", "lower", "false"]);

View File

@ -0,0 +1,33 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Intl.Collator.prototype functions throw a
* TypeError if called on a non-object value or an object that hasn't been
* initialized as a Collator.
* @author Norbert Lindenberg
*/
var functions = {
"compare getter": Object.getOwnPropertyDescriptor(Intl.Collator.prototype, "compare").get,
resolvedOptions: Intl.Collator.prototype.resolvedOptions
};
var invalidTargets = [undefined, null, true, 0, "Collator", [], {}];
Object.getOwnPropertyNames(functions).forEach(function (functionName) {
var f = functions[functionName];
invalidTargets.forEach(function (target) {
var error;
try {
f.call(target);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Calling " + functionName + " on " + target + " was not rejected.");
} else if (error.name !== "TypeError") {
$ERROR("Calling " + functionName + " on " + target + " was rejected with wrong error " + error.name + ".");
}
});
});

View File

@ -0,0 +1,15 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Intl.Collator instances have the specified properties.
* @author Norbert Lindenberg
*/
var obj = new Intl.Collator();
var toStringValue = Object.prototype.toString.call(obj);
if (toStringValue !== "[object Object]") {
$ERROR("Intl.Collator instance produces wrong [[Class]] - toString returns " + toStringValue + ".");
}

View File

@ -0,0 +1,43 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that an object can't be re-initialized as a NumberFormat.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testWithIntlConstructors(function (Constructor) {
var obj, error;
// variant 1: use constructor in a "new" expression
obj = new Constructor();
try {
Intl.NumberFormat.call(obj);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Re-initializing object created with \"new\" as NumberFormat was not rejected.");
} else if (error.name !== "TypeError") {
$ERROR("Re-initializing object created with \"new\" as NumberFormat was rejected with wrong error " + error.name + ".");
}
// variant 2: use constructor as a function
obj = Constructor.call({});
error = undefined;
try {
Intl.NumberFormat.call(obj);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Re-initializing object created with constructor as function as NumberFormat was not rejected.");
} else if (error.name !== "TypeError") {
$ERROR("Re-initializing object created with constructor as function as NumberFormat was rejected with wrong error " + error.name + ".");
}
return true;
});

View File

@ -0,0 +1,13 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the option style is processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testOption(Intl.NumberFormat, "style", "string", ["decimal", "percent", "currency"], "decimal",
{extra: {"currency": {currency: "CNY"}}});

View File

@ -0,0 +1,81 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the option currency is processed correctly.
* @author Norbert Lindenberg
*/
var validValues = ["CNY", "USD", "EUR", "IDR", "jpy", {toString: function () {return "INR";}}];
var invalidValues = ["$", "SFr.", "US$", "ßP", {toString: function () {return;}}];
var defaultLocale = new Intl.NumberFormat().resolvedOptions().locale;
validValues.forEach(function (value) {
var format, actual, expected;
// with currency style, we should get the upper case form back
format = new Intl.NumberFormat([defaultLocale], {style: "currency", currency: value});
actual = format.resolvedOptions().currency;
expected = value.toString().toUpperCase();
if (actual !== expected) {
$ERROR("Incorrect resolved currency with currency style - expected " +
expected + "; got " + actual + ".");
}
// without currency style, we shouldn't get any currency back
format = new Intl.NumberFormat([defaultLocale], {currency: value});
actual = format.resolvedOptions().currency;
expected = undefined;
if (actual !== expected) {
$ERROR("Incorrect resolved currency with non-currency style - expected " +
expected + "; got " + actual + ".");
}
// currencies specified through the locale must be ignored
format = new Intl.NumberFormat([defaultLocale + "-u-cu-krw"], {style: "currency", currency: value});
actual = format.resolvedOptions().currency;
expected = value.toString().toUpperCase();
if (actual !== expected) {
$ERROR("Incorrect resolved currency with -u-cu- and currency style - expected " +
expected + "; got " + actual + ".");
}
format = new Intl.NumberFormat([defaultLocale + "-u-cu-krw"], {currency: value});
actual = format.resolvedOptions().currency;
expected = undefined;
if (actual !== expected) {
$ERROR("Incorrect resolved currency with -u-cu- and non-currency style - expected " +
expected + "; got " + actual + ".");
}
});
invalidValues.forEach(function (value) {
function expectError(f) {
var error;
try {
f();
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Invalid currency value " + value + " was not rejected.");
} else if (error.name !== "RangeError") {
$ERROR("Invalid currency value " + value + " was rejected with wrong error " + error.name + ".");
}
}
expectError(function () {
return new Intl.NumberFormat([defaultLocale], {style: "currency", currency: value});
});
expectError(function () {
return new Intl.NumberFormat([defaultLocale], {currency: value});
});
expectError(function () {
return new Intl.NumberFormat([defaultLocale + "-u-cu-krw"], {style: "currency", currency: value});
});
expectError(function () {
return new Intl.NumberFormat([defaultLocale + "-u-cu-krw"], {currency: value});
});
});

View File

@ -0,0 +1,31 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the currency style can not be used without a specified currency.
* @author Norbert Lindenberg
*/
var defaultLocale = new Intl.NumberFormat().resolvedOptions().locale;
function expectError(f) {
var error;
try {
f();
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Invalid currency value " + value + " was not rejected.");
} else if (error.name !== "TypeError") {
$ERROR("Invalid currency value " + value + " was rejected with wrong error " + error.name + ".");
}
}
expectError(function () {
return new Intl.NumberFormat([defaultLocale], {style: "currency"});
});
expectError(function () {
return new Intl.NumberFormat([defaultLocale + "-u-cu-krw"], {style: "currency"});
});

View File

@ -0,0 +1,196 @@
// Copyright 2011-2012 Norbert Lindenberg. All rights reserved.
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the number of fractional digits is determined correctly for currencies.
* @author Norbert Lindenberg
*/
// data from http://www.currency-iso.org/dl_iso_table_a1.xml, 2012-08-10
var currencyDigits = {
AED: 2,
AFN: 2,
ALL: 2,
AMD: 2,
ANG: 2,
AOA: 2,
ARS: 2,
AUD: 2,
AWG: 2,
AZN: 2,
BAM: 2,
BBD: 2,
BDT: 2,
BGN: 2,
BHD: 3,
BIF: 0,
BMD: 2,
BND: 2,
BOB: 2,
BOV: 2,
BRL: 2,
BSD: 2,
BTN: 2,
BWP: 2,
BYR: 0,
BZD: 2,
CAD: 2,
CDF: 2,
CHE: 2,
CHF: 2,
CHW: 2,
CLF: 0,
CLP: 0,
CNY: 2,
COP: 2,
COU: 2,
CRC: 2,
CUC: 2,
CUP: 2,
CVE: 2,
CZK: 2,
DJF: 0,
DKK: 2,
DOP: 2,
DZD: 2,
EGP: 2,
ERN: 2,
ETB: 2,
EUR: 2,
FJD: 2,
FKP: 2,
GBP: 2,
GEL: 2,
GHS: 2,
GIP: 2,
GMD: 2,
GNF: 0,
GTQ: 2,
GYD: 2,
HKD: 2,
HNL: 2,
HRK: 2,
HTG: 2,
HUF: 2,
IDR: 2,
ILS: 2,
INR: 2,
IQD: 3,
IRR: 2,
ISK: 0,
JMD: 2,
JOD: 3,
JPY: 0,
KES: 2,
KGS: 2,
KHR: 2,
KMF: 0,
KPW: 2,
KRW: 0,
KWD: 3,
KYD: 2,
KZT: 2,
LAK: 2,
LBP: 2,
LKR: 2,
LRD: 2,
LSL: 2,
LTL: 2,
LVL: 2,
LYD: 3,
MAD: 2,
MDL: 2,
MGA: 2,
MKD: 2,
MMK: 2,
MNT: 2,
MOP: 2,
MRO: 2,
MUR: 2,
MVR: 2,
MWK: 2,
MXN: 2,
MXV: 2,
MYR: 2,
MZN: 2,
NAD: 2,
NGN: 2,
NIO: 2,
NOK: 2,
NPR: 2,
NZD: 2,
OMR: 3,
PAB: 2,
PEN: 2,
PGK: 2,
PHP: 2,
PKR: 2,
PLN: 2,
PYG: 0,
QAR: 2,
RON: 2,
RSD: 2,
RUB: 2,
RWF: 0,
SAR: 2,
SBD: 2,
SCR: 2,
SDG: 2,
SEK: 2,
SGD: 2,
SHP: 2,
SLL: 2,
SOS: 2,
SRD: 2,
SSP: 2,
STD: 2,
SVC: 2,
SYP: 2,
SZL: 2,
THB: 2,
TJS: 2,
TMT: 2,
TND: 3,
TOP: 2,
TRY: 2,
TTD: 2,
TWD: 2,
TZS: 2,
UAH: 2,
UGX: 2,
USD: 2,
USN: 2,
USS: 2,
UYI: 0,
UYU: 2,
UZS: 2,
VEF: 2,
VND: 0,
VUV: 0,
WST: 2,
XAF: 0,
XCD: 2,
XOF: 0,
XPF: 0,
YER: 2,
ZAR: 2,
ZMK: 2,
ZWL: 2
};
Object.getOwnPropertyNames(currencyDigits).forEach(function (currency) {
var digits = currencyDigits[currency];
format = Intl.NumberFormat([], {style: "currency", currency: currency});
var min = format.resolvedOptions().minimumFractionDigits;
var max = format.resolvedOptions().maximumFractionDigits;
if (min !== digits) {
$ERROR("Didn't get correct minimumFractionDigits for currency " +
currency + "; expected " + digits + ", got " + min + ".");
}
if (max !== digits) {
$ERROR("Didn't get correct maximumFractionDigits for currency " +
currency + "; expected " + digits + ", got " + max + ".");
}
});

View File

@ -0,0 +1,15 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the option currencyDisplay is processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testOption(Intl.NumberFormat, "currencyDisplay", "string", ["code", "symbol", "name"],
"symbol", {extra: {any: {style: "currency", currency: "XDR"}}});
testOption(Intl.NumberFormat, "currencyDisplay", "string", ["code", "symbol", "name"],
undefined, {noReturn: true});

View File

@ -0,0 +1,12 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the option useGrouping is processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testOption(Intl.NumberFormat, "useGrouping", "boolean", undefined, true);

View File

@ -0,0 +1,18 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the behavior of a Record is not affected by adversarial
* changes to Object.prototype.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
taintProperties(["localeMatcher"]);
var locale = new Intl.NumberFormat(undefined, {localeMatcher: "lookup"}).resolvedOptions().locale;
if (!isCanonicalizedStructurallyValidLanguageTag(locale)) {
$ERROR("NumberFormat returns invalid locale " + locale + ".");
}

View File

@ -0,0 +1,12 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the option localeMatcher is processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testOption(Intl.NumberFormat, "localeMatcher", "string", ["lookup", "best fit"], "best fit", {noReturn: true});

View File

@ -0,0 +1,30 @@
// Copyright 2011-2012 Norbert Lindenberg. All rights reserved.
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Intl.NumberFormat can be subclassed.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
// get a number format and have it format an array of numbers for comparison with the subclass
var locales = ["tlh", "id", "en"];
var a = [0, 1, -1, -123456.789, -Infinity, NaN];
var referenceNumberFormat = new Intl.NumberFormat(locales);
var referenceFormatted = a.map(referenceNumberFormat.format);
function MyNumberFormat(locales, options) {
Intl.NumberFormat.call(this, locales, options);
// could initialize MyNumberFormat properties
}
MyNumberFormat.prototype = Object.create(Intl.NumberFormat.prototype);
MyNumberFormat.prototype.constructor = MyNumberFormat;
// could add methods to MyNumberFormat.prototype
var format = new MyNumberFormat(locales);
var actual = a.map(format.format);
testArraysAreSame(referenceFormatted, actual);

View File

@ -0,0 +1,19 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that objects constructed by Intl.NumberFormat have the specified internal properties.
* @author Norbert Lindenberg
*/
var obj = new Intl.NumberFormat();
var actualPrototype = Object.getPrototypeOf(obj);
if (actualPrototype !== Intl.NumberFormat.prototype) {
$ERROR("Prototype of object constructed by Intl.NumberFormat isn't Intl.NumberFormat.prototype; got " + actualPrototype);
}
if (!Object.isExtensible(obj)) {
$ERROR("Object constructed by Intl.NumberFormat must be extensible.");
}

View File

@ -0,0 +1,22 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/**
* @description Tests that Intl.NumberFormat.prototype has the required attributes.
* @author Norbert Lindenberg
*/
var desc = Object.getOwnPropertyDescriptor(Intl.NumberFormat, "prototype");
if (desc === undefined) {
$ERROR("Intl.NumberFormat.prototype is not defined.");
}
if (desc.writable) {
$ERROR("Intl.NumberFormat.prototype must not be writable.");
}
if (desc.enumerable) {
$ERROR("Intl.NumberFormat.prototype must not be enumerable.");
}
if (desc.configurable) {
$ERROR("Intl.NumberFormat.prototype must not be configurable.");
}

View File

@ -0,0 +1,46 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/**
* @description Tests that Intl.NumberFormat does not accept Unicode locale
* extension keys and values that are not allowed.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var locales = ["ja-JP", "zh-Hans-CN", "zh-Hant-TW"];
var input = 1234567.89;
locales.forEach(function (locale) {
var defaultNumberFormat = new Intl.NumberFormat([locale]);
var defaultOptions = defaultNumberFormat.resolvedOptions();
var defaultOptionsJSON = JSON.stringify(defaultOptions);
var defaultLocale = defaultOptions.locale;
var defaultFormatted = defaultNumberFormat.format(input);
var keyValues = {
"cu": ["USD", "EUR", "JPY", "CNY", "TWD", "invalid"],
"nu": ["native", "traditio", "finance", "invalid"]
};
Object.getOwnPropertyNames(keyValues).forEach(function (key) {
keyValues[key].forEach(function (value) {
var numberFormat = new Intl.NumberFormat([locale + "-u-" + key + "-" + value]);
var options = numberFormat.resolvedOptions();
if (options.locale !== defaultLocale) {
$ERROR("Locale " + options.locale + " is affected by key " +
key + "; value " + value + ".");
}
if (JSON.stringify(options) !== defaultOptionsJSON) {
$ERROR("Resolved options " + JSON.stringify(options) + " are affected by key " +
key + "; value " + value + ".");
}
if (defaultFormatted !== numberFormat.format(input)) {
$ERROR("Formatted value " + numberFormat.format(input) + " is affected by key " +
key + "; value " + value + ".");
}
});
});
});

View File

@ -0,0 +1,41 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that format function is bound to its Intl.NumberFormat.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var numbers = [0, -0, 1, -1, 5.5, 123, -123, -123.45, 123.44501, 0.001234,
-0.00000000123, 0.00000000000000000000000000000123, 1.2, 0.0000000012344501,
123445.01, 12344501000000000000000000000000000, -12344501000000000000000000000000000,
Infinity, -Infinity, NaN];
var locales = [undefined, ["de"], ["th-u-nu-thai"], ["en"], ["ja-u-nu-jpanfin"], ["ar-u-nu-arab"]];
var options = [
undefined,
{style: "percent"},
{style: "currency", currency: "EUR", currencyDisplay: "symbol"},
{style: "currency", currency: "IQD", currencyDisplay: "symbol"},
{style: "currency", currency: "KMF", currencyDisplay: "symbol"},
{style: "currency", currency: "CLF", currencyDisplay: "symbol"},
{useGrouping: false, minimumIntegerDigits: 3, minimumFractionDigits: 1, maximumFractionDigits: 3}
];
locales.forEach(function (locales) {
options.forEach(function (options) {
var formatObj = new Intl.NumberFormat(locales, options);
var formatFunc = formatObj.format;
numbers.forEach(function (number) {
var referenceFormatted = formatObj.format(number);
var formatted = formatFunc(number);
if (referenceFormatted !== formatted) {
$ERROR("format function produces different result than format method for locales " +
locales + "; options: " + (options ? JSON.stringify(options) : options) +
" : " + formatted + " vs. " + referenceFormatted + ".");
}
});
});
});

View File

@ -0,0 +1,52 @@
// Copyright 2011-2012 Norbert Lindenberg. All rights reserved.
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the digits are determined correctly when specifying pre/post decimal digits.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var locales = [
new Intl.NumberFormat().resolvedOptions().locale,
"ar", "de", "th", "ja"
];
var numberingSystems = [
"arab",
"latn",
"thai",
"hanidec"
];
var testData = {
"0": "000.0",
"-0": "000.0",
"123": "123.0",
"-123": "-123.0",
"12345": "12345.0",
"-12345": "-12345.0",
"123.45": "123.45",
"-123.45": "-123.45",
"123.44501": "123.445",
"-123.44501": "-123.445",
"0.001234": "000.001",
"-0.001234": "-000.001",
"0.00000000123": "000.0",
"-0.00000000123": "-000.0",
"0.00000000000000000000000000000123": "000.0",
"-0.00000000000000000000000000000123": "-000.0",
"1.2": "001.2",
"-1.2": "-001.2",
"0.0000000012344501": "000.0",
"-0.0000000012344501": "-000.0",
"123445.01": "123445.01",
"-123445.01": "-123445.01",
"12344501000000000000000000000000000": "12344501000000000000000000000000000.0",
"-12344501000000000000000000000000000": "-12344501000000000000000000000000000.0"
};
testNumberFormat(locales, numberingSystems,
{useGrouping: false, minimumIntegerDigits: 3, minimumFractionDigits: 1, maximumFractionDigits: 3},
testData);

View File

@ -0,0 +1,52 @@
// Copyright 2011-2012 Norbert Lindenberg. All rights reserved.
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the digits are determined correctly when specifying significant digits.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var locales = [
new Intl.NumberFormat().resolvedOptions().locale,
"ar", "de", "th", "ja"
];
var numberingSystems = [
"arab",
"latn",
"thai",
"hanidec"
];
var testData = {
"0": "0.00",
"-0": "0.00",
"123": "123",
"-123": "-123",
"12345": "12345",
"-12345": "-12345",
"123.45": "123.45",
"-123.45": "-123.45",
"123.44501": "123.45",
"-123.44501": "-123.45",
"0.001234": "0.001234",
"-0.001234": "-0.001234",
"0.00000000123": "0.00000000123",
"-0.00000000123": "-0.00000000123",
"0.00000000000000000000000000000123": "0.00000000000000000000000000000123",
"-0.00000000000000000000000000000123": "-0.00000000000000000000000000000123",
"1.2": "1.20",
"-1.2": "-1.20",
"0.0000000012344501": "0.0000000012345",
"-0.0000000012344501": "-0.0000000012345",
"123445.01": "123450",
"-123445.01": "-123450",
"12344501000000000000000000000000000": "12345000000000000000000000000000000",
"-12344501000000000000000000000000000": "-12345000000000000000000000000000000"
};
testNumberFormat(locales, numberingSystems,
{useGrouping: false, minimumSignificantDigits: 3, maximumSignificantDigits: 5},
testData);

View File

@ -0,0 +1,31 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/**
* @description Tests that the object returned by Intl.NumberFormat.prototype.resolvedOptions
* has the right properties.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var actual = new Intl.NumberFormat().resolvedOptions();
var actual2 = new Intl.NumberFormat().resolvedOptions();
if (actual2 === actual) {
$ERROR("resolvedOptions returned the same object twice.");
}
// this assumes the default values where the specification provides them
mustHaveProperty(actual, "locale", isCanonicalizedStructurallyValidLanguageTag);
mustHaveProperty(actual, "numberingSystem", isValidNumberingSystem);
mustHaveProperty(actual, "style", ["decimal"]);
mustNotHaveProperty(actual, "currency");
mustNotHaveProperty(actual, "currencyDisplay");
mustHaveProperty(actual, "minimumIntegerDigits", [1]);
mustHaveProperty(actual, "minimumFractionDigits", [0]);
mustHaveProperty(actual, "maximumFractionDigits", [3]);
mustNotHaveProperty(actual, "minimumSignificantDigits");
mustNotHaveProperty(actual, "maximumSignificantDigits");
mustHaveProperty(actual, "useGrouping", [true, false]);

View File

@ -0,0 +1,33 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Intl.NumberFormat.prototype functions throw a
* TypeError if called on a non-object value or an object that hasn't been
* initialized as a NumberFormat.
* @author Norbert Lindenberg
*/
var functions = {
"format getter": Object.getOwnPropertyDescriptor(Intl.NumberFormat.prototype, "format").get,
resolvedOptions: Intl.NumberFormat.prototype.resolvedOptions
};
var invalidTargets = [undefined, null, true, 0, "NumberFormat", [], {}];
Object.getOwnPropertyNames(functions).forEach(function (functionName) {
var f = functions[functionName];
invalidTargets.forEach(function (target) {
var error;
try {
f.call(target);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Calling " + functionName + " on " + target + " was not rejected.");
} else if (error.name !== "TypeError") {
$ERROR("Calling " + functionName + " on " + target + " was rejected with wrong error " + error.name + ".");
}
});
});

View File

@ -0,0 +1,15 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Intl.NumberFormat instances have the specified properties.
* @author Norbert Lindenberg
*/
var obj = new Intl.NumberFormat();
var toStringValue = Object.prototype.toString.call(obj);
if (toStringValue !== "[object Object]") {
$ERROR("Intl.NumberFormat instance produces wrong [[Class]] - toString returns " + toStringValue + ".");
}

View File

@ -0,0 +1,43 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that an object can't be re-initialized as a DateTimeFormat.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testWithIntlConstructors(function (Constructor) {
var obj, error;
// variant 1: use constructor in a "new" expression
obj = new Constructor();
try {
Intl.DateTimeFormat.call(obj);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Re-initializing object created with \"new\" as DateTimeFormat was not rejected.");
} else if (error.name !== "TypeError") {
$ERROR("Re-initializing object created with \"new\" as DateTimeFormat was rejected with wrong error " + error.name + ".");
}
// variant 2: use constructor as a function
obj = Constructor.call({});
error = undefined;
try {
Intl.DateTimeFormat.call(obj);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Re-initializing object created with constructor as function as DateTimeFormat was not rejected.");
} else if (error.name !== "TypeError") {
$ERROR("Re-initializing object created with constructor as function as DateTimeFormat was rejected with wrong error " + error.name + ".");
}
return true;
});

View File

@ -0,0 +1,12 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the option hour12 is processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testOption(Intl.DateTimeFormat, "hour12", "boolean");

View File

@ -0,0 +1,18 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the behavior of a Record is not affected by adversarial
* changes to Object.prototype.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
taintProperties(["weekday", "era", "year", "month", "day", "hour", "minute", "second", "timeZone"]);
var locale = new Intl.DateTimeFormat(undefined, {localeMatcher: "lookup"}).resolvedOptions().locale;
if (!isCanonicalizedStructurallyValidLanguageTag(locale)) {
$ERROR("DateTimeFormat returns invalid locale " + locale + ".");
}

View File

@ -0,0 +1,14 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the options for the date and time components are processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
getDateTimeComponents().forEach(function (component) {
testOption(Intl.DateTimeFormat, component, "string", getDateTimeComponentValues(component), undefined, {isILD: true});
});

View File

@ -0,0 +1,12 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the option formatMatcher is processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testOption(Intl.DateTimeFormat, "formatMatcher", "string", ["basic", "best fit"], "best fit", {noReturn: true});

View File

@ -0,0 +1,18 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the behavior of a Record is not affected by adversarial
* changes to Object.prototype.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
taintProperties(["localeMatcher"]);
var locale = new Intl.DateTimeFormat(undefined, {localeMatcher: "lookup"}).resolvedOptions().locale;
if (!isCanonicalizedStructurallyValidLanguageTag(locale)) {
$ERROR("DateTimeFormat returns invalid locale " + locale + ".");
}

View File

@ -0,0 +1,12 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the option localeMatcher is processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
testOption(Intl.DateTimeFormat, "localeMatcher", "string", ["lookup", "best fit"], "best fit", {noReturn: true});

View File

@ -0,0 +1,107 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the set of options for the date and time components is processed correctly.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var locales = [[], ["zh-Hans-CN"], ["hi-IN"], ["en-US"], ["id-ID"]];
var dates = [new Date(), new Date(0), new Date(Date.parse("1989-11-09T17:57:00Z"))];
function testWithDateTimeFormat(options, expected) {
locales.forEach(function (locales) {
var format = new Intl.DateTimeFormat(locales, options);
var resolvedOptions = format.resolvedOptions();
getDateTimeComponents().forEach(function (component) {
if (resolvedOptions.hasOwnProperty(component)) {
if (!expected.hasOwnProperty(component)) {
$ERROR("Unrequested component " + component +
" added to expected subset " + JSON.stringify(expected) +
"; locales " + locales + ", options " +
(options ? JSON.stringify(options) : options) + ".");
}
} else {
if (expected.hasOwnProperty(component)) {
$ERROR("Missing component " + component +
" from expected subset " + JSON.stringify(expected) +
"; locales " + locales + ", options " +
(options ? JSON.stringify(options) : options) + ".");
}
}
});
});
}
function testWithToLocale(f, options, expected) {
// expected can be either one subset or an array of possible subsets
if (expected.length === undefined) {
expected = [expected];
}
locales.forEach(function (locales) {
dates.forEach(function (date) {
var formatted = Date.prototype[f].call(date, locales, options);
var expectedStrings = [];
expected.forEach(function (expected) {
var referenceFormat = new Intl.DateTimeFormat(locales, expected);
expectedStrings.push(referenceFormat.format(date));
});
if (expectedStrings.indexOf(formatted) === -1) {
$ERROR("Function " + f + " did not return expected string for locales " +
locales + ", options " + (options? JSON.stringify(options) : options) +
"; expected " +
(expectedStrings.length === 1 ? expectedStrings[0] : "one of " + expectedStrings) +
", got " + formatted + ".");
}
});
});
}
// any/date: steps 5a, 6a, 7a
testWithDateTimeFormat(undefined, {year: "numeric", month: "numeric", day: "numeric"});
// any/date: steps 5a, 6a
testWithDateTimeFormat({year: "numeric", month: "numeric"}, {year: "numeric", month: "numeric"});
// any/date: steps 5a, 6a
testWithDateTimeFormat({hour: "numeric", minute: "numeric"}, {hour: "numeric", minute: "numeric"});
// any/all: steps 5a, 6a, 7a, 8a
testWithToLocale("toLocaleString", undefined, [
// the first one is not guaranteed to be supported; the second one is
{year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric"},
{weekday: "short", year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric"}
]);
// any/all: steps 5a, 6a
testWithToLocale("toLocaleString", {year: "numeric", month: "numeric"}, {year: "numeric", month: "numeric"});
// any/all: steps 5a, 6a
testWithToLocale("toLocaleString", {hour: "numeric", minute: "numeric"}, {hour: "numeric", minute: "numeric"});
// date/date: steps 5a, 7a
testWithToLocale("toLocaleDateString", undefined, {year: "numeric", month: "numeric", day: "numeric"});
// date/date: steps 5a
testWithToLocale("toLocaleDateString", {year: "numeric", month: "numeric"}, {year: "numeric", month: "numeric"});
// date/date: steps 5a, 7a
testWithToLocale("toLocaleDateString", {hour: "numeric", minute: "numeric", second: "numeric"}, [
// the first one is not guaranteed to be supported; the second one is
{year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric"},
{weekday: "short", year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric"}
]);
// time/time: steps 6a, 8a
testWithToLocale("toLocaleTimeString", undefined, {hour: "numeric", minute: "numeric", second: "numeric"});
// time/time: steps 6a, 8a
testWithToLocale("toLocaleTimeString", {weekday: "short", year: "numeric", month: "numeric", day: "numeric"},
{weekday: "short", year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric"});
// time/time: steps 6a
testWithToLocale("toLocaleTimeString", {hour: "numeric", minute: "numeric"}, {hour: "numeric", minute: "numeric"});

View File

@ -0,0 +1,30 @@
// Copyright 2011-2012 Norbert Lindenberg. All rights reserved.
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Intl.DateTimeFormat can be subclassed.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
// get a date-time format and have it format an array of dates for comparison with the subclass
var locales = ["tlh", "id", "en"];
var a = [new Date(0), Date.now(), new Date(Date.parse("1989-11-09T17:57:00Z"))];
var referenceDateTimeFormat = new Intl.DateTimeFormat(locales);
var referenceFormatted = a.map(referenceDateTimeFormat.format);
function MyDateTimeFormat(locales, options) {
Intl.DateTimeFormat.call(this, locales, options);
// could initialize MyDateTimeFormat properties
}
MyDateTimeFormat.prototype = Object.create(Intl.DateTimeFormat.prototype);
MyDateTimeFormat.prototype.constructor = MyDateTimeFormat;
// could add methods to MyDateTimeFormat.prototype
var format = new MyDateTimeFormat(locales);
var actual = a.map(format.format);
testArraysAreSame(referenceFormatted, actual);

View File

@ -0,0 +1,19 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that objects constructed by Intl.DateTimeFormat have the specified internal properties.
* @author Norbert Lindenberg
*/
var obj = new Intl.DateTimeFormat();
var actualPrototype = Object.getPrototypeOf(obj);
if (actualPrototype !== Intl.DateTimeFormat.prototype) {
$ERROR("Prototype of object constructed by Intl.DateTimeFormat isn't Intl.DateTimeFormat.prototype; got " + actualPrototype);
}
if (!Object.isExtensible(obj)) {
$ERROR("Object constructed by Intl.DateTimeFormat must be extensible.");
}

View File

@ -0,0 +1,22 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/**
* @description Tests that Intl.DateTimeFormat.prototype has the required attributes.
* @author Norbert Lindenberg
*/
var desc = Object.getOwnPropertyDescriptor(Intl.DateTimeFormat, "prototype");
if (desc === undefined) {
$ERROR("Intl.DateTimeFormat.prototype is not defined.");
}
if (desc.writable) {
$ERROR("Intl.DateTimeFormat.prototype must not be writable.");
}
if (desc.enumerable) {
$ERROR("Intl.DateTimeFormat.prototype must not be enumerable.");
}
if (desc.configurable) {
$ERROR("Intl.DateTimeFormat.prototype must not be configurable.");
}

View File

@ -0,0 +1,47 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/**
* @description Tests that Intl.DateTimeFormat does not accept Unicode locale
* extension keys and values that are not allowed.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var locales = ["ja-JP", "zh-Hans-CN", "zh-Hant-TW"];
var input = new Date(Date.parse("1989-11-09T17:57:00Z"));
locales.forEach(function (locale) {
var defaultDateTimeFormat = new Intl.DateTimeFormat([locale]);
var defaultOptions = defaultDateTimeFormat.resolvedOptions();
var defaultOptionsJSON = JSON.stringify(defaultOptions);
var defaultLocale = defaultOptions.locale;
var defaultFormatted = defaultDateTimeFormat.format(input);
var keyValues = {
"cu": ["USD", "EUR", "JPY", "CNY", "TWD", "invalid"], // DateTimeFormat internally uses NumberFormat
"nu": ["native", "traditio", "finance", "invalid"],
"tz": ["usnavajo", "utcw01", "aumel", "uslax", "usnyc", "deber", "invalid"]
};
Object.getOwnPropertyNames(keyValues).forEach(function (key) {
keyValues[key].forEach(function (value) {
var dateTimeFormat = new Intl.DateTimeFormat([locale + "-u-" + key + "-" + value]);
var options = dateTimeFormat.resolvedOptions();
if (options.locale !== defaultLocale) {
$ERROR("Locale " + options.locale + " is affected by key " +
key + "; value " + value + ".");
}
if (JSON.stringify(options) !== defaultOptionsJSON) {
$ERROR("Resolved options " + JSON.stringify(options) + " are affected by key " +
key + "; value " + value + ".");
}
if (defaultFormatted !== dateTimeFormat.format(input)) {
$ERROR("Formatted value " + dateTimeFormat.format(input) + " is affected by key " +
key + "; value " + value + ".");
}
});
});
});

View File

@ -0,0 +1,52 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/**
* @description Tests that Intl.DateTimeFormat provides the required date-time
* format component subsets.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var locales = ["de-DE", "en-US", "hi-IN", "id-ID", "ja-JP", "th-TH", "zh-Hans-CN", "zh-Hant-TW", "zxx"];
var subsets = [
{weekday: "long", year: "numeric", month: "numeric", day: "numeric",
hour: "numeric", minute: "numeric", second: "numeric"},
{weekday: "long", year: "numeric", month: "numeric", day: "numeric"},
{year: "numeric", month: "numeric", day: "numeric"},
{year: "numeric", month: "numeric"},
{month: "numeric", day: "numeric"},
{hour: "numeric", minute: "numeric", second: "numeric"},
{hour: "numeric", minute: "numeric"}
];
locales.forEach(function (locale) {
subsets.forEach(function (subset) {
var format = new Intl.DateTimeFormat([locale], subset);
var actual = format.resolvedOptions();
getDateTimeComponents().forEach(function (component) {
if (actual.hasOwnProperty(component)) {
if (!subset.hasOwnProperty(component)) {
$ERROR("Unrequested component " + component +
" added to requested subset " + JSON.stringify(subset) +
"; locale " + locale + ".");
}
try {
testValidDateTimeComponentValue(component, actual[component]);
} catch (e) {
e.message += " (Testing locale " + locale + "; subset " +
JSON.stringify(subset) + ")";
throw e;
}
} else {
if (subset.hasOwnProperty(component)) {
$ERROR("Missing component " + component +
" from requested subset " + JSON.stringify(subset) +
"; locale " + locale + ".");
}
}
});
});
});

View File

@ -0,0 +1,34 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that format function is bound to its Intl.DateTimeFormat.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var dates = [new Date(), new Date(0), new Date(Date.parse("1989-11-09T17:57:00Z"))];
var locales = [undefined, ["de"], ["th-u-ca-gregory-nu-thai"], ["en"], ["ja-u-ca-japanese"], ["ar-u-ca-islamicc-nu-arab"]];
var options = [
undefined,
{hour12: false},
{month: "long", day: "numeric", hour: "2-digit", minute: "2-digit"}
];
locales.forEach(function (locales) {
options.forEach(function (options) {
var formatObj = new Intl.DateTimeFormat(locales, options);
var formatFunc = formatObj.format;
dates.forEach(function (date) {
var referenceFormatted = formatObj.format(date);
var formatted = formatFunc(date);
if (referenceFormatted !== formatted) {
$ERROR("format function produces different result than format method for locales " +
locales + "; options: " + (options ? JSON.stringify(options) : options) +
" : " + formatted + " vs. " + referenceFormatted + ".");
}
});
});
});

View File

@ -0,0 +1,26 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that format handles non-finite values correctly.
* @author Norbert Lindenberg
*/
var invalidValues = [NaN, Infinity, -Infinity];
var format = new Intl.DateTimeFormat();
invalidValues.forEach(function (value) {
var error;
try {
var result = format.format(value);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Invalid value " + value + " was not rejected.");
} else if (error.name !== "RangeError") {
$ERROR("Invalid value " + value + " was rejected with wrong error " + error.name + ".");
}
});

View File

@ -0,0 +1,16 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that the behavior of a Record is not affected by adversarial
* changes to Object.prototype.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
taintProperties(["weekday", "era", "year", "month", "day", "hour", "minute", "second", "inDST"]);
var format = new Intl.DateTimeFormat();
var time = format.format();

View File

@ -0,0 +1,52 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/**
* @description Tests that the object returned by Intl.DateTimeFormat.prototype.resolvedOptions
* has the right properties.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var actual = new Intl.DateTimeFormat().resolvedOptions();
var actual2 = new Intl.DateTimeFormat().resolvedOptions();
if (actual2 === actual) {
$ERROR("resolvedOptions returned the same object twice.");
}
// source: CLDR file common/bcp47/calendar.xml; version CLDR 21.
var calendars = [
"buddhist",
"chinese",
"coptic",
"ethioaa",
"ethiopic",
"gregory",
"hebrew",
"indian",
"islamic",
"islamicc",
"iso8601",
"japanese",
"persian",
"roc"
];
// this assumes the default values where the specification provides them
mustHaveProperty(actual, "locale", isCanonicalizedStructurallyValidLanguageTag);
mustHaveProperty(actual, "calendar", calendars);
mustHaveProperty(actual, "numberingSystem", isValidNumberingSystem);
mustHaveProperty(actual, "timeZone", [undefined]);
mustHaveProperty(actual, "hour12", [true, false]);
mustNotHaveProperty(actual, "weekday");
mustNotHaveProperty(actual, "era");
mustHaveProperty(actual, "year", ["2-digit", "numeric"]);
mustHaveProperty(actual, "month", ["2-digit", "numeric", "narrow", "short", "long"]);
mustHaveProperty(actual, "day", ["2-digit", "numeric"]);
mustNotHaveProperty(actual, "hour");
mustNotHaveProperty(actual, "minute");
mustNotHaveProperty(actual, "second");
mustNotHaveProperty(actual, "timeZoneName");

View File

@ -0,0 +1,33 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Intl.DateTimeFormat.prototype functions throw a
* TypeError if called on a non-object value or an object that hasn't been
* initialized as a DateTimeFormat.
* @author Norbert Lindenberg
*/
var functions = {
"format getter": Object.getOwnPropertyDescriptor(Intl.DateTimeFormat.prototype, "format").get,
resolvedOptions: Intl.DateTimeFormat.prototype.resolvedOptions
};
var invalidTargets = [undefined, null, true, 0, "DateTimeFormat", [], {}];
Object.getOwnPropertyNames(functions).forEach(function (functionName) {
var f = functions[functionName];
invalidTargets.forEach(function (target) {
var error;
try {
f.call(target);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Calling " + functionName + " on " + target + " was not rejected.");
} else if (error.name !== "TypeError") {
$ERROR("Calling " + functionName + " on " + target + " was rejected with wrong error " + error.name + ".");
}
});
});

View File

@ -0,0 +1,15 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Intl.DateTimeFormat instances have the specified properties.
* @author Norbert Lindenberg
*/
var obj = new Intl.DateTimeFormat();
var toStringValue = Object.prototype.toString.call(obj);
if (toStringValue !== "[object Object]") {
$ERROR("Intl.DateTimeFormat instance produces wrong [[Class]] - toString returns " + toStringValue + ".");
}

View File

@ -0,0 +1,24 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that localeCompare rejects values that can't be coerced to an object.
* @author Norbert Lindenberg
*/
var invalidValues = [undefined, null];
invalidValues.forEach(function (value) {
var error;
try {
var result = String.prototype.localeCompare.call(value, "");
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("String.prototype.localeCompare did not reject this = " + value + ".");
} else if (error.name !== "TypeError") {
$ERROR("String.prototype.localeCompare rejected this = " + value + " with wrong error " + error.name + ".");
}
});

View File

@ -0,0 +1,26 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that localeCompare coerces this to a string.
* @author Norbert Lindenberg
*/
var thisValues = [true, 5, "hello", {toString: function () { return "good bye"; }}];
var thatValues = ["true", "5", "hello", "good bye"];
var i;
for (i = 0; i < thisValues.length; i++) {
var j;
for (j = 0; j < thatValues.length; j++) {
var result = String.prototype.localeCompare.call(thisValues[i], thatValues[j]);
if ((result === 0) !== (i === j)) {
if (result === 0) {
$ERROR("localeCompare treats " + thisValues[i] + " and " + thatValues[j] + " as equal.");
} else {
$ERROR("localeCompare treats " + thisValues[i] + " and " + thatValues[j] + " as different.");
}
}
}
}

View File

@ -0,0 +1,26 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that localeCompare coerces that to a string.
* @author Norbert Lindenberg
*/
var thisValues = ["true", "5", "hello", "good bye"];
var thatValues = [true, 5, "hello", {toString: function () { return "good bye"; }}];
var i;
for (i = 0; i < thisValues.length; i++) {
var j;
for (j = 0; j < thatValues.length; j++) {
var result = String.prototype.localeCompare.call(thisValues[i], thatValues[j]);
if ((result === 0) !== (i === j)) {
if (result === 0) {
$ERROR("localeCompare treats " + thisValues[i] + " and " + thatValues[j] + " as equal.");
} else {
$ERROR("localeCompare treats " + thisValues[i] + " and " + thatValues[j] + " as different.");
}
}
}
}

View File

@ -0,0 +1,65 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that String.prototype.localeCompare throws the same exceptions as Intl.Collator.
* @author Norbert Lindenberg
*/
var locales = [null, [NaN], ["i"], ["de_DE"]];
var options = [
{localeMatcher: null},
{usage: "invalid"},
{sensitivity: "invalid"}
];
locales.forEach(function (locales) {
var referenceError, error;
try {
var collator = new Intl.Collator(locales);
} catch (e) {
referenceError = e;
}
if (referenceError === undefined) {
$ERROR("Internal error: Expected exception was not thrown by Intl.Collator for locales " + locales + ".");
}
try {
var result = "".localeCompare("", locales);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("String.prototype.localeCompare didn't throw exception for locales " + locales + ".");
} else if (error.name !== referenceError.name) {
$ERROR("String.prototype.localeCompare threw exception " + error.name +
" for locales " + locales + "; expected " + referenceError.name + ".");
}
});
options.forEach(function (options) {
var referenceError, error;
try {
var collator = new Intl.Collator([], options);
} catch (e) {
referenceError = e;
}
if (referenceError === undefined) {
$ERROR("Internal error: Expected exception was not thrown by Intl.Collator for options " +
JSON.stringify(options) + ".");
}
try {
var result = "".localeCompare("", [], options);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("String.prototype.localeCompare didn't throw exception for options " +
JSON.stringify(options) + ".");
} else if (error.name !== referenceError.name) {
$ERROR("String.prototype.localeCompare threw exception " + error.name +
" for options " + JSON.stringify(options) + "; expected " + referenceError.name + ".");
}
});

View File

@ -0,0 +1,33 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that localeCompare produces the same results as Intl.Collator.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var strings = ["d", "O", "od", "oe", "of", "ö", "o\u0308", "X", "y", "Z", "Z.", "\uD842\uDFB7野家", "吉野家", "!A", "A", "b", "C"];
var locales = [undefined, ["de"], ["de-u-co-phonebk"], ["en"], ["ja"], ["sv"]];
var options = [
undefined,
{usage: "search"},
{sensitivity: "base", ignorePunctuation: true}
];
locales.forEach(function (locales) {
options.forEach(function (options) {
var referenceCollator = new Intl.Collator(locales, options);
var referenceSorted = strings.slice().sort(referenceCollator.compare);
strings.sort(function (a, b) { return a.localeCompare(b, locales, options); });
try {
testArraysAreSame(referenceSorted, strings);
} catch (e) {
e.message += " (Testing with locales " + locales + "; options " + JSON.stringify(options) + ".)";
throw e;
}
});
});

View File

@ -0,0 +1,37 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that toLocaleString handles "this Number value" correctly.
* @author Norbert Lindenberg
*/
var invalidValues = [undefined, null, "5", false, {valueOf: function () { return 5; }}];
var validValues = [5, NaN, -1234567.89, -Infinity];
invalidValues.forEach(function (value) {
var error;
try {
var result = Number.prototype.toLocaleString.call(value);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Number.prototype.toLocaleString did not reject this = " + value + ".");
} else if (error.name !== "TypeError") {
$ERROR("Number.prototype.toLocaleString rejected this = " + value + " with wrong error " + error.name + ".");
}
});
// for valid values, just check that a Number value and the corresponding
// Number object get the same result.
validValues.forEach(function (value) {
var Constructor = Number; // to keep jshint happy
var valueResult = Number.prototype.toLocaleString.call(value);
var objectResult = Number.prototype.toLocaleString.call(new Constructor(value));
if (valueResult !== objectResult) {
$ERROR("Number.prototype.toLocaleString produces different results for Number value " +
value + " and corresponding Number object: " + valueResult + " vs. " + objectResult + ".");
}
});

View File

@ -0,0 +1,67 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Number.prototype.toLocaleString throws the same exceptions as Intl.NumberFormat.
* @author Norbert Lindenberg
*/
var locales = [null, [NaN], ["i"], ["de_DE"]];
var options = [
{localeMatcher: null},
{style: "invalid"},
{style: "currency"},
{style: "currency", currency: "ßP"},
{maximumSignificantDigits: -Infinity}
];
locales.forEach(function (locales) {
var referenceError, error;
try {
var format = new Intl.NumberFormat(locales);
} catch (e) {
referenceError = e;
}
if (referenceError === undefined) {
$ERROR("Internal error: Expected exception was not thrown by Intl.NumberFormat for locales " + locales + ".");
}
try {
var result = (0).toLocaleString(locales);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Number.prototype.toLocaleString didn't throw exception for locales " + locales + ".");
} else if (error.name !== referenceError.name) {
$ERROR("Number.prototype.toLocaleString threw exception " + error.name +
" for locales " + locales + "; expected " + referenceError.name + ".");
}
});
options.forEach(function (options) {
var referenceError, error;
try {
var format = new Intl.NumberFormat([], options);
} catch (e) {
referenceError = e;
}
if (referenceError === undefined) {
$ERROR("Internal error: Expected exception was not thrown by Intl.NumberFormat for options " +
JSON.stringify(options) + ".");
}
try {
var result = (0).toLocaleString([], options);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Number.prototype.toLocaleString didn't throw exception for options " +
JSON.stringify(options) + ".");
} else if (error.name !== referenceError.name) {
$ERROR("Number.prototype.toLocaleString threw exception " + error.name +
" for options " + JSON.stringify(options) + "; expected " + referenceError.name + ".");
}
});

View File

@ -0,0 +1,41 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Number.prototype.toLocaleString produces the same results as Intl.NumberFormat.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var numbers = [0, -0, 1, -1, 5.5, 123, -123, -123.45, 123.44501, 0.001234,
-0.00000000123, 0.00000000000000000000000000000123, 1.2, 0.0000000012344501,
123445.01, 12344501000000000000000000000000000, -12344501000000000000000000000000000,
Infinity, -Infinity, NaN];
var locales = [undefined, ["de"], ["th-u-nu-thai"], ["en"], ["ja-u-nu-jpanfin"], ["ar-u-nu-arab"]];
var options = [
undefined,
{style: "percent"},
{style: "currency", currency: "EUR", currencyDisplay: "symbol"},
{style: "currency", currency: "IQD", currencyDisplay: "symbol"},
{style: "currency", currency: "KMF", currencyDisplay: "symbol"},
{style: "currency", currency: "CLF", currencyDisplay: "symbol"},
{useGrouping: false, minimumIntegerDigits: 3, minimumFractionDigits: 1, maximumFractionDigits: 3}
];
locales.forEach(function (locales) {
options.forEach(function (options) {
var referenceNumberFormat = new Intl.NumberFormat(locales, options);
var referenceFormatted = numbers.map(referenceNumberFormat.format);
var formatted = numbers.map(function (a) { return a.toLocaleString(locales, options); });
try {
testArraysAreSame(referenceFormatted, formatted);
} catch (e) {
e.message += " (Testing with locales " + locales + "; options " +
(options ? JSON.stringify(options) : options) + ".)";
throw e;
}
});
});

View File

@ -0,0 +1,32 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Date.prototype.toLocaleString & Co. handle "this time value" correctly.
* @author Norbert Lindenberg
*/
var functions = {
toLocaleString: Date.prototype.toLocaleString,
toLocaleDateString: Date.prototype.toLocaleDateString,
toLocaleTimeString: Date.prototype.toLocaleTimeString
};
var invalidValues = [undefined, null, 5, "5", false, {valueOf: function () { return 5; }}];
Object.getOwnPropertyNames(functions).forEach(function (p) {
var f = functions[p];
invalidValues.forEach(function (value) {
var error;
try {
var result = f.call(value);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Date.prototype." + p + " did not reject this = " + value + ".");
} else if (error.name !== "TypeError") {
$ERROR("Date.prototype." + p + " rejected this = " + value + " with wrong error " + error.name + ".");
}
});
});

View File

@ -0,0 +1,26 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Date.prototype.toLocaleString & Co. handle non-finite values correctly.
* @author Norbert Lindenberg
*/
var functions = {
toLocaleString: Date.prototype.toLocaleString,
toLocaleDateString: Date.prototype.toLocaleDateString,
toLocaleTimeString: Date.prototype.toLocaleTimeString
};
var invalidValues = [NaN, Infinity, -Infinity];
Object.getOwnPropertyNames(functions).forEach(function (p) {
var f = functions[p];
invalidValues.forEach(function (value) {
var result = f.call(new Date(value));
if (result !== "Invalid Date") {
$ERROR("Date.prototype." + p + " did not return \"Invalid Date\" for " +
value + " got " + result + " instead.");
}
});
});

View File

@ -0,0 +1,74 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Date.prototype.toLocaleString & Co. throws the same exceptions as Intl.DateTimeFormat.
* @author Norbert Lindenberg
*/
var functions = {
toLocaleString: Date.prototype.toLocaleString,
toLocaleDateString: Date.prototype.toLocaleDateString,
toLocaleTimeString: Date.prototype.toLocaleTimeString
};
var locales = [null, [NaN], ["i"], ["de_DE"]];
var options = [
{localeMatcher: null},
{timeZone: "invalid"},
{hour: "long"},
{formatMatcher: "invalid"}
];
Object.getOwnPropertyNames(functions).forEach(function (p) {
var f = functions[p];
locales.forEach(function (locales) {
var referenceError, error;
try {
var format = new Intl.DateTimeFormat(locales);
} catch (e) {
referenceError = e;
}
if (referenceError === undefined) {
$ERROR("Internal error: Expected exception was not thrown by Intl.DateTimeFormat for locales " + locales + ".");
}
try {
var result = f.call(new Date(), locales);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Date.prototype." + p + " didn't throw exception for locales " + locales + ".");
} else if (error.name !== referenceError.name) {
$ERROR("Date.prototype." + p + " threw exception " + error.name +
" for locales " + locales + "; expected " + referenceError.name + ".");
}
});
options.forEach(function (options) {
var referenceError, error;
try {
var format = new Intl.DateTimeFormat([], options);
} catch (e) {
referenceError = e;
}
if (referenceError === undefined) {
$ERROR("Internal error: Expected exception was not thrown by Intl.DateTimeFormat for options " +
JSON.stringify(options) + ".");
}
try {
var result = f.call(new Date(), [], options);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Date.prototype." + p + " didn't throw exception for options " +
JSON.stringify(options) + ".");
} else if (error.name !== referenceError.name) {
$ERROR("Date.prototype." + p + " threw exception " + error.name +
" for options " + JSON.stringify(options) + "; expected " + referenceError.name + ".");
}
});
});

View File

@ -0,0 +1,58 @@
// Copyright 2012 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/**
* @description Tests that Date.prototype.toLocaleString & Co. produces the same results as Intl.DateTimeFormat.
* @author Norbert Lindenberg
*/
$INCLUDE("testIntl.js");
var functions = {
toLocaleString: [Date.prototype.toLocaleString,
{year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric"}],
toLocaleDateString: [Date.prototype.toLocaleDateString,
{year: "numeric", month: "numeric", day: "numeric"}],
toLocaleTimeString: [Date.prototype.toLocaleTimeString,
{hour: "numeric", minute: "numeric", second: "numeric"}]
};
var dates = [new Date(), new Date(0), new Date(Date.parse("1989-11-09T17:57:00Z"))];
var locales = [undefined, ["de"], ["th-u-ca-gregory-nu-thai"], ["en"], ["ja-u-ca-japanese"], ["ar-u-ca-islamicc-nu-arab"]];
var options = [
undefined,
{hour12: false},
{month: "long", day: "numeric", hour: "2-digit", minute: "2-digit"}
];
Object.getOwnPropertyNames(functions).forEach(function (p) {
var f = functions[p][0];
var defaults = functions[p][1];
locales.forEach(function (locales) {
options.forEach(function (options) {
var constructorOptions = options;
if (options === undefined) {
constructorOptions = defaults;
} else if (options.day === undefined) {
// for simplicity, our options above have either both date and time or neither
constructorOptions = Object.create(defaults);
for (var prop in options) {
if (options.hasOwnProperty(prop)) {
constructorOptions[prop] = options[prop];
}
}
}
var referenceDateTimeFormat = new Intl.DateTimeFormat(locales, constructorOptions);
var referenceFormatted = dates.map(referenceDateTimeFormat.format);
var formatted = dates.map(function (a) { return f.call(a, locales, options); });
try {
testArraysAreSame(referenceFormatted, formatted);
} catch (e) {
e.message += " (Testing with locales " + locales + "; options " +
(options ? JSON.stringify(options) : options) + ".)";
throw e;
}
});
});
});

View File

@ -656,3 +656,474 @@ function isCanonicalizedStructurallyValidLanguageTag(locale) {
canonicalizeLanguageTag(locale) === locale;
}
/**
* Tests whether the named options property is correctly handled by the given constructor.
* @param {object} Constructor the constructor to test.
* @param {string} property the name of the options property to test.
* @param {string} type the type that values of the property are expected to have
* @param {Array} [values] an array of allowed values for the property. Not needed for boolean.
* @param {any} fallback the fallback value that the property assumes if not provided.
* @param {object} testOptions additional options:
* @param {boolean} isOptional whether support for this property is optional for implementations.
* @param {boolean} noReturn whether the resulting value of the property is not returned.
* @param {boolean} isILD whether the resulting value of the property is implementation and locale dependent.
* @param {object} extra additional option to pass along, properties are value -> {option: value}.
* @return {boolean} whether the test succeeded.
*/
function testOption(Constructor, property, type, values, fallback, testOptions) {
var isOptional = testOptions !== undefined && testOptions.isOptional === true;
var noReturn = testOptions !== undefined && testOptions.noReturn === true;
var isILD = testOptions !== undefined && testOptions.isILD === true;
function addExtraOptions(options, value, testOptions) {
if (testOptions !== undefined && testOptions.extra !== undefined) {
var extra;
if (value !== undefined && testOptions.extra[value] !== undefined) {
extra = testOptions.extra[value];
} else if (testOptions.extra.any !== undefined) {
extra = testOptions.extra.any;
}
if (extra !== undefined) {
Object.getOwnPropertyNames(extra).forEach(function (prop) {
options[prop] = extra[prop];
});
}
}
}
var testValues, options, obj, expected, actual, error;
// test that the specified values are accepted. Also add values that convert to specified values.
if (type === "boolean") {
if (values === undefined) {
values = [true, false];
}
testValues = values.slice(0);
testValues.push(888);
testValues.push(0);
} else if (type === "string") {
testValues = values.slice(0);
testValues.push({toString: function () { return values[0]; }});
}
testValues.forEach(function (value) {
options = {};
options[property] = value;
addExtraOptions(options, value, testOptions);
obj = new Constructor(undefined, options);
if (noReturn) {
if (obj.resolvedOptions().hasOwnProperty(property)) {
$ERROR("Option property " + property + " is returned, but shouldn't be.");
}
} else {
actual = obj.resolvedOptions()[property];
if (isILD) {
if (actual !== undefined && values.indexOf(actual) === -1) {
$ERROR("Invalid value " + actual + " returned for property " + property + ".");
}
} else {
if (type === "boolean") {
expected = Boolean(value);
} else if (type === "string") {
expected = String(value);
}
if (actual !== expected && !(isOptional && actual === undefined)) {
$ERROR("Option value " + value + " for property " + property +
" was not accepted; got " + actual + " instead.");
}
}
}
});
// test that invalid values are rejected
if (type === "string") {
var invalidValues = ["invalidValue", -1, null];
// assume that we won't have values in caseless scripts
if (values[0].toUpperCase() !== values[0]) {
invalidValues.push(values[0].toUpperCase());
} else {
invalidValues.push(values[0].toLowerCase());
}
invalidValues.forEach(function (value) {
options = {};
options[property] = value;
addExtraOptions(options, value, testOptions);
error = undefined;
try {
obj = new Constructor(undefined, options);
} catch (e) {
error = e;
}
if (error === undefined) {
$ERROR("Invalid option value " + value + " for property " + property + " was not rejected.");
} else if (error.name !== "RangeError") {
$ERROR("Invalid option value " + value + " for property " + property + " was rejected with wrong error " + error.name + ".");
}
});
}
// test that fallback value or another valid value is used if no options value is provided
if (!noReturn) {
options = {};
addExtraOptions(options, undefined, testOptions);
obj = new Constructor(undefined, options);
actual = obj.resolvedOptions()[property];
if (!(isOptional && actual === undefined)) {
if (fallback !== undefined) {
if (actual !== fallback) {
$ERROR("Option fallback value " + fallback + " for property " + property +
" was not used; got " + actual + " instead.");
}
} else {
if (values.indexOf(actual) === -1 && !(isILD && actual === undefined)) {
$ERROR("Invalid value " + actual + " returned for property " + property + ".");
}
}
}
}
return true;
}
/**
* Tests whether the named property of the given object has a valid value
* and the default attributes of the properties of an object literal.
* @param {Object} obj the object to be tested.
* @param {string} property the name of the property
* @param {Function|Array} valid either a function that tests value for validity and returns a boolean,
* an array of valid values.
* @exception if the property has an invalid value.
*/
function testProperty(obj, property, valid) {
var desc = Object.getOwnPropertyDescriptor(obj, property);
if (!desc.writable) {
$ERROR("Property " + property + " must be writable.");
}
if (!desc.enumerable) {
$ERROR("Property " + property + " must be enumerable.");
}
if (!desc.configurable) {
$ERROR("Property " + property + " must be configurable.");
}
var value = desc.value;
var isValid = (typeof valid === "function") ? valid(value) : (valid.indexOf(value) !== -1);
if (!isValid) {
$ERROR("Property value " + value + " is not allowed for property " + property + ".");
}
}
/**
* Tests whether the named property of the given object, if present at all, has a valid value
* and the default attributes of the properties of an object literal.
* @param {Object} obj the object to be tested.
* @param {string} property the name of the property
* @param {Function|Array} valid either a function that tests value for validity and returns a boolean,
* an array of valid values.
* @exception if the property is present and has an invalid value.
*/
function mayHaveProperty(obj, property, valid) {
if (obj.hasOwnProperty(property)) {
testProperty(obj, property, valid);
}
}
/**
* Tests whether the given object has the named property with a valid value
* and the default attributes of the properties of an object literal.
* @param {Object} obj the object to be tested.
* @param {string} property the name of the property
* @param {Function|Array} valid either a function that tests value for validity and returns a boolean,
* an array of valid values.
* @exception if the property is missing or has an invalid value.
*/
function mustHaveProperty(obj, property, valid) {
if (!obj.hasOwnProperty(property)) {
$ERROR("Object is missing property " + property + ".");
}
testProperty(obj, property, valid);
}
/**
* Tests whether the given object does not have the named property.
* @param {Object} obj the object to be tested.
* @param {string} property the name of the property
* @exception if the property is present.
*/
function mustNotHaveProperty(obj, property) {
if (obj.hasOwnProperty(property)) {
$ERROR("Object has property it mustn't have: " + property + ".");
}
}
/**
* Tests whether name is a valid BCP 47 numbering system name
* and not excluded from use in the ECMAScript Internationalization API.
* @param {string} name the name to be tested.
* @return {boolean} whether name is a valid BCP 47 numbering system name and
* allowed for use in the ECMAScript Internationalization API.
*/
function isValidNumberingSystem(name) {
// source: CLDR file common/bcp47/number.xml; version CLDR 21.
var numberingSystems = [
"arab",
"arabext",
"armn",
"armnlow",
"bali",
"beng",
"brah",
"cakm",
"cham",
"deva",
"ethi",
"finance",
"fullwide",
"geor",
"grek",
"greklow",
"gujr",
"guru",
"hanidec",
"hans",
"hansfin",
"hant",
"hantfin",
"hebr",
"java",
"jpan",
"jpanfin",
"kali",
"khmr",
"knda",
"osma",
"lana",
"lanatham",
"laoo",
"latn",
"lepc",
"limb",
"mlym",
"mong",
"mtei",
"mymr",
"mymrshan",
"native",
"nkoo",
"olck",
"orya",
"roman",
"romanlow",
"saur",
"shrd",
"sora",
"sund",
"talu",
"takr",
"taml",
"tamldec",
"telu",
"thai",
"tibt",
"traditio",
"vaii"
];
var excluded = [
"finance",
"native",
"traditio"
];
return numberingSystems.indexOf(name) !== -1 && excluded.indexOf(name) === -1;
}
/**
* Provides the digits of numbering systems with simple digit mappings,
* as specified in 11.3.2.
*/
var numberingSystemDigits = {
arab: "٠١٢٣٤٥٦٧٨٩",
arabext: "۰۱۲۳۴۵۶۷۸۹",
beng: "০১২৩৪৫৬৭৮৯",
deva: "०१२३४५६७८९",
fullwide: "",
gujr: "૦૧૨૩૪૫૬૭૮૯",
guru: "੦੧੨੩੪੫੬੭੮੯",
hanidec: "〇一二三四五六七八九",
khmr: "០១២៣៤៥៦៧៨៩",
knda: "೦೧೨೩೪೫೬೭೮೯",
laoo: "໐໑໒໓໔໕໖໗໘໙",
latn: "0123456789",
mlym: "൦൧൨൩൪൫൬൭൮൯",
mong: "᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙",
mymr: "၀၁၂၃၄၅၆၇၈၉",
orya: "୦୧୨୩୪୫୬୭୮୯",
tamldec: "௦௧௨௩௪௫௬௭௮௯",
telu: "౦౧౨౩౪౫౬౭౮౯",
thai: "๐๑๒๓๔๕๖๗๘๙",
tibt: "༠༡༢༣༤༥༦༧༨༩"
};
/**
* Tests that number formatting is handled correctly. The function checks that the
* digit sequences in formatted output are as specified, converted to the
* selected numbering system, and embedded in consistent localized patterns.
* @param {Array} locales the locales to be tested.
* @param {Array} numberingSystems the numbering systems to be tested.
* @param {Object} options the options to pass to Intl.NumberFormat. Options
* must include {useGrouping: false}, and must cause 1.1 to be formatted
* pre- and post-decimal digits.
* @param {Object} testData maps input data (in ES5 9.3.1 format) to expected output strings
* in unlocalized format with Western digits.
*/
function testNumberFormat(locales, numberingSystems, options, testData) {
locales.forEach(function (locale) {
numberingSystems.forEach(function (numbering) {
var digits = numberingSystemDigits[numbering];
var format = new Intl.NumberFormat([locale + "-u-nu-" + numbering], options);
function getPatternParts(positive) {
var n = positive ? 1.1 : -1.1;
var formatted = format.format(n);
var oneoneRE = "([^" + digits + "]*)[" + digits + "]+([^" + digits + "]+)[" + digits + "]+([^" + digits + "]*)";
var match = formatted.match(new RegExp(oneoneRE));
if (match === null) {
$ERROR("Unexpected formatted " + n + " for " +
format.resolvedOptions().locale + " and options " +
JSON.stringify(options) + ": " + formatted);
}
return match;
}
function toNumbering(raw) {
return raw.replace(/[0-9]/g, function (digit) {
return digits[digit.charCodeAt(0) - "0".charCodeAt(0)];
});
}
function buildExpected(raw, patternParts) {
var period = raw.indexOf(".");
if (period === -1) {
return patternParts[1] + toNumbering(raw) + patternParts[3];
} else {
return patternParts[1] +
toNumbering(raw.substring(0, period)) +
patternParts[2] +
toNumbering(raw.substring(period + 1)) +
patternParts[3];
}
}
if (format.resolvedOptions().numberingSystem === numbering) {
// figure out prefixes, infixes, suffixes for positive and negative values
var posPatternParts = getPatternParts(true);
var negPatternParts = getPatternParts(false);
Object.getOwnPropertyNames(testData).forEach(function (input) {
var rawExpected = testData[input];
var patternParts;
if (rawExpected[0] === "-") {
patternParts = negPatternParts;
rawExpected = rawExpected.substring(1);
} else {
patternParts = posPatternParts;
}
var expected = buildExpected(rawExpected, patternParts);
var actual = format.format(input);
if (actual !== expected) {
$ERROR("Formatted value for " + input + ", " +
format.resolvedOptions().locale + " and options " +
JSON.stringify(options) + " is " + actual + "; expected " + expected + ".");
}
});
}
});
});
}
/**
* Return the components of date-time formats.
* @return {Array} an array with all date-time components.
*/
function getDateTimeComponents() {
return ["weekday", "era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"];
}
/**
* Return the valid values for the given date-time component, as specified
* by the table in section 12.1.1.
* @param {string} component a date-time component.
* @return {Array} an array with the valid values for the component.
*/
function getDateTimeComponentValues(component) {
var components = {
weekday: ["narrow", "short", "long"],
era: ["narrow", "short", "long"],
year: ["2-digit", "numeric"],
month: ["2-digit", "numeric", "narrow", "short", "long"],
day: ["2-digit", "numeric"],
hour: ["2-digit", "numeric"],
minute: ["2-digit", "numeric"],
second: ["2-digit", "numeric"],
timeZoneName: ["short", "long"]
};
var result = components[component];
if (result === undefined) {
$ERROR("Internal error: No values defined for date-time component " + component + ".");
}
return result;
}
/**
* Tests that the given value is valid for the given date-time component.
* @param {string} component a date-time component.
* @param {string} value the value to be tested.
* @return {boolean} true if the test succeeds.
* @exception if the test fails.
*/
function testValidDateTimeComponentValue(component, value) {
if (getDateTimeComponentValues(component).indexOf(value) === -1) {
$ERROR("Invalid value " + value + " for date-time component " + component + ".");
}
return true;
}
/**
* Verifies that the actual array matches the expected one in length, elements,
* and element order.
* @param {Array} expected the expected array.
* @param {Array} actual the actual array.
* @return {boolean} true if the test succeeds.
* @exception if the test fails.
*/
function testArraysAreSame(expected, actual) {
for (i = 0; i < Math.max(actual.length, expected.length); i++) {
if (actual[i] !== expected[i]) {
$ERROR("Result array element at index " + i + " should be \"" +
expected[i] + "\" but is \"" + actual[i] + "\".");
}
}
return true;
}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"numTests":66,"testSuite":["json/intl402.json"]}
{"numTests":137,"testSuite":["json/intl402.json"]}