revamp tests for localize controller

This commit is contained in:
Jacob Overgaard
2023-08-03 11:46:40 +02:00
parent 567b2f34e5
commit 44dfbe3e09
4 changed files with 144 additions and 161 deletions

View File

@@ -1,256 +1,240 @@
import { aTimeout, elementUpdated, expect, fixture, html } from '@open-wc/testing';
import { umbTranslationRegistry } from '@umbraco-cms/backoffice/localization';
import { DefaultTranslationSet, TranslationSet, registerTranslation, translations } from './manager.js';
import { UmbLocalizeController } from './localize.controller.js';
import { customElement, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { ManifestTranslations, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { LitElement, customElement, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
@customElement('umb-localize-controller-host')
class UmbLocalizeControllerHostElement extends UmbLitElement {
@property()
lang = 'en-us';
class UmbLocalizeControllerHostElement extends UmbElementMixin(LitElement) {
@property() lang = 'en-us';
}
interface TestTranslation extends TranslationSet {
close: string;
logout: string;
withInlineToken: string;
withInlineTokenLegacy: string;
notOnRegional: string;
numUsersSelected: (count: number) => string;
}
//#region Translations
const english: ManifestTranslations = {
type: 'translations',
alias: 'test.en',
name: 'Test English',
meta: {
culture: 'en-us',
direction: 'ltr',
translations: {
general: {
close: 'Close',
logout: 'Log out',
withInlineToken: '{0} {1}',
withInlineTokenLegacy: '%0% %1%',
numUsersSelected: (count: number) => {
if (count === 0) return 'No users selected';
if (count === 1) return 'One user selected';
return `${count} users selected`;
},
},
},
const english: TestTranslation = {
$code: 'en-us',
$dir: 'ltr',
close: 'Close',
logout: 'Log out',
withInlineToken: '{0} {1}',
withInlineTokenLegacy: '%0% %1%',
notOnRegional: 'Not on regional',
numUsersSelected: (count: number) => {
if (count === 0) return 'No users selected';
if (count === 1) return 'One user selected';
return `${count} users selected`;
},
};
const englishOverride: ManifestTranslations = {
type: 'translations',
alias: 'test.en.override',
name: 'Test English',
meta: {
culture: 'en-us',
translations: {
general: {
close: 'Close 2',
},
},
},
const englishOverride: DefaultTranslationSet = {
$code: 'en-us',
$dir: 'ltr',
close: 'Close 2',
};
const danish: ManifestTranslations = {
type: 'translations',
alias: 'test.da',
name: 'Test Danish',
meta: {
culture: 'da',
translations: {
general: {
close: 'Luk',
notOnRegional: 'Not on regional',
},
},
},
const danish: DefaultTranslationSet = {
$code: 'da',
$dir: 'ltr',
close: 'Luk',
notOnRegional: 'Not on regional',
};
const danishRegional: ManifestTranslations = {
type: 'translations',
alias: 'test.da-DK',
name: 'Test Danish (Denmark)',
meta: {
culture: 'da-DK',
translations: {
general: {
close: 'Luk',
},
},
},
const danishRegional: DefaultTranslationSet = {
$code: 'da-dk',
$dir: 'ltr',
close: 'Luk',
};
//#endregion
describe('UmbLocalizeController', () => {
umbExtensionsRegistry.register(english);
umbExtensionsRegistry.register(danish);
umbExtensionsRegistry.register(danishRegional);
let element: UmbLocalizeControllerHostElement;
let controller: UmbLocalizeController<TestTranslation>;
beforeEach(async () => {
umbTranslationRegistry.loadLanguage(english.meta.culture);
element = await fixture(html`<umb-localize-controller-host></umb-localize-controller-host>`);
registerTranslation(english, danish, danishRegional);
document.documentElement.lang = english.$code;
document.documentElement.dir = english.$dir;
await aTimeout(0);
const host = {
getHostElement: () => document.createElement('div'),
addController: () => {},
removeController: () => {},
hasController: () => false,
getControllers: () => [],
removeControllerByAlias: () => {},
} satisfies UmbControllerHost;
controller = new UmbLocalizeController(host);
});
it('should have a localize controller', () => {
expect(element.localize).to.be.instanceOf(UmbLocalizeController);
afterEach(() => {
controller.destroy();
translations.clear();
});
it('should have a default language', () => {
expect(element.localize.lang()).to.equal(english.meta.culture);
expect(controller.lang()).to.equal(english.$code);
});
it('should update the language when the language changes', async () => {
document.documentElement.lang = danishRegional.$code;
await aTimeout(0);
expect(controller.lang()).to.equal(danishRegional.$code);
});
it('should have a default dir', () => {
expect(element.localize.dir()).to.equal(english.meta.direction);
expect(controller.dir()).to.equal(english.$dir);
});
it('should update the dir when the dir changes', async () => {
document.documentElement.dir = 'rtl';
await aTimeout(0);
expect(controller.dir()).to.equal('rtl');
});
describe('term', () => {
it('should return a term', async () => {
expect(element.localize.term('general_close')).to.equal('Close');
expect(controller.term('close')).to.equal('Close');
});
it('should update the term when the language changes', async () => {
// Load Danish
umbTranslationRegistry.loadLanguage(danishRegional.meta.culture);
await aTimeout(0);
expect(document.documentElement.lang).to.equal(danishRegional.meta.culture);
// Force an element update as well
element.lang = danishRegional.meta.culture;
await elementUpdated(element);
expect(element.localize.term('general_close')).to.equal('Luk');
});
it('should update the term when the dir changes', async () => {
expect(element.localize.term('general_close')).to.equal('Close');
element.dir = 'rtl';
await elementUpdated(element);
expect(element.localize.term('general_close')).to.equal('Close');
});
it('should provide a secondary term when the term is not found', async () => {
// Load Danish
umbTranslationRegistry.loadLanguage(danishRegional.meta.culture);
// Change language
document.documentElement.lang = danishRegional.$code;
await aTimeout(0);
element.lang = danishRegional.meta.culture;
await elementUpdated(element);
expect(element.localize.term('general_notOnRegional')).to.equal('Not on regional');
expect(controller.term('close')).to.equal('Luk');
});
it('should provide a fallback term when the term is not found', async () => {
it('should provide a secondary term when the term is not found on the regional language', async () => {
// Load Danish
umbTranslationRegistry.loadLanguage(danishRegional.meta.culture);
document.documentElement.lang = danishRegional.$code;
await aTimeout(0);
element.lang = danishRegional.meta.culture;
await elementUpdated(element);
expect(element.localize.term('general_close')).to.equal('Luk');
expect(element.localize.term('general_logout')).to.equal('Log out');
expect(controller.term('notOnRegional')).to.equal('Not on regional');
});
it('should override a term if new extension is registered', async () => {
umbExtensionsRegistry.register(englishOverride);
it('should provide a fallback term from the fallback language when the term is not found on primary or secondary', async () => {
// Load Danish
document.documentElement.lang = danishRegional.$code;
await aTimeout(0);
expect(controller.term('close')).to.equal('Luk'); // Primary
expect(controller.term('logout')).to.equal('Log out'); // Fallback
});
it('should override a term if new translation is registered', () => {
// Let the registry load the new extension
await aTimeout(0);
await elementUpdated(element);
expect(element.localize.term('general_close')).to.equal('Close 2');
registerTranslation(englishOverride);
expect(controller.term('close')).to.equal('Close 2');
});
it('should return a term with a custom format', async () => {
expect(element.localize.term('general_numUsersSelected', 0)).to.equal('No users selected');
expect(element.localize.term('general_numUsersSelected', 1)).to.equal('One user selected');
expect(element.localize.term('general_numUsersSelected', 2)).to.equal('2 users selected');
expect(controller.term('numUsersSelected', 0)).to.equal('No users selected');
expect(controller.term('numUsersSelected', 1)).to.equal('One user selected');
expect(controller.term('numUsersSelected', 2)).to.equal('2 users selected');
});
it('should return a term with a custom format with inline tokens', async () => {
expect(element.localize.term('general_withInlineToken', 'Hello', 'World')).to.equal('Hello World');
expect(element.localize.term('general_withInlineTokenLegacy', 'Hello', 'World')).to.equal('Hello World');
expect(controller.term('withInlineToken', 'Hello', 'World')).to.equal('Hello World');
expect(controller.term('withInlineTokenLegacy', 'Hello', 'World')).to.equal('Hello World');
});
it('should return a term with no tokens even though they are provided', async () => {
expect(element.localize.term('general_logout', 'Hello', 'World')).to.equal('Log out');
expect(controller.term('logout', 'Hello', 'World')).to.equal('Log out');
});
});
describe('date', () => {
it('should return a date', async () => {
expect(element.localize.date(new Date(2020, 0, 1))).to.equal('1/1/2020');
expect(controller.date(new Date(2020, 0, 1))).to.equal('1/1/2020');
});
it('should accept a string input', async () => {
expect(element.localize.date('2020-01-01')).to.equal('1/1/2020');
expect(controller.date('2020-01-01')).to.equal('1/1/2020');
});
it('should update the date when the language changes', async () => {
expect(element.localize.date(new Date(2020, 11, 31))).to.equal('12/31/2020');
expect(controller.date(new Date(2020, 11, 31))).to.equal('12/31/2020');
// Switch browser to Danish
element.lang = danish.meta.culture;
document.documentElement.lang = danishRegional.$code;
await aTimeout(0);
await elementUpdated(element);
expect(element.localize.date(new Date(2020, 11, 31))).to.equal('31.12.2020');
expect(controller.date(new Date(2020, 11, 31))).to.equal('31.12.2020');
});
it('should update the date when the dir changes', async () => {
expect(element.localize.date(new Date(2020, 11, 31))).to.equal('12/31/2020');
element.dir = 'rtl';
await elementUpdated(element);
expect(element.localize.date(new Date(2020, 11, 31))).to.equal('12/31/2020');
});
it('should return a date with a custom format', async () => {
expect(
element.localize.date(new Date(2020, 11, 31), { month: 'long', day: '2-digit', year: 'numeric' })
).to.equal('December 31, 2020');
it('should return a date with a custom format', () => {
expect(controller.date(new Date(2020, 11, 31), { month: 'long', day: '2-digit', year: 'numeric' })).to.equal(
'December 31, 2020'
);
});
});
describe('number', () => {
it('should return a number', async () => {
expect(element.localize.number(123456.789)).to.equal('123,456.789');
it('should return a number', () => {
expect(controller.number(123456.789)).to.equal('123,456.789');
});
it('should accept a string input', async () => {
expect(element.localize.number('123456.789')).to.equal('123,456.789');
it('should accept a string input', () => {
expect(controller.number('123456.789')).to.equal('123,456.789');
});
it('should update the number when the language changes', async () => {
expect(element.localize.number(123456.789)).to.equal('123,456.789');
// Switch browser to Danish
element.lang = danish.meta.culture;
document.documentElement.lang = danishRegional.$code;
await aTimeout(0);
await elementUpdated(element);
expect(element.localize.number(123456.789)).to.equal('123.456,789');
expect(controller.number(123456.789)).to.equal('123.456,789');
});
it('should update the number when the dir changes', async () => {
expect(element.localize.number(123456.789)).to.equal('123,456.789');
element.dir = 'rtl';
await elementUpdated(element);
expect(element.localize.number(123456.789)).to.equal('123,456.789');
});
it('should return a number with a custom format', async () => {
expect(element.localize.number(123456.789, { minimumFractionDigits: 2, maximumFractionDigits: 2 })).to.equal(
it('should return a number with a custom format', () => {
expect(controller.number(123456.789, { minimumFractionDigits: 2, maximumFractionDigits: 2 })).to.equal(
'123,456.79'
);
});
});
describe('relative time', () => {
it('should return a relative time', async () => {
expect(element.localize.relativeTime(2, 'days')).to.equal('in 2 days');
it('should return a relative time', () => {
expect(controller.relativeTime(2, 'days')).to.equal('in 2 days');
});
it('should update the relative time when the language changes', async () => {
expect(element.localize.relativeTime(2, 'days')).to.equal('in 2 days');
// Switch browser to Danish
document.documentElement.lang = danishRegional.$code;
await aTimeout(0);
expect(controller.relativeTime(2, 'days')).to.equal('om 2 dage');
});
});
describe('host element', () => {
let element: UmbLocalizeControllerHostElement;
beforeEach(async () => {
element = await fixture(html`<umb-localize-controller-host></umb-localize-controller-host>`);
});
it('should have a localize controller', () => {
expect(element.localize).to.be.instanceOf(UmbLocalizeController);
});
it('should update the term when the language changes', async () => {
expect(element.localize.term('close')).to.equal('Close');
// Switch browser to Danish
element.lang = danish.meta.culture;
element.lang = danishRegional.$code;
await elementUpdated(element);
expect(element.localize.relativeTime(2, 'days')).to.equal('om 2 dage');
expect(element.localize.term('close')).to.equal('Luk');
});
});
});

View File

@@ -68,8 +68,6 @@ export class UmbLocalizeController<TranslationType extends TranslationSet = Defa
}
destroy(): void {
// We do not need to call delete here, as hostDisconnected is called when controller is removed.
//connectedElements.delete(this.host);
this.#host.removeController(this);
this.#hostEl = undefined as any;
}

View File

@@ -11,7 +11,7 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import type { UmbTranslationEntry } from '@umbraco-cms/backoffice/extension-registry';
import type { UmbTranslationEntry } from './types/translation.js';
import type { LitElement } from '@umbraco-cms/backoffice/external/lit';
export type FunctionParams<T> = T extends (...args: infer U) => string ? U : [];

View File

@@ -1,3 +1,4 @@
export type UmbTranslationEntry = string | ((...args: never[]) => string);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type UmbTranslationEntry = string | ((...args: any[]) => string);
export type UmbTranslationsDictionary = Record<string, Record<string, UmbTranslationEntry>>;
export type UmbTranslationsFlatDictionary = Record<string, UmbTranslationEntry>;