diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.jsx b/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.jsx
index 0483bffe5..a97bb38df 100644
--- a/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.jsx
+++ b/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.jsx
@@ -47,5 +47,4 @@ EditableHeader.propTypes = {
cancelEdit: PropTypes.func.isRequired,
};
-export const EditableHeaderInternal = EditableHeader; // For testing only
export default EditableHeader;
diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.test.jsx b/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.test.jsx
deleted file mode 100644
index 349b89b5a..000000000
--- a/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.test.jsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import 'CourseAuthoring/editors/setupEditorTest';
-import React from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
-import { Form } from '@openedx/paragon';
-import { EditableHeaderInternal as EditableHeader } from './EditableHeader';
-import EditConfirmationButtons from './EditConfirmationButtons';
-
-describe('EditableHeader', () => {
- const props = {
- handleChange: jest.fn().mockName('args.handleChange'),
- updateTitle: jest.fn().mockName('args.updateTitle'),
- handleKeyDown: jest.fn().mockName('args.handleKeyDown'),
- inputRef: jest.fn().mockName('args.inputRef'),
- localTitle: 'test-title-text',
- cancelEdit: jest.fn().mockName('args.cancelEdit'),
- };
- let el;
- beforeEach(() => {
- el = shallow();
- });
-
- describe('snapshot', () => {
- test('snapshot', () => {
- expect(el.snapshot).toMatchSnapshot();
- });
- test('displays Edit Icon', () => {
- const formControl = el.instance.findByType(Form.Control)[0];
- expect(formControl.props.trailingElement).toMatchObject(
- ,
- );
- });
- });
-});
diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.test.tsx b/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.test.tsx
new file mode 100644
index 000000000..97147ad44
--- /dev/null
+++ b/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.test.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import {
+ render, screen, initializeMocks, fireEvent,
+} from '@src/testUtils';
+import EditableHeader from './EditableHeader';
+
+describe('EditableHeader', () => {
+ const props = {
+ handleChange: jest.fn().mockName('args.handleChange'),
+ updateTitle: jest.fn().mockName('args.updateTitle'),
+ handleKeyDown: jest.fn().mockName('args.handleKeyDown'),
+ inputRef: jest.fn().mockName('args.inputRef'),
+ localTitle: 'test-title-text',
+ cancelEdit: jest.fn().mockName('args.cancelEdit'),
+ };
+
+ beforeEach(() => {
+ initializeMocks();
+ });
+
+ test('renders input with correct value and placeholder', () => {
+ render();
+ const input = screen.getByPlaceholderText('Title');
+ expect(input).toBeInTheDocument();
+ expect((input as HTMLInputElement).value).toBe(props.localTitle);
+ });
+
+ test('calls handleChange when input changes', () => {
+ render();
+ const input = screen.getByPlaceholderText('Title');
+ fireEvent.change(input, { target: { value: 'New title' } });
+ expect(props.handleChange).toHaveBeenCalled();
+ });
+
+ test('calls handleKeyDown on keydown', () => {
+ render();
+ const input = screen.getByPlaceholderText('Title');
+ fireEvent.keyDown(input, { target: { value: 'New title' } });
+ expect(props.handleKeyDown).toHaveBeenCalled();
+ });
+
+ test('calls updateTitle on blur', () => {
+ render();
+ const input = screen.getByPlaceholderText('Title');
+ fireEvent.blur(input);
+ expect(props.updateTitle).toHaveBeenCalled();
+ });
+
+ test('calls inputRef if provided', () => {
+ const inputRef = jest.fn();
+ render();
+ expect(inputRef).toHaveBeenCalled();
+ });
+
+ test('renders buttons from trailing element EditConfirmationButtons', () => {
+ render();
+ const cancelButton = screen.getByRole('button', { name: /cancel/i });
+ expect(cancelButton).toBeInTheDocument();
+ const saveButton = screen.getByRole('button', { name: /save/i });
+ expect(saveButton).toBeInTheDocument();
+ });
+});
diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/__snapshots__/EditableHeader.test.jsx.snap b/src/editors/containers/EditorContainer/components/TitleHeader/__snapshots__/EditableHeader.test.jsx.snap
deleted file mode 100644
index 0d202841d..000000000
--- a/src/editors/containers/EditorContainer/components/TitleHeader/__snapshots__/EditableHeader.test.jsx.snap
+++ /dev/null
@@ -1,26 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`EditableHeader snapshot snapshot 1`] = `
-
-
- }
- value="test-title-text"
- />
-
-`;
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.jsx
index dc7a05907..2f941cd02 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.jsx
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { injectIntl, FormattedMessage, intlShape } from '@edx/frontend-platform/i18n';
+import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
import SettingsOption from '../SettingsOption';
import { ProblemTypeKeys } from '../../../../../../data/constants/problem';
import messages from '../messages';
@@ -15,9 +15,8 @@ const HintsCard = ({
images,
isLibrary,
learningContextId,
- // inject
- intl,
}) => {
+ const intl = useIntl();
const { summary, handleAdd } = hintsCardHooks(hints, updateSettings);
if (problemType === ProblemTypeKeys.ADVANCED) { return null; }
@@ -55,7 +54,6 @@ const HintsCard = ({
};
HintsCard.propTypes = {
- intl: intlShape.isRequired,
hints: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
@@ -67,5 +65,4 @@ HintsCard.propTypes = {
isLibrary: PropTypes.bool.isRequired,
};
-export const HintsCardInternal = HintsCard; // For testing only
-export default injectIntl(HintsCard);
+export default HintsCard;
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.test.jsx
deleted file mode 100644
index 9dd5e06a4..000000000
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.test.jsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import 'CourseAuthoring/editors/setupEditorTest';
-import React from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
-import { formatMessage } from '../../../../../../testUtils';
-import { HintsCardInternal as HintsCard } from './HintsCard';
-import { hintsCardHooks, hintsRowHooks } from '../hooks';
-import messages from '../messages';
-
-jest.mock('../hooks', () => ({
- hintsCardHooks: jest.fn(),
- hintsRowHooks: jest.fn(),
-}));
-
-describe('HintsCard', () => {
- const hint1 = { id: 1, value: 'hint1' };
- const hint2 = { id: 2, value: '' };
- const hints0 = [];
- const hints1 = [hint1];
- const hints2 = [hint1, hint2];
- const props = {
- intl: { formatMessage },
- hints: hints0,
- updateSettings: jest.fn().mockName('args.updateSettings'),
- };
-
- const hintsRowHooksProps = {
- handleChange: jest.fn().mockName('hintsRowHooks.handleChange'),
- handleDelete: jest.fn().mockName('hintsRowHooks.handleDelete'),
- images: {},
- isLibrary: false,
- learningContextId: 'course+org+run',
- };
- hintsRowHooks.mockReturnValue(hintsRowHooksProps);
-
- describe('behavior', () => {
- it(' calls hintsCardHooks when initialized', () => {
- const hintsCardHooksProps = {
- summary: { message: messages.noHintSummary, values: {} },
- handleAdd: jest.fn().mockName('hintsCardHooks.handleAdd'),
- };
-
- hintsCardHooks.mockReturnValue(hintsCardHooksProps);
- shallow();
- expect(hintsCardHooks).toHaveBeenCalledWith(hints0, props.updateSettings);
- });
- });
-
- describe('snapshot', () => {
- test('snapshot: renders hints setting card no hints', () => {
- const hintsCardHooksProps = {
- summary: { message: messages.noHintSummary, values: {} },
- handleAdd: jest.fn().mockName('hintsCardHooks.handleAdd'),
- };
-
- hintsCardHooks.mockReturnValue(hintsCardHooksProps);
- expect(shallow().snapshot).toMatchSnapshot();
- });
- test('snapshot: renders hints setting card one hint', () => {
- const hintsCardHooksProps = {
- summary: {
- message: messages.hintSummary,
- values: { hint: hint1.value, count: 1 },
- },
- handleAdd: jest.fn().mockName('hintsCardHooks.handleAdd'),
- };
-
- hintsCardHooks.mockReturnValue(hintsCardHooksProps);
- expect(shallow().snapshot).toMatchSnapshot();
- });
- test('snapshot: renders hints setting card multiple hints', () => {
- const hintsCardHooksProps = {
- summary: {
- message: messages.hintSummary,
- values: { hint: hint2.value, count: 2 },
- },
- handleAdd: jest.fn().mockName('hintsCardHooks.handleAdd'),
- };
-
- hintsCardHooks.mockReturnValue(hintsCardHooksProps);
- expect(shallow().snapshot).toMatchSnapshot();
- });
- });
-});
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.test.tsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.test.tsx
new file mode 100644
index 000000000..3d13b6a71
--- /dev/null
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.test.tsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import { render, screen, initializeMocks } from '@src/testUtils';
+import HintsCard from './HintsCard';
+
+jest.mock('./HintRow', () => 'HintRow');
+
+describe('HintsCard', () => {
+ const hint1 = { id: '1', value: 'hint-1' };
+ const hint2 = { id: '2', value: 'hint-2' };
+ const hints0 = [];
+ const hints1 = [hint1];
+ const hints2 = [hint1, hint2];
+
+ const props = {
+ hints: hints0,
+ updateSettings: jest.fn().mockName('args.updateSettings'),
+ problemType: 'multiplechoiceresponse',
+ images: {},
+ isLibrary: false,
+ learningContextId: 'ID+',
+ };
+
+ beforeEach(() => {
+ initializeMocks();
+ });
+
+ describe('HintsCard', () => {
+ test('renders component', () => {
+ render();
+ expect(screen.getByText('Hints')).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Add hint' })).toBeInTheDocument();
+ });
+
+ test('does not render component when problemType is advanced', () => {
+ render();
+ expect(screen.queryByText('Hints')).not.toBeInTheDocument();
+ expect(screen.queryByText('button')).not.toBeInTheDocument();
+ });
+
+ test('renders hints setting card one hint', () => {
+ render();
+ expect(document.querySelector('hintrow[value="hint-1"]')).toBeInTheDocument();
+ });
+
+ test('snapshot: renders hints setting card multiple hints', () => {
+ render();
+ expect(document.querySelector('hintrow[value="hint-1"]')).toBeInTheDocument();
+ expect(document.querySelector('hintrow[value="hint-2"]')).toBeInTheDocument();
+ });
+ });
+});
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.jsx
index b0b56add1..6b12e552a 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.jsx
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import isNil from 'lodash/isNil';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
-import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import { Form, Hyperlink } from '@openedx/paragon';
import { selectors } from '../../../../../../data/redux';
import SettingsOption from '../SettingsOption';
@@ -13,13 +13,12 @@ const ScoringCard = ({
scoring,
defaultValue,
updateSettings,
- // inject
- intl,
// redux
studioEndpointUrl,
learningContextId,
isLibrary,
}) => {
+ const intl = useIntl();
const {
handleUnlimitedChange,
handleMaxAttemptChange,
@@ -93,7 +92,6 @@ const ScoringCard = ({
};
ScoringCard.propTypes = {
- intl: intlShape.isRequired,
// eslint-disable-next-line
scoring: PropTypes.any.isRequired,
updateSettings: PropTypes.func.isRequired,
@@ -117,5 +115,4 @@ export const mapStateToProps = (state) => ({
export const mapDispatchToProps = {};
-export const ScoringCardInternal = ScoringCard; // For testing only
-export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ScoringCard));
+export default connect(mapStateToProps, mapDispatchToProps)(ScoringCard);
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.test.jsx
index 29d8e922c..e5f632a39 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.test.jsx
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.test.jsx
@@ -1,13 +1,11 @@
-import 'CourseAuthoring/editors/setupEditorTest';
import React from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
-import { formatMessage } from '../../../../../../testUtils';
-import { scoringCardHooks } from '../hooks';
-import { ScoringCardInternal as ScoringCard } from './ScoringCard';
+import {
+ render, screen, initializeMocks, fireEvent,
+} from '@src/testUtils';
+import ScoringCard from './ScoringCard';
+import { selectors } from '../../../../../../data/redux';
-jest.mock('../hooks', () => ({
- scoringCardHooks: jest.fn(),
-}));
+const { app } = selectors;
describe('ScoringCard', () => {
const scoring = {
@@ -17,55 +15,69 @@ describe('ScoringCard', () => {
number: 5,
},
updateSettings: jest.fn().mockName('args.updateSettings'),
- intl: { formatMessage },
};
const props = {
scoring,
- intl: { formatMessage },
defaultValue: 1,
+ updateSettings: jest.fn(),
};
- const scoringCardHooksProps = {
- handleMaxAttemptChange: jest.fn().mockName('scoringCardHooks.handleMaxAttemptChange'),
- handleWeightChange: jest.fn().mockName('scoringCardHooks.handleWeightChange'),
- handleOnChange: jest.fn().mockName('scoringCardHooks.handleOnChange'),
- local: 5,
- };
-
- scoringCardHooks.mockReturnValue(scoringCardHooksProps);
-
- describe('behavior', () => {
- it(' calls scoringCardHooks when initialized', () => {
- shallow();
- expect(scoringCardHooks).toHaveBeenCalledWith(scoring, props.updateSettings, props.defaultValue);
- });
+ beforeEach(() => {
+ jest.spyOn(app, 'studioEndpointUrl').mockReturnValue('studioEndpointUrl');
+ jest.spyOn(app, 'learningContextId').mockReturnValue('learningContextId');
+ jest.spyOn(app, 'isLibrary').mockReturnValue(false);
+ initializeMocks();
});
- describe('snapshot', () => {
- test('snapshot: scoring setting card', () => {
- expect(shallow().snapshot).toMatchSnapshot();
- });
- test('snapshot: scoring setting card zero zero weight', () => {
- expect(shallow().snapshot).toMatchSnapshot();
- });
- test('snapshot: scoring setting card max attempts', () => {
- expect(shallow().snapshot).toMatchSnapshot();
- });
+ test('render the component', () => {
+ render();
+ expect(screen.getByText('Scoring')).toBeInTheDocument();
+ });
+
+ test('should not render advance settings link when isLibrary is true', () => {
+ jest.spyOn(app, 'isLibrary').mockReturnValue(true);
+ render();
+ fireEvent.click(screen.getByText('Scoring'));
+ expect(screen.queryByText('Set a default value in advanced settings')).not.toBeInTheDocument();
+ });
+
+ test('should render advance settings link when isLibrary is false', () => {
+ jest.spyOn(app, 'isLibrary').mockReturnValue(false);
+ render();
+ fireEvent.click(screen.getByText('Scoring'));
+ expect(screen.getByText('Set a default value in advanced settings')).toBeInTheDocument();
+ });
+
+ test('should call updateSettings when clicking points button', () => {
+ render();
+ fireEvent.click(screen.getByText('Scoring'));
+ const pointsButton = screen.getByRole('spinbutton', { name: 'Points' });
+ expect(pointsButton).toBeInTheDocument();
+ expect(pointsButton.value).toBe('0');
+ fireEvent.change(pointsButton, { target: { value: '0.1' } });
+ expect(props.updateSettings).toHaveBeenCalled();
+ });
+
+ test('should call updateSettings when clicking attempts button', () => {
+ const scoringUnlimited = { ...scoring, attempts: { unlimited: true, number: 0 } };
+ render();
+ fireEvent.click(screen.getByText('Scoring'));
+ fireEvent.click(screen.getByText('Attempts'));
+ const attemptsButton = screen.getByRole('spinbutton', { name: 'Points' });
+ expect(attemptsButton).toBeInTheDocument();
+ expect(attemptsButton.value).toBe('1.5');
+ fireEvent.change(attemptsButton, { target: { value: '2' } });
+ expect(props.updateSettings).toHaveBeenCalled();
+ });
+
+ test('should display checked checkbox when unlimited is true', () => {
+ const scoringUnlimited = { ...scoring, attempts: { unlimited: true, number: 0 } };
+ render();
+ fireEvent.click(screen.getByText('Scoring'));
+ const checkbox = screen.getByRole('checkbox', { name: 'Unlimited attempts' });
+ expect(checkbox).toBeChecked();
+ fireEvent.click(checkbox);
+ expect(props.updateSettings).toHaveBeenCalled();
});
});
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/HintsCard.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/HintsCard.test.jsx.snap
deleted file mode 100644
index 64eff3fdc..000000000
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/HintsCard.test.jsx.snap
+++ /dev/null
@@ -1,97 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`HintsCard snapshot snapshot: renders hints setting card multiple hints 1`] = `
-
-
-
-
-
-`;
-
-exports[`HintsCard snapshot snapshot: renders hints setting card no hints 1`] = `
-
-
-
-`;
-
-exports[`HintsCard snapshot snapshot: renders hints setting card one hint 1`] = `
-
-
-
-
-`;
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ScoringCard.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ScoringCard.test.jsx.snap
deleted file mode 100644
index 9f07b02d0..000000000
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ScoringCard.test.jsx.snap
+++ /dev/null
@@ -1,238 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ScoringCard snapshot snapshot: scoring setting card 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`;
-
-exports[`ScoringCard snapshot snapshot: scoring setting card max attempts 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`;
-
-exports[`ScoringCard snapshot snapshot: scoring setting card zero zero weight 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`;
diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.jsx b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.jsx
index 5b06c6f8f..e4ece2222 100644
--- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.jsx
+++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Form } from '@openedx/paragon';
-import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import * as hooks from './hooks';
import messages from './messages';
@@ -22,39 +22,40 @@ const AltTextControls = ({
setValue,
validation,
value,
- // inject
- intl,
-}) => (
-
-
-
-
-
- {validation.show
+}) => {
+ const intl = useIntl();
+ return (
+
+
+
+
+
+ {validation.show
&& (
)}
-
-
-
-
-
-
-);
+
+
+
+
+
+
+ );
+};
AltTextControls.propTypes = {
error: PropTypes.shape({
show: PropTypes.bool,
@@ -66,9 +67,7 @@ AltTextControls.propTypes = {
show: PropTypes.bool,
}).isRequired,
value: PropTypes.string.isRequired,
- // inject
- intl: intlShape.isRequired,
};
export const AltTextControlsInternal = AltTextControls; // For testing only
-export default injectIntl(AltTextControls);
+export default AltTextControls;
diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.test.jsx b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.test.jsx
deleted file mode 100644
index 36780669d..000000000
--- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.test.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import 'CourseAuthoring/editors/setupEditorTest';
-import React from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
-
-import { formatMessage } from '../../../testUtils';
-import { AltTextControlsInternal as AltTextControls } from './AltTextControls';
-
-jest.mock('./hooks', () => ({
- onInputChange: (handler) => ({ 'hooks.onInputChange': handler }),
- onCheckboxChange: (handler) => ({ 'hooks.onCheckboxChange': handler }),
-}));
-
-describe('AltTextControls', () => {
- const props = {
- isDecorative: true,
- value: 'props.value',
- // inject
- intl: { formatMessage },
- };
- beforeEach(() => {
- props.setValue = jest.fn().mockName('props.setValue');
- props.setIsDecorative = jest.fn().mockName('props.setIsDecorative');
- props.validation = { show: true };
- });
- describe('render', () => {
- test('snapshot: isDecorative=true errorProps.showAltTextSubmissionError=true', () => {
- expect(shallow().snapshot).toMatchSnapshot();
- });
- test('snapshot: isDecorative=true errorProps.showAltTextSubmissionError=false', () => {
- props.validation.show = false;
- expect(shallow().snapshot).toMatchSnapshot();
- });
- });
-});
diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.test.tsx b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.test.tsx
new file mode 100644
index 000000000..4c2d2b882
--- /dev/null
+++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.test.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import {
+ render, screen, initializeMocks, fireEvent,
+} from '@src/testUtils';
+import AltTextControls from './AltTextControls';
+
+describe('AltTextControls', () => {
+ const props = {
+ isDecorative: true,
+ value: 'props.value',
+ setValue: jest.fn().mockName('props.setValue'),
+ setIsDecorative: jest.fn().mockName('props.setIsDecorative'),
+ validation: { show: false },
+ error: { show: false },
+ };
+
+ beforeEach(() => {
+ initializeMocks();
+ });
+
+ test('renders component on screen', () => {
+ render();
+ expect(screen.getByRole('checkbox', { name: 'This image is decorative (no alt text required).' })).toBeInTheDocument();
+ expect(screen.getByRole('textbox', { name: 'Accessibility' })).toBeInTheDocument();
+ });
+
+ test('renders validation feedback when validation.show is true', () => {
+ const feedbackMessage = 'Enter alt text';
+ render();
+ expect(screen.getByText(feedbackMessage)).toBeInTheDocument();
+ });
+
+ test('does not render validation feedback when validation.show is false', () => {
+ const feedbackMessage = 'Enter alt text';
+ render();
+ expect(screen.queryByText(feedbackMessage)).not.toBeInTheDocument();
+ });
+
+ test('disables textbox when isDecorative is true', () => {
+ render();
+ expect(screen.getByRole('textbox', { name: 'Accessibility' })).toBeDisabled();
+ });
+
+ test('enables textbox when isDecorative is false', () => {
+ render();
+ expect(screen.getByRole('textbox', { name: 'Accessibility' })).not.toBeDisabled();
+ });
+
+ test('calls setValue on textbox change', () => {
+ render();
+ const textbox = screen.getByRole('textbox', { name: 'Accessibility' });
+ fireEvent.change(textbox, { target: { value: 'new alt text' } });
+ expect(props.setValue).toHaveBeenCalled();
+ });
+
+ test('calls setIsDecorative on checkbox change', () => {
+ render();
+ const checkbox = screen.getByRole('checkbox', { name: 'This image is decorative (no alt text required).' });
+ checkbox.click();
+ expect(props.setIsDecorative).toHaveBeenCalled();
+ });
+});
diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.jsx b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.jsx
index c05ec4fae..c9b56e07e 100644
--- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.jsx
+++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.jsx
@@ -9,7 +9,7 @@ import {
Locked,
Unlocked,
} from '@openedx/paragon/icons';
-import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import * as hooks from './hooks';
import messages from './messages';
@@ -32,42 +32,44 @@ const DimensionControls = ({
unlock,
updateDimensions,
value,
- // inject
- intl,
-}) => ((value !== null) && (
-
-
-
-
-
-
-
-
{
+ const intl = useIntl();
+ if (!value) { return null; }
+ return (
+
+
+
+
+
+
+
+
-
-
-));
+ iconAs={Icon}
+ src={isLocked ? Locked : Unlocked}
+ onClick={isLocked ? unlock : lock}
+ />
+
+
+ );
+};
DimensionControls.defaultProps = {
value: {
height: '100',
@@ -85,9 +87,6 @@ DimensionControls.propTypes = ({
lock: PropTypes.func.isRequired,
unlock: PropTypes.func.isRequired,
updateDimensions: PropTypes.func.isRequired,
- // inject
- intl: intlShape.isRequired,
});
-export const DimensionControlsInternal = DimensionControls; // For testing only
-export default injectIntl(DimensionControls);
+export default DimensionControls;
diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.test.jsx b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.test.jsx
deleted file mode 100644
index 0cf9a6d27..000000000
--- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.test.jsx
+++ /dev/null
@@ -1,141 +0,0 @@
-import 'CourseAuthoring/editors/setupEditorTest';
-import React, { useEffect } from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
-import * as paragon from '@openedx/paragon';
-import * as icons from '@openedx/paragon/icons';
-
-import {
- fireEvent, render, screen, waitFor,
-} from '@testing-library/react';
-import { formatMessage } from '../../../testUtils';
-import { DimensionControlsInternal as DimensionControls } from './DimensionControls';
-import * as hooks from './hooks';
-
-const WrappedDimensionControls = () => {
- const dimensions = hooks.dimensionHooks('altText');
-
- useEffect(() => {
- dimensions.onImgLoad({ })({ target: { naturalWidth: 1517, naturalHeight: 803 } });
- }, []);
-
- return ;
-};
-
-const UnlockedDimensionControls = () => {
- const dimensions = hooks.dimensionHooks('altText');
-
- useEffect(() => {
- dimensions.onImgLoad({ })({ target: { naturalWidth: 1517, naturalHeight: 803 } });
- dimensions.unlock();
- }, []);
-
- return ;
-};
-
-describe('DimensionControls', () => {
- describe('render', () => {
- const props = {
- lockAspectRatio: { width: 4, height: 5 },
- locked: { 'props.locked': 'lockedValue' },
- isLocked: true,
- value: { width: 20, height: 40 },
- // inject
- intl: { formatMessage },
- };
- beforeEach(() => {
- jest.spyOn(hooks, 'onInputChange').mockImplementation((handler) => ({ 'hooks.onInputChange': handler }));
- props.setWidth = jest.fn().mockName('props.setWidth');
- props.setHeight = jest.fn().mockName('props.setHeight');
- props.lock = jest.fn().mockName('props.lock');
- props.unlock = jest.fn().mockName('props.unlock');
- props.updateDimensions = jest.fn().mockName('props.updateDimensions');
- });
- afterEach(() => {
- jest.spyOn(hooks, 'onInputChange').mockRestore();
- });
- test('snapshot', () => {
- expect(shallow().snapshot).toMatchSnapshot();
- });
- test('null value: empty snapshot', () => {
- const el = shallow();
- expect(el.snapshot).toMatchSnapshot();
- expect(el.isEmptyRender()).toEqual(true);
- });
- test('unlocked dimensions', () => {
- const el = shallow();
- expect(el.snapshot).toMatchSnapshot();
- });
- });
- describe('component tests for dimensions', () => {
- beforeEach(() => {
- paragon.Form.Group = jest.fn().mockImplementation(({ children }) => (
- {children}
- ));
- paragon.Form.Label = jest.fn().mockImplementation(({ children }) => (
- {children}
- ));
- // eslint-disable-next-line no-import-assign
- paragon.Icon = jest.fn().mockImplementation(({ children }) => (
- {children}
- ));
- // eslint-disable-next-line no-import-assign
- paragon.IconButton = jest.fn().mockImplementation(({ children }) => (
- {children}
- ));
- paragon.Form.Control = jest.fn().mockImplementation(({ value, onChange, onBlur }) => (
-
- ));
- // eslint-disable-next-line no-import-assign
- icons.Locked = jest.fn().mockImplementation(() => {});
- // eslint-disable-next-line no-import-assign
- icons.Unlocked = jest.fn().mockImplementation(() => {});
- });
- afterEach(() => {
- paragon.Form.Group.mockRestore();
- paragon.Form.Label.mockRestore();
- paragon.Form.Control.mockRestore();
- paragon.Icon.mockRestore();
- paragon.IconButton.mockRestore();
- icons.Locked.mockRestore();
- icons.Unlocked.mockRestore();
- });
-
- it('renders with initial dimensions', () => {
- const { container } = render();
- const widthInput = container.querySelector('.formControl');
- expect(widthInput.value).toBe('1517');
- });
-
- it('resizes dimensions proportionally', async () => {
- const { container } = render();
- const widthInput = container.querySelector('.formControl');
- expect(widthInput.value).toBe('1517');
- fireEvent.change(widthInput, { target: { value: 758 } });
- await waitFor(() => {
- expect(container.querySelectorAll('.formControl')[0].value).toBe('758');
- });
- fireEvent.blur(widthInput);
- await waitFor(() => {
- expect(container.querySelectorAll('.formControl')[0].value).toBe('758');
- expect(container.querySelectorAll('.formControl')[1].value).toBe('401');
- });
- screen.debug();
- });
-
- it('resizes only changed dimension when unlocked', async () => {
- const { container } = render();
- const widthInput = container.querySelector('.formControl');
- expect(widthInput.value).toBe('1517');
- fireEvent.change(widthInput, { target: { value: 758 } });
- await waitFor(() => {
- expect(container.querySelectorAll('.formControl')[0].value).toBe('758');
- });
- fireEvent.blur(widthInput);
- await waitFor(() => {
- expect(container.querySelectorAll('.formControl')[0].value).toBe('758');
- expect(container.querySelectorAll('.formControl')[1].value).toBe('803');
- });
- screen.debug();
- });
- });
-});
diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.test.tsx b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.test.tsx
new file mode 100644
index 000000000..288e847ae
--- /dev/null
+++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.test.tsx
@@ -0,0 +1,111 @@
+import React, { useEffect } from 'react';
+import {
+ fireEvent, render, screen, waitFor, initializeMocks,
+} from '@src/testUtils';
+import DimensionControls from './DimensionControls';
+import * as hooks from './hooks';
+
+const WrappedDimensionControls = () => {
+ const dimensions = hooks.dimensionHooks('altText');
+
+ useEffect(() => {
+ dimensions.onImgLoad({ })({ target: { naturalWidth: 1517, naturalHeight: 803 } });
+ }, []);
+
+ return ;
+};
+
+const UnlockedDimensionControls = () => {
+ const dimensions = hooks.dimensionHooks('altText');
+
+ useEffect(() => {
+ dimensions.onImgLoad({ })({ target: { naturalWidth: 1517, naturalHeight: 803 } });
+ dimensions.unlock();
+ }, []);
+
+ return ;
+};
+
+describe('DimensionControls', () => {
+ describe('render', () => {
+ const props = {
+ lockAspectRatio: { width: 4, height: 5 },
+ locked: { 'props.locked': 'lockedValue' },
+ isLocked: true,
+ value: { width: '20', height: '40' },
+ setWidth: jest.fn(),
+ setHeight: jest.fn(),
+ lock: jest.fn(),
+ unlock: jest.fn(),
+ updateDimensions: jest.fn(),
+ };
+ beforeEach(() => {
+ jest.spyOn(hooks, 'onInputChange').mockImplementation((handler) => ({ 'hooks.onInputChange': handler }));
+ initializeMocks();
+ });
+ afterEach(() => {
+ jest.spyOn(hooks, 'onInputChange').mockRestore();
+ });
+ test('renders component', () => {
+ render();
+ expect(screen.getByText('Image Dimensions')).toBeInTheDocument();
+ });
+ test('renders nothing with null value', () => {
+ const reduxProviderWrapper = '';
+ const { container } = render();
+ expect(screen.queryByText('Image Dimensions')).not.toBeInTheDocument();
+ expect(container.innerHTML).toBe(reduxProviderWrapper);
+ expect(container.firstChild?.textContent).toBe('');
+ });
+
+ test('renders locked and unlocked icon button according to isLocked prop', () => {
+ const { rerender } = render();
+ expect(screen.getByRole('button', { name: 'lock dimensions' })).toBeInTheDocument();
+ rerender();
+ expect(screen.getByRole('button', { name: 'unlock dimensions' })).toBeInTheDocument();
+ });
+ });
+
+ describe('component tests for dimensions', () => {
+ beforeEach(() => {
+ initializeMocks();
+ });
+
+ it('renders with initial dimensions', () => {
+ const { container } = render();
+ const widthInput = container.querySelector('input.form-control');
+ expect(widthInput).not.toBeNull();
+ expect((widthInput as HTMLInputElement).value).toBe('1517');
+ });
+
+ it('resizes dimensions proportionally', async () => {
+ const { container } = render();
+ const widthInput = container.querySelector('input.form-control') as HTMLInputElement;
+ expect((widthInput as HTMLInputElement).value).toBe('1517');
+ fireEvent.change(widthInput, { target: { value: 758 } });
+ await waitFor(() => {
+ expect((container.querySelectorAll('input.form-control')[0] as HTMLInputElement).value).toBe('758');
+ });
+ fireEvent.blur(widthInput);
+ await waitFor(() => {
+ expect((container.querySelectorAll('input.form-control')[0] as HTMLInputElement).value).toBe('758');
+ expect((container.querySelectorAll('input.form-control')[1] as HTMLInputElement).value).toBe('401');
+ });
+ });
+
+ it('resizes only changed dimension when unlocked', async () => {
+ const { container } = render();
+ const widthInput = container.querySelector('input.form-control') as HTMLInputElement;
+ expect(widthInput.value).toBe('1517');
+ fireEvent.change(widthInput, { target: { value: 758 } });
+ await waitFor(() => {
+ expect((container.querySelectorAll('input.form-control')[0] as HTMLInputElement).value).toBe('758');
+ });
+ fireEvent.blur(widthInput);
+ await waitFor(() => {
+ expect((container.querySelectorAll('input.form-control')[0] as HTMLInputElement).value).toBe('758');
+ expect((container.querySelectorAll('input.form-control')[1] as HTMLInputElement).value).toBe('803');
+ });
+ });
+ });
+});
diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/__snapshots__/AltTextControls.test.jsx.snap b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/__snapshots__/AltTextControls.test.jsx.snap
deleted file mode 100644
index 7e0b029a8..000000000
--- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/__snapshots__/AltTextControls.test.jsx.snap
+++ /dev/null
@@ -1,102 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`AltTextControls render snapshot: isDecorative=true errorProps.showAltTextSubmissionError=false 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-`;
-
-exports[`AltTextControls render snapshot: isDecorative=true errorProps.showAltTextSubmissionError=true 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`;
diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/__snapshots__/DimensionControls.test.jsx.snap b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/__snapshots__/DimensionControls.test.jsx.snap
deleted file mode 100644
index 6f1bf0cdc..000000000
--- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/__snapshots__/DimensionControls.test.jsx.snap
+++ /dev/null
@@ -1,97 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`DimensionControls render null value: empty snapshot 1`] = `false`;
-
-exports[`DimensionControls render snapshot 1`] = `
-
-
-
-
-
-
-
-
-
-
-`;
-
-exports[`DimensionControls render unlocked dimensions 1`] = `
-
-
-
-
-
-
-
-
-
-
-`;