Skip to content

test: deprecate react-unit-test-utils 10/14 #348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 44 additions & 93 deletions src/App.test.jsx
Original file line number Diff line number Diff line change
@@ -1,117 +1,68 @@
import React from 'react';
import { when } from 'jest-when';
import { Route, Routes } from 'react-router-dom';

import { ErrorPage } from '@edx/frontend-platform/react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { formatMessage, shallow } from '@edx/react-unit-test-utils';

import AssessmentView from 'views/AssessmentView';
import SubmissionView from 'views/SubmissionView';
import XBlockView from 'views/XBlockView';
import XBlockStudioView from 'views/XBlockStudioView';
import GradeView from 'views/GradeView';

import PageRoute from 'components/PageRoute';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { MemoryRouter } from 'react-router-dom';

import { useHandleModalCloseEvent } from 'hooks/modal';

import messages from './messages';
import routes from './routes';

import App from './App';

jest.mock('react-router-dom', () => ({
Routes: 'Routes',
Route: 'Route',
}));
/* eslint-disable react/prop-types */

jest.mock('@edx/frontend-platform/react', () => ({
AuthenticatedPageRoute: 'AuthenticatedPageRoute',
ErrorPage: 'ErrorPage',
}));
jest.mock('views/AssessmentView', () => 'AssessmentView');
jest.mock('views/SubmissionView', () => 'SubmissionView');
jest.mock('views/XBlockView', () => 'XBlockView');
jest.mock('views/XBlockStudioView', () => 'XBlockStudioView');
jest.mock('views/GradeView', () => 'GradeView');
jest.mock('components/PageRoute', () => 'PageRoute');
jest.unmock('@openedx/paragon');
jest.unmock('react');
jest.unmock('@edx/frontend-platform/i18n');

jest.mock('hooks/modal', () => ({
useHandleModalCloseEvent: jest.fn(),
}));

jest.mock('@edx/frontend-platform/react', () => ({
ErrorPage: ({ message }) => <div role="alert">{message || 'Error Page'}</div>,
}));

const handleModalClose = jest.fn();
when(useHandleModalCloseEvent).calledWith().mockReturnValue(handleModalClose);
const addEventListener = jest.fn();
const removeEventListener = jest.fn();

