test: replacing snapshot tests with RTL tests part 4 (#2135)
* test: replacing snapshot tests with rtl tests part 4 * test: removing not needed icon mocks, and changing name to render function for editors
This commit is contained in:
committed by
GitHub
parent
4a1f454855
commit
ca85ca8e4b
@@ -1,22 +1,9 @@
|
||||
import React from 'react';
|
||||
import * as reactRedux from 'react-redux';
|
||||
import { Provider } from 'react-redux';
|
||||
import * as hooks from './hooks';
|
||||
import VideoSelector from './VideoSelector';
|
||||
import { render as baseRender, initializeMocks, screen } from '../testUtils';
|
||||
|
||||
import { EditorContextProvider } from './EditorContext';
|
||||
import editorStore from './data/store';
|
||||
|
||||
const render = (ui) => baseRender(ui, {
|
||||
extraWrapper: ({ children }) => (
|
||||
<EditorContextProvider learningContextId="course-v1:Org+COURSE+RUN">
|
||||
<Provider store={editorStore}>
|
||||
{children}
|
||||
</Provider>
|
||||
</EditorContextProvider>
|
||||
),
|
||||
});
|
||||
import editorRender from './editorTestRender';
|
||||
import { initializeMocks, screen } from '../testUtils';
|
||||
|
||||
const defaultProps = {
|
||||
blockId: 'block-v1:edX+DemoX+Demo_Course+type@html+block@030e35c4756a4ddc8d40b95fbbfff4d4',
|
||||
@@ -32,7 +19,7 @@ describe('VideoSelector', () => {
|
||||
|
||||
test('renders VideoGallery when loading is false', () => {
|
||||
jest.spyOn(hooks, 'useInitializeApp').mockReturnValue(false);
|
||||
render(<VideoSelector {...defaultProps} />);
|
||||
editorRender(<VideoSelector {...defaultProps} />);
|
||||
expect(screen.getByText('Add video to your course')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -40,7 +27,7 @@ describe('VideoSelector', () => {
|
||||
// "testing the application components in the way the user would use it"
|
||||
test('renders nothing when loading is true', () => {
|
||||
jest.spyOn(hooks, 'useInitializeApp').mockReturnValue(true);
|
||||
render(<VideoSelector {...defaultProps} />);
|
||||
editorRender(<VideoSelector {...defaultProps} />);
|
||||
expect(screen.queryByText('Add video to your course')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -54,7 +41,7 @@ describe('VideoSelector', () => {
|
||||
const mockDispatch = jest.fn();
|
||||
jest.spyOn(reactRedux, 'useDispatch').mockReturnValue(mockDispatch);
|
||||
jest.spyOn(hooks, 'useInitializeApp');
|
||||
render(<VideoSelector {...defaultProps} />);
|
||||
editorRender(<VideoSelector {...defaultProps} />);
|
||||
expect(hooks.useInitializeApp).toHaveBeenCalledWith({
|
||||
dispatch: mockDispatch,
|
||||
data: initData,
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import 'CourseAuthoring/editors/setupEditorTest';
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import { act, render, waitFor } from '@testing-library/react';
|
||||
|
||||
import { actions, selectors } from '../../../../../data/redux';
|
||||
|
||||
import { AnswersContainerInternal as AnswersContainer, mapStateToProps, mapDispatchToProps } from './AnswersContainer';
|
||||
import { ProblemTypeKeys } from '../../../../../data/constants/problem';
|
||||
|
||||
jest.mock('@edx/frontend-platform/i18n', () => ({
|
||||
FormattedMessage: ({ defaultMessage }) => (<p>{defaultMessage}</p>),
|
||||
defineMessages: m => m,
|
||||
injectIntl: (args) => args,
|
||||
intlShape: {},
|
||||
getLocale: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('./AnswerOption', () => function mockAnswerOption() {
|
||||
return <div>MockAnswerOption</div>;
|
||||
});
|
||||
|
||||
jest.mock('../../../../../data/redux', () => ({
|
||||
actions: {
|
||||
problem: {
|
||||
updateField: jest.fn().mockName('actions.problem.updateField'),
|
||||
addAnswer: jest.fn().mockName('actions.problem.addAnswer'),
|
||||
},
|
||||
},
|
||||
selectors: {
|
||||
problem: {
|
||||
answers: jest.fn(state => ({ answers: state })),
|
||||
},
|
||||
},
|
||||
}));
|
||||
describe('AnswersContainer', () => {
|
||||
const props = {
|
||||
answers: [],
|
||||
updateField: jest.fn(),
|
||||
addAnswer: jest.fn(),
|
||||
};
|
||||
describe('render', () => {
|
||||
test('snapshot: renders correct default', () => {
|
||||
act(() => {
|
||||
expect(shallow(<AnswersContainer {...props} />).snapshot).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
test('snapshot: renders correctly with answers', () => {
|
||||
act(() => {
|
||||
expect(shallow(
|
||||
<AnswersContainer
|
||||
{...props}
|
||||
answers={[{ id: 'a', title: 'sOMetITlE', correct: true }, { id: 'b', title: 'sOMetITlE', correct: true }]}
|
||||
/>,
|
||||
).snapshot).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
test('snapshot: numeric problems: answer range/answer select button: empty', () => {
|
||||
act(() => {
|
||||
const emptyAnswerProps = {
|
||||
problemType: ProblemTypeKeys.NUMERIC,
|
||||
answers: [],
|
||||
updateField: jest.fn(),
|
||||
addAnswer: jest.fn(),
|
||||
addAnswerRange: jest.fn(),
|
||||
};
|
||||
expect(shallow(
|
||||
<AnswersContainer
|
||||
{...emptyAnswerProps}
|
||||
/>,
|
||||
).snapshot).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
test('snapshot: numeric problems: answer range/answer select button: Range disables the additon of more adds', () => {
|
||||
act(() => {
|
||||
const answerRangeProps = {
|
||||
problemType: ProblemTypeKeys.NUMERIC,
|
||||
answers: [{
|
||||
id: 'A',
|
||||
title: 'Answer 1',
|
||||
correct: true,
|
||||
selectedFeedback: 'selected feedback',
|
||||
unselectedFeedback: 'unselected feedback',
|
||||
isAnswerRange: true,
|
||||
}],
|
||||
updateField: jest.fn(),
|
||||
addAnswer: jest.fn(),
|
||||
addAnswerRange: jest.fn(),
|
||||
};
|
||||
expect(shallow(
|
||||
<AnswersContainer
|
||||
{...answerRangeProps}
|
||||
/>,
|
||||
).snapshot).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
test('snapshot: numeric problems: answer range/answer select button: multiple answers disables range.', () => {
|
||||
act(() => {
|
||||
const answersProps = {
|
||||
problemType: ProblemTypeKeys.NUMERIC,
|
||||
answers: [{
|
||||
id: 'A',
|
||||
title: 'Answer 1',
|
||||
correct: true,
|
||||
selectedFeedback: 'selected feedback',
|
||||
unselectedFeedback: 'unselected feedback',
|
||||
isAnswerRange: false,
|
||||
},
|
||||
{
|
||||
id: 'B',
|
||||
title: 'Answer 1',
|
||||
correct: true,
|
||||
selectedFeedback: 'selected feedback',
|
||||
unselectedFeedback: 'unselected feedback',
|
||||
isAnswerRange: false,
|
||||
},
|
||||
],
|
||||
updateField: jest.fn(),
|
||||
addAnswer: jest.fn(),
|
||||
addAnswerRange: jest.fn(),
|
||||
};
|
||||
expect(shallow(
|
||||
<AnswersContainer
|
||||
{...answersProps}
|
||||
/>,
|
||||
).snapshot).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
test('useAnswerContainer', async () => {
|
||||
let container = null;
|
||||
await act(async () => {
|
||||
const wrapper = render(
|
||||
<AnswersContainer
|
||||
{...props}
|
||||
answers={[{ id: 'a', title: 'sOMetITlE', correct: true }, { id: 'b', title: 'sOMetITlE', correct: true }]}
|
||||
/>,
|
||||
);
|
||||
container = wrapper.container;
|
||||
});
|
||||
|
||||
await waitFor(() => expect(container.querySelector('button')).toBeTruthy());
|
||||
await new Promise(resolve => { setTimeout(resolve, 500); });
|
||||
|
||||
expect(props.updateField).toHaveBeenCalledWith(expect.objectContaining({ correctAnswerCount: 2 }));
|
||||
});
|
||||
});
|
||||
describe('mapStateToProps', () => {
|
||||
const testState = { A: 'pple', B: 'anana', C: 'ucumber' };
|
||||
test('answers from problem.answers', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).answers,
|
||||
).toEqual(selectors.problem.answers(testState));
|
||||
});
|
||||
});
|
||||
describe('mapDispatchToProps', () => {
|
||||
test('updateField from actions.problem.updateField', () => {
|
||||
expect(mapDispatchToProps.updateField).toEqual(actions.problem.updateField);
|
||||
});
|
||||
test('updateField from actions.problem.addAnswer', () => {
|
||||
expect(mapDispatchToProps.addAnswer).toEqual(actions.problem.addAnswer);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
render, screen, fireEvent, initializeMocks,
|
||||
} from '../../../../../../testUtils';
|
||||
import { AnswersContainerInternal as AnswersContainer } from './AnswersContainer';
|
||||
import { ProblemTypeKeys } from '../../../../../data/constants/problem';
|
||||
|
||||
const { useAnswerContainer } = require('./hooks');
|
||||
|
||||
jest.mock('./AnswerOption', () => jest.fn(({ answer }) => <div>AnswerOption-{answer.id}</div>));
|
||||
jest.mock(
|
||||
'../../../../../sharedComponents/Button',
|
||||
() => jest.fn(({ children, ...props }) => <button type="button" {...props}>{children}</button>),
|
||||
);
|
||||
|
||||
jest.mock('./hooks', () => ({
|
||||
useAnswerContainer: jest.fn(),
|
||||
isSingleAnswerProblem: jest.fn(() => false),
|
||||
}));
|
||||
|
||||
describe('AnswersContainer', () => {
|
||||
const defaultProps = {
|
||||
problemType: 'multiple_choice',
|
||||
answers: [
|
||||
{ id: 'a1', isAnswerRange: false },
|
||||
{ id: 'a2', isAnswerRange: false },
|
||||
],
|
||||
addAnswer: jest.fn(),
|
||||
addAnswerRange: jest.fn(),
|
||||
updateField: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
initializeMocks();
|
||||
});
|
||||
|
||||
it('renders AnswerOption for each answer', () => {
|
||||
render(<AnswersContainer {...defaultProps} />);
|
||||
expect(screen.getByText('AnswerOption-a1')).toBeInTheDocument();
|
||||
expect(screen.getByText('AnswerOption-a2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders add answer Button for non-NUMERIC problemType and calls addAnswer on click', () => {
|
||||
render(<AnswersContainer {...defaultProps} />);
|
||||
const button = screen.getByRole('button', { name: 'Add answer' });
|
||||
expect(button).toBeInTheDocument();
|
||||
fireEvent.click(button);
|
||||
expect(defaultProps.addAnswer).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders Dropdown for NUMERIC problemType', () => {
|
||||
render(<AnswersContainer {...defaultProps} problemType={ProblemTypeKeys.NUMERIC} />);
|
||||
expect(screen.getByRole('button', { name: 'Add answer' })).toBeInTheDocument();
|
||||
expect(screen.getByText('Add answer').closest('.dropdown')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls useAnswerContainer with correct args', () => {
|
||||
render(<AnswersContainer {...defaultProps} />);
|
||||
expect(useAnswerContainer).toHaveBeenCalledWith({
|
||||
answers: defaultProps.answers,
|
||||
problemType: defaultProps.problemType,
|
||||
updateField: defaultProps.updateField,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,238 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AnswersContainer render snapshot: numeric problems: answer range/answer select button: Range disables the additon of more adds 1`] = `
|
||||
<div
|
||||
className="answers-container border border-light-700 rounded py-4 pl-4 pr-3"
|
||||
>
|
||||
<mockAnswerOption
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"isAnswerRange": true,
|
||||
"selectedFeedback": "selected feedback",
|
||||
"title": "Answer 1",
|
||||
"unselectedFeedback": "unselected feedback",
|
||||
}
|
||||
}
|
||||
hasSingleAnswer={false}
|
||||
key="A"
|
||||
/>
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle
|
||||
className="pl-0"
|
||||
id="Add-Answer-Or-Answer-Range"
|
||||
variant="tertiary"
|
||||
>
|
||||
<Icon />
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer"
|
||||
description="Button text to add answer"
|
||||
id="authoring.answerwidget.answer.addAnswerButton"
|
||||
/>
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item
|
||||
className="AddAnswerRange disabled"
|
||||
key="add-answer"
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer"
|
||||
description="Button text to add answer"
|
||||
id="authoring.answerwidget.answer.addAnswerButton"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
className="AddAnswerRange disabled"
|
||||
key="add-answer-range"
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer range"
|
||||
description="Button text to add a range of answers"
|
||||
id="authoring.answerwidget.answer.addAnswerRangeButton"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`AnswersContainer render snapshot: numeric problems: answer range/answer select button: empty 1`] = `
|
||||
<div
|
||||
className="answers-container border border-light-700 rounded py-4 pl-4 pr-3"
|
||||
>
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle
|
||||
className="pl-0"
|
||||
id="Add-Answer-Or-Answer-Range"
|
||||
variant="tertiary"
|
||||
>
|
||||
<Icon />
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer"
|
||||
description="Button text to add answer"
|
||||
id="authoring.answerwidget.answer.addAnswerButton"
|
||||
/>
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item
|
||||
className="AddAnswerRange "
|
||||
key="add-answer"
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer"
|
||||
description="Button text to add answer"
|
||||
id="authoring.answerwidget.answer.addAnswerButton"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
className="AddAnswerRange "
|
||||
key="add-answer-range"
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer range"
|
||||
description="Button text to add a range of answers"
|
||||
id="authoring.answerwidget.answer.addAnswerRangeButton"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`AnswersContainer render snapshot: numeric problems: answer range/answer select button: multiple answers disables range. 1`] = `
|
||||
<div
|
||||
className="answers-container border border-light-700 rounded py-4 pl-4 pr-3"
|
||||
>
|
||||
<mockAnswerOption
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"isAnswerRange": false,
|
||||
"selectedFeedback": "selected feedback",
|
||||
"title": "Answer 1",
|
||||
"unselectedFeedback": "unselected feedback",
|
||||
}
|
||||
}
|
||||
hasSingleAnswer={false}
|
||||
key="A"
|
||||
/>
|
||||
<mockAnswerOption
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "B",
|
||||
"isAnswerRange": false,
|
||||
"selectedFeedback": "selected feedback",
|
||||
"title": "Answer 1",
|
||||
"unselectedFeedback": "unselected feedback",
|
||||
}
|
||||
}
|
||||
hasSingleAnswer={false}
|
||||
key="B"
|
||||
/>
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle
|
||||
className="pl-0"
|
||||
id="Add-Answer-Or-Answer-Range"
|
||||
variant="tertiary"
|
||||
>
|
||||
<Icon />
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer"
|
||||
description="Button text to add answer"
|
||||
id="authoring.answerwidget.answer.addAnswerButton"
|
||||
/>
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item
|
||||
className="AddAnswerRange "
|
||||
key="add-answer"
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer"
|
||||
description="Button text to add answer"
|
||||
id="authoring.answerwidget.answer.addAnswerButton"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
className="AddAnswerRange disabled"
|
||||
key="add-answer-range"
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer range"
|
||||
description="Button text to add a range of answers"
|
||||
id="authoring.answerwidget.answer.addAnswerRangeButton"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`AnswersContainer render snapshot: renders correct default 1`] = `
|
||||
<div
|
||||
className="answers-container border border-light-700 rounded py-4 pl-4 pr-3"
|
||||
>
|
||||
<Button
|
||||
className={null}
|
||||
onClick={[MockFunction]}
|
||||
text={null}
|
||||
variant="add"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer"
|
||||
description="Button text to add answer"
|
||||
id="authoring.answerwidget.answer.addAnswerButton"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`AnswersContainer render snapshot: renders correctly with answers 1`] = `
|
||||
<div
|
||||
className="answers-container border border-light-700 rounded py-4 pl-4 pr-3"
|
||||
>
|
||||
<mockAnswerOption
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "a",
|
||||
"title": "sOMetITlE",
|
||||
}
|
||||
}
|
||||
hasSingleAnswer={false}
|
||||
key="a"
|
||||
/>
|
||||
<mockAnswerOption
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "b",
|
||||
"title": "sOMetITlE",
|
||||
}
|
||||
}
|
||||
hasSingleAnswer={false}
|
||||
key="b"
|
||||
/>
|
||||
<Button
|
||||
className={null}
|
||||
onClick={[MockFunction]}
|
||||
text={null}
|
||||
variant="add"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer"
|
||||
description="Button text to add answer"
|
||||
id="authoring.answerwidget.answer.addAnswerButton"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,60 +0,0 @@
|
||||
import 'CourseAuthoring/editors/setupEditorTest';
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import TypeRow from './TypeRow';
|
||||
import { typeRowHooks } from '../hooks';
|
||||
|
||||
jest.mock('../hooks', () => ({
|
||||
typeRowHooks: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('TypeRow', () => {
|
||||
const typeKey = 'TEXTINPUT';
|
||||
const props = {
|
||||
answers: [],
|
||||
blockTitle: 'bLoCkTiTLE',
|
||||
correctAnswerCount: 0,
|
||||
typeKey,
|
||||
label: 'Text Input Problem',
|
||||
selected: true,
|
||||
lastRow: false,
|
||||
problemType: 'prOBlEMtyPE',
|
||||
setBlockTitle: jest.fn().mockName('args.setBlockTitle'),
|
||||
updateField: jest.fn().mockName('args.updateField'),
|
||||
updateAnswer: jest.fn().mockName('args.updateAnswer'),
|
||||
};
|
||||
|
||||
const typeRowHooksProps = {
|
||||
onClick: jest.fn().mockName('typeRowHooks.onClick'),
|
||||
};
|
||||
|
||||
typeRowHooks.mockReturnValue(typeRowHooksProps);
|
||||
|
||||
describe('behavior', () => {
|
||||
it(' calls typeRowHooks when initialized', () => {
|
||||
shallow(<TypeRow {...props} />);
|
||||
expect(typeRowHooks).toHaveBeenCalledWith({
|
||||
answers: props.answers,
|
||||
blockTitle: props.blockTitle,
|
||||
correctAnswerCount: props.correctAnswerCount,
|
||||
problemType: props.problemType,
|
||||
typeKey,
|
||||
setBlockTitle: props.setBlockTitle,
|
||||
updateField: props.updateField,
|
||||
updateAnswer: props.updateAnswer,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('snapshot', () => {
|
||||
test('snapshot: renders type row setting card', () => {
|
||||
expect(shallow(<TypeRow {...props} />).snapshot).toMatchSnapshot();
|
||||
});
|
||||
test('snapshot: renders type row setting card not selected', () => {
|
||||
expect(shallow(<TypeRow {...props} selected={false} />).snapshot).toMatchSnapshot();
|
||||
});
|
||||
test('snapshot: renders type row setting card last row', () => {
|
||||
expect(shallow(<TypeRow {...props} lastRow />).snapshot).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
render, screen, fireEvent, initializeMocks,
|
||||
} from '../../../../../../../testUtils';
|
||||
import TypeRow from './TypeRow';
|
||||
|
||||
const mockOnClick = jest.fn();
|
||||
jest.mock('../hooks', () => ({
|
||||
typeRowHooks: () => ({ onClick: mockOnClick }),
|
||||
}));
|
||||
|
||||
const defaultProps = {
|
||||
answers: [
|
||||
{
|
||||
correct: true, id: '1', selectedFeedback: 'Good', title: 'A', unselectedFeedback: 'Try again',
|
||||
},
|
||||
{
|
||||
correct: false, id: '2', selectedFeedback: 'No', title: 'B', unselectedFeedback: 'Nope',
|
||||
},
|
||||
],
|
||||
blockTitle: 'Block Title',
|
||||
correctAnswerCount: 1,
|
||||
typeKey: 'multiple_choice',
|
||||
label: 'Multiple Choice',
|
||||
selected: false,
|
||||
lastRow: false,
|
||||
problemType: 'choice',
|
||||
setBlockTitle: jest.fn(),
|
||||
updateField: jest.fn(),
|
||||
updateAnswer: jest.fn(),
|
||||
};
|
||||
|
||||
describe('TypeRow Component', () => {
|
||||
beforeEach(() => {
|
||||
initializeMocks();
|
||||
});
|
||||
|
||||
test('renders label and Check icon when not selected', () => {
|
||||
const { container } = render(<TypeRow {...defaultProps} />);
|
||||
expect(screen.getByText('Multiple Choice')).toBeInTheDocument();
|
||||
const icon = container.querySelector('svg');
|
||||
expect(icon).toBeInTheDocument();
|
||||
expect(icon?.parentElement).toHaveClass('text-success');
|
||||
});
|
||||
|
||||
test('calls onClick from typeRowHooks when Button is clicked', () => {
|
||||
render(<TypeRow {...defaultProps} />);
|
||||
const button = screen.getByRole('button');
|
||||
fireEvent.click(button);
|
||||
expect(mockOnClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('renders with minimal valid props', () => {
|
||||
const minimalProps = {
|
||||
...defaultProps,
|
||||
answers: [],
|
||||
blockTitle: '',
|
||||
correctAnswerCount: 0,
|
||||
typeKey: 'short_answer',
|
||||
label: 'Short Answer',
|
||||
selected: false,
|
||||
lastRow: false,
|
||||
problemType: 'short',
|
||||
};
|
||||
render(<TypeRow {...minimalProps} />);
|
||||
expect(screen.getByText('Short Answer')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,82 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TypeRow snapshot snapshot: renders type row setting card 1`] = `
|
||||
<Fragment>
|
||||
<Button
|
||||
className="d-flex p-0 flex-row justify-content-between w-100"
|
||||
onClick={[MockFunction typeRowHooks.onClick]}
|
||||
text={null}
|
||||
variant="default"
|
||||
>
|
||||
<span
|
||||
className="small text-primary-500"
|
||||
>
|
||||
Text Input Problem
|
||||
</span>
|
||||
<span
|
||||
hidden={true}
|
||||
>
|
||||
<Icon
|
||||
className="text-success"
|
||||
/>
|
||||
</span>
|
||||
</Button>
|
||||
<hr
|
||||
className="d-block"
|
||||
/>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
exports[`TypeRow snapshot snapshot: renders type row setting card last row 1`] = `
|
||||
<Fragment>
|
||||
<Button
|
||||
className="d-flex p-0 flex-row justify-content-between w-100"
|
||||
onClick={[MockFunction typeRowHooks.onClick]}
|
||||
text={null}
|
||||
variant="default"
|
||||
>
|
||||
<span
|
||||
className="small text-primary-500"
|
||||
>
|
||||
Text Input Problem
|
||||
</span>
|
||||
<span
|
||||
hidden={true}
|
||||
>
|
||||
<Icon
|
||||
className="text-success"
|
||||
/>
|
||||
</span>
|
||||
</Button>
|
||||
<hr
|
||||
className="d-none"
|
||||
/>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
exports[`TypeRow snapshot snapshot: renders type row setting card not selected 1`] = `
|
||||
<Fragment>
|
||||
<Button
|
||||
className="d-flex p-0 flex-row justify-content-between w-100"
|
||||
onClick={[MockFunction typeRowHooks.onClick]}
|
||||
text={null}
|
||||
variant="default"
|
||||
>
|
||||
<span
|
||||
className="small text-primary-500"
|
||||
>
|
||||
Text Input Problem
|
||||
</span>
|
||||
<span
|
||||
hidden={false}
|
||||
>
|
||||
<Icon
|
||||
className="text-success"
|
||||
/>
|
||||
</span>
|
||||
</Button>
|
||||
<hr
|
||||
className="d-block"
|
||||
/>
|
||||
</Fragment>
|
||||
`;
|
||||
@@ -1,12 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import {
|
||||
render as baseRender, screen, fireEvent, initializeMocks,
|
||||
} from '../../../../../testUtils';
|
||||
import { screen, fireEvent, initializeMocks } from '../../../../../testUtils';
|
||||
import editorRender from '../../../../editorTestRender';
|
||||
import { EditProblemViewInternal, mapStateToProps } from './index';
|
||||
import { ProblemTypeKeys } from '../../../../data/constants/problem';
|
||||
import { EditorContextProvider } from '../../../../EditorContext';
|
||||
import editorStore from '../../../../data/store';
|
||||
import { selectors } from '../../../../data/redux';
|
||||
|
||||
const { saveBlock } = require('../../../../hooks');
|
||||
@@ -44,16 +40,6 @@ jest.mock('./hooks', () => ({
|
||||
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',
|
||||
@@ -71,7 +57,7 @@ describe('EditProblemView', () => {
|
||||
});
|
||||
|
||||
it('renders standard problem widgets', () => {
|
||||
render(<EditProblemViewInternal {...baseProps} />);
|
||||
editorRender(<EditProblemViewInternal {...baseProps} />);
|
||||
expect(screen.getByText('QuestionWidget')).toBeInTheDocument();
|
||||
expect(screen.getByText('ExplanationWidget')).toBeInTheDocument();
|
||||
expect(screen.getByText('AnswerWidget')).toBeInTheDocument();
|
||||
@@ -81,23 +67,23 @@ describe('EditProblemView', () => {
|
||||
});
|
||||
|
||||
it('renders advanced problem with RawEditor', () => {
|
||||
render(<EditProblemViewInternal {...baseProps} problemType={ProblemTypeKeys.ADVANCED} />);
|
||||
editorRender(<EditProblemViewInternal {...baseProps} problemType={ProblemTypeKeys.ADVANCED} />);
|
||||
expect(screen.getByText('xml:<problem></problem>')).toBeInTheDocument();
|
||||
expect(screen.getByText('SettingsWidget')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders markdown editor with RawEditor', () => {
|
||||
render(<EditProblemViewInternal {...baseProps} isMarkdownEditorEnabled />);
|
||||
editorRender(<EditProblemViewInternal {...baseProps} isMarkdownEditorEnabled />);
|
||||
expect(screen.getByText('markdown:## Problem')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows AlertModal with correct title/body for standard', () => {
|
||||
render(<EditProblemViewInternal {...baseProps} />);
|
||||
editorRender(<EditProblemViewInternal {...baseProps} />);
|
||||
expect(screen.getAllByText('No correct answer has been specified.').length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('calls saveBlock when save button is clicked', () => {
|
||||
render(<EditProblemViewInternal {...baseProps} />);
|
||||
editorRender(<EditProblemViewInternal {...baseProps} />);
|
||||
const saveBtn = screen.getByRole('button', { name: 'Ok' });
|
||||
fireEvent.click(saveBtn);
|
||||
expect(saveBlock).toHaveBeenCalled();
|
||||
@@ -110,7 +96,7 @@ describe('EditProblemView', () => {
|
||||
openSaveWarningModal: jest.fn(),
|
||||
closeSaveWarningModal,
|
||||
});
|
||||
render(<EditProblemViewInternal {...baseProps} />);
|
||||
editorRender(<EditProblemViewInternal {...baseProps} />);
|
||||
const cancelBtn = screen.getByRole('button', { name: 'Cancel' });
|
||||
fireEvent.click(cancelBtn);
|
||||
expect(closeSaveWarningModal).toHaveBeenCalled();
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SelectTypeWrapper snapshot 1`] = `
|
||||
<EditorModalWrapper>
|
||||
<ModalDialog.Header
|
||||
className="shadow-sm zindex-10"
|
||||
>
|
||||
<ModalDialog.Title>
|
||||
<FormattedMessage
|
||||
defaultMessage="Select problem type"
|
||||
description="Title for select problem type modal"
|
||||
id="authoring.problemEditor.selectType.title"
|
||||
/>
|
||||
<div
|
||||
className="pgn__modal-close-container"
|
||||
>
|
||||
<IconButton
|
||||
alt="Exit the editor"
|
||||
iconAs="Icon"
|
||||
src={[MockFunction icons.Close]}
|
||||
/>
|
||||
</div>
|
||||
</ModalDialog.Title>
|
||||
</ModalDialog.Header>
|
||||
<EditorModalBody>
|
||||
<h1>
|
||||
test child
|
||||
</h1>
|
||||
</EditorModalBody>
|
||||
<FooterWrapper>
|
||||
<ModalDialog.Footer
|
||||
className="border-top-0"
|
||||
>
|
||||
<ActionRow>
|
||||
<ActionRow.Spacer />
|
||||
<Button
|
||||
aria-label="Cancel"
|
||||
variant="tertiary"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Cancel"
|
||||
description="Label for cancel button."
|
||||
id="authoring.problemeditor.selecttype.cancelButton.label"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Select"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Select"
|
||||
description="Label for select button."
|
||||
id="authoring.problemeditor.selecttype.selectButton.label"
|
||||
/>
|
||||
</Button>
|
||||
</ActionRow>
|
||||
</ModalDialog.Footer>
|
||||
</FooterWrapper>
|
||||
</EditorModalWrapper>
|
||||
`;
|
||||
@@ -1,34 +1,85 @@
|
||||
import 'CourseAuthoring/editors/setupEditorTest';
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import { IconButton } from '@openedx/paragon';
|
||||
import SelectTypeWrapper from '.';
|
||||
import { handleCancel } from '../../../../EditorContainer/hooks';
|
||||
|
||||
jest.mock('../../../../EditorContainer/hooks', () => ({
|
||||
handleCancel: jest.fn().mockName('handleCancel'),
|
||||
}));
|
||||
import {
|
||||
screen, fireEvent, initializeMocks,
|
||||
} from '../../../../../../testUtils';
|
||||
import editorRender from '../../../../../editorTestRender';
|
||||
import SelectTypeWrapper from './index';
|
||||
import * as hooks from '../hooks';
|
||||
|
||||
describe('SelectTypeWrapper', () => {
|
||||
const props = {
|
||||
children: (<h1>test child</h1>),
|
||||
onClose: jest.fn(),
|
||||
selected: 'iMAsElecTedValUE',
|
||||
};
|
||||
const mockOnClose = jest.fn();
|
||||
|
||||
test('snapshot', () => {
|
||||
expect(shallow(<SelectTypeWrapper {...props} />).snapshot).toMatchSnapshot();
|
||||
beforeEach(() => {
|
||||
initializeMocks();
|
||||
});
|
||||
|
||||
describe('behavior', () => {
|
||||
let el;
|
||||
beforeEach(() => {
|
||||
el = shallow(<SelectTypeWrapper {...props} />);
|
||||
});
|
||||
test('close behavior is linked to modal onClose', () => {
|
||||
const expected = handleCancel({ onClose: props.onClose });
|
||||
expect(el.instance.findByType(IconButton)[0].props.onClick)
|
||||
.toEqual(expected);
|
||||
});
|
||||
it('renders component with provided content', () => {
|
||||
editorRender(
|
||||
<SelectTypeWrapper selected="foo" onClose={mockOnClose}>
|
||||
<div>Child Content</div>
|
||||
</SelectTypeWrapper>,
|
||||
);
|
||||
expect(screen.getByText('Child Content')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onClose when close button is clicked', () => {
|
||||
editorRender(
|
||||
<SelectTypeWrapper selected="foo" onClose={mockOnClose}>
|
||||
<div />
|
||||
</SelectTypeWrapper>,
|
||||
);
|
||||
fireEvent.click(screen.getByLabelText('Exit the editor'));
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls onClose when cancel button is clicked', () => {
|
||||
editorRender(
|
||||
<SelectTypeWrapper selected="foo" onClose={mockOnClose}>
|
||||
<div />
|
||||
</SelectTypeWrapper>,
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls hooks.onSelect with correct args when select button is clicked', () => {
|
||||
const onSelectMock = jest.fn();
|
||||
jest.spyOn(hooks, 'onSelect').mockImplementation(onSelectMock);
|
||||
|
||||
editorRender(
|
||||
<SelectTypeWrapper selected="foo" onClose={mockOnClose}>
|
||||
<div />
|
||||
</SelectTypeWrapper>,
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Select' }));
|
||||
expect(hooks.onSelect).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
selected: 'foo',
|
||||
updateField: expect.any(Function),
|
||||
setBlockTitle: expect.any(Function),
|
||||
defaultSettings: expect.any(Object),
|
||||
}),
|
||||
);
|
||||
expect(onSelectMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('disables select button when selected is empty', () => {
|
||||
editorRender(
|
||||
<SelectTypeWrapper selected="" onClose={mockOnClose}>
|
||||
<div />
|
||||
</SelectTypeWrapper>,
|
||||
);
|
||||
const selectBtn = screen.getByRole('button', { name: 'Select' });
|
||||
expect(selectBtn).toBeDisabled();
|
||||
});
|
||||
|
||||
it('enables select button when selected is not empty', () => {
|
||||
editorRender(
|
||||
<SelectTypeWrapper selected="bar" onClose={mockOnClose}>
|
||||
<div />
|
||||
</SelectTypeWrapper>,
|
||||
);
|
||||
const selectBtn = screen.getByRole('button', { name: 'Select' });
|
||||
expect(selectBtn).not.toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,17 +3,15 @@ import PropTypes from 'prop-types';
|
||||
import { Hyperlink, Image, Container } from '@openedx/paragon';
|
||||
import {
|
||||
FormattedMessage,
|
||||
injectIntl,
|
||||
intlShape,
|
||||
useIntl,
|
||||
} from '@edx/frontend-platform/i18n';
|
||||
import messages from './messages';
|
||||
import { ProblemTypes } from '../../../../../data/constants/problem';
|
||||
|
||||
const Preview = ({
|
||||
problemType,
|
||||
// injected
|
||||
intl,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
if (problemType === null) {
|
||||
return null;
|
||||
}
|
||||
@@ -48,9 +46,6 @@ Preview.defaultProps = {
|
||||
|
||||
Preview.propTypes = {
|
||||
problemType: PropTypes.string,
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export const PreviewInternal = Preview; // For testing only
|
||||
export default injectIntl(Preview);
|
||||
export default Preview;
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import 'CourseAuthoring/editors/setupEditorTest';
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
|
||||
import { formatMessage } from '../../../../../testUtils';
|
||||
import { PreviewInternal as Preview } from './Preview';
|
||||
|
||||
describe('Preview', () => {
|
||||
const props = {
|
||||
intl: { formatMessage },
|
||||
problemType: null,
|
||||
};
|
||||
describe('snapshots', () => {
|
||||
test('snapshots: renders as expected with default props', () => {
|
||||
expect(
|
||||
shallow(<Preview {...props} />).snapshot,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with problemType is stringresponse', () => {
|
||||
expect(
|
||||
shallow(<Preview {...props} problemType="stringresponse" />).snapshot,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with problemType is numericalresponse', () => {
|
||||
expect(
|
||||
shallow(<Preview {...props} problemType="numericalresponse" />).snapshot,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with problemType is optionresponse', () => {
|
||||
expect(
|
||||
shallow(<Preview {...props} problemType="optionresponse" />).snapshot,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with problemType is choiceresponse', () => {
|
||||
expect(
|
||||
shallow(<Preview {...props} problemType="choiceresponse" />).snapshot,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with problemType is multiplechoiceresponse', () => {
|
||||
expect(
|
||||
shallow(<Preview {...props} problemType="multiplechoiceresponse" />).snapshot,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { render, screen, initializeMocks } from '../../../../../../testUtils';
|
||||
import Preview from './Preview';
|
||||
|
||||
// Mock ProblemTypes and messages
|
||||
jest.mock('../../../../../data/constants/problem', () => ({
|
||||
ProblemTypes: {
|
||||
example: {
|
||||
title: 'Example Title',
|
||||
preview: 'example.png',
|
||||
previewDescription: 'Example description',
|
||||
helpLink: 'https://help.example.com',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe('Preview', () => {
|
||||
beforeEach(() => {
|
||||
initializeMocks();
|
||||
});
|
||||
|
||||
it('renders nothing if problemType is null', () => {
|
||||
const { container } = render(<Preview problemType={null} />);
|
||||
const reduxProviderDiv = container.querySelector('div[data-testid="redux-provider"]');
|
||||
expect(reduxProviderDiv?.innerHTML).toBe('');
|
||||
});
|
||||
|
||||
it('renders preview with correct data for a valid problemType', () => {
|
||||
render(<Preview problemType="example" />);
|
||||
expect(screen.getByText('Example Title problem')).toBeInTheDocument();
|
||||
expect(screen.getByText('Example description')).toBeInTheDocument();
|
||||
expect(screen.getByRole('img')).toHaveAttribute('src', 'example.png');
|
||||
expect(screen.getByRole('img')).toHaveAttribute('alt', 'A preview illustration of a null problem');
|
||||
expect(screen.getByRole('link', { name: 'Learn more in a new tab' })).toHaveAttribute('href', 'https://help.example.com');
|
||||
});
|
||||
|
||||
it('renders the help link with target="_blank"', () => {
|
||||
render(<Preview problemType="example" />);
|
||||
const link = screen.getByRole('link', { name: 'Learn more in a new tab' });
|
||||
expect(link).toHaveAttribute('target', '_blank');
|
||||
});
|
||||
|
||||
it('renders the correct title and description', () => {
|
||||
render(<Preview problemType="example" />);
|
||||
expect(screen.getByText('Example Title problem')).toBeInTheDocument();
|
||||
expect(screen.getByText('Example description')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,233 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Preview snapshots snapshots: renders as expected with default props 1`] = `null`;
|
||||
|
||||
exports[`Preview snapshots snapshots: renders as expected with problemType is choiceresponse 1`] = `
|
||||
<Container
|
||||
className="bg-light-300 rounded p-4"
|
||||
style={
|
||||
{
|
||||
"height": "400px",
|
||||
"width": "494px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="small"
|
||||
>
|
||||
Multi-select problem
|
||||
</div>
|
||||
<Image
|
||||
alt="A preview illustration of a {problemType, select,
|
||||
multiplechoiceresponse {single select}
|
||||
stringreponse {text input}
|
||||
numericalresponse {numerical input}
|
||||
optionresponse {dropdown}
|
||||
choiceresponse {multiple select}
|
||||
other {null}
|
||||
} problem"
|
||||
className="my-3"
|
||||
fluid={true}
|
||||
src="test-file-stub"
|
||||
/>
|
||||
<div
|
||||
className="mb-3"
|
||||
>
|
||||
Learners must select all correct answers from a list of possible options.
|
||||
</div>
|
||||
<Hyperlink
|
||||
destination="https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_multi_select.html"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Learn more"
|
||||
description="Label for Learn more button"
|
||||
id="authoring.problemEditor.learnMoreButtonLabel.label"
|
||||
/>
|
||||
</Hyperlink>
|
||||
</Container>
|
||||
`;
|
||||
|
||||
exports[`Preview snapshots snapshots: renders as expected with problemType is multiplechoiceresponse 1`] = `
|
||||
<Container
|
||||
className="bg-light-300 rounded p-4"
|
||||
style={
|
||||
{
|
||||
"height": "400px",
|
||||
"width": "494px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="small"
|
||||
>
|
||||
Single select problem
|
||||
</div>
|
||||
<Image
|
||||
alt="A preview illustration of a {problemType, select,
|
||||
multiplechoiceresponse {single select}
|
||||
stringreponse {text input}
|
||||
numericalresponse {numerical input}
|
||||
optionresponse {dropdown}
|
||||
choiceresponse {multiple select}
|
||||
other {null}
|
||||
} problem"
|
||||
className="my-3"
|
||||
fluid={true}
|
||||
src="test-file-stub"
|
||||
/>
|
||||
<div
|
||||
className="mb-3"
|
||||
>
|
||||
Learners must select the correct answer from a list of possible options.
|
||||
</div>
|
||||
<Hyperlink
|
||||
destination="https://docs.openedx.org/en/latest/educators/concepts/exercise_tools/about_multi_select.html"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Learn more"
|
||||
description="Label for Learn more button"
|
||||
id="authoring.problemEditor.learnMoreButtonLabel.label"
|
||||
/>
|
||||
</Hyperlink>
|
||||
</Container>
|
||||
`;
|
||||
|
||||
exports[`Preview snapshots snapshots: renders as expected with problemType is numericalresponse 1`] = `
|
||||
<Container
|
||||
className="bg-light-300 rounded p-4"
|
||||
style={
|
||||
{
|
||||
"height": "400px",
|
||||
"width": "494px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="small"
|
||||
>
|
||||
Numerical input problem
|
||||
</div>
|
||||
<Image
|
||||
alt="A preview illustration of a {problemType, select,
|
||||
multiplechoiceresponse {single select}
|
||||
stringreponse {text input}
|
||||
numericalresponse {numerical input}
|
||||
optionresponse {dropdown}
|
||||
choiceresponse {multiple select}
|
||||
other {null}
|
||||
} problem"
|
||||
className="my-3"
|
||||
fluid={true}
|
||||
src="test-file-stub"
|
||||
/>
|
||||
<div
|
||||
className="mb-3"
|
||||
>
|
||||
Specify one or more correct numeric answers, submitted in a response field.
|
||||
</div>
|
||||
<Hyperlink
|
||||
destination="https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/manage_numerical_input_problem.html"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Learn more"
|
||||
description="Label for Learn more button"
|
||||
id="authoring.problemEditor.learnMoreButtonLabel.label"
|
||||
/>
|
||||
</Hyperlink>
|
||||
</Container>
|
||||
`;
|
||||
|
||||
exports[`Preview snapshots snapshots: renders as expected with problemType is optionresponse 1`] = `
|
||||
<Container
|
||||
className="bg-light-300 rounded p-4"
|
||||
style={
|
||||
{
|
||||
"height": "400px",
|
||||
"width": "494px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="small"
|
||||
>
|
||||
Dropdown problem
|
||||
</div>
|
||||
<Image
|
||||
alt="A preview illustration of a {problemType, select,
|
||||
multiplechoiceresponse {single select}
|
||||
stringreponse {text input}
|
||||
numericalresponse {numerical input}
|
||||
optionresponse {dropdown}
|
||||
choiceresponse {multiple select}
|
||||
other {null}
|
||||
} problem"
|
||||
className="my-3"
|
||||
fluid={true}
|
||||
src="test-file-stub"
|
||||
/>
|
||||
<div
|
||||
className="mb-3"
|
||||
>
|
||||
Learners must select the correct answer from a list of possible options
|
||||
</div>
|
||||
<Hyperlink
|
||||
destination="https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_dropdown.html"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Learn more"
|
||||
description="Label for Learn more button"
|
||||
id="authoring.problemEditor.learnMoreButtonLabel.label"
|
||||
/>
|
||||
</Hyperlink>
|
||||
</Container>
|
||||
`;
|
||||
|
||||
exports[`Preview snapshots snapshots: renders as expected with problemType is stringresponse 1`] = `
|
||||
<Container
|
||||
className="bg-light-300 rounded p-4"
|
||||
style={
|
||||
{
|
||||
"height": "400px",
|
||||
"width": "494px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="small"
|
||||
>
|
||||
Text input problem
|
||||
</div>
|
||||
<Image
|
||||
alt="A preview illustration of a {problemType, select,
|
||||
multiplechoiceresponse {single select}
|
||||
stringreponse {text input}
|
||||
numericalresponse {numerical input}
|
||||
optionresponse {dropdown}
|
||||
choiceresponse {multiple select}
|
||||
other {null}
|
||||
} problem"
|
||||
className="my-3"
|
||||
fluid={true}
|
||||
src="test-file-stub"
|
||||
/>
|
||||
<div
|
||||
className="mb-3"
|
||||
>
|
||||
Specify one or more correct text answers, including numbers and special characters, submitted in a response field.
|
||||
</div>
|
||||
<Hyperlink
|
||||
destination="https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_text_input.html"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Learn more"
|
||||
description="Label for Learn more button"
|
||||
id="authoring.problemEditor.learnMoreButtonLabel.label"
|
||||
/>
|
||||
</Hyperlink>
|
||||
</Container>
|
||||
`;
|
||||
@@ -1,12 +1,9 @@
|
||||
import { Provider } from 'react-redux';
|
||||
import {
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
initializeMocks,
|
||||
} from '../../../../../testUtils';
|
||||
import editorStore from '../../../../data/store';
|
||||
import { EditorContextProvider } from '../../../../EditorContext';
|
||||
import editorRender from '../../../../editorTestRender';
|
||||
import * as hooks from './hooks';
|
||||
import SelectTypeModal from '.';
|
||||
|
||||
@@ -19,12 +16,8 @@ describe('SelectTypeModal', () => {
|
||||
const mockSelect = jest.fn();
|
||||
jest.spyOn(hooks, 'onSelect').mockImplementation(mockSelect);
|
||||
// This is a new-style test, unlike most of the old snapshot-based editor tests.
|
||||
render(
|
||||
<EditorContextProvider learningContextId="course-v1:Org+COURSE+RUN">
|
||||
<Provider store={editorStore}>
|
||||
<SelectTypeModal onClose={mockClose} />
|
||||
</Provider>
|
||||
</EditorContextProvider>,
|
||||
editorRender(
|
||||
<SelectTypeModal onClose={mockClose} />,
|
||||
);
|
||||
|
||||
// First we see the menu of problem types:
|
||||
|
||||
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import {
|
||||
FormattedMessage,
|
||||
injectIntl,
|
||||
} from '@edx/frontend-platform/i18n';
|
||||
import {
|
||||
Stack,
|
||||
@@ -55,5 +54,4 @@ LicenseDisplay.propTypes = {
|
||||
licenseDescription: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export const LicenseDisplayInternal = LicenseDisplay; // For testing only
|
||||
export default injectIntl(LicenseDisplay);
|
||||
export default LicenseDisplay;
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import 'CourseAuthoring/editors/setupEditorTest';
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
|
||||
import { LicenseDisplayInternal as LicenseDisplay } from './LicenseDisplay';
|
||||
|
||||
jest.mock('react', () => ({
|
||||
...jest.requireActual('react'),
|
||||
useContext: jest.fn(() => ({ license: ['error.license', jest.fn().mockName('error.setLicense')] })),
|
||||
}));
|
||||
|
||||
describe('LicenseDisplay', () => {
|
||||
const props = {
|
||||
license: 'all-rights-reserved',
|
||||
details: {},
|
||||
licenseDescription: 'FormattedMessage component with license description',
|
||||
level: 'course',
|
||||
};
|
||||
|
||||
describe('snapshots', () => {
|
||||
test('snapshots: renders as expected with default props', () => {
|
||||
expect(
|
||||
shallow(<LicenseDisplay {...props} />).snapshot,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with level set to library', () => {
|
||||
expect(
|
||||
shallow(<LicenseDisplay {...props} level="library" />).snapshot,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with level set to block', () => {
|
||||
expect(
|
||||
shallow(<LicenseDisplay {...props} level="block" />).snapshot,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with level set to block and license set to select', () => {
|
||||
expect(
|
||||
shallow(<LicenseDisplay {...props} level="block" license="select" />).snapshot,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with level set to block and license set to Creative Commons', () => {
|
||||
expect(
|
||||
shallow(<LicenseDisplay {...props} level="block" license="creative-commons" />).snapshot,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import React from 'react';
|
||||
import { initializeMocks, render, screen } from '../../../../../../../testUtils';
|
||||
import LicenseDisplay from './LicenseDisplay';
|
||||
|
||||
jest.mock('../../../../../../data/constants/licenses', () => ({
|
||||
LicenseTypes: {
|
||||
select: 'select',
|
||||
creativeCommons: 'creativeCommons',
|
||||
proprietary: 'proprietary',
|
||||
},
|
||||
}));
|
||||
|
||||
const defaultDetails = {
|
||||
attribution: true,
|
||||
noncommercial: false,
|
||||
noDerivatives: false,
|
||||
shareAlike: false,
|
||||
};
|
||||
|
||||
describe('LicenseDisplay', () => {
|
||||
beforeEach(() => {
|
||||
initializeMocks();
|
||||
});
|
||||
it('renders nothing if license is select', () => {
|
||||
const { container } = render(
|
||||
<LicenseDisplay
|
||||
license="select"
|
||||
details={defaultDetails}
|
||||
level="course"
|
||||
licenseDescription="Some description"
|
||||
/>,
|
||||
);
|
||||
const reduxProviderDiv = container.querySelector('div[data-testid="redux-provider"]');
|
||||
expect(reduxProviderDiv?.innerHTML).toBe('');
|
||||
});
|
||||
|
||||
it('renders displaySubsectionTitle and licenseDescription for non-select license', () => {
|
||||
render(
|
||||
<LicenseDisplay
|
||||
license="proprietary"
|
||||
details={defaultDetails}
|
||||
level="course"
|
||||
licenseDescription="Proprietary license description"
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText('License Display')).toBeInTheDocument();
|
||||
expect(screen.getByText('Proprietary license description')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Hyperlink for creativeCommons license', () => {
|
||||
render(
|
||||
<LicenseDisplay
|
||||
license="creativeCommons"
|
||||
details={defaultDetails}
|
||||
level="course"
|
||||
licenseDescription="Creative Commons description"
|
||||
/>,
|
||||
);
|
||||
const link = screen.getByRole('link', { name: 'View license details in a new tab' });
|
||||
expect(link).toHaveAttribute('href', 'https://creativecommons.org/about');
|
||||
expect(link).toHaveAttribute('target', '_blank');
|
||||
});
|
||||
|
||||
it('does not render Hyperlink for non-creativeCommons license', () => {
|
||||
render(
|
||||
<LicenseDisplay
|
||||
license="proprietary"
|
||||
details={defaultDetails}
|
||||
level="course"
|
||||
licenseDescription="desc"
|
||||
/>,
|
||||
);
|
||||
expect(screen.queryByRole('link', { name: 'View Details' })).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -1,130 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LicenseDisplay snapshots snapshots: renders as expected with default props 1`] = `
|
||||
<Stack
|
||||
gap={3}
|
||||
>
|
||||
<div
|
||||
className="x-small"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="License Display"
|
||||
description="Title for license display subsection"
|
||||
id="authoring.videoeditor.license.displaySubsection.title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="small border border-gray-300 rounded p-4"
|
||||
>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
details={{}}
|
||||
license="all-rights-reserved"
|
||||
/>
|
||||
<div
|
||||
className="x-small mt-3"
|
||||
>
|
||||
FormattedMessage component with license description
|
||||
</div>
|
||||
</div>
|
||||
</Stack>
|
||||
`;
|
||||
|
||||
exports[`LicenseDisplay snapshots snapshots: renders as expected with level set to block 1`] = `
|
||||
<Stack
|
||||
gap={3}
|
||||
>
|
||||
<div
|
||||
className="x-small"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="License Display"
|
||||
description="Title for license display subsection"
|
||||
id="authoring.videoeditor.license.displaySubsection.title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="small border border-gray-300 rounded p-4"
|
||||
>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
details={{}}
|
||||
license="all-rights-reserved"
|
||||
/>
|
||||
<div
|
||||
className="x-small mt-3"
|
||||
>
|
||||
FormattedMessage component with license description
|
||||
</div>
|
||||
</div>
|
||||
</Stack>
|
||||
`;
|
||||
|
||||
exports[`LicenseDisplay snapshots snapshots: renders as expected with level set to block and license set to Creative Commons 1`] = `
|
||||
<Stack
|
||||
gap={3}
|
||||
>
|
||||
<div
|
||||
className="x-small"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="License Display"
|
||||
description="Title for license display subsection"
|
||||
id="authoring.videoeditor.license.displaySubsection.title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="small border border-gray-300 rounded p-4"
|
||||
>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
details={{}}
|
||||
license="creative-commons"
|
||||
/>
|
||||
<div
|
||||
className="x-small mt-3"
|
||||
>
|
||||
FormattedMessage component with license description
|
||||
</div>
|
||||
</div>
|
||||
<Hyperlink
|
||||
className="text-primary-500 x-small"
|
||||
destination="https://creativecommons.org/about"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="View license details"
|
||||
description="Label for view license details button"
|
||||
id="authoring.videoeditor.license.viewLicenseDetailsLabel.label"
|
||||
/>
|
||||
</Hyperlink>
|
||||
</Stack>
|
||||
`;
|
||||
|
||||
exports[`LicenseDisplay snapshots snapshots: renders as expected with level set to block and license set to select 1`] = `null`;
|
||||
|
||||
exports[`LicenseDisplay snapshots snapshots: renders as expected with level set to library 1`] = `
|
||||
<Stack
|
||||
gap={3}
|
||||
>
|
||||
<div
|
||||
className="x-small"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="License Display"
|
||||
description="Title for license display subsection"
|
||||
id="authoring.videoeditor.license.displaySubsection.title"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="small border border-gray-300 rounded p-4"
|
||||
>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
details={{}}
|
||||
license="all-rights-reserved"
|
||||
/>
|
||||
<div
|
||||
className="x-small mt-3"
|
||||
>
|
||||
FormattedMessage component with license description
|
||||
</div>
|
||||
</div>
|
||||
</Stack>
|
||||
`;
|
||||
@@ -34,7 +34,7 @@ exports[`LicenseWidget snapshots snapshots: renders as expected with default pro
|
||||
level="course"
|
||||
license="all-rights-reserved"
|
||||
/>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
<LicenseDisplay
|
||||
details={{}}
|
||||
license="all-rights-reserved"
|
||||
licenseDescription={
|
||||
@@ -101,7 +101,7 @@ exports[`LicenseWidget snapshots snapshots: renders as expected with isLibrary t
|
||||
level="library"
|
||||
license="all-rights-reserved"
|
||||
/>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
<LicenseDisplay
|
||||
details={{}}
|
||||
license="all-rights-reserved"
|
||||
licenseDescription={
|
||||
@@ -151,7 +151,7 @@ exports[`LicenseWidget snapshots snapshots: renders as expected with licenseType
|
||||
level="block"
|
||||
license="all-rights-reserved"
|
||||
/>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
<LicenseDisplay
|
||||
details={{}}
|
||||
license="all-rights-reserved"
|
||||
licenseDescription={
|
||||
|
||||
28
src/editors/editorTestRender.jsx
Normal file
28
src/editors/editorTestRender.jsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { render as baseRender } from '../testUtils';
|
||||
import { EditorContextProvider } from './EditorContext';
|
||||
import editorStore from './data/store';
|
||||
|
||||
/**
|
||||
* Custom render function for testing React components with the editor context and Redux store.
|
||||
*
|
||||
* Wraps the provided UI in both the EditorContextProvider and Redux Provider,
|
||||
* ensuring that components under test have access to the necessary context and store.
|
||||
*
|
||||
* @param {React.ReactElement} ui - The React element to render.
|
||||
* @param {string} [learningContextId='course-v1:Org+COURSE+RUN'] - Optional learning context ID
|
||||
* for the EditorContextProvider.
|
||||
* @returns {RenderResult} The result of the render, as returned by RTL render.
|
||||
*/
|
||||
const editorRender = (ui, learningContextId = 'course-v1:Org+COURSE+RUN') => baseRender(ui, {
|
||||
extraWrapper: ({ children }) => (
|
||||
<EditorContextProvider learningContextId={learningContextId}>
|
||||
<Provider store={editorStore}>
|
||||
{children}
|
||||
</Provider>
|
||||
</EditorContextProvider>
|
||||
),
|
||||
});
|
||||
|
||||
export default editorRender;
|
||||
Reference in New Issue
Block a user