test: replacing snapshot tests with RTL tests part 2 (#2132)

This commit is contained in:
jacobo-dominguez-wgu
2025-06-11 11:15:59 -06:00
committed by GitHub
parent acef2e70cc
commit 3097976b7b
14 changed files with 312 additions and 1033 deletions

View File

@@ -1,224 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EditorProblemView component renders markdown editor when isMarkdownEditorEnabled is true 1`] = `
<EditorContainer
getContent={[Function]}
isDirty={[Function]}
returnFunction={null}
>
<AlertModal
footerNode={
<ActionRow>
<Button
onClick={[Function]}
variant="tertiary"
>
<FormattedMessage
defaultMessage="Cancel"
description="Label for cancel button in the save warning modal"
id="authoring.problemEditor.editProblemView.saveWarningModal.cancelButton.label"
/>
</Button>
<Button
onClick={[Function]}
>
<FormattedMessage
defaultMessage="Ok"
description="Label for save button in the save warning modal"
id="authoring.problemEditor.editProblemView.saveWarningModal.saveButton.label"
/>
</Button>
</ActionRow>
}
isOpen={false}
onClose={[Function]}
title="No answer specified"
>
<Fragment>
<div>
<FormattedMessage
defaultMessage="Are you sure you want to exit the editor?"
description="Question in body of save warning modal"
id="authoring.problemEditor.editProblemView.saveWarningModal.body.question"
/>
</div>
<div>
<FormattedMessage
defaultMessage="No correct answer has been specified."
description="Explanation in body of no answer modal"
id="authoring.problemEditor.editProblemView.saveWarningModal.noAnswer.body.explanation"
/>
</div>
</Fragment>
</AlertModal>
<div
className="editProblemView d-flex flex-row flex-nowrap justify-content-end"
>
<Container
className="advancedEditorTopMargin p-0"
fluid={true}
>
<RawEditor
content="# Markdown content"
editorRef={
{
"current": null,
}
}
lang="markdown"
/>
</Container>
<span
className="editProblemView-settingsColumn"
>
<injectIntl(ShimmedIntlComponent)
problemType="multiplechoiceresponse"
/>
</span>
</div>
</EditorContainer>
`;
exports[`EditorProblemView component renders raw editor for advanced problem type 1`] = `
<EditorContainer
getContent={[Function]}
isDirty={[Function]}
returnFunction={null}
>
<AlertModal
footerNode={
<ActionRow>
<Button
onClick={[Function]}
variant="tertiary"
>
<FormattedMessage
defaultMessage="Cancel"
description="Label for cancel button in the save warning modal"
id="authoring.problemEditor.editProblemView.saveWarningModal.cancelButton.label"
/>
</Button>
<Button
onClick={[Function]}
>
<FormattedMessage
defaultMessage="Ok"
description="Label for save button in the save warning modal"
id="authoring.problemEditor.editProblemView.saveWarningModal.saveButton.label"
/>
</Button>
</ActionRow>
}
isOpen={false}
onClose={[Function]}
title="OLX settings discrepancy"
>
<FormattedMessage
defaultMessage="A discrepancy was found between the settings defined in the OLX's problem tag and the
settings selected in the sidebar. The settings defined in the OLX's problem tag will be saved and
corresponding values in the sidebar will be discarded."
description="Explanation in body of mismatched settings modal"
id="authoring.problemEditor.editProblemView.saveWarningModal.olxSettingDiscrepancy.body.explanation"
/>
</AlertModal>
<div
className="editProblemView d-flex flex-row flex-nowrap justify-content-end"
>
<Container
className="advancedEditorTopMargin p-0"
fluid={true}
>
<RawEditor
content="<problem>...</problem>"
editorRef={
{
"current": null,
}
}
lang="xml"
/>
</Container>
<span
className="editProblemView-settingsColumn"
>
<injectIntl(ShimmedIntlComponent)
problemType="advanced"
/>
</span>
</div>
</EditorContainer>
`;
exports[`EditorProblemView component renders simple view 1`] = `
<EditorContainer
getContent={[Function]}
isDirty={[Function]}
returnFunction={null}
>
<AlertModal
footerNode={
<ActionRow>
<Button
onClick={[Function]}
variant="tertiary"
>
<FormattedMessage
defaultMessage="Cancel"
description="Label for cancel button in the save warning modal"
id="authoring.problemEditor.editProblemView.saveWarningModal.cancelButton.label"
/>
</Button>
<Button
onClick={[Function]}
>
<FormattedMessage
defaultMessage="Ok"
description="Label for save button in the save warning modal"
id="authoring.problemEditor.editProblemView.saveWarningModal.saveButton.label"
/>
</Button>
</ActionRow>
}
isOpen={false}
onClose={[Function]}
title="No answer specified"
>
<Fragment>
<div>
<FormattedMessage
defaultMessage="Are you sure you want to exit the editor?"
description="Question in body of save warning modal"
id="authoring.problemEditor.editProblemView.saveWarningModal.body.question"
/>
</div>
<div>
<FormattedMessage
defaultMessage="No correct answer has been specified."
description="Explanation in body of no answer modal"
id="authoring.problemEditor.editProblemView.saveWarningModal.noAnswer.body.explanation"
/>
</div>
</Fragment>
</AlertModal>
<div
className="editProblemView d-flex flex-row flex-nowrap justify-content-end"
>
<span
className="flex-grow-1 mb-5"
>
<injectIntl(ShimmedIntlComponent) />
<injectIntl(ShimmedIntlComponent) />
<injectIntl(ShimmedIntlComponent)
problemType="multiplechoiceresponse"
/>
</span>
<span
className="editProblemView-settingsColumn"
>
<injectIntl(ShimmedIntlComponent)
problemType="multiplechoiceresponse"
/>
</span>
</div>
</EditorContainer>
`;

View File

@@ -1,7 +1,7 @@
import React, { useRef } from 'react';
import PropTypes from 'prop-types';
import { connect, useDispatch } from 'react-redux';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
import {
Container,
@@ -36,17 +36,15 @@ const EditProblemView = ({
returnUrl,
analytics,
isDirty,
// injected
intl,
}) => {
const intl = useIntl();
const dispatch = useDispatch();
const editorRef = useRef(null);
const isAdvancedProblemType = problemType === ProblemTypeKeys.ADVANCED;
const { isSaveWarningModalOpen, openSaveWarningModal, closeSaveWarningModal } = saveWarningModalToggle();
/* istanbul ignore next */
const checkIfDirty = () => {
if (isAdvancedProblemType && editorRef && editorRef?.current) {
/* istanbul ignore next */
return editorRef.current.observer?.lastChange !== 0;
}
return isDirty || checkIfEditorsDirty();
@@ -145,8 +143,6 @@ EditProblemView.propTypes = {
returnUrl: PropTypes.string.isRequired,
isDirty: PropTypes.bool,
isMarkdownEditorEnabled: PropTypes.bool,
// injected
intl: intlShape.isRequired,
};
export const mapStateToProps = (state) => ({
@@ -161,4 +157,4 @@ export const mapStateToProps = (state) => ({
});
export const EditProblemViewInternal = EditProblemView; // For testing only
export default injectIntl(connect(mapStateToProps)(EditProblemView));
export default connect(mapStateToProps)(EditProblemView);

View File

@@ -1,62 +1,147 @@
import 'CourseAuthoring/editors/setupEditorTest';
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import { EditProblemViewInternal as EditProblemView } from '.';
import { AnswerWidgetInternal as AnswerWidget } from './AnswerWidget';
import { Provider } from 'react-redux';
import {
render as baseRender, screen, fireEvent, initializeMocks,
} from '../../../../../testUtils';
import { EditProblemViewInternal, mapStateToProps } from './index';
import { ProblemTypeKeys } from '../../../../data/constants/problem';
import RawEditor from '../../../../sharedComponents/RawEditor';
import { formatMessage } from '../../../../testUtils';
import { EditorContextProvider } from '../../../../EditorContext';
import editorStore from '../../../../data/store';
import { selectors } from '../../../../data/redux';
describe('EditorProblemView component', () => {
test('renders simple view', () => {
const wrapper = shallow(<EditProblemView
problemType={ProblemTypeKeys.SINGLESELECT}
problemState={{}}
assets={{}}
intl={{ formatMessage }}
/>);
expect(wrapper.snapshot).toMatchSnapshot();
const { saveBlock } = require('../../../../hooks');
const { saveWarningModalToggle } = require('./hooks');
const AnswerWidgetComponent = wrapper.shallowWrapper.props.children[1].props.children[1].props.children;
expect(AnswerWidgetComponent.props.problemType).toBe(ProblemTypeKeys.SINGLESELECT);
expect(wrapper.instance.findByType(RawEditor).length).toBe(0);
jest.mock('./AnswerWidget', () => function mockAnswerWidget() {
return <div>AnswerWidget</div>;
});
jest.mock('./SettingsWidget', () => function mockSettingsWidget() {
return <div>SettingsWidget</div>;
});
jest.mock('./QuestionWidget', () => function mmockQuestionWidget() {
return <div>QuestionWidget</div>;
});
jest.mock('../../../EditorContainer', () => function mockEditorContainer({ children }) {
return <section>{children}</section>;
});
jest.mock('../../../../sharedComponents/RawEditor', () => function mockRawEditor({ lang, content }) {
return <div>{lang}:{content}</div>;
});
jest.mock('./ExplanationWidget', () => function mockExplanationWidget() {
return <div>ExplanationWidget</div>;
});
jest.mock('../../../../hooks', () => ({
saveBlock: jest.fn(),
}));
jest.mock('./hooks', () => ({
checkIfEditorsDirty: jest.fn(() => false),
parseState: jest.fn(() => () => 'parsed-content'),
saveWarningModalToggle: jest.fn(() => ({
isSaveWarningModalOpen: true,
openSaveWarningModal: jest.fn(),
closeSaveWarningModal: jest.fn(),
})),
getContent: jest.fn(() => 'content'),
}));
const render = (ui) => baseRender(ui, {
extraWrapper: ({ children }) => (
<EditorContextProvider learningContextId="course-v1:Org+COURSE+RUN">
<Provider store={editorStore}>
{children}
</Provider>
</EditorContextProvider>
),
});
describe('EditProblemView', () => {
const baseProps = {
problemType: 'standard',
isMarkdownEditorEnabled: false,
problemState: { rawOLX: '<problem></problem>', rawMarkdown: '## Problem' },
lmsEndpointUrl: null,
returnUrl: '/return',
analytics: {},
isDirty: false,
returnFunction: jest.fn(),
};
beforeEach(() => {
initializeMocks();
});
test('renders raw editor for advanced problem type', () => {
const wrapper = shallow(<EditProblemView
problemType={ProblemTypeKeys.ADVANCED}
isMarkdownEditorEnabled={false}
problemState={{ rawOLX: '<problem>...</problem>' }}
assets={{}}
intl={{ formatMessage }}
/>);
expect(wrapper.snapshot).toMatchSnapshot();
const rawEditor = wrapper.instance.findByType(RawEditor);
expect(rawEditor.length).toBe(1);
expect(rawEditor[0].props.lang).toBe('xml');
const answerWidget = wrapper.instance.findByType(AnswerWidget);
expect(answerWidget.length).toBe(0); // since advanced problem type skips AnswerWidget
it('renders standard problem widgets', () => {
render(<EditProblemViewInternal {...baseProps} />);
expect(screen.getByText('QuestionWidget')).toBeInTheDocument();
expect(screen.getByText('ExplanationWidget')).toBeInTheDocument();
expect(screen.getByText('AnswerWidget')).toBeInTheDocument();
expect(screen.getByText('SettingsWidget')).toBeInTheDocument();
expect(screen.queryByText(/xml:/)).not.toBeInTheDocument();
expect(screen.queryByText(/markdown:/)).not.toBeInTheDocument();
});
test('renders markdown editor when isMarkdownEditorEnabled is true', () => {
const wrapper = shallow(<EditProblemView
problemType={ProblemTypeKeys.SINGLESELECT}
isMarkdownEditorEnabled
problemState={{ rawMarkdown: '# Markdown content' }}
assets={{}}
intl={{ formatMessage }}
/>);
it('renders advanced problem with RawEditor', () => {
render(<EditProblemViewInternal {...baseProps} problemType={ProblemTypeKeys.ADVANCED} />);
expect(screen.getByText('xml:<problem></problem>')).toBeInTheDocument();
expect(screen.getByText('SettingsWidget')).toBeInTheDocument();
});
expect(wrapper.snapshot).toMatchSnapshot();
it('renders markdown editor with RawEditor', () => {
render(<EditProblemViewInternal {...baseProps} isMarkdownEditorEnabled />);
expect(screen.getByText('markdown:## Problem')).toBeInTheDocument();
});
const rawEditor = wrapper.instance.findByType(RawEditor);
expect(rawEditor.length).toBe(1);
expect(rawEditor[0].props.lang).toBe('markdown');
it('shows AlertModal with correct title/body for standard', () => {
render(<EditProblemViewInternal {...baseProps} />);
expect(screen.getAllByText('No correct answer has been specified.').length).toBeGreaterThan(0);
});
const answerWidget = wrapper.instance.findByType(AnswerWidget);
expect(answerWidget.length).toBe(0); // since markdown view skips AnswerWidget
it('calls saveBlock when save button is clicked', () => {
render(<EditProblemViewInternal {...baseProps} />);
const saveBtn = screen.getByRole('button', { name: 'Ok' });
fireEvent.click(saveBtn);
expect(saveBlock).toHaveBeenCalled();
});
it('calls closeSaveWarningModal when cancel button is clicked', () => {
const closeSaveWarningModal = jest.fn();
saveWarningModalToggle.mockReturnValue({
isSaveWarningModalOpen: true,
openSaveWarningModal: jest.fn(),
closeSaveWarningModal,
});
render(<EditProblemViewInternal {...baseProps} />);
const cancelBtn = screen.getByRole('button', { name: 'Cancel' });
fireEvent.click(cancelBtn);
expect(closeSaveWarningModal).toHaveBeenCalled();
});
it('sets isMarkdownEditorEnabled true only if both selectors return true', () => {
const state = { };
selectors.problem = {
isMarkdownEditorEnabled: jest.fn(() => true),
problemType: jest.fn(),
completeState: jest.fn(),
isDirty: jest.fn(),
};
selectors.app = {
isMarkdownEditorEnabledForCourse: jest.fn(() => true),
analytics: jest.fn(),
lmsEndpointUrl: jest.fn(),
returnUrl: jest.fn(),
};
const props = mapStateToProps(state);
expect(selectors.problem.isMarkdownEditorEnabled).toHaveBeenCalledWith(state);
expect(selectors.app.isMarkdownEditorEnabledForCourse).toHaveBeenCalledWith(state);
expect(props.isMarkdownEditorEnabled).toBe(true);
selectors.problem.isMarkdownEditorEnabled.mockReturnValue(false);
expect(mapStateToProps(state).isMarkdownEditorEnabled).toBe(false);
selectors.problem.isMarkdownEditorEnabled.mockReturnValue(true);
selectors.app.isMarkdownEditorEnabledForCourse.mockReturnValue(false);
expect(mapStateToProps(state).isMarkdownEditorEnabled).toBe(false);
});
});

View File

@@ -1,39 +1,42 @@
import 'CourseAuthoring/editors/setupEditorTest';
import { shallow } from '@edx/react-unit-test-utils';
import React from 'react';
import {
render, screen, fireEvent, initializeMocks,
} from '../../../../../../testUtils';
import { ProblemTypeKeys } from '../../../../../data/constants/problem';
import ProblemTypeSelect from './ProblemTypeSelect';
describe('ProblemTypeSelect', () => {
const props = {
setSelected: jest.fn(),
};
beforeEach(() => {
initializeMocks();
});
describe('snapshot', () => {
test('SINGLESELECT', () => {
expect(shallow(
<ProblemTypeSelect {...props} selected={ProblemTypeKeys.SINGLESELECT} />,
).snapshot).toMatchSnapshot();
});
test('MULTISELECT', () => {
expect(shallow(
<ProblemTypeSelect {...props} selected={ProblemTypeKeys.MULTISELECT} />,
).snapshot).toMatchSnapshot();
});
test('DROPDOWN', () => {
expect(shallow(
<ProblemTypeSelect {...props} selected={ProblemTypeKeys.DROPDOWN} />,
).snapshot).toMatchSnapshot();
});
test('NUMERIC', () => {
expect(shallow(
<ProblemTypeSelect {...props} selected={ProblemTypeKeys.NUMERIC} />,
).snapshot).toMatchSnapshot();
});
test('TEXTINPUT', () => {
expect(shallow(
<ProblemTypeSelect {...props} selected={ProblemTypeKeys.TEXTINPUT} />,
).snapshot).toMatchSnapshot();
});
it('renders the component with the selected element checked', () => {
render(<ProblemTypeSelect setSelected={jest.fn()} selected={ProblemTypeKeys.SINGLESELECT} />);
expect(screen.getByRole('radiogroup')).toBeInTheDocument();
const radioSingle = screen.getByDisplayValue('multiplechoiceresponse');
expect(radioSingle).toBeChecked();
});
it('does not render advanced element', () => {
render(<ProblemTypeSelect setSelected={jest.fn()} selected={ProblemTypeKeys.MULTISELECT} />);
expect(screen.getByRole('radiogroup')).toBeInTheDocument();
expect(screen.queryByText('advanced')).not.toBeInTheDocument();
});
it('should call setSelected with correct value when clicking one option', () => {
const mockSetSelected = jest.fn();
render(<ProblemTypeSelect setSelected={mockSetSelected} selected={ProblemTypeKeys.NUMERIC} />);
const multiSelectOption = screen.getByRole('button', { name: 'Multi-select' });
fireEvent.click(multiSelectOption);
expect(mockSetSelected).toHaveBeenCalledWith('choiceresponse');
});
it('should call setSelected with blankadvanced when clicking the advanced button', () => {
const mockSetSelected = jest.fn();
render(<ProblemTypeSelect setSelected={mockSetSelected} selected={ProblemTypeKeys.MULTISELECT} />);
const button = screen.getByRole('button', { name: 'Advanced problem types' });
expect(button).toBeInTheDocument();
fireEvent.click(button);
expect(mockSetSelected).toHaveBeenCalledWith('blankadvanced');
});
});

View File

@@ -1,531 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ProblemTypeSelect snapshot DROPDOWN 1`] = `
<Container
style={
{
"height": "400px",
"width": "494px",
}
}
>
<ForwardRef
columns={1}
name="problem-type"
onChange={[Function]}
type="radio"
value="optionresponse"
>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="multiplechoiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="multiplechoiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="multiplechoiceresponse"
>
Single select
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="choiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="choiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="choiceresponse"
>
Multi-select
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="optionresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="optionresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="optionresponse"
>
Dropdown
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="numericalresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="numericalresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="numericalresponse"
>
Numerical input
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="stringresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="stringresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="stringresponse"
>
Text input
</ForwardRef>
</ForwardRef>
<Button
className="pl-0 mt-2"
onClick={[Function]}
variant="link"
>
<FormattedMessage
defaultMessage="Advanced problem types"
description="Button label for advance problem types option"
id="authoring.problemEditor.problemSelect.advanceButton.label"
/>
</Button>
</Container>
`;
exports[`ProblemTypeSelect snapshot MULTISELECT 1`] = `
<Container
style={
{
"height": "400px",
"width": "494px",
}
}
>
<ForwardRef
columns={1}
name="problem-type"
onChange={[Function]}
type="radio"
value="choiceresponse"
>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="multiplechoiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="multiplechoiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="multiplechoiceresponse"
>
Single select
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="choiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="choiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="choiceresponse"
>
Multi-select
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="optionresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="optionresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="optionresponse"
>
Dropdown
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="numericalresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="numericalresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="numericalresponse"
>
Numerical input
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="stringresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="stringresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="stringresponse"
>
Text input
</ForwardRef>
</ForwardRef>
<Button
className="pl-0 mt-2"
onClick={[Function]}
variant="link"
>
<FormattedMessage
defaultMessage="Advanced problem types"
description="Button label for advance problem types option"
id="authoring.problemEditor.problemSelect.advanceButton.label"
/>
</Button>
</Container>
`;
exports[`ProblemTypeSelect snapshot NUMERIC 1`] = `
<Container
style={
{
"height": "400px",
"width": "494px",
}
}
>
<ForwardRef
columns={1}
name="problem-type"
onChange={[Function]}
type="radio"
value="numericalresponse"
>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="multiplechoiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="multiplechoiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="multiplechoiceresponse"
>
Single select
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="choiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="choiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="choiceresponse"
>
Multi-select
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="optionresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="optionresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="optionresponse"
>
Dropdown
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="numericalresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="numericalresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="numericalresponse"
>
Numerical input
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="stringresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="stringresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="stringresponse"
>
Text input
</ForwardRef>
</ForwardRef>
<Button
className="pl-0 mt-2"
onClick={[Function]}
variant="link"
>
<FormattedMessage
defaultMessage="Advanced problem types"
description="Button label for advance problem types option"
id="authoring.problemEditor.problemSelect.advanceButton.label"
/>
</Button>
</Container>
`;
exports[`ProblemTypeSelect snapshot SINGLESELECT 1`] = `
<Container
style={
{
"height": "400px",
"width": "494px",
}
}
>
<ForwardRef
columns={1}
name="problem-type"
onChange={[Function]}
type="radio"
value="multiplechoiceresponse"
>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="multiplechoiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="multiplechoiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="multiplechoiceresponse"
>
Single select
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="choiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="choiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="choiceresponse"
>
Multi-select
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="optionresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="optionresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="optionresponse"
>
Dropdown
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="numericalresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="numericalresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="numericalresponse"
>
Numerical input
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="stringresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="stringresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="stringresponse"
>
Text input
</ForwardRef>
</ForwardRef>
<Button
className="pl-0 mt-2"
onClick={[Function]}
variant="link"
>
<FormattedMessage
defaultMessage="Advanced problem types"
description="Button label for advance problem types option"
id="authoring.problemEditor.problemSelect.advanceButton.label"
/>
</Button>
</Container>
`;
exports[`ProblemTypeSelect snapshot TEXTINPUT 1`] = `
<Container
style={
{
"height": "400px",
"width": "494px",
}
}
>
<ForwardRef
columns={1}
name="problem-type"
onChange={[Function]}
type="radio"
value="stringresponse"
>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="multiplechoiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="multiplechoiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="multiplechoiceresponse"
>
Single select
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="choiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="choiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="choiceresponse"
>
Multi-select
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="optionresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="optionresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="optionresponse"
>
Dropdown
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="numericalresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="numericalresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="numericalresponse"
>
Numerical input
</ForwardRef>
<ForwardRef
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="stringresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="stringresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
value="stringresponse"
>
Text input
</ForwardRef>
</ForwardRef>
<Button
className="pl-0 mt-2"
onClick={[Function]}
variant="link"
>
<FormattedMessage
defaultMessage="Advanced problem types"
description="Button label for advance problem types option"
id="authoring.problemEditor.problemSelect.advanceButton.label"
/>
</Button>
</Container>
`;

