test: replacing snapshot tests with RTL tests part 6 (#2173)
* test: replacing snapshot tests with rtl tests part 6 * fix: removing testing purposed text * test: fixing test mocking issues
This commit is contained in:
committed by
GitHub
parent
fa9d66c5e5
commit
19f81cc05d
@@ -11,10 +11,6 @@ import { useMessageHandlers } from '..';
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
jest.mock('@edx/react-unit-test-utils', () => ({
|
||||
useKeyedState: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@edx/frontend-platform/logging', () => ({
|
||||
logError: jest.fn(),
|
||||
}));
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Form,
|
||||
} from '@openedx/paragon';
|
||||
import { FeedbackOutline, DeleteOutline } from '@openedx/paragon/icons';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import messages from './messages';
|
||||
import { selectors } from '../../../../../data/redux';
|
||||
@@ -22,8 +22,6 @@ import ExpandableTextArea from '../../../../../sharedComponents/ExpandableTextAr
|
||||
const AnswerOption = ({
|
||||
answer,
|
||||
hasSingleAnswer,
|
||||
// injected
|
||||
intl,
|
||||
// redux
|
||||
problemType,
|
||||
images,
|
||||
@@ -31,6 +29,7 @@ const AnswerOption = ({
|
||||
blockId,
|
||||
learningContextId,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const removeAnswer = hooks.removeAnswer({ answer, dispatch });
|
||||
const setAnswer = hooks.setAnswer({ answer, hasSingleAnswer, dispatch });
|
||||
@@ -151,8 +150,6 @@ const AnswerOption = ({
|
||||
AnswerOption.propTypes = {
|
||||
answer: answerOptionProps.isRequired,
|
||||
hasSingleAnswer: PropTypes.bool.isRequired,
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
// redux
|
||||
problemType: PropTypes.string.isRequired,
|
||||
images: PropTypes.shape({}).isRequired,
|
||||
@@ -171,4 +168,4 @@ export const mapStateToProps = (state) => ({
|
||||
|
||||
export const mapDispatchToProps = {};
|
||||
export const AnswerOptionInternal = AnswerOption; // For testing only
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(memo(AnswerOption)));
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(memo(AnswerOption));
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
import 'CourseAuthoring/editors/setupEditorTest';
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import { formatMessage } from '../../../../../testUtils';
|
||||
import { selectors } from '../../../../../data/redux';
|
||||
import { AnswerOptionInternal as AnswerOption, mapStateToProps } from './AnswerOption';
|
||||
|
||||
jest.mock('../../../../../data/redux', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
selectors: {
|
||||
problem: {
|
||||
problemType: jest.fn(state => ({ problemType: state })),
|
||||
},
|
||||
app: {
|
||||
images: jest.fn(state => ({ images: state })),
|
||||
isLibrary: jest.fn(state => ({ isLibrary: state })),
|
||||
learningContextId: jest.fn(state => ({ learningContextId: state })),
|
||||
blockId: jest.fn(state => ({ blockId: state })),
|
||||
},
|
||||
},
|
||||
thunkActions: {
|
||||
video: {
|
||||
importTranscripts: jest.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe('AnswerOption', () => {
|
||||
const answerWithOnlyFeedback = {
|
||||
id: 'A',
|
||||
title: 'Answer 1',
|
||||
correct: true,
|
||||
selectedFeedback: 'some feedback',
|
||||
};
|
||||
const answerWithSelectedUnselectedFeedback = {
|
||||
id: 'A',
|
||||
title: 'Answer 1',
|
||||
correct: true,
|
||||
selectedFeedback: 'selected feedback',
|
||||
unselectedFeedback: 'unselected feedback',
|
||||
};
|
||||
const answerRange = {
|
||||
id: 'A',
|
||||
title: 'Answer 1',
|
||||
correct: true,
|
||||
selectedFeedback: 'selected feedback',
|
||||
unselectedFeedback: 'unselected feedback',
|
||||
isAnswerRange: true,
|
||||
};
|
||||
|
||||
const props = {
|
||||
hasSingleAnswer: false,
|
||||
answer: answerWithOnlyFeedback,
|
||||
// inject
|
||||
intl: { formatMessage },
|
||||
// redux
|
||||
problemType: 'multiplechoiceresponse',
|
||||
images: {},
|
||||
isLibrary: false,
|
||||
learningContextId: 'course+org+run',
|
||||
};
|
||||
describe('render', () => {
|
||||
test('snapshot: renders correct option with feedback', () => {
|
||||
expect(shallow(<AnswerOption {...props} />).snapshot).toMatchSnapshot();
|
||||
});
|
||||
test('snapshot: renders correct option with selected unselected feedback', () => {
|
||||
expect(shallow(<AnswerOption {...props} problemType="choiceresponse" answer={answerWithSelectedUnselectedFeedback} />).snapshot).toMatchSnapshot();
|
||||
});
|
||||
test('snapshot: renders correct option with numeric input problem', () => {
|
||||
expect(shallow(<AnswerOption {...props} problemType="numericalresponse" />).snapshot).toMatchSnapshot();
|
||||
});
|
||||
test('snapshot: renders correct option with numeric input problem and answer range', () => {
|
||||
expect(shallow(<AnswerOption {...props} problemType="numericalresponse" answer={answerRange} />).snapshot).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapStateToProps', () => {
|
||||
const testState = { A: 'pple', B: 'anana', C: 'ucumber' };
|
||||
test('problemType from problem.problemType', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).problemType,
|
||||
).toEqual(selectors.problem.problemType(testState));
|
||||
});
|
||||
test('images from app.images', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).images,
|
||||
).toEqual(selectors.app.images(testState));
|
||||
});
|
||||
test('learningContextId from app.learningContextId', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).learningContextId,
|
||||
).toEqual(selectors.app.learningContextId(testState));
|
||||
});
|
||||
test('isLibrary from app.isLibrary', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).isLibrary,
|
||||
).toEqual(selectors.app.isLibrary(testState));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,110 @@
|
||||
import React from 'react';
|
||||
import { render, screen, initializeMocks } from '@src/testUtils';
|
||||
import AnswerOption from './AnswerOption';
|
||||
import * as hooks from './hooks';
|
||||
import { selectors } from '../../../../../data/redux';
|
||||
|
||||
const { problem } = selectors;
|
||||
|
||||
jest.mock('../../../../../data/redux', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
selectors: {
|
||||
problem: {
|
||||
problemType: jest.fn().mockReturnValue(''),
|
||||
},
|
||||
app: {
|
||||
images: jest.fn(state => ({ images: state })),
|
||||
isLibrary: jest.fn().mockReturnValue(true),
|
||||
learningContextId: jest.fn(state => ({ learningContextId: state })),
|
||||
blockId: jest.fn(state => ({ blockId: state })),
|
||||
},
|
||||
},
|
||||
thunkActions: {
|
||||
video: {
|
||||
importTranscripts: jest.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('../../../../../sharedComponents/ExpandableTextArea', () => 'ExpandableTextArea');
|
||||
|
||||
describe('AnswerOption', () => {
|
||||
const answerWithOnlyFeedback = {
|
||||
id: 'A',
|
||||
title: 'Answer 1',
|
||||
correct: true,
|
||||
selectedFeedback: 'some feedback',
|
||||
isAnswerRange: true,
|
||||
};
|
||||
const answerWithSelectedUnselectedFeedback = {
|
||||
id: 'B',
|
||||
title: 'Answer 2',
|
||||
correct: true,
|
||||
selectedFeedback: 'selected feedback',
|
||||
unselectedFeedback: 'unselected feedback',
|
||||
isAnswerRange: false,
|
||||
};
|
||||
const answerRange = {
|
||||
id: 'A',
|
||||
title: 'Answer Range 1',
|
||||
correct: true,
|
||||
selectedFeedback: 'selected feedback',
|
||||
unselectedFeedback: 'unselected feedback',
|
||||
isAnswerRange: true,
|
||||
};
|
||||
|
||||
const props = {
|
||||
hasSingleAnswer: false,
|
||||
answer: answerWithOnlyFeedback,
|
||||
// redux
|
||||
problemType: 'multiplechoiceresponse',
|
||||
images: {},
|
||||
isLibrary: false,
|
||||
learningContextId: 'course+org+run',
|
||||
blockId: 'block-id',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(hooks, 'removeAnswer').mockReturnValue(jest.fn());
|
||||
jest.spyOn(hooks, 'setAnswer').mockReturnValue(jest.fn());
|
||||
jest.spyOn(hooks, 'setAnswerTitle').mockReturnValue(jest.fn());
|
||||
jest.spyOn(hooks, 'setSelectedFeedback').mockReturnValue(jest.fn());
|
||||
jest.spyOn(hooks, 'setUnselectedFeedback').mockReturnValue(jest.fn());
|
||||
jest.spyOn(hooks, 'useFeedback').mockReturnValue({
|
||||
isFeedbackVisible: false,
|
||||
toggleFeedback: jest.fn(),
|
||||
});
|
||||
initializeMocks();
|
||||
});
|
||||
|
||||
test('renders correct option with feedback', () => {
|
||||
jest.spyOn(problem, 'problemType').mockReturnValue('multiplechoiceresponse');
|
||||
render(<AnswerOption {...props} />);
|
||||
expect(screen.getByPlaceholderText('Enter an answer')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Delete answer' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders correct option with selected unselected feedback', () => {
|
||||
jest.spyOn(problem, 'problemType').mockReturnValue('choiceresponse');
|
||||
const myProps = { ...props, answer: answerWithSelectedUnselectedFeedback };
|
||||
render(<AnswerOption {...myProps} />);
|
||||
expect(screen.getByText(answerWithSelectedUnselectedFeedback.id)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders correct option with optionresponse input problem', () => {
|
||||
jest.spyOn(problem, 'problemType').mockReturnValue('optionresponse');
|
||||
const myProps = { ...props };
|
||||
render(<AnswerOption {...myProps} />);
|
||||
expect(screen.getByRole('textbox')).toBeInTheDocument();
|
||||
expect(screen.getByText(answerWithOnlyFeedback.title)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders correct option with numeric input problem and answer range', () => {
|
||||
jest.spyOn(problem, 'problemType').mockReturnValue('numericalresponse');
|
||||
const myProps = { ...props };
|
||||
render(<AnswerOption {...myProps} answer={answerRange} />);
|
||||
expect(screen.getByText(answerRange.title)).toBeInTheDocument();
|
||||
expect(screen.getByRole('textbox')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,342 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AnswerOption render snapshot: renders correct option with feedback 1`] = `
|
||||
<Advanced
|
||||
className="answer-option d-flex flex-row justify-content-between flex-nowrap pb-2 pt-2"
|
||||
onToggle={[Function]}
|
||||
open={false}
|
||||
>
|
||||
<div
|
||||
className="mr-1 d-flex"
|
||||
>
|
||||
<Checker
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"selectedFeedback": "some feedback",
|
||||
"title": "Answer 1",
|
||||
}
|
||||
}
|
||||
disabled={false}
|
||||
hasSingleAnswer={false}
|
||||
setAnswer={[Function]}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="ml-1 flex-grow-1"
|
||||
>
|
||||
<ExpandableTextArea
|
||||
error={false}
|
||||
errorMessage={null}
|
||||
id="answer-A"
|
||||
images={{}}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
placeholder="Enter an answer"
|
||||
setContent={[Function]}
|
||||
value="Answer 1"
|
||||
/>
|
||||
<Body>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"selectedFeedback": "some feedback",
|
||||
"title": "Answer 1",
|
||||
}
|
||||
}
|
||||
images={{}}
|
||||
intl={
|
||||
{
|
||||
"formatMessage": [Function],
|
||||
}
|
||||
}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
problemType="multiplechoiceresponse"
|
||||
setSelectedFeedback={[Function]}
|
||||
setUnselectedFeedback={[Function]}
|
||||
/>
|
||||
</Body>
|
||||
</div>
|
||||
<div
|
||||
className="d-flex flex-row flex-nowrap"
|
||||
>
|
||||
<Trigger
|
||||
aria-label="Toggle feedback"
|
||||
className="btn-icon btn-icon-primary btn-icon-md align-items-center"
|
||||
>
|
||||
<Icon
|
||||
alt="Toggle feedback"
|
||||
/>
|
||||
</Trigger>
|
||||
<IconButton
|
||||
alt="Delete answer"
|
||||
iconAs="Icon"
|
||||
onClick={[Function]}
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
</Advanced>
|
||||
`;
|
||||
|
||||
exports[`AnswerOption render snapshot: renders correct option with numeric input problem 1`] = `
|
||||
<Advanced
|
||||
className="answer-option d-flex flex-row justify-content-between flex-nowrap pb-2 pt-2"
|
||||
onToggle={[Function]}
|
||||
open={false}
|
||||
>
|
||||
<div
|
||||
className="mr-1 d-flex"
|
||||
>
|
||||
<Checker
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"selectedFeedback": "some feedback",
|
||||
"title": "Answer 1",
|
||||
}
|
||||
}
|
||||
disabled={true}
|
||||
hasSingleAnswer={false}
|
||||
setAnswer={[Function]}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="ml-1 flex-grow-1"
|
||||
>
|
||||
<Form.Control
|
||||
as="textarea"
|
||||
autoResize={true}
|
||||
className="answer-option-textarea text-gray-500 small"
|
||||
onChange={[Function]}
|
||||
placeholder="Enter an answer"
|
||||
rows={1}
|
||||
value="Answer 1"
|
||||
/>
|
||||
<Body>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"selectedFeedback": "some feedback",
|
||||
"title": "Answer 1",
|
||||
}
|
||||
}
|
||||
images={{}}
|
||||
intl={
|
||||
{
|
||||
"formatMessage": [Function],
|
||||
}
|
||||
}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
problemType="numericalresponse"
|
||||
setSelectedFeedback={[Function]}
|
||||
setUnselectedFeedback={[Function]}
|
||||
/>
|
||||
</Body>
|
||||
</div>
|
||||
<div
|
||||
className="d-flex flex-row flex-nowrap"
|
||||
>
|
||||
<Trigger
|
||||
aria-label="Toggle feedback"
|
||||
className="btn-icon btn-icon-primary btn-icon-md align-items-center"
|
||||
>
|
||||
<Icon
|
||||
alt="Toggle feedback"
|
||||
/>
|
||||
</Trigger>
|
||||
<IconButton
|
||||
alt="Delete answer"
|
||||
iconAs="Icon"
|
||||
onClick={[Function]}
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
</Advanced>
|
||||
`;
|
||||
|
||||
exports[`AnswerOption render snapshot: renders correct option with numeric input problem and answer range 1`] = `
|
||||
<Advanced
|
||||
className="answer-option d-flex flex-row justify-content-between flex-nowrap pb-2 pt-2"
|
||||
onToggle={[Function]}
|
||||
open={false}
|
||||
>
|
||||
<div
|
||||
className="mr-1 d-flex"
|
||||
>
|
||||
<Checker
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"isAnswerRange": true,
|
||||
"selectedFeedback": "selected feedback",
|
||||
"title": "Answer 1",
|
||||
"unselectedFeedback": "unselected feedback",
|
||||
}
|
||||
}
|
||||
disabled={true}
|
||||
hasSingleAnswer={false}
|
||||
setAnswer={[Function]}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="ml-1 flex-grow-1"
|
||||
>
|
||||
<div>
|
||||
<Form.Control
|
||||
as="textarea"
|
||||
autoResize={true}
|
||||
className="answer-option-textarea text-gray-500 small"
|
||||
onChange={[Function]}
|
||||
placeholder="Enter an answer range"
|
||||
rows={1}
|
||||
value="Answer 1"
|
||||
/>
|
||||
<div
|
||||
className="pgn__form-switch-helper-text"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Enter min and max values separated by a comma. Use a bracket to include the number next to it in the range, or a parenthesis to exclude the number. For example, to identify the correct answers as 5, 6, or 7, but not 8, specify [5,8)."
|
||||
description="Helper text describing usage of answer ranges"
|
||||
id="authoring.answerwidget.answer.answerRangeHelperText"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Body>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"isAnswerRange": true,
|
||||
"selectedFeedback": "selected feedback",
|
||||
"title": "Answer 1",
|
||||
"unselectedFeedback": "unselected feedback",
|
||||
}
|
||||
}
|
||||
images={{}}
|
||||
intl={
|
||||
{
|
||||
"formatMessage": [Function],
|
||||
}
|
||||
}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
problemType="numericalresponse"
|
||||
setSelectedFeedback={[Function]}
|
||||
setUnselectedFeedback={[Function]}
|
||||
/>
|
||||
</Body>
|
||||
</div>
|
||||
<div
|
||||
className="d-flex flex-row flex-nowrap"
|
||||
>
|
||||
<Trigger
|
||||
aria-label="Toggle feedback"
|
||||
className="btn-icon btn-icon-primary btn-icon-md align-items-center"
|
||||
>
|
||||
<Icon
|
||||
alt="Toggle feedback"
|
||||
/>
|
||||
</Trigger>
|
||||
<IconButton
|
||||
alt="Delete answer"
|
||||
iconAs="Icon"
|
||||
onClick={[Function]}
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
</Advanced>
|
||||
`;
|
||||
|
||||
exports[`AnswerOption render snapshot: renders correct option with selected unselected feedback 1`] = `
|
||||
<Advanced
|
||||
className="answer-option d-flex flex-row justify-content-between flex-nowrap pb-2 pt-2"
|
||||
onToggle={[Function]}
|
||||
open={false}
|
||||
>
|
||||
<div
|
||||
className="mr-1 d-flex"
|
||||
>
|
||||
<Checker
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"selectedFeedback": "selected feedback",
|
||||
"title": "Answer 1",
|
||||
"unselectedFeedback": "unselected feedback",
|
||||
}
|
||||
}
|
||||
disabled={false}
|
||||
hasSingleAnswer={false}
|
||||
setAnswer={[Function]}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="ml-1 flex-grow-1"
|
||||
>
|
||||
<ExpandableTextArea
|
||||
error={false}
|
||||
errorMessage={null}
|
||||
id="answer-A"
|
||||
images={{}}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
placeholder="Enter an answer"
|
||||
setContent={[Function]}
|
||||
value="Answer 1"
|
||||
/>
|
||||
<Body>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
answer={
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"selectedFeedback": "selected feedback",
|
||||
"title": "Answer 1",
|
||||
"unselectedFeedback": "unselected feedback",
|
||||
}
|
||||
}
|
||||
images={{}}
|
||||
intl={
|
||||
{
|
||||
"formatMessage": [Function],
|
||||
}
|
||||
}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
problemType="choiceresponse"
|
||||
setSelectedFeedback={[Function]}
|
||||
setUnselectedFeedback={[Function]}
|
||||
/>
|
||||
</Body>
|
||||
</div>
|
||||
<div
|
||||
className="d-flex flex-row flex-nowrap"
|
||||
>
|
||||
<Trigger
|
||||
aria-label="Toggle feedback"
|
||||
className="btn-icon btn-icon-primary btn-icon-md align-items-center"
|
||||
>
|
||||
<Icon
|
||||
alt="Toggle feedback"
|
||||
/>
|
||||
</Trigger>
|
||||
<IconButton
|
||||
alt="Delete answer"
|
||||
iconAs="Icon"
|
||||
onClick={[Function]}
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
</Advanced>
|
||||
`;
|
||||
@@ -1,24 +0,0 @@
|
||||
import 'CourseAuthoring/editors/setupEditorTest';
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import SettingsOption from './SettingsOption';
|
||||
|
||||
describe('SettingsOption', () => {
|
||||
describe('default with children', () => {
|
||||
const children = (<h1>My test content</h1>);
|
||||
test('snapshot: renders correct', () => {
|
||||
expect(shallow(<SettingsOption title="Settings Option Title" summary="Settings Option Summary">{children}</SettingsOption>).snapshot).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
describe('with additional sections', () => {
|
||||
const children = (<h1>First Section</h1>);
|
||||
const sections = [<h1>Second Section</h1>, <h1>Third Section</h1>];
|
||||
test('snapshot: renders correct', () => {
|
||||
expect(shallow(
|
||||
<SettingsOption title="Settings Option Title" summary="Settings Option Summary" extraSections={sections}>
|
||||
{children}
|
||||
</SettingsOption>,
|
||||
).snapshot).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
render, screen, initializeMocks,
|
||||
} from '@src/testUtils';
|
||||
import SettingsOption from './SettingsOption';
|
||||
|
||||
describe('SettingsOption', () => {
|
||||
const defaultProps = {
|
||||
title: 'Settings Option Title',
|
||||
summary: 'Settings Option Summary',
|
||||
hasExpandableTextArea: true,
|
||||
className: 'test-classname',
|
||||
extraSections: [],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
initializeMocks();
|
||||
});
|
||||
|
||||
test('renders correct expanded area', () => {
|
||||
const children = <h1>My test content</h1>;
|
||||
render(<SettingsOption {...defaultProps}>{children}</SettingsOption>);
|
||||
expect(screen.getByText('Settings Option Title')).toBeInTheDocument();
|
||||
expect(screen.getByText('My test content')).toBeInTheDocument();
|
||||
expect(document.querySelector('.test-classname')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders summary when not expanded', () => {
|
||||
const children = <h1>My test content</h1>;
|
||||
render((<SettingsOption {...defaultProps} hasExpandableTextArea={false}>{children}</SettingsOption>));
|
||||
expect(screen.getByText('Settings Option Title')).toBeInTheDocument();
|
||||
expect(screen.queryByText('My test content')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Settings Option Summary')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders sections when expanded', () => {
|
||||
const children = (<h1>First Section</h1>);
|
||||
const sections = [{ children: <h1>Second Section</h1> }, { children: <h1>Third Section</h1> }];
|
||||
render(<SettingsOption {...defaultProps} extraSections={sections}>{children}</SettingsOption>);
|
||||
expect(screen.getByText('First Section')).toBeInTheDocument();
|
||||
expect(screen.getByText('Second Section')).toBeInTheDocument();
|
||||
expect(screen.getByText('Third Section')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,109 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SettingsOption default with children snapshot: renders correct 1`] = `
|
||||
<Card
|
||||
className=" settingsOption border border-light-700 shadow-none"
|
||||
>
|
||||
<Card.Section
|
||||
className="settingsCardTitleSection"
|
||||
key="settingsOption-Settings Option Title-header"
|
||||
>
|
||||
<Advanced
|
||||
onToggle={[Function]}
|
||||
open={false}
|
||||
>
|
||||
<Trigger
|
||||
className="collapsible-trigger d-flex"
|
||||
>
|
||||
<span
|
||||
className="flex-grow-1 text-primary-500 x-small"
|
||||
>
|
||||
Settings Option Title
|
||||
</span>
|
||||
<Visible
|
||||
whenClosed={true}
|
||||
>
|
||||
<Icon />
|
||||
</Visible>
|
||||
<Visible
|
||||
whenOpen={true}
|
||||
>
|
||||
<Icon />
|
||||
</Visible>
|
||||
</Trigger>
|
||||
</Advanced>
|
||||
</Card.Section>
|
||||
<CardSection
|
||||
isCardCollapsibleOpen={false}
|
||||
key="settingsOption-Settings Option Title-children"
|
||||
none={false}
|
||||
summary="Settings Option Summary"
|
||||
>
|
||||
<h1>
|
||||
My test content
|
||||
</h1>
|
||||
</CardSection>
|
||||
</Card>
|
||||
`;
|
||||
|
||||
exports[`SettingsOption with additional sections snapshot: renders correct 1`] = `
|
||||
<Card
|
||||
className=" settingsOption border border-light-700 shadow-none"
|
||||
>
|
||||
<Card.Section
|
||||
className="settingsCardTitleSection"
|
||||
key="settingsOption-Settings Option Title-header"
|
||||
>
|
||||
<Advanced
|
||||
onToggle={[Function]}
|
||||
open={false}
|
||||
>
|
||||
<Trigger
|
||||
className="collapsible-trigger d-flex"
|
||||
>
|
||||
<span
|
||||
className="flex-grow-1 text-primary-500 x-small"
|
||||
>
|
||||
Settings Option Title
|
||||
</span>
|
||||
<Visible
|
||||
whenClosed={true}
|
||||
>
|
||||
<Icon />
|
||||
</Visible>
|
||||
<Visible
|
||||
whenOpen={true}
|
||||
>
|
||||
<Icon />
|
||||
</Visible>
|
||||
</Trigger>
|
||||
</Advanced>
|
||||
</Card.Section>
|
||||
<CardSection
|
||||
isCardCollapsibleOpen={false}
|
||||
key="settingsOption-Settings Option Title-children"
|
||||
none={false}
|
||||
summary="Settings Option Summary"
|
||||
>
|
||||
<h1>
|
||||
First Section
|
||||
</h1>
|
||||
</CardSection>
|
||||
<Fragment>
|
||||
<CardSection
|
||||
isCardCollapsibleOpen={false}
|
||||
key="settingsOption-Settings Option Title-0"
|
||||
none={false}
|
||||
summary={null}
|
||||
/>
|
||||
</Fragment>
|
||||
<Fragment>
|
||||
<CardSection
|
||||
isCardCollapsibleOpen={false}
|
||||
key="settingsOption-Settings Option Title-1"
|
||||
none={false}
|
||||
summary={null}
|
||||
/>
|
||||
</Fragment>
|
||||
</Card>
|
||||
`;
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import {
|
||||
ActionRow, Form, Icon, IconButton, Row,
|
||||
} from '@openedx/paragon';
|
||||
import { DeleteOutline } from '@openedx/paragon/icons';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import messages from '../../messages';
|
||||
|
||||
const GroupFeedbackRow = ({
|
||||
@@ -13,48 +13,47 @@ const GroupFeedbackRow = ({
|
||||
handleFeedbackChange,
|
||||
handleDelete,
|
||||
answers,
|
||||
// injected
|
||||
intl,
|
||||
}) => (
|
||||
|
||||
<div className="mb-4">
|
||||
<ActionRow className="mb-2">
|
||||
<Form.Control
|
||||
value={value.feedback}
|
||||
onChange={handleFeedbackChange}
|
||||
/>
|
||||
<div className="d-flex flex-row flex-nowrap">
|
||||
<IconButton
|
||||
src={DeleteOutline}
|
||||
iconAs={Icon}
|
||||
alt={intl.formatMessage(messages.settingsDeleteIconAltText)}
|
||||
onClick={handleDelete}
|
||||
variant="primary"
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<ActionRow className="mb-2">
|
||||
<Form.Control
|
||||
value={value.feedback}
|
||||
onChange={handleFeedbackChange}
|
||||
/>
|
||||
</div>
|
||||
</ActionRow>
|
||||
<Form.CheckboxSet
|
||||
onChange={handleAnswersSelectedChange}
|
||||
value={value.answers}
|
||||
>
|
||||
<Row className="mx-0">
|
||||
{answers.map((letter) => (
|
||||
<Form.Checkbox
|
||||
className="mr-4 mt-1"
|
||||
value={letter.id}
|
||||
checked={value.answers.indexOf(letter.id)}
|
||||
isValid={value.answers.indexOf(letter.id) >= 0}
|
||||
>
|
||||
<div className="x-small">
|
||||
{letter.id}
|
||||
</div>
|
||||
</Form.Checkbox>
|
||||
))}
|
||||
</Row>
|
||||
</Form.CheckboxSet>
|
||||
</div>
|
||||
|
||||
);
|
||||
<div className="d-flex flex-row flex-nowrap">
|
||||
<IconButton
|
||||
src={DeleteOutline}
|
||||
iconAs={Icon}
|
||||
alt={intl.formatMessage(messages.settingsDeleteIconAltText)}
|
||||
onClick={handleDelete}
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
</ActionRow>
|
||||
<Form.CheckboxSet
|
||||
onChange={handleAnswersSelectedChange}
|
||||
value={value.answers}
|
||||
>
|
||||
<Row className="mx-0">
|
||||
{answers.map((letter) => (
|
||||
<Form.Checkbox
|
||||
className="mr-4 mt-1"
|
||||
value={letter.id}
|
||||
checked={value.answers.indexOf(letter.id)}
|
||||
isValid={value.answers.indexOf(letter.id) >= 0}
|
||||
>
|
||||
<div className="x-small">
|
||||
{letter.id}
|
||||
</div>
|
||||
</Form.Checkbox>
|
||||
))}
|
||||
</Row>
|
||||
</Form.CheckboxSet>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
GroupFeedbackRow.propTypes = {
|
||||
answers: PropTypes.arrayOf(PropTypes.shape({
|
||||
@@ -72,9 +71,6 @@ GroupFeedbackRow.propTypes = {
|
||||
answers: PropTypes.arrayOf(PropTypes.string),
|
||||
feedback: PropTypes.string,
|
||||
}).isRequired,
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export const GroupFeedbackRowInternal = GroupFeedbackRow; // For testing only
|
||||
export default injectIntl(GroupFeedbackRow);
|
||||
export default GroupFeedbackRow;
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import { formatMessage } from '../../../../../../../testUtils';
|
||||
import { GroupFeedbackRowInternal as GroupFeedbackRow } from './GroupFeedbackRow';
|
||||
|
||||
jest.mock('@openedx/paragon', () => ({
|
||||
...jest.requireActual('@openedx/paragon'),
|
||||
Row: 'Row',
|
||||
IconButton: 'IconButton',
|
||||
Icon: 'Icon',
|
||||
Form: {
|
||||
CheckboxSet: 'Form.CheckboxSet',
|
||||
Checkbox: 'Form.CheckboxSet',
|
||||
Control: 'Form.Control',
|
||||
},
|
||||
ActionRow: 'ActionRow',
|
||||
}));
|
||||
jest.mock('@openedx/paragon/icons', () => ({
|
||||
...jest.requireActual('@openedx/paragon/icons'),
|
||||
DeleteOutline: 'DeleteOutline',
|
||||
}));
|
||||
|
||||
describe('GroupFeedbackRow', () => {
|
||||
const props = {
|
||||
value: { answers: ['A', 'C'], feedback: 'sOmE FeEDBACK' },
|
||||
answers: ['A', 'B', 'C', 'D'],
|
||||
handleAnswersSelectedChange: jest.fn().mockName('handleAnswersSelectedChange'),
|
||||
handleFeedbackChange: jest.fn().mockName('handleFeedbackChange'),
|
||||
handleDelete: jest.fn().mockName('handleDelete'),
|
||||
intl: { formatMessage },
|
||||
};
|
||||
|
||||
describe('snapshot', () => {
|
||||
test('snapshot: renders hints row', () => {
|
||||
expect(shallow(<GroupFeedbackRow {...props} />).snapshot).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
render,
|
||||
screen,
|
||||
initializeMocks,
|
||||
fireEvent,
|
||||
} from '@src/testUtils';
|
||||
import GroupFeedbackRow from './GroupFeedbackRow';
|
||||
|
||||
describe('GroupFeedbackRow', () => {
|
||||
const answers = [
|
||||
{ id: '1', text: 'Answer 1', isCorrect: false },
|
||||
{ id: '2', text: 'Answer 2', isCorrect: true },
|
||||
{ id: '3', text: 'Answer 3', isCorrect: false },
|
||||
{ id: '4', text: 'Answer 4', isCorrect: false },
|
||||
];
|
||||
const props = {
|
||||
value: { id: 1, answers: answers.map(a => a.id), feedback: 'sOmE FeEDBACK' },
|
||||
answers,
|
||||
handleAnswersSelectedChange: jest.fn(),
|
||||
handleFeedbackChange: jest.fn(),
|
||||
handleDelete: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
initializeMocks();
|
||||
});
|
||||
|
||||
test('renders component and anwers', () => {
|
||||
render(<GroupFeedbackRow {...props} />);
|
||||
expect(screen.getByRole('button', { name: 'Delete answer' })).toBeInTheDocument();
|
||||
const options = screen.getAllByRole('checkbox');
|
||||
expect(options).toHaveLength(answers.length);
|
||||
});
|
||||
|
||||
test('handles delete answear correctly', () => {
|
||||
const mockDelete = jest.fn();
|
||||
render(<GroupFeedbackRow {...props} handleDelete={mockDelete} />);
|
||||
const button = screen.getByRole('button', { name: 'Delete answer' });
|
||||
fireEvent.click(button);
|
||||
expect(mockDelete).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('handles selected answer change correctly', () => {
|
||||
const handleAnswersSelectedChangeMock = jest.fn();
|
||||
render(<GroupFeedbackRow {...props} handleAnswersSelectedChange={handleAnswersSelectedChangeMock} />);
|
||||
const checkbox2 = screen.getByRole('checkbox', { name: '2' });
|
||||
fireEvent.click(checkbox2);
|
||||
expect(handleAnswersSelectedChangeMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('handles feedback change correctly', () => {
|
||||
const handleFeedbackChangeMock = jest.fn();
|
||||
render(<GroupFeedbackRow {...props} handleFeedbackChange={handleFeedbackChangeMock} />);
|
||||
const feedbackInput = screen.getByRole('textbox');
|
||||
fireEvent.change(feedbackInput, { target: { value: 'New feedback' } });
|
||||
expect(handleFeedbackChangeMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,77 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`GroupFeedbackRow snapshot snapshot: renders hints row 1`] = `
|
||||
<div
|
||||
className="mb-4"
|
||||
>
|
||||
<ActionRow
|
||||
className="mb-2"
|
||||
>
|
||||
<Form.Control
|
||||
onChange={[MockFunction handleFeedbackChange]}
|
||||
value="sOmE FeEDBACK"
|
||||
/>
|
||||
<div
|
||||
className="d-flex flex-row flex-nowrap"
|
||||
>
|
||||
<IconButton
|
||||
alt="Delete answer"
|
||||
iconAs="Icon"
|
||||
onClick={[MockFunction handleDelete]}
|
||||
src="DeleteOutline"
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
</ActionRow>
|
||||
<Form.CheckboxSet
|
||||
onChange={[MockFunction handleAnswersSelectedChange]}
|
||||
value={
|
||||
[
|
||||
"A",
|
||||
"C",
|
||||
]
|
||||
}
|
||||
>
|
||||
<Row
|
||||
className="mx-0"
|
||||
>
|
||||
<Form.CheckboxSet
|
||||
checked={-1}
|
||||
className="mr-4 mt-1"
|
||||
isValid={false}
|
||||
>
|
||||
<div
|
||||
className="x-small"
|
||||
/>
|
||||
</Form.CheckboxSet>
|
||||
<Form.CheckboxSet
|
||||
checked={-1}
|
||||
className="mr-4 mt-1"
|
||||
isValid={false}
|
||||
>
|
||||
<div
|
||||
className="x-small"
|
||||
/>
|
||||
</Form.CheckboxSet>
|
||||
<Form.CheckboxSet
|
||||
checked={-1}
|
||||
className="mr-4 mt-1"
|
||||
isValid={false}
|
||||
>
|
||||
<div
|
||||
className="x-small"
|
||||
/>
|
||||
</Form.CheckboxSet>
|
||||
<Form.CheckboxSet
|
||||
checked={-1}
|
||||
className="mr-4 mt-1"
|
||||
isValid={false}
|
||||
>
|
||||
<div
|
||||
className="x-small"
|
||||
/>
|
||||
</Form.CheckboxSet>
|
||||
</Row>
|
||||
</Form.CheckboxSet>
|
||||
</div>
|
||||
`;
|
||||
@@ -18,7 +18,7 @@ exports[`HintsCard snapshot snapshot: renders groupFeedbacks setting card multip
|
||||
id="authoring.problemeditor.settings.GroupFeedbackInputLabel"
|
||||
/>
|
||||
</div>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
<GroupFeedbackRow
|
||||
answers={
|
||||
[
|
||||
"A",
|
||||
@@ -41,7 +41,7 @@ exports[`HintsCard snapshot snapshot: renders groupFeedbacks setting card multip
|
||||
}
|
||||
}
|
||||
/>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
<GroupFeedbackRow
|
||||
answers={
|
||||
[
|
||||
"A",
|
||||
@@ -131,7 +131,7 @@ exports[`HintsCard snapshot snapshot: renders groupFeedbacks setting card one gr
|
||||
id="authoring.problemeditor.settings.GroupFeedbackInputLabel"
|
||||
/>
|
||||
</div>
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
<GroupFeedbackRow
|
||||
answers={
|
||||
[
|
||||
"A",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { injectIntl, FormattedMessage, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { Form } from '@openedx/paragon';
|
||||
import PropTypes from 'prop-types';
|
||||
import SettingsOption from '../SettingsOption';
|
||||
@@ -9,9 +9,8 @@ import { timerCardHooks } from '../hooks';
|
||||
const TimerCard = ({
|
||||
timeBetween,
|
||||
updateSettings,
|
||||
// inject
|
||||
intl,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const { handleChange } = timerCardHooks(updateSettings);
|
||||
|
||||
return (
|
||||
@@ -40,8 +39,6 @@ const TimerCard = ({
|
||||
TimerCard.propTypes = {
|
||||
timeBetween: PropTypes.number.isRequired,
|
||||
updateSettings: PropTypes.func.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export const TimerCardInternal = TimerCard; // For testing only
|
||||
export default injectIntl(TimerCard);
|
||||
export default TimerCard;
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import 'CourseAuthoring/editors/setupEditorTest';
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import { formatMessage } from '../../../../../../testUtils';
|
||||
import { TimerCardInternal as TimerCard } from './TimerCard';
|
||||
import { timerCardHooks } from '../hooks';
|
||||
|
||||
jest.mock('../hooks', () => ({
|
||||
timerCardHooks: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('TimerCard', () => {
|
||||
const props = {
|
||||
timeBetween: 5,
|
||||
updateSettings: jest.fn().mockName('args.updateSettings'),
|
||||
intl: { formatMessage },
|
||||
};
|
||||
|
||||
const timerCardHooksProps = {
|
||||
handleChange: jest.fn().mockName('timerCardHooks.handleChange'),
|
||||
};
|
||||
|
||||
timerCardHooks.mockReturnValue(timerCardHooksProps);
|
||||
|
||||
describe('behavior', () => {
|
||||
it(' calls timerCardHooks when initialized', () => {
|
||||
shallow(<TimerCard {...props} />);
|
||||
expect(timerCardHooks).toHaveBeenCalledWith(props.updateSettings);
|
||||
});
|
||||
});
|
||||
|
||||
describe('snapshot', () => {
|
||||
test('snapshot: renders reset true setting card', () => {
|
||||
expect(shallow(<TimerCard {...props} />).snapshot).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
render, screen, initializeMocks,
|
||||
} from '@src/testUtils';
|
||||
import TimerCard from './TimerCard';
|
||||
|
||||
describe('TimerCard', () => {
|
||||
const updateSettingsMock = jest.fn().mockName('updateSettingsMock');
|
||||
const props = {
|
||||
timeBetween: 5,
|
||||
updateSettings: updateSettingsMock,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
initializeMocks();
|
||||
});
|
||||
|
||||
test('renders component', () => {
|
||||
render(<TimerCard {...props} />);
|
||||
expect(screen.getByText('Time between attempts')).toBeInTheDocument();
|
||||
expect(screen.getByText(`${props.timeBetween} seconds`)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TimerCard snapshot snapshot: renders reset true setting card 1`] = `
|
||||
<SettingsOption
|
||||
className=""
|
||||
extraSections={[]}
|
||||
hasExpandableTextArea={false}
|
||||
summary="5 seconds"
|
||||
title="Time between attempts"
|
||||
>
|
||||
<div
|
||||
className="spacedMessage"
|
||||
>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
defaultMessage="Seconds a student must wait between submissions for a problem with multiple attempts."
|
||||
description="Timer settings card text"
|
||||
id="authoring.problemeditor.settings.timer.text"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
floatingLabel="Seconds"
|
||||
min={0}
|
||||
onChange={[MockFunction timerCardHooks.handleChange]}
|
||||
type="number"
|
||||
value={5}
|
||||
/>
|
||||
</Form.Group>
|
||||
</SettingsOption>
|
||||
`;
|
||||
Reference in New Issue
Block a user