From ca85ca8e4b62a018787d9667f0f94b9207a69c97 Mon Sep 17 00:00:00 2001 From: jacobo-dominguez-wgu Date: Thu, 12 Jun 2025 15:12:28 -0600 Subject: [PATCH] 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 --- src/editors/VideoSelector.test.tsx | 23 +- .../AnswerWidget/AnswersContainer.test.jsx | 165 ------------ .../AnswerWidget/AnswersContainer.test.tsx | 65 +++++ .../AnswersContainer.test.jsx.snap | 238 ------------------ .../settingsComponents/TypeRow.test.jsx | 60 ----- .../settingsComponents/TypeRow.test.tsx | 68 +++++ .../__snapshots__/TypeRow.test.jsx.snap | 82 ------ .../components/EditProblemView/index.test.jsx | 30 +-- .../__snapshots__/index.test.tsx.snap | 61 ----- .../SelectTypeWrapper/index.test.tsx | 103 ++++++-- .../SelectTypeModal/content/Preview.jsx | 11 +- .../SelectTypeModal/content/Preview.test.jsx | 45 ---- .../SelectTypeModal/content/Preview.test.tsx | 48 ++++ .../__snapshots__/Preview.test.jsx.snap | 233 ----------------- .../components/SelectTypeModal/index.test.tsx | 13 +- .../LicenseWidget/LicenseDisplay.jsx | 4 +- .../LicenseWidget/LicenseDisplay.test.jsx | 47 ---- .../LicenseWidget/LicenseDisplay.test.tsx | 75 ++++++ .../LicenseDisplay.test.jsx.snap | 130 ---------- .../__snapshots__/index.test.jsx.snap | 6 +- src/editors/editorTestRender.jsx | 28 +++ 21 files changed, 384 insertions(+), 1151 deletions(-) delete mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.tsx delete mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap delete mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.tsx delete mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeRow.test.jsx.snap delete mode 100644 src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/index.test.tsx.snap delete mode 100644 src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.jsx create mode 100644 src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.tsx delete mode 100644 src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap delete mode 100644 src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.test.jsx create mode 100644 src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.test.tsx delete mode 100644 src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/LicenseDisplay.test.jsx.snap create mode 100644 src/editors/editorTestRender.jsx diff --git a/src/editors/VideoSelector.test.tsx b/src/editors/VideoSelector.test.tsx index 25b5b2d17..f839bf475 100644 --- a/src/editors/VideoSelector.test.tsx +++ b/src/editors/VideoSelector.test.tsx @@ -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 }) => ( - - - {children} - - - ), -}); +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(); + editorRender(); 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(); + editorRender(); 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(); + editorRender(); expect(hooks.useInitializeApp).toHaveBeenCalledWith({ dispatch: mockDispatch, data: initData, diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx deleted file mode 100644 index c52933af2..000000000 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx +++ /dev/null @@ -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 }) => (

{defaultMessage}

), - defineMessages: m => m, - injectIntl: (args) => args, - intlShape: {}, - getLocale: jest.fn(), -})); - -jest.mock('./AnswerOption', () => function mockAnswerOption() { - return
MockAnswerOption
; -}); - -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().snapshot).toMatchSnapshot(); - }); - }); - test('snapshot: renders correctly with answers', () => { - act(() => { - expect(shallow( - , - ).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( - , - ).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( - , - ).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( - , - ).snapshot).toMatchSnapshot(); - }); - }); - - test('useAnswerContainer', async () => { - let container = null; - await act(async () => { - const wrapper = render( - , - ); - 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); - }); - }); -}); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.tsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.tsx new file mode 100644 index 000000000..69e9d23e1 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.tsx @@ -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 }) =>
AnswerOption-{answer.id}
)); +jest.mock( + '../../../../../sharedComponents/Button', + () => jest.fn(({ children, ...props }) => ), +); + +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(); + 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(); + 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(); + expect(screen.getByRole('button', { name: 'Add answer' })).toBeInTheDocument(); + expect(screen.getByText('Add answer').closest('.dropdown')).toBeInTheDocument(); + }); + + it('calls useAnswerContainer with correct args', () => { + render(); + expect(useAnswerContainer).toHaveBeenCalledWith({ + answers: defaultProps.answers, + problemType: defaultProps.problemType, + updateField: defaultProps.updateField, + }); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap deleted file mode 100644 index 9977f0dac..000000000 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap +++ /dev/null @@ -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`] = ` -
- - - - - - - - - - - - - - - -
-`; - -exports[`AnswersContainer render snapshot: numeric problems: answer range/answer select button: empty 1`] = ` -
- - - - - - - - - - - - - - -
-`; - -exports[`AnswersContainer render snapshot: numeric problems: answer range/answer select button: multiple answers disables range. 1`] = ` -
- - - - - - - - - - - - - - - - -
-`; - -exports[`AnswersContainer render snapshot: renders correct default 1`] = ` -
- -
-`; - -exports[`AnswersContainer render snapshot: renders correctly with answers 1`] = ` -
- - - -
-`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx deleted file mode 100644 index 3902fd965..000000000 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx +++ /dev/null @@ -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(); - 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().snapshot).toMatchSnapshot(); - }); - test('snapshot: renders type row setting card not selected', () => { - expect(shallow().snapshot).toMatchSnapshot(); - }); - test('snapshot: renders type row setting card last row', () => { - expect(shallow().snapshot).toMatchSnapshot(); - }); - }); -}); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.tsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.tsx new file mode 100644 index 000000000..32e887bbd --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.tsx @@ -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(); + 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(); + 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(); + expect(screen.getByText('Short Answer')).toBeInTheDocument(); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeRow.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeRow.test.jsx.snap deleted file mode 100644 index af70e8dfd..000000000 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeRow.test.jsx.snap +++ /dev/null @@ -1,82 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TypeRow snapshot snapshot: renders type row setting card 1`] = ` - - -
-
-`; - -exports[`TypeRow snapshot snapshot: renders type row setting card last row 1`] = ` - - -
-
-`; - -exports[`TypeRow snapshot snapshot: renders type row setting card not selected 1`] = ` - - -
-
-`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx index 5232c7b31..18c58fc53 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx @@ -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 }) => ( - - - {children} - - - ), -}); - describe('EditProblemView', () => { const baseProps = { problemType: 'standard', @@ -71,7 +57,7 @@ describe('EditProblemView', () => { }); it('renders standard problem widgets', () => { - render(); + editorRender(); 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(); + editorRender(); expect(screen.getByText('xml:')).toBeInTheDocument(); expect(screen.getByText('SettingsWidget')).toBeInTheDocument(); }); it('renders markdown editor with RawEditor', () => { - render(); + editorRender(); expect(screen.getByText('markdown:## Problem')).toBeInTheDocument(); }); it('shows AlertModal with correct title/body for standard', () => { - render(); + editorRender(); expect(screen.getAllByText('No correct answer has been specified.').length).toBeGreaterThan(0); }); it('calls saveBlock when save button is clicked', () => { - render(); + editorRender(); const saveBtn = screen.getByRole('button', { name: 'Ok' }); fireEvent.click(saveBtn); expect(saveBlock).toHaveBeenCalled(); @@ -110,7 +96,7 @@ describe('EditProblemView', () => { openSaveWarningModal: jest.fn(), closeSaveWarningModal, }); - render(); + editorRender(); const cancelBtn = screen.getByRole('button', { name: 'Cancel' }); fireEvent.click(cancelBtn); expect(closeSaveWarningModal).toHaveBeenCalled(); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/index.test.tsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/index.test.tsx.snap deleted file mode 100644 index 44c699487..000000000 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,61 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SelectTypeWrapper snapshot 1`] = ` - - - - -
- -
-
-
- -

- test child -

-
- - - - - - - - - -
-`; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.tsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.tsx index 8ac745605..155afb94d 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.tsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.tsx @@ -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: (

test child

), - onClose: jest.fn(), - selected: 'iMAsElecTedValUE', - }; + const mockOnClose = jest.fn(); - test('snapshot', () => { - expect(shallow().snapshot).toMatchSnapshot(); + beforeEach(() => { + initializeMocks(); }); - describe('behavior', () => { - let el; - beforeEach(() => { - el = shallow(); - }); - 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( + +
Child Content
+
, + ); + expect(screen.getByText('Child Content')).toBeInTheDocument(); + }); + + it('calls onClose when close button is clicked', () => { + editorRender( + +
+ , + ); + fireEvent.click(screen.getByLabelText('Exit the editor')); + expect(mockOnClose).toHaveBeenCalled(); + }); + + it('calls onClose when cancel button is clicked', () => { + editorRender( + +
+ , + ); + 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( + +
+ , + ); + 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( + +
+ , + ); + const selectBtn = screen.getByRole('button', { name: 'Select' }); + expect(selectBtn).toBeDisabled(); + }); + + it('enables select button when selected is not empty', () => { + editorRender( + +
+ , + ); + const selectBtn = screen.getByRole('button', { name: 'Select' }); + expect(selectBtn).not.toBeDisabled(); }); }); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.jsx index 7077c18a5..71698b941 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.jsx @@ -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; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.jsx deleted file mode 100644 index 36cf961fc..000000000 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.jsx +++ /dev/null @@ -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().snapshot, - ).toMatchSnapshot(); - }); - test('snapshots: renders as expected with problemType is stringresponse', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); - test('snapshots: renders as expected with problemType is numericalresponse', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); - test('snapshots: renders as expected with problemType is optionresponse', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); - test('snapshots: renders as expected with problemType is choiceresponse', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); - test('snapshots: renders as expected with problemType is multiplechoiceresponse', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); - }); -}); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.tsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.tsx new file mode 100644 index 000000000..acb338435 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.tsx @@ -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(); + const reduxProviderDiv = container.querySelector('div[data-testid="redux-provider"]'); + expect(reduxProviderDiv?.innerHTML).toBe(''); + }); + + it('renders preview with correct data for a valid problemType', () => { + render(); + 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(); + 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(); + expect(screen.getByText('Example Title problem')).toBeInTheDocument(); + expect(screen.getByText('Example description')).toBeInTheDocument(); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap deleted file mode 100644 index 4bd50f084..000000000 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap +++ /dev/null @@ -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`] = ` - -
- Multi-select problem -
- A preview illustration of a {problemType, select,
-      multiplechoiceresponse {single select}
-      stringreponse {text input}
-      numericalresponse {numerical input}
-      optionresponse {dropdown}
-      choiceresponse {multiple select}
-      other {null}
-    } problem -
- Learners must select all correct answers from a list of possible options. -
- - - -
-`; - -exports[`Preview snapshots snapshots: renders as expected with problemType is multiplechoiceresponse 1`] = ` - -
- Single select problem -
- A preview illustration of a {problemType, select,
-      multiplechoiceresponse {single select}
-      stringreponse {text input}
-      numericalresponse {numerical input}
-      optionresponse {dropdown}
-      choiceresponse {multiple select}
-      other {null}
-    } problem -
- Learners must select the correct answer from a list of possible options. -
- - - -
-`; - -exports[`Preview snapshots snapshots: renders as expected with problemType is numericalresponse 1`] = ` - -
- Numerical input problem -
- A preview illustration of a {problemType, select,
-      multiplechoiceresponse {single select}
-      stringreponse {text input}
-      numericalresponse {numerical input}
-      optionresponse {dropdown}
-      choiceresponse {multiple select}
-      other {null}
-    } problem -
- Specify one or more correct numeric answers, submitted in a response field. -
- - - -
-`; - -exports[`Preview snapshots snapshots: renders as expected with problemType is optionresponse 1`] = ` - -
- Dropdown problem -
- A preview illustration of a {problemType, select,
-      multiplechoiceresponse {single select}
-      stringreponse {text input}
-      numericalresponse {numerical input}
-      optionresponse {dropdown}
-      choiceresponse {multiple select}
-      other {null}
-    } problem -
- Learners must select the correct answer from a list of possible options -
- - - -
-`; - -exports[`Preview snapshots snapshots: renders as expected with problemType is stringresponse 1`] = ` - -
- Text input problem -
- A preview illustration of a {problemType, select,
-      multiplechoiceresponse {single select}
-      stringreponse {text input}
-      numericalresponse {numerical input}
-      optionresponse {dropdown}
-      choiceresponse {multiple select}
-      other {null}
-    } problem -
- Specify one or more correct text answers, including numbers and special characters, submitted in a response field. -
- - - -
-`; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.tsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.tsx index 2cef8c36b..ade0843a0 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.tsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.tsx @@ -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( - - - - - , + editorRender( + , ); // First we see the menu of problem types: diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.jsx index a67d40607..52f4b3e0a 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.jsx @@ -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; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.test.jsx deleted file mode 100644 index 111169499..000000000 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.test.jsx +++ /dev/null @@ -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().snapshot, - ).toMatchSnapshot(); - }); - test('snapshots: renders as expected with level set to library', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); - test('snapshots: renders as expected with level set to block', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); - test('snapshots: renders as expected with level set to block and license set to select', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); - test('snapshots: renders as expected with level set to block and license set to Creative Commons', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); - }); -}); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.test.tsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.test.tsx new file mode 100644 index 000000000..30968f7c8 --- /dev/null +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.test.tsx @@ -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( + , + ); + const reduxProviderDiv = container.querySelector('div[data-testid="redux-provider"]'); + expect(reduxProviderDiv?.innerHTML).toBe(''); + }); + + it('renders displaySubsectionTitle and licenseDescription for non-select license', () => { + render( + , + ); + expect(screen.getByText('License Display')).toBeInTheDocument(); + expect(screen.getByText('Proprietary license description')).toBeInTheDocument(); + }); + + it('renders Hyperlink for creativeCommons license', () => { + render( + , + ); + 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( + , + ); + expect(screen.queryByRole('link', { name: 'View Details' })).toBeNull(); + }); +}); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/LicenseDisplay.test.jsx.snap b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/LicenseDisplay.test.jsx.snap deleted file mode 100644 index 655802d54..000000000 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/LicenseDisplay.test.jsx.snap +++ /dev/null @@ -1,130 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`LicenseDisplay snapshots snapshots: renders as expected with default props 1`] = ` - -
- -
-
- -
- FormattedMessage component with license description -
-
-
-`; - -exports[`LicenseDisplay snapshots snapshots: renders as expected with level set to block 1`] = ` - -
- -
-
- -
- FormattedMessage component with license description -
-
-
-`; - -exports[`LicenseDisplay snapshots snapshots: renders as expected with level set to block and license set to Creative Commons 1`] = ` - -
- -
-
- -
- FormattedMessage component with license description -
-
- - - -
-`; - -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`] = ` - -
- -
-
- -
- FormattedMessage component with license description -
-
-
-`; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/index.test.jsx.snap b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/index.test.jsx.snap index cfc25e06a..d63bc7705 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/index.test.jsx.snap @@ -34,7 +34,7 @@ exports[`LicenseWidget snapshots snapshots: renders as expected with default pro level="course" license="all-rights-reserved" /> - - - baseRender(ui, { + extraWrapper: ({ children }) => ( + + + {children} + + + ), +}); + +export default editorRender;