Skip to content

Commit

Permalink
i18n: throw on excess placeholder replacement values (#9580)
Browse files Browse the repository at this point in the history
  • Loading branch information
brendankenny authored and paulirish committed Nov 6, 2019
1 parent d3590bb commit c93fcd2
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 8 deletions.
1 change: 0 additions & 1 deletion lighthouse-core/audits/third-party-summary.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ class ThirdPartySummary extends Audit {
return {
score: Number(summary.wastedMs <= PASS_THRESHOLD_IN_MS),
displayValue: str_(UIStrings.displayValue, {
itemCount: results.length,
timeInMs: summary.wastedMs,
}),
details: Audit.makeTableDetails(headings, results, summary),
Expand Down
11 changes: 11 additions & 0 deletions lighthouse-core/lib/i18n/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,17 @@ function _processParsedElements(icuMessage, argumentElements, values = {}) {
}
}

// Throw an error if a value is provided but has no placeholder in the message.
for (const valueId of Object.keys(values)) {
// errorCode is a special case always allowed to help ease of LHError use.
if (valueId === 'errorCode') continue;

if (!argumentElements.find(el => el.id === valueId)) {
throw new Error(`Provided value "${valueId}" does not match any placeholder in ` +
`ICU message "${icuMessage}"`);
}
}

return values;
}

Expand Down
3 changes: 2 additions & 1 deletion lighthouse-core/lib/lh-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ class LighthouseError extends Error {
super(errorDefinition.code);
this.name = 'LHError';
this.code = errorDefinition.code;
// Insert the i18n reference with errorCode and all additional ICU replacement properties.
// Add additional properties to be ICU replacements in the error string.
// `code` is always added as `errorCode` so callers don't need to specify the code multiple times.
this.friendlyMessage = str_(errorDefinition.message, {errorCode: this.code, ...properties});
this.lhrRuntimeError = !!errorDefinition.lhrRuntimeError;
if (properties) Object.assign(this, properties);
Expand Down
25 changes: 20 additions & 5 deletions lighthouse-core/test/lib/i18n/i18n-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,19 @@ describe('i18n', () => {

describe('#replaceIcuMessageInstanceIds', () => {
it('replaces the references in the LHR', () => {
const templateID = 'lighthouse-core/test/lib/i18n/fake-file.js | daString';
const reference = templateID + ' # 0';
const lhr = {audits: {'fake-audit': {title: reference}}};
const fakeFile = path.join(__dirname, 'fake-file-number-2.js');
const UIStrings = {aString: 'different {x}!'};
const formatter = i18n.createMessageInstanceIdFn(fakeFile, UIStrings);

const title = formatter(UIStrings.aString, {x: 1});
const lhr = {audits: {'fake-audit': {title}}};

const icuMessagePaths = i18n.replaceIcuMessageInstanceIds(lhr, 'en-US');
expect(lhr.audits['fake-audit'].title).toBe('use me!');
expect(lhr.audits['fake-audit'].title).toBe('different 1!');

const expectedPathId = 'lighthouse-core/test/lib/i18n/fake-file-number-2.js | aString';
expect(icuMessagePaths).toEqual({
[templateID]: [{path: 'audits[fake-audit].title', values: {x: 1}}]});
[expectedPathId]: [{path: 'audits[fake-audit].title', values: {x: 1}}]});
});
});

Expand Down Expand Up @@ -191,5 +196,15 @@ describe('i18n', () => {
// eslint-disable-next-line max-len
.toThrow(`ICU Message "Hello {timeInMs, number, seconds} World" contains a numeric reference ("timeInMs") but provided value was not a number`);
});

it('throws an error if a value is provided that has no placeholder in the message', () => {
const helloStr = str_(UIStrings.helloTimeInMsWorld, {
timeInMs: 55,
sirNotAppearingInThisString: 66,
});
expect(_ => i18n.getFormatted(helloStr, 'en-US'))
// eslint-disable-next-line max-len
.toThrow(`Provided value "sirNotAppearingInThisString" does not match any placeholder in ICU message "Hello {timeInMs, number, seconds} World"`);
});
});
});
1 change: 0 additions & 1 deletion lighthouse-core/test/results/sample_v2.json
Original file line number Diff line number Diff line change
Expand Up @@ -5606,7 +5606,6 @@
"lighthouse-core/audits/third-party-summary.js | displayValue": [
{
"values": {
"itemCount": 1,
"timeInMs": 22.918000000000006
},
"path": "audits[third-party-summary].displayValue"
Expand Down

0 comments on commit c93fcd2

Please sign in to comment.