From 6ca93a729778662ccb8f478e3eb35744a4ba14cd Mon Sep 17 00:00:00 2001 From: connorhaugh <49422820+connorhaugh@users.noreply.github.com> Date: Tue, 22 Feb 2022 13:24:15 -0500 Subject: [PATCH] test: add EditorFooter tests (#16) * test: add EditorFooter tests --- .../__snapshots__/index.test.jsx.snap | 142 +++++++++++++ src/editors/components/EditorFooter/index.jsx | 5 +- .../components/EditorFooter/index.test.jsx | 187 +++++++++--------- src/editors/hooks.js | 7 +- 4 files changed, 239 insertions(+), 102 deletions(-) create mode 100644 src/editors/components/EditorFooter/__snapshots__/index.test.jsx.snap diff --git a/src/editors/components/EditorFooter/__snapshots__/index.test.jsx.snap b/src/editors/components/EditorFooter/__snapshots__/index.test.jsx.snap new file mode 100644 index 000000000..6c506e04e --- /dev/null +++ b/src/editors/components/EditorFooter/__snapshots__/index.test.jsx.snap @@ -0,0 +1,142 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EditorFooter snapshots Save Failed, error message raised 1`] = ` +
+ + + + + + + + + + +
+`; + +exports[`EditorFooter snapshots not intialized, Spinner appears and button is disabled 1`] = ` +
+ + + + + + + +
+`; + +exports[`EditorFooter snapshots renders as expected with default behavior 1`] = ` +
+ + + + + + + +
+`; diff --git a/src/editors/components/EditorFooter/index.jsx b/src/editors/components/EditorFooter/index.jsx index 728245a4e..c2b3de3ca 100644 --- a/src/editors/components/EditorFooter/index.jsx +++ b/src/editors/components/EditorFooter/index.jsx @@ -10,10 +10,11 @@ import { Toast, } from '@edx/paragon'; import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import { nullMethod, saveTextBlock, navigateCallback } from '../../hooks'; import { RequestKeys } from '../../data/constants/requests'; import { selectors, thunkActions } from '../../data/redux'; -import { saveTextBlock, navigateCallback } from '../../hooks'; + import messages from '../messages'; import * as module from '.'; @@ -30,7 +31,7 @@ export const EditorFooter = ({ }) => (
{saveFailed && ( - + )} diff --git a/src/editors/components/EditorFooter/index.test.jsx b/src/editors/components/EditorFooter/index.test.jsx index 6f2ce95c4..3711cc87d 100644 --- a/src/editors/components/EditorFooter/index.test.jsx +++ b/src/editors/components/EditorFooter/index.test.jsx @@ -1,104 +1,93 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { mount } from 'enzyme'; -import EditorFooter from './EditorFooter'; -import EditorPageContext from './EditorPageContext'; -import { ActionStates } from './data/constants'; -import EditorPageProvider from './EditorPageProvider'; -import { saveBlock } from './data/api'; +import { shallow } from 'enzyme'; +import * as module from './index'; +import { selectors, thunkActions } from '../../data/redux'; +import { RequestKeys } from '../../data/constants/requests'; +import { saveTextBlock, navigateCallback } from '../../hooks'; -const locationTemp = window.location; -beforeAll(() => { - delete window.location; - window.location = { - assign: jest.fn(), - }; -}); -afterAll(() => { - window.location = locationTemp; -}); - -jest.mock('./data/api', () => { - const originalModule = jest.requireActual('./data/api'); - // Mock the default export and named export saveBlock - return { - __esModule: true, - ...originalModule, - saveBlock: jest.fn(() => {}), - }; -}); - -jest.spyOn(React, 'useRef').mockReturnValue({ - current: { - getContent: () => '', - }, -}); - -test('Rendering: loaded', () => { - const context = { - unitUrlLoading: ActionStates.FINISHED, - }; - render( - - - , - ); - expect(screen.getByText('Cancel')).toBeTruthy(); - expect(screen.getByText('Add To Course')).toBeTruthy(); -}); - -test('Rendering: loading url', () => { - const context = { - unitUrlLoading: ActionStates.NOT_BEGUN, - }; - render( - - - , - ); - expect(screen.getByText('Cancel')).toBeTruthy(); - expect(screen.getAllByRole('button', { 'aria-label': 'Save' })).toBeTruthy(); - expect(screen.queryByText('Add To Course')).toBeNull(); -}); - -test('Navigation: Cancel', () => { - const context = { - unitUrlLoading: ActionStates.FINISHED, - unitUrl: { - data: { - ancestors: - [ - { id: 'fakeblockid' }, - ], - }, +jest.mock('../../data/redux', () => ({ + thunkActions: { + app: { + saveBlock: jest.fn().mockName('thunkActions.app.saveBlock'), }, - studioEndpointUrl: 'Testurl', - }; - render( - - - , - ); - expect(screen.getByText('Cancel')).toBeTruthy(); - userEvent.click(screen.getByText('Cancel')); - expect(window.location.assign).toHaveBeenCalled(); -}); + }, + selectors: { + app: { + isInitialized: jest.fn(state => ({ isInitialized: state })), + studioEndpointUrl: jest.fn(state => ({ studioEndpointUrl: state })), + }, + requests: { + isFailed: jest.fn((state, params) => ({ isFailed: { state, params } })), + }, + }, +})); -test('Navigation: Save', () => { - const wrapper = mount( - - - , - ); - const button = wrapper.find({ children: 'Add To Course' }); - expect(button).toBeTruthy(); - button.simulate('click'); - expect(saveBlock).toHaveBeenCalled(); - expect(window.location.assign).toHaveBeenCalled(); +jest.mock('.', () => ({ + __esModule: true, // Use it when dealing with esModules + ...jest.requireActual('./index'), + handleCancelClicked: jest.fn(args => ({ handleCancelClicked: args })), + handleSaveClicked: jest.fn(args => ({ handleSaveClicked: args })), + } +)); + +jest.mock('../../hooks', () => ({ + saveTextBlock: jest.fn(), + navigateCallback: jest.fn(), + nullMethod: jest.fn().mockName('nullMethod'), +})); + +describe('EditorFooter', () => { + const props = { + editorRef: jest.fn().mockName('args.editorRef'), + isInitialized: true, + returnUrl: 'hocuspocus.ca', + saveFailed: false, + saveBlock: jest.fn().mockName('args.saveBlock'), + }; + describe('behavior', () => { + const realmodule = jest.requireActual('./index'); + test('handleSaveClicked calls saveTextBlock', () => { + const createdCallback = realmodule.handleSaveClicked(props); + createdCallback(); + expect(saveTextBlock).toHaveBeenCalled(); + }); + test('handleCancelClicked calls navigateCallback', () => { + realmodule.handleCancelClicked({ returnUrl: props.returnUrl }); + expect(navigateCallback).toHaveBeenCalledWith(props.returnUrl); + }); + }); + describe('snapshots', () => { + test('renders as expected with default behavior', () => { + expect(shallow()).toMatchSnapshot(); + }); + test('not intialized, Spinner appears and button is disabled', () => { + expect(shallow()).toMatchSnapshot(); + }); + test('Save Failed, error message raised', () => { + expect(shallow()).toMatchSnapshot(); + }); + }); + describe('mapStateToProps', () => { + const testState = { A: 'pple', B: 'anana', C: 'ucumber' }; + test('isInitialized from app.isInitialized', () => { + expect( + module.mapStateToProps(testState).isInitialized, + ).toEqual(selectors.app.isInitialized(testState)); + }); + test('studioEndpointUrl from app.studioEndpointUrl', () => { + expect( + module.mapStateToProps(testState).studioEndpointUrl, + ).toEqual(selectors.app.studioEndpointUrl(testState)); + }); + test('saveFailed from requests.isFailed', () => { + expect( + module.mapStateToProps(testState).saveFailed, + ).toEqual(selectors.requests.isFailed(testState, { requestKey: RequestKeys.saveBlock })); + }); + }); + describe('mapDispatchToProps', () => { + test('saveBlock from thunkActions.app.saveBlock', () => { + expect(module.mapDispatchToProps.saveBlock).toEqual(thunkActions.app.saveBlock); + }); + }); }); diff --git a/src/editors/hooks.js b/src/editors/hooks.js index a86147665..e9f6fcea9 100644 --- a/src/editors/hooks.js +++ b/src/editors/hooks.js @@ -1,4 +1,6 @@ -import { useRef, useEffect, useCallback, useState } from 'react'; +import { + useRef, useEffect, useCallback, useState, +} from 'react'; export const initializeApp = ({ initialize, data }) => useEffect(() => initialize(data), []); @@ -28,3 +30,6 @@ export const saveTextBlock = ({ content: editorRef.current.getContent(), }); }; + +// for toast onClose to avoid console warnings +export const nullMethod = () => {};