blob: 8c14346d0bf73b80663715de5856238b9e507312 [file] [log] [blame]
function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error(`expected ${expected} but got ${actual}`);
}
const icuVersion = $vm.icuVersion();
function shouldBeForICUVersion(minimumVersion, actual, expected) {
if (icuVersion < minimumVersion)
return;
if (actual !== expected)
throw new Error(`expected ${expected} but got ${actual}`);
}
function shouldNotThrow(func) {
func();
}
function shouldThrow(func, errorType) {
let error;
try {
func();
} catch (e) {
error = e;
}
if (!(error instanceof errorType))
throw new Error(`Expected ${errorType.name}!`);
}
shouldBe(Intl.RelativeTimeFormat instanceof Function, true);
shouldBe(Intl.RelativeTimeFormat.length, 0);
shouldBe(Object.getOwnPropertyDescriptor(Intl.RelativeTimeFormat, 'prototype').writable, false);
shouldBe(Object.getOwnPropertyDescriptor(Intl.RelativeTimeFormat, 'prototype').enumerable, false);
shouldBe(Object.getOwnPropertyDescriptor(Intl.RelativeTimeFormat, 'prototype').configurable, false);
shouldThrow(() => Intl.RelativeTimeFormat(), TypeError);
shouldThrow(() => Intl.RelativeTimeFormat.call({}), TypeError);
shouldThrow(() => new Intl.RelativeTimeFormat('$'), RangeError);
shouldThrow(() => new Intl.RelativeTimeFormat('en', null), TypeError);
shouldBe(new Intl.RelativeTimeFormat() instanceof Intl.RelativeTimeFormat, true);
{
class DerivedRelativeTimeFormat extends Intl.RelativeTimeFormat {}
const drtf = new DerivedRelativeTimeFormat();
shouldBe(drtf instanceof DerivedRelativeTimeFormat, true);
shouldBe(drtf instanceof Intl.RelativeTimeFormat, true);
shouldBe(drtf.format, Intl.RelativeTimeFormat.prototype.format);
shouldBe(drtf.formatToParts, Intl.RelativeTimeFormat.prototype.formatToParts);
shouldBe(Object.getPrototypeOf(drtf), DerivedRelativeTimeFormat.prototype);
shouldBe(Object.getPrototypeOf(DerivedRelativeTimeFormat.prototype), Intl.RelativeTimeFormat.prototype);
}
shouldBe(Intl.RelativeTimeFormat.supportedLocalesOf.length, 1);
shouldBe(Intl.RelativeTimeFormat.supportedLocalesOf() instanceof Array, true);
shouldBe(JSON.stringify(Intl.RelativeTimeFormat.supportedLocalesOf.call(null, 'en')), '["en"]');
shouldBe(JSON.stringify(Intl.RelativeTimeFormat.supportedLocalesOf.call({}, 'en')), '["en"]');
shouldBe(JSON.stringify(Intl.RelativeTimeFormat.supportedLocalesOf.call(1, 'en')), '["en"]');
shouldBe(JSON.stringify(Intl.RelativeTimeFormat.supportedLocalesOf(9)), '[]');
shouldBe(JSON.stringify(Intl.RelativeTimeFormat.supportedLocalesOf('en')), '["en"]');
shouldBe(JSON.stringify(Intl.RelativeTimeFormat.supportedLocalesOf({ length: 4, 1: 'en', 0: 'es', 3: 'de' })), '["es","en","de"]');
shouldBe(JSON.stringify(Intl.RelativeTimeFormat.supportedLocalesOf(['en', 'pt', 'en', 'es'])), '["en","pt","es"]');
shouldBe(
JSON.stringify(Intl.RelativeTimeFormat.supportedLocalesOf('En-laTn-us-variAnt-fOObar-1abc-U-kn-tRue-A-aa-aaa-x-RESERVED')),
$vm.icuVersion() >= 67
? '["en-Latn-US-1abc-foobar-variant-a-aa-aaa-u-kn-x-reserved"]'
: '["en-Latn-US-variant-foobar-1abc-a-aa-aaa-u-kn-x-reserved"]'
);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('no-bok'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('x-some-thing'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf(Object.create(null, { length: { get() { throw new Error(); } } })), Error);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf(Object.create(null, { length: { value: 1 }, 0: { get() { throw new Error(); } } })), Error);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf([{ toString() { throw new Error(); } }]), Error);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf([5]), TypeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf(''), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('a'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('abcdefghij'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('#$'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('en-@-abc'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('en-u'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('en-u-kn-true-u-ko-true'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('en-x'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('en-*'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('en-'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('en--US'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('i-klingon'), RangeError); // grandfathered tag is not accepted by IsStructurallyValidLanguageTag
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('x-en-US-12345'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('x-12345-12345-en-US'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('x-en-US-12345-12345'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('x-en-u-foo'), RangeError);
shouldThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf('x-en-u-foo-u-bar'), RangeError);
const validLanguageTags = [
'de', // ISO 639 language code
'de-DE', // + ISO 3166-1 country code
'DE-de', // tags are case-insensitive
'cmn', // ISO 639 language code
'cmn-Hans', // + script code
'CMN-hANS', // tags are case-insensitive
'cmn-hans-cn', // + ISO 3166-1 country code
'es-419', // + UN M.49 region code
'es-419-u-nu-latn-cu-bob', // + Unicode locale extension sequence
'cmn-hans-cn-t-ca-u-ca-x-t-u', // singleton subtags can also be used as private use subtags
'enochian-enochian', // language and variant subtags may be the same
'de-gregory-u-ca-gregory', // variant and extension subtags may be the same
'aa-a-foo-x-a-foo-bar', // variant subtags can also be used as private use subtags
];
for (let validLanguageTag of validLanguageTags)
shouldNotThrow(() => Intl.RelativeTimeFormat.supportedLocalesOf(validLanguageTag));
shouldBe(Object.getPrototypeOf(Intl.RelativeTimeFormat.prototype), Object.prototype);
shouldBe(Intl.RelativeTimeFormat.prototype.constructor, Intl.RelativeTimeFormat);
shouldBe(Intl.RelativeTimeFormat.prototype[Symbol.toStringTag], 'Intl.RelativeTimeFormat');
shouldBe(Object.prototype.toString.call(Intl.RelativeTimeFormat.prototype), '[object Intl.RelativeTimeFormat]');
shouldBe(Object.getOwnPropertyDescriptor(Intl.RelativeTimeFormat.prototype, Symbol.toStringTag).writable, false);
shouldBe(Object.getOwnPropertyDescriptor(Intl.RelativeTimeFormat.prototype, Symbol.toStringTag).enumerable, false);
shouldBe(Object.getOwnPropertyDescriptor(Intl.RelativeTimeFormat.prototype, Symbol.toStringTag).configurable, true);
shouldThrow(() => new Intl.RelativeTimeFormat('en', { localeMatcher: { toString() { throw new Error(); } } }), Error);
shouldThrow(() => new Intl.RelativeTimeFormat('en', { localeMatcher:'bad' }), RangeError);
shouldNotThrow(() => new Intl.RelativeTimeFormat('en', { localeMatcher:'lookup' }));
shouldNotThrow(() => new Intl.RelativeTimeFormat('en', { localeMatcher:'best fit' }));
const defaultRTF = new Intl.RelativeTimeFormat('en');
shouldBe(Intl.RelativeTimeFormat.prototype.resolvedOptions.length, 0);
shouldThrow(() => Intl.RelativeTimeFormat.prototype.resolvedOptions.call(5), TypeError);
shouldThrow(() => Intl.RelativeTimeFormat.prototype.resolvedOptions.call({}), TypeError);
shouldBe(defaultRTF.resolvedOptions() instanceof Object, true);
shouldBe(defaultRTF.resolvedOptions() !== defaultRTF.resolvedOptions(), true);
shouldBe(defaultRTF.resolvedOptions().locale, 'en');
shouldBe(defaultRTF.resolvedOptions().style, 'long');
shouldBe(defaultRTF.resolvedOptions().numeric, 'always');
shouldBe(defaultRTF.resolvedOptions().numberingSystem, 'latn');
shouldBe(new Intl.RelativeTimeFormat('en-u-nu-hanidec').resolvedOptions().locale, 'en-u-nu-hanidec');
shouldBe(new Intl.RelativeTimeFormat('en-u-nu-hanidec', { numberingSystem: 'gujr' }).resolvedOptions().locale, 'en');
shouldBe(new Intl.RelativeTimeFormat('en', { numberingSystem: 'hanidec' }).resolvedOptions().locale, 'en');
shouldBe(new Intl.RelativeTimeFormat('en-u-ca-chinese').resolvedOptions().locale, 'en');
shouldThrow(() => new Intl.RelativeTimeFormat('en', { style: { toString() { throw new Error(); } } }), Error);
shouldThrow(() => new Intl.RelativeTimeFormat('en', { style: 'bad' }), RangeError);
shouldBe(new Intl.RelativeTimeFormat('en', { style: 'long' }).resolvedOptions().style, 'long');
shouldBe(new Intl.RelativeTimeFormat('en', { style: 'short' }).resolvedOptions().style, 'short');
shouldBe(new Intl.RelativeTimeFormat('en', { style: 'narrow' }).resolvedOptions().style, 'narrow');
shouldThrow(() => new Intl.RelativeTimeFormat('en', { numeric: { toString() { throw new Error(); } } }), Error);
shouldThrow(() => new Intl.RelativeTimeFormat('en', { numeric: 'bad' }), RangeError);
shouldBe(new Intl.RelativeTimeFormat('en', { numeric: 'always' }).resolvedOptions().numeric, 'always');
shouldBe(new Intl.RelativeTimeFormat('en', { numeric: 'auto' }).resolvedOptions().numeric, 'auto');
const numberingSystems = [
'arab', 'arabext', 'bali', 'beng', 'deva', 'fullwide', 'gujr', 'guru',
'hanidec', 'khmr', 'knda', 'laoo', 'latn', 'limb', 'mlym', 'mong', 'mymr',
'orya', 'tamldec', 'telu', 'thai', 'tibt'
]
for (let numberingSystem of numberingSystems) {
shouldBe(new Intl.RelativeTimeFormat('en', { numberingSystem }).resolvedOptions().numberingSystem, numberingSystem);
shouldBe(new Intl.RelativeTimeFormat(`en-u-nu-${numberingSystem}`).resolvedOptions().numberingSystem, numberingSystem);
}
shouldBe(Intl.RelativeTimeFormat.prototype.format.length, 2);
shouldThrow(() => Intl.RelativeTimeFormat.prototype.format.call({}, 3, 'days'), TypeError);
shouldThrow(() => defaultRTF.format(Symbol(), 'days'), TypeError);
shouldThrow(() => defaultRTF.format(3, Symbol()), TypeError);
shouldThrow(() => defaultRTF.format(Infinity, 'days'), RangeError);
shouldThrow(() => defaultRTF.format(3, 'centuries'), RangeError);
const units = ['second', 'minute', 'hour', 'day', 'week', 'month', 'year'];
if (icuVersion >= 63)
units.push('quarter');
for (let unit of units) {
shouldBe(defaultRTF.format(10, unit), defaultRTF.format(10, `${unit}s`));
shouldBe([`in 10,000.5 ${unit}s`, `in 10000.5 ${unit}s`].includes(defaultRTF.format(10000.5, unit)), true);
shouldBe(defaultRTF.format(10, unit), `in 10 ${unit}s`);
shouldBe(defaultRTF.format(0, unit), `in 0 ${unit}s`);
shouldBe(defaultRTF.format(-10, unit), `10 ${unit}s ago`);
shouldBe([`10,000.5 ${unit}s ago`, `10000.5 ${unit}s ago`].includes(defaultRTF.format(-10000.5, unit)), true);
shouldBeForICUVersion(64, defaultRTF.format(1, unit), `in 1 ${unit}`);
shouldBeForICUVersion(63, defaultRTF.format(-0, unit), `0 ${unit}s ago`);
shouldBeForICUVersion(64, defaultRTF.format(-1, unit), `1 ${unit} ago`);
}
shouldBe(new Intl.RelativeTimeFormat('en', { style: 'short' }).format(10, 'second'), 'in 10 sec.');
shouldBe(new Intl.RelativeTimeFormat('ru', { style: 'short' }).format(10, 'second'), 'через 10 сек.');
shouldBe(new Intl.RelativeTimeFormat('en', { style: 'narrow' }).format(10, 'second'), 'in 10 sec.');
shouldBe(new Intl.RelativeTimeFormat('ru', { style: 'narrow' }).format(10, 'second'), '+10 с');
shouldBe(new Intl.RelativeTimeFormat('en', { numeric: 'auto' }).format(0, 'second'), 'now');
shouldBe(new Intl.RelativeTimeFormat('ja', { numeric: 'auto' }).format(0, 'second'), '今');
shouldBe(new Intl.RelativeTimeFormat('en', { numeric: 'auto' }).format(0, 'day'), 'today');
shouldBe(new Intl.RelativeTimeFormat('ja', { numeric: 'auto' }).format(0, 'day'), '今日');
shouldBe(new Intl.RelativeTimeFormat('en', { numeric: 'auto' }).format(0, 'year'), 'this year');
shouldBe(new Intl.RelativeTimeFormat('ja', { numeric: 'auto' }).format(0, 'year'), '今年');
shouldBe(new Intl.RelativeTimeFormat('en', { numberingSystem: 'thai' }).format(-10, 'hour'), '๑๐ hours ago');
shouldBe(new Intl.RelativeTimeFormat('en-u-nu-thai').format(-10, 'hour'), '๑๐ hours ago');
shouldBe(new Intl.RelativeTimeFormat('ko', { numberingSystem: 'hanidec' }).format(-10, 'hour'), '一〇시간 전');
shouldBe(new Intl.RelativeTimeFormat('ko-u-nu-hanidec').format(-10, 'hour'), '一〇시간 전');
shouldBe(Intl.RelativeTimeFormat.prototype.formatToParts.length, 2);
shouldThrow(() => Intl.RelativeTimeFormat.prototype.formatToParts.call({}, 3, 'days'), TypeError);
shouldThrow(() => defaultRTF.formatToParts(Symbol(), 'days'), TypeError);
shouldThrow(() => defaultRTF.formatToParts(3, Symbol()), TypeError);
shouldThrow(() => defaultRTF.formatToParts(Infinity, 'days'), RangeError);
shouldThrow(() => defaultRTF.formatToParts(3, 'centuries'), RangeError);
for (let unit of units) {
shouldBe(JSON.stringify(defaultRTF.formatToParts(10, unit)), JSON.stringify(defaultRTF.formatToParts(10, `${unit}s`)));
const concatenateValues = (parts) => parts.map(part => part.value).join('');
shouldBe([`in 10,000.5 ${unit}s`, `in 10000.5 ${unit}s`].includes(concatenateValues(defaultRTF.formatToParts(10000.5, unit))), true);
shouldBe(concatenateValues(defaultRTF.formatToParts(10, unit)), `in 10 ${unit}s`);
shouldBe(concatenateValues(defaultRTF.formatToParts(0, unit)), `in 0 ${unit}s`);
shouldBe(concatenateValues(defaultRTF.formatToParts(-10, unit)), `10 ${unit}s ago`);
shouldBe([`10,000.5 ${unit}s ago`, `10000.5 ${unit}s ago`].includes(concatenateValues(defaultRTF.formatToParts(-10000.5, unit))), true);
shouldBeForICUVersion(64, concatenateValues(defaultRTF.formatToParts(1, unit)), `in 1 ${unit}`);
shouldBeForICUVersion(63, concatenateValues(defaultRTF.formatToParts(-0, unit)), `0 ${unit}s ago`);
shouldBeForICUVersion(64, concatenateValues(defaultRTF.formatToParts(-1, unit)), `1 ${unit} ago`);
}
if ($vm.icuVersion() >= 64) {
shouldBe(
JSON.stringify(defaultRTF.formatToParts(10000.5, 'day')),
JSON.stringify([
{ type: 'literal', value: 'in ' },
{ type: 'integer', value: '10', unit: 'day' },
{ type: 'group', value: ',', unit: 'day' },
{ type: 'integer', value: '000', unit: 'day' },
{ type: 'decimal', value: '.', unit: 'day' },
{ type: 'fraction', value: '5', unit: 'day' },
{ type: 'literal', value: ' days' }
])
);
}
shouldBe(
JSON.stringify(new Intl.RelativeTimeFormat('sw').formatToParts(10, 'year')),
JSON.stringify([
{ type: 'literal', value: 'baada ya miaka ' },
{ type: 'integer', value: '10', unit: 'year' },
])
);
shouldBe(
JSON.stringify(new Intl.RelativeTimeFormat('ru', { style: 'narrow' }).formatToParts(10, 'second')),
JSON.stringify([
{ type: 'literal', value: '+' },
{ type: 'integer', value: '10', unit: 'second' },
{ type: 'literal', value: ' с' }
])
);
shouldBe(
JSON.stringify(new Intl.RelativeTimeFormat('ja', { numeric: 'auto' }).formatToParts(0, 'week')),
JSON.stringify([
{ type: 'literal', value: '今週' }
])
);
shouldBe(
JSON.stringify(new Intl.RelativeTimeFormat('ko', { numberingSystem: 'hanidec' }).formatToParts(-10, 'hour')),
JSON.stringify([
{ type: 'integer', value: '一〇', unit: 'hour' },
{ type: 'literal', value: '시간 전' }
])
);