lighthouse 10.0.0 → 10.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/changelog-pre10.md +6085 -0
  2. package/core/audits/dobetterweb/doctype.d.ts +3 -2
  3. package/core/audits/dobetterweb/doctype.js +15 -4
  4. package/core/audits/dobetterweb/paste-preventing-inputs.js +2 -2
  5. package/core/gather/driver/network-monitor.js +1 -3
  6. package/core/gather/gatherers/bf-cache-failures.js +7 -8
  7. package/package.json +2 -2
  8. package/shared/localization/locales/en-US.json +1 -1
  9. package/shared/localization/locales/en-XL.json +1 -1
  10. package/.codecov.yml +0 -22
  11. package/.cz-config.js +0 -24
  12. package/.gitattributes +0 -5
  13. package/.mailmap +0 -6
  14. package/CODE_OF_CONDUCT.md +0 -93
  15. package/eslint-local-rules.cjs +0 -106
  16. package/flow-report/test/app-test.d.ts +0 -7
  17. package/flow-report/test/app-test.tsx +0 -52
  18. package/flow-report/test/common-test.d.ts +0 -7
  19. package/flow-report/test/common-test.tsx +0 -103
  20. package/flow-report/test/flow-report-pptr-test.d.ts +0 -7
  21. package/flow-report/test/flow-report-pptr-test.ts +0 -59
  22. package/flow-report/test/header-test.d.ts +0 -7
  23. package/flow-report/test/header-test.tsx +0 -55
  24. package/flow-report/test/run-flow-report-tests.sh +0 -20
  25. package/flow-report/test/sample-flow.d.ts +0 -7
  26. package/flow-report/test/sample-flow.ts +0 -17
  27. package/flow-report/test/setup/env-setup.d.ts +0 -11
  28. package/flow-report/test/setup/env-setup.ts +0 -55
  29. package/flow-report/test/sidebar/flow-test.d.ts +0 -7
  30. package/flow-report/test/sidebar/flow-test.tsx +0 -65
  31. package/flow-report/test/sidebar/sidebar-test.d.ts +0 -7
  32. package/flow-report/test/sidebar/sidebar-test.tsx +0 -116
  33. package/flow-report/test/summary/category-test.d.ts +0 -7
  34. package/flow-report/test/summary/category-test.tsx +0 -206
  35. package/flow-report/test/summary/summary-test.d.ts +0 -7
  36. package/flow-report/test/summary/summary-test.tsx +0 -124
  37. package/flow-report/test/topbar-test.d.ts +0 -7
  38. package/flow-report/test/topbar-test.tsx +0 -84
  39. package/flow-report/test/util-test.d.ts +0 -7
  40. package/flow-report/test/util-test.tsx +0 -133
  41. package/flow-report/test/wrappers/category-score-test.d.ts +0 -7
  42. package/flow-report/test/wrappers/category-score-test.tsx +0 -84
  43. package/flow-report/test/wrappers/markdown-test.d.ts +0 -7
  44. package/flow-report/test/wrappers/markdown-test.tsx +0 -17
  45. package/report/test/clients/bundle-test.js +0 -65
  46. package/report/test/generator/file-namer-test.js +0 -27
  47. package/report/test/generator/report-generator-test.js +0 -160
  48. package/report/test/renderer/__snapshots__/report-renderer-axe-test.js.snap +0 -15
  49. package/report/test/renderer/category-renderer-test.js +0 -575
  50. package/report/test/renderer/components-test.js +0 -124
  51. package/report/test/renderer/crc-details-renderer-test.js +0 -116
  52. package/report/test/renderer/details-renderer-test.js +0 -882
  53. package/report/test/renderer/dom-test.js +0 -257
  54. package/report/test/renderer/element-screenshot-renderer-test.js +0 -183
  55. package/report/test/renderer/i18n-formatter-test.js +0 -179
  56. package/report/test/renderer/performance-category-renderer-test.js +0 -417
  57. package/report/test/renderer/pwa-category-renderer-test.js +0 -301
  58. package/report/test/renderer/report-renderer-axe-test.js +0 -85
  59. package/report/test/renderer/report-renderer-test.js +0 -268
  60. package/report/test/renderer/report-ui-features-test.js +0 -773
  61. package/report/test/renderer/report-utils-test.js +0 -219
  62. package/report/test/renderer/snippet-renderer-test.js +0 -291
  63. package/report/test/renderer/text-encoding-test.js +0 -43
  64. package/report/test-assets/faux-psi.d.ts +0 -34
  65. package/shared/test/localization/format-test.js +0 -430
  66. package/shared/test/localization/locales-test.js +0 -45
  67. package/shared/test/localization/swap-locale-test.js +0 -127
  68. package/shared/test/util-test.js +0 -214
  69. package/vercel.json +0 -6
