[INTL] Implement Intl.Collator.prototype.resolvedOptions ()
[WebKit-https.git] / LayoutTests / js / script-tests / intl-collator.js
1 description("This test checks the behavior of Intl.Collator as described in the ECMAScript Internationalization API Specification (ECMA-402 2.0).");
2
3 // 10.1 The Intl.Collator Constructor
4
5 // The Intl.Collator constructor is a standard built-in property of the Intl object.
6 shouldBeType("Intl.Collator", "Function");
7
8 // 10.1.2 Intl.Collator([ locales [, options]])
9 shouldBeType("Intl.Collator()", "Intl.Collator");
10 shouldBeType("Intl.Collator.call({})", "Intl.Collator");
11 shouldBeType("new Intl.Collator()", "Intl.Collator");
12 shouldBeType("Intl.Collator('en')", "Intl.Collator");
13 shouldBeType("Intl.Collator(42)", "Intl.Collator");
14 shouldThrow("Intl.Collator(null)", "'TypeError: null is not an object (evaluating \\'Intl.Collator(null)\\')'");
15 shouldThrow("Intl.Collator({ get length() { throw 42; } })", "'42'");
16 shouldBeType("Intl.Collator('en', { })", "Intl.Collator");
17 shouldBeType("Intl.Collator('en', 42)", "Intl.Collator");
18 shouldThrow("Intl.Collator('en', null)", "'TypeError: null is not an object (evaluating \\'Intl.Collator(\\'en\\', null)\\')'");
19
20 // Subclassable
21 var classPrefix = "class DerivedCollator extends Intl.Collator {};";
22 shouldBeTrue(classPrefix + "(new DerivedCollator) instanceof DerivedCollator");
23 shouldBeTrue(classPrefix + "(new DerivedCollator) instanceof Intl.Collator");
24 shouldBeTrue(classPrefix + "new DerivedCollator().compare('a', 'b') === -1");
25 shouldBeTrue(classPrefix + "Object.getPrototypeOf(new DerivedCollator) === DerivedCollator.prototype");
26 shouldBeTrue(classPrefix + "Object.getPrototypeOf(Object.getPrototypeOf(new DerivedCollator)) === Intl.Collator.prototype");
27
28 var testCollator = function(collator, possibleOptionDifferences) {
29     var possibleOptions = possibleOptionDifferences.map(function(difference) {
30         var defaultOptions = {
31             locale: "en",
32             usage: "sort",
33             sensitivity: "variant",
34             ignorePunctuation: false,
35             collation: "default",
36             numeric: false
37         }
38         Object.assign(defaultOptions, difference);
39         return JSON.stringify(defaultOptions);
40     });
41     var actualOptions = JSON.stringify(collator.resolvedOptions())
42     return possibleOptions.includes(actualOptions);
43 }
44
45 // Locale is processed correctly.
46 shouldBeTrue("testCollator(Intl.Collator('en'), [{locale: 'en'}])");
47 shouldBeTrue("testCollator(Intl.Collator('eN-uS'), [{locale: 'en-US'}])");
48 shouldBeTrue("testCollator(Intl.Collator(['en', 'de']), [{locale: 'en'}])");
49 shouldBeTrue("testCollator(Intl.Collator('de'), [{locale: 'de'}])");
50
51 // The "co" key is processed correctly.
52 shouldBeTrue("testCollator(Intl.Collator('en-u-co-eor'), [{locale: 'en-u-co-eor', collation: 'eor'}, {locale: 'en'}])");
53 shouldBeTrue("testCollator(new Intl.Collator('en-u-co-eor'), [{locale: 'en-u-co-eor', collation: 'eor'}, {locale: 'en'}])");
54 shouldBeTrue("testCollator(Intl.Collator('En-U-Co-Eor'), [{locale: 'en-u-co-eor', collation: 'eor'}, {locale: 'en'}])");
55 shouldBeTrue("testCollator(Intl.Collator('en-u-co-phonebk'), [{locale: 'en'}])");
56 shouldBeTrue("testCollator(Intl.Collator('en-u-co-standard'), [{locale: 'en'}])");
57 shouldBeTrue("testCollator(Intl.Collator('en-u-co-search'), [{locale: 'en'}])");
58 shouldBeTrue("testCollator(Intl.Collator('en-u-co-abcd'), [{locale: 'en'}])");
59 shouldBeTrue("testCollator(Intl.Collator('de-u-co-phonebk'), [{locale: 'de-u-co-phonebk', collation: 'phonebk'}])");
60
61 // The "kn" key is processed correctly.
62 shouldBeTrue("testCollator(Intl.Collator('en-u-kn'), [{locale: 'en', numeric: true}])");
63 shouldBeTrue("testCollator(Intl.Collator('en-u-kn-true'), [{locale: 'en-u-kn-true', numeric: true}])");
64 shouldBeTrue("testCollator(Intl.Collator('en-u-kn-false'), [{locale: 'en-u-kn-false', numeric: false}])");
65 shouldBeTrue("testCollator(Intl.Collator('en-u-kn-abcd'), [{locale: 'en'}])");
66
67 // Ignores irrelevant extension keys.
68 shouldBeTrue("testCollator(Intl.Collator('en-u-aa-aaaa-kn-true-bb-bbbb-co-eor-cc-cccc-y-yyd'), [{locale: 'en-u-co-eor-kn-true', collation: 'eor', numeric: true}, {locale: 'en-u-kn-true', numeric: true}])");
69
70 // Ignores other extensions.
71 shouldBeTrue("testCollator(Intl.Collator('en-u-kn-true-a-aa'), [{locale: 'en-u-kn-true', numeric: true}])");
72 shouldBeTrue("testCollator(Intl.Collator('en-a-aa-u-kn-true'), [{locale: 'en-u-kn-true', numeric: true}])");
73 shouldBeTrue("testCollator(Intl.Collator('en-a-aa-u-kn-true-b-bb'), [{locale: 'en-u-kn-true', numeric: true}])");
74
75 // The option usage is processed correctly.
76 shouldBeTrue("testCollator(Intl.Collator('en', {usage: 'sort'}), [{locale: 'en', usage: 'sort'}])");
77 shouldBeTrue("testCollator(Intl.Collator('en', {usage: 'search'}), [{locale: 'en', usage: 'search'}])");
78 shouldThrow("Intl.Collator('en', {usage: 'Sort'})", '\'RangeError: usage must be either "sort" or "search"\'');
79 shouldThrow("Intl.Collator('en', { get usage() { throw 42; } })", "'42'");
80 shouldThrow("Intl.Collator('en', {usage: {toString() { throw 42; }}})", "'42'");
81
82 // The option localeMatcher is processed correctly.
83 shouldBeTrue("testCollator(Intl.Collator('en', {localeMatcher: 'lookup'}), [{locale: 'en'}])");
84 shouldBeTrue("testCollator(Intl.Collator('en', {localeMatcher: 'best fit'}), [{locale: 'en'}])");
85 shouldThrow("Intl.Collator('en', {localeMatcher: 'LookUp'})", '\'RangeError: localeMatcher must be either "lookup" or "best fit"\'');
86 shouldThrow("Intl.Collator('en', { get localeMatcher() { throw 42; } })", "'42'");
87
88 // The option numeric is processed correctly.
89 shouldBeTrue("testCollator(Intl.Collator('en', {numeric: true}), [{locale: 'en', numeric: true}])");
90 shouldBeTrue("testCollator(Intl.Collator('en', {numeric: false}), [{locale: 'en', numeric: false}])");
91 shouldBeTrue("testCollator(Intl.Collator('en', {numeric: 'false'}), [{locale: 'en', numeric: true}])");
92 shouldBeTrue("testCollator(Intl.Collator('en', {numeric: { }}), [{locale: 'en', numeric: true}])");
93 shouldThrow("Intl.Collator('en', { get numeric() { throw 42; } })", "'42'");
94
95 // The option caseFirst is processed correctly.
96 shouldBeTrue("testCollator(Intl.Collator('en', {caseFirst: 'upper'}), [{locale: 'en'}])");
97 shouldBeTrue("testCollator(Intl.Collator('en', {caseFirst: 'lower'}), [{locale: 'en'}])");
98 shouldBeTrue("testCollator(Intl.Collator('en', {caseFirst: 'false'}), [{locale: 'en'}])");
99 shouldBeTrue("testCollator(Intl.Collator('en', {caseFirst: false}), [{locale: 'en'}])");
100 shouldThrow("Intl.Collator('en', {caseFirst: 'true'})", '\'RangeError: caseFirst must be either "upper", "lower", or "false"\'');
101 shouldThrow("Intl.Collator('en', { get caseFirst() { throw 42; } })", "'42'");
102
103 // The option sensitivity is processed correctly.
104 shouldBeTrue("testCollator(Intl.Collator('en', {sensitivity: 'base'}), [{locale: 'en', sensitivity: 'base'}])");
105 shouldBeTrue("testCollator(Intl.Collator('en', {sensitivity: 'accent'}), [{locale: 'en', sensitivity: 'accent'}])");
106 shouldBeTrue("testCollator(Intl.Collator('en', {sensitivity: 'case'}), [{locale: 'en', sensitivity: 'case'}])");
107 shouldBeTrue("testCollator(Intl.Collator('en', {sensitivity: 'variant'}), [{locale: 'en', sensitivity: 'variant'}])");
108 shouldThrow("Intl.Collator('en', {sensitivity: 'abcd'})", '\'RangeError: sensitivity must be either "base", "accent", "case", or "variant"\'');
109 shouldThrow("Intl.Collator('en', { get sensitivity() { throw 42; } })", "'42'");
110
111 // The option ignorePunctuation is processed correctly.
112 shouldBeTrue("testCollator(Intl.Collator('en', {ignorePunctuation: true}), [{locale: 'en', ignorePunctuation: true}])");
113 shouldBeTrue("testCollator(Intl.Collator('en', {ignorePunctuation: false}), [{locale: 'en', ignorePunctuation: false}])");
114 shouldBeTrue("testCollator(Intl.Collator('en', {ignorePunctuation: 'false'}), [{locale: 'en', ignorePunctuation: true}])");
115 shouldThrow("Intl.Collator('en', { get ignorePunctuation() { throw 42; } })", "'42'");
116
117 // Options override the language tag.
118 shouldBeTrue("testCollator(Intl.Collator('en-u-kn-true', {numeric: false}), [{locale: 'en', numeric: false}])");
119 shouldBeTrue("testCollator(Intl.Collator('en-u-kn-false', {numeric: true}), [{locale: 'en', numeric: true}])");
120 shouldBeTrue("testCollator(Intl.Collator('en-u-kn-true', {numeric: true}), [{locale: 'en-u-kn-true', numeric: true}])");
121 shouldBeTrue("testCollator(Intl.Collator('en-u-kn-false', {numeric: false}), [{locale: 'en-u-kn-false', numeric: false}])");
122
123 // Options and extension keys are processed correctly.
124 shouldBeTrue("testCollator(Intl.Collator('en-a-aa-u-kn-false-co-eor-b-bb', {usage: 'sort', numeric: true, caseFirst: 'lower', sensitivity: 'case', ignorePunctuation: true}), [{locale: 'en-u-co-eor', usage: 'sort', sensitivity: 'case', ignorePunctuation: true, collation: 'eor', numeric: true}, {locale: 'en', usage: 'sort', sensitivity: 'case', ignorePunctuation: true, numeric: true}])");
125
126 // 10.2 Properties of the Intl.Collator Constructor
127
128 // length property (whose value is 0)
129 shouldBe("Intl.Collator.length", "0");
130
131 // 10.2.1 Intl.Collator.prototype
132
133 // This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.
134 shouldBeFalse("Object.getOwnPropertyDescriptor(Intl.Collator, 'prototype').writable");
135 shouldBeFalse("Object.getOwnPropertyDescriptor(Intl.Collator, 'prototype').enumerable");
136 shouldBeFalse("Object.getOwnPropertyDescriptor(Intl.Collator, 'prototype').configurable");
137
138 // 10.2.2 Intl.Collator.supportedLocalesOf (locales [, options ])
139
140 // The value of the length property of the supportedLocalesOf method is 1.
141 shouldBe("Intl.Collator.supportedLocalesOf.length", "1");
142
143 // Returns SupportedLocales.
144 shouldBeType("Intl.Collator.supportedLocalesOf()", "Array");
145 // Doesn't care about `this`.
146 shouldBe("Intl.Collator.supportedLocalesOf.call(null, 'en')", "[ 'en' ]");
147 shouldBe("Intl.Collator.supportedLocalesOf.call({}, 'en')", "[ 'en' ]");
148 shouldBe("Intl.Collator.supportedLocalesOf.call(1, 'en')", "[ 'en' ]");
149 // Ignores non-object, non-string list.
150 shouldBe("Intl.Collator.supportedLocalesOf(9)", "[]");
151 // Makes an array of tags.
152 shouldBe("Intl.Collator.supportedLocalesOf('en')", "[ 'en' ]");
153 // Handles array-like objects with holes.
154 shouldBe("Intl.Collator.supportedLocalesOf({ length: 4, 1: 'en', 0: 'es', 3: 'de' })", "[ 'es', 'en', 'de' ]");
155 // Deduplicates tags.
156 shouldBe("Intl.Collator.supportedLocalesOf([ 'en', 'pt', 'en', 'es' ])", "[ 'en', 'pt', 'es' ]");
157 // Canonicalizes tags.
158 shouldBe("Intl.Collator.supportedLocalesOf('En-laTn-us-variant2-variant1-1abc-U-ko-tRue-A-aa-aaa-x-RESERVED')", "[ 'en-Latn-US-variant2-variant1-1abc-a-aa-aaa-u-ko-true-x-reserved' ]");
159 // Replaces outdated tags.
160 shouldBe("Intl.Collator.supportedLocalesOf('no-bok')", "[ 'nb' ]");
161 // Doesn't throw, but ignores private tags.
162 shouldBe("Intl.Collator.supportedLocalesOf('x-some-thing')", "[]");
163 // Throws on problems with length, get, or toString.
164 shouldThrow("Intl.Collator.supportedLocalesOf(Object.create(null, { length: { get() { throw Error('a') } } }))", "'Error: a'");
165 shouldThrow("Intl.Collator.supportedLocalesOf(Object.create(null, { length: { value: 1 }, 0: { get() { throw Error('b') } } }))", "'Error: b'");
166 shouldThrow("Intl.Collator.supportedLocalesOf([ { toString() { throw Error('c') } } ])", "'Error: c'");
167 // Throws on bad tags.
168 shouldThrow("Intl.Collator.supportedLocalesOf([ 5 ])", "'TypeError: locale value must be a string or object'");
169 shouldThrow("Intl.Collator.supportedLocalesOf('')", "'RangeError: invalid language tag: '");
170 shouldThrow("Intl.Collator.supportedLocalesOf('a')", "'RangeError: invalid language tag: a'");
171 shouldThrow("Intl.Collator.supportedLocalesOf('abcdefghij')", "'RangeError: invalid language tag: abcdefghij'");
172 shouldThrow("Intl.Collator.supportedLocalesOf('#$')", "'RangeError: invalid language tag: #$'");
173 shouldThrow("Intl.Collator.supportedLocalesOf('en-@-abc')", "'RangeError: invalid language tag: en-@-abc'");
174 shouldThrow("Intl.Collator.supportedLocalesOf('en-u')", "'RangeError: invalid language tag: en-u'");
175 shouldThrow("Intl.Collator.supportedLocalesOf('en-u-kn-true-u-ko-true')", "'RangeError: invalid language tag: en-u-kn-true-u-ko-true'");
176 shouldThrow("Intl.Collator.supportedLocalesOf('en-x')", "'RangeError: invalid language tag: en-x'");
177 shouldThrow("Intl.Collator.supportedLocalesOf('en-*')", "'RangeError: invalid language tag: en-*'");
178 shouldThrow("Intl.Collator.supportedLocalesOf('en-')", "'RangeError: invalid language tag: en-'");
179 shouldThrow("Intl.Collator.supportedLocalesOf('en--US')", "'RangeError: invalid language tag: en--US'");
180
181 // 10.3 Properties of the Intl.Collator Prototype Object
182
183 // The value of Intl.Collator.prototype.constructor is %Collator%.
184 shouldBe("Intl.Collator.prototype.constructor", "Intl.Collator");
185
186 // 10.3.3 Intl.Collator.prototype.compare
187
188 // This named accessor property returns a function that compares two strings according to the sort order of this Collator object.
189 shouldBeType("Intl.Collator.prototype.compare", "Function");
190
191 // The value of the [[Get]] attribute is a function
192 shouldBeType("Object.getOwnPropertyDescriptor(Intl.Collator.prototype, 'compare').get", "Function");
193
194 // The value of the [[Set]] attribute is undefined.
195 shouldBe("Object.getOwnPropertyDescriptor(Intl.Collator.prototype, 'compare').set", "undefined");
196
197 // Match Firefox where unspecifed.
198 shouldBeFalse("Object.getOwnPropertyDescriptor(Intl.Collator.prototype, 'compare').enumerable");
199 shouldBeTrue("Object.getOwnPropertyDescriptor(Intl.Collator.prototype, 'compare').configurable");
200
201 // The value of F’s length property is 2.
202 shouldBe("Intl.Collator.prototype.compare.length", "2");
203
204 // Throws on non-Collator this.
205 shouldThrow("Object.defineProperty({}, 'compare', Object.getOwnPropertyDescriptor(Intl.Collator.prototype, 'compare')).compare", "'TypeError: Intl.Collator.prototype.compare called on value that\\'s not an object initialized as a Collator'");
206
207 // The compare function is unique per instance.
208 shouldBeTrue("Intl.Collator.prototype.compare !== Intl.Collator().compare");
209 shouldBeTrue("new Intl.Collator().compare !== new Intl.Collator().compare");
210
211 // 10.3.4 Collator Compare Functions
212
213 // 1. Let collator be the this value.
214 // 2. Assert: Type(collator) is Object and collator has an [[initializedCollator]] internal slot whose value is true.
215 // This should not be reachable, since compare is bound to an initialized collator.
216
217 // 3. If x is not provided, let x be undefined.
218 // 4. If y is not provided, let y be undefined.
219 // 5. Let X be ToString(x).
220 // 6. ReturnIfAbrupt(X).
221 var badCalls = 0;
222 shouldThrow("Intl.Collator.prototype.compare({ toString() { throw Error('6') } }, { toString() { ++badCalls; return ''; } })", "'Error: 6'");
223 shouldBe("badCalls", "0");
224
225 // 7. Let Y be ToString(y).
226 // 8. ReturnIfAbrupt(Y).
227 shouldThrow("Intl.Collator.prototype.compare('a', { toString() { throw Error('8') } })", "'Error: 8'");
228
229 // Compare is bound, so calling with alternate "this" has no effect.
230 shouldBe("Intl.Collator.prototype.compare.call(null, 'a', 'b')", "-1");
231 shouldBe("Intl.Collator.prototype.compare.call(Intl.Collator('en', { sensitivity:'base' }), 'A', 'a')", "-1");
232 shouldBe("Intl.Collator.prototype.compare.call(5, 'a', 'b')", "-1");
233 shouldBe("new Intl.Collator().compare.call(null, 'a', 'b')", "-1");
234 shouldBe("new Intl.Collator().compare.call(Intl.Collator('en', { sensitivity:'base' }), 'A', 'a')", "-1");
235 shouldBe("new Intl.Collator().compare.call(5, 'a', 'b')", "-1");
236
237 // 10.3.5 Intl.Collator.prototype.resolvedOptions ()
238
239 shouldBe("Intl.Collator.prototype.resolvedOptions.length", "0");
240
241 // Returns a new object whose properties and attributes are set as if constructed by an object literal.
242 shouldBeType("Intl.Collator.prototype.resolvedOptions()", "Object");
243
244 // Returns a new object each time.
245 shouldBeFalse("Intl.Collator.prototype.resolvedOptions() === Intl.Collator.prototype.resolvedOptions()");
246
247 // Throws on non-Collator this.
248 shouldThrow("Intl.Collator.prototype.resolvedOptions.call(5)", "'TypeError: Intl.Collator.prototype.resolvedOptions called on value that\\'s not an object initialized as a Collator'");
249