View File

@@ -1,84 +0,0 @@
import 'CourseAuthoring/editors/setupEditorTest';
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import ErrorAlert, { hooks } from './ErrorAlert';
import { MockUseState } from '../../testUtils';
jest.mock('react', () => ({
...jest.requireActual('react'),
useRef: jest.fn(val => ({ current: val })),
useEffect: jest.fn(),
useCallback: (cb, prereqs) => ({ cb, prereqs }),
}));
const state = new MockUseState(hooks);
let hook;
const testValue = 'testVALUE';
describe('ErrorAlert component', () => {
describe('Hooks', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('state hooks', () => {
state.testGetter(state.keys.isDismissed);
});
describe('using state', () => {
beforeEach(() => { state.mock(); });
afterEach(() => { state.restore(); });
describe('dismissalHooks', () => {
const props = {
dismissError: jest.fn(),
isError: testValue,
};
beforeEach(() => {
hook = hooks.dismissalHooks(props);
});
it('returns isDismissed value, initialized to false', () => {
expect(state.stateVals.isDismissed).toEqual(hook.isDismissed);
});
test('dismissAlert sets isDismissed to true and calls dismissError', () => {
hook.dismissAlert();
expect(state.setState.isDismissed).toHaveBeenCalledWith(true);
expect(props.dismissError).toHaveBeenCalled();
});
test('On Render, calls setIsDismissed', () => {
expect(React.useEffect.mock.calls.length).toEqual(1);
const [cb, prereqs] = React.useEffect.mock.calls[0];
expect(prereqs[0]).toEqual(testValue);
cb();
expect(state.setState.isDismissed).toHaveBeenCalledWith(state.stateVals.isDismissed && !testValue);
});
});
});
});
describe('Component', () => {
describe('Snapshots', () => {
let props;
const msg = <p> An Error Message </p>;
beforeAll(() => {
props = {
dismissError: jest.fn(),
};
jest.spyOn(hooks, 'dismissalHooks').mockImplementation(() => ({
isDismissed: false,
dismissAlert: jest.fn().mockName('dismissAlert'),
}));
});
afterAll(() => {
jest.clearAllMocks();
});
test('snapshot: is Null when no error (ErrorAlert)', () => {
expect(shallow(<ErrorAlert {...props}> <p> An Error Message </p> </ErrorAlert>).snapshot).toMatchSnapshot();
});
test('snapshot: Loads children and component when error (ErrorAlert)', () => {
expect(
shallow(<ErrorAlert {...props} isError hideHeading={false}>{msg}</ErrorAlert>).snapshot,
).toMatchSnapshot();
});
test('snapshot: Does not load heading when hideHeading is true', () => {
expect(shallow(<ErrorAlert {...props} isError hideHeading>{msg}</ErrorAlert>).snapshot).toMatchSnapshot();
});
});
});
});

