From 042246be86f0b226c5439801effe58669c74087a Mon Sep 17 00:00:00 2001 From: Raymond Zhou <56318341+rayzhou-bit@users.noreply.github.com> Date: Fri, 25 Feb 2022 12:15:13 -0500 Subject: [PATCH] Test: editor header component (#20) * test: header tests start * test: EditableHeader test ready for review 1 * test: editor header tests complete * test: fixing up nits * test: ben's corrections --- .../EditorHeader/EditableHeader.test.jsx | 29 +++++ .../components/EditorHeader/HeaderTitle.jsx | 9 +- .../EditorHeader/HeaderTitle.test.jsx | 86 +++++++++++++++ .../EditableHeader.test.jsx.snap | 37 +++++++ .../__snapshots__/HeaderTitle.test.jsx.snap | 45 ++++++++ .../__snapshots__/index.test.jsx.snap | 24 +++++ src/editors/components/EditorHeader/index.jsx | 6 +- .../components/EditorHeader/index.test.jsx | 100 +++++++++--------- .../containers/TextEditor/TextEditor.jsx | 8 +- src/setupTest.js | 4 +- 10 files changed, 284 insertions(+), 64 deletions(-) create mode 100644 src/editors/components/EditorHeader/EditableHeader.test.jsx create mode 100644 src/editors/components/EditorHeader/HeaderTitle.test.jsx create mode 100644 src/editors/components/EditorHeader/__snapshots__/EditableHeader.test.jsx.snap create mode 100644 src/editors/components/EditorHeader/__snapshots__/HeaderTitle.test.jsx.snap create mode 100644 src/editors/components/EditorHeader/__snapshots__/index.test.jsx.snap diff --git a/src/editors/components/EditorHeader/EditableHeader.test.jsx b/src/editors/components/EditorHeader/EditableHeader.test.jsx new file mode 100644 index 000000000..31cad7204 --- /dev/null +++ b/src/editors/components/EditorHeader/EditableHeader.test.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { Icon, Form } from '@edx/paragon'; +import { Edit } from '@edx/paragon/icons'; +import * as module 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', + }; + let el; + beforeEach(() => { + el = shallow(); + }); + + describe('snapshot', () => { + test('snapshot', () => { + expect(el).toMatchSnapshot(); + }); + test('displays Edit Icon', () => { + const formControl = el.find(Form.Control); + expect(formControl.props().trailingInputElement).toMatchObject(); + }); + }); +}); diff --git a/src/editors/components/EditorHeader/HeaderTitle.jsx b/src/editors/components/EditorHeader/HeaderTitle.jsx index 49c3020f1..7d0ac0939 100644 --- a/src/editors/components/EditorHeader/HeaderTitle.jsx +++ b/src/editors/components/EditorHeader/HeaderTitle.jsx @@ -7,9 +7,9 @@ import { Edit } from '@edx/paragon/icons'; import { FormattedMessage } from '@edx/frontend-platform/i18n'; import { actions, selectors } from '../../data/redux'; +import { localTitleHooks } from './hooks'; import messages from '../messages'; import EditableHeader from './EditableHeader'; -import { localTitleHooks } from './hooks'; export const HeaderTitle = ({ editorRef, @@ -19,7 +19,6 @@ export const HeaderTitle = ({ }) => { if (!isInitialized) { return ; } - console.log('HeaderTitle'); const { inputRef, isEditing, @@ -53,13 +52,13 @@ export const HeaderTitle = ({ {localTitle} ); diff --git a/src/editors/components/EditorHeader/HeaderTitle.test.jsx b/src/editors/components/EditorHeader/HeaderTitle.test.jsx new file mode 100644 index 000000000..6ff64689a --- /dev/null +++ b/src/editors/components/EditorHeader/HeaderTitle.test.jsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import * as module from './HeaderTitle'; +import { actions, selectors } from '../../data/redux'; +import { localTitleHooks } from './hooks'; + +jest.mock('../../data/redux', () => ({ + actions: { + app: { + setBlockTitle: jest.fn().mockName('actions.app.setBlockTitle'), + }, + }, + selectors: { + app: { + typeHeader: jest.fn(state => ({ typeHeader: state })), + isInitialized: jest.fn(state => ({ studioEndpointUrl: state })), + }, + }, +})); + +jest.mock('./hooks', () => ({ + localTitleHooks: jest.fn(), +})); + +describe('HeaderTitle', () => { + const props = { + editorRef: jest.fn().mockName('args.editorRef'), + isInitialized: false, + setBlockTitle: jest.fn().mockName('args.setBlockTitle'), + typeHeader: 'html', + }; + const localTitleHooksProps = { + inputRef: jest.fn().mockName('localTitleHooks.inputRef'), + isEditing: false, + handleChange: jest.fn().mockName('localTitleHooks.handleChange'), + handleKeyDown: jest.fn().mockName('localTitleHooks.handleKeyDown'), + localTitle: 'TeST LocALtitLE', + startEditing: jest.fn().mockName('localTitleHooks.startEditing'), + updateTitle: jest.fn().mockName('localTitleHooks.updateTitle'), + }; + + describe('behavior', () => { + it(' calls localTitleHooks with initialization args', () => { + localTitleHooks.mockReturnValue(localTitleHooksProps); + shallow(); + expect(localTitleHooks).toHaveBeenCalledWith({ + editorRef: props.editorRef, + setBlockTitle: props.setBlockTitle, + typeHeader: props.typeHeader, + }); + }); + }); + + describe('snapshots', () => { + test('not initialized', () => { + expect(shallow()).toMatchSnapshot(); + }); + test('initialized', () => { + localTitleHooks.mockReturnValue(hookProps); + expect(shallow()).toMatchSnapshot(); + }); + test('editing', () => { + localTitleHooks.mockReturnValue({ ...hookProps, isEditing: true }); + expect(shallow()).toMatchSnapshot(); + }); + }); + + describe('mapStateToProps', () => { + const testState = { T: 'esting', S: 'tate' }; + test('typeHeader from app.typeHeader', () => { + expect(module.mapStateToProps(testState).typeHeader) + .toEqual(selectors.app.typeHeader(testState)); + }); + test('isInitialized from app.isInitialized', () => { + expect(module.mapStateToProps(testState).isInitialized) + .toEqual(selectors.app.isInitialized(testState)); + }); + }); + + describe('mapDispatchToProps', () => { + test('setBlockTitle from actions.app.setBlockTitle', () => { + expect(module.mapDispatchToProps.setBlockTitle).toEqual(actions.app.setBlockTitle); + }); + }); +}); diff --git a/src/editors/components/EditorHeader/__snapshots__/EditableHeader.test.jsx.snap b/src/editors/components/EditorHeader/__snapshots__/EditableHeader.test.jsx.snap new file mode 100644 index 000000000..bf9018fed --- /dev/null +++ b/src/editors/components/EditorHeader/__snapshots__/EditableHeader.test.jsx.snap @@ -0,0 +1,37 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EditableHeader snapshot 1`] = ` + + + } + value="test-title-text" + /> + +`; + +exports[`EditableHeader snapshot snapshot 1`] = ` + + + } + value="test-title-text" + /> + +`; diff --git a/src/editors/components/EditorHeader/__snapshots__/HeaderTitle.test.jsx.snap b/src/editors/components/EditorHeader/__snapshots__/HeaderTitle.test.jsx.snap new file mode 100644 index 000000000..a9ef5e0c6 --- /dev/null +++ b/src/editors/components/EditorHeader/__snapshots__/HeaderTitle.test.jsx.snap @@ -0,0 +1,45 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`HeaderTitle snapshots editing 1`] = ` + +`; + +exports[`HeaderTitle snapshots initialized 1`] = ` +
+
+ TeST LocALtitLE +
+ +
+`; + +exports[`HeaderTitle snapshots not initialized 1`] = ` + +`; diff --git a/src/editors/components/EditorHeader/__snapshots__/index.test.jsx.snap b/src/editors/components/EditorHeader/__snapshots__/index.test.jsx.snap new file mode 100644 index 000000000..f806986ed --- /dev/null +++ b/src/editors/components/EditorHeader/__snapshots__/index.test.jsx.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` 1`] = ` +
+ + + + + + + + + +
+`; diff --git a/src/editors/components/EditorHeader/index.jsx b/src/editors/components/EditorHeader/index.jsx index f8f0179f9..f94db3baa 100644 --- a/src/editors/components/EditorHeader/index.jsx +++ b/src/editors/components/EditorHeader/index.jsx @@ -2,9 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { - ActionRow, IconButton, Icon, ModalDialog, -} from '@edx/paragon'; +import { ActionRow, IconButton, Icon, ModalDialog } from '@edx/paragon'; import { Close } from '@edx/paragon/icons'; import { selectors } from '../../data/redux'; @@ -12,7 +10,7 @@ import * as appHooks from '../../hooks'; import HeaderTitle from './HeaderTitle'; -const EditorHeader = ({ +export const EditorHeader = ({ returnUrl, }) => (
diff --git a/src/editors/components/EditorHeader/index.test.jsx b/src/editors/components/EditorHeader/index.test.jsx index f9acbd507..d599e27d9 100644 --- a/src/editors/components/EditorHeader/index.test.jsx +++ b/src/editors/components/EditorHeader/index.test.jsx @@ -1,58 +1,56 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import EditorHeader from './EditorHeader'; -import EditorPageContext from './EditorPageContext'; -import { ActionStates } from './data/constants'; +import { shallow } from 'enzyme'; -const locationTemp = window.location; -beforeEach(() => { - delete window.location; - window.location = { - assign: jest.fn(), - }; -}); -afterAll(() => { - window.location = locationTemp; -}); +import { IconButton } from '@edx/paragon'; +import * as module from './index'; +import { selectors } from '../../data/redux'; +import * as appHooks from '../../hooks'; -test('Rendering And Click Close Button: Does not Navigate off of Page When Loading', () => { - const title = 'An Awesome Block'; - const context = { - unitUrlLoading: ActionStates.IN_PROGRESS, - }; - render( - - - , - ); - expect(screen.getByText(title)).toBeTruthy(); - expect(screen.getByLabelText('Close')).toBeTruthy(); - userEvent.click(screen.getByLabelText('Close')); - expect(window.location.assign).not.toHaveBeenCalled(); -}); +jest.mock('.', () => ({ + __esModule: true, // Use it when dealing with esModules + ...jest.requireActual('./index'), + handleCloseClicked: jest.fn(args => ({ handleCloseClicked: args })), +})); -test('Rendering And Click Button: Loaded Navigates Away', () => { - const title = 'An Awesome Block'; - const context = { - unitUrlLoading: ActionStates.FINISHED, - unitUrl: { - data: { - ancestors: - [ - { id: 'fakeblockid' }, - ], - }, +jest.mock('../../data/redux', () => ({ + selectors: { + app: { + returnUrl: jest.fn().mockName('actions.app.returnUrl'), }, - studioEndpointUrl: 'Testurl', + }, +})); + +jest.mock('../../hooks', () => ({ + navigateCallback: jest.fn(), +})); + +jest.mock('./HeaderTitle', () => 'HeaderTitle'); + +describe('Editor Header index', () => { + const props = { + returnUrl: 'TeST-ReTurNurL', }; - render( - - - , - ); - expect(screen.getByText(title)).toBeTruthy(); - expect(screen.getByLabelText('Close')).toBeTruthy(); - userEvent.click(screen.getByLabelText('Close')); - expect(window.location.assign).toHaveBeenCalled(); + const { EditorHeader } = module; + let el; + el = shallow(); + + describe('behavior', () => { + test('IconButton onClick calls navigateCallback', () => { + const iconButtonControl = el.find(IconButton); + iconButtonControl.simulate('click'); + expect(appHooks.navigateCallback).toHaveBeenCalledWith(props.returnUrl); + }); + }); + + describe('snapshot', () => { + expect(el).toMatchSnapshot(); + }); + + describe('mapStateToProps', () => { + const testState = { T: 'est', S: 'tate' }; + test('returnUrl from app.returnUrl', () => { + expect(module.mapStateToProps(testState).returnUrl) + .toEqual(selectors.app.returnUrl(testState)); + }); + }); }); diff --git a/src/editors/containers/TextEditor/TextEditor.jsx b/src/editors/containers/TextEditor/TextEditor.jsx index c4b9a4163..bb8727e9f 100644 --- a/src/editors/containers/TextEditor/TextEditor.jsx +++ b/src/editors/containers/TextEditor/TextEditor.jsx @@ -40,7 +40,6 @@ export const TextEditor = ({ blockFinished, initializeEditor, }) => { - console.log({ blockValue, blockFailed, blockFinished, test: 1 }); const { isOpen, openModal, closeModal } = modalToggle(); return ( @@ -61,7 +60,10 @@ export const TextEditor = ({
) : ( - + )} ); @@ -87,7 +89,7 @@ export const mapStateToProps = (state) => ({ }); export const mapDispatchToProps = { - initializeEditor: actions.app.initializeEditor, + initializeEditor: actions.app.initializeEditor, }; export default connect(mapStateToProps, mapDispatchToProps)(TextEditor); diff --git a/src/setupTest.js b/src/setupTest.js index be8991ff4..d13ce2788 100644 --- a/src/setupTest.js +++ b/src/setupTest.js @@ -58,7 +58,9 @@ jest.mock('@edx/frontend-platform/i18n', () => { }); jest.mock('@edx/paragon', () => jest.requireActual('testUtils').mockNestedComponents({ - ActionRow: 'ActionRow', + ActionRow: { + Spacer: 'ActionRow.Spacer', + }, Button: 'Button', Icon: 'Icon', IconButton: 'IconButton',