let el;
describe('App component', () => {
const renderWithProviders = (component) => render(
<MemoryRouter>
<IntlProvider locale="en" messages={{}}>
{component}
</IntlProvider>
</MemoryRouter>,
);

beforeEach(() => {
jest.clearAllMocks();
useHandleModalCloseEvent.mockReturnValue(handleModalClose);
jest.spyOn(window, 'addEventListener').mockImplementation(addEventListener);
jest.spyOn(window, 'removeEventListener').mockImplementation(removeEventListener);
el = shallow(<App />);
jest
.spyOn(window, 'removeEventListener')
.mockImplementation(removeEventListener);
});

it('renders app with accessible error page fallback', () => {
renderWithProviders(<App />);

expect(screen.getByRole('alert')).toHaveTextContent('Page not found');
});
describe('behavior', () => {
it('initializes i18n and refresh event from hooks', () => {
expect(useIntl).toHaveBeenCalled();
expect(useHandleModalCloseEvent).toHaveBeenCalled();
});
it('adds handler for modal close event that refreshes page data', () => {
expect(React.useEffect.mock.calls.length).toEqual(1);
const [[effect, prereqs]] = React.useEffect.mock.calls;
expect(prereqs).toEqual([handleModalClose]);
const out = effect();
expect(addEventListener).toHaveBeenCalledWith('message', handleModalClose);
out();
expect(removeEventListener).toHaveBeenCalledWith('message', handleModalClose);
});

it('calls useHandleModalCloseEvent hook and sets up event listeners', () => {
renderWithProviders(<App />);
expect(useHandleModalCloseEvent).toHaveBeenCalled();
expect(addEventListener).toHaveBeenCalledWith('message', handleModalClose);
});
describe('render', () => {
test('snapshot', () => {
expect(el.snapshot).toMatchSnapshot();
});
const testComponent = (toTest, { route, Component, isModal }) => {
expect(toTest.type).toEqual(Route);
expect(toTest.props.path).toEqual(route);
const { element } = toTest.props;
expect(toTest.props.element.type).toEqual(PageRoute);
if (isModal) {
expect(toTest.props.element.props.isModal).toEqual(true);
}
const expectedElement = shallow(<PageRoute><Component /></PageRoute>);
expect(shallow(element)).toMatchObject(expectedElement);
};
const testAssessmentRoute = (toTest, { route }) => {
testComponent(toTest, { route, Component: AssessmentView, isModal: true });
};
test('route order', () => {
const renderedRoutes = el.instance.findByType(Routes)[0].children;
testComponent(renderedRoutes[0], { route: routes.xblock, Component: XBlockView });
testComponent(renderedRoutes[1], { route: routes.xblockStudio, Component: XBlockStudioView });
testComponent(renderedRoutes[2], { route: routes.xblockPreview, Component: XBlockView });
testAssessmentRoute(renderedRoutes[3], { route: routes.peerAssessment });
testAssessmentRoute(renderedRoutes[4], { route: routes.selfAssessment });
testAssessmentRoute(renderedRoutes[5], { route: routes.studentTraining });
testComponent(renderedRoutes[6], {
route: routes.submission,
Component: SubmissionView,
isModal: true,
});
testComponent(renderedRoutes[7], {
route: routes.graded,
Component: GradeView,
isModal: true,
});
expect(renderedRoutes[8].matches(shallow(
<Route
key="error"
path={routes.root}
element={<ErrorPage message={formatMessage(messages.error404Message)} />}
/>,
)));
});

it('removes event listener on unmount', () => {
const { unmount } = renderWithProviders(<App />);

unmount();
expect(removeEventListener).toHaveBeenCalledWith(
'message',
handleModalClose,
);
});
});
89 changes: 0 additions & 89 deletions src/__snapshots__/App.test.jsx.snap

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,58 +1,83 @@
import { shallow } from '@edx/react-unit-test-utils';
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { IntlProvider } from '@edx/frontend-platform/i18n';

import { useOverallFeedbackPrompt, useOverallFeedbackFormFields } from 'hooks/assessment';
import { useViewStep } from 'hooks/routing';
import { stepNames } from 'constants/index';
import OverallFeedback from '.';

jest.unmock('@openedx/paragon');
jest.unmock('react');
jest.unmock('@edx/frontend-platform/i18n');

jest.mock('hooks/assessment', () => ({
useOverallFeedbackPrompt: jest.fn(),
useOverallFeedbackFormFields: jest.fn(),
}));

jest.mock('components/InfoPopover', () => 'InfoPopover');
jest.mock('components/InfoPopover', () => ({
__esModule: true,
default: ({ children }) => (
<div role="button" aria-label="Help information">
{children}
</div>
),
}));

jest.mock('hooks/routing', () => ({
useViewStep: jest.fn().mockReturnValue('step'),
useViewStep: jest.fn(),
}));

const messages = {
'frontend-app-ora.EditableAssessment.overallComments': 'Overall comments',
'frontend-app-ora.EditableAssessment.addComments': 'Add comments (Optional)',
};

describe('<OverallFeedback />', () => {
const mockOnChange = jest
.fn()
.mockName('useOverallFeedbackFormFields.onChange');
const mockFeedbackValue = 'useOverallFeedbackFormFields.value';
const mockPrompt = 'useOverallFeedbackPrompt';
const renderWithIntl = (component) => render(
<IntlProvider locale="en" messages={messages}>
{component}
</IntlProvider>,
);

const mockOnChange = jest.fn();
const mockFeedbackValue = 'Test feedback content';
const mockPrompt = 'Please provide overall feedback';

beforeAll(() => {
beforeEach(() => {
jest.clearAllMocks();
useOverallFeedbackPrompt.mockReturnValue(mockPrompt);
useOverallFeedbackFormFields.mockReturnValue({
value: mockFeedbackValue,
onChange: mockOnChange,
});
useViewStep.mockReturnValue('assessment');
});

it('render default', () => {
const wrapper = shallow(<OverallFeedback />);
expect(wrapper.snapshot).toMatchSnapshot();
it('renders overall feedback form with prompt and textarea', () => {
renderWithIntl(<OverallFeedback />);

expect(screen.getByText('Overall comments')).toBeInTheDocument();
expect(screen.getByText('Please provide overall feedback')).toBeInTheDocument();
expect(screen.getByRole('textbox')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Help information' })).toBeInTheDocument();
});

it('render empty on studentTraining', () => {
useViewStep.mockReturnValueOnce(stepNames.studentTraining);
const wrapper = shallow(<OverallFeedback />);
expect(wrapper.snapshot).toMatchSnapshot();
it('renders nothing when step is studentTraining', () => {
useViewStep.mockReturnValue(stepNames.studentTraining);

expect(wrapper.isEmptyRender()).toBe(true);
});
const { container } = renderWithIntl(<OverallFeedback />);

it('has correct mock value', () => {
const wrapper = shallow(<OverallFeedback />);
expect(container.firstChild).toBeNull();
});

expect(wrapper.instance.findByTestId('prompt-test-id')[0].children[0].el).toBe(
mockPrompt,
);
it('displays correct form field values from hooks', () => {
renderWithIntl(<OverallFeedback />);

const { props } = wrapper.instance.findByType('Form.Control')[0];
expect(props.value).toBe(mockFeedbackValue);
expect(props.onChange).toBe(mockOnChange);
const textarea = screen.getByRole('textbox');
expect(textarea).toHaveValue(mockFeedbackValue);
expect(screen.getByText('Please provide overall feedback')).toBeInTheDocument();
});
});
Loading
Loading