View File

@@ -0,0 +1,84 @@
import React from 'react';
import {
render, screen, fireEvent, initializeMocks,
} from '../../../testUtils';
import ErrorAlert, { hooks } from './ErrorAlert';
describe('ErrorAlert (integration, no Paragon mocks)', () => {
beforeEach(() => {
initializeMocks();
});
afterEach(() => {
jest.restoreAllMocks();
});
it('renders nothing if isError is false', () => {
render(<ErrorAlert isError={false}>Some error</ErrorAlert>);
expect(screen.queryByRole('alert')).toBeNull();
});
it('renders nothing if isDismissed is true', () => {
jest.spyOn(hooks, 'dismissalHooks').mockReturnValue({
isDismissed: true,
dismissAlert: jest.fn(),
});
render(<ErrorAlert isError>Some error</ErrorAlert>);
expect(screen.queryByRole('alert')).toBeNull();
});
it('renders alert with heading and children when isError is true', () => {
jest.spyOn(hooks, 'dismissalHooks').mockReturnValue({
isDismissed: false,
dismissAlert: jest.fn(),
});
render(<ErrorAlert isError>Some error</ErrorAlert>);
expect(screen.getByRole('alert')).toBeInTheDocument();
expect(screen.getByText('Error')).toBeInTheDocument();
expect(screen.getByText('Some error')).toBeInTheDocument();
});
it('renders alert without heading when hideHeading is true', () => {
render(<ErrorAlert isError hideHeading>Some error</ErrorAlert>);
expect(screen.getByRole('alert')).toBeInTheDocument();
expect(screen.queryByText('Error')).toBeNull();
expect(screen.getByText('Some error')).toBeInTheDocument();
});
it('calls dismissError when dismiss button is clicked', () => {
const dismissError = jest.fn();
render(<ErrorAlert isError dismissError={dismissError}>Some error</ErrorAlert>);
const closeBtn = screen.getByRole('button');
fireEvent.click(closeBtn);
expect(dismissError).toHaveBeenCalled();
});
it('does not throw if dismissError is not provided and dismiss button is clicked', () => {
render(<ErrorAlert isError>Some error</ErrorAlert>);
const closeBtn = screen.getByRole('button');
expect(() => fireEvent.click(closeBtn)).not.toThrow();
});
it('renders children as array', () => {
render(<ErrorAlert isError>{['foo', <span key="bar">bar</span>]}</ErrorAlert>);
expect(screen.getByText('foo')).toBeInTheDocument();
expect(screen.getByText('bar')).toBeInTheDocument();
});
it('resets isDismissed when isError changes from false to true', () => {
const { rerender } = render(<ErrorAlert isError={false}>err</ErrorAlert>);
expect(screen.queryByRole('alert')).toBeNull();
rerender(<ErrorAlert isError>err</ErrorAlert>);
expect(screen.getByRole('alert')).toBeInTheDocument();
});
it('dismisses alert when dismiss button is clicked (integration)', () => {
const dismissError = jest.fn();
render(<ErrorAlert isError dismissError={dismissError}>err</ErrorAlert>);
const closeBtn = screen.getByRole('button');
fireEvent.click(closeBtn);
expect(screen.queryByRole('alert')).toBeNull();
expect(dismissError).toHaveBeenCalled();
});
});

