test: deprecate react-unit-test-utils part-8 (#444)

* test: deprecate react-unit-test-utils part-8

* test: change fireEvent to userEvent

* test: rebase fixes

* test: change fireEvent to userEvent

* test: fixes and adress review

* test: improve description test

---------

Co-authored-by: diana-villalvazo-wgu <diana.villalvazo@wgu.edu>
This commit is contained in:
Victor Navarro
2025-09-02 09:46:50 -06:00
committed by GitHub
parent 8e7bba5365
commit ac03594943
13 changed files with 462 additions and 437 deletions

View File

@@ -1,4 +1,5 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ConfirmModal } from './ConfirmModal';
jest.unmock('@openedx/paragon');
@@ -29,15 +30,17 @@ describe('ConfirmModal', () => {
expect(getByText(props.content)).toBeInTheDocument();
});
it('should call onCancel when cancel button is clicked', () => {
it('should call onCancel when cancel button is clicked', async () => {
render(<ConfirmModal {...props} isOpen />);
fireEvent.click(screen.getByText(props.cancelText));
const user = userEvent.setup();
await user.click(screen.getByText(props.cancelText));
expect(props.onCancel).toHaveBeenCalledTimes(1);
});
it('should call onConfirm when confirm button is clicked', () => {
it('should call onConfirm when confirm button is clicked', async () => {
render(<ConfirmModal {...props} isOpen />);
fireEvent.click(screen.getByText(props.confirmText));
const user = userEvent.setup();
await user.click(screen.getByText(props.confirmText));
expect(props.onConfirm).toHaveBeenCalledTimes(1);
});
});

View File

@@ -1,4 +1,5 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import FileInfo from './FileInfo';
@@ -20,10 +21,10 @@ describe('FileInfo component', () => {
expect(screen.getByText('FormattedMessage')).toBeInTheDocument();
});
it('calls onClick when button is clicked', () => {
it('calls onClick when button is clicked', async () => {
render(<FileInfo {...props}>{children}</FileInfo>);
fireEvent.click(screen.getByText('FormattedMessage'));
const user = userEvent.setup();
await user.click(screen.getByText('FormattedMessage'));
expect(props.onClick).toHaveBeenCalledTimes(1);
});
});

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { actions, selectors } from 'data/redux';
import {
@@ -49,26 +50,26 @@ describe('Criterion Feedback', () => {
describe('component', () => {
describe('render', () => {
it('shows a non-disabled input when grading', () => {
const { getByTestId } = render(<CriterionFeedback {...props} />);
const input = getByTestId('criterion-feedback-input');
render(<CriterionFeedback {...props} />);
const input = screen.getByTestId('criterion-feedback-input');
expect(input).toBeInTheDocument();
expect(input).not.toBeDisabled();
expect(input).toHaveValue(props.value);
});
it('shows a disabled input when not grading', () => {
const { getByTestId } = render(
render(
<CriterionFeedback {...props} isGrading={false} gradeStatus={gradeStatuses.graded} />,
);
const input = getByTestId('criterion-feedback-input');
const input = screen.getByTestId('criterion-feedback-input');
expect(input).toBeInTheDocument();
expect(input).toBeDisabled();
expect(input).toHaveValue(props.value);
});
it('displays an error message when feedback is invalid', () => {
const { getByTestId } = render(<CriterionFeedback {...props} isInvalid />);
expect(getByTestId('criterion-feedback-error-msg')).toBeInTheDocument();
render(<CriterionFeedback {...props} isInvalid />);
expect(screen.getByTestId('criterion-feedback-error-msg')).toBeInTheDocument();
});
it('does not render anything when config is set to disabled', () => {
@@ -80,12 +81,13 @@ describe('Criterion Feedback', () => {
});
describe('behavior', () => {
it('calls setValue when input value changes', () => {
const { getByTestId } = render(<CriterionFeedback {...props} />);
const input = getByTestId('criterion-feedback-input');
fireEvent.change(input, { target: { value: 'some value' } });
it('calls setValue when input value changes', async () => {
render(<CriterionFeedback {...props} />);
const user = userEvent.setup();
const input = screen.getByTestId('criterion-feedback-input');
await user.clear(input);
expect(props.setValue).toHaveBeenCalledWith({
value: 'some value',
value: '',
orderNum: props.orderNum,
});
});

View File

@@ -1,18 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StartGradingButton component component snapshots hide: renders empty component if hook.hide is true 1`] = `null`;
exports[`StartGradingButton component component snapshots smoke test: forwards props to components from hooks 1`] = `
<Fragment>
<Button
props="hooks.buttonArgs"
variant="primary"
/>
<OverrideGradeConfirmModal
props="hooks.overrideGradeArgs"
/>
<StopGradingConfirmModal
props="hooks.stopGradingArgs"
/>
</Fragment>
`;

View File

@@ -1,49 +1,131 @@
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import { render, screen } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { useDispatch } from 'react-redux';
import * as hooks from './hooks';
import { StartGradingButton } from '.';
import messages from '../messages';
jest.mock('../OverrideGradeConfirmModal', () => 'OverrideGradeConfirmModal');
jest.mock('../StopGradingConfirmModal', () => 'StopGradingConfirmModal');
jest.unmock('@openedx/paragon');
jest.unmock('react');
jest.unmock('@edx/frontend-platform/i18n');
jest.mock('react-redux', () => ({
useDispatch: jest.fn(),
}));
jest.mock('./hooks', () => ({
buttonHooks: jest.fn(),
}));
let el;
describe('StartGradingButton component', () => {
describe('component', () => {
const dispatch = useDispatch();
const buttonHooks = {
hide: false,
buttonArgs: { props: 'hooks.buttonArgs' },
overrideGradeArgs: { props: 'hooks.overrideGradeArgs' },
stopGradingArgs: { props: 'hooks.stopGradingArgs' },
};
describe('behavior', () => {
it('initializes buttonHooks with dispatch and intl fields', () => {
hooks.buttonHooks.mockReturnValueOnce(buttonHooks);
el = shallow(<StartGradingButton />);
const expected = { dispatch, intl: { formatMessage: expect.any(Function), formatDate: expect.any(Function) } };
expect(hooks.buttonHooks).toHaveBeenCalledWith(expected);
});
describe('StartGradingButton', () => {
const mockDispatch = jest.fn();
const renderWithIntl = (component) => render(
<IntlProvider locale="en" messages={{}}>
{component}
</IntlProvider>,
);
beforeEach(() => {
jest.clearAllMocks();
useDispatch.mockReturnValue(mockDispatch);
});
it('does not render when hide is true', () => {
hooks.buttonHooks.mockReturnValue({
hide: true,
buttonArgs: {},
overrideGradeArgs: {},
stopGradingArgs: {},
});
describe('snapshots', () => {
test('hide: renders empty component if hook.hide is true', () => {
hooks.buttonHooks.mockReturnValueOnce({ ...buttonHooks, hide: true });
el = shallow(<StartGradingButton />);
expect(el.snapshot).toMatchSnapshot();
expect(el.isEmptyRender()).toEqual(true);
});
test('smoke test: forwards props to components from hooks', () => {
hooks.buttonHooks.mockReturnValueOnce(buttonHooks);
el = shallow(<StartGradingButton />);
expect(el.snapshot).toMatchSnapshot();
expect(el.isEmptyRender()).toEqual(false);
});
const { container } = renderWithIntl(<StartGradingButton />);
expect(container.firstChild).toBeNull();
});
it('renders primary button when visible', () => {
hooks.buttonHooks.mockReturnValue({
hide: false,
buttonArgs: { children: 'Start Grading' },
overrideGradeArgs: {
isOpen: false,
onCancel: jest.fn(),
onConfirm: jest.fn(),
},
stopGradingArgs: {
isOpen: false,
isOverride: false,
onCancel: jest.fn(),
onConfirm: jest.fn(),
},
});
renderWithIntl(<StartGradingButton />);
const button = screen.getByRole('button', { name: 'Start Grading' });
expect(button).toBeInTheDocument();
expect(button).toHaveClass('btn-primary');
});
it('renders override grade modal components', () => {
hooks.buttonHooks.mockReturnValue({
hide: false,
buttonArgs: { children: 'Start Grading' },
overrideGradeArgs: {
isOpen: true,
onCancel: jest.fn(),
onConfirm: jest.fn(),
},
stopGradingArgs: {
isOpen: false,
isOverride: false,
onCancel: jest.fn(),
onConfirm: jest.fn(),
},
});
renderWithIntl(<StartGradingButton />);
const overrideModalTitle = screen.getByText(messages.overrideConfirmTitle.defaultMessage);
expect(overrideModalTitle).toBeInTheDocument();
});
it('renders stop grading modal components', () => {
hooks.buttonHooks.mockReturnValue({
hide: false,
buttonArgs: { children: 'Start Grading' },
overrideGradeArgs: {
isOpen: false,
onCancel: jest.fn(),
onConfirm: jest.fn(),
},
stopGradingArgs: {
isOpen: true,
isOverride: false,
onCancel: jest.fn(),
onConfirm: jest.fn(),
},
});
renderWithIntl(<StartGradingButton />);
const stopGradingModalTitle = screen.getByText(messages.confirmStopGradingTitle.defaultMessage);
expect(stopGradingModalTitle).toBeInTheDocument();
});
it('calls buttonHooks with dispatch and intl', () => {
hooks.buttonHooks.mockReturnValue({
hide: false,
buttonArgs: { children: 'Start Grading' },
overrideGradeArgs: {
isOpen: false,
onCancel: jest.fn(),
onConfirm: jest.fn(),
},
stopGradingArgs: {
isOpen: false,
isOverride: false,
onCancel: jest.fn(),
onConfirm: jest.fn(),
},
});
renderWithIntl(<StartGradingButton />);
expect(hooks.buttonHooks).toHaveBeenCalledWith({
dispatch: mockDispatch,
intl: expect.any(Object),
});
});
});

View File

@@ -1,60 +1,89 @@
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import { selectors } from 'data/redux';
import { RequestKeys } from 'data/constants/requests';
import { render, screen } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import {
ReviewContent,
mapStateToProps,
} from './ReviewContent';
jest.unmock('@openedx/paragon');
jest.unmock('react');
jest.unmock('@edx/frontend-platform/i18n');
// Since we are only testing the ReviewContent component,
// we can mock the child components to avoid unnecessary complexity on mocking the redux store.
jest.mock('containers/ReviewModal/ReviewErrors/FetchErrors', () => {
const FetchErrors = () => <div>FetchErrors</div>;
return FetchErrors;
});
jest.mock('containers/ReviewModal/ReviewErrors/SubmitErrors', () => {
const SubmitErrors = () => <div>SubmitErrors</div>;
return SubmitErrors;
});
jest.mock('containers/ReviewModal/ReviewErrors/LockErrors', () => {
const LockErrors = () => <div>LockErrors</div>;
return LockErrors;
});
jest.mock('containers/ReviewModal/ReviewErrors/DownloadErrors', () => {
const DownloadErrors = () => <div>DownloadErrors</div>;
return DownloadErrors;
});
jest.mock('containers/ResponseDisplay', () => {
const ResponseDisplay = () => <div>ResponseDisplay</div>;
return ResponseDisplay;
});
jest.mock('containers/Rubric', () => {
const Rubric = () => <div>Rubric</div>;
return Rubric;
});
jest.mock('data/redux', () => ({
selectors: {
app: {
showRubric: (...args) => ({ showRubric: args }),
showRubric: jest.fn(() => true),
},
requests: {
isCompleted: (...args) => ({ isCompleted: args }),
isFailed: (...args) => ({ isFailed: args }),
isCompleted: jest.fn(() => false),
isFailed: jest.fn(() => false),
},
},
}));
jest.mock('containers/ResponseDisplay', () => 'ResponseDisplay');
jest.mock('containers/Rubric', () => 'Rubric');
jest.mock('./ReviewErrors', () => 'ReviewErrors');
describe('ReviewContent component', () => {
describe('component', () => {
describe('render tests', () => {
test('snapshot: not loaded, no error', () => {
expect(shallow(<ReviewContent />).isEmptyRender()).toEqual(true);
});
test('snapshot: show rubric', () => {
expect(shallow(<ReviewContent isLoaded />).snapshot).toMatchSnapshot();
});
test('snapshot: hide rubric', () => {
expect(shallow(<ReviewContent isLoaded showRubric />).snapshot).toMatchSnapshot();
});
test('snapshot: failed, showRubric (errors only)', () => {
expect(shallow(<ReviewContent showRubric isFailed />).snapshot).toMatchSnapshot();
});
});
const renderWithIntl = (component) => render(
<IntlProvider locale="en" messages={{}}>
{component}
</IntlProvider>,
);
beforeEach(() => {
jest.clearAllMocks();
});
describe('mapStateToProps', () => {
let mapped;
const testState = { some: 'test-state' };
beforeEach(() => {
mapped = mapStateToProps(testState);
describe('behavior', () => {
it('renders nothing when not loaded and no error', () => {
const { container } = renderWithIntl(<ReviewContent />);
expect(container.querySelector('.content-block')).not.toBeInTheDocument();
});
const requestKey = RequestKeys.fetchSubmission;
test('showRubric loads from app.showRubric', () => {
expect(mapped.showRubric).toEqual(selectors.app.showRubric(testState));
it('renders review errors when failed', () => {
renderWithIntl(<ReviewContent isFailed />);
expect(screen.getByText('FetchErrors')).toBeInTheDocument();
expect(screen.getByText('SubmitErrors')).toBeInTheDocument();
expect(screen.getByText('LockErrors')).toBeInTheDocument();
expect(screen.getByText('DownloadErrors')).toBeInTheDocument();
});
test('isFailed loads from requests.isFailed(fetchSubmission)', () => {
expect(mapped.isFailed).toEqual(selectors.requests.isFailed(testState, { requestKey }));
it('renders response display when loaded', () => {
renderWithIntl(<ReviewContent isLoaded />);
expect(screen.getByText('ResponseDisplay')).toBeInTheDocument();
});
test('isLoaded loads from requests.isCompleted(fetchSubmission)', () => {
expect(mapped.isLoaded).toEqual(selectors.requests.isCompleted(testState, { requestKey }));
it('renders with rubric when showRubric is true and loaded', () => {
const { container, getByText } = renderWithIntl(<ReviewContent isLoaded showRubric />);
expect(container.querySelector('.content-block')).toBeInTheDocument();
expect(container.querySelector('.flex-nowrap')).toBeInTheDocument();
expect(getByText('Rubric')).toBeInTheDocument();
});
});
});

View File

@@ -1,22 +1,19 @@
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { selectors, actions, thunkActions } from 'data/redux';
import { RequestKeys } from 'data/constants/requests';
import { DownloadErrors, mapStateToProps, mapDispatchToProps } from './DownloadErrors';
import {
DownloadErrors,
mapStateToProps,
mapDispatchToProps,
} from './DownloadErrors';
let el;
jest.unmock('@openedx/paragon');
jest.unmock('react');
jest.unmock('@edx/frontend-platform/i18n');
jest.mock('data/redux', () => ({
selectors: {
requests: {
isFailed: (...args) => ({ isFailed: args }),
error: (...args) => ({ error: args }),
isFailed: jest.fn((state) => state.isFailed || false),
error: jest.fn((state) => state.error || { files: [] }),
},
},
actions: {
@@ -26,62 +23,101 @@ jest.mock('data/redux', () => ({
download: { downloadFiles: jest.fn() },
},
}));
jest.mock('./ReviewError', () => 'ReviewError');
const renderWithIntl = (component) => render(
<IntlProvider locale="en" messages={{}}>
{component}
</IntlProvider>,
);
describe('DownloadErrors component', () => {
const props = {
const defaultProps = {
isFailed: false,
error: {
files: [],
},
error: { files: [] },
clearState: jest.fn(),
downloadFiles: jest.fn(),
};
describe('component', () => {
beforeEach(() => {
props.clearState = jest.fn();
props.downloadFiles = jest.fn().mockName('this.props.downloadFiles');
el = shallow(<DownloadErrors {...props} />);
});
describe('snapshots', () => {
test('failed: show error', () => {
el = shallow(<DownloadErrors {...props} isFailed error={{ files: ['file-1-failed.error', 'file-2.failed'] }} />);
expect(el.snapshot).toMatchSnapshot();
expect(el.isEmptyRender()).toEqual(false);
});
test('not failed: hide error', () => {
expect(el.snapshot).toMatchSnapshot();
expect(el.isEmptyRender()).toEqual(true);
});
});
describe('behavior', () => {
describe('clearState', () => {
it('calls props.clearState with requestKey: downloadFiles', () => {
el = shallow(<DownloadErrors {...props} isFailed error={{ files: ['file-1-failed.error', 'file-2.failed'] }} />);
el.instance.props.actions.cancel.onClick();
expect(props.clearState).toHaveBeenCalledWith({ requestKey: RequestKeys.downloadFiles });
});
});
});
beforeEach(() => {
jest.clearAllMocks();
});
it('should not render when isFailed is false', () => {
const { container } = renderWithIntl(<DownloadErrors {...defaultProps} />);
expect(container.firstChild).toBeNull();
});
it('should render error message when isFailed is true', () => {
const props = {
...defaultProps,
isFailed: true,
error: { files: ['file-1-failed.error', 'file-2.failed'] },
};
const { getByText } = renderWithIntl(<DownloadErrors {...props} />);
expect(getByText("Couldn't download files")).toBeInTheDocument();
});
it('should display list of failed files', () => {
const props = {
...defaultProps,
isFailed: true,
error: { files: ['file-1-failed.error', 'file-2.failed'] },
};
const { getByText } = renderWithIntl(<DownloadErrors {...props} />);
expect(getByText('file-1-failed.error')).toBeInTheDocument();
expect(getByText('file-2.failed')).toBeInTheDocument();
});
it('should call clearState when dismiss button is clicked', async () => {
const props = {
...defaultProps,
isFailed: true,
error: { files: ['test-file.error'] },
};
renderWithIntl(<DownloadErrors {...props} />);
const user = userEvent.setup();
await user.click(screen.getByText('Dismiss'));
expect(props.clearState).toHaveBeenCalledWith({ requestKey: RequestKeys.downloadFiles });
});
it('should call downloadFiles when retry button is clicked', async () => {
const props = {
...defaultProps,
isFailed: true,
error: { files: ['test-file.error'] },
};
renderWithIntl(<DownloadErrors {...props} />);
const user = userEvent.setup();
await user.click(screen.getByText('Retry download'));
expect(props.downloadFiles).toHaveBeenCalled();
});
describe('mapStateToProps', () => {
let mapped;
const testState = { some: 'test-state' };
beforeEach(() => {
mapped = mapStateToProps(testState);
it('should map isFailed from requests selector', () => {
const testState = { some: 'test-state' };
const mapped = mapStateToProps(testState);
expect(selectors.requests.isFailed).toHaveBeenCalledWith(testState, { requestKey: RequestKeys.downloadFiles });
expect(mapped.isFailed).toEqual(
selectors.requests.isFailed(testState, { requestKey: RequestKeys.downloadFiles }),
);
});
test('isFailed loads from requests.isFailed(downloadFiles)', () => {
const requestKey = RequestKeys.downloadFiles;
expect(mapped.isFailed).toEqual(selectors.requests.isFailed(testState, { requestKey }));
});
test('error loads from requests.error(downloadFiles)', () => {
const requestKey = RequestKeys.downloadFiles;
expect(mapped.error).toEqual(selectors.requests.error(testState, { requestKey }));
it('should map error from requests selector', () => {
const testState = { some: 'test-state' };
const mapped = mapStateToProps(testState);
expect(selectors.requests.error).toHaveBeenCalledWith(testState, { requestKey: RequestKeys.downloadFiles });
expect(mapped.error).toEqual(
selectors.requests.error(testState, { requestKey: RequestKeys.downloadFiles }),
);
});
});
describe('mapDispatchToProps', () => {
it('loads clearState from actions.requests.clearRequest', () => {
it('should map clearState to actions.requests.clearRequest', () => {
expect(mapDispatchToProps.clearState).toEqual(actions.requests.clearRequest);
});
it('loads downloadFiles from thunkActions.download.downloadFiles', () => {
it('should map downloadFiles to thunkActions.download.downloadFiles', () => {
expect(mapDispatchToProps.downloadFiles).toEqual(thunkActions.download.downloadFiles);
});
});

View File

@@ -1,59 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DownloadErrors component component snapshots failed: show error 1`] = `
<ReviewError
actions={
{
"cancel": {
"message": {
"defaultMessage": "Dismiss",
"description": "Dismiss error action button text",
"id": "ora-grading.ReviewModal.dismiss",
},
"onClick": [Function],
},
"confirm": {
"message": {
"defaultMessage": "Retry download",
"description": "Failed download retry button text",
"id": "ora-grading.ReviewModal.errorRetryDownload",
},
"onClick": [MockFunction this.props.downloadFiles],
},
}
}
headingMessage={
{
"defaultMessage": "Couldn't download files",
"id": "ora-grading.ReviewModal.errorDownloadFailed",
}
}
key="downloadFailed"
>
<FormattedMessage
defaultMessage="We're sorry, something went wrong when we tried to download these files. Please try again."
description="Failed download error content"
id="ora-grading.ReviewModal.errorDownloadFailedContent"
/>
<br />
<FormattedMessage
defaultMessage="Failed files:"
description="List header for file download failure alert"
id="ora-grading.ReviewModal.errorDownloadFailedFiles"
/>
<ul>
<li
key="file-1-failed.error"
>
file-1-failed.error
</li>
<li
key="file-2.failed"
>
file-2.failed
</li>
</ul>
</ReviewError>
`;
exports[`DownloadErrors component component snapshots not failed: hide error 1`] = `null`;

View File

@@ -1,56 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ReviewContent component component render tests snapshot: failed, showRubric (errors only) 1`] = `
<div
className="content-block"
>
<div
className="content-wrapper"
>
<ReviewErrors />
</div>
</div>
`;
exports[`ReviewContent component component render tests snapshot: hide rubric 1`] = `
<div
className="content-block"
>
<div
className="content-wrapper"
>
<ReviewErrors />
<Row
className="flex-nowrap m-0"
>
<Col
className="p-0"
>
<ResponseDisplay />
</Col>
<Rubric />
</Row>
</div>
</div>
`;
exports[`ReviewContent component component render tests snapshot: show rubric 1`] = `
<div
className="content-block"
>
<div
className="content-wrapper"
>
<ReviewErrors />
<Row
className="flex-nowrap m-0"
>
<Col
className="p-0"
>
<ResponseDisplay />
</Col>
</Row>
</div>
</div>
`;

View File

@@ -1,72 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ReviewModal component component snapshots closed 1`] = `
<FullscreenModal
beforeBodyNode={
<React.Fragment>
<ReviewActions />
<DemoWarning />
</React.Fragment>
}
className="review-modal"
isOpen={false}
modalBodyClassName="review-modal-body"
onClose={[MockFunction hooks.onClose]}
title="test-ora-name"
>
<CloseReviewConfirmModal
prop="hooks.closeConfirmModalProps"
/>
</FullscreenModal>
`;
exports[`ReviewModal component component snapshots loading 1`] = `
<FullscreenModal
beforeBodyNode={
<React.Fragment>
<ReviewActions />
<DemoWarning />
</React.Fragment>
}
className="review-modal"
isOpen={true}
modalBodyClassName="review-modal-body"
onClose={[MockFunction hooks.onClose]}
title="test-ora-name"
>
<ReviewContent />
<LoadingMessage
message={
{
"defaultMessage": "Loading response",
"description": "loading text for submission response review screen",
"id": "ora-grading.ReviewModal.loadingResponse",
}
}
/>
<CloseReviewConfirmModal
prop="hooks.closeConfirmModalProps"
/>
</FullscreenModal>
`;
exports[`ReviewModal component component snapshots success 1`] = `
<FullscreenModal
beforeBodyNode={
<React.Fragment>
<ReviewActions />
<DemoWarning />
</React.Fragment>
}
className="review-modal"
isOpen={true}
modalBodyClassName="review-modal-body"
onClose={[MockFunction hooks.onClose]}
title="test-ora-name"
>
<ReviewContent />
<CloseReviewConfirmModal
prop="hooks.closeConfirmModalProps"
/>
</FullscreenModal>
`;

View File

@@ -1,22 +1,51 @@
import { shallow } from '@edx/react-unit-test-utils';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import CloseReviewConfirmModal from './CloseReviewConfirmModal';
import { CloseReviewConfirmModal } from './CloseReviewConfirmModal';
jest.unmock('@openedx/paragon');
jest.unmock('react');
jest.unmock('@edx/frontend-platform/i18n');
jest.mock('components/ConfirmModal', () => 'ConfirmModal');
const renderWithIntl = (component) => render(
<IntlProvider locale="en" messages={{}}>
{component}
</IntlProvider>,
);
describe('CloseReviewConfirmModal', () => {
const props = {
isOpen: false,
onCancel: jest.fn().mockName('this.props.onCancel'),
onConfirm: jest.fn().mockName('this.props.onConfirm'),
onCancel: jest.fn(),
onConfirm: jest.fn(),
};
describe('snapshot', () => {
test('closed', () => {
expect(shallow(<CloseReviewConfirmModal {...props} />).snapshot).toMatchSnapshot();
});
test('open', () => {
expect(shallow(<CloseReviewConfirmModal {...props} isOpen />).snapshot).toMatchSnapshot();
});
beforeEach(() => {
jest.clearAllMocks();
});
it('should not render content when modal is closed', () => {
const { queryByText } = renderWithIntl(<CloseReviewConfirmModal {...props} />);
expect(queryByText('This cannot be undone')).toBeNull();
});
it('should display content when modal is open', () => {
const { getByText } = renderWithIntl(<CloseReviewConfirmModal {...props} isOpen />);
expect(getByText('Are you sure you want to close this modal?')).toBeInTheDocument();
expect(getByText(/This cannot be undone.*This will discard unsaved work/)).toBeInTheDocument();
});
it('should call onCancel when cancel button is clicked', async () => {
renderWithIntl(<CloseReviewConfirmModal {...props} isOpen />);
const user = userEvent.setup();
await user.click(screen.getByText('Go back'));
expect(props.onCancel).toHaveBeenCalledTimes(1);
});
it('should call onConfirm when confirm button is clicked', async () => {
renderWithIntl(<CloseReviewConfirmModal {...props} isOpen />);
const user = userEvent.setup();
await user.click(screen.getByText('Close Modal'));
expect(props.onConfirm).toHaveBeenCalledTimes(1);
});
});

View File

@@ -1,25 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CloseReviewConfirmModal snapshot closed 1`] = `
<ConfirmModal
cancelText="Go back"
confirmText="Close Modal"
content="This cannot be undone. This will discard unsaved work and stop this grading process."
isOpen={false}
onCancel={[MockFunction this.props.onCancel]}
onConfirm={[MockFunction this.props.onConfirm]}
title="Are you sure you want to close this modal?"
/>
`;
exports[`CloseReviewConfirmModal snapshot open 1`] = `
<ConfirmModal
cancelText="Go back"
confirmText="Close Modal"
content="This cannot be undone. This will discard unsaved work and stop this grading process."
isOpen={true}
onCancel={[MockFunction this.props.onCancel]}
onConfirm={[MockFunction this.props.onConfirm]}
title="Are you sure you want to close this modal?"
/>
`;

View File

@@ -1,60 +1,133 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import { shallow } from '@edx/react-unit-test-utils';
import { formatMessage } from 'testUtils';
import { render, screen } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import * as hooks from './hooks';
import { ReviewModal } from '.';
import messages from './messages';
jest.useFakeTimers('modern');
jest.unmock('@openedx/paragon');
jest.unmock('react');
jest.unmock('@edx/frontend-platform/i18n');
jest.mock('components/LoadingMessage', () => 'LoadingMessage');
jest.mock('containers/DemoWarning', () => 'DemoWarning');
jest.mock('containers/ReviewActions', () => 'ReviewActions');
jest.mock('./ReviewContent', () => 'ReviewContent');
jest.mock('./components/CloseReviewConfirmModal', () => 'CloseReviewConfirmModal');
jest.mock('react-redux', () => ({
useDispatch: jest.fn(),
connect: jest.fn(() => (Component) => Component),
}));
jest.mock('./hooks', () => ({
rendererHooks: jest.fn(),
}));
describe('ReviewModal component', () => {
const dispatch = useDispatch();
const hookProps = {
isLoading: false,
title: 'test-ora-name',
onClose: jest.fn().mockName('hooks.onClose'),
isOpen: false,
closeConfirmModalProps: {
prop: 'hooks.closeConfirmModalProps',
},
};
jest.mock('containers/ReviewModal/ReviewContent', () => {
const ReviewContent = () => <div>ReviewContent</div>;
return ReviewContent;
});
const render = (newVals) => {
hooks.rendererHooks.mockReturnValueOnce({ ...hookProps, ...newVals });
return shallow(<ReviewModal />);
};
describe('component', () => {
describe('snapshots', () => {
test('closed', () => {
expect(render().snapshot).toMatchSnapshot();
});
test('loading', () => {
expect(render({ isOpen: true, isLoading: true }).snapshot).toMatchSnapshot();
});
test('success', () => {
expect(render({ isOpen: true }).snapshot).toMatchSnapshot();
});
jest.mock('containers/ReviewActions', () => {
const ReviewActions = () => <div>ReviewActions</div>;
return ReviewActions;
});
jest.mock('containers/DemoWarning', () => {
const DemoWarning = () => <div>DemoWarning</div>;
return DemoWarning;
});
jest.mock('containers/ReviewModal/components/CloseReviewConfirmModal', () => {
const CloseReviewConfirmModal = () => <div>CloseReviewConfirmModal</div>;
return CloseReviewConfirmModal;
});
describe('ReviewModal', () => {
const mockDispatch = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
useDispatch.mockReturnValue(mockDispatch);
});
it('calls rendererHooks with dispatch and intl', () => {
hooks.rendererHooks.mockReturnValue({
isLoading: false,
title: 'test-ora-name',
onClose: jest.fn(),
isOpen: false,
closeConfirmModalProps: {
isOpen: false,
onCancel: jest.fn(),
onConfirm: jest.fn(),
},
});
render(<IntlProvider locale="en" messages={{}}><ReviewModal /></IntlProvider>);
expect(hooks.rendererHooks).toHaveBeenCalledWith({
dispatch: mockDispatch,
intl: expect.any(Object),
});
});
describe('behavior', () => {
it('initializes renderer hook with dispatch and intl props', () => {
render();
expect(hooks.rendererHooks).toHaveBeenCalledWith({
dispatch,
intl: { formatMessage, formatDate: expect.any(Function) },
});
it('calls useDispatch hook', () => {
hooks.rendererHooks.mockReturnValue({
isLoading: false,
title: 'test-ora-name',
onClose: jest.fn(),
isOpen: false,
closeConfirmModalProps: {
isOpen: false,
onCancel: jest.fn(),
onConfirm: jest.fn(),
},
});
render(<IntlProvider locale="en" messages={{}}><ReviewModal /></IntlProvider>);
screen.debug();
expect(useDispatch).toHaveBeenCalled();
});
it('renders correctly when open', () => {
hooks.rendererHooks.mockReturnValue({
isLoading: false,
title: 'test-ora-name',
onClose: jest.fn(),
isOpen: true,
closeConfirmModalProps: {
isOpen: false,
onCancel: jest.fn(),
onConfirm: jest.fn(),
},
});
render(<IntlProvider locale="en" messages={{}}><ReviewModal /></IntlProvider>);
const reviewActions = screen.getByText('ReviewActions');
expect(reviewActions).toBeInTheDocument();
const demoWarning = screen.getByText('DemoWarning');
expect(demoWarning).toBeInTheDocument();
const reviewContent = screen.getByText('ReviewContent');
expect(reviewContent).toBeInTheDocument();
const closeReviewConfirmModal = screen.getByText('CloseReviewConfirmModal');
expect(closeReviewConfirmModal).toBeInTheDocument();
});
it('renders correctly loading message', () => {
hooks.rendererHooks.mockReturnValue({
isLoading: true,
title: 'test-ora-name',
onClose: jest.fn(),
isOpen: true,
closeConfirmModalProps: {
isOpen: false,
onCancel: jest.fn(),
onConfirm: jest.fn(),
},
});
render(<IntlProvider locale="en" messages={{}}><ReviewModal /></IntlProvider>);
screen.debug();
const loadingMessage = screen.getByText(messages.loadingResponse.defaultMessage);
expect(loadingMessage).toBeInTheDocument();
});
});