@lion/ui
Version:
A package of extendable web components
888 lines (737 loc) • 29.9 kB
JavaScript
import { expect, oneEvent, aTimeout } from '@open-wc/testing';
import sinon from 'sinon';
// @ts-ignore
import { fetchMock } from '@bundled-es-modules/fetch-mock';
import { setupFakeImport, resetFakeImport, fakeImport } from '@lion/ui/localize-test-helpers.js';
import { LocalizeManager } from '@lion/ui/localize-no-side-effects.js';
/**
* @param {LocalizeManager} localizeManagerEl
*/
function getProtectedMembers(localizeManagerEl) {
// @ts-ignore
const { __storage: storage, _supportExternalTranslationTools: supportExternalTranslationTools } =
localizeManagerEl;
return {
storage,
supportExternalTranslationTools,
};
}
/**
* @param {string} str
* Useful for IE11 where LTR and RTL symbols are put by Intl when rendering dates
*/
function removeLtrRtl(str) {
return str.replace(/(\u200E|\u200E)/g, '');
}
describe('LocalizeManager', () => {
/** @type {LocalizeManager} */
let manager;
beforeEach(() => {
// makes sure that between tests the localization is reset to default state
document.documentElement.lang = 'en-GB';
});
afterEach(() => {
manager.teardown();
});
afterEach(() => {
fetchMock.restore();
resetFakeImport();
});
it('syncs locale back to <html> if changed', () => {
manager = new LocalizeManager();
manager.locale = 'nl-NL';
expect(document.documentElement.lang).to.equal('nl-NL');
});
it('sets locale to "en-GB" by default if nothing is set on <html>', () => {
document.documentElement.lang = '';
manager = new LocalizeManager();
expect(manager.locale).to.equal('en-GB');
expect(document.documentElement.lang).to.equal('en-GB');
});
it('empties storage after reset() is invoked', async () => {
manager = new LocalizeManager();
let deferredResolve;
manager.loadNamespace({
generic: () =>
new Promise(resolve => {
deferredResolve = () => resolve({ greeting: 'Hello!' });
}),
});
const { loadingComplete } = manager;
manager.reset();
expect(getProtectedMembers(manager).storage).to.be.empty;
// @ts-ignore
deferredResolve();
await loadingComplete;
// storage still needs to be empty after promise is fulfilled.
expect(getProtectedMembers(manager).storage).to.be.empty;
});
it('has teardown() method removing all side effects', () => {
manager = new LocalizeManager();
const disconnectObserverSpy = sinon.spy(
manager._htmlLangAttributeObserver,
/** @type {never} */ ('disconnect'),
);
manager.teardown();
expect(disconnectObserverSpy.callCount).to.equal(1);
});
describe('"localeChanged" event with detail.newLocale and detail.oldLocale', () => {
it('fires "localeChanged" event if locale was changed via manager', async () => {
manager = new LocalizeManager();
setTimeout(() => {
manager.locale = 'en-US';
});
const event = await oneEvent(
/** @type {EventTarget} */ (/** @type {unknown} */ (manager)),
'localeChanged',
);
expect(event.detail.newLocale).to.equal('en-US');
expect(event.detail.oldLocale).to.equal('en-GB');
});
it('does not fire "localeChanged" event if it was set to the same locale', () => {
manager = new LocalizeManager();
const eventSpy = sinon.spy();
manager.addEventListener('localeChanged', eventSpy);
manager.locale = 'en-US';
manager.locale = 'en-US';
expect(eventSpy.callCount).to.equal(1);
});
});
describe('addData()', () => {
it('allows to provide inline data', () => {
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
manager.addData('en-GB', 'lion-hello', { greeting: 'Hi!' });
expect(storage).to.deep.equal({
'en-GB': {
'lion-hello': { greeting: 'Hi!' },
},
});
manager.addData('en-GB', 'lion-goodbye', { farewell: 'Cheers!' });
expect(storage).to.deep.equal({
'en-GB': {
'lion-hello': { greeting: 'Hi!' },
'lion-goodbye': { farewell: 'Cheers!' },
},
});
manager.addData('nl-NL', 'lion-hello', { greeting: 'Hoi!' });
manager.addData('nl-NL', 'lion-goodbye', { farewell: 'Doei!' });
expect(storage).to.deep.equal({
'en-GB': {
'lion-hello': { greeting: 'Hi!' },
'lion-goodbye': { farewell: 'Cheers!' },
},
'nl-NL': {
'lion-hello': { greeting: 'Hoi!' },
'lion-goodbye': { farewell: 'Doei!' },
},
});
});
it('prevents mutating existing data for the same locale & namespace when "allowOverridesForExistingNamespaces" option is not given in constructor', () => {
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
manager.addData('en-GB', 'lion-hello', { greeting: 'Hi!' });
expect(() => {
manager.addData('en-GB', 'lion-hello', { greeting: 'Hello!' });
}).to.throw();
expect(storage).to.deep.equal({
'en-GB': { 'lion-hello': { greeting: 'Hi!' } },
});
});
it('allows mutating existing data for the same locale & namespace when "allowOverridesForExistingNamespaces" option is set to "true" in constructor', () => {
manager = new LocalizeManager({ allowOverridesForExistingNamespaces: true });
const { storage } = getProtectedMembers(manager);
manager.addData('en-GB', 'lion-hello', { greeting: 'Hi!' });
expect(storage).to.deep.equal({
'en-GB': { 'lion-hello': { greeting: 'Hi!' } },
});
manager.addData('en-GB', 'lion-hello', { greeting: 'Hi!', alternative: 'Hello!' });
expect(storage).to.deep.equal({
'en-GB': { 'lion-hello': { greeting: 'Hi!', alternative: 'Hello!' } },
});
});
});
describe('loading via dynamic imports', () => {
it('loads a namespace via loadNamespace()', async () => {
setupFakeImport('./my-component/en-GB.js', { default: { greeting: 'Hello!' } });
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
await manager.loadNamespace({
/** @param {string} locale */
'my-component': locale => fakeImport(`./my-component/${locale}.js`),
});
expect(storage).to.deep.equal({
'en-GB': {
'my-component': { greeting: 'Hello!' },
},
});
});
it('can load a namespace for a different locale', async () => {
setupFakeImport('./my-component/nl-NL.js', { default: { greeting: 'Hello!' } });
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
manager.locale = 'en-US';
await manager.loadNamespace(
{
/** @param {string} locale */
'my-component': locale => fakeImport(`./my-component/${locale}.js`),
},
{ locale: 'nl-NL' },
);
expect(storage).to.deep.equal({
'nl-NL': {
'my-component': { greeting: 'Hello!' },
},
});
});
it('loads multiple namespaces via loadNamespaces()', async () => {
setupFakeImport('./my-defaults/en-GB.js', { default: { submit: 'Submit' } });
setupFakeImport('./my-send-button/en-GB.js', { default: { submit: 'Send' } });
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
await manager.loadNamespaces([
{
/** @param {string} locale */
'my-defaults': locale => fakeImport(`./my-defaults/${locale}.js`),
},
{
/** @param {string} locale */
'my-send-button': locale => fakeImport(`./my-send-button/${locale}.js`),
},
]);
expect(storage).to.deep.equal({
'en-GB': {
'my-defaults': { submit: 'Submit' },
'my-send-button': { submit: 'Send' },
},
});
});
it('can load multiple namespaces for a different locale', async () => {
setupFakeImport('./my-defaults/nl-NL.js', { default: { submit: 'Submit' } });
setupFakeImport('./my-send-button/nl-NL.js', { default: { submit: 'Send' } });
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
manager.locale = 'en-US';
await manager.loadNamespaces(
[
{
/** @param {string} locale */
'my-defaults': locale => fakeImport(`./my-defaults/${locale}.js`),
},
{
/** @param {string} locale */
'my-send-button': locale => fakeImport(`./my-send-button/${locale}.js`),
},
],
{ locale: 'nl-NL' },
);
expect(storage).to.deep.equal({
'nl-NL': {
'my-defaults': { submit: 'Submit' },
'my-send-button': { submit: 'Send' },
},
});
});
it('loads generic language file if locale file is not found', async () => {
setupFakeImport('./my-component/en.js', { default: { greeting: 'Hello!' } });
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
await manager.loadNamespace({
/** @param {string} locale */
'my-component': locale => fakeImport(`./my-component/${locale}.js`),
});
expect(storage).to.deep.equal({
'en-GB': {
'my-component': { greeting: 'Hello!' },
},
});
});
it('throws if both locale and language files could not be loaded', async () => {
manager = new LocalizeManager();
try {
await manager.loadNamespace({
/** @param {string} locale */
'my-component': locale => fakeImport(`./my-component/${locale}.js`),
});
} catch (e) {
expect(e).to.be.instanceof(Error);
expect(/** @type {Error} */ (e).message).to.equal(
'Data for namespace "my-component" and locale "en-GB" could not be loaded. ' +
'Make sure you have data for locale "en-GB" (and/or generic language "en").',
);
return;
}
throw new Error('did not throw');
});
describe('fallback locale', () => {
it('can load a fallback locale if current one can not be loaded', async () => {
manager = new LocalizeManager({ fallbackLocale: 'en-GB' });
const { storage } = getProtectedMembers(manager);
manager.locale = 'nl-NL';
setupFakeImport('./my-component/en-GB.js', { default: { greeting: 'Hello!' } });
await manager.loadNamespace({
/** @param {string} locale */
'my-component': locale => fakeImport(`./my-component/${locale}.js`),
});
expect(storage).to.deep.equal({
'nl-NL': {
'my-component': { greeting: 'Hello!' },
},
});
});
it('can load fallback generic language file if fallback locale file is not found', async () => {
manager = new LocalizeManager({ fallbackLocale: 'en-GB' });
const { storage } = getProtectedMembers(manager);
manager.locale = 'nl-NL';
setupFakeImport('./my-component/en.js', { default: { greeting: 'Hello!' } });
await manager.loadNamespace({
/** @param {string} locale */
'my-component': locale => fakeImport(`./my-component/${locale}.js`),
});
expect(storage).to.deep.equal({
'nl-NL': {
'my-component': { greeting: 'Hello!' },
},
});
});
it('throws if neither current locale nor fallback locale are found', async () => {
manager = new LocalizeManager({ fallbackLocale: 'en-GB' });
manager.locale = 'nl-NL';
try {
await manager.loadNamespace({
/** @param {string} locale */
'my-component': locale => fakeImport(`./my-component/${locale}.js`),
});
} catch (e) {
expect(e).to.be.instanceof(Error);
expect(/** @type {Error} */ (e).message).to.equal(
'Data for namespace "my-component" and current locale "nl-NL" or fallback locale "en-GB" could not be loaded. ' +
'Make sure you have data either for locale "nl-NL" (and/or generic language "nl") or for fallback "en-GB" (and/or "en").',
);
return;
}
throw new Error('did not throw');
});
it('throws an error if the locale set by the user is not a full language locale', async () => {
manager = new LocalizeManager();
expect(() => {
manager.locale = 'nl';
}).to.throw(`
Locale was set to nl.
Language only locales are not allowed, please use the full language locale e.g. 'en-GB' instead of 'en'.
See https://github.com/ing-bank/lion/issues/187 for more information.
`);
});
it('does not throw an error if locale was set through the html lang attribute', async () => {
manager = new LocalizeManager();
expect(() => {
document.documentElement.lang = 'nl';
}).to.not.throw();
});
});
});
describe('loading using routes predefined via setupNamespaceLoader()', () => {
it('loads a namespace via loadNamespace() using string route', async () => {
fetchMock.get('./my-component/en-GB.json', { greeting: 'Hello!' });
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
manager.setupNamespaceLoader(
'my-component',
/** @param {string} locale */
async locale => {
const response = await fetch(`./my-component/${locale}.json`);
return response.json();
},
);
await manager.loadNamespace('my-component');
expect(storage).to.deep.equal({
'en-GB': {
'my-component': { greeting: 'Hello!' },
},
});
});
it('loads multiple namespaces via loadNamespaces() using string routes', async () => {
fetchMock.get('./my-defaults/en-GB.json', {
submit: 'Submit',
});
fetchMock.get('./my-send-button/en-GB.json', {
submit: 'Send',
});
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
manager.setupNamespaceLoader(
'my-defaults',
/** @param {string} locale */
async locale => {
const response = await fetch(`./my-defaults/${locale}.json`);
return response.json();
},
);
manager.setupNamespaceLoader(
'my-send-button',
/** @param {string} locale */
async locale => {
const response = await fetch(`./my-send-button/${locale}.json`);
return response.json();
},
);
await manager.loadNamespaces(['my-defaults', 'my-send-button']);
expect(storage).to.deep.equal({
'en-GB': {
'my-send-button': {
submit: 'Send',
},
'my-defaults': {
submit: 'Submit',
},
},
});
});
it('loads a namespace via loadNamespace() using RegExp route', async () => {
fetchMock.get('./my-component/en-GB.json', { greeting: 'Hello!' });
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
manager.setupNamespaceLoader(
/my-.+/,
/**
* @param {string} locale
* @param {string} namespace
*/
async (locale, namespace) => {
const response = await fetch(`./${namespace}/${locale}.json`);
return response.json();
},
);
await manager.loadNamespace('my-component');
expect(storage).to.deep.equal({
'en-GB': {
'my-component': { greeting: 'Hello!' },
},
});
});
it('loads multiple namespaces via loadNamespaces() using RegExp routes', async () => {
fetchMock.get('./my-defaults/en-GB.json', { submit: 'Submit' });
fetchMock.get('./my-send-button/en-GB.json', { submit: 'Send' });
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
manager.setupNamespaceLoader(
/my-.+/,
/**
* @param {string} locale
* @param {string} namespace
*/
async (locale, namespace) => {
const response = await fetch(`./${namespace}/${locale}.json`);
return response.json();
},
);
await manager.loadNamespaces(['my-defaults', 'my-send-button']);
expect(storage).to.deep.equal({
'en-GB': {
'my-defaults': { submit: 'Submit' },
'my-send-button': { submit: 'Send' },
},
});
});
});
describe('{ autoLoadOnLocaleChange: true }', () => {
it('loads namespaces automatically when locale is changed via manager', async () => {
setupFakeImport('./my-component/en-GB.js', { default: { greeting: 'Hello!' } });
setupFakeImport('./my-component/nl-NL.js', { default: { greeting: 'Hallo!' } });
manager = new LocalizeManager({ autoLoadOnLocaleChange: true });
const { storage } = getProtectedMembers(manager);
await manager.loadNamespace({
/** @param {string} locale */
'my-component': locale => fakeImport(`./my-component/${locale}.js`, 25),
});
expect(storage).to.deep.equal({
'en-GB': { 'my-component': { greeting: 'Hello!' } },
});
manager.locale = 'nl-NL';
await manager.loadingComplete;
expect(storage).to.deep.equal({
'en-GB': { 'my-component': { greeting: 'Hello!' } },
'nl-NL': { 'my-component': { greeting: 'Hallo!' } },
});
});
});
describe('loading extra features', () => {
it('has a Promise "loadingComplete" that resolved once all pending loading is done', async () => {
setupFakeImport('./my-component/en-GB.js', { default: { greeting: 'Hello!' } });
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
manager.loadNamespace({
/** @param {string} locale */
'my-component': locale => fakeImport(`./my-component/${locale}.js`, 25),
});
expect(storage).to.deep.equal({});
await manager.loadingComplete;
expect(storage).to.deep.equal({
'en-GB': { 'my-component': { greeting: 'Hello!' } },
});
});
it('loads namespace only once for the same locale', async () => {
let called = 0;
const myNamespace = {
'my-namespace': () => {
called += 1;
return Promise.resolve({ default: {} });
},
};
manager = new LocalizeManager();
await Promise.all([
manager.loadNamespace(myNamespace),
manager.loadNamespace(myNamespace),
manager.loadNamespace(myNamespace),
]);
expect(called).to.equal(1);
});
it('does not load inlined data', async () => {
setupFakeImport('./my-component/en-GB.js', { default: { greeting: 'Loaded hello!' } });
manager = new LocalizeManager();
const { storage } = getProtectedMembers(manager);
manager.addData('en-GB', 'my-component', { greeting: 'Hello!' });
await manager.loadNamespace('my-component');
expect(storage).to.deep.equal({
'en-GB': { 'my-component': { greeting: 'Hello!' } },
});
let called = 0;
await manager.loadNamespace({
/** @param {string} locale */
'my-component': locale => {
called += 1;
return fakeImport(`./my-component/${locale}.js`);
},
});
expect(called).to.equal(0);
expect(storage).to.deep.equal({
'en-GB': { 'my-component': { greeting: 'Hello!' } },
});
});
});
describe('message()', () => {
it('gets the message for the key in the format of "namespace:name"', () => {
manager = new LocalizeManager();
manager.addData('en-GB', 'my-ns', { greeting: 'Hello!' });
expect(manager.msg('my-ns:greeting')).to.equal('Hello!');
});
it('supports nested names in the format of "namespace:path.to.deep.name"', () => {
manager = new LocalizeManager();
manager.addData('en-GB', 'my-ns', { 'login-section': { greeting: 'Hello!' } });
expect(manager.msg('my-ns:login-section.greeting')).to.equal('Hello!');
});
it('supports variables', () => {
manager = new LocalizeManager();
manager.addData('en-GB', 'my-ns', { greeting: 'Hello {name}!' });
expect(manager.msg('my-ns:greeting', { name: 'John' })).to.equal('Hello John!');
});
it('supports Intl MessageFormat proposal for messages', () => {
manager = new LocalizeManager();
manager.addData('en-GB', 'my-ns', {
date: 'I was written on {today, date}.',
number: 'You have {n, plural, =0 {no photos.} =1 {one photo.} other {# photos.}}',
});
const today = new Date('2018/04/30');
expect(removeLtrRtl(manager.msg('my-ns:date', { today }))).to.equal(
'I was written on 30 Apr 2018.',
);
expect(manager.msg('my-ns:number', { n: 0 })).to.equal('You have no photos.');
expect(manager.msg('my-ns:number', { n: 1 })).to.equal('You have one photo.');
expect(manager.msg('my-ns:number', { n: 2 })).to.equal('You have 2 photos.');
});
it('takes into account globally changed locale', () => {
manager = new LocalizeManager();
manager.locale = 'nl-NL';
manager.addData('en-GB', 'my-ns', { greeting: 'Hi!' });
manager.addData('nl-NL', 'my-ns', { greeting: 'Hey!' });
expect(manager.msg('my-ns:greeting')).to.equal('Hey!');
});
it('allows to provide a different locale for specific call', () => {
manager = new LocalizeManager();
manager.addData('en-GB', 'my-ns', { greeting: 'Hi!' });
manager.addData('nl-NL', 'my-ns', { greeting: 'Hey!' });
expect(manager.msg('my-ns:greeting', undefined, { locale: 'nl-NL' })).to.equal('Hey!');
manager.reset();
manager.addData('en-GB', 'my-ns', { greeting: 'Hi {name}!' });
manager.addData('nl-NL', 'my-ns', { greeting: 'Hey {name}!' });
expect(manager.msg('my-ns:greeting', { name: 'John' }, { locale: 'nl-NL' })).to.equal(
'Hey John!',
);
});
it('allows to provide an ordered list of keys where the first resolved is used', () => {
manager = new LocalizeManager();
const keys = ['overridden-ns:greeting', 'default-ns:greeting'];
expect(manager.msg(keys)).to.equal('');
manager.addData('en-GB', 'default-ns', { greeting: 'Hi!' });
expect(manager.msg(keys)).to.equal('Hi!');
manager.addData('en-GB', 'overridden-ns', { greeting: 'Hello!' });
expect(manager.msg(keys)).to.equal('Hello!');
});
it('throws a custom error when namespace prefix is missing', () => {
manager = new LocalizeManager();
const msgKey = 'greeting';
manager.addData('en-GB', 'my-ns', { [msgKey]: 'Hello!' });
expect(() => manager.msg(msgKey)).to.throw(
`Namespace is missing in the key "${msgKey}". The format for keys is "namespace:name".`,
);
});
});
describe('show key as fallback', () => {
it('shows the key as a fallback when a translation cannot be found', () => {
manager = new LocalizeManager({ showKeyAsFallback: true });
manager.addData('en-GB', 'my-ns', { greeting: 'Hello!' });
expect(manager.msg('my-ns:unknownKey')).to.equal('my-ns:unknownKey');
});
it('shows nothing when a translation cannot be found by default', () => {
manager = new LocalizeManager();
manager.addData('en-GB', 'my-ns', { greeting: 'Hello!' });
expect(manager.msg('my-ns:unknownKey')).to.equal('');
});
});
});
describe('When supporting external translation tools like Google Translate', () => {
let manager;
const originalLang = document.documentElement.lang;
/** @param {string} lang */
async function simulateGoogleTranslateOn(lang) {
document.documentElement.lang = lang;
}
async function simulateGoogleTranslateOff() {
document.documentElement.lang = 'auto';
}
/**
* @param {...*} [cfg]
* @returns {LocalizeManager}
*/
function getInstance(cfg) {
return new LocalizeManager(cfg || {});
}
afterEach(() => {
document.documentElement.removeAttribute('lang');
document.documentElement.removeAttribute('data-localize-lang');
});
after(() => {
document.documentElement.lang = originalLang;
});
describe('On initialization', () => {
/** A default scenario */
it('synchronizes from html[data-localize-lang] attribute to LocalizeManager', async () => {
document.documentElement.setAttribute('data-localize-lang', 'nl-NL');
document.documentElement.lang = 'nl-NL';
manager = getInstance();
expect(manager.locale).to.equal('nl-NL');
});
/** A scenario where Google Translate kicked in before initialization */
it(`synchronizes from html[data-localize-lang] attribute to LocalizeManager when html[lang]
has a different value`, async () => {
document.documentElement.setAttribute('data-localize-lang', 'en-US');
document.documentElement.lang = 'fr';
manager = getInstance();
expect(manager.locale).to.equal('en-US');
});
it("doesn't synchronize from html[lang] attribute to LocalizeManager", async () => {
document.documentElement.setAttribute('data-localize-lang', 'en-US');
manager = getInstance();
document.documentElement.lang = 'nl-NL';
expect(manager.locale).to.not.equal('nl-NL');
});
it('triggers support for external translation tools via data-localize-lang', async () => {
document.documentElement.removeAttribute('data-localize-lang');
manager = getInstance();
const { supportExternalTranslationTools: first } = getProtectedMembers(manager);
expect(first).to.be.false;
document.documentElement.setAttribute('data-localize-lang', 'nl-NL');
manager = getInstance();
const { supportExternalTranslationTools: second } = getProtectedMembers(manager);
expect(second).to.be.true;
});
});
describe('After initialization', () => {
it(`synchronizes from LocalizeManager to html[lang] when
3rd party translation tool is NOT in control`, async () => {
document.documentElement.removeAttribute('lang');
manager = getInstance();
expect(document.documentElement.lang).to.equal('en-GB');
manager.locale = 'nl-NL';
expect(document.documentElement.lang).to.equal('nl-NL');
});
it(`doesn't synchronize from LocalizeManager to html[lang] when
3rd party translation tool is in control`, async () => {
document.documentElement.setAttribute('data-localize-lang', 'en-US');
manager = getInstance();
await simulateGoogleTranslateOn('fr');
manager.locale = 'nl-NL';
expect(document.documentElement.lang).to.equal('fr');
});
it(`doesn't synchronize from html[lang] attribute to LocalizeManager`, async () => {
document.documentElement.setAttribute('data-localize-lang', 'en-US');
manager = getInstance();
manager.locale = 'nl-NL';
// When a 3rd party like Google Translate alters lang attr of the page, we want to
// keep this for accessibility, but it should NOT be synchronized to our manager.
await simulateGoogleTranslateOn('fr');
expect(manager.locale).to.equal('nl-NL');
});
it(`restores html[lang] when 3rd party translation tool is turned off again`, async () => {
manager = getInstance();
manager.locale = 'nl-NL';
await simulateGoogleTranslateOn('fr');
expect(document.documentElement.lang).to.equal('fr');
await simulateGoogleTranslateOff();
expect(document.documentElement.lang).to.equal('nl-NL');
});
});
});
describe('[deprecated] When not supporting external translation tools like Google Translate', () => {
/** @type {LocalizeManager} */
let manager;
beforeEach(() => {
// makes sure that between tests the localization is reset to default state
document.documentElement.lang = 'en-GB';
});
afterEach(() => {
manager.teardown();
});
afterEach(() => {
fetchMock.restore();
resetFakeImport();
});
it('initializes locale from <html> by default', () => {
manager = new LocalizeManager({});
expect(manager.locale).to.equal('en-GB');
});
it('fires "localeChanged" event if locale was changed via <html lang> attribute', async () => {
manager = new LocalizeManager({});
setTimeout(() => {
document.documentElement.lang = 'en-US';
});
const event = await oneEvent(
/** @type {EventTarget} */ (/** @type {unknown} */ (manager)),
'localeChanged',
);
expect(event.detail.newLocale).to.equal('en-US');
expect(event.detail.oldLocale).to.equal('en-GB');
});
it('loads namespaces automatically when locale is changed via <html lang> attribute', async () => {
setupFakeImport('./my-component/en-GB.js', { default: { greeting: 'Hello!' } });
setupFakeImport('./my-component/nl-NL.js', { default: { greeting: 'Hallo!' } });
manager = new LocalizeManager({
autoLoadOnLocaleChange: true,
});
const { storage } = getProtectedMembers(manager);
await manager.loadNamespace({
/** @param {string} locale */
'my-component': locale => fakeImport(`./my-component/${locale}.js`, 25),
});
expect(storage).to.deep.equal({
'en-GB': { 'my-component': { greeting: 'Hello!' } },
});
document.documentElement.lang = 'nl-NL';
await aTimeout(0); // wait for mutation observer to be called
await manager.loadingComplete;
expect(storage).to.deep.equal({
'en-GB': { 'my-component': { greeting: 'Hello!' } },
'nl-NL': { 'my-component': { greeting: 'Hallo!' } },
});
});
});