@@ -1,430 +0,0 @@
1
- /**
2
- * @license Copyright 2018 The Lighthouse Authors. All Rights Reserved.
3
- * Licensed 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
4
- * 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.
5
- */
6
-
7
- import path from 'path';
8
-
9
- import * as i18n from '../../../core/lib/i18n/i18n.js';
10
- import * as constants from '../../../core/config/constants.js';
11
- import * as format from '../../localization/format.js';
12
- import {locales} from '../../localization/locales.js';
13
- import {getModuleDirectory, getModulePath} from '../../../esm-utils.js';
14
- import {LH_ROOT} from '../../../root.js';
15
-
16
- const moduleDir = getModuleDirectory(import.meta);
17
- const modulePath = getModulePath(import.meta);
18
-
19
- describe('format', () => {
20
- describe('DEFAULT_LOCALE', () => {
21
- it('is the same as the default config locale', () => {
22
- expect(format.DEFAULT_LOCALE).toBe(constants.defaultSettings.locale);
23
- });
24
- });
25
-
26
- describe('#getAvailableLocales', () => {
27
- it('has all the available locales', () => {
28
- const availableLocales = format.getAvailableLocales();
29
- for (const locale of ['en', 'es', 'ru', 'zh']) {
30
- expect(availableLocales).toContain(locale);
31
- }
32
-
33
- const rawLocales = Object.keys(locales).sort();
34
- expect(availableLocales.sort()).toEqual(rawLocales);
35
- });
36
-
37
- it('contains the default locale', () => {
38
- expect(format.getAvailableLocales()).toContain(format.DEFAULT_LOCALE);
39
- });
40
- });
41
-
42
- describe('#getCanonicalLocales', () => {
43
- it('contains some canonical locales', () => {
44
- const canonicalLocales = format.getCanonicalLocales();
45
- for (const locale of ['en-US', 'es', 'ru', 'zh']) {
46
- expect(canonicalLocales).toContain(locale);
47
- }
48
- });
49
-
50
- it('is a subset of the available locales', () => {
51
- const canonicalLocales = format.getCanonicalLocales();
52
- const availableLocales = format.getAvailableLocales();
53
-
54
- for (const canonicalLocale of canonicalLocales) {
55
- expect(availableLocales).toContain(canonicalLocale);
56
- }
57
-
58
- expect(canonicalLocales.length).toBeLessThan(availableLocales.length);
59
- });
60
- });
61
-
62
- describe('#_formatPathAsString', () => {
63
- it('handles simple paths', () => {
64
- expect(format._formatPathAsString(['foo'])).toBe('foo');
65
- expect(format._formatPathAsString(['foo', 'bar', 'baz'])).toBe('foo.bar.baz');
66
- });
67
-
68
- it('handles array paths', () => {
69
- expect(format._formatPathAsString(['foo', 0])).toBe('foo[0]');
70
- });
71
-
72
- it('handles complex paths', () => {
73
- const propertyPath = ['foo', 'what-the', 'bar', 0, 'no'];
74
- expect(format._formatPathAsString(propertyPath)).toBe('foo[what-the].bar[0].no');
75
- });
76
-
77
- it('throws on unhandleable paths', () => {
78
- expect(() => format._formatPathAsString(['Bobby "DROP TABLE'])).toThrow(/Cannot handle/);
79
- });
80
- });
81
-
82
- describe('#replaceIcuMessages', () => {
83
- it('replaces the references in the LHR', () => {
84
- const fakeFile = path.join(moduleDir, 'fake-file-number-2.js');
85
- const UIStrings = {aString: 'different {x}!'};
86
- const formatter = i18n.createIcuMessageFn(fakeFile, UIStrings);
87
-
88
- const title = formatter(UIStrings.aString, {x: 1});
89
- const lhr = {audits: {'fake-audit': {title}}};
90
-
91
- const icuMessagePaths = format.replaceIcuMessages(lhr, 'en-US');
92
- expect(lhr.audits['fake-audit'].title).toBe('different 1!');
93
-
94
- const expectedPathId = 'shared/test/localization/fake-file-number-2.js | aString';
95
- expect(icuMessagePaths).toEqual({
96
- [expectedPathId]: [{path: 'audits[fake-audit].title', values: {x: 1}}]});
97
- });
98
- });
99
-
100
- describe('#getRendererFormattedStrings', () => {
101
- it('returns icu messages in the specified locale', () => {
102
- const strings = format.getRendererFormattedStrings('en-XA');
103
- expect(strings.passedAuditsGroupTitle).toEqual('[Þåššéð åûðîţš one two]');
104
- expect(strings.snippetCollapseButtonLabel).toEqual('[Çöļļåþšé šñîþþéţ one two]');
105
- });
106
-
107
- it('throws an error for invalid locales', () => {
108
- expect(_ => format.getRendererFormattedStrings('not-a-locale'))
109
- .toThrow(`Unsupported locale 'not-a-locale'`);
110
- });
111
- });
112
-
113
- describe('#getFormatted', () => {
114
- it('returns the formatted string', () => {
115
- const UIStrings = {testMessage: 'happy test'};
116
- const str_ = i18n.createIcuMessageFn(modulePath, UIStrings);
117
- const formattedStr = format.getFormatted(str_(UIStrings.testMessage), 'en');
118
- expect(formattedStr).toEqual('happy test');
119
- });
120
-
121
- it('returns the formatted string with replacements', () => {
122
- const UIStrings = {testMessage: 'replacement test ({errorCode})'};
123
- const str_ = i18n.createIcuMessageFn(modulePath, UIStrings);
124
- const formattedStr = format.getFormatted(str_(UIStrings.testMessage,
125
- {errorCode: 'BOO'}), 'en');
126
- expect(formattedStr).toEqual('replacement test (BOO)');
127
- });
128
-
129
- it('throws an error for invalid locales', () => {
130
- // Populate a string to try to localize to a bad locale.
131
- const UIStrings = {testMessage: 'testy test'};
132
- const str_ = i18n.createIcuMessageFn(modulePath, UIStrings);
133
-
134
- expect(_ => format.getFormatted(str_(UIStrings.testMessage), 'still-not-a-locale'))
135
- .toThrow(`Unsupported locale 'still-not-a-locale'`);
136
- });
137
-
138
- it('does not alter the passed-in replacement values object', () => {
139
- const UIStrings = {
140
- testMessage: 'needs {count, number, bytes}KB test {str} in {timeInMs, number, seconds}s',
141
- };
142
- const str_ = i18n.createIcuMessageFn(modulePath, UIStrings);
143
-
144
- const replacements = {
145
- count: 2555,
146
- str: '*units*',
147
- timeInMs: 314159265,
148
- };
149
- const replacementsClone = JSON.parse(JSON.stringify(replacements));
150
-
151
- const formattedStr = format.getFormatted(str_(UIStrings.testMessage, replacements), 'en');
152
- expect(formattedStr).toEqual('needs 2KB test *units* in 314,159.3s');
153
-
154
- expect(replacements).toEqual(replacementsClone);
155
- });
156
-
157
- it('returns a message that is already a string unchanged', () => {
158
- const testString = 'kind of looks like it needs ({formatting})';
159
- const formattedStr = format.getFormatted(testString, 'pl');
160
- expect(formattedStr).toBe(testString);
161
- });
162
-
163
- it('throws an error if formatting something other than IcuMessages or strings', () => {
164
- expect(_ => format.getFormatted(15, 'lt'))
165
- .toThrow(`Attempted to format invalid icuMessage type`);
166
- expect(_ => format.getFormatted(new Date(), 'sr-Latn'))
167
- .toThrow(`Attempted to format invalid icuMessage type`);
168
- });
169
- });
170
-
171
- describe('#registerLocaleData', () => {
172
- // Store original locale data so we can restore at the end
173
- const clonedLocales = JSON.parse(JSON.stringify(locales));
174
-
175
- it('installs new locale strings', () => {
176
- const localeData = {
177
- 'shared/test/localization/format-test.js | testString': {
178
- 'message': 'en-XZ cuerda!',
179
- },
180
- };
181
- format.registerLocaleData('en-XZ', localeData);
182
-
183
- const UIStrings = {testString: 'en-US string!'};
184
- const str_ = i18n.createIcuMessageFn(modulePath, UIStrings);
185
- const formattedStr = format.getFormatted(str_(UIStrings.testString), 'en-XZ');
186
- expect(formattedStr).toEqual('en-XZ cuerda!');
187
- });
188
-
189
- [
190
- {label: 'relative path', filename: 'core/audits/is-on-https.js'},
191
- {label: 'absolute path', filename: `${LH_ROOT}/core/audits/is-on-https.js`},
192
- ].forEach(({label, filename}) => {
193
- it(`overwrites existing locale strings: ${label}`, async () => {
194
- const {UIStrings} = await import('../../../core/audits/is-on-https.js');
195
- const str_ = i18n.createIcuMessageFn(filename, UIStrings);
196
-
197
- // To start with, we get back the intended string..
198
- const origTitle = format.getFormatted(str_(UIStrings.title), 'es-419');
199
- expect(origTitle).toEqual('Usa HTTPS');
200
- const origFailureTitle = format.getFormatted(str_(UIStrings.failureTitle), 'es-419');
201
- expect(origFailureTitle).toEqual('No usa HTTPS');
202
-
203
- // Now we declare and register the new string...
204
- const localeData = {
205
- 'core/audits/is-on-https.js | title': {
206
- 'message': 'new string for es-419 uses https!',
207
- },
208
- };
209
- format.registerLocaleData('es-419', localeData);
210
-
211
- // And confirm that's what is returned
212
- const newTitle = format.getFormatted(str_(UIStrings.title), 'es-419');
213
- expect(newTitle).toEqual('new string for es-419 uses https!');
214
-
215
- // Meanwhile another string that wasn't set in registerLocaleData just falls back to english
216
- const newFailureTitle = format.getFormatted(str_(UIStrings.failureTitle), 'es-419');
217
- expect(newFailureTitle).toEqual('Does not use HTTPS');
218
-
219
- // Restore overwritten strings to avoid messing with other tests
220
- locales['es-419'] = clonedLocales['es-419'];
221
- const title = format.getFormatted(str_(UIStrings.title), 'es-419');
222
- expect(title).toEqual('Usa HTTPS');
223
- });
224
- });
225
- });
226
-
227
- describe('#isIcuMessage', () => {
228
- const icuMessage = {
229
- i18nId: 'shared/test/localization/fake-file.js | title',
230
- values: {x: 1},
231
- formattedDefault: 'a default',
232
- };
233
-
234
- it('passes a valid LH.IcuMessage', () => {
235
- expect(format.isIcuMessage(icuMessage)).toBe(true);
236
- });
237
-
238
- it('fails non-objects', () => {
239
- expect(format.isIcuMessage(undefined)).toBe(false);
240
- expect(format.isIcuMessage(null)).toBe(false);
241
- expect(format.isIcuMessage('ICU!')).toBe(false);
242
- expect(format.isIcuMessage(55)).toBe(false);
243
- expect(format.isIcuMessage([
244
- icuMessage,
245
- icuMessage,
246
- ])).toBe(false);
247
- });
248
-
249
- it('fails invalid or missing i18nIds', () => {
250
- const badIdMessage = {...icuMessage, i18nId: 0};
251
- expect(format.isIcuMessage(badIdMessage)).toBe(false);
252
-
253
- const noIdMessage = {...icuMessage};
254
- delete noIdMessage.i18nId;
255
- expect(format.isIcuMessage(noIdMessage)).toBe(false);
256
- });
257
-
258
- it('fails invalid or missing formattedDefault', () => {
259
- const badDefaultMessage = {...icuMessage, formattedDefault: -0};
260
- expect(format.isIcuMessage(badDefaultMessage)).toBe(false);
261
-
262
- const noDefaultMessage = {...icuMessage};
263
- delete noDefaultMessage.formattedDefault;
264
- expect(format.isIcuMessage(noDefaultMessage)).toBe(false);
265
- });
266
-
267
- it('passes missing values', () => {
268
- const emptyValuesMessage = {...icuMessage, values: {}};
269
- expect(format.isIcuMessage(emptyValuesMessage)).toBe(true);
270
-
271
- const noValuesMessage = {...icuMessage};
272
- delete noValuesMessage.values;
273
- expect(format.isIcuMessage(noValuesMessage)).toBe(true);
274
- });
275
-
276
- it('fails invalid values types', () => {
277
- const badValuesMessage = {...icuMessage, values: NaN};
278
- expect(format.isIcuMessage(badValuesMessage)).toBe(false);
279
- const nullValuesMessage = {...icuMessage, values: null};
280
- expect(format.isIcuMessage(nullValuesMessage)).toBe(false);
281
- });
282
-
283
- it(`fails invalid values' values types`, () => {
284
- const badValuesValuesMessage = {...icuMessage, values: {a: false}};
285
- expect(format.isIcuMessage(badValuesValuesMessage)).toBe(false);
286
- });
287
- });
288
-
289
- describe('#getIcuMessageIdParts', () => {
290
- it('returns valid ICU message id parts', () => {
291
- const {filename, key} = format.getIcuMessageIdParts('path/to/file.js | modeName');
292
- expect(filename).toEqual('path/to/file.js');
293
- expect(key).toEqual('modeName');
294
- });
295
-
296
- it('throws on invalid ICU message id', () => {
297
- expect(() => {
298
- format.getIcuMessageIdParts('path/to/file.js');
299
- }).toThrow();
300
- });
301
- });
302
-
303
- describe('Message values are properly formatted', () => {
304
- // Message strings won't be in locale files, so will fall back to values given here.
305
- const UIStrings = {
306
- helloWorld: 'Hello World',
307
- helloBytesWorld: 'Hello {in, number, bytes} World',
308
- helloMsWorld: 'Hello {in, number, milliseconds} World',
309
- helloSecWorld: 'Hello {in, number, seconds} World',
310
- helloTimeInMsWorld: 'Hello {timeInMs, number, seconds} World',
311
- helloPercentWorld: 'Hello {in, number, extendedPercent} World',
312
- helloWorldMultiReplace: '{hello} {world}',
313
- helloPlural: '{itemCount, plural, =1{1 hello} other{hellos}}',
314
- helloPluralNestedICU: '{itemCount, plural, ' +
315
- '=1{1 hello {in, number, bytes}} ' +
316
- 'other{hellos {in, number, bytes}}}',
317
- helloPluralNestedPluralAndICU: '{itemCount, plural, ' +
318
- '=1{{innerItemCount, plural, ' +
319
- '=1{1 hello 1 goodbye {in, number, bytes}} ' +
320
- 'other{1 hello, goodbyes {in, number, bytes}}}} ' +
321
- 'other{{innerItemCount, plural, ' +
322
- '=1{hellos 1 goodbye {in, number, bytes}} ' +
323
- 'other{hellos, goodbyes {in, number, bytes}}}}}',
324
- };
325
-
326
- let str_;
327
- before(() => {
328
- str_ = i18n.createIcuMessageFn(modulePath, UIStrings);
329
- });
330
-
331
- it('formats a basic message', () => {
332
- const helloStr = str_(UIStrings.helloWorld);
333
- expect(helloStr).toBeDisplayString('Hello World');
334
- });
335
-
336
- it('formats a message with bytes', () => {
337
- const helloBytesStr = str_(UIStrings.helloBytesWorld, {in: 1875});
338
- expect(helloBytesStr).toBeDisplayString('Hello 2 World');
339
- });
340
-
341
- it('formats a message with milliseconds', () => {
342
- const helloMsStr = str_(UIStrings.helloMsWorld, {in: 432});
343
- expect(helloMsStr).toBeDisplayString('Hello 430 World');
344
- });
345
-
346
- it('formats a message with seconds', () => {
347
- const helloSecStr = str_(UIStrings.helloSecWorld, {in: 753});
348
- expect(helloSecStr).toBeDisplayString('Hello 753.0 World');
349
- });
350
-
351
- it('formats a message with seconds timeInMs', () => {
352
- const helloTimeInMsStr = str_(UIStrings.helloTimeInMsWorld, {timeInMs: 753543});
353
- expect(helloTimeInMsStr).toBeDisplayString('Hello 753.5 World');
354
- });
355
-
356
- it('formats a message with extended percent', () => {
357
- const helloPercentStr = str_(UIStrings.helloPercentWorld, {in: 0.43078});
358
- expect(helloPercentStr).toBeDisplayString('Hello 43.08% World');
359
- });
360
-
361
- it('throws an error when values are needed but not provided', () => {
362
- expect(_ => format.getFormatted(str_(UIStrings.helloBytesWorld), 'en-US'))
363
- // eslint-disable-next-line max-len
364
- .toThrow(`ICU Message "Hello {in, number, bytes} World" contains a value reference ("in") that wasn't provided`);
365
- });
366
-
367
- it('throws an error when a value is missing', () => {
368
- expect(_ => format.getFormatted(str_(UIStrings.helloWorldMultiReplace,
369
- {hello: 'hello'}), 'en-US'))
370
- // eslint-disable-next-line max-len
371
- .toThrow(`ICU Message "{hello} {world}" contains a value reference ("world") that wasn't provided`);
372
- });
373
-
374
- it('formats a message with plurals', () => {
375
- const helloStr = str_(UIStrings.helloPlural, {itemCount: 3});
376
- expect(helloStr).toBeDisplayString('hellos');
377
- });
378
-
379
- it('throws an error when a plural control value is missing', () => {
380
- expect(_ => i18n.getFormatted(str_(UIStrings.helloPlural), 'en-US'))
381
- // eslint-disable-next-line max-len
382
- .toThrow(`ICU Message "{itemCount, plural, =1{1 hello} other{hellos}}" contains a value reference ("itemCount") that wasn't provided`);
383
- });
384
-
385
- it('formats a message with plurals and nested custom ICU', () => {
386
- const helloStr = str_(UIStrings.helloPluralNestedICU, {itemCount: 3, in: 1875});
387
- expect(helloStr).toBeDisplayString('hellos 2');
388
- });
389
-
390
- it('formats a message with plurals and nested custom ICU and nested plural', () => {
391
- const helloStr = str_(UIStrings.helloPluralNestedPluralAndICU, {itemCount: 3,
392
- innerItemCount: 1,
393
- in: 1875});
394
- expect(helloStr).toBeDisplayString('hellos 1 goodbye 2');
395
- });
396
-
397
- it('throws an error if a string value is used for a numeric placeholder', () => {
398
- expect(_ => str_(UIStrings.helloTimeInMsWorld, {
399
- timeInMs: 'string not a number',
400
- }))
401
- // eslint-disable-next-line max-len
402
- .toThrow(`ICU Message "Hello {timeInMs, number, seconds} World" contains a numeric reference ("timeInMs") but provided value was not a number`);
403
- });
404
-
405
- it('throws an error if a value is provided that has no placeholder in the message', () => {
406
- expect(_ => str_(UIStrings.helloTimeInMsWorld, {
407
- timeInMs: 55,
408
- sirNotAppearingInThisString: 66,
409
- }))
410
- // eslint-disable-next-line max-len
411
- .toThrow(`Provided value "sirNotAppearingInThisString" does not match any placeholder in ICU message "Hello {timeInMs, number, seconds} World"`);
412
- });
413
-
414
- it('throws an error if a value is provided for a message with no placeholders', () => {
415
- expect(_ => str_(UIStrings.helloWorld, {
416
- extraCreditValue: 100,
417
- }))
418
- // eslint-disable-next-line max-len
419
- .toThrow(`Provided value "extraCreditValue" does not match any placeholder in ICU message "Hello World"`);
420
- });
421
-
422
- it('formats correctly with NaN and Infinity numeric values', () => {
423
- const helloInfinityStr = str_(UIStrings.helloBytesWorld, {in: Infinity});
424
- expect(helloInfinityStr).toBeDisplayString('Hello ∞ World');
425
-
426
- const helloNaNStr = str_(UIStrings.helloBytesWorld, {in: NaN});
427
- expect(helloNaNStr).toBeDisplayString('Hello NaN World');
428
- });
429
- });
430
- });
@@ -1,45 +0,0 @@
1
- /**
2
- * @license Copyright 2018 The Lighthouse Authors. All Rights Reserved.
3
- * Licensed 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
4
- * 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.
5
- */
6
-
7
- import assert from 'assert/strict';
8
-
9
- import {locales} from '../../localization/locales.js';
10
-
11
- describe('locales', () => {
12
- it('has only canonical (or expected-deprecated) language tags', () => {
13
- // Map of deprecated codes to their canonical version. Depending on the ICU
14
- // version used to run Lighthouse/this test, these *may* come back as their
15
- // substitute, not themselves.
16
- const deprecatedCodes = {
17
- in: 'id',
18
- iw: 'he',
19
- mo: 'ro',
20
- tl: 'fil',
21
- };
22
-
23
- for (const locale of Object.keys(locales)) {
24
- const canonicalLocale = Intl.getCanonicalLocales(locale)[0];
25
- const substitute = deprecatedCodes[locale];
26
- assert.ok(locale === canonicalLocale || substitute === canonicalLocale,
27
- `locale code '${locale}' not canonical ('${canonicalLocale}' found instead)`);
28
- }
29
-
30
- // Deprecation subsitutes should be removed from the test if no longer used.
31
- for (const locale of Object.keys(deprecatedCodes)) {
32
- assert.ok(locales[locale], `${locale} substitute should be removed from test`);
33
- }
34
- });
35
-
36
- it('has a base language prefix fallback for all supported languages', () => {
37
- for (const locale of Object.keys(locales)) {
38
- const basePrefix = locale.split('-')[0];
39
- // The internet sez there is no canonical Chinese, so we exclude that one.
40
- if (basePrefix !== 'zh') {
41
- assert.ok(locales[basePrefix], `${locale} is missing a base fallback`);
42
- }
43
- }
44
- });
45
- });
@@ -1,127 +0,0 @@
1
- /**
2
- * @license Copyright 2019 The Lighthouse Authors. All Rights Reserved.
3
- * Licensed 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
4
- * 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.
5
- */
6
-
7
- import {swapLocale} from '../../localization/swap-locale.js';
8
- import {readJson} from '../../../core/test/test-utils.js';
9
-
10
- const lhr = readJson('core/test/results/sample_v2.json');
11
-
12
- describe('swap-locale', () => {
13
- it('does not mutate the original lhr', () => {
14
- /** @type {LH.Result} */
15
- const lhrClone = JSON.parse(JSON.stringify(lhr));
16
-
17
- const lhrPt = swapLocale(lhr, 'pt').lhr;
18
- expect(lhrPt).not.toStrictEqual(lhr);
19
- expect(lhr).toStrictEqual(lhrClone);
20
- });
21
-
22
- it('can change golden LHR english strings into german', () => {
23
- /** @type {LH.Result} */
24
- const lhrEn = JSON.parse(JSON.stringify(lhr));
25
- const lhrDe = swapLocale(lhrEn, 'de').lhr;
26
-
27
- // Basic replacement
28
- expect(lhrEn.audits.plugins.title).toEqual('Document avoids plugins');
29
- expect(lhrDe.audits.plugins.title).toEqual('Dokument verwendet keine Plug-ins');
30
-
31
- // With ICU string argument values
32
- expect(lhrEn.audits['dom-size'].displayValue).toMatchInlineSnapshot(`"153 elements"`);
33
- /* eslint-disable no-irregular-whitespace */
34
- expect(lhrDe.audits['dom-size'].displayValue).toMatchInlineSnapshot(`"153 Elemente"`);
35
-
36
- // Renderer formatted strings
37
- expect(lhrEn.i18n.rendererFormattedStrings.labDataTitle).toEqual('Lab Data');
38
- expect(lhrDe.i18n.rendererFormattedStrings.labDataTitle).toEqual('Labdaten');
39
-
40
- // Formatted numbers in placeholders.
41
- expect(lhrEn.audits['mainthread-work-breakdown'].displayValue).
42
- toMatchInlineSnapshot(`"2.2 s"`);
43
- expect(lhrDe.audits['mainthread-work-breakdown'].displayValue).
44
- toMatchInlineSnapshot(`"2,2 s"`);
45
- /* eslint-enable no-irregular-whitespace */
46
- });
47
-
48
- it('can roundtrip back to english correctly', () => {
49
- /** @type {LH.Result} */
50
- const lhrEn = JSON.parse(JSON.stringify(lhr));
51
-
52
- // via Spanish
53
- const lhrEnEsRT = swapLocale(swapLocale(lhrEn, 'es').lhr, 'en-US').lhr;
54
- expect(lhrEnEsRT).toEqual(lhrEn);
55
-
56
- // via Arabic
57
- const lhrEnArRT = swapLocale(swapLocale(lhrEn, 'ar').lhr, 'en-US').lhr;
58
- expect(lhrEnArRT).toEqual(lhrEn);
59
- });
60
-
61
- it('leaves alone messages where there is no translation available', () => {
62
- const miniLHR = {
63
- audits: {
64
- redirects: {
65
- id: 'redirects',
66
- title: 'Avoid multiple page redirects',
67
- doesntExist: 'A string that does not have localized versions',
68
- },
69
- fakeaudit: {
70
- id: 'fakeaudit',
71
- title: 'An audit without translations',
72
- },
73
- },
74
- configSettings: {
75
- locale: 'en-US',
76
- },
77
- i18n: {
78
- icuMessagePaths: {
79
- 'core/audits/redirects.js | title': ['audits.redirects.title'],
80
- // File that exists, but `doesntExist` message within it does not.
81
- 'core/audits/redirects.js | doesntExist': ['audits.redirects.doesntExist'],
82
- // File and message which do not exist.
83
- 'core/audits/fakeaudit.js | title': ['audits.fakeaudit.title'],
84
- },
85
- },
86
- };
87
- const {missingIcuMessageIds} = swapLocale(miniLHR, 'es');
88
-
89
- // Updated strings are not found, so these remain in the original language
90
- expect(missingIcuMessageIds).toMatchInlineSnapshot(`
91
- Array [
92
- "core/audits/redirects.js | doesntExist",
93
- "core/audits/fakeaudit.js | title",
94
- ]
95
- `);
96
- });
97
-
98
- it('does not change properties that are not strings', () => {
99
- // Unlikely, but possible e.g. if an audit details changed shape over LH versions.
100
- const miniLhr = {
101
- audits: {
102
- redirects: {
103
- id: 'redirects',
104
- title: 'Avoid multiple page redirects',
105
- },
106
- },
107
- configSettings: {
108
- locale: 'en-US',
109
- },
110
- i18n: {
111
- icuMessagePaths: {
112
- // Points to audit object, not string.
113
- 'core/audits/redirects.js | title': ['audits.redirects'],
114
- // Path does not point to anything in LHR.
115
- 'core/audits/redirects.js | description': ['gatherers..X'],
116
- },
117
- },
118
- };
119
- const testLocale = 'ru';
120
- const {lhr} = swapLocale(miniLhr, testLocale);
121
-
122
- // LHR remains unchanged except for locale and injected `rendererFormattedStrings`.
123
- miniLhr.configSettings.locale = testLocale;
124
- miniLhr.i18n.rendererFormattedStrings = expect.any(Object);
125
- expect(lhr).toEqual(miniLhr);
126
- });
127
- });