UNPKG

chrome-devtools-frontend

Version:
208 lines (174 loc) • 8.34 kB
// Copyright 2021 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as i18nRaw from '../../third_party/i18n/i18n.js'; import * as i18n from './i18n.js'; describe('serializeUIString', () => { it('serializes strings without placeholders', () => { const output = i18n.i18n.serializeUIString('foo'); assert.deepEqual(output, JSON.stringify({ string: 'foo', values: {}, })); }); it('serializes strings with placeholder values', () => { const output = i18n.i18n.serializeUIString('a string', {PH1: 'value1', PH2: 'value2'}); assert.deepEqual(output, JSON.stringify({ string: 'a string', values: {PH1: 'value1', PH2: 'value2'}, })); }); }); describe('deserializeUIString', () => { it('returns an empty object for an empty string input', () => { const output = i18n.i18n.deserializeUIString(''); assert.deepEqual(output, {string: '', values: {}}); }); it('deserializes correctly for a string with no placeholders', () => { const output = i18n.i18n.deserializeUIString('{"string":"foo", "values":{}}'); assert.deepEqual(output, {string: 'foo', values: {}}); }); it('deserializes correctly for a string with placeholders', () => { const output = i18n.i18n.deserializeUIString('{"string":"foo", "values":{"PH1": "value1"}}'); assert.deepEqual(output, {string: 'foo', values: {PH1: 'value1'}}); }); }); describe('serialize/deserialize round-trip', () => { it('returns a matching input/output', () => { const inputString = 'a string'; const serializedString = i18n.i18n.serializeUIString(inputString); const deserializedString = i18n.i18n.deserializeUIString(serializedString); assert.deepEqual(deserializedString, { string: inputString, values: {}, }); }); }); describe('getLocalizedLanguageRegion', () => { function createMockDevToolsLocale(locale: string): i18n.DevToolsLocale.DevToolsLocale { return {locale, forceFallbackLocale: () => {}} as i18n.DevToolsLocale.DevToolsLocale; } it('build the correct language/region string', () => { assert.strictEqual( i18n.i18n.getLocalizedLanguageRegion('de-AT', createMockDevToolsLocale('en-US')), 'German (Austria) - Deutsch (Österreich)'); assert.strictEqual( i18n.i18n.getLocalizedLanguageRegion('de', createMockDevToolsLocale('en-US')), 'German - Deutsch'); assert.strictEqual( i18n.i18n.getLocalizedLanguageRegion('en-US', createMockDevToolsLocale('de')), 'Englisch (USA) - English (US)'); }); it('uses english for the target locale if the languages match', () => { assert.strictEqual( i18n.i18n.getLocalizedLanguageRegion('de-AT', createMockDevToolsLocale('de')), 'Deutsch (Österreich) - German (Austria)'); assert.strictEqual(i18n.i18n.getLocalizedLanguageRegion('de', createMockDevToolsLocale('de')), 'Deutsch - German'); }); }); describe('getFormatLocalizedString', () => { let i18nInstance: i18nRaw.I18n.I18n; beforeEach(() => { i18n.DevToolsLocale.DevToolsLocale.instance({ create: true, data: { navigatorLanguage: 'en-US', settingLanguage: 'en-US', lookupClosestDevToolsLocale: () => 'en-US', }, }); i18nInstance = new i18nRaw.I18n.I18n(['en-US'], 'en-US'); i18nInstance.registerLocaleData('en-US', {}); // Always fall back to UIStrings. }); it('returns an HTML element', () => { const uiStrings = {simple: 'a simple message'}; const registeredStrings = i18nInstance.registerFileStrings('test.ts', uiStrings); const messageElement = i18n.i18n.getFormatLocalizedString(registeredStrings, uiStrings.simple, {}); assert.instanceOf(messageElement, HTMLElement); assert.strictEqual(messageElement.innerText, 'a simple message'); }); it('nests HTML placeholders in the message element', () => { const uiStrings = {placeholder: 'a message with a {PH1} placeholder'}; const registeredStrings = i18nInstance.registerFileStrings('test.ts', uiStrings); const placeholder = document.createElement('span'); placeholder.innerText = 'very pretty'; const messageElement = i18n.i18n.getFormatLocalizedString(registeredStrings, uiStrings.placeholder, {PH1: placeholder}); assert.instanceOf(messageElement, HTMLElement); assert.strictEqual(messageElement.innerHTML, 'a message with a <span>very pretty</span> placeholder'); }); it('nests string placeholders in the message element', () => { const uiStrings = {placeholder: 'a message with a {PH1} placeholder'}; const registeredStrings = i18nInstance.registerFileStrings('test.ts', uiStrings); const messageElement = i18n.i18n.getFormatLocalizedString(registeredStrings, uiStrings.placeholder, {PH1: 'somewhat nice'}); assert.instanceOf(messageElement, HTMLElement); assert.strictEqual(messageElement.innerHTML, 'a message with a somewhat nice placeholder'); }); }); describe('falling back when a locale errors', () => { it('reverts to using the UIStrings directly', async () => { i18n.DevToolsLocale.DevToolsLocale.instance({ create: true, data: { // Create the locale and set en-GB to default. This test is going to // assert that when there is an error with the en-GB string that we // fallback. navigatorLanguage: 'en-GB', settingLanguage: 'en-GB', lookupClosestDevToolsLocale: () => 'en-GB', }, }); const i18nInstance = new i18nRaw.I18n.I18n(['en-GB', 'en-US'], 'en-US'); // Recreate the bug that can happen in Canary: the en-US translation is up // to date (no PH1 placeholder), but the en-GB translation is out of date. // We expect that in this instance we should fallback to en-US. i18nInstance.registerLocaleData('en-US', { 'test.ts | placeholder': {message: 'US: hello world'}, }); // Register the (outdated) en-GB string. i18nInstance.registerLocaleData('en-GB', { 'test.ts | placeholder': {message: 'GB: hello {PH1}'}, }); const uiStrings = {placeholder: 'US: hello world'}; const registeredStrings = i18nInstance.registerFileStrings('test.ts', uiStrings); // We should see "hello world" because we don't pass the placeholder value, // and that means the en-GB translations are invalid, and we fallback to // the UIStrings. const output = i18n.i18n.getLocalizedString(registeredStrings, uiStrings.placeholder); assert.strictEqual(output, 'US: hello world'); }); }); describe('fetchAndRegisterLocaleData', () => { let fetchStub: sinon.SinonStub; beforeEach(() => { fetchStub = sinon.stub(window, 'fetch'); fetchStub.returns(Promise.resolve(new window.Response(JSON.stringify({}), { // Always return an empty JSON object. status: 200, headers: {'Content-type': 'application/json'}, }))); }); afterEach(() => { fetchStub.restore(); i18n.i18n.resetLocaleDataForTest(); }); const bundled = 'devtools://devtools/bundled/devtools_app.html'; const version = '@ffe848af6a5df4fa127e2929331116b7f9f1cb30'; const remoteOrigin = 'https://chrome-devtools-frontend.appspot.com/'; const remote = `${remoteOrigin}serve_file/${version}/`; const fullLocation = `${bundled}?remoteBase=${remote}&can_dock=true&dockSide=undocked`; it('fetches bundled locale files the same way as i18nImpl.ts itself is loaded', async () => { await i18n.i18n.fetchAndRegisterLocaleData('en-US', fullLocation); // We can't mock `import.meta.url` from i18nImpl so the Karam host leaks into // this test. This means we only check the last part of the URL with which `fetch` // was called. const actualUrl = fetchStub.args[0][0]; assert.isTrue(actualUrl.endsWith('front_end/core/i18n/locales/en-US.json'), `Actually called with ${actualUrl}`); }); it('fetches non-bundled locale files from the remote service endpoint', async () => { await i18n.i18n.fetchAndRegisterLocaleData('de', fullLocation); assert.isTrue( fetchStub.calledWith( 'devtools://devtools/remote/serve_file/@ffe848af6a5df4fa127e2929331116b7f9f1cb30/core/i18n/locales/de.json'), `Actually called with ${fetchStub.args[0][0]}`); }); });