Skip to content

Commit

Permalink
feat(jest-transformer): add transform for @salesforce/i18n (#758)
Browse files Browse the repository at this point in the history
* feat(jest-transformer): add transform for @salesforce/i18n

* Update quotes and tests
  • Loading branch information
chengli-sfdc authored and Trevor committed Oct 24, 2018
1 parent 7ad2944 commit 00a13cc
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 5 deletions.
2 changes: 2 additions & 0 deletions packages/lwc-jest-transformer/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const engineVersion = require('lwc-engine/package.json').version;
const compilerVersion = require('lwc-compiler/package.json').version;
const { waitForPromise } = require('./utils');
const apexScopedImport = require('./transforms/apex-scoped-import');
const i18nScopedImport = require('./transforms/i18n-scoped-import');
const labelScopedImport = require('./transforms/label-scoped-import');
const resourceScopedImport = require('./transforms/resource-scoped-import');
const contentAssetUrlScopedImport = require('./transforms/content-asset-url-scoped-import');
Expand All @@ -22,6 +23,7 @@ const BABEL_CONFIG = {
"plugins": [
babelCommonJs,
apexScopedImport,
i18nScopedImport,
labelScopedImport,
contentAssetUrlScopedImport,
resourceScopedImport,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`example-i18n default snapshot 1`] = `
<example-i18n>
#shadow-root(open)
<p
dir="value set in test"
lang="en"
>
M/d/yyyy
</p>
</example-i18n>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { createElement } from 'lwc';
import I18n from 'example/i18n';

jest.mock('@salesforce/i18n/dir', () => {
return { default: 'value set in test' };
}, { virtual: true });

afterEach(() => {
while (document.body.firstChild) {
document.body.removeChild(document.body.firstChild);
}
});

describe('example-i18n', () => {

it('default snapshot', () => {
const element = createElement('example-i18n', { is: I18n });
document.body.appendChild(element);
expect(element).toMatchSnapshot();
});

it('returns default label value as import path', () => {
const element = createElement('example-i18n', { is: I18n });
document.body.appendChild(element);
const targetElement = element.shadowRoot.querySelector('p');
expect(targetElement.getAttribute('lang')).toBe('en');
expect(targetElement.textContent).toBe('M/d/yyyy');
});

it('returns value from mock defined in test file', () => {
const element = createElement('example-i18n', { is: I18n });
document.body.appendChild(element);
const value = element.shadowRoot.querySelector('p').getAttribute('dir');
expect(value).toBe('value set in test');
});

it.each([
'locale', 'timeZone', 'currency', 'firstDayOfWeek',
'dateTime.shortDateFormat', 'dateTime.mediumDateFormat', 'dateTime.longDateFormat',
'dateTime.shortDateTimeFormat', 'dateTime.mediumDateTimeFormat',
'dateTime.shortTimeFormat', 'dateTime.mediumTimeFormat',
'number.numberFormat', 'number.percentFormat', 'number.currencyFormat', 'number.currencySymbol'
])('@salesforce/i18n/%s should be resolved as a default mock value', id => {
const values = createElement('example-i18n', { is: I18n }).getI18nValues();
const key = id.split('.').pop();
expect(values[key]).toBeDefined();
expect(values[key]).not.toEqual('');
});

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<p lang={defaultLang} dir={mockDir}>{defaultDateFormat}</p>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { LightningElement, track, api } from 'lwc';
import lang from '@salesforce/i18n/lang';
import dir from '@salesforce/i18n/dir';
import locale from '@salesforce/i18n/locale';
import timeZone from '@salesforce/i18n/timeZone';
import currency from '@salesforce/i18n/currency';
import firstDayOfWeek from '@salesforce/i18n/firstDayOfWeek';
import shortDateFormat from '@salesforce/i18n/dateTime.shortDateFormat';
import mediumDateFormat from '@salesforce/i18n/dateTime.mediumDateFormat';
import longDateFormat from '@salesforce/i18n/dateTime.longDateFormat';
import shortDateTimeFormat from '@salesforce/i18n/dateTime.shortDateTimeFormat';
import mediumDateTimeFormat from '@salesforce/i18n/dateTime.mediumDateTimeFormat';
import shortTimeFormat from '@salesforce/i18n/dateTime.shortTimeFormat';
import mediumTimeFormat from '@salesforce/i18n/dateTime.mediumTimeFormat';
import numberFormat from '@salesforce/i18n/number.numberFormat';
import percentFormat from '@salesforce/i18n/number.percentFormat';
import currencyFormat from '@salesforce/i18n/number.currencyFormat';
import currencySymbol from '@salesforce/i18n/number.currencySymbol';

export default class I18n extends LightningElement {
@track
defaultLang = lang;

@track
mockDir = dir;

@track
defaultDateFormat = shortDateFormat;

@api getI18nValues() {
return {
lang,
dir,
locale,
timeZone,
currency,
firstDayOfWeek,
shortDateFormat,
mediumDateFormat,
longDateFormat,
shortDateTimeFormat,
mediumDateTimeFormat,
shortTimeFormat,
mediumTimeFormat,
numberFormat,
percentFormat,
currencyFormat,
currencySymbol
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const test = require('./utils/test-transform').test(
require('../i18n-scoped-import')
);

describe('@salesforce/i18n import', () => {
test('does default transformation', `
import lang from '@salesforce/i18n/lang';
`, `
let lang;
try {
lang = require('@salesforce/i18n/lang').default;
} catch (e) {
lang = 'en';
}
`);

test('does default transformation for import id with prefix', `
import shortDateFormat from '@salesforce/i18n/dateTime.shortDateFormat';
`, `
let shortDateFormat;
try {
shortDateFormat = require('@salesforce/i18n/dateTime.shortDateFormat').default;
} catch (e) {
shortDateFormat = 'M/d/yyyy';
}
`);

test('allows non-@salesforce/i18n named imports', `
import { otherNamed } from './something-valid';
import lang from '@salesforce/i18n/lang';
`, `
import { otherNamed } from './something-valid';
let lang;
try {
lang = require('@salesforce/i18n/lang').default;
} catch (e) {
lang = 'en';
}
`);

test('throws error if using named import', `
import { lang } from '@salesforce/i18n/lang';
`, undefined, 'Invalid import from @salesforce/i18n/lang');

test('throws error if renamed default imports', `
import { default as label } from '@salesforce/i18n/lang';
`, undefined, 'Invalid import from @salesforce/i18n/lang');

test('throws error if renamed multiple default imports', `
import { default as label, foo } from '@salesforce/i18n/lang';
`, undefined, 'Invalid import from @salesforce/i18n/lang');
});
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('@salesforce/label import', () => {
import { default as label } from '@salesforce/label/c.foo';
`, undefined, 'Invalid import from @salesforce/label/c.foo');

test('throws error if renamed multipel default imports', `
test('throws error if renamed multiple default imports', `
import { default as label, foo } from '@salesforce/label/c.foo';
`, undefined, 'Invalid import from @salesforce/label/c.foo');
});
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe('@salesforce/schema import', () => {
import { default as resource } from '@salesforce/schema/Opportunity.Account.Name';
`, undefined, 'Invalid import from @salesforce/schema/Opportunity.Account.Name');

test('throws error if renamed multipel default imports', `
test('throws error if renamed multiple default imports', `
import { default as resource, foo } from '@salesforce/schema/Opportunity.Account.Name';
`, undefined, 'Invalid import from @salesforce/schema/Opportunity.Account.Name');
});
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('@salesforce/user import', () => {
import { default as label } from '@salesforce/user/Id';
`, undefined, 'Invalid import from @salesforce/user/Id');

test('throws error if renamed multipel default imports', `
test('throws error if renamed multiple default imports', `
import { default as label, foo } from '@salesforce/user/Id';
`, undefined, 'Invalid import from @salesforce/user/Id');
});
70 changes: 70 additions & 0 deletions packages/lwc-jest-transformer/src/transforms/i18n-scoped-import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const { stringScopedImportTransform } = require('./utils');

const I18N_IMPORT_IDENTIFIER = '@salesforce/i18n/';

module.exports = function ({ types: t }) {
return {
visitor: {
ImportDeclaration(path) {
const importId = path.get('source.value').node;
if (importId.startsWith(I18N_IMPORT_IDENTIFIER)) {
const mockValue = getMockValue(importId);
stringScopedImportTransform(t, path, importId, mockValue);
}
}
}
};
};

function getMockValue(importId) {
importId = importId.substring(I18N_IMPORT_IDENTIFIER.length);
const parts = importId.split('.');
switch (parts[0]) {
case 'lang':
return 'en';
case 'dir':
return 'ltr';
case 'locale':
return 'en-US';
case 'timeZone':
return 'America/Los_Angeles';
case 'currency':
return 'USD';
case 'firstDayOfWeek':
return 0;
case 'dateTime':
switch (parts[1]) {
case 'shortDateFormat':
return 'M/d/yyyy';
case 'mediumDateFormat':
return 'MMM d, yyyy';
case 'longDateFormat':
return 'MMMM d, yyyy';
case 'shortDateTimeFormat':
return 'M/d/yyyy h:mm a';
case 'mediumDateTimeFormat':
return 'MMM d, yyyy h:mm:ss a';
case 'shortTimeFormat':
return 'h:mm a';
case 'mediumTimeFormat':
return 'h:mm:ss a';
default:
return undefined;
}
case 'number':
switch (parts[1]) {
case 'numberFormat':
return '#,##0.###';
case 'percentFormat':
return '#,##0%';
case 'currencyFormat':
return '¤#,##0.00;(¤#,##0.00)';
case 'currencySymbol':
return '$';
default:
return undefined;
}
default:
return undefined;
}
}
12 changes: 10 additions & 2 deletions packages/lwc-jest-transformer/src/transforms/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,20 @@ function stringScopedImportTransform(t, path, importIdentifier, fallbackData) {
const { importSource, resourceName } = getImportInfo(path);

// if no fallback value provided, use the resource path from the import statement
fallbackData = fallbackData || importSource.substring(importIdentifier.length);
if (fallbackData === undefined) {
fallbackData = importSource.substring(importIdentifier.length);
}

if (typeof fallbackData === 'number') {
fallbackData = t.numericLiteral(fallbackData);
} else {
fallbackData = t.stringLiteral(fallbackData);
}

path.replaceWithMultiple(defaultTemplate({
RESOURCE_NAME: t.identifier(resourceName),
IMPORT_SOURCE: t.stringLiteral(importSource),
FALLBACK_DATA: t.stringLiteral(fallbackData)
FALLBACK_DATA: fallbackData
}));
}

Expand Down

0 comments on commit 00a13cc

Please sign in to comment.