Use translations for all tests (#1568)

* Toolbar ARIA Labels
* Ensure all tests use real translations

All tests should import the 'test-utils' file which re-exports all of
the @testing-library functions. It wraps render() in a react-i18next
instance.

It's important that the component being tested is the one returned from
withTranslation().

Co-authored-by: ov <omar.verduga@gmail.com>
This commit is contained in:
Andrew Nicolaou 2020-08-26 13:19:34 +01:00 committed by GitHub
parent 4d04952213
commit af1a5cc2f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 83 additions and 32 deletions

26
client/i18n-test.js Normal file
View file

@ -0,0 +1,26 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import translations from '../translations/locales/en-US/translations.json';
i18n
.use(initReactI18next)
.init({
lng: 'en-US',
fallbackLng: 'en-US',
// have a common namespace used around the full app
ns: ['translations'],
defaultNS: 'translations',
debug: false,
interpolation: {
escapeValue: false, // not needed for react!!
},
resources: { 'en-US': { translations } },
});
export default i18n;

View file

@ -3,21 +3,3 @@ import '@babel/polyfill';
// See: https://github.com/testing-library/jest-dom
// eslint-disable-next-line import/no-extraneous-dependencies
import '@testing-library/jest-dom';
import lodash from 'lodash';
// For testing, we use en-US and provide a mock implementation
// of t() that finds the correct translation
import translations from '../translations/locales/en-US/translations.json';
// This function name needs to be prefixed with "mock" so that Jest doesn't
// complain that it's out-of-scope in the mock below
const mockTranslate = key => lodash.get(translations, key);
jest.mock('react-i18next', () => ({
// this mock makes sure any components using the translate HoC receive the t function as a prop
withTranslation: () => (Component) => {
Component.defaultProps = { ...Component.defaultProps, t: mockTranslate };
return Component;
},
}));

View file

@ -67,7 +67,7 @@ FileName.propTypes = {
name: PropTypes.string.isRequired
};
export class FileNode extends React.Component {
class FileNode extends React.Component {
constructor(props) {
super(props);
@ -419,4 +419,7 @@ const TranslatedFileNode = withTranslation()(FileNode);
const ConnectedFileNode = connect(mapStateToProps, mapDispatchToProps)(TranslatedFileNode);
export default ConnectedFileNode;
export {
TranslatedFileNode as FileNode,
ConnectedFileNode as default
};

View file

@ -1,5 +1,6 @@
import React from 'react';
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react';
import { fireEvent, render, screen, waitFor, within } from '../../../test-utils';
import { FileNode } from './FileNode';
describe('<FileNode />', () => {

View file

@ -86,7 +86,7 @@ class Toolbar extends React.Component {
this.props.setTextOutput(true);
this.props.setGridOutput(true);
}}
aria-label="Play sketch"
aria-label={this.props.t('Toolbar.PlaySketchARIA')}
disabled={this.props.infiniteLoop}
>
<PlayIcon focusable="false" aria-hidden="true" />
@ -94,7 +94,7 @@ class Toolbar extends React.Component {
<button
className={playButtonClass}
onClick={this.props.startSketch}
aria-label="Play only visual sketch"
aria-label={this.props.t('Toolbar.PlayOnlyVisualSketchARIA')}
disabled={this.props.infiniteLoop}
>
<PlayIcon focusable="false" aria-hidden="true" />
@ -102,7 +102,7 @@ class Toolbar extends React.Component {
<button
className={stopButtonClass}
onClick={this.props.stopSketch}
aria-label="Stop sketch"
aria-label={this.props.t('Toolbar.StopSketchARIA')}
>
<StopIcon focusable="false" aria-hidden="true" />
</button>
@ -129,7 +129,7 @@ class Toolbar extends React.Component {
}
}}
disabled={!canEditProjectName}
aria-label="Edit sketch name"
aria-label={this.props.t('Toolbar.EditSketchARIA')}
>
<span>{this.props.project.name}</span>
{
@ -145,7 +145,7 @@ class Toolbar extends React.Component {
type="text"
maxLength="128"
className="toolbar__project-name-input"
aria-label="New sketch name"
aria-label={this.props.t('Toolbar.NewSketchNameARIA')}
value={this.state.projectNameInputValue}
onChange={this.handleProjectNameChange}
ref={(element) => { this.projectNameInput = element; }}
@ -165,7 +165,7 @@ class Toolbar extends React.Component {
<button
className={preferencesButtonClass}
onClick={this.props.openPreferences}
aria-label="Open Preferences"
aria-label={this.props.t('Toolbar.OpenPreferencesARIA')}
>
<PreferencesIcon focusable="false" aria-hidden="true" />
</button>
@ -200,6 +200,7 @@ Toolbar.propTypes = {
saveProject: PropTypes.func.isRequired,
currentUser: PropTypes.string,
t: PropTypes.func.isRequired
};
Toolbar.defaultProps = {
@ -225,6 +226,5 @@ const mapDispatchToProps = {
...projectActions,
};
export const ToolbarComponent = Toolbar;
// export default connect(mapStateToProps, mapDispatchToProps)(Toolbar);
export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(Toolbar));
export const ToolbarComponent = withTranslation()(Toolbar);
export default connect(mapStateToProps, mapDispatchToProps)(ToolbarComponent);

View file

@ -1,8 +1,7 @@
import React from 'react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import lodash from 'lodash';
import { fireEvent, render, screen, waitFor } from '../../../test-utils';
import { ToolbarComponent } from './Toolbar';
const renderComponent = (extraProps = {}) => {

40
client/test-utils.js Normal file
View file

@ -0,0 +1,40 @@
/**
* This file re-exports @testing-library but ensures that
* any calls to render have translations available.
*
* This means tested components will be able to call
* `t()` and have the translations of the default
* language
*
* See: https://react.i18next.com/misc/testing#testing-without-stubbing
*/
// eslint-disable-next-line import/no-extraneous-dependencies
import { render } from '@testing-library/react';
import React from 'react';
import PropTypes from 'prop-types';
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n-test';
// re-export everything
// eslint-disable-next-line import/no-extraneous-dependencies
export * from '@testing-library/react';
const Providers = ({ children }) => (
// eslint-disable-next-line react/jsx-filename-extension
<I18nextProvider i18n={i18n}>
{children}
</I18nextProvider>
);
Providers.propTypes = {
children: PropTypes.element.isRequired,
};
const customRender = (ui, options) =>
render(ui, { wrapper: Providers, ...options });
// override render method
export { customRender as render };