View File

@@ -1,20 +0,0 @@
import 'CourseAuthoring/editors/setupEditorTest';
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import FetchErrorAlert from './FetchErrorAlert';
jest.mock('../../data/redux', () => ({
selectors: {
requests: {
isFailed: jest.fn((state, params) => ({ isFailed: { state, params } })),
},
},
}));
describe('FetchErrorAlert', () => {
describe('Snapshots', () => {
test('snapshot: is ErrorAlert with Message error (ErrorAlert)', () => {
expect(shallow(<FetchErrorAlert isFetchError />).snapshot).toMatchSnapshot();
});
});
});

View File

@@ -0,0 +1,41 @@
import React from 'react';
import { render, screen, initializeMocks } from '../../../testUtils';
import FetchErrorAlert from './FetchErrorAlert';
const message = {
id: 'test.error',
defaultMessage: 'Something went wrong!',
description: 'Test error message',
};
describe('FetchErrorAlert', () => {
beforeEach(() => {
initializeMocks();
});
it('renders the error message when isFetchError is true', () => {
render(
<FetchErrorAlert message={message} isFetchError />,
);
expect(screen.getByText('Something went wrong!')).toBeInTheDocument();
});
it('does not render the error message when isFetchError is false', () => {
render(
<FetchErrorAlert message={message} isFetchError={false} />,
);
expect(screen.queryByText('Something went wrong!')).not.toBeInTheDocument();
});
it('renders error with a custom message', () => {
const customMessage = {
id: 'another.error',
defaultMessage: 'Another error occurred.',
description: 'Another error',
};
render(
<FetchErrorAlert message={customMessage} isFetchError />,
);
expect(screen.getByText('Another error occurred.')).toBeInTheDocument();
});
});

