Skip to content

Commit fea692b

Browse files
authored
Migrate frontend tests under entitiesList, entitydetails and historyTranslation to testing-library (#3992)
1 parent 9e10cdb commit fea692b

File tree

10 files changed

+366
-280
lines changed

10 files changed

+366
-280
lines changed

translate/public/locale/en-US/translate.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,9 @@ entitydetails-ContextIssueButton--context-issue-button = REQUEST CONTEXT or REPO
300300
entitieslist-Entity--sibling-strings-title =
301301
.title = Click to reveal sibling strings
302302
303+
entitieslist-Entity--listitem-label =
304+
.aria-label = Select "{ $original }" for translation.
305+
303306
entitieslist-EntitiesList--clear-selected = <glyph></glyph>CLEAR
304307
.title = Uncheck selected strings
305308

translate/src/modules/entitieslist/components/EntitiesList.test.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ describe('<EntitiesList>', () => {
8888
const history = createMemoryHistory({
8989
initialEntries: ['/kg/firefox/all-resources/?string=1'],
9090
});
91+
const testLabel = 'test-listitem';
92+
const resource = `entitieslist-Entity--listitem-label =
93+
.aria-label = ${testLabel}`;
9194

9295
const store = createReduxStore();
9396
store.dispatch({
@@ -100,9 +103,10 @@ describe('<EntitiesList>', () => {
100103
store,
101104
{},
102105
history,
106+
resource,
103107
);
104108

105-
expect(getAllByRole('listitem')).toHaveLength(2);
109+
expect(getAllByRole('button', { name: testLabel })).toHaveLength(2);
106110
});
107111

108112
// FIXME: https://github.com/mozilla/pontoon/issues/3883

translate/src/modules/entitieslist/components/Entity.test.jsx

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { mount, shallow } from 'enzyme';
21
import React from 'react';
32

43
import * as hookModule from '~/hooks/useTranslator';
54
import { Entity } from './Entity';
6-
import { vitest } from 'vitest';
5+
import { it, vitest } from 'vitest';
6+
import { fireEvent, render } from '@testing-library/react';
7+
import { MockLocalizationProvider } from '~/test/utils';
78

89
beforeAll(() => {
910
vitest.mock('~/hooks/useTranslator', () => ({
@@ -61,91 +62,94 @@ describe('<Entity>', () => {
6162
warnings: ['warning'],
6263
},
6364
};
65+
const WrapEntity = (props) => {
66+
return (
67+
<MockLocalizationProvider>
68+
<Entity {...props} />
69+
</MockLocalizationProvider>
70+
);
71+
};
6472

6573
it('renders the source string and the first translation', () => {
66-
const wrapper = shallow(<Entity entity={ENTITY_A} parameters={{}} />);
67-
68-
const contents = wrapper.find('Translation');
69-
expect(contents.first().props().content).toContain(ENTITY_A.original);
70-
expect(contents.last().props().content).toContain(
71-
ENTITY_A.translation.string,
74+
const { getByText } = render(
75+
<WrapEntity entity={ENTITY_A} parameters={{}} />,
7276
);
73-
});
7477

75-
it('shows the correct status class', () => {
76-
let wrapper = shallow(<Entity entity={ENTITY_A} parameters={{}} />);
77-
expect(wrapper.find('.approved')).toHaveLength(1);
78-
79-
wrapper = shallow(<Entity entity={ENTITY_B} parameters={{}} />);
80-
expect(wrapper.find('.pretranslated')).toHaveLength(1);
81-
82-
wrapper = shallow(<Entity entity={ENTITY_C} parameters={{}} />);
83-
expect(wrapper.find('.missing')).toHaveLength(1);
78+
getByText(ENTITY_A.original);
79+
getByText(ENTITY_A.translation.string);
80+
});
8481

85-
wrapper = shallow(<Entity entity={ENTITY_D} parameters={{}} />);
86-
expect(wrapper.find('.errors')).toHaveLength(1);
82+
it.each([
83+
{ entity: ENTITY_A, classname: 'approved' },
84+
{ entity: ENTITY_B, classname: 'pretranslated' },
85+
{ entity: ENTITY_C, classname: 'missing' },
86+
{ entity: ENTITY_D, classname: 'errors' },
87+
{ entity: ENTITY_E, classname: 'warnings' },
88+
])('renders $classname state correctly', ({ entity, classname }) => {
89+
const { container } = render(
90+
<WrapEntity entity={entity} parameters={{}} />,
91+
);
8792

88-
wrapper = shallow(<Entity entity={ENTITY_E} parameters={{}} />);
89-
expect(wrapper.find('.warnings')).toHaveLength(1);
93+
expect(container.querySelector(`.${classname}`)).toBeInTheDocument();
9094
});
9195

9296
it('calls the selectEntity function on click on li', () => {
9397
const selectEntityFn = vi.fn();
94-
const wrapper = mount(
95-
<Entity
98+
const { getByRole } = render(
99+
<WrapEntity
96100
entity={ENTITY_A}
97101
selectEntity={selectEntityFn}
98102
parameters={{}}
99103
/>,
100104
);
101-
wrapper.find('li').simulate('click');
105+
fireEvent.click(getByRole('button'));
102106
expect(selectEntityFn).toHaveBeenCalledOnce();
103107
});
104108

105109
it('calls the toggleForBatchEditing function on click on .status', () => {
106110
hookModule.useTranslator.mockReturnValue(true);
107111
const toggleForBatchEditingFn = vi.fn();
108-
const wrapper = mount(
109-
<Entity
112+
const { getByRole } = render(
113+
<WrapEntity
110114
entity={ENTITY_A}
111115
isReadOnlyEditor={false}
112116
toggleForBatchEditing={toggleForBatchEditingFn}
113117
parameters={{}}
114118
/>,
115119
);
116-
wrapper.find('.status').simulate('click');
120+
fireEvent.click(getByRole('checkbox'));
117121
expect(toggleForBatchEditingFn).toHaveBeenCalledOnce();
118122
});
119123

120124
it('does not call the toggleForBatchEditing function if user not translator', () => {
121125
const toggleForBatchEditingFn = vi.fn();
122126
const selectEntityFn = vi.fn();
123-
const wrapper = mount(
124-
<Entity
127+
const { getByRole } = render(
128+
<WrapEntity
125129
entity={ENTITY_A}
126130
isReadOnlyEditor={false}
127131
toggleForBatchEditing={toggleForBatchEditingFn}
128132
selectEntity={selectEntityFn}
129133
parameters={{}}
130134
/>,
131135
);
132-
wrapper.find('.status').simulate('click');
136+
fireEvent.click(getByRole('checkbox'));
133137
expect(toggleForBatchEditingFn).not.toHaveBeenCalled();
134138
});
135139

136140
it('does not call the toggleForBatchEditing function if read-only editor', () => {
137141
const toggleForBatchEditingFn = vi.fn();
138142
const selectEntityFn = vi.fn();
139-
const wrapper = mount(
140-
<Entity
143+
const { getByRole } = render(
144+
<WrapEntity
141145
entity={ENTITY_A}
142146
isReadOnlyEditor={true}
143147
toggleForBatchEditing={toggleForBatchEditingFn}
144148
selectEntity={selectEntityFn}
145149
parameters={{}}
146150
/>,
147151
);
148-
wrapper.find('.status').simulate('click');
152+
fireEvent.click(getByRole('checkbox'));
149153
expect(toggleForBatchEditingFn).not.toHaveBeenCalled();
150154
});
151155
});

translate/src/modules/entitieslist/components/Entity.tsx

Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -113,47 +113,58 @@ export function Entity({
113113
);
114114

115115
return (
116-
<li className={cn} onClick={handleSelectEntity}>
117-
<span className='status fas' onClick={handleForBatchEditing} />
118-
{selected && !entity.isSibling ? (
116+
<Localized
117+
id='entitieslist-Entity--listitem-label'
118+
attrs={{ 'aria-label': true }}
119+
vars={{ original: entity.original }}
120+
>
121+
<li className={cn} role='button' onClick={handleSelectEntity}>
122+
<span
123+
className='status fas'
124+
role='checkbox'
125+
aria-selected={checkedForBatchEditing}
126+
onClick={handleForBatchEditing}
127+
/>
128+
{selected && !entity.isSibling ? (
129+
<div>
130+
{!areSiblingsActive && showSiblingEntitiesButton() && (
131+
<Localized id='entitieslist-Entity--sibling-strings-title'>
132+
<i
133+
className={'sibling-entities-icon fas fa-expand-arrows-alt'}
134+
title='Click to reveal sibling strings'
135+
onClick={showSiblingEntities}
136+
></i>
137+
</Localized>
138+
)}
139+
</div>
140+
) : null}
119141
<div>
120-
{!areSiblingsActive && showSiblingEntitiesButton() && (
121-
<Localized id='entitieslist-Entity--sibling-strings-title'>
122-
<i
123-
className={'sibling-entities-icon fas fa-expand-arrows-alt'}
124-
title='Click to reveal sibling strings'
125-
onClick={showSiblingEntities}
126-
></i>
127-
</Localized>
128-
)}
142+
<p className='source-string'>
143+
<Translation
144+
content={entity.original}
145+
format={entity.format}
146+
search={
147+
parameters.search_exclude_source_strings
148+
? null
149+
: parameters.search
150+
}
151+
/>
152+
</p>
153+
<p
154+
className='translation-string'
155+
dir={direction}
156+
lang={code}
157+
data-script={script}
158+
>
159+
<Translation
160+
content={entity.translation?.string ?? ''}
161+
format={entity.format}
162+
search={parameters.search}
163+
/>
164+
</p>
165+
<div className='indicator fas fa-chevron-right'></div>
129166
</div>
130-
) : null}
131-
<div>
132-
<p className='source-string'>
133-
<Translation
134-
content={entity.original}
135-
format={entity.format}
136-
search={
137-
parameters.search_exclude_source_strings
138-
? null
139-
: parameters.search
140-
}
141-
/>
142-
</p>
143-
<p
144-
className='translation-string'
145-
dir={direction}
146-
lang={code}
147-
data-script={script}
148-
>
149-
<Translation
150-
content={entity.translation?.string ?? ''}
151-
format={entity.format}
152-
search={parameters.search}
153-
/>
154-
</p>
155-
<div className='indicator fas fa-chevron-right'></div>
156-
</div>
157-
</li>
167+
</li>
168+
</Localized>
158169
);
159170
}
Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import ftl from '@fluent/dedent';
2-
import { shallow } from 'enzyme';
32
import React from 'react';
43
import { parseEntry } from '~/utils/message';
54
import { FluentAttribute } from './FluentAttribute';
5+
import { render } from '@testing-library/react';
6+
import { MockLocalizationProvider } from '~/test/utils';
67

78
describe('isSimpleSingleAttributeMessage', () => {
89
it('renders nonempty for a string with a single attribute', () => {
@@ -11,8 +12,12 @@ describe('isSimpleSingleAttributeMessage', () => {
1112
.an-atribute = Hello!
1213
`;
1314
const entry = parseEntry('fluent', original);
14-
const wrapper = shallow(<FluentAttribute entry={entry} />);
15-
expect(wrapper.isEmptyRender()).toEqual(false);
15+
const { container } = render(
16+
<MockLocalizationProvider>
17+
<FluentAttribute entry={entry} />
18+
</MockLocalizationProvider>,
19+
);
20+
expect(container).not.toBeEmptyDOMElement();
1621
});
1722

1823
it('renders null for string with value', () => {
@@ -21,8 +26,8 @@ describe('isSimpleSingleAttributeMessage', () => {
2126
.an-atribute = Hello!
2227
`;
2328
const entry = parseEntry('fluent', original);
24-
const wrapper = shallow(<FluentAttribute entry={entry} />);
25-
expect(wrapper.isEmptyRender()).toEqual(true);
29+
const { container } = render(<FluentAttribute entry={entry} />);
30+
expect(container).toBeEmptyDOMElement();
2631
});
2732

2833
it('renders null for string with several attributes', () => {
@@ -32,7 +37,7 @@ describe('isSimpleSingleAttributeMessage', () => {
3237
.two-attrites = World!
3338
`;
3439
const entry = parseEntry('fluent', original);
35-
const wrapper = shallow(<FluentAttribute entry={entry} />);
36-
expect(wrapper.isEmptyRender()).toEqual(true);
40+
const { container } = render(<FluentAttribute entry={entry} />);
41+
expect(container).toBeEmptyDOMElement();
3742
});
3843
});

0 commit comments

Comments
 (0)