@@ -0,0 +1,142 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`EditorFooter snapshots Save Failed, error message raised 1`] = `
|
||||
<div
|
||||
className="editor-footer mt-auto"
|
||||
>
|
||||
<Toast
|
||||
onClose={[MockFunction nullMethod]}
|
||||
show={true}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Error: Content save failed. Try again later."
|
||||
description="Error message displayed when content fails to save."
|
||||
id="authoring.editorfooter.save.error"
|
||||
/>
|
||||
</Toast>
|
||||
<Component>
|
||||
<ActionRow>
|
||||
<Component />
|
||||
<Button
|
||||
aria-label="Discard Changes and Return to Learning Context"
|
||||
onClick={
|
||||
Object {
|
||||
"handleCancelClicked": Object {
|
||||
"returnUrl": "hocuspocus.ca",
|
||||
},
|
||||
}
|
||||
}
|
||||
variant="tertiary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Save Changes and Return to Learning Context"
|
||||
disabled={false}
|
||||
onClick={
|
||||
Object {
|
||||
"handleSaveClicked": Object {
|
||||
"editorRef": [MockFunction args.editorRef],
|
||||
"returnUrl": "hocuspocus.ca",
|
||||
"saveBlock": [MockFunction args.saveBlock],
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Save"
|
||||
description="Label for Save button"
|
||||
id="authoring.editorfooter.savebutton.label"
|
||||
/>
|
||||
</Button>
|
||||
</ActionRow>
|
||||
</Component>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EditorFooter snapshots not intialized, Spinner appears and button is disabled 1`] = `
|
||||
<div
|
||||
className="editor-footer mt-auto"
|
||||
>
|
||||
<Component>
|
||||
<ActionRow>
|
||||
<Component />
|
||||
<Button
|
||||
aria-label="Discard Changes and Return to Learning Context"
|
||||
onClick={
|
||||
Object {
|
||||
"handleCancelClicked": Object {
|
||||
"returnUrl": "hocuspocus.ca",
|
||||
},
|
||||
}
|
||||
}
|
||||
variant="tertiary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Save Changes and Return to Learning Context"
|
||||
disabled={true}
|
||||
onClick={
|
||||
Object {
|
||||
"handleSaveClicked": Object {
|
||||
"editorRef": [MockFunction args.editorRef],
|
||||
"returnUrl": "hocuspocus.ca",
|
||||
"saveBlock": [MockFunction args.saveBlock],
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<Spinner
|
||||
animation="border"
|
||||
className="mr-3"
|
||||
/>
|
||||
</Button>
|
||||
</ActionRow>
|
||||
</Component>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EditorFooter snapshots renders as expected with default behavior 1`] = `
|
||||
<div
|
||||
className="editor-footer mt-auto"
|
||||
>
|
||||
<Component>
|
||||
<ActionRow>
|
||||
<Component />
|
||||
<Button
|
||||
aria-label="Discard Changes and Return to Learning Context"
|
||||
onClick={
|
||||
Object {
|
||||
"handleCancelClicked": Object {
|
||||
"returnUrl": "hocuspocus.ca",
|
||||
},
|
||||
}
|
||||
}
|
||||
variant="tertiary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Save Changes and Return to Learning Context"
|
||||
disabled={false}
|
||||
onClick={
|
||||
Object {
|
||||
"handleSaveClicked": Object {
|
||||
"editorRef": [MockFunction args.editorRef],
|
||||
"returnUrl": "hocuspocus.ca",
|
||||
"saveBlock": [MockFunction args.saveBlock],
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Save"
|
||||
description="Label for Save button"
|
||||
id="authoring.editorfooter.savebutton.label"
|
||||
/>
|
||||
</Button>
|
||||
</ActionRow>
|
||||
</Component>
|
||||
</div>
|
||||
`;
|
||||
@@ -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 = ({
|
||||
}) => (
|
||||
<div className="editor-footer mt-auto">
|
||||
{saveFailed && (
|
||||
<Toast><FormattedMessage {...messages.contentSaveFailed} /></Toast>
|
||||
<Toast show onClose={nullMethod}><FormattedMessage {...messages.contentSaveFailed} /></Toast>
|
||||
)}
|
||||
|
||||
<ModalDialog.Footer>
|
||||
|
||||
@@ -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(
|
||||
<EditorPageContext.Provider value={context}>
|
||||
<EditorFooter />
|
||||
</EditorPageContext.Provider>,
|
||||
);
|
||||
expect(screen.getByText('Cancel')).toBeTruthy();
|
||||
expect(screen.getByText('Add To Course')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('Rendering: loading url', () => {
|
||||
const context = {
|
||||
unitUrlLoading: ActionStates.NOT_BEGUN,
|
||||
};
|
||||
render(
|
||||
<EditorPageContext.Provider value={context}>
|
||||
<EditorFooter />
|
||||
</EditorPageContext.Provider>,
|
||||
);
|
||||
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(
|
||||
<EditorPageContext.Provider value={context}>
|
||||
<EditorFooter />
|
||||
</EditorPageContext.Provider>,
|
||||
);
|
||||
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(
|
||||
<EditorPageProvider
|
||||
blockType="html"
|
||||
courseId="myCourse101"
|
||||
blockId="redosablocksalot"
|
||||
studioEndpointUrl="celaicboss.axe"
|
||||
>
|
||||
<EditorFooter />
|
||||
</EditorPageProvider>,
|
||||
);
|
||||
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(<module.EditorFooter {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
test('not intialized, Spinner appears and button is disabled', () => {
|
||||
expect(shallow(<module.EditorFooter {...props} isInitialized={false} />)).toMatchSnapshot();
|
||||
});
|
||||
test('Save Failed, error message raised', () => {
|
||||
expect(shallow(<module.EditorFooter {...props} saveFailed />)).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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 = () => {};
|
||||
|
||||
Reference in New Issue
Block a user