|
1 | 1 | import React from 'react';
|
2 |
| -import { when } from 'jest-when'; |
3 |
| -import { Route, Routes } from 'react-router-dom'; |
4 |
| - |
5 |
| -import { ErrorPage } from '@edx/frontend-platform/react'; |
6 |
| -import { useIntl } from '@edx/frontend-platform/i18n'; |
7 |
| -import { formatMessage, shallow } from '@edx/react-unit-test-utils'; |
8 |
| - |
9 |
| -import AssessmentView from 'views/AssessmentView'; |
10 |
| -import SubmissionView from 'views/SubmissionView'; |
11 |
| -import XBlockView from 'views/XBlockView'; |
12 |
| -import XBlockStudioView from 'views/XBlockStudioView'; |
13 |
| -import GradeView from 'views/GradeView'; |
14 |
| - |
15 |
| -import PageRoute from 'components/PageRoute'; |
| 2 | +import { render, screen } from '@testing-library/react'; |
| 3 | +import '@testing-library/jest-dom'; |
| 4 | +import { IntlProvider } from '@edx/frontend-platform/i18n'; |
| 5 | +import { MemoryRouter } from 'react-router-dom'; |
16 | 6 |
|
17 | 7 | import { useHandleModalCloseEvent } from 'hooks/modal';
|
18 | 8 |
|
19 |
| -import messages from './messages'; |
20 |
| -import routes from './routes'; |
21 |
| - |
22 | 9 | import App from './App';
|
23 | 10 |
|
24 |
| -jest.mock('react-router-dom', () => ({ |
25 |
| - Routes: 'Routes', |
26 |
| - Route: 'Route', |
27 |
| -})); |
| 11 | +/* eslint-disable react/prop-types */ |
28 | 12 |
|
29 |
| -jest.mock('@edx/frontend-platform/react', () => ({ |
30 |
| - AuthenticatedPageRoute: 'AuthenticatedPageRoute', |
31 |
| - ErrorPage: 'ErrorPage', |
32 |
| -})); |
33 |
| -jest.mock('views/AssessmentView', () => 'AssessmentView'); |
34 |
| -jest.mock('views/SubmissionView', () => 'SubmissionView'); |
35 |
| -jest.mock('views/XBlockView', () => 'XBlockView'); |
36 |
| -jest.mock('views/XBlockStudioView', () => 'XBlockStudioView'); |
37 |
| -jest.mock('views/GradeView', () => 'GradeView'); |
38 |
| -jest.mock('components/PageRoute', () => 'PageRoute'); |
| 13 | +jest.unmock('@openedx/paragon'); |
| 14 | +jest.unmock('react'); |
| 15 | +jest.unmock('@edx/frontend-platform/i18n'); |
39 | 16 |
|
40 | 17 | jest.mock('hooks/modal', () => ({
|
41 | 18 | useHandleModalCloseEvent: jest.fn(),
|
42 | 19 | }));
|
43 | 20 |
|
| 21 | +jest.mock('@edx/frontend-platform/react', () => ({ |
| 22 | + ErrorPage: ({ message }) => <div role="alert">{message || 'Error Page'}</div>, |
| 23 | +})); |
| 24 | + |
44 | 25 | const handleModalClose = jest.fn();
|
45 |
| -when(useHandleModalCloseEvent).calledWith().mockReturnValue(handleModalClose); |
46 | 26 | const addEventListener = jest.fn();
|
47 | 27 | const removeEventListener = jest.fn();
|
48 | 28 |
|
49 |
| -let el; |
50 | 29 | describe('App component', () => {
|
| 30 | + const renderWithProviders = (component) => render( |
| 31 | + <MemoryRouter> |
| 32 | + <IntlProvider locale="en" messages={{}}> |
| 33 | + {component} |
| 34 | + </IntlProvider> |
| 35 | + </MemoryRouter>, |
| 36 | + ); |
| 37 | + |
51 | 38 | beforeEach(() => {
|
52 | 39 | jest.clearAllMocks();
|
| 40 | + useHandleModalCloseEvent.mockReturnValue(handleModalClose); |
53 | 41 | jest.spyOn(window, 'addEventListener').mockImplementation(addEventListener);
|
54 |
| - jest.spyOn(window, 'removeEventListener').mockImplementation(removeEventListener); |
55 |
| - el = shallow(<App />); |
| 42 | + jest |
| 43 | + .spyOn(window, 'removeEventListener') |
| 44 | + .mockImplementation(removeEventListener); |
| 45 | + }); |
| 46 | + |
| 47 | + it('renders app with accessible error page fallback', () => { |
| 48 | + renderWithProviders(<App />); |
| 49 | + |
| 50 | + expect(screen.getByRole('alert')).toHaveTextContent('Page not found'); |
56 | 51 | });
|
57 |
| - describe('behavior', () => { |
58 |
| - it('initializes i18n and refresh event from hooks', () => { |
59 |
| - expect(useIntl).toHaveBeenCalled(); |
60 |
| - expect(useHandleModalCloseEvent).toHaveBeenCalled(); |
61 |
| - }); |
62 |
| - it('adds handler for modal close event that refreshes page data', () => { |
63 |
| - expect(React.useEffect.mock.calls.length).toEqual(1); |
64 |
| - const [[effect, prereqs]] = React.useEffect.mock.calls; |
65 |
| - expect(prereqs).toEqual([handleModalClose]); |
66 |
| - const out = effect(); |
67 |
| - expect(addEventListener).toHaveBeenCalledWith('message', handleModalClose); |
68 |
| - out(); |
69 |
| - expect(removeEventListener).toHaveBeenCalledWith('message', handleModalClose); |
70 |
| - }); |
| 52 | + |
| 53 | + it('calls useHandleModalCloseEvent hook and sets up event listeners', () => { |
| 54 | + renderWithProviders(<App />); |
| 55 | + expect(useHandleModalCloseEvent).toHaveBeenCalled(); |
| 56 | + expect(addEventListener).toHaveBeenCalledWith('message', handleModalClose); |
71 | 57 | });
|
72 |
| - describe('render', () => { |
73 |
| - test('snapshot', () => { |
74 |
| - expect(el.snapshot).toMatchSnapshot(); |
75 |
| - }); |
76 |
| - const testComponent = (toTest, { route, Component, isModal }) => { |
77 |
| - expect(toTest.type).toEqual(Route); |
78 |
| - expect(toTest.props.path).toEqual(route); |
79 |
| - const { element } = toTest.props; |
80 |
| - expect(toTest.props.element.type).toEqual(PageRoute); |
81 |
| - if (isModal) { |
82 |
| - expect(toTest.props.element.props.isModal).toEqual(true); |
83 |
| - } |
84 |
| - const expectedElement = shallow(<PageRoute><Component /></PageRoute>); |
85 |
| - expect(shallow(element)).toMatchObject(expectedElement); |
86 |
| - }; |
87 |
| - const testAssessmentRoute = (toTest, { route }) => { |
88 |
| - testComponent(toTest, { route, Component: AssessmentView, isModal: true }); |
89 |
| - }; |
90 |
| - test('route order', () => { |
91 |
| - const renderedRoutes = el.instance.findByType(Routes)[0].children; |
92 |
| - testComponent(renderedRoutes[0], { route: routes.xblock, Component: XBlockView }); |
93 |
| - testComponent(renderedRoutes[1], { route: routes.xblockStudio, Component: XBlockStudioView }); |
94 |
| - testComponent(renderedRoutes[2], { route: routes.xblockPreview, Component: XBlockView }); |
95 |
| - testAssessmentRoute(renderedRoutes[3], { route: routes.peerAssessment }); |
96 |
| - testAssessmentRoute(renderedRoutes[4], { route: routes.selfAssessment }); |
97 |
| - testAssessmentRoute(renderedRoutes[5], { route: routes.studentTraining }); |
98 |
| - testComponent(renderedRoutes[6], { |
99 |
| - route: routes.submission, |
100 |
| - Component: SubmissionView, |
101 |
| - isModal: true, |
102 |
| - }); |
103 |
| - testComponent(renderedRoutes[7], { |
104 |
| - route: routes.graded, |
105 |
| - Component: GradeView, |
106 |
| - isModal: true, |
107 |
| - }); |
108 |
| - expect(renderedRoutes[8].matches(shallow( |
109 |
| - <Route |
110 |
| - key="error" |
111 |
| - path={routes.root} |
112 |
| - element={<ErrorPage message={formatMessage(messages.error404Message)} />} |
113 |
| - />, |
114 |
| - ))); |
115 |
| - }); |
| 58 | + |
| 59 | + it('removes event listener on unmount', () => { |
| 60 | + const { unmount } = renderWithProviders(<App />); |
| 61 | + |
| 62 | + unmount(); |
| 63 | + expect(removeEventListener).toHaveBeenCalledWith( |
| 64 | + 'message', |
| 65 | + handleModalClose, |
| 66 | + ); |
116 | 67 | });
|
117 | 68 | });
|
0 commit comments