diff options
Diffstat (limited to 'website/harness/testIntl.js')
-rw-r--r-- | website/harness/testIntl.js | 1167 |
1 files changed, 0 insertions, 1167 deletions
diff --git a/website/harness/testIntl.js b/website/harness/testIntl.js deleted file mode 100644 index b009a2e9e..000000000 --- a/website/harness/testIntl.js +++ /dev/null @@ -1,1167 +0,0 @@ -// Copyright 2011-2012 Norbert Lindenberg. All rights reserved. -// Copyright 2012-2013 Mozilla Corporation. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/** - * This file contains shared functions for the tests in the conformance test - * suite for the ECMAScript Internationalization API. - * @author Norbert Lindenberg - */ - - -/** - * @description Calls the provided function for every service constructor in - * the Intl object, until f returns a falsy value. It returns the result of the - * last call to f, mapped to a boolean. - * @param {Function} f the function to call for each service constructor in - * the Intl object. - * @param {Function} Constructor the constructor object to test with. - * @result {Boolean} whether the test succeeded. - */ -function testWithIntlConstructors(f) { - var constructors = ["Collator", "NumberFormat", "DateTimeFormat"]; - return constructors.every(function (constructor) { - var Constructor = Intl[constructor]; - var result; - try { - result = f(Constructor); - } catch (e) { - e.message += " (Testing with " + constructor + ".)"; - throw e; - } - return result; - }); -} - - -/** - * Returns the name of the given constructor object, which must be one of - * Intl.Collator, Intl.NumberFormat, or Intl.DateTimeFormat. - * @param {object} Constructor a constructor - * @return {string} the name of the constructor - */ -function getConstructorName(Constructor) { - switch (Constructor) { - case Intl.Collator: - return "Collator"; - case Intl.NumberFormat: - return "NumberFormat"; - case Intl.DateTimeFormat: - return "DateTimeFormat"; - default: - $ERROR("test internal error: unknown Constructor"); - } -} - - -/** - * Taints a named data property of the given object by installing - * a setter that throws an exception. - * @param {object} obj the object whose data property to taint - * @param {string} property the property to taint - */ -function taintDataProperty(obj, property) { - Object.defineProperty(obj, property, { - set: function(value) { - $ERROR("Client code can adversely affect behavior: setter for " + property + "."); - }, - enumerable: false, - configurable: true - }); -} - - -/** - * Taints a named method of the given object by replacing it with a function - * that throws an exception. - * @param {object} obj the object whose method to taint - * @param {string} property the name of the method to taint - */ -function taintMethod(obj, property) { - Object.defineProperty(obj, property, { - value: function() { - $ERROR("Client code can adversely affect behavior: method " + property + "."); - }, - writable: true, - enumerable: false, - configurable: true - }); -} - - -/** - * Taints the given properties (and similarly named properties) by installing - * setters on Object.prototype that throw exceptions. - * @param {Array} properties an array of property names to taint - */ -function taintProperties(properties) { - properties.forEach(function (property) { - var adaptedProperties = [property, "__" + property, "_" + property, property + "_", property + "__"]; - adaptedProperties.forEach(function (property) { - taintDataProperty(Object.prototype, property); - }); - }); -} - - -/** - * Taints the Array object by creating a setter for the property "0" and - * replacing some key methods with functions that throw exceptions. - */ -function taintArray() { - taintDataProperty(Array.prototype, "0"); - taintMethod(Array.prototype, "indexOf"); - taintMethod(Array.prototype, "join"); - taintMethod(Array.prototype, "push"); - taintMethod(Array.prototype, "slice"); - taintMethod(Array.prototype, "sort"); -} - - -// auxiliary data for getLocaleSupportInfo -var languages = ["zh", "es", "en", "hi", "ur", "ar", "ja", "pa"]; -var scripts = ["Latn", "Hans", "Deva", "Arab", "Jpan", "Hant"]; -var countries = ["CN", "IN", "US", "PK", "JP", "TW", "HK", "SG"]; -var localeSupportInfo = {}; - - -/** - * Gets locale support info for the given constructor object, which must be one - * of Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat. - * @param {object} Constructor the constructor for which to get locale support info - * @return {object} locale support info with the following properties: - * supported: array of fully supported language tags - * byFallback: array of language tags that are supported through fallbacks - * unsupported: array of unsupported language tags - */ -function getLocaleSupportInfo(Constructor) { - var constructorName = getConstructorName(Constructor); - if (localeSupportInfo[constructorName] !== undefined) { - return localeSupportInfo[constructorName]; - } - - var allTags = []; - var i, j, k; - var language, script, country; - for (i = 0; i < languages.length; i++) { - language = languages[i]; - allTags.push(language); - for (j = 0; j < scripts.length; j++) { - script = scripts[j]; - allTags.push(language + "-" + script); - for (k = 0; k < countries.length; k++) { - country = countries[k]; - allTags.push(language + "-" + script + "-" + country); - } - } - for (k = 0; k < countries.length; k++) { - country = countries[k]; - allTags.push(language + "-" + country); - } - } - - var supported = []; - var byFallback = []; - var unsupported = []; - for (i = 0; i < allTags.length; i++) { - var request = allTags[i]; - var result = new Constructor([request], {localeMatcher: "lookup"}).resolvedOptions().locale; - if (request === result) { - supported.push(request); - } else if (request.indexOf(result) === 0) { - byFallback.push(request); - } else { - unsupported.push(request); - } - } - - localeSupportInfo[constructorName] = { - supported: supported, - byFallback: byFallback, - unsupported: unsupported - }; - - return localeSupportInfo[constructorName]; -} - - -/** - * @description Tests whether locale is a String value representing a - * structurally valid and canonicalized BCP 47 language tag, as defined in - * sections 6.2.2 and 6.2.3 of the ECMAScript Internationalization API - * Specification. - * @param {String} locale the string to be tested. - * @result {Boolean} whether the test succeeded. - */ -function isCanonicalizedStructurallyValidLanguageTag(locale) { - - /** - * Regular expression defining BCP 47 language tags. - * - * Spec: RFC 5646 section 2.1. - */ - var alpha = "[a-zA-Z]", - digit = "[0-9]", - alphanum = "(" + alpha + "|" + digit + ")", - regular = "(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)", - irregular = "(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)", - grandfathered = "(" + irregular + "|" + regular + ")", - privateuse = "(x(-[a-z0-9]{1,8})+)", - singleton = "(" + digit + "|[A-WY-Za-wy-z])", - extension = "(" + singleton + "(-" + alphanum + "{2,8})+)", - variant = "(" + alphanum + "{5,8}|(" + digit + alphanum + "{3}))", - region = "(" + alpha + "{2}|" + digit + "{3})", - script = "(" + alpha + "{4})", - extlang = "(" + alpha + "{3}(-" + alpha + "{3}){0,2})", - language = "(" + alpha + "{2,3}(-" + extlang + ")?|" + alpha + "{4}|" + alpha + "{5,8})", - langtag = language + "(-" + script + ")?(-" + region + ")?(-" + variant + ")*(-" + extension + ")*(-" + privateuse + ")?", - languageTag = "^(" + langtag + "|" + privateuse + "|" + grandfathered + ")$", - languageTagRE = new RegExp(languageTag, "i"); - var duplicateSingleton = "-" + singleton + "-(.*-)?\\1(?!" + alphanum + ")", - duplicateSingletonRE = new RegExp(duplicateSingleton, "i"), - duplicateVariant = "(" + alphanum + "{2,8}-)+" + variant + "-(" + alphanum + "{2,8}-)*\\3(?!" + alphanum + ")", - duplicateVariantRE = new RegExp(duplicateVariant, "i"); - - - /** - * Verifies that the given string is a well-formed BCP 47 language tag - * with no duplicate variant or singleton subtags. - * - * Spec: ECMAScript Internationalization API Specification, draft, 6.2.2. - */ - function isStructurallyValidLanguageTag(locale) { - if (!languageTagRE.test(locale)) { - return false; - } - locale = locale.split(/-x-/)[0]; - return !duplicateSingletonRE.test(locale) && !duplicateVariantRE.test(locale); - } - - - /** - * Mappings from complete tags to preferred values. - * - * Spec: IANA Language Subtag Registry. - */ - var __tagMappings = { - // property names must be in lower case; values in canonical form - - // grandfathered tags from IANA language subtag registry, file date 2011-08-25 - "art-lojban": "jbo", - "cel-gaulish": "cel-gaulish", - "en-gb-oed": "en-GB-oed", - "i-ami": "ami", - "i-bnn": "bnn", - "i-default": "i-default", - "i-enochian": "i-enochian", - "i-hak": "hak", - "i-klingon": "tlh", - "i-lux": "lb", - "i-mingo": "i-mingo", - "i-navajo": "nv", - "i-pwn": "pwn", - "i-tao": "tao", - "i-tay": "tay", - "i-tsu": "tsu", - "no-bok": "nb", - "no-nyn": "nn", - "sgn-be-fr": "sfb", - "sgn-be-nl": "vgt", - "sgn-ch-de": "sgg", - "zh-guoyu": "cmn", - "zh-hakka": "hak", - "zh-min": "zh-min", - "zh-min-nan": "nan", - "zh-xiang": "hsn", - // deprecated redundant tags from IANA language subtag registry, file date 2011-08-25 - "sgn-br": "bzs", - "sgn-co": "csn", - "sgn-de": "gsg", - "sgn-dk": "dsl", - "sgn-es": "ssp", - "sgn-fr": "fsl", - "sgn-gb": "bfi", - "sgn-gr": "gss", - "sgn-ie": "isg", - "sgn-it": "ise", - "sgn-jp": "jsl", - "sgn-mx": "mfs", - "sgn-ni": "ncs", - "sgn-nl": "dse", - "sgn-no": "nsl", - "sgn-pt": "psr", - "sgn-se": "swl", - "sgn-us": "ase", - "sgn-za": "sfs", - "zh-cmn": "cmn", - "zh-cmn-hans": "cmn-Hans", - "zh-cmn-hant": "cmn-Hant", - "zh-gan": "gan", - "zh-wuu": "wuu", - "zh-yue": "yue", - // deprecated variant with prefix from IANA language subtag registry, file date 2011-08-25 - "ja-latn-hepburn-heploc": "ja-Latn-alalc97" - }; - - - /** - * Mappings from non-extlang subtags to preferred values. - * - * Spec: IANA Language Subtag Registry. - */ - var __subtagMappings = { - // property names and values must be in canonical case - // language subtags with Preferred-Value mappings from IANA language subtag registry, file date 2011-08-25 - "in": "id", - "iw": "he", - "ji": "yi", - "jw": "jv", - "mo": "ro", - "ayx": "nun", - "cjr": "mom", - "cmk": "xch", - "drh": "khk", - "drw": "prs", - "gav": "dev", - "mst": "mry", - "myt": "mry", - "tie": "ras", - "tkk": "twm", - "tnf": "prs", - // region subtags with Preferred-Value mappings from IANA language subtag registry, file date 2011-08-25 - "BU": "MM", - "DD": "DE", - "FX": "FR", - "TP": "TL", - "YD": "YE", - "ZR": "CD" - }; - - - /** - * Mappings from extlang subtags to preferred values. - * - * Spec: IANA Language Subtag Registry. - */ - var __extlangMappings = { - // extlang subtags with Preferred-Value mappings from IANA language subtag registry, file date 2011-08-25 - // values are arrays with [0] the replacement value, [1] (if present) the prefix to be removed - "aao": ["aao", "ar"], - "abh": ["abh", "ar"], - "abv": ["abv", "ar"], - "acm": ["acm", "ar"], - "acq": ["acq", "ar"], - "acw": ["acw", "ar"], - "acx": ["acx", "ar"], - "acy": ["acy", "ar"], - "adf": ["adf", "ar"], - "ads": ["ads", "sgn"], - "aeb": ["aeb", "ar"], - "aec": ["aec", "ar"], - "aed": ["aed", "sgn"], - "aen": ["aen", "sgn"], - "afb": ["afb", "ar"], - "afg": ["afg", "sgn"], - "ajp": ["ajp", "ar"], - "apc": ["apc", "ar"], - "apd": ["apd", "ar"], - "arb": ["arb", "ar"], - "arq": ["arq", "ar"], - "ars": ["ars", "ar"], - "ary": ["ary", "ar"], - "arz": ["arz", "ar"], - "ase": ["ase", "sgn"], - "asf": ["asf", "sgn"], - "asp": ["asp", "sgn"], - "asq": ["asq", "sgn"], - "asw": ["asw", "sgn"], - "auz": ["auz", "ar"], - "avl": ["avl", "ar"], - "ayh": ["ayh", "ar"], - "ayl": ["ayl", "ar"], - "ayn": ["ayn", "ar"], - "ayp": ["ayp", "ar"], - "bbz": ["bbz", "ar"], - "bfi": ["bfi", "sgn"], - "bfk": ["bfk", "sgn"], - "bjn": ["bjn", "ms"], - "bog": ["bog", "sgn"], - "bqn": ["bqn", "sgn"], - "bqy": ["bqy", "sgn"], - "btj": ["btj", "ms"], - "bve": ["bve", "ms"], - "bvl": ["bvl", "sgn"], - "bvu": ["bvu", "ms"], - "bzs": ["bzs", "sgn"], - "cdo": ["cdo", "zh"], - "cds": ["cds", "sgn"], - "cjy": ["cjy", "zh"], - "cmn": ["cmn", "zh"], - "coa": ["coa", "ms"], - "cpx": ["cpx", "zh"], - "csc": ["csc", "sgn"], - "csd": ["csd", "sgn"], - "cse": ["cse", "sgn"], - "csf": ["csf", "sgn"], - "csg": ["csg", "sgn"], - "csl": ["csl", "sgn"], - "csn": ["csn", "sgn"], - "csq": ["csq", "sgn"], - "csr": ["csr", "sgn"], - "czh": ["czh", "zh"], - "czo": ["czo", "zh"], - "doq": ["doq", "sgn"], - "dse": ["dse", "sgn"], - "dsl": ["dsl", "sgn"], - "dup": ["dup", "ms"], - "ecs": ["ecs", "sgn"], - "esl": ["esl", "sgn"], - "esn": ["esn", "sgn"], - "eso": ["eso", "sgn"], - "eth": ["eth", "sgn"], - "fcs": ["fcs", "sgn"], - "fse": ["fse", "sgn"], - "fsl": ["fsl", "sgn"], - "fss": ["fss", "sgn"], - "gan": ["gan", "zh"], - "gom": ["gom", "kok"], - "gse": ["gse", "sgn"], - "gsg": ["gsg", "sgn"], - "gsm": ["gsm", "sgn"], - "gss": ["gss", "sgn"], - "gus": ["gus", "sgn"], - "hab": ["hab", "sgn"], - "haf": ["haf", "sgn"], - "hak": ["hak", "zh"], - "hds": ["hds", "sgn"], - "hji": ["hji", "ms"], - "hks": ["hks", "sgn"], - "hos": ["hos", "sgn"], - "hps": ["hps", "sgn"], - "hsh": ["hsh", "sgn"], - "hsl": ["hsl", "sgn"], - "hsn": ["hsn", "zh"], - "icl": ["icl", "sgn"], - "ils": ["ils", "sgn"], - "inl": ["inl", "sgn"], - "ins": ["ins", "sgn"], - "ise": ["ise", "sgn"], - "isg": ["isg", "sgn"], - "isr": ["isr", "sgn"], - "jak": ["jak", "ms"], - "jax": ["jax", "ms"], - "jcs": ["jcs", "sgn"], - "jhs": ["jhs", "sgn"], - "jls": ["jls", "sgn"], - "jos": ["jos", "sgn"], - "jsl": ["jsl", "sgn"], - "jus": ["jus", "sgn"], - "kgi": ["kgi", "sgn"], - "knn": ["knn", "kok"], - "kvb": ["kvb", "ms"], - "kvk": ["kvk", "sgn"], - "kvr": ["kvr", "ms"], - "kxd": ["kxd", "ms"], - "lbs": ["lbs", "sgn"], - "lce": ["lce", "ms"], - "lcf": ["lcf", "ms"], - "liw": ["liw", "ms"], - "lls": ["lls", "sgn"], - "lsg": ["lsg", "sgn"], - "lsl": ["lsl", "sgn"], - "lso": ["lso", "sgn"], - "lsp": ["lsp", "sgn"], - "lst": ["lst", "sgn"], - "lsy": ["lsy", "sgn"], - "ltg": ["ltg", "lv"], - "lvs": ["lvs", "lv"], - "lzh": ["lzh", "zh"], - "max": ["max", "ms"], - "mdl": ["mdl", "sgn"], - "meo": ["meo", "ms"], - "mfa": ["mfa", "ms"], - "mfb": ["mfb", "ms"], - "mfs": ["mfs", "sgn"], - "min": ["min", "ms"], - "mnp": ["mnp", "zh"], - "mqg": ["mqg", "ms"], - "mre": ["mre", "sgn"], - "msd": ["msd", "sgn"], - "msi": ["msi", "ms"], - "msr": ["msr", "sgn"], - "mui": ["mui", "ms"], - "mzc": ["mzc", "sgn"], - "mzg": ["mzg", "sgn"], - "mzy": ["mzy", "sgn"], - "nan": ["nan", "zh"], - "nbs": ["nbs", "sgn"], - "ncs": ["ncs", "sgn"], - "nsi": ["nsi", "sgn"], - "nsl": ["nsl", "sgn"], - "nsp": ["nsp", "sgn"], - "nsr": ["nsr", "sgn"], - "nzs": ["nzs", "sgn"], - "okl": ["okl", "sgn"], - "orn": ["orn", "ms"], - "ors": ["ors", "ms"], - "pel": ["pel", "ms"], - "pga": ["pga", "ar"], - "pks": ["pks", "sgn"], - "prl": ["prl", "sgn"], - "prz": ["prz", "sgn"], - "psc": ["psc", "sgn"], - "psd": ["psd", "sgn"], - "pse": ["pse", "ms"], - "psg": ["psg", "sgn"], - "psl": ["psl", "sgn"], - "pso": ["pso", "sgn"], - "psp": ["psp", "sgn"], - "psr": ["psr", "sgn"], - "pys": ["pys", "sgn"], - "rms": ["rms", "sgn"], - "rsi": ["rsi", "sgn"], - "rsl": ["rsl", "sgn"], - "sdl": ["sdl", "sgn"], - "sfb": ["sfb", "sgn"], - "sfs": ["sfs", "sgn"], - "sgg": ["sgg", "sgn"], - "sgx": ["sgx", "sgn"], - "shu": ["shu", "ar"], - "slf": ["slf", "sgn"], - "sls": ["sls", "sgn"], - "sqs": ["sqs", "sgn"], - "ssh": ["ssh", "ar"], - "ssp": ["ssp", "sgn"], - "ssr": ["ssr", "sgn"], - "svk": ["svk", "sgn"], - "swc": ["swc", "sw"], - "swh": ["swh", "sw"], - "swl": ["swl", "sgn"], - "syy": ["syy", "sgn"], - "tmw": ["tmw", "ms"], - "tse": ["tse", "sgn"], - "tsm": ["tsm", "sgn"], - "tsq": ["tsq", "sgn"], - "tss": ["tss", "sgn"], - "tsy": ["tsy", "sgn"], - "tza": ["tza", "sgn"], - "ugn": ["ugn", "sgn"], - "ugy": ["ugy", "sgn"], - "ukl": ["ukl", "sgn"], - "uks": ["uks", "sgn"], - "urk": ["urk", "ms"], - "uzn": ["uzn", "uz"], - "uzs": ["uzs", "uz"], - "vgt": ["vgt", "sgn"], - "vkk": ["vkk", "ms"], - "vkt": ["vkt", "ms"], - "vsi": ["vsi", "sgn"], - "vsl": ["vsl", "sgn"], - "vsv": ["vsv", "sgn"], - "wuu": ["wuu", "zh"], - "xki": ["xki", "sgn"], - "xml": ["xml", "sgn"], - "xmm": ["xmm", "ms"], - "xms": ["xms", "sgn"], - "yds": ["yds", "sgn"], - "ysl": ["ysl", "sgn"], - "yue": ["yue", "zh"], - "zib": ["zib", "sgn"], - "zlm": ["zlm", "ms"], - "zmi": ["zmi", "ms"], - "zsl": ["zsl", "sgn"], - "zsm": ["zsm", "ms"] - }; - - - /** - * Canonicalizes the given well-formed BCP 47 language tag, including regularized case of subtags. - * - * Spec: ECMAScript Internationalization API Specification, draft, 6.2.3. - * Spec: RFC 5646, section 4.5. - */ - function canonicalizeLanguageTag(locale) { - - // start with lower case for easier processing, and because most subtags will need to be lower case anyway - locale = locale.toLowerCase(); - - // handle mappings for complete tags - if (__tagMappings.hasOwnProperty(locale)) { - return __tagMappings[locale]; - } - - var subtags = locale.split("-"); - var i = 0; - - // handle standard part: all subtags before first singleton or "x" - while (i < subtags.length) { - var subtag = subtags[i]; - if (subtag.length === 1 && (i > 0 || subtag === "x")) { - break; - } else if (i !== 0 && subtag.length === 2) { - subtag = subtag.toUpperCase(); - } else if (subtag.length === 4) { - subtag = subtag[0].toUpperCase() + subtag.substring(1).toLowerCase(); - } - if (__subtagMappings.hasOwnProperty(subtag)) { - subtag = __subtagMappings[subtag]; - } else if (__extlangMappings.hasOwnProperty(subtag)) { - subtag = __extlangMappings[subtag][0]; - if (i === 1 && __extlangMappings[subtag][1] === subtags[0]) { - subtags.shift(); - i--; - } - } - subtags[i] = subtag; - i++; - } - var normal = subtags.slice(0, i).join("-"); - - // handle extensions - var extensions = []; - while (i < subtags.length && subtags[i] !== "x") { - var extensionStart = i; - i++; - while (i < subtags.length && subtags[i].length > 1) { - i++; - } - var extension = subtags.slice(extensionStart, i).join("-"); - extensions.push(extension); - } - extensions.sort(); - - // handle private use - var privateUse; - if (i < subtags.length) { - privateUse = subtags.slice(i).join("-"); - } - - // put everything back together - var canonical = normal; - if (extensions.length > 0) { - canonical += "-" + extensions.join("-"); - } - if (privateUse !== undefined) { - if (canonical.length > 0) { - canonical += "-" + privateUse; - } else { - canonical = privateUse; - } - } - - return canonical; - } - - return typeof locale === "string" && isStructurallyValidLanguageTag(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 + "."); - } -} - - -/** - * Properties of the RegExp constructor that may be affected by use of regular - * expressions, and the default values of these properties. Properties are from - * https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Deprecated_and_obsolete_features#RegExp_Properties - */ -var regExpProperties = ["$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", - "$_", "$*", "$&", "$+", "$`", "$'", - "input", "lastMatch", "lastParen", "leftContext", "rightContext" -]; - -var regExpPropertiesDefaultValues = (function () { - var values = Object.create(null); - regExpProperties.forEach(function (property) { - values[property] = RegExp[property]; - }); - return values; -}()); - - -/** - * Tests that executing the provided function (which may use regular expressions - * in its implementation) does not create or modify unwanted properties on the - * RegExp constructor. - */ -function testForUnwantedRegExpChanges(testFunc) { - regExpProperties.forEach(function (property) { - RegExp[property] = regExpPropertiesDefaultValues[property]; - }); - testFunc(); - regExpProperties.forEach(function (property) { - if (RegExp[property] !== regExpPropertiesDefaultValues[property]) { - $ERROR("RegExp has unexpected property " + property + " with value " + - RegExp[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: "0123456789", - 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; -} - |