mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
I18n tests (#20306)
* Add unit-tests for i18n engine * tests for angular wrappers under i18n engine * split to packages in accordance to specific library * split to packages depending on tech and environment * make env modules names consistent * remove intl polyfilling * move laoder to root, i18n folder to core * Add unit-tests for i18n engine * tests for angular wrappers under i18n engine * rebase on split package, injectIntl component * eslint fixes * use method names for checking, test i18n-values in directive * use real html content while testing directive * add test for angular directive without values
This commit is contained in:
parent
86a9e46b4a
commit
1e7ce26303
17 changed files with 1719 additions and 1 deletions
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"a.b.c": "bar",
|
||||
"d.e.f": "foo"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"a.b.c": "foo",
|
||||
"d.e.f": "bar"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"a.b.c.custom": "foo.custom",
|
||||
"d.e.f.custom": "bar.custom"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
test: 'test' // JSON5 test
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"test": "test"
|
||||
}
|
5
packages/kbn-i18n/src/__snapshots__/loader.test.js.snap
Normal file
5
packages/kbn-i18n/src/__snapshots__/loader.test.js.snap
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`I18n loader registerTranslationFile should throw error if path to translation file is not an absolute 1`] = `"Paths to translation files must be absolute. Got relative path: \\"./en.json\\""`;
|
||||
|
||||
exports[`I18n loader registerTranslationFile should throw error if path to translation file is not specified 1`] = `"Path must be a string. Received undefined"`;
|
77
packages/kbn-i18n/src/angular/directive.test.js
Normal file
77
packages/kbn-i18n/src/angular/directive.test.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import angular from 'angular';
|
||||
import 'angular-mocks';
|
||||
import { i18nDirective } from './directive';
|
||||
import { i18nProvider } from './provider';
|
||||
|
||||
angular
|
||||
.module('app', [])
|
||||
.provider('i18n', i18nProvider)
|
||||
.directive('i18nId', i18nDirective);
|
||||
|
||||
describe('i18nDirective', () => {
|
||||
let compile;
|
||||
let scope;
|
||||
|
||||
beforeEach(angular.mock.module('app'));
|
||||
beforeEach(
|
||||
angular.mock.inject(($compile, $rootScope) => {
|
||||
compile = $compile;
|
||||
scope = $rootScope.$new();
|
||||
})
|
||||
);
|
||||
|
||||
it('inserts correct translation html content', () => {
|
||||
const id = 'id';
|
||||
const defaultMessage = 'default-message';
|
||||
|
||||
const element = angular.element(
|
||||
`<div
|
||||
i18n-id="${id}"
|
||||
i18n-default-message="${defaultMessage}"
|
||||
/>`
|
||||
);
|
||||
|
||||
compile(element)(scope);
|
||||
scope.$digest();
|
||||
|
||||
expect(element.html()).toEqual(defaultMessage);
|
||||
});
|
||||
|
||||
it('inserts correct translation html content with values', () => {
|
||||
const id = 'id';
|
||||
const defaultMessage = 'default-message {word}';
|
||||
const compiledContent = 'default-message word';
|
||||
|
||||
const element = angular.element(
|
||||
`<div
|
||||
i18n-id="${id}"
|
||||
i18n-default-message="${defaultMessage}"
|
||||
i18n-values="{ word: 'word' }"
|
||||
/>`
|
||||
);
|
||||
|
||||
compile(element)(scope);
|
||||
scope.$digest();
|
||||
|
||||
expect(element.html()).toEqual(compiledContent);
|
||||
});
|
||||
});
|
59
packages/kbn-i18n/src/angular/filter.test.js
Normal file
59
packages/kbn-i18n/src/angular/filter.test.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import angular from 'angular';
|
||||
import 'angular-mocks';
|
||||
import { i18nProvider } from './provider';
|
||||
import { i18nFilter } from './filter';
|
||||
import * as i18n from '../core/i18n';
|
||||
|
||||
jest.mock('../core/i18n', () => ({
|
||||
translate: jest.fn().mockImplementation(() => 'translation'),
|
||||
}));
|
||||
|
||||
angular
|
||||
.module('app', [])
|
||||
.provider('i18n', i18nProvider)
|
||||
.filter('i18n', i18nFilter);
|
||||
|
||||
describe('i18nFilter', () => {
|
||||
let filter;
|
||||
|
||||
beforeEach(angular.mock.module('app'));
|
||||
beforeEach(
|
||||
angular.mock.inject(i18nFilter => {
|
||||
filter = i18nFilter;
|
||||
})
|
||||
);
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('provides wrapper around i18n engine', () => {
|
||||
const id = 'id';
|
||||
const defaultMessage = 'default-message';
|
||||
const values = {};
|
||||
|
||||
const result = filter(id, { defaultMessage, values });
|
||||
|
||||
expect(i18n.translate).toHaveBeenCalledTimes(1);
|
||||
expect(i18n.translate).toHaveBeenCalledWith(id, { defaultMessage, values });
|
||||
expect(result).toEqual('translation');
|
||||
});
|
||||
});
|
57
packages/kbn-i18n/src/angular/provider.test.js
Normal file
57
packages/kbn-i18n/src/angular/provider.test.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import angular from 'angular';
|
||||
import 'angular-mocks';
|
||||
import { i18nProvider } from './provider';
|
||||
import * as i18n from '../core/i18n';
|
||||
|
||||
angular.module('app', []).provider('i18n', i18nProvider);
|
||||
|
||||
describe('i18nProvider', () => {
|
||||
let provider;
|
||||
let service;
|
||||
|
||||
beforeEach(
|
||||
angular.mock.module('app', [
|
||||
'i18nProvider',
|
||||
i18n => {
|
||||
service = i18n;
|
||||
},
|
||||
])
|
||||
);
|
||||
beforeEach(
|
||||
angular.mock.inject(i18n => {
|
||||
provider = i18n;
|
||||
})
|
||||
);
|
||||
|
||||
it('provides wrapper around i18n engine', () => {
|
||||
expect(provider).toEqual(i18n.translate);
|
||||
});
|
||||
|
||||
it('provides service wrapper around i18n engine', () => {
|
||||
const serviceMethodNames = Object.keys(service);
|
||||
const pluginMethodNames = Object.keys(i18n);
|
||||
|
||||
expect([...serviceMethodNames, 'translate'].sort()).toEqual(
|
||||
[...pluginMethodNames, '$get'].sort()
|
||||
);
|
||||
});
|
||||
});
|
49
packages/kbn-i18n/src/core/__snapshots__/i18n.test.js.snap
Normal file
49
packages/kbn-i18n/src/core/__snapshots__/i18n.test.js.snap
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`I18n engine addMessages should throw error if locale is not specified or empty 1`] = `"[I18n] A \`locale\` must be a non-empty string to add messages."`;
|
||||
|
||||
exports[`I18n engine addMessages should throw error if locale is not specified or empty 2`] = `"[I18n] A \`locale\` must be a non-empty string to add messages."`;
|
||||
|
||||
exports[`I18n engine addMessages should throw error if locale specified in messages is different from one provided as second argument 1`] = `"[I18n] A \`locale\` in the messages object is different from the one provided as a second argument."`;
|
||||
|
||||
exports[`I18n engine translate should throw error if id is not a non-empty string 1`] = `"[I18n] An \`id\` must be a non-empty string to translate a message."`;
|
||||
|
||||
exports[`I18n engine translate should throw error if id is not a non-empty string 2`] = `"[I18n] An \`id\` must be a non-empty string to translate a message."`;
|
||||
|
||||
exports[`I18n engine translate should throw error if id is not a non-empty string 3`] = `"[I18n] An \`id\` must be a non-empty string to translate a message."`;
|
||||
|
||||
exports[`I18n engine translate should throw error if id is not a non-empty string 4`] = `"[I18n] An \`id\` must be a non-empty string to translate a message."`;
|
||||
|
||||
exports[`I18n engine translate should throw error if id is not a non-empty string 5`] = `"[I18n] An \`id\` must be a non-empty string to translate a message."`;
|
||||
|
||||
exports[`I18n engine translate should throw error if id is not a non-empty string 6`] = `"[I18n] An \`id\` must be a non-empty string to translate a message."`;
|
||||
|
||||
exports[`I18n engine translate should throw error if translation message and defaultMessage are not provided 1`] = `"[I18n] Cannot format message: \\"foo\\". Default message must be provided."`;
|
||||
|
||||
exports[`I18n engine translate should throw error if used format is not specified 1`] = `
|
||||
"[I18n] Error formatting message: \\"a.b.c\\" for locale: \\"en\\".
|
||||
SyntaxError: Expected \\"date\\", \\"number\\", \\"plural\\", \\"select\\", \\"selectordinal\\" or \\"time\\" but \\"f\\" found."
|
||||
`;
|
||||
|
||||
exports[`I18n engine translate should throw error if used format is not specified 2`] = `
|
||||
"[I18n] Error formatting the default message for: \\"d.e.f\\".
|
||||
SyntaxError: Expected \\"date\\", \\"number\\", \\"plural\\", \\"select\\", \\"selectordinal\\" or \\"time\\" but \\"b\\" found."
|
||||
`;
|
||||
|
||||
exports[`I18n engine translate should throw error if wrong context is provided to the translation string 1`] = `
|
||||
"[I18n] Error formatting message: \\"a.b.c\\" for locale: \\"en\\".
|
||||
Error: The intl string context variable 'numPhotos' was not provided to the string 'You have {numPhotos, plural,
|
||||
=0 {no photos.}
|
||||
=1 {one photo.}
|
||||
other {# photos.}
|
||||
}'"
|
||||
`;
|
||||
|
||||
exports[`I18n engine translate should throw error if wrong context is provided to the translation string 2`] = `
|
||||
"[I18n] Error formatting the default message for: \\"d.e.f\\".
|
||||
Error: The intl string context variable 'numPhotos' was not provided to the string 'You have {numPhotos, plural,
|
||||
=0 {no photos.}
|
||||
=1 {one photo.}
|
||||
other {# photos.}
|
||||
}'"
|
||||
`;
|
197
packages/kbn-i18n/src/core/helper.test.js
Normal file
197
packages/kbn-i18n/src/core/helper.test.js
Normal file
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { isString, isObject, hasValues, unique, mergeAll } from './helper';
|
||||
|
||||
describe('I18n helper', () => {
|
||||
describe('isString', () => {
|
||||
test('should return true for string literal', () => {
|
||||
expect(isString('test')).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false for string object', () => {
|
||||
expect(isString(new String('test'))).toBe(false);
|
||||
});
|
||||
|
||||
test('should return false for non-string values', () => {
|
||||
expect(isString(undefined)).toBe(false);
|
||||
expect(isString(null)).toBe(false);
|
||||
expect(isString(0)).toBe(false);
|
||||
expect(isString(true)).toBe(false);
|
||||
expect(isString({})).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isObject', () => {
|
||||
test('should return true for object literal', () => {
|
||||
expect(isObject({})).toBe(true);
|
||||
});
|
||||
|
||||
test('should return true for array literal', () => {
|
||||
expect(isObject([])).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false for primitives', () => {
|
||||
expect(isObject(undefined)).toBe(false);
|
||||
expect(isObject(null)).toBe(false);
|
||||
expect(isObject(0)).toBe(false);
|
||||
expect(isObject(true)).toBe(false);
|
||||
expect(isObject('test')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasValues', () => {
|
||||
test('should return false for empty object', () => {
|
||||
expect(hasValues({})).toBe(false);
|
||||
});
|
||||
|
||||
test('should return true for non-empty object', () => {
|
||||
expect(hasValues({ foo: 'bar' })).toBe(true);
|
||||
});
|
||||
|
||||
test('should throw error for null and undefined', () => {
|
||||
expect(() => hasValues(undefined)).toThrow();
|
||||
expect(() => hasValues(null)).toThrow();
|
||||
});
|
||||
|
||||
test('should return false for number and boolean', () => {
|
||||
expect(hasValues(true)).toBe(false);
|
||||
expect(hasValues(0)).toBe(false);
|
||||
});
|
||||
|
||||
test('should return false for empty string', () => {
|
||||
expect(hasValues('')).toBe(false);
|
||||
});
|
||||
|
||||
test('should return true for non-empty string', () => {
|
||||
expect(hasValues('test')).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false for empty array', () => {
|
||||
expect(hasValues([])).toBe(false);
|
||||
});
|
||||
|
||||
test('should return true for non-empty array', () => {
|
||||
expect(hasValues([1, 2, 3])).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unique', () => {
|
||||
test('should return an array with unique values', () => {
|
||||
expect(unique([1, 2, 7, 2, 6, 7, 1])).toEqual([1, 2, 7, 6]);
|
||||
});
|
||||
|
||||
test('should create a new array', () => {
|
||||
const value = [1, 2, 3];
|
||||
|
||||
expect(unique(value)).toEqual(value);
|
||||
expect(unique(value)).not.toBe(value);
|
||||
});
|
||||
|
||||
test('should filter unique values only by reference', () => {
|
||||
expect(unique([{ foo: 'bar' }, { foo: 'bar' }])).toEqual([{ foo: 'bar' }, { foo: 'bar' }]);
|
||||
|
||||
const value = { foo: 'bar' };
|
||||
|
||||
expect(unique([value, value])).toEqual([value]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mergeAll', () => {
|
||||
test('should throw error for empty arguments', () => {
|
||||
expect(() => mergeAll()).toThrow();
|
||||
});
|
||||
|
||||
test('should merge only objects', () => {
|
||||
expect(mergeAll(undefined, null, true, 5, '5', { foo: 'bar' })).toEqual({
|
||||
foo: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
test('should return the only argument as is', () => {
|
||||
const value = { foo: 'bar' };
|
||||
|
||||
expect(mergeAll(value)).toBe(value);
|
||||
});
|
||||
|
||||
test('should return a deep merge of 2 objects nested objects', () => {
|
||||
expect(
|
||||
mergeAll(
|
||||
{
|
||||
foo: { bar: 3 },
|
||||
array: [
|
||||
{
|
||||
does: 'work',
|
||||
too: [1, 2, 3],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
foo: { baz: 4 },
|
||||
quux: 5,
|
||||
array: [
|
||||
{
|
||||
does: 'work',
|
||||
too: [4, 5, 6],
|
||||
},
|
||||
{
|
||||
really: 'yes',
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
).toEqual({
|
||||
foo: {
|
||||
bar: 3,
|
||||
baz: 4,
|
||||
},
|
||||
array: [
|
||||
{
|
||||
does: 'work',
|
||||
too: [4, 5, 6],
|
||||
},
|
||||
{
|
||||
really: 'yes',
|
||||
},
|
||||
],
|
||||
quux: 5,
|
||||
});
|
||||
});
|
||||
|
||||
test('should override arrays', () => {
|
||||
expect(mergeAll({ foo: [1, 2] }, { foo: [3, 4] })).toEqual({
|
||||
foo: [3, 4],
|
||||
});
|
||||
});
|
||||
|
||||
test('should merge any number of objects', () => {
|
||||
expect(mergeAll({ a: 1 }, { b: 2 }, { c: 3 })).toEqual({
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
});
|
||||
expect(mergeAll({ a: 1 }, { b: 2 }, { c: 3 }, { d: 4 })).toEqual({
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
d: 4,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -73,6 +73,12 @@ export function addMessages(newMessages = {}, locale = newMessages.locale) {
|
|||
throw new Error('[I18n] A `locale` must be a non-empty string to add messages.');
|
||||
}
|
||||
|
||||
if (newMessages.locale && newMessages.locale !== locale) {
|
||||
throw new Error(
|
||||
'[I18n] A `locale` in the messages object is different from the one provided as a second argument.'
|
||||
);
|
||||
}
|
||||
|
||||
const normalizedLocale = normalizeLocale(locale);
|
||||
|
||||
messages[normalizedLocale] = {
|
||||
|
|
774
packages/kbn-i18n/src/core/i18n.test.js
Normal file
774
packages/kbn-i18n/src/core/i18n.test.js
Normal file
|
@ -0,0 +1,774 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
describe('I18n engine', () => {
|
||||
let i18n;
|
||||
|
||||
beforeEach(() => {
|
||||
i18n = require('./i18n');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// isolate modules for every test so that local module state doesn't conflict between tests
|
||||
jest.resetModules();
|
||||
});
|
||||
|
||||
describe('addMessages', () => {
|
||||
test('should throw error if locale is not specified or empty', () => {
|
||||
expect(() => i18n.addMessages({ foo: 'bar' })).toThrowErrorMatchingSnapshot();
|
||||
expect(() => i18n.addMessages({ locale: '' })).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('should throw error if locale specified in messages is different from one provided as second argument', () => {
|
||||
expect(() =>
|
||||
i18n.addMessages({ foo: 'bar', locale: 'en' }, 'ru')
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('should add messages if locale prop is passed as second argument', () => {
|
||||
const locale = 'ru';
|
||||
|
||||
expect(i18n.getMessages()).toEqual({});
|
||||
|
||||
i18n.addMessages({ foo: 'bar' }, locale);
|
||||
|
||||
expect(i18n.getMessages()).toEqual({});
|
||||
|
||||
i18n.setLocale(locale);
|
||||
|
||||
expect(i18n.getMessages()).toEqual({ foo: 'bar' });
|
||||
});
|
||||
|
||||
test('should add messages if locale prop is passed as messages property', () => {
|
||||
const locale = 'ru';
|
||||
|
||||
expect(i18n.getMessages()).toEqual({});
|
||||
|
||||
i18n.addMessages({
|
||||
locale,
|
||||
foo: 'bar',
|
||||
});
|
||||
|
||||
expect(i18n.getMessages()).toEqual({});
|
||||
|
||||
i18n.setLocale(locale);
|
||||
|
||||
expect(i18n.getMessages()).toEqual({
|
||||
foo: 'bar',
|
||||
locale: 'ru',
|
||||
});
|
||||
});
|
||||
|
||||
test('should merge messages with the same locale', () => {
|
||||
const locale = 'ru';
|
||||
|
||||
i18n.setLocale(locale);
|
||||
i18n.addMessages({
|
||||
locale,
|
||||
['a.b.c']: 'foo',
|
||||
});
|
||||
|
||||
expect(i18n.getMessages()).toEqual({
|
||||
locale: 'ru',
|
||||
['a.b.c']: 'foo',
|
||||
});
|
||||
|
||||
i18n.addMessages({
|
||||
locale,
|
||||
['d.e.f']: 'bar',
|
||||
});
|
||||
|
||||
expect(i18n.getMessages()).toEqual({
|
||||
locale: 'ru',
|
||||
['a.b.c']: 'foo',
|
||||
['d.e.f']: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
test('should override messages with the same locale and id', () => {
|
||||
const locale = 'ru';
|
||||
|
||||
i18n.setLocale(locale);
|
||||
i18n.addMessages({
|
||||
locale,
|
||||
['a.b.c']: 'foo',
|
||||
});
|
||||
|
||||
expect(i18n.getMessages()).toEqual({
|
||||
locale: 'ru',
|
||||
['a.b.c']: 'foo',
|
||||
});
|
||||
|
||||
i18n.addMessages({
|
||||
locale,
|
||||
['a.b.c']: 'bar',
|
||||
});
|
||||
|
||||
expect(i18n.getMessages()).toEqual({
|
||||
locale: 'ru',
|
||||
['a.b.c']: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
test('should add messages with normalized passed locale', () => {
|
||||
const locale = 'en-us';
|
||||
i18n.setLocale(locale);
|
||||
|
||||
i18n.addMessages(
|
||||
{
|
||||
['a.b.c']: 'bar',
|
||||
},
|
||||
'en_US'
|
||||
);
|
||||
|
||||
expect(i18n.getLocale()).toBe(locale);
|
||||
expect(i18n.getMessages()).toEqual({
|
||||
['a.b.c']: 'bar',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMessages', () => {
|
||||
test('should return messages for the current language', () => {
|
||||
i18n.addMessages({
|
||||
locale: 'ru',
|
||||
foo: 'bar',
|
||||
});
|
||||
i18n.addMessages({
|
||||
locale: 'en',
|
||||
bar: 'foo',
|
||||
});
|
||||
|
||||
i18n.setLocale('ru');
|
||||
expect(i18n.getMessages()).toEqual({
|
||||
locale: 'ru',
|
||||
foo: 'bar',
|
||||
});
|
||||
|
||||
i18n.setLocale('en');
|
||||
expect(i18n.getMessages()).toEqual({
|
||||
locale: 'en',
|
||||
bar: 'foo',
|
||||
});
|
||||
});
|
||||
|
||||
test('should return an empty object if messages for current locale are not specified', () => {
|
||||
expect(i18n.getMessages()).toEqual({});
|
||||
|
||||
i18n.setLocale('fr');
|
||||
expect(i18n.getMessages()).toEqual({});
|
||||
|
||||
i18n.setLocale('en');
|
||||
expect(i18n.getMessages()).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setLocale', () => {
|
||||
test('should throw error if locale is not a non-empty string', () => {
|
||||
expect(() => i18n.setLocale(undefined)).toThrow();
|
||||
expect(() => i18n.setLocale(null)).toThrow();
|
||||
expect(() => i18n.setLocale(true)).toThrow();
|
||||
expect(() => i18n.setLocale(5)).toThrow();
|
||||
expect(() => i18n.setLocale({})).toThrow();
|
||||
expect(() => i18n.setLocale('')).toThrow();
|
||||
});
|
||||
|
||||
test('should update current locale', () => {
|
||||
expect(i18n.getLocale()).not.toBe('foo');
|
||||
i18n.setLocale('foo');
|
||||
expect(i18n.getLocale()).toBe('foo');
|
||||
});
|
||||
|
||||
test('should normalize passed locale', () => {
|
||||
i18n.setLocale('en_US');
|
||||
expect(i18n.getLocale()).toBe('en-us');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLocale', () => {
|
||||
test('should return "en" locale by default', () => {
|
||||
expect(i18n.getLocale()).toBe('en');
|
||||
});
|
||||
|
||||
test('should return updated locale', () => {
|
||||
i18n.setLocale('foo');
|
||||
expect(i18n.getLocale()).toBe('foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('setDefaultLocale', () => {
|
||||
test('should throw error if locale is not a non-empty string', () => {
|
||||
expect(() => i18n.setDefaultLocale(undefined)).toThrow();
|
||||
expect(() => i18n.setDefaultLocale(null)).toThrow();
|
||||
expect(() => i18n.setDefaultLocale(true)).toThrow();
|
||||
expect(() => i18n.setDefaultLocale(5)).toThrow();
|
||||
expect(() => i18n.setDefaultLocale({})).toThrow();
|
||||
expect(() => i18n.setDefaultLocale('')).toThrow();
|
||||
});
|
||||
|
||||
test('should update the default locale', () => {
|
||||
expect(i18n.getDefaultLocale()).not.toBe('foo');
|
||||
i18n.setDefaultLocale('foo');
|
||||
expect(i18n.getDefaultLocale()).toBe('foo');
|
||||
});
|
||||
|
||||
test('should normalize passed locale', () => {
|
||||
i18n.setDefaultLocale('en_US');
|
||||
expect(i18n.getDefaultLocale()).toBe('en-us');
|
||||
});
|
||||
|
||||
test('should set "en" locale as default for IntlMessageFormat and IntlRelativeFormat', () => {
|
||||
const IntlMessageFormat = require('intl-messageformat');
|
||||
const IntlRelativeFormat = require('intl-relativeformat');
|
||||
|
||||
expect(IntlMessageFormat.defaultLocale).toBe('en');
|
||||
expect(IntlRelativeFormat.defaultLocale).toBe('en');
|
||||
});
|
||||
|
||||
test('should update defaultLocale for IntlMessageFormat and IntlRelativeFormat', () => {
|
||||
const IntlMessageFormat = require('intl-messageformat');
|
||||
const IntlRelativeFormat = require('intl-relativeformat');
|
||||
|
||||
i18n.setDefaultLocale('foo');
|
||||
|
||||
expect(IntlMessageFormat.defaultLocale).toBe('foo');
|
||||
expect(IntlRelativeFormat.defaultLocale).toBe('foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDefaultLocale', () => {
|
||||
test('should return "en" locale by default', () => {
|
||||
expect(i18n.getDefaultLocale()).toBe('en');
|
||||
});
|
||||
|
||||
test('should return updated locale', () => {
|
||||
i18n.setDefaultLocale('foo');
|
||||
expect(i18n.getDefaultLocale()).toBe('foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('setFormats', () => {
|
||||
test('should throw error if formats parameter is not a non-empty object', () => {
|
||||
expect(() => i18n.setFormats(undefined)).toThrow();
|
||||
expect(() => i18n.setFormats(null)).toThrow();
|
||||
expect(() => i18n.setFormats(true)).toThrow();
|
||||
expect(() => i18n.setFormats(5)).toThrow();
|
||||
expect(() => i18n.setFormats('foo')).toThrow();
|
||||
expect(() => i18n.setFormats({})).toThrow();
|
||||
});
|
||||
|
||||
test('should merge current formats with a passed formats', () => {
|
||||
expect(i18n.getFormats().date.short).not.toEqual({
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
});
|
||||
|
||||
i18n.setFormats({
|
||||
date: {
|
||||
short: {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(i18n.getFormats().date.short).toEqual({
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
});
|
||||
|
||||
i18n.setFormats({
|
||||
date: {
|
||||
short: {
|
||||
month: 'long',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(i18n.getFormats().date.short).toEqual({
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFormats', () => {
|
||||
test('should return "en" formats by default', () => {
|
||||
const { formats } = require('./formats');
|
||||
|
||||
expect(i18n.getFormats()).toEqual(formats);
|
||||
});
|
||||
|
||||
test('should return updated formats', () => {
|
||||
const { formats } = require('./formats');
|
||||
|
||||
i18n.setFormats({
|
||||
foo: 'bar',
|
||||
});
|
||||
|
||||
expect(i18n.getFormats()).toEqual({
|
||||
...formats,
|
||||
foo: 'bar',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRegisteredLocales', () => {
|
||||
test('should return empty array by default', () => {
|
||||
expect(i18n.getRegisteredLocales()).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return array of registered locales', () => {
|
||||
i18n.addMessages({
|
||||
locale: 'en',
|
||||
});
|
||||
|
||||
expect(i18n.getRegisteredLocales()).toEqual(['en']);
|
||||
|
||||
i18n.addMessages({
|
||||
locale: 'ru',
|
||||
});
|
||||
|
||||
expect(i18n.getRegisteredLocales()).toContain('en', 'ru');
|
||||
expect(i18n.getRegisteredLocales().length).toBe(2);
|
||||
|
||||
i18n.addMessages({
|
||||
locale: 'fr',
|
||||
});
|
||||
|
||||
expect(i18n.getRegisteredLocales()).toContain('en', 'ru', 'fr');
|
||||
expect(i18n.getRegisteredLocales().length).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('translate', () => {
|
||||
test('should throw error if id is not a non-empty string', () => {
|
||||
expect(() => i18n.translate(undefined)).toThrowErrorMatchingSnapshot();
|
||||
expect(() => i18n.translate(null)).toThrowErrorMatchingSnapshot();
|
||||
expect(() => i18n.translate(true)).toThrowErrorMatchingSnapshot();
|
||||
expect(() => i18n.translate(5)).toThrowErrorMatchingSnapshot();
|
||||
expect(() => i18n.translate({})).toThrowErrorMatchingSnapshot();
|
||||
expect(() => i18n.translate('')).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('should throw error if translation message and defaultMessage are not provided', () => {
|
||||
expect(() => i18n.translate('foo')).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('should return message as is if values are not provided', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
['a.b.c']: 'foo',
|
||||
});
|
||||
|
||||
expect(i18n.translate('a.b.c')).toBe('foo');
|
||||
});
|
||||
|
||||
test('should return default message as is if values are not provided', () => {
|
||||
expect(i18n.translate('a.b.c', { defaultMessage: 'foo' })).toBe('foo');
|
||||
});
|
||||
|
||||
test('should not return defaultMessage as is if values are provided', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
['a.b.c']: 'foo',
|
||||
});
|
||||
expect(i18n.translate('a.b.c', { defaultMessage: 'bar' })).toBe('foo');
|
||||
});
|
||||
|
||||
test('should interpolate variables', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
['a.b.c']: 'foo {a}, {b}, {c} bar',
|
||||
['d.e.f']: '{foo}',
|
||||
});
|
||||
|
||||
expect(
|
||||
i18n.translate('a.b.c', {
|
||||
values: { a: 1, b: 2, c: 3 },
|
||||
})
|
||||
).toBe('foo 1, 2, 3 bar');
|
||||
|
||||
expect(i18n.translate('d.e.f', { values: { foo: 'bar' } })).toBe('bar');
|
||||
});
|
||||
|
||||
test('should interpolate variables for default messages', () => {
|
||||
expect(
|
||||
i18n.translate('a.b.c', {
|
||||
defaultMessage: 'foo {a}, {b}, {c} bar',
|
||||
values: { a: 1, b: 2, c: 3 },
|
||||
})
|
||||
).toBe('foo 1, 2, 3 bar');
|
||||
});
|
||||
|
||||
test('should format pluralized messages', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
['a.b.c']: `You have {numPhotos, plural,
|
||||
=0 {no photos.}
|
||||
=1 {one photo.}
|
||||
other {# photos.}
|
||||
}`,
|
||||
});
|
||||
|
||||
expect(i18n.translate('a.b.c', { values: { numPhotos: 0 } })).toBe('You have no photos.');
|
||||
expect(i18n.translate('a.b.c', { values: { numPhotos: 1 } })).toBe('You have one photo.');
|
||||
expect(i18n.translate('a.b.c', { values: { numPhotos: 1000 } })).toBe(
|
||||
'You have 1,000 photos.'
|
||||
);
|
||||
});
|
||||
|
||||
test('should format pluralized default messages', () => {
|
||||
i18n.setDefaultLocale('en');
|
||||
|
||||
expect(
|
||||
i18n.translate('a.b.c', {
|
||||
values: { numPhotos: 0 },
|
||||
defaultMessage: `You have {numPhotos, plural,
|
||||
=0 {no photos.}
|
||||
=1 {one photo.}
|
||||
other {# photos.}
|
||||
}`,
|
||||
})
|
||||
).toBe('You have no photos.');
|
||||
|
||||
expect(
|
||||
i18n.translate('a.b.c', {
|
||||
values: { numPhotos: 1 },
|
||||
defaultMessage: `You have {numPhotos, plural,
|
||||
=0 {no photos.}
|
||||
=1 {one photo.}
|
||||
other {# photos.}
|
||||
}`,
|
||||
})
|
||||
).toBe('You have one photo.');
|
||||
|
||||
expect(
|
||||
i18n.translate('a.b.c', {
|
||||
values: { numPhotos: 1000 },
|
||||
defaultMessage: `You have {numPhotos, plural,
|
||||
=0 {no photos.}
|
||||
=1 {one photo.}
|
||||
other {# photos.}
|
||||
}`,
|
||||
})
|
||||
).toBe('You have 1,000 photos.');
|
||||
});
|
||||
|
||||
test('should throw error if wrong context is provided to the translation string', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
['a.b.c']: `You have {numPhotos, plural,
|
||||
=0 {no photos.}
|
||||
=1 {one photo.}
|
||||
other {# photos.}
|
||||
}`,
|
||||
});
|
||||
i18n.setDefaultLocale('en');
|
||||
|
||||
expect(() => i18n.translate('a.b.c', { values: { foo: 0 } })).toThrowErrorMatchingSnapshot();
|
||||
|
||||
expect(() =>
|
||||
i18n.translate('d.e.f', {
|
||||
values: { bar: 1000 },
|
||||
defaultMessage: `You have {numPhotos, plural,
|
||||
=0 {no photos.}
|
||||
=1 {one photo.}
|
||||
other {# photos.}
|
||||
}`,
|
||||
})
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('should format messages with percent formatter', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
['a.b.c']: 'Result: {result, number, percent}',
|
||||
});
|
||||
i18n.setDefaultLocale('en');
|
||||
|
||||
expect(i18n.translate('a.b.c', { values: { result: 0.15 } })).toBe('Result: 15%');
|
||||
|
||||
expect(
|
||||
i18n.translate('d.e.f', {
|
||||
values: { result: 0.15 },
|
||||
defaultMessage: 'Result: {result, number, percent}',
|
||||
})
|
||||
).toBe('Result: 15%');
|
||||
});
|
||||
|
||||
test('should format messages with date formatter', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
['a.short']: 'Sale begins {start, date, short}',
|
||||
['a.medium']: 'Sale begins {start, date, medium}',
|
||||
['a.long']: 'Sale begins {start, date, long}',
|
||||
['a.full']: 'Sale begins {start, date, full}',
|
||||
});
|
||||
|
||||
expect(
|
||||
i18n.translate('a.short', {
|
||||
values: { start: new Date(2018, 5, 20) },
|
||||
})
|
||||
).toBe('Sale begins 6/20/18');
|
||||
|
||||
expect(
|
||||
i18n.translate('a.medium', {
|
||||
values: { start: new Date(2018, 5, 20) },
|
||||
})
|
||||
).toBe('Sale begins Jun 20, 2018');
|
||||
|
||||
expect(
|
||||
i18n.translate('a.long', {
|
||||
values: { start: new Date(2018, 5, 20) },
|
||||
})
|
||||
).toBe('Sale begins June 20, 2018');
|
||||
|
||||
expect(
|
||||
i18n.translate('a.full', {
|
||||
values: { start: new Date(2018, 5, 20) },
|
||||
})
|
||||
).toBe('Sale begins Wednesday, June 20, 2018');
|
||||
});
|
||||
|
||||
test('should format default messages with date formatter', () => {
|
||||
i18n.setDefaultLocale('en');
|
||||
|
||||
expect(
|
||||
i18n.translate('foo', {
|
||||
defaultMessage: 'Sale begins {start, date, short}',
|
||||
values: { start: new Date(2018, 5, 20) },
|
||||
})
|
||||
).toBe('Sale begins 6/20/18');
|
||||
|
||||
expect(
|
||||
i18n.translate('foo', {
|
||||
defaultMessage: 'Sale begins {start, date, medium}',
|
||||
values: { start: new Date(2018, 5, 20) },
|
||||
})
|
||||
).toBe('Sale begins Jun 20, 2018');
|
||||
|
||||
expect(
|
||||
i18n.translate('foo', {
|
||||
defaultMessage: 'Sale begins {start, date, long}',
|
||||
values: { start: new Date(2018, 5, 20) },
|
||||
})
|
||||
).toBe('Sale begins June 20, 2018');
|
||||
|
||||
expect(
|
||||
i18n.translate('foo', {
|
||||
defaultMessage: 'Sale begins {start, date, full}',
|
||||
values: { start: new Date(2018, 5, 20) },
|
||||
})
|
||||
).toBe('Sale begins Wednesday, June 20, 2018');
|
||||
});
|
||||
|
||||
test('should format messages with time formatter', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
['a.short']: 'Coupon expires at {expires, time, short}',
|
||||
['a.medium']: 'Coupon expires at {expires, time, medium}',
|
||||
});
|
||||
|
||||
expect(
|
||||
i18n.translate('a.short', {
|
||||
values: { expires: new Date(2018, 5, 20, 18, 40, 30, 50) },
|
||||
})
|
||||
).toBe('Coupon expires at 6:40 PM');
|
||||
|
||||
expect(
|
||||
i18n.translate('a.medium', {
|
||||
values: { expires: new Date(2018, 5, 20, 18, 40, 30, 50) },
|
||||
})
|
||||
).toBe('Coupon expires at 6:40:30 PM');
|
||||
});
|
||||
|
||||
test('should format default messages with time formatter', () => {
|
||||
i18n.setDefaultLocale('en');
|
||||
|
||||
expect(
|
||||
i18n.translate('foo', {
|
||||
defaultMessage: 'Coupon expires at {expires, time, short}',
|
||||
values: { expires: new Date(2018, 5, 20, 18, 40, 30, 50) },
|
||||
})
|
||||
).toBe('Coupon expires at 6:40 PM');
|
||||
|
||||
expect(
|
||||
i18n.translate('foo', {
|
||||
defaultMessage: 'Coupon expires at {expires, time, medium}',
|
||||
values: { expires: new Date(2018, 5, 20, 18, 40, 30, 50) },
|
||||
})
|
||||
).toBe('Coupon expires at 6:40:30 PM');
|
||||
});
|
||||
|
||||
test('should format message with a custom format', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
formats: {
|
||||
number: {
|
||||
usd: { style: 'currency', currency: 'USD' },
|
||||
},
|
||||
},
|
||||
['a.b.c']: 'Your total is {total, number, usd}',
|
||||
['d.e.f']: 'Your total is {total, number, eur}',
|
||||
});
|
||||
|
||||
expect(i18n.translate('a.b.c', { values: { total: 1000 } })).toBe('Your total is $1,000.00');
|
||||
|
||||
i18n.setFormats({
|
||||
number: {
|
||||
eur: { style: 'currency', currency: 'EUR' },
|
||||
},
|
||||
});
|
||||
|
||||
expect(i18n.translate('a.b.c', { values: { total: 1000 } })).toBe('Your total is $1,000.00');
|
||||
|
||||
expect(i18n.translate('d.e.f', { values: { total: 1000 } })).toBe('Your total is €1,000.00');
|
||||
});
|
||||
|
||||
test('should format default message with a custom format', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
formats: {
|
||||
number: {
|
||||
usd: { style: 'currency', currency: 'USD' },
|
||||
},
|
||||
},
|
||||
});
|
||||
i18n.setDefaultLocale('en');
|
||||
|
||||
expect(
|
||||
i18n.translate('a.b.c', {
|
||||
values: { total: 1000 },
|
||||
defaultMessage: 'Your total is {total, number, usd}',
|
||||
})
|
||||
).toBe('Your total is $1,000.00');
|
||||
|
||||
i18n.setFormats({
|
||||
number: {
|
||||
eur: { style: 'currency', currency: 'EUR' },
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
i18n.translate('a.b.c', {
|
||||
values: { total: 1000 },
|
||||
defaultMessage: 'Your total is {total, number, usd}',
|
||||
})
|
||||
).toBe('Your total is $1,000.00');
|
||||
|
||||
expect(
|
||||
i18n.translate('d.e.f', {
|
||||
values: { total: 1000 },
|
||||
defaultMessage: 'Your total is {total, number, eur}',
|
||||
})
|
||||
).toBe('Your total is €1,000.00');
|
||||
});
|
||||
|
||||
test('should use default format if passed format option is not specified', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
['a.b.c']: 'Your total is {total, number, usd}',
|
||||
});
|
||||
i18n.setDefaultLocale('en');
|
||||
|
||||
expect(i18n.translate('a.b.c', { values: { total: 1000 } })).toBe('Your total is 1,000');
|
||||
|
||||
expect(
|
||||
i18n.translate('d.e.f', {
|
||||
values: { total: 1000 },
|
||||
defaultMessage: 'Your total is {total, number, foo}',
|
||||
})
|
||||
).toBe('Your total is 1,000');
|
||||
});
|
||||
|
||||
test('should throw error if used format is not specified', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
['a.b.c']: 'Your total is {total, foo}',
|
||||
});
|
||||
i18n.setDefaultLocale('en');
|
||||
|
||||
expect(() =>
|
||||
i18n.translate('a.b.c', { values: { total: 1 } })
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
|
||||
expect(() =>
|
||||
i18n.translate('d.e.f', {
|
||||
values: { total: 1000 },
|
||||
defaultMessage: 'Your total is {total, bar}',
|
||||
})
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('init', () => {
|
||||
test('should not initialize the engine if messages are not specified', () => {
|
||||
i18n.init();
|
||||
expect(i18n.getMessages()).toEqual({});
|
||||
});
|
||||
|
||||
test('should throw error if messages are empty', () => {
|
||||
expect(() => i18n.init({})).toThrow();
|
||||
expect(i18n.getMessages()).toEqual({});
|
||||
});
|
||||
|
||||
test('should add messages if locale is specified', () => {
|
||||
i18n.init({
|
||||
locale: 'en',
|
||||
foo: 'bar',
|
||||
});
|
||||
|
||||
expect(i18n.getMessages()).toEqual({
|
||||
locale: 'en',
|
||||
foo: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
test('should set the current locale', () => {
|
||||
i18n.init({ locale: 'ru' });
|
||||
expect(i18n.getLocale()).toBe('ru');
|
||||
});
|
||||
|
||||
test('should add custom formats', () => {
|
||||
i18n.init({
|
||||
locale: 'ru',
|
||||
formats: {
|
||||
date: {
|
||||
custom: {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(i18n.getFormats().date.custom).toEqual({
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
281
packages/kbn-i18n/src/loader.test.js
Normal file
281
packages/kbn-i18n/src/loader.test.js
Normal file
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { join } from 'path';
|
||||
|
||||
describe('I18n loader', () => {
|
||||
let i18nLoader;
|
||||
|
||||
beforeEach(() => {
|
||||
i18nLoader = require('./loader');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// isolate modules for every test so that local module state doesn't conflict between tests
|
||||
jest.resetModules();
|
||||
});
|
||||
|
||||
describe('registerTranslationFile', () => {
|
||||
test('should throw error if path to translation file is not specified', () => {
|
||||
expect(() => i18nLoader.registerTranslationFile()).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('should throw error if path to translation file is not an absolute', () => {
|
||||
expect(() => i18nLoader.registerTranslationFile('./en.json')).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('should throw error if path to translation file does not have an extension', () => {
|
||||
expect(() =>
|
||||
i18nLoader.registerTranslationFile(
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en')
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
test('should throw error if translation file is not a json', () => {
|
||||
expect(() =>
|
||||
i18nLoader.registerTranslationFile(
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en.txt')
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
test('should register a translation file', () => {
|
||||
expect(i18nLoader.getRegisteredLocales()).toEqual([]);
|
||||
|
||||
i18nLoader.registerTranslationFile(
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en.json')
|
||||
);
|
||||
|
||||
expect(i18nLoader.getRegisteredLocales()).toEqual(['en']);
|
||||
|
||||
i18nLoader.registerTranslationFile(
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en-US.json')
|
||||
);
|
||||
|
||||
expect(i18nLoader.getRegisteredLocales()).toContain('en', 'en-US');
|
||||
expect(i18nLoader.getRegisteredLocales().length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('registerTranslationFiles', () => {
|
||||
test('should register array of translation files', () => {
|
||||
expect(i18nLoader.getRegisteredLocales()).toEqual([]);
|
||||
|
||||
i18nLoader.registerTranslationFiles([
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en.json'),
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en-US.json'),
|
||||
]);
|
||||
|
||||
expect(i18nLoader.getRegisteredLocales()).toContain('en', 'en-US');
|
||||
expect(i18nLoader.getRegisteredLocales().length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTranslationsByLocale', () => {
|
||||
test('should return translation messages by specified locale', async () => {
|
||||
i18nLoader.registerTranslationFile(
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en.json')
|
||||
);
|
||||
|
||||
expect(await i18nLoader.getTranslationsByLocale('en')).toEqual({
|
||||
locale: 'en',
|
||||
['a.b.c']: 'foo',
|
||||
['d.e.f']: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
test('should return empty object if passed locale is not registered', async () => {
|
||||
i18nLoader.registerTranslationFile(
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en.json')
|
||||
);
|
||||
|
||||
expect(await i18nLoader.getTranslationsByLocale('ru')).toEqual({});
|
||||
});
|
||||
|
||||
test('should return translation messages from a couple of files by specified locale', async () => {
|
||||
i18nLoader.registerTranslationFiles([
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en.json'),
|
||||
join(__dirname, './__fixtures__/test_plugin_2/translations/en.json'),
|
||||
]);
|
||||
|
||||
expect(await i18nLoader.getTranslationsByLocale('en')).toEqual({
|
||||
locale: 'en',
|
||||
['a.b.c']: 'foo',
|
||||
['d.e.f']: 'bar',
|
||||
['a.b.c.custom']: 'foo.custom',
|
||||
['d.e.f.custom']: 'bar.custom',
|
||||
});
|
||||
});
|
||||
|
||||
test('should return translation messages for different locales', async () => {
|
||||
i18nLoader.registerTranslationFiles([
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en.json'),
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en-US.json'),
|
||||
join(__dirname, './__fixtures__/test_plugin_2/translations/en.json'),
|
||||
join(__dirname, './__fixtures__/test_plugin_2/translations/ru.json'),
|
||||
]);
|
||||
|
||||
expect(await i18nLoader.getTranslationsByLocale('en')).toEqual({
|
||||
locale: 'en',
|
||||
['a.b.c']: 'foo',
|
||||
['d.e.f']: 'bar',
|
||||
['a.b.c.custom']: 'foo.custom',
|
||||
['d.e.f.custom']: 'bar.custom',
|
||||
});
|
||||
|
||||
expect(await i18nLoader.getTranslationsByLocale('en-US')).toEqual({
|
||||
locale: 'en-US',
|
||||
['a.b.c']: 'bar',
|
||||
['d.e.f']: 'foo',
|
||||
});
|
||||
|
||||
expect(await i18nLoader.getTranslationsByLocale('ru')).toEqual({
|
||||
locale: 'ru',
|
||||
test: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
test('should return translation messages from JSON5 file', async () => {
|
||||
i18nLoader.registerTranslationFile(
|
||||
join(__dirname, './__fixtures__/test_plugin_2/translations/fr.json')
|
||||
);
|
||||
|
||||
expect(await i18nLoader.getTranslationsByLocale('fr')).toEqual({
|
||||
locale: 'fr',
|
||||
test: 'test',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTranslationsByLanguageHeader', () => {
|
||||
test('should return empty object if there are no registered locales', async () => {
|
||||
expect(
|
||||
await i18nLoader.getTranslationsByLanguageHeader('en-GB,en-US;q=0.9,fr-CA;q=0.7,en;q=0.8')
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
test('should return empty object if registered locales do not match to accept-language header', async () => {
|
||||
i18nLoader.registerTranslationFile(
|
||||
join(__dirname, './__fixtures__/test_plugin_2/translations/ru.json')
|
||||
);
|
||||
|
||||
expect(
|
||||
await i18nLoader.getTranslationsByLanguageHeader('en-GB,en-US;q=0.9,fr-CA;q=0.7,en;q=0.8')
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
test('should return translation messages for the only matched locale', async () => {
|
||||
i18nLoader.registerTranslationFile(
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en.json')
|
||||
);
|
||||
|
||||
expect(
|
||||
await i18nLoader.getTranslationsByLanguageHeader('en-GB,en-US;q=0.9,fr-CA;q=0.7,en;q=0.8')
|
||||
).toEqual({
|
||||
locale: 'en',
|
||||
['a.b.c']: 'foo',
|
||||
['d.e.f']: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
test('should return translation messages for the best matched locale', async () => {
|
||||
i18nLoader.registerTranslationFiles([
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en.json'),
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en-US.json'),
|
||||
]);
|
||||
|
||||
expect(
|
||||
await i18nLoader.getTranslationsByLanguageHeader('en-GB,en-US;q=0.9,fr-CA;q=0.7,en;q=0.8')
|
||||
).toEqual({
|
||||
locale: 'en-US',
|
||||
['a.b.c']: 'bar',
|
||||
['d.e.f']: 'foo',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAllTranslations', () => {
|
||||
test('should return translation messages for all registered locales', async () => {
|
||||
i18nLoader.registerTranslationFiles([
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en.json'),
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en-US.json'),
|
||||
join(__dirname, './__fixtures__/test_plugin_2/translations/en.json'),
|
||||
join(__dirname, './__fixtures__/test_plugin_2/translations/ru.json'),
|
||||
]);
|
||||
|
||||
expect(await i18nLoader.getAllTranslations()).toEqual({
|
||||
en: {
|
||||
locale: 'en',
|
||||
['a.b.c']: 'foo',
|
||||
['d.e.f']: 'bar',
|
||||
['a.b.c.custom']: 'foo.custom',
|
||||
['d.e.f.custom']: 'bar.custom',
|
||||
},
|
||||
['en-US']: {
|
||||
locale: 'en-US',
|
||||
['a.b.c']: 'bar',
|
||||
['d.e.f']: 'foo',
|
||||
},
|
||||
ru: {
|
||||
locale: 'ru',
|
||||
test: 'test',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('should return empty object if there are no registered locales', async () => {
|
||||
expect(await i18nLoader.getAllTranslations()).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAllTranslationsFromPaths', () => {
|
||||
test('should return translation messages for all passed paths to translation files', async () => {
|
||||
expect(
|
||||
await i18nLoader.getAllTranslationsFromPaths([
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en.json'),
|
||||
join(__dirname, './__fixtures__/test_plugin_1/translations/en-US.json'),
|
||||
join(__dirname, './__fixtures__/test_plugin_2/translations/en.json'),
|
||||
join(__dirname, './__fixtures__/test_plugin_2/translations/ru.json'),
|
||||
])
|
||||
).toEqual({
|
||||
en: {
|
||||
locale: 'en',
|
||||
['a.b.c']: 'foo',
|
||||
['d.e.f']: 'bar',
|
||||
['a.b.c.custom']: 'foo.custom',
|
||||
['d.e.f.custom']: 'bar.custom',
|
||||
},
|
||||
['en-US']: {
|
||||
locale: 'en-US',
|
||||
['a.b.c']: 'bar',
|
||||
['d.e.f']: 'foo',
|
||||
},
|
||||
ru: {
|
||||
locale: 'ru',
|
||||
test: 'test',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('should return empty object if there are no translation files', async () => {
|
||||
expect(await i18nLoader.getAllTranslationsFromPaths()).toEqual({});
|
||||
});
|
||||
});
|
||||
});
|
139
packages/kbn-i18n/src/react/__snapshots__/provider.test.js.snap
Normal file
139
packages/kbn-i18n/src/react/__snapshots__/provider.test.js.snap
Normal file
|
@ -0,0 +1,139 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`I18nProvider provides with context 1`] = `
|
||||
Object {
|
||||
"defaultFormats": Object {
|
||||
"date": Object {
|
||||
"full": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"weekday": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"long": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"medium": Object {
|
||||
"day": "numeric",
|
||||
"month": "short",
|
||||
"year": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"day": "numeric",
|
||||
"month": "numeric",
|
||||
"year": "2-digit",
|
||||
},
|
||||
},
|
||||
"number": Object {
|
||||
"currency": Object {
|
||||
"style": "currency",
|
||||
},
|
||||
"percent": Object {
|
||||
"style": "percent",
|
||||
},
|
||||
},
|
||||
"time": Object {
|
||||
"full": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"long": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"medium": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
},
|
||||
},
|
||||
},
|
||||
"defaultLocale": "en",
|
||||
"formatDate": [Function],
|
||||
"formatHTMLMessage": [Function],
|
||||
"formatMessage": [Function],
|
||||
"formatNumber": [Function],
|
||||
"formatPlural": [Function],
|
||||
"formatRelative": [Function],
|
||||
"formatTime": [Function],
|
||||
"formats": Object {
|
||||
"date": Object {
|
||||
"full": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"weekday": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"long": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"medium": Object {
|
||||
"day": "numeric",
|
||||
"month": "short",
|
||||
"year": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"day": "numeric",
|
||||
"month": "numeric",
|
||||
"year": "2-digit",
|
||||
},
|
||||
},
|
||||
"number": Object {
|
||||
"currency": Object {
|
||||
"style": "currency",
|
||||
},
|
||||
"percent": Object {
|
||||
"style": "percent",
|
||||
},
|
||||
},
|
||||
"time": Object {
|
||||
"full": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"long": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"medium": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
},
|
||||
},
|
||||
},
|
||||
"formatters": Object {
|
||||
"getDateTimeFormat": [Function],
|
||||
"getMessageFormat": [Function],
|
||||
"getNumberFormat": [Function],
|
||||
"getPluralFormat": [Function],
|
||||
"getRelativeFormat": [Function],
|
||||
},
|
||||
"locale": "en",
|
||||
"messages": Object {},
|
||||
"now": [Function],
|
||||
"textComponent": "span",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`I18nProvider renders children 1`] = `<ChildrenMock />`;
|
56
packages/kbn-i18n/src/react/provider.test.js
Normal file
56
packages/kbn-i18n/src/react/provider.test.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { intlShape } from 'react-intl';
|
||||
import { shallow, mount } from 'enzyme';
|
||||
import { I18nProvider } from './provider';
|
||||
import { injectI18n } from './inject';
|
||||
|
||||
describe('I18nProvider', () => {
|
||||
it('renders children', () => {
|
||||
const ChildrenMock = () => {};
|
||||
|
||||
const wrapper = shallow(
|
||||
<I18nProvider>
|
||||
<ChildrenMock />
|
||||
</I18nProvider>
|
||||
);
|
||||
|
||||
expect(wrapper.children()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('provides with context', () => {
|
||||
const childrenMock = () => <div />;
|
||||
const WithIntl = injectI18n(childrenMock);
|
||||
|
||||
const wrapper = mount(
|
||||
<I18nProvider>
|
||||
<WithIntl />
|
||||
</I18nProvider>,
|
||||
{
|
||||
childContextTypes: {
|
||||
intl: intlShape,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(wrapper.find(childrenMock).prop('intl')).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -42,7 +42,7 @@ export const IGNORE_FILE_GLOBS = [
|
|||
'src/dev/tslint/rules/*',
|
||||
|
||||
// filename must match language code which requires capital letters
|
||||
'src/ui/ui_i18n/__tests__/fixtures/translations/test_plugin_1/es-ES.json',
|
||||
'**/translations/*.json',
|
||||
];
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue