|
| 1 | +function shouldBe(actual, expected) { |
| 2 | + if (actual !== expected) |
| 3 | + throw new Error(`expected ${expected} but got ${actual}`); |
| 4 | +} |
| 5 | + |
| 6 | +function shouldNotThrow(func) { |
| 7 | + func(); |
| 8 | +} |
| 9 | + |
| 10 | +function shouldThrow(func, errorType) { |
| 11 | + let error; |
| 12 | + try { |
| 13 | + func(); |
| 14 | + } catch (e) { |
| 15 | + error = e; |
| 16 | + } |
| 17 | + |
| 18 | + if (!(error instanceof errorType)) |
| 19 | + throw new Error(`Expected ${errorType.name}!`); |
| 20 | +} |
| 21 | + |
| 22 | +function test() { |
| 23 | + { |
| 24 | + // https://tc39.github.io/ecma402/#pluralrules-objects |
| 25 | + // The Intl.ListFormat Constructor |
| 26 | + |
| 27 | + // The ListFormat constructor is the %ListFormat% intrinsic object and a standard built-in property of the Intl object. |
| 28 | + shouldBe(Intl.ListFormat instanceof Function, true); |
| 29 | + |
| 30 | + // Intl.ListFormat ([ locales [, options ] ]) |
| 31 | + |
| 32 | + // If NewTarget is undefined, throw a TypeError exception. |
| 33 | + shouldThrow(() => Intl.ListFormat(), TypeError); |
| 34 | + shouldThrow(() => Intl.ListFormat.call({}), TypeError); |
| 35 | + |
| 36 | + shouldThrow(() => new Intl.ListFormat('$'), RangeError); |
| 37 | + shouldThrow(() => new Intl.ListFormat('en', null), TypeError); |
| 38 | + shouldBe(new Intl.ListFormat() instanceof Intl.ListFormat, true); |
| 39 | + |
| 40 | + // Subclassable |
| 41 | + { |
| 42 | + class DerivedListFormat extends Intl.ListFormat {}; |
| 43 | + shouldBe((new DerivedListFormat) instanceof DerivedListFormat, true); |
| 44 | + shouldBe((new DerivedListFormat) instanceof Intl.ListFormat, true); |
| 45 | + shouldBe(new DerivedListFormat('en').format(['Orange', 'Apple', 'Lemon']), 'Orange, Apple, and Lemon'); |
| 46 | + shouldBe(Object.getPrototypeOf(new DerivedListFormat), DerivedListFormat.prototype); |
| 47 | + shouldBe(Object.getPrototypeOf(Object.getPrototypeOf(new DerivedListFormat)), Intl.ListFormat.prototype); |
| 48 | + } |
| 49 | + |
| 50 | + // Properties of the Intl.ListFormat Constructor |
| 51 | + |
| 52 | + // length property (whose value is 0) |
| 53 | + shouldBe(Intl.ListFormat.length, 0); |
| 54 | + |
| 55 | + // Intl.ListFormat.prototype |
| 56 | + |
| 57 | + // This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }. |
| 58 | + shouldBe(Object.getOwnPropertyDescriptor(Intl.ListFormat, 'prototype').writable, false); |
| 59 | + shouldBe(Object.getOwnPropertyDescriptor(Intl.ListFormat, 'prototype').enumerable, false); |
| 60 | + shouldBe(Object.getOwnPropertyDescriptor(Intl.ListFormat, 'prototype').configurable, false); |
| 61 | + |
| 62 | + // Intl.ListFormat.supportedLocalesOf (locales [, options ]) |
| 63 | + |
| 64 | + // The value of the length property of the supportedLocalesOf method is 1. |
| 65 | + shouldBe(Intl.ListFormat.supportedLocalesOf.length, 1); |
| 66 | + |
| 67 | + // Returns SupportedLocales |
| 68 | + shouldBe(Intl.ListFormat.supportedLocalesOf() instanceof Array, true); |
| 69 | + // Doesn't care about `this`. |
| 70 | + shouldBe(JSON.stringify(Intl.ListFormat.supportedLocalesOf.call(null, 'en')), '["en"]'); |
| 71 | + shouldBe(JSON.stringify(Intl.ListFormat.supportedLocalesOf.call({}, 'en')), '["en"]'); |
| 72 | + shouldBe(JSON.stringify(Intl.ListFormat.supportedLocalesOf.call(1, 'en')), '["en"]'); |
| 73 | + // Ignores non-object, non-string list. |
| 74 | + shouldBe(JSON.stringify(Intl.ListFormat.supportedLocalesOf(9)), '[]'); |
| 75 | + // Makes an array of tags. |
| 76 | + shouldBe(JSON.stringify(Intl.ListFormat.supportedLocalesOf('en')), '["en"]'); |
| 77 | + // Handles array-like objects with holes. |
| 78 | + shouldBe(JSON.stringify(Intl.ListFormat.supportedLocalesOf({ length: 4, 1: 'en', 0: 'es', 3: 'de' })), '["es","en","de"]'); |
| 79 | + // Deduplicates tags. |
| 80 | + shouldBe(JSON.stringify(Intl.ListFormat.supportedLocalesOf([ 'en', 'pt', 'en', 'es' ])), '["en","pt","es"]'); |
| 81 | + // Canonicalizes tags. |
| 82 | + shouldBe( |
| 83 | + JSON.stringify(Intl.ListFormat.supportedLocalesOf('En-laTn-us-variAnt-fOObar-1abc-U-kn-tRue-A-aa-aaa-x-RESERVED')), |
| 84 | + $vm.icuVersion() >= 67 |
| 85 | + ? '["en-Latn-US-1abc-foobar-variant-a-aa-aaa-u-kn-x-reserved"]' |
| 86 | + : '["en-Latn-US-variant-foobar-1abc-a-aa-aaa-u-kn-x-reserved"]' |
| 87 | + ); |
| 88 | + // Throws on problems with length, get, or toString. |
| 89 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf(Object.create(null, { length: { get() { throw new Error(); } } })), Error); |
| 90 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf(Object.create(null, { length: { value: 1 }, 0: { get() { throw new Error(); } } })), Error); |
| 91 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf([ { toString() { throw new Error(); } } ]), Error); |
| 92 | + // Throws on bad tags. |
| 93 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('no-bok'), RangeError); |
| 94 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('x-some-thing'), RangeError); |
| 95 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf([ 5 ]), TypeError); |
| 96 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf(''), RangeError); |
| 97 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('a'), RangeError); |
| 98 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('abcdefghij'), RangeError); |
| 99 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('#$'), RangeError); |
| 100 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('en-@-abc'), RangeError); |
| 101 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('en-u'), RangeError); |
| 102 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('en-u-kn-true-u-ko-true'), RangeError); |
| 103 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('en-x'), RangeError); |
| 104 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('en-*'), RangeError); |
| 105 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('en-'), RangeError); |
| 106 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('en--US'), RangeError); |
| 107 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('i-klingon'), RangeError); // grandfathered tag is not accepted by IsStructurallyValidLanguageTag |
| 108 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('x-en-US-12345'), RangeError); |
| 109 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('x-12345-12345-en-US'), RangeError); |
| 110 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('x-en-US-12345-12345'), RangeError); |
| 111 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('x-en-u-foo'), RangeError); |
| 112 | + shouldThrow(() => Intl.ListFormat.supportedLocalesOf('x-en-u-foo-u-bar'), RangeError); |
| 113 | + |
| 114 | + // Accepts valid tags |
| 115 | + var validLanguageTags = [ |
| 116 | + 'de', // ISO 639 language code |
| 117 | + 'de-DE', // + ISO 3166-1 country code |
| 118 | + 'DE-de', // tags are case-insensitive |
| 119 | + 'cmn', // ISO 639 language code |
| 120 | + 'cmn-Hans', // + script code |
| 121 | + 'CMN-hANS', // tags are case-insensitive |
| 122 | + 'cmn-hans-cn', // + ISO 3166-1 country code |
| 123 | + 'es-419', // + UN M.49 region code |
| 124 | + 'es-419-u-nu-latn-cu-bob', // + Unicode locale extension sequence |
| 125 | + 'cmn-hans-cn-t-ca-u-ca-x-t-u', // singleton subtags can also be used as private use subtags |
| 126 | + 'enochian-enochian', // language and variant subtags may be the same |
| 127 | + 'de-gregory-u-ca-gregory', // variant and extension subtags may be the same |
| 128 | + 'aa-a-foo-x-a-foo-bar', // variant subtags can also be used as private use subtags |
| 129 | + ]; |
| 130 | + for (var validLanguageTag of validLanguageTags) |
| 131 | + shouldNotThrow(() => Intl.ListFormat.supportedLocalesOf(validLanguageTag)); |
| 132 | + |
| 133 | + // Properties of the Intl.ListFormat Prototype Object |
| 134 | + |
| 135 | + // The Intl.ListFormat prototype object is itself an ordinary object. |
| 136 | + shouldBe(Object.getPrototypeOf(Intl.ListFormat.prototype), Object.prototype); |
| 137 | + |
| 138 | + // Intl.ListFormat.prototype.constructor |
| 139 | + // The initial value of Intl.ListFormat.prototype.constructor is the intrinsic object %ListFormat%. |
| 140 | + shouldBe(Intl.ListFormat.prototype.constructor, Intl.ListFormat); |
| 141 | + |
| 142 | + // Intl.ListFormat.prototype [ @@toStringTag ] |
| 143 | + // The initial value of the @@toStringTag property is the string value "Intl.ListFormat". |
| 144 | + shouldBe(Intl.ListFormat.prototype[Symbol.toStringTag], 'Intl.ListFormat'); |
| 145 | + shouldBe(Object.prototype.toString.call(Intl.ListFormat.prototype), '[object Intl.ListFormat]'); |
| 146 | + // This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. |
| 147 | + shouldBe(Object.getOwnPropertyDescriptor(Intl.ListFormat.prototype, Symbol.toStringTag).writable, false); |
| 148 | + shouldBe(Object.getOwnPropertyDescriptor(Intl.ListFormat.prototype, Symbol.toStringTag).enumerable, false); |
| 149 | + shouldBe(Object.getOwnPropertyDescriptor(Intl.ListFormat.prototype, Symbol.toStringTag).configurable, true); |
| 150 | + } |
| 151 | + { |
| 152 | + const lf = new Intl.ListFormat("en", { |
| 153 | + localeMatcher: "best fit", |
| 154 | + type: "conjunction", |
| 155 | + style: "long", |
| 156 | + }); |
| 157 | + shouldBe(lf.format(['Motorcycle', 'Truck' , 'Car']), `Motorcycle, Truck, and Car`); |
| 158 | + shouldBe(lf.format([]), ``); |
| 159 | + shouldBe(lf.format(), ``); |
| 160 | + shouldBe(lf.format(undefined), ``); |
| 161 | + shouldBe(lf.format("Apple"), `A, p, p, l, and e`); |
| 162 | + shouldThrow(() => lf.format(42), TypeError); |
| 163 | + shouldThrow(() => lf.format(null), TypeError); |
| 164 | + shouldThrow(() => lf.format([null]), TypeError); |
| 165 | + shouldBe(JSON.stringify(lf.resolvedOptions()), `{"locale":"en","type":"conjunction","style":"long"}`); |
| 166 | + shouldBe(JSON.stringify(Reflect.getOwnPropertyDescriptor(Intl.ListFormat.prototype, Symbol.toStringTag)), `{"value":"Intl.ListFormat","writable":false,"enumerable":false,"configurable":true}`); |
| 167 | + } |
| 168 | + { |
| 169 | + const list = ['Motorcycle', 'Bus', 'Car']; |
| 170 | + shouldBe(new Intl.ListFormat('en-GB', { style: 'long', type: 'conjunction' }).format(list), `Motorcycle, Bus and Car`); |
| 171 | + shouldBe(new Intl.ListFormat('en-GB', { style: 'short', type: 'conjunction' }).format(list), `Motorcycle, Bus and Car`); |
| 172 | + shouldBe(new Intl.ListFormat('en-GB', { style: 'narrow', type: 'conjunction' }).format(list), `Motorcycle, Bus, Car`); |
| 173 | + shouldBe(new Intl.ListFormat('en-GB', { style: 'long', type: 'disjunction' }).format(list), `Motorcycle, Bus or Car`); |
| 174 | + shouldBe(new Intl.ListFormat('en-GB', { style: 'short', type: 'disjunction' }).format(list), `Motorcycle, Bus or Car`); |
| 175 | + shouldBe(new Intl.ListFormat('en-GB', { style: 'narrow', type: 'disjunction' }).format(list), `Motorcycle, Bus or Car`); |
| 176 | + shouldBe(new Intl.ListFormat('en-GB', { style: 'long', type: 'unit' }).format(list), `Motorcycle, Bus, Car`); |
| 177 | + shouldBe(new Intl.ListFormat('en-GB', { style: 'short', type: 'unit' }).format(list), `Motorcycle, Bus, Car`); |
| 178 | + shouldBe(new Intl.ListFormat('en-GB', { style: 'narrow', type: 'unit' }).format(list), `Motorcycle Bus Car`); |
| 179 | + } |
| 180 | + { |
| 181 | + const list = ['バイク', 'バス', '車']; |
| 182 | + shouldBe(new Intl.ListFormat('ja-JP', { style: 'long', type: 'conjunction' }).format(list), `バイク、バス、車`); |
| 183 | + shouldBe(new Intl.ListFormat('ja-JP', { style: 'short', type: 'conjunction' }).format(list), `バイク、バス、車`); |
| 184 | + shouldBe(new Intl.ListFormat('ja-JP', { style: 'narrow', type: 'conjunction' }).format(list), `バイク、バス、車`); |
| 185 | + shouldBe(new Intl.ListFormat('ja-JP', { style: 'long', type: 'disjunction' }).format(list), `バイク、バス、または車`); |
| 186 | + shouldBe(new Intl.ListFormat('ja-JP', { style: 'short', type: 'disjunction' }).format(list), `バイク、バス、または車`); |
| 187 | + shouldBe(new Intl.ListFormat('ja-JP', { style: 'narrow', type: 'disjunction' }).format(list), `バイク、バス、または車`); |
| 188 | + shouldBe(new Intl.ListFormat('ja-JP', { style: 'long', type: 'unit' }).format(list), `バイク バス 車`); |
| 189 | + shouldBe(new Intl.ListFormat('ja-JP', { style: 'short', type: 'unit' }).format(list), `バイク バス 車`); |
| 190 | + shouldBe(new Intl.ListFormat('ja-JP', { style: 'narrow', type: 'unit' }).format(list), `バイクバス車`); |
| 191 | + } |
| 192 | + { |
| 193 | + const list = ['Motorcycle', 'Bus', 'Car']; |
| 194 | + const expected = [ |
| 195 | + { "type": "element", "value": "Motorcycle" }, |
| 196 | + { "type": "literal", "value": ", " }, |
| 197 | + { "type": "element", "value": "Bus" }, |
| 198 | + { "type": "literal", "value": " and " }, |
| 199 | + { "type": "element", "value": "Car" } |
| 200 | + ]; |
| 201 | + |
| 202 | + const lf = new Intl.ListFormat('en-GB', { style: 'long', type: 'conjunction' }); |
| 203 | + const parts = lf.formatToParts(list); |
| 204 | + shouldBe(parts.length, expected.length); |
| 205 | + for (let i = 0; i < parts.length; ++i) { |
| 206 | + shouldBe(parts[i].type, expected[i].type); |
| 207 | + shouldBe(parts[i].value, expected[i].value); |
| 208 | + } |
| 209 | + |
| 210 | + shouldBe(JSON.stringify(lf.formatToParts([])), `[]`); |
| 211 | + shouldBe(JSON.stringify(lf.formatToParts()), `[]`); |
| 212 | + shouldBe(JSON.stringify(lf.formatToParts(undefined)), `[]`); |
| 213 | + shouldBe(JSON.stringify(lf.formatToParts("Apple")), `[{"type":"element","value":"A"},{"type":"literal","value":", "},{"type":"element","value":"p"},{"type":"literal","value":", "},{"type":"element","value":"p"},{"type":"literal","value":", "},{"type":"element","value":"l"},{"type":"literal","value":" and "},{"type":"element","value":"e"}]`); |
| 214 | + shouldThrow(() => lf.formatToParts(42), TypeError); |
| 215 | + shouldThrow(() => lf.formatToParts(null), TypeError); |
| 216 | + shouldThrow(() => lf.formatToParts([null]), TypeError); |
| 217 | + } |
| 218 | +} |
| 219 | + |
| 220 | +if (typeof Intl.ListFormat !== 'undefined') |
| 221 | + test(); |
0 commit comments