diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.jsx index 31fa88ca6..1a7ea9f03 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import { useIntl } from '@edx/frontend-platform/i18n'; import { ActionRow, Container, @@ -19,34 +19,35 @@ const HintRow = ({ images, isLibrary, learningContextId, - // injected - intl, -}) => ( - - - - -
- -
-
-); +}) => { + const intl = useIntl(); + return ( + + + + +
+ +
+
+ ); +}; HintRow.propTypes = { value: PropTypes.string.isRequired, @@ -56,9 +57,6 @@ HintRow.propTypes = { images: PropTypes.shape({}).isRequired, learningContextId: PropTypes.string.isRequired, isLibrary: PropTypes.bool.isRequired, - // injected - intl: intlShape.isRequired, }; -export const HintRowInternal = HintRow; // For testing only -export default injectIntl(HintRow); +export default HintRow; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.test.jsx deleted file mode 100644 index 89ec54b23..000000000 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.test.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import 'CourseAuthoring/editors/setupEditorTest'; -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../testUtils'; -import { HintRowInternal as HintRow } from './HintRow'; - -describe('HintRow', () => { - const props = { - value: 'hint_1', - handleChange: jest.fn(), - handleDelete: jest.fn(), - id: '0', - intl: { formatMessage }, - images: {}, - isLibrary: false, - learningContextId: 'course+org+run', - }; - - describe('snapshot', () => { - test('snapshot: renders hints row', () => { - expect(shallow().snapshot).toMatchSnapshot(); - }); - }); -}); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.test.tsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.test.tsx new file mode 100644 index 000000000..b16fcfd87 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.test.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { + render, screen, initializeMocks, fireEvent, +} from '@src/testUtils'; +import HintRow from './HintRow'; + +jest.mock('../../../../../../sharedComponents/ExpandableTextArea', () => 'ExpandableTextArea'); + +describe('HintRow', () => { + const props = { + value: 'hint_1', + handleChange: jest.fn(), + handleDelete: jest.fn(), + id: '0', + images: {}, + isLibrary: false, + learningContextId: 'course+org+run', + }; + + beforeEach(() => { + initializeMocks(); + }); + + test('renders hints row', () => { + render(); + expect(screen.getByPlaceholderText('Hint')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Delete answer' })).toBeInTheDocument(); + }); + + test('calls handleDelete when button is clicked', () => { + render(); + expect(screen.getByRole('button', { name: 'Delete answer' })).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: 'Delete answer' })); + expect(props.handleDelete).toHaveBeenCalled(); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/HintRow.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/HintRow.test.jsx.snap deleted file mode 100644 index b557d1dc1..000000000 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/HintRow.test.jsx.snap +++ /dev/null @@ -1,34 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`HintRow snapshot snapshot: renders hints row 1`] = ` - - - - -
- -
-
-`; 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 index 7e7fe0fc0..64eff3fdc 100644 --- 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 @@ -9,14 +9,14 @@ exports[`HintsCard snapshot snapshot: renders hints setting card multiple hints summary=" {count, plural, =0 {} other {(+# more)}}" title="Hints" > - - - { widgetWithError: [{ err1: 'mSg', err2: 'msG2' }, jest.fn()], widgetWithNoError: [{}, jest.fn()], }; + beforeEach(() => { + initializeMocks(); + }); + afterEach(() => { jest.restoreAllMocks(); }); - describe('render', () => { - beforeEach(() => { - jest.spyOn(React, 'useContext').mockReturnValueOnce({}); - }); - test('snapshots: renders as expected when there are no errors', () => { + + describe('renders', () => { + jest.spyOn(React, 'useContext').mockReturnValueOnce({}); + test('renders as expected when there are no errors', () => { jest.spyOn(mod, 'showAlert').mockReturnValue(false); - expect(shallow().snapshot).toMatchSnapshot(); + render(); + expect(screen.queryByRole('alert')).not.toBeInTheDocument(); }); + test('snapshots: renders as expected when there are errors', () => { jest.spyOn(mod, 'showAlert').mockReturnValue(true); - expect(shallow().snapshot).toMatchSnapshot(); + render(); + expect(screen.getByRole('alert')).toBeInTheDocument(); }); }); + describe('hasNoError', () => { it('returns true', () => { expect(mod.hasNoError(errors.widgetWithError)).toEqual(false); @@ -35,6 +43,7 @@ describe('ErrorSummary', () => { expect(mod.hasNoError(errors.widgetWithNoError)).toEqual(true); }); }); + describe('showAlert', () => { it('returns true', () => { jest.spyOn(mod, 'hasNoError').mockReturnValue(false); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/__snapshots__/ErrorSummary.test.tsx.snap b/src/editors/containers/VideoEditor/components/VideoSettingsModal/__snapshots__/ErrorSummary.test.tsx.snap deleted file mode 100644 index 88baf91bd..000000000 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/__snapshots__/ErrorSummary.test.tsx.snap +++ /dev/null @@ -1,45 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ErrorSummary render snapshots: renders as expected when there are errors 1`] = ` - - - - -

- -

-
-`; - -exports[`ErrorSummary render snapshots: renders as expected when there are no errors 1`] = ` - - - - -

- -

-
-`; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx index 90d3ec235..ad441c562 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx @@ -10,16 +10,11 @@ import { import { Check } from '@openedx/paragon/icons'; import { connect, useDispatch } from 'react-redux'; -import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import { useIntl } from '@edx/frontend-platform/i18n'; import { thunkActions, selectors } from '../../../../../../data/redux'; import { videoTranscriptLanguages } from '../../../../../../data/constants/video'; import { FileInput, fileInput } from '../../../../../../sharedComponents/FileInput'; import messages from './messages'; -// This 'module' self-import hack enables mocking during tests. -// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested -// should be re-thought and cleaned up to avoid this pattern. -// eslint-disable-next-line import/no-self-import -import * as module from './LanguageSelector'; export const hooks = { onSelectLanguage: ({ @@ -54,13 +49,11 @@ const LanguageSelector = ({ language, // Redux openLanguages, // Only allow those languages not already associated with a transcript to be selected - // intl - intl, - }) => { + const intl = useIntl(); const [localLang, setLocalLang] = React.useState(language); const input = fileInput({ onAddFile: hooks.addFileCallback({ dispatch: useDispatch(), localLang }) }); - const onLanguageChange = module.hooks.onSelectLanguage({ + const onLanguageChange = hooks.onSelectLanguage({ dispatch: useDispatch(), languageBeforeChange: localLang, setLocalLang, triggerupload: input.click, }); @@ -124,7 +117,6 @@ LanguageSelector.propTypes = { openLanguages: PropTypes.arrayOf(PropTypes.string), index: PropTypes.number.isRequired, language: PropTypes.string.isRequired, - intl: intlShape.isRequired, }; export const mapStateToProps = (state) => ({ @@ -134,4 +126,4 @@ export const mapStateToProps = (state) => ({ export const mapDispatchToProps = {}; export const LanguageSelectorInternal = LanguageSelector; // For testing only -export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(LanguageSelector)); +export default connect(mapStateToProps, mapDispatchToProps)(LanguageSelector); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.test.jsx index 0eae896c6..3fc95017b 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.test.jsx @@ -1,8 +1,9 @@ -import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; -import { LanguageSelectorInternal as LanguageSelector } from './LanguageSelector'; -import { formatMessage } from '../../../../../../testUtils'; +import { + render, screen, initializeMocks, fireEvent, +} from '@src/testUtils'; +import LanguageSelector from './LanguageSelector'; +import { selectors } from '../../../../../../data/redux'; const lang1 = 'kLinGon'; const lang1Code = 'kl'; @@ -21,25 +22,37 @@ jest.mock('../../../../../../data/constants/video', () => ({ describe('LanguageSelector', () => { const props = { - intl: { formatMessage }, onSelect: jest.fn().mockName('props.OnSelect'), - title: 'tITle', + index: 1, language: lang1Code, openLanguages: [[lang2Code, lang2], [lang3Code, lang3]], - }; - describe('snapshot', () => { - test('transcript option', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); + beforeEach(() => { + initializeMocks(); }); - describe('snapshots -- no', () => { - test('transcripts no Open Languages, all should be disabled', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); + + test('renders component with selected language', () => { + const { video } = selectors; + jest.spyOn(video, 'openLanguages').mockReturnValue(props.openLanguages); + const { container } = render(); + expect(screen.getByRole('button', { name: 'Languages' })).toBeInTheDocument(); + expect(screen.getByText(lang1)).toBeInTheDocument(); + expect(container.querySelector('input.upload[type="file"]')).toBeInTheDocument(); + }); + + test('renders component with no selection', () => { + const { video } = selectors; + jest.spyOn(video, 'openLanguages').mockReturnValue(props.openLanguages); + render(); + expect(screen.getByText('Select Language')).toBeInTheDocument(); + }); + + test('transcripts no Open Languages, all dropdown items should be disabled', () => { + const { video } = selectors; + jest.spyOn(video, 'openLanguages').mockReturnValue([]); + const { container } = render(); + fireEvent.click(screen.getByRole('button', { name: 'Languages' })); + const disabledItems = container.querySelectorAll('.disabled.dropdown-item'); + expect(disabledItems.length).toBe(3); }); }); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/LanguageSelector.test.jsx.snap b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/LanguageSelector.test.jsx.snap deleted file mode 100644 index 281e71184..000000000 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/LanguageSelector.test.jsx.snap +++ /dev/null @@ -1,111 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`LanguageSelector snapshot transcript option 1`] = ` - - - - - kLinGon - - - - - - - kLinGon - - - - eLvIsh - - - sImLisH - - - - - -`; - -exports[`LanguageSelector snapshots -- no transcripts no Open Languages, all should be disabled 1`] = ` - - - - - kLinGon - - - - - - - kLinGon - - - - eLvIsh - - - sImLisH - - - - - -`; diff --git a/src/editors/sharedComponents/BaseModal/__snapshots__/index.test.jsx.snap b/src/editors/sharedComponents/BaseModal/__snapshots__/index.test.jsx.snap deleted file mode 100644 index f76ae5cea..000000000 --- a/src/editors/sharedComponents/BaseModal/__snapshots__/index.test.jsx.snap +++ /dev/null @@ -1,53 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`BaseModal ImageUploadModal template component snapshot 1`] = ` - - - - props.title node - - - - - props.children node - - - - - - props.footerAction node - - - - - - props.confirmAction node - - - -`; diff --git a/src/editors/sharedComponents/BaseModal/index.test.jsx b/src/editors/sharedComponents/BaseModal/index.test.jsx deleted file mode 100644 index cca9a4b57..000000000 --- a/src/editors/sharedComponents/BaseModal/index.test.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import 'CourseAuthoring/editors/setupEditorTest'; -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; - -import BaseModal from '.'; - -describe('BaseModal ImageUploadModal template component', () => { - test('snapshot', () => { - const props = { - isOpen: true, - close: jest.fn().mockName('props.close'), - title: 'props.title node', - children: 'props.children node', - confirmAction: 'props.confirmAction node', - footerAction: 'props.footerAction node', - }; - expect(shallow().snapshot).toMatchSnapshot(); - }); -}); diff --git a/src/editors/sharedComponents/BaseModal/index.test.tsx b/src/editors/sharedComponents/BaseModal/index.test.tsx new file mode 100644 index 000000000..6d3fb5d0b --- /dev/null +++ b/src/editors/sharedComponents/BaseModal/index.test.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { render, screen, initializeMocks } from '@src/testUtils'; + +import BaseModal from '.'; + +const props = { + isOpen: true, + close: jest.fn().mockName('props.close'), + title: 'title node', + children: 'children node', + confirmAction: 'confirmAction node', + footerAction: 'footerAction node', +}; + +describe('BaseModal', () => { + beforeEach(() => { + initializeMocks(); + }); + + test('renders component with all elements', () => { + render(); + expect(screen.getByText(props.title)).toBeInTheDocument(); + expect(screen.getByText(props.children)).toBeInTheDocument(); + const confirmAction = screen.getByText((content) => content.includes(props.confirmAction)); + expect(confirmAction).toBeInTheDocument(); + const footerAction = screen.getByText((content) => content.includes(props.footerAction)); + expect(footerAction).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); + }); + + test('does not render cancel buton when hideCancelButton is true', () => { + render(); + expect(screen.queryByRole('button', { name: 'Cancel' })).not.toBeInTheDocument(); + }); + + test('does not render footerAction node when not provided', () => { + render(); + const footerAction = screen.queryByText((content) => content.includes(props.footerAction)); + expect(footerAction).not.toBeInTheDocument(); + }); +}); diff --git a/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.test.jsx b/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.test.jsx deleted file mode 100644 index 09c8a7df5..000000000 --- a/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.test.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import 'CourseAuthoring/editors/setupEditorTest'; -import React from 'react'; -import { shallow } from '@edx/react-unit-test-utils'; -import UploadErrorAlert from './UploadErrorAlert'; - -jest.mock('../../data/redux', () => ({ - selectors: { - requests: { - isFailed: jest.fn((state, params) => ({ isFailed: { state, params } })), - }, - }, -})); - -describe('UploadErrorAlert', () => { - describe('Snapshots', () => { - test('snapshot: is ErrorAlert with Message error (ErrorAlert)', () => { - expect(shallow().snapshot).toMatchSnapshot(); - }); - }); -}); diff --git a/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.test.tsx b/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.test.tsx new file mode 100644 index 000000000..9671239a0 --- /dev/null +++ b/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.test.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { render, screen, initializeMocks } from '@src/testUtils'; +import UploadErrorAlert from './UploadErrorAlert'; + +const errorMessage = { + id: 'error.errorTitle', + defaultMessage: 'Example error message', + description: 'Title of message presented to user when something goes wrong', +}; + +const defaultProps = { + isUploadError: true, + message: errorMessage, +}; + +describe('UploadErrorAlert', () => { + beforeEach(() => { + initializeMocks(); + }); + + test('renders the error message', () => { + render(); + expect(screen.queryByRole('alert')).toBeInTheDocument(); + expect(screen.getByText(errorMessage.defaultMessage)).toBeInTheDocument(); + }); + + test('does not render the error message when isUploadError is false', () => { + render(); + expect(screen.queryByRole('alert')).not.toBeInTheDocument(); + expect(screen.queryByText(errorMessage.defaultMessage)).not.toBeInTheDocument(); + }); +}); diff --git a/src/editors/sharedComponents/ErrorAlerts/__snapshots__/UploadErrorAlert.test.jsx.snap b/src/editors/sharedComponents/ErrorAlerts/__snapshots__/UploadErrorAlert.test.jsx.snap deleted file mode 100644 index 68f5d8b3d..000000000 --- a/src/editors/sharedComponents/ErrorAlerts/__snapshots__/UploadErrorAlert.test.jsx.snap +++ /dev/null @@ -1,11 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`UploadErrorAlert Snapshots snapshot: is ErrorAlert with Message error (ErrorAlert) 1`] = ` - - - -`;