Skip to content

Commit 377bb6b

Browse files
test: deprecate react-unit-test-utils part-4 (#462)
* test: deprecate react-unit-test-utils part-4 * test: address feedback * test: address feedback * test: improve tests and address feedback --------- Co-authored-by: diana-villalvazo-wgu <diana.villalvazo@wgu.edu>
1 parent ac03594 commit 377bb6b

File tree

10 files changed

+343
-391
lines changed

10 files changed

+343
-391
lines changed

src/containers/DemoWarning/DemoWarning.test.jsx

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,39 @@
1-
import React from 'react';
2-
import { shallow } from '@edx/react-unit-test-utils';
3-
1+
import { render, screen } from '@testing-library/react';
2+
import { IntlProvider } from '@edx/frontend-platform/i18n';
43
import { selectors } from 'data/redux';
54
import { DemoWarning, mapStateToProps } from '.';
5+
import messages from './messages';
6+
7+
jest.unmock('@openedx/paragon');
8+
jest.unmock('react');
9+
jest.unmock('@edx/frontend-platform/i18n');
610

711
jest.mock('data/redux', () => ({
812
selectors: {
913
app: { isEnabled: (args) => ({ isEnabled: args }) },
1014
},
1115
}));
1216

13-
let el;
14-
1517
describe('DemoWarning component', () => {
16-
describe('snapshots', () => {
17-
test('does not render if disabled flag is missing', () => {
18-
el = shallow(<DemoWarning hide />);
19-
expect(el.snapshot).toMatchSnapshot();
20-
expect(el.isEmptyRender()).toEqual(true);
18+
describe('behavior', () => {
19+
it('does not render when hide prop is true', () => {
20+
const { container } = render(<IntlProvider locale="en"><DemoWarning hide /></IntlProvider>);
21+
expect(container.firstChild).toBeNull();
2122
});
22-
test('snapshot: disabled flag is present', () => {
23-
el = shallow(<DemoWarning hide={false} />);
24-
expect(el.snapshot).toMatchSnapshot();
25-
expect(el.isEmptyRender()).toEqual(false);
23+
24+
it('renders alert with warning message when hide prop is false', () => {
25+
render(<IntlProvider locale="en"><DemoWarning hide={false} /></IntlProvider>);
26+
const alert = screen.getByRole('alert');
27+
expect(alert).toBeInTheDocument();
28+
expect(alert).toHaveClass('alert-warning');
29+
expect(alert).toHaveTextContent(messages.demoModeMessage.defaultMessage);
30+
expect(alert).toHaveTextContent(messages.demoModeHeading.defaultMessage);
2631
});
2732
});
33+
2834
describe('mapStateToProps', () => {
29-
const testState = { some: 'test-state' };
30-
test('hide is forwarded from app.isEnabled', () => {
35+
it('maps hide prop from app.isEnabled selector', () => {
36+
const testState = { some: 'test-state' };
3137
expect(mapStateToProps(testState).hide).toEqual(
3238
selectors.app.isEnabled(testState),
3339
);

src/containers/DemoWarning/__snapshots__/DemoWarning.test.jsx.snap

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,48 @@
1-
import React from 'react';
2-
import { shallow } from '@edx/react-unit-test-utils';
3-
4-
import { Hyperlink } from '@openedx/paragon';
5-
1+
import { render } from '@testing-library/react';
2+
import { IntlProvider } from '@edx/frontend-platform/i18n';
63
import urls from 'data/services/lms/urls';
7-
84
import EmptySubmission from './EmptySubmission';
95

6+
jest.unmock('@openedx/paragon');
7+
jest.unmock('react');
8+
jest.unmock('@edx/frontend-platform/i18n');
9+
1010
jest.mock('data/services/lms/urls', () => ({
1111
openResponse: (courseId) => `openResponseUrl(${courseId})`,
1212
}));
1313

14-
jest.mock('./assets/emptyState.svg', () => './assets/emptyState.svg');
15-
16-
let el;
14+
jest.mock('./assets/empty-state.svg', () => './assets/empty-state.svg');
1715

1816
describe('EmptySubmission component', () => {
19-
describe('component', () => {
20-
const props = { courseId: 'test-course-id' };
21-
beforeEach(() => {
22-
el = shallow(<EmptySubmission {...props} />);
23-
});
24-
test('snapshot', () => {
25-
expect(el.snapshot).toMatchSnapshot();
26-
});
27-
test('openResponse destination', () => {
28-
expect(
29-
el.instance.findByType(Hyperlink)[0].props.destination,
30-
).toEqual(urls.openResponse(props.courseId));
31-
});
17+
const props = { courseId: 'test-course-id' };
18+
19+
const renderWithIntl = (component) => render(
20+
<IntlProvider locale="en" messages={{}}>
21+
{component}
22+
</IntlProvider>,
23+
);
24+
25+
it('renders the empty state image with correct alt text', () => {
26+
const { getByAltText } = renderWithIntl(<EmptySubmission {...props} />);
27+
expect(getByAltText('empty state')).toBeInTheDocument();
28+
});
29+
30+
it('renders the no results found title message', () => {
31+
const { getByText } = renderWithIntl(<EmptySubmission {...props} />);
32+
expect(getByText('Nothing here yet')).toBeInTheDocument();
33+
});
34+
35+
it('renders hyperlink with correct destination URL', () => {
36+
const { container } = renderWithIntl(<EmptySubmission {...props} />);
37+
const hyperlink = container.querySelector('a');
38+
expect(hyperlink).toHaveAttribute(
39+
'href',
40+
urls.openResponse(props.courseId),
41+
);
42+
});
43+
44+
it('renders the back to responses button', () => {
45+
const { getByText } = renderWithIntl(<EmptySubmission {...props} />);
46+
expect(getByText('Back to all open responses')).toBeInTheDocument();
3247
});
3348
});
Lines changed: 96 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,21 @@
11
import React from 'react';
2-
import { shallow } from '@edx/react-unit-test-utils';
2+
import PropTypes from 'prop-types';
3+
import { render } from '@testing-library/react';
4+
import { DataTableContext } from '@openedx/paragon';
35

46
import * as module from './FilterStatusComponent';
57

6-
const fieldIds = [
7-
'field-id-0',
8-
'field-id-1',
9-
'field-id-2',
10-
'field-id-3',
11-
];
8+
jest.unmock('@openedx/paragon');
9+
jest.unmock('react');
10+
11+
const fieldIds = ['field-id-0', 'field-id-1', 'field-id-2', 'field-id-3'];
1212
const filterOrder = [1, 0, 3, 2];
13-
const filters = filterOrder.map(v => ({ id: fieldIds[v] }));
14-
const headers = [0, 1, 2, 3].map(v => ({
13+
const filters = filterOrder.map((v) => ({ id: fieldIds[v] }));
14+
const headers = [0, 1, 2, 3].map((v) => ({
1515
id: fieldIds[v],
1616
Header: `HeaDer-${v}`,
1717
}));
1818

19-
describe('FilterStatusComponent hooks', () => {
20-
const context = { headers, state: { filters } };
21-
const mockTableContext = (newContext) => {
22-
React.useContext.mockReturnValueOnce(newContext);
23-
};
24-
beforeEach(() => {
25-
context.setAllFilters = jest.fn();
26-
});
27-
it('returns empty dict if setAllFilters or state.filters is falsey', () => {
28-
mockTableContext({ ...context, setAllFilters: null });
29-
expect(module.filterHooks()).toEqual({});
30-
mockTableContext({ ...context, state: { filters: null } });
31-
expect(module.filterHooks()).toEqual({});
32-
});
33-
describe('clearFilters', () => {
34-
it('uses React.useCallback to clear filters, only once', () => {
35-
mockTableContext(context);
36-
const { cb, prereqs } = module.filterHooks().clearFilters.useCallback;
37-
expect(prereqs).toEqual([context.setAllFilters]);
38-
expect(context.setAllFilters).not.toHaveBeenCalled();
39-
cb();
40-
expect(context.setAllFilters).toHaveBeenCalledWith([]);
41-
});
42-
});
43-
describe('filterNames', () => {
44-
it('returns list of Header values by filter order', () => {
45-
mockTableContext(context);
46-
expect(module.filterHooks().filterNames).toEqual(
47-
filterOrder.map(v => headers[v].Header),
48-
);
49-
});
50-
});
51-
});
5219
describe('FilterStatusComponent component', () => {
5320
const props = {
5421
className: 'css-class-name',
@@ -58,34 +25,98 @@ describe('FilterStatusComponent component', () => {
5825
buttonClassName: 'css-class-name-for-button',
5926
showFilteredFields: true,
6027
};
61-
const hookProps = {
62-
clearFilters: jest.fn().mockName('hookProps.clearFilters'),
63-
filterNames: ['filter-name-0', 'filter-name-1'],
64-
};
6528
const { FilterStatusComponent } = module;
66-
const mockHooks = (value) => {
67-
jest.spyOn(module, 'filterHooks').mockReturnValueOnce(value);
29+
30+
const renderWithContext = (contextValue, componentProps = props) => {
31+
const TestWrapper = ({ children }) => (
32+
<DataTableContext.Provider value={contextValue}>
33+
{children}
34+
</DataTableContext.Provider>
35+
);
36+
TestWrapper.propTypes = {
37+
children: PropTypes.node,
38+
};
39+
return render(
40+
<TestWrapper>
41+
<FilterStatusComponent {...componentProps} />
42+
</TestWrapper>,
43+
);
6844
};
69-
describe('snapshot', () => {
70-
describe('with filters', () => {
71-
test('showFilteredFields', () => {
72-
mockHooks(hookProps);
73-
const el = shallow(<FilterStatusComponent {...props} />);
74-
expect(el.snapshot).toMatchSnapshot();
45+
46+
beforeEach(() => {
47+
jest.clearAllMocks();
48+
});
49+
50+
describe('behavior', () => {
51+
it('does not render when there are no filters', () => {
52+
const contextValue = {
53+
headers,
54+
state: { filters: null },
55+
setAllFilters: jest.fn(),
56+
};
57+
const { container } = renderWithContext(contextValue);
58+
expect(container.firstChild).toBeNull();
59+
});
60+
61+
it('does not render when setAllFilters is not available', () => {
62+
const contextValue = { headers, state: { filters }, setAllFilters: null };
63+
const { container } = renderWithContext(contextValue);
64+
expect(container.firstChild).toBeNull();
65+
});
66+
67+
it('renders clear filters button with correct text when filters exist', () => {
68+
const contextValue = {
69+
headers,
70+
state: { filters },
71+
setAllFilters: jest.fn(),
72+
};
73+
const { getByText } = renderWithContext(contextValue);
74+
expect(getByText(props.clearFiltersText)).toBeInTheDocument();
75+
});
76+
77+
it('displays filtered field names when showFilteredFields is true', () => {
78+
const contextValue = {
79+
headers,
80+
state: { filters },
81+
setAllFilters: jest.fn(),
82+
};
83+
const { getByText } = renderWithContext(contextValue);
84+
const expectedFilterNames = filterOrder.map((v) => headers[v].Header);
85+
expectedFilterNames.forEach((name) => {
86+
expect(getByText(name, { exact: false })).toBeInTheDocument();
7587
});
76-
test('showFilteredFields=false - hide filterTexts', () => {
77-
mockHooks(hookProps);
78-
const el = shallow(
79-
<FilterStatusComponent {...props} showFilteredFields={false} />,
80-
);
81-
expect(el.snapshot).toMatchSnapshot();
88+
});
89+
90+
it('does not display filtered field names when showFilteredFields is false', () => {
91+
const contextValue = {
92+
headers,
93+
state: { filters },
94+
setAllFilters: jest.fn(),
95+
};
96+
const { queryByText } = renderWithContext(contextValue, {
97+
...props,
98+
showFilteredFields: false,
8299
});
100+
expect(queryByText(/Filtered by/)).not.toBeInTheDocument();
83101
});
84-
test('without filters', () => {
85-
mockHooks({});
86-
const el = shallow(<FilterStatusComponent {...props} />);
87-
expect(el.snapshot).toMatchSnapshot();
88-
expect(el.isEmptyRender()).toEqual(true);
102+
103+
it('applies correct CSS classes to the component', () => {
104+
const contextValue = {
105+
headers,
106+
state: { filters },
107+
setAllFilters: jest.fn(),
108+
};
109+
const { container } = renderWithContext(contextValue);
110+
expect(container.firstChild).toHaveClass(props.className);
111+
});
112+
113+
it('calls setAllFilters with empty array when clear button is clicked', () => {
114+
const setAllFilters = jest.fn();
115+
const contextValue = { headers, state: { filters }, setAllFilters };
116+
const { getByText } = renderWithContext(contextValue);
117+
const clearButton = getByText(props.clearFiltersText);
118+
clearButton.click();
119+
expect(setAllFilters).toHaveBeenCalledWith([]);
89120
});
90121
});
91122
});

0 commit comments

Comments
 (0)