View File

@@ -1,34 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ErrorAlert component Component Snapshots snapshot: Does not load heading when hideHeading is true 1`] = `
<Alert
dismissible={true}
onClose={[MockFunction dismissAlert]}
variant="danger"
>
<p>
An Error Message
</p>
</Alert>
`;
exports[`ErrorAlert component Component Snapshots snapshot: Loads children and component when error (ErrorAlert) 1`] = `
<Alert
dismissible={true}
onClose={[MockFunction dismissAlert]}
variant="danger"
>
<Alert.Heading>
<FormattedMessage
defaultMessage="Error"
description="Title of message presented to user when something goes wrong"
id="authoring.texteditor.selectimagemodal.error.errorTitle"
/>
</Alert.Heading>
<p>
An Error Message
</p>
</Alert>
`;
exports[`ErrorAlert component Component Snapshots snapshot: is Null when no error (ErrorAlert) 1`] = `null`;

View File

@@ -1,11 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FetchErrorAlert Snapshots snapshot: is ErrorAlert with Message error (ErrorAlert) 1`] = `
<ErrorAlert
dismissError={null}
hideHeading={false}
isError={true}
>
<FormattedMessage />
</ErrorAlert>
`;

View File

@@ -1,26 +0,0 @@
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import VideoSelectorContainer from './VideoSelectorContainer';
jest.mock('../editors/VideoSelectorPage', () => ({
default: function VideoSelectorPage() { return 'HeaderTitle'; },
__esModule: true, // Required to mock a default export
}));
jest.mock('react-router', () => ({
...jest.requireActual('react-router'), // use actual for all non-hook parts
useParams: () => ({
blockId: 'company-id1',
blockType: 'html',
}),
}));
const props = { courseId: 'cOuRsEId' };
describe('Video Selector Container', () => {
describe('snapshots', () => {
test('rendering correctly with expected Input', () => {
expect(shallow(<VideoSelectorContainer {...props} />).snapshot).toMatchSnapshot();
});
});
});

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { render, initializeMocks } from '../testUtils';
import VideoSelectorContainer from './VideoSelectorContainer';
describe('VideoSelectorContainer', () => {
beforeEach(() => {
initializeMocks();
});
it('renders the wrapper div with correct class', () => {
const { container } = render(<VideoSelectorContainer courseId="course-v1:edX+Test+2024" />);
expect(container.querySelector('.selector-page')).toBeInTheDocument();
});
});

View File

@@ -1,14 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Video Selector Container snapshots rendering correctly with expected Input 1`] = `
<div
className="selector-page"
>
<VideoSelectorPage
blockId="company-id1"
courseId="cOuRsEId"
lmsEndpointUrl="http://localhost:18000"
studioEndpointUrl="http://localhost:18010"
/>
</div>
`;