Chore: Test coverage hunt (#36)
* chore: add brand mocking in gallery view * feat: dev gallery app * chore: link mock block ids to real block type api * feat: image settings page features * chore: more tests * chore: keystore util and more testing * chore: more tests * chore: re-install lint plugin... * chore: lint fixes * chore: moar tests * chore: remove brand from module.config and link gallery to edx.org brand
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -108,3 +108,5 @@ dist
|
||||
*.swo
|
||||
*.swp
|
||||
|
||||
### local overrides ###
|
||||
module.config.js
|
||||
|
||||
23601
package-lock.json
generated
23601
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -40,11 +40,13 @@
|
||||
"@testing-library/dom": "^8.11.1",
|
||||
"@testing-library/react": "12.1.1",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"axios": "^0.26.1",
|
||||
"codecov": "3.8.3",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"enzyme": "3.11.0",
|
||||
"enzyme-adapter-react-16": "1.15.6",
|
||||
"enzyme-to-json": "^3.6.2",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"husky": "7.0.4",
|
||||
"prop-types": "15.7.2",
|
||||
"react": "16.14.0",
|
||||
|
||||
@@ -48,7 +48,7 @@ export const TextEditor = ({
|
||||
|
||||
// selected image file reference data object.
|
||||
// this field determines the step of the ImageUploadModal
|
||||
const [imageSelection, setImageSelection] = selectedImage(null);
|
||||
const imageSelection = selectedImage(null);
|
||||
|
||||
return (
|
||||
<div className="editor-body h-75">
|
||||
@@ -56,8 +56,7 @@ export const TextEditor = ({
|
||||
isOpen={isOpen}
|
||||
close={closeModal}
|
||||
editorRef={editorRef}
|
||||
selection={imageSelection}
|
||||
setSelection={setImageSelection}
|
||||
{...imageSelection}
|
||||
/>
|
||||
|
||||
<Toast show={blockFailed} onClose={nullMethod}>
|
||||
@@ -77,7 +76,8 @@ export const TextEditor = ({
|
||||
blockValue,
|
||||
openModal,
|
||||
initializeEditor,
|
||||
setSelection: setImageSelection,
|
||||
setSelection: imageSelection.setSelection,
|
||||
clearSelection: imageSelection.clearSelection,
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -12,21 +12,26 @@ jest.mock('@tinymce/tinymce-react', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
...originalModule,
|
||||
Editor: () => 'TiNYmCE EDitOR'
|
||||
,
|
||||
Editor: () => 'TiNYmCE EDitOR',
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('./components/ImageUploadModal', () => 'ImageUploadModal');
|
||||
|
||||
jest.mock('./hooks', () => {
|
||||
const updateState = jest.fn();
|
||||
return ({
|
||||
editorConfig: jest.fn(args => ({ editorConfig: args })),
|
||||
modalToggle: jest.fn(args => ({ modalToggle: args })),
|
||||
selectedImage: jest.fn(val => ([{ state: val }, jest.fn((newVal) => updateState({ val, newVal })).mockName('setSelection')])),
|
||||
nullMethod: jest.fn().mockName('nullMethod'),
|
||||
});
|
||||
});
|
||||
jest.mock('./hooks', () => ({
|
||||
editorConfig: jest.fn(args => ({ editorConfig: args })),
|
||||
modalToggle: jest.fn(() => ({
|
||||
isOpen: true,
|
||||
openModal: jest.fn().mockName('openModal'),
|
||||
closeModal: jest.fn().mockName('closeModal'),
|
||||
})),
|
||||
selectedImage: jest.fn(() => ({
|
||||
selection: 'hooks.selectedImage.selection',
|
||||
setSelection: jest.fn().mockName('hooks.selectedImage.setSelection'),
|
||||
clearSelection: jest.fn().mockName('hooks.selectedImage.clearSelection'),
|
||||
})),
|
||||
nullMethod: jest.fn().mockName('hooks.nullMethod'),
|
||||
}));
|
||||
|
||||
jest.mock('react', () => {
|
||||
const updateState = jest.fn();
|
||||
|
||||
@@ -5,6 +5,7 @@ exports[`TextEditor snapshots block failed to load, Toast is shown 1`] = `
|
||||
className="editor-body h-75"
|
||||
>
|
||||
<ImageUploadModal
|
||||
clearSelection={[MockFunction hooks.selectedImage.clearSelection]}
|
||||
close={[MockFunction modal.closeModal]}
|
||||
editorRef={
|
||||
Object {
|
||||
@@ -14,15 +15,11 @@ exports[`TextEditor snapshots block failed to load, Toast is shown 1`] = `
|
||||
}
|
||||
}
|
||||
isOpen={false}
|
||||
selection={
|
||||
Object {
|
||||
"state": null,
|
||||
}
|
||||
}
|
||||
setSelection={[MockFunction setSelection]}
|
||||
selection="hooks.selectedImage.selection"
|
||||
setSelection={[MockFunction hooks.selectedImage.setSelection]}
|
||||
/>
|
||||
<Toast
|
||||
onClose={[MockFunction nullMethod]}
|
||||
onClose={[MockFunction hooks.nullMethod]}
|
||||
show={true}
|
||||
>
|
||||
<FormattedMessage
|
||||
@@ -39,10 +36,11 @@ exports[`TextEditor snapshots block failed to load, Toast is shown 1`] = `
|
||||
"some": "eDiTablE Text",
|
||||
},
|
||||
},
|
||||
"clearSelection": [MockFunction hooks.selectedImage.clearSelection],
|
||||
"initializeEditor": [MockFunction args.intializeEditor],
|
||||
"openModal": [MockFunction modal.openModal],
|
||||
"setEditorRef": [MockFunction args.setEditorRef],
|
||||
"setSelection": [MockFunction setSelection],
|
||||
"setSelection": [MockFunction hooks.selectedImage.setSelection],
|
||||
}
|
||||
}
|
||||
/>
|
||||
@@ -54,6 +52,7 @@ exports[`TextEditor snapshots not yet loaded, Spinner appears 1`] = `
|
||||
className="editor-body h-75"
|
||||
>
|
||||
<ImageUploadModal
|
||||
clearSelection={[MockFunction hooks.selectedImage.clearSelection]}
|
||||
close={[MockFunction modal.closeModal]}
|
||||
editorRef={
|
||||
Object {
|
||||
@@ -63,15 +62,11 @@ exports[`TextEditor snapshots not yet loaded, Spinner appears 1`] = `
|
||||
}
|
||||
}
|
||||
isOpen={false}
|
||||
selection={
|
||||
Object {
|
||||
"state": null,
|
||||
}
|
||||
}
|
||||
setSelection={[MockFunction setSelection]}
|
||||
selection="hooks.selectedImage.selection"
|
||||
setSelection={[MockFunction hooks.selectedImage.setSelection]}
|
||||
/>
|
||||
<Toast
|
||||
onClose={[MockFunction nullMethod]}
|
||||
onClose={[MockFunction hooks.nullMethod]}
|
||||
show={false}
|
||||
>
|
||||
<FormattedMessage
|
||||
@@ -97,6 +92,7 @@ exports[`TextEditor snapshots renders as expected with default behavior 1`] = `
|
||||
className="editor-body h-75"
|
||||
>
|
||||
<ImageUploadModal
|
||||
clearSelection={[MockFunction hooks.selectedImage.clearSelection]}
|
||||
close={[MockFunction modal.closeModal]}
|
||||
editorRef={
|
||||
Object {
|
||||
@@ -106,15 +102,11 @@ exports[`TextEditor snapshots renders as expected with default behavior 1`] = `
|
||||
}
|
||||
}
|
||||
isOpen={false}
|
||||
selection={
|
||||
Object {
|
||||
"state": null,
|
||||
}
|
||||
}
|
||||
setSelection={[MockFunction setSelection]}
|
||||
selection="hooks.selectedImage.selection"
|
||||
setSelection={[MockFunction hooks.selectedImage.setSelection]}
|
||||
/>
|
||||
<Toast
|
||||
onClose={[MockFunction nullMethod]}
|
||||
onClose={[MockFunction hooks.nullMethod]}
|
||||
show={false}
|
||||
>
|
||||
<FormattedMessage
|
||||
@@ -131,10 +123,11 @@ exports[`TextEditor snapshots renders as expected with default behavior 1`] = `
|
||||
"some": "eDiTablE Text",
|
||||
},
|
||||
},
|
||||
"clearSelection": [MockFunction hooks.selectedImage.clearSelection],
|
||||
"initializeEditor": [MockFunction args.intializeEditor],
|
||||
"openModal": [MockFunction modal.openModal],
|
||||
"setEditorRef": [MockFunction args.setEditorRef],
|
||||
"setSelection": [MockFunction setSelection],
|
||||
"setSelection": [MockFunction hooks.selectedImage.setSelection],
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { BaseModal } from './BaseModal';
|
||||
|
||||
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',
|
||||
};
|
||||
expect(shallow(<BaseModal {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -87,7 +87,7 @@ export const dimensionLockHooks = () => {
|
||||
|
||||
const initializeLock = ({ width, height }) => {
|
||||
// find minimum viable increment
|
||||
let gcd = findGcd(width, height);
|
||||
let gcd = module.findGcd(width, height);
|
||||
if ([width, height].some(v => !Number.isInteger(v / gcd))) {
|
||||
gcd = 1;
|
||||
}
|
||||
|
||||
@@ -135,6 +135,11 @@ describe('ImageSettingsModal hooks', () => {
|
||||
hook.initializeLock(multiDims);
|
||||
expect(state.setState.lockDims).toHaveBeenCalledWith(reducedDims);
|
||||
});
|
||||
it('returns the values themselves if they have no gcd', () => {
|
||||
jest.spyOn(hooks, hookKeys.findGcd).mockReturnValueOnce(2);
|
||||
hook.initializeLock(simpleDims);
|
||||
expect(state.setState.lockDims).toHaveBeenCalledWith(simpleDims);
|
||||
});
|
||||
});
|
||||
test('lock sets isLocked to true', () => {
|
||||
hook = hooks.dimensionLockHooks({ dimensions: simpleDims });
|
||||
@@ -280,4 +285,12 @@ describe('ImageSettingsModal hooks', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('isSaveDisabled', () => {
|
||||
it('returns true iff is not decorative and altText value is empty', () => {
|
||||
hook = hooks.isSaveDisabled;
|
||||
expect(hook({ isDecorative: false, value: '' })).toEqual(true);
|
||||
expect(hook({ isDecorative: false, value: 'test' })).toEqual(false);
|
||||
expect(hook({ isDecorative: true, value: '' })).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,44 +1,67 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import tinyMCEKeys from '../../../data/constants/tinyMCE';
|
||||
import ImageSettingsModal from './ImageSettingsModal';
|
||||
import SelectImageModal from './SelectImageModal';
|
||||
import * as module from './ImageUploadModal';
|
||||
|
||||
export const propsString = (props) => Object.keys(props)
|
||||
.map(key => `${key}="${props[key]}"`)
|
||||
.join(' ');
|
||||
|
||||
export const imgProps = ({ settings, selection }) => ({
|
||||
src: selection.externalUrl,
|
||||
alt: settings.isDecorative ? '' : settings.altText,
|
||||
width: settings.dimensions.width,
|
||||
height: settings.dimensions.height,
|
||||
});
|
||||
|
||||
export const hooks = {
|
||||
createSaveCallback: ({
|
||||
close, editorRef, setSelection, selection,
|
||||
}) => (settings) => {
|
||||
editorRef.current.execCommand('mceInsertContent', false, module.hooks.getImgTag({ settings, selection }));
|
||||
editorRef.current.execCommand(
|
||||
tinyMCEKeys.commands.insertContent,
|
||||
false,
|
||||
module.hooks.imgTag({ settings, selection }),
|
||||
);
|
||||
setSelection(null);
|
||||
close();
|
||||
},
|
||||
getImgTag: ({ settings, selection }) => `<img src="${selection.externalUrl}" alt="${settings.isDecorative ? '' : settings.altText}" width="${settings.dimensions.width}" height="${settings.dimensions.height}">`,
|
||||
onClose: ({ clearSelection, close }) => {
|
||||
clearSelection();
|
||||
close();
|
||||
},
|
||||
imgTag: ({ settings, selection }) => {
|
||||
const props = module.imgProps({ settings, selection });
|
||||
return `<img ${propsString(props)} />`;
|
||||
},
|
||||
};
|
||||
|
||||
const ImageUploadModal = ({
|
||||
export const ImageUploadModal = ({
|
||||
// eslint-disable-next-line
|
||||
editorRef,
|
||||
isOpen,
|
||||
close,
|
||||
clearSelection,
|
||||
selection,
|
||||
setSelection,
|
||||
}) => {
|
||||
const saveToEditor = module.hooks.createSaveCallback({
|
||||
close, editorRef, setSelection, selection,
|
||||
});
|
||||
const closeAndReset = () => {
|
||||
setSelection(null);
|
||||
close();
|
||||
};
|
||||
if (selection) {
|
||||
return (
|
||||
<ImageSettingsModal
|
||||
{...{
|
||||
isOpen,
|
||||
close: closeAndReset,
|
||||
close: module.hooks.onClose({ clearSelection, close }),
|
||||
selection,
|
||||
saveToEditor,
|
||||
returnToSelection: () => setSelection(null),
|
||||
saveToEditor: module.hooks.createSaveCallback({
|
||||
close,
|
||||
editorRef,
|
||||
selection,
|
||||
setSelection,
|
||||
}),
|
||||
returnToSelection: clearSelection,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -51,17 +74,18 @@ ImageUploadModal.defaultProps = {
|
||||
selection: null,
|
||||
};
|
||||
ImageUploadModal.propTypes = {
|
||||
clearSelection: PropTypes.func.isRequired,
|
||||
close: PropTypes.func.isRequired,
|
||||
editorRef: PropTypes.oneOfType([
|
||||
PropTypes.func,
|
||||
PropTypes.shape({ current: PropTypes.any }),
|
||||
]),
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
selection: PropTypes.shape({
|
||||
url: PropTypes.string,
|
||||
externalUrl: PropTypes.string,
|
||||
altText: PropTypes.bool,
|
||||
}),
|
||||
setSelection: PropTypes.func.isRequired,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
close: PropTypes.func.isRequired,
|
||||
editorRef: PropTypes.oneOfType([
|
||||
PropTypes.func,
|
||||
PropTypes.shape({ current: PropTypes.any }),
|
||||
]),
|
||||
};
|
||||
export default ImageUploadModal;
|
||||
|
||||
@@ -1,68 +1,122 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { keyStore } from '../../../utils';
|
||||
import tinyMCEKeys from '../../../data/constants/tinyMCE';
|
||||
|
||||
import * as module from './ImageUploadModal';
|
||||
|
||||
describe('ImageUploadModal hooks', () => {
|
||||
describe('getImgTag', () => {
|
||||
const mockSelection = { externalUrl: 'sOmEuRl.cOm' };
|
||||
let output;
|
||||
test('It returns a html string which matches an image tag', () => {
|
||||
const mockSettings = {
|
||||
altText: 'aLt tExt',
|
||||
isDecorative: false,
|
||||
dimensions: {
|
||||
width: 2022,
|
||||
height: 1619,
|
||||
},
|
||||
jest.mock('./ImageSettingsModal', () => 'ImageSettingsModal');
|
||||
jest.mock('./SelectImageModal', () => 'SelectImageModal');
|
||||
|
||||
const { ImageUploadModal } = module;
|
||||
|
||||
const hookKeys = keyStore(module.hooks);
|
||||
|
||||
const settings = {
|
||||
altText: 'aLt tExt',
|
||||
isDecorative: false,
|
||||
dimensions: {
|
||||
width: 2022,
|
||||
height: 1619,
|
||||
},
|
||||
};
|
||||
|
||||
describe('ImageUploadModal', () => {
|
||||
describe('hooks', () => {
|
||||
describe('imgTag', () => {
|
||||
const selection = { externalUrl: 'sOmEuRl.cOm' };
|
||||
const expected = {
|
||||
src: selection.externalUrl,
|
||||
alt: settings.altText,
|
||||
width: settings.dimensions.width,
|
||||
height: settings.dimensions.height,
|
||||
};
|
||||
output = module.hooks.getImgTag({ selection: mockSelection, settings: mockSettings });
|
||||
expect(output).toEqual(`<img src="${mockSelection.externalUrl}" alt="${mockSettings.altText}" width="${mockSettings.dimensions.width}" height="${mockSettings.dimensions.height}">`);
|
||||
const testImgTag = (args) => {
|
||||
const output = module.hooks.imgTag({
|
||||
settings: args.settings,
|
||||
selection,
|
||||
});
|
||||
expect(output).toEqual(`<img ${module.propsString(args.expected)} />`);
|
||||
};
|
||||
test('It returns a html string which matches an image tag', () => {
|
||||
testImgTag({ settings, expected });
|
||||
});
|
||||
test('If isDecorative is true, alt text is an empty string', () => {
|
||||
testImgTag({
|
||||
settings: { ...settings, isDecorative: true },
|
||||
expected: { ...expected, alt: '' },
|
||||
});
|
||||
});
|
||||
});
|
||||
test('If Is decorative is true, alt text is an empty string', () => {
|
||||
const mockSettings = {
|
||||
isDecorative: true,
|
||||
altText: 'aLt tExt',
|
||||
dimensions: {
|
||||
width: 2022,
|
||||
height: 1619,
|
||||
},
|
||||
};
|
||||
output = module.hooks.getImgTag({ selection: mockSelection, settings: mockSettings });
|
||||
expect(output).toEqual(`<img src="${mockSelection.externalUrl}" alt="" width="${mockSettings.dimensions.width}" height="${mockSettings.dimensions.height}">`);
|
||||
describe('createSaveCallback', () => {
|
||||
const close = jest.fn();
|
||||
const execCommandMock = jest.fn();
|
||||
const editorRef = { current: { some: 'dATa', execCommand: execCommandMock } };
|
||||
const setSelection = jest.fn();
|
||||
const selection = jest.fn();
|
||||
let output;
|
||||
beforeEach(() => {
|
||||
output = module.hooks.createSaveCallback({
|
||||
close, editorRef, setSelection, selection,
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
test('It creates a callback, that when called, inserts to the editor, sets the selection to be null, and calls close', () => {
|
||||
jest.spyOn(module.hooks, hookKeys.imgTag)
|
||||
.mockImplementationOnce((props) => ({ selection, settings: props.settings }));
|
||||
expect(execCommandMock).not.toBeCalled();
|
||||
expect(setSelection).not.toBeCalled();
|
||||
expect(close).not.toBeCalled();
|
||||
output(settings);
|
||||
expect(execCommandMock).toBeCalledWith(
|
||||
tinyMCEKeys.commands.insertContent,
|
||||
false,
|
||||
{ selection, settings },
|
||||
);
|
||||
expect(setSelection).toBeCalledWith(null);
|
||||
expect(close).toBeCalled();
|
||||
});
|
||||
});
|
||||
describe('onClose', () => {
|
||||
it('takes and calls clearSelection and close callbacks', () => {
|
||||
const clearSelection = jest.fn();
|
||||
const close = jest.fn();
|
||||
module.hooks.onClose({ clearSelection, close });
|
||||
expect(clearSelection).toHaveBeenCalled();
|
||||
expect(close).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createSaveCallback', () => {
|
||||
const close = jest.fn();
|
||||
const execCommandMock = jest.fn();
|
||||
const editorRef = { current: { some: 'dATa', execCommand: execCommandMock } };
|
||||
const setSelection = jest.fn();
|
||||
const selection = jest.fn();
|
||||
const mockSettings = {
|
||||
altText: 'aLt tExt',
|
||||
isDecorative: false,
|
||||
dimensions: {
|
||||
width: 2022,
|
||||
height: 1619,
|
||||
},
|
||||
};
|
||||
let output;
|
||||
beforeEach(() => {
|
||||
output = module.hooks.createSaveCallback({
|
||||
close, editorRef, setSelection, selection,
|
||||
});
|
||||
describe('component', () => {
|
||||
let props;
|
||||
let hooks;
|
||||
beforeAll(() => {
|
||||
hooks = module.hooks;
|
||||
props = {
|
||||
editorRef: { current: null },
|
||||
isOpen: false,
|
||||
close: jest.fn().mockName('props.close'),
|
||||
clearSelection: jest.fn().mockName('props.clearSelection'),
|
||||
selection: { some: 'images' },
|
||||
setSelection: jest.fn().mockName('props.setSelection'),
|
||||
};
|
||||
module.hooks = {
|
||||
createSaveCallback: jest.fn().mockName('hooks.createSaveCallback'),
|
||||
onClose: jest.fn().mockName('hooks.onClose'),
|
||||
};
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
afterAll(() => {
|
||||
module.hooks = hooks;
|
||||
});
|
||||
test('It creates a callback, that when called, inserts to the editor, sets the selection to be null, and calls close', () => {
|
||||
jest.spyOn(module.hooks, 'getImgTag').mockImplementationOnce(({ settings }) => ({ selection, settings }));
|
||||
expect(execCommandMock).not.toBeCalled();
|
||||
expect(setSelection).not.toBeCalled();
|
||||
expect(close).not.toBeCalled();
|
||||
output(mockSettings);
|
||||
expect(execCommandMock).toBeCalledWith('mceInsertContent', false, { selection, settings: mockSettings });
|
||||
expect(setSelection).toBeCalledWith(null);
|
||||
expect(close).toBeCalled();
|
||||
test('snapshot: with selection content (ImageSettingsUpload)', () => {
|
||||
expect(shallow(<ImageUploadModal {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
test('snapshot: no selection (Select Image Modal)', () => {
|
||||
expect(shallow(<ImageUploadModal {...props} selection={null} />)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`BaseModal ImageUploadModal template component snapshot 1`] = `
|
||||
<ModalDialog
|
||||
hasCloseButton={true}
|
||||
isFullscreenOnMobile={true}
|
||||
isOpen={true}
|
||||
onClose={[MockFunction props.close]}
|
||||
size="lg"
|
||||
title="My dialog"
|
||||
variant="default"
|
||||
>
|
||||
<ModalDialog.Header>
|
||||
<ModalDialog.Title>
|
||||
props.title node
|
||||
</ModalDialog.Title>
|
||||
</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
props.children node
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<ActionRow>
|
||||
<ModalDialog.CloseButton
|
||||
onClick={[MockFunction props.close]}
|
||||
variant="tertiary"
|
||||
>
|
||||
Cancel
|
||||
</ModalDialog.CloseButton>
|
||||
props.confirmAction node
|
||||
</ActionRow>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
`;
|
||||
@@ -0,0 +1,21 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ImageUploadModal component snapshot: no selection (Select Image Modal) 1`] = `
|
||||
<SelectImageModal
|
||||
close={[MockFunction props.close]}
|
||||
isOpen={false}
|
||||
setSelection={[MockFunction props.setSelection]}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`ImageUploadModal component snapshot: with selection content (ImageSettingsUpload) 1`] = `
|
||||
<ImageSettingsModal
|
||||
isOpen={false}
|
||||
returnToSelection={[MockFunction props.clearSelection]}
|
||||
selection={
|
||||
Object {
|
||||
"some": "images",
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
||||
@@ -1,6 +1,12 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { StrictDict } from '../../utils';
|
||||
import * as module from './hooks';
|
||||
import { StrictDict } from '../../utils/index';
|
||||
|
||||
export const state = StrictDict({
|
||||
isModalOpen: (val) => useState(val),
|
||||
imageSelection: (val) => useState(val),
|
||||
});
|
||||
|
||||
export const openModalWithSelectedImage = (editor, setImage, openModal) => () => {
|
||||
const imgHTML = editor.selection.getNode();
|
||||
@@ -96,10 +102,17 @@ export const editorConfig = ({
|
||||
},
|
||||
});
|
||||
|
||||
export const selectedImage = (val) => useState(val);
|
||||
export const selectedImage = (val) => {
|
||||
const [selection, setSelection] = module.state.imageSelection(val);
|
||||
return {
|
||||
clearSelection: () => setSelection(null),
|
||||
selection,
|
||||
setSelection,
|
||||
};
|
||||
};
|
||||
|
||||
export const modalToggle = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isOpen, setIsOpen] = module.state.isModalOpen(false);
|
||||
return {
|
||||
isOpen,
|
||||
openModal: () => setIsOpen(true),
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import React from 'react';
|
||||
import * as module from './hooks';
|
||||
|
||||
jest.mock('react', () => {
|
||||
const updateState = jest.fn();
|
||||
return {
|
||||
updateState,
|
||||
useState: jest.fn(val => ([{ state: val }, (newVal) => updateState({ val, newVal })])),
|
||||
createRef: jest.fn(val => ({ ref: val })),
|
||||
};
|
||||
});
|
||||
import { MockUseState } from '../../../testUtils';
|
||||
|
||||
jest.mock('react', () => ({
|
||||
...jest.requireActual('react'),
|
||||
createRef: jest.fn(val => ({ ref: val })),
|
||||
}));
|
||||
|
||||
const state = new MockUseState(module);
|
||||
|
||||
describe('TextEditor hooks', () => {
|
||||
describe('Editor Init hooks', () => {
|
||||
describe('state hooks', () => {
|
||||
state.testGetter(state.keys.isModalOpen);
|
||||
state.testGetter(state.keys.imageSelection);
|
||||
});
|
||||
const mockOpenModal = jest.fn();
|
||||
const mockAddbutton = jest.fn(val => ({ onAction: val }));
|
||||
const mockNode = {
|
||||
@@ -136,37 +139,42 @@ describe('TextEditor hooks', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('selectedImage', () => {
|
||||
describe('selectedImage hooks', () => {
|
||||
const val = { a: 'VaLUe' };
|
||||
const newVal = { some: 'vAlUe' };
|
||||
let output;
|
||||
let setter;
|
||||
let hook;
|
||||
beforeEach(() => {
|
||||
[output, setter] = module.selectedImage(val);
|
||||
state.mock();
|
||||
hook = module.selectedImage(val);
|
||||
});
|
||||
test('returns a field which with state input val', () => {
|
||||
expect(output).toMatchObject({ state: val });
|
||||
test('selection: state value', () => {
|
||||
expect(hook.selection).toEqual(state.stateVals[state.keys.imageSelection]);
|
||||
});
|
||||
test('calling setter with new val sets with respect to new val', () => {
|
||||
setter(newVal);
|
||||
expect(React.updateState).toHaveBeenCalledWith({ val, newVal });
|
||||
test('setSelection: setter for value', () => {
|
||||
expect(hook.setSelection).toEqual(state.setState[state.keys.imageSelection]);
|
||||
});
|
||||
test('clearSelection: calls setter with null', () => {
|
||||
expect(hook.setSelection).not.toHaveBeenCalled();
|
||||
hook.clearSelection();
|
||||
expect(hook.setSelection).toHaveBeenCalledWith(null);
|
||||
});
|
||||
});
|
||||
describe('modalToggle hook', () => {
|
||||
let output;
|
||||
let hook;
|
||||
const hookKey = state.keys.isModalOpen;
|
||||
beforeEach(() => {
|
||||
output = module.modalToggle();
|
||||
state.mock();
|
||||
hook = module.modalToggle();
|
||||
});
|
||||
test('returns isOpen field, defaulted to false', () => {
|
||||
expect(output.isOpen).toEqual({ state: false });
|
||||
test('isOpen: state value', () => {
|
||||
expect(hook.isOpen).toEqual(state.stateVals[hookKey]);
|
||||
});
|
||||
test('returns openModal field, which sets modal to true and calls updateState', () => {
|
||||
output.openModal();
|
||||
expect(React.updateState).toHaveBeenCalledWith({ val: false, newVal: true });
|
||||
test('openModal: calls setter with true', () => {
|
||||
hook.openModal();
|
||||
expect(state.setState[hookKey]).toHaveBeenCalledWith(true);
|
||||
});
|
||||
test('returns closeModal field, which sets modal to true and calls updateState', () => {
|
||||
output.closeModal();
|
||||
expect(React.updateState).toHaveBeenCalledWith({ val: false, newVal: false });
|
||||
test('closeModal: calls setter with false', () => {
|
||||
hook.closeModal();
|
||||
expect(state.setState[hookKey]).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
describe('nullMethod hook', () => {
|
||||
|
||||
9
src/editors/data/constants/tinyMCE.js
Normal file
9
src/editors/data/constants/tinyMCE.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import { StrictDict } from '../../utils';
|
||||
|
||||
const commands = StrictDict({
|
||||
insertContent: 'mceInsertContent',
|
||||
});
|
||||
|
||||
export default StrictDict({
|
||||
commands,
|
||||
});
|
||||
@@ -46,6 +46,16 @@ describe('app reducer', () => {
|
||||
['setBlockTitle', 'blockTitle'],
|
||||
['setSaveResponse', 'saveResponse'],
|
||||
].map(args => setterTest(...args));
|
||||
describe('setBlockValue', () => {
|
||||
it('sets blockValue, as well as setting the blockTitle from data.display_name', () => {
|
||||
const blockValue = { data: { display_name: 'my test name' }, other: 'data' };
|
||||
expect(reducer(testingState, actions.setBlockValue(blockValue))).toEqual({
|
||||
...testingState,
|
||||
blockValue,
|
||||
blockTitle: blockValue.data.display_name,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('initializeEditor', () => {
|
||||
it('sets editorInitialized to true', () => {
|
||||
expect(reducer(testingState, actions.initializeEditor())).toEqual({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// import * in order to mock in-file references
|
||||
import { keyStore } from '../../../utils';
|
||||
import * as urls from '../../services/cms/urls';
|
||||
import * as selectors from './selectors';
|
||||
|
||||
@@ -30,17 +31,19 @@ describe('app selectors unit tests', () => {
|
||||
expect(cb({ ...testState, [key]: testValue })).toEqual(testValue);
|
||||
});
|
||||
};
|
||||
const simpleKeys = keyStore(simpleSelectors);
|
||||
describe('simple selectors link their values from app store', () => {
|
||||
[
|
||||
'blockContent',
|
||||
'blockId',
|
||||
'blockType',
|
||||
'blockValue',
|
||||
'courseId',
|
||||
'editorInitialized',
|
||||
'saveResponse',
|
||||
'studioEndpointUrl',
|
||||
'unitUrl',
|
||||
simpleKeys.blockContent,
|
||||
simpleKeys.blockId,
|
||||
simpleKeys.blockTitle,
|
||||
simpleKeys.blockType,
|
||||
simpleKeys.blockValue,
|
||||
simpleKeys.courseId,
|
||||
simpleKeys.editorInitialized,
|
||||
simpleKeys.saveResponse,
|
||||
simpleKeys.studioEndpointUrl,
|
||||
simpleKeys.unitUrl,
|
||||
].map(testSimpleSelector);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,50 +36,88 @@ describe('requests thunkActions module', () => {
|
||||
const testData = ({ some: 'test data' });
|
||||
let resolveFn;
|
||||
let rejectFn;
|
||||
beforeEach(() => {
|
||||
onSuccess = jest.fn();
|
||||
onFailure = jest.fn();
|
||||
requests.networkRequest({
|
||||
requestKey,
|
||||
promise: new Promise((resolve, reject) => {
|
||||
resolveFn = resolve;
|
||||
rejectFn = reject;
|
||||
}),
|
||||
onSuccess,
|
||||
onFailure,
|
||||
})(dispatch);
|
||||
});
|
||||
test('calls startRequest action with requestKey', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([[actions.requests.startRequest(requestKey)]]);
|
||||
});
|
||||
describe('on success', () => {
|
||||
beforeEach(async () => {
|
||||
await resolveFn(testData);
|
||||
describe('without success and failure handlers', () => {
|
||||
beforeEach(() => {
|
||||
requests.networkRequest({
|
||||
requestKey,
|
||||
promise: new Promise((resolve, reject) => {
|
||||
resolveFn = resolve;
|
||||
rejectFn = reject;
|
||||
}),
|
||||
})(dispatch);
|
||||
});
|
||||
it('dispatches completeRequest', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
[actions.requests.startRequest(requestKey)],
|
||||
[actions.requests.completeRequest({ requestKey, response: testData })],
|
||||
]);
|
||||
test('calls startRequest action with requestKey', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([[actions.requests.startRequest(requestKey)]]);
|
||||
});
|
||||
it('calls onSuccess with response', async () => {
|
||||
expect(onSuccess).toHaveBeenCalledWith(testData);
|
||||
expect(onFailure).not.toHaveBeenCalled();
|
||||
describe('on success', () => {
|
||||
beforeEach(async () => {
|
||||
await resolveFn(testData);
|
||||
});
|
||||
it('dispatches completeRequest', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
[actions.requests.startRequest(requestKey)],
|
||||
[actions.requests.completeRequest({ requestKey, response: testData })],
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('on failure', () => {
|
||||
beforeEach(async () => {
|
||||
await rejectFn(testData);
|
||||
});
|
||||
test('dispatches completeRequest', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
[actions.requests.startRequest(requestKey)],
|
||||
[actions.requests.failRequest({ requestKey, error: testData })],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('on failure', () => {
|
||||
beforeEach(async () => {
|
||||
await rejectFn(testData);
|
||||
describe('with handlers', () => {
|
||||
beforeEach(() => {
|
||||
onSuccess = jest.fn();
|
||||
onFailure = jest.fn();
|
||||
requests.networkRequest({
|
||||
requestKey,
|
||||
promise: new Promise((resolve, reject) => {
|
||||
resolveFn = resolve;
|
||||
rejectFn = reject;
|
||||
}),
|
||||
onSuccess,
|
||||
onFailure,
|
||||
})(dispatch);
|
||||
});
|
||||
test('dispatches completeRequest', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
[actions.requests.startRequest(requestKey)],
|
||||
[actions.requests.failRequest({ requestKey, error: testData })],
|
||||
]);
|
||||
test('calls startRequest action with requestKey', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([[actions.requests.startRequest(requestKey)]]);
|
||||
});
|
||||
test('calls onSuccess with response', async () => {
|
||||
expect(onFailure).toHaveBeenCalledWith(testData);
|
||||
expect(onSuccess).not.toHaveBeenCalled();
|
||||
describe('on success', () => {
|
||||
beforeEach(async () => {
|
||||
await resolveFn(testData);
|
||||
});
|
||||
it('dispatches completeRequest', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
[actions.requests.startRequest(requestKey)],
|
||||
[actions.requests.completeRequest({ requestKey, response: testData })],
|
||||
]);
|
||||
});
|
||||
it('calls onSuccess with response', async () => {
|
||||
expect(onSuccess).toHaveBeenCalledWith(testData);
|
||||
expect(onFailure).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe('on failure', () => {
|
||||
beforeEach(async () => {
|
||||
await rejectFn(testData);
|
||||
});
|
||||
test('dispatches completeRequest', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
[actions.requests.startRequest(requestKey)],
|
||||
[actions.requests.failRequest({ requestKey, error: testData })],
|
||||
]);
|
||||
});
|
||||
test('calls onFailure with response', async () => {
|
||||
expect(onFailure).toHaveBeenCalledWith(testData);
|
||||
expect(onSuccess).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
/* istanbul ignore file */
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
|
||||
@@ -50,8 +50,6 @@ export const apiMethods = {
|
||||
|
||||
export const checkMockApi = (key) => {
|
||||
if (process.env.REACT_APP_DEVGALLERY) {
|
||||
// eslint-disable-next-line
|
||||
console.log('use devgallery api methods');
|
||||
return mockApi[key];
|
||||
}
|
||||
return module.apiMethods[key];
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
import * as urls from './urls';
|
||||
|
||||
const mockPromise = (returnValue) => new Promise(resolve => resolve(returnValue));
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
import {
|
||||
useRef, useEffect, useCallback, useState,
|
||||
} from 'react';
|
||||
|
||||
import { StrictDict } from './utils';
|
||||
import * as module from './hooks';
|
||||
|
||||
export const initializeApp = ({ initialize, data }) => useEffect(() => initialize(data), []);
|
||||
export const state = StrictDict({
|
||||
refReady: (val) => useState(val),
|
||||
});
|
||||
|
||||
export const initializeApp = ({ initialize, data }) => useEffect(
|
||||
() => initialize(data),
|
||||
[],
|
||||
);
|
||||
|
||||
export const prepareEditorRef = () => {
|
||||
const editorRef = useRef(null);
|
||||
const setEditorRef = useCallback((ref) => {
|
||||
editorRef.current = ref;
|
||||
}, []);
|
||||
const [refReady, setRefReady] = useState(false);
|
||||
const [refReady, setRefReady] = module.state.refReady(false);
|
||||
useEffect(() => setRefReady(true), []);
|
||||
return { editorRef, refReady, setEditorRef };
|
||||
};
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { useEffect, updateState } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { MockUseState } from '../testUtils';
|
||||
import * as module from './hooks';
|
||||
|
||||
jest.mock('react', () => {
|
||||
const updateStateMock = jest.fn();
|
||||
return {
|
||||
updateState: updateStateMock,
|
||||
useState: jest.fn(val => ([{ state: val }, (newVal) => updateStateMock({ val, newVal })])),
|
||||
useRef: jest.fn(val => ({ current: val })),
|
||||
useEffect: jest.fn(),
|
||||
useCallback: (cb, prereqs) => ({ cb, prereqs }),
|
||||
};
|
||||
});
|
||||
jest.mock('react', () => ({
|
||||
...jest.requireActual('react'),
|
||||
useRef: jest.fn(val => ({ current: val })),
|
||||
useEffect: jest.fn(),
|
||||
useCallback: (cb, prereqs) => ({ cb, prereqs }),
|
||||
}));
|
||||
|
||||
const state = new MockUseState(module);
|
||||
|
||||
describe('hooks', () => {
|
||||
const locationTemp = window.location;
|
||||
beforeAll(() => {
|
||||
@@ -22,6 +23,9 @@ describe('hooks', () => {
|
||||
afterAll(() => {
|
||||
window.location = locationTemp;
|
||||
});
|
||||
describe('state hooks', () => {
|
||||
state.testGetter(state.keys.refReady);
|
||||
});
|
||||
describe('initializeApp', () => {
|
||||
test('calls provided function with provided data as args when useEffect is called', () => {
|
||||
const mockIntialize = jest.fn(val => (val));
|
||||
@@ -35,29 +39,32 @@ describe('hooks', () => {
|
||||
});
|
||||
});
|
||||
describe('prepareEditorRef', () => {
|
||||
let output;
|
||||
let hook;
|
||||
beforeEach(() => {
|
||||
output = module.prepareEditorRef();
|
||||
state.mock();
|
||||
hook = module.prepareEditorRef();
|
||||
});
|
||||
afterEach(() => {
|
||||
state.restore();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
const key = state.keys.refReady;
|
||||
test('sets refReady to false by default, ref is null', () => {
|
||||
expect(output.refReady.state).toBe(false);
|
||||
expect(output.editorRef.current).toBe(null);
|
||||
expect(state.stateVals[key]).toEqual(false);
|
||||
expect(hook.editorRef.current).toBe(null);
|
||||
});
|
||||
test('when useEffect triggers, refReady is set to true', () => {
|
||||
expect(updateState).not.toHaveBeenCalled();
|
||||
expect(state.setState[key]).not.toHaveBeenCalled();
|
||||
const [cb, prereqs] = useEffect.mock.calls[0];
|
||||
expect(prereqs).toStrictEqual([]);
|
||||
cb();
|
||||
expect(updateState).toHaveBeenCalledWith({ newVal: true, val: false });
|
||||
expect(state.setState[key]).toHaveBeenCalledWith(true);
|
||||
});
|
||||
test('calling setEditorRef sets the ref value', () => {
|
||||
const fakeEditor = { editor: 'faKe Editor' };
|
||||
expect(output.editorRef.current).not.toBe(fakeEditor);
|
||||
output.setEditorRef.cb(fakeEditor);
|
||||
expect(output.editorRef.current).toBe(fakeEditor);
|
||||
expect(hook.editorRef.current).not.toBe(fakeEditor);
|
||||
hook.setEditorRef.cb(fakeEditor);
|
||||
expect(hook.editorRef.current).toBe(fakeEditor);
|
||||
});
|
||||
});
|
||||
describe('navigateTo', () => {
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export { default as StrictDict } from './StrictDict';
|
||||
export { default as keyStore } from './keyStore';
|
||||
|
||||
10
src/editors/utils/keyStore.js
Normal file
10
src/editors/utils/keyStore.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import StrictDict from './StrictDict';
|
||||
|
||||
const keyStore = (collection) => StrictDict(
|
||||
Object.keys(collection).reduce(
|
||||
(obj, key) => ({ ...obj, [key]: key }),
|
||||
{},
|
||||
),
|
||||
);
|
||||
|
||||
export default keyStore;
|
||||
@@ -69,6 +69,8 @@ jest.mock('@edx/paragon', () => jest.requireActual('testUtils').mockNestedCompon
|
||||
Footer: 'ModalDialog.Footer',
|
||||
Header: 'ModalDialog.Header',
|
||||
Title: 'ModalDialog.Title',
|
||||
Body: 'ModalDialog.Body',
|
||||
CloseButton: 'ModalDialog.CloseButton',
|
||||
},
|
||||
Form: {
|
||||
Checkbox: 'Form.Checkbox',
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* istanbul ignore file */
|
||||
import react from 'react';
|
||||
import { StrictDict } from './editors/utils';
|
||||
/**
|
||||
* Mocked formatMessage provided by react-intl
|
||||
@@ -90,6 +92,11 @@ export const mockNestedComponents = (mapping) => Object.entries(mapping).reduce(
|
||||
* import * as hooks from './hooks';
|
||||
* const state = new MockUseState(hooks)
|
||||
* ...
|
||||
* describe('state hooks', () => {
|
||||
* state.testGetter(state.keys.isOpen);
|
||||
* state.testGetter(state.keys.hasDoors);
|
||||
* state.testGetter(state.keys.selected);
|
||||
* });
|
||||
* describe('exampleHook', () => {
|
||||
* beforeEach(() => { state.mock(); });
|
||||
* it('returns null if isOpen is default value', () => {
|
||||
@@ -112,10 +119,12 @@ export class MockUseState {
|
||||
this.hooks = hooks;
|
||||
this.oldState = null;
|
||||
this.setState = {};
|
||||
this.stateVals = {};
|
||||
|
||||
this.mock = this.mock.bind(this);
|
||||
this.restore = this.restore.bind(this);
|
||||
this.mockVal = this.mockVal.bind(this);
|
||||
this.testGetter = this.testGetter.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,10 +143,18 @@ export class MockUseState {
|
||||
mock() {
|
||||
this.oldState = this.hooks.state;
|
||||
Object.keys(this.keys).forEach(key => {
|
||||
this.hooks.state[key] = jest.fn(val => [val, this.setState[key]]);
|
||||
this.hooks.state[key] = jest.fn(val => {
|
||||
this.stateVals[key] = val;
|
||||
return [val, this.setState[key]];
|
||||
});
|
||||
});
|
||||
this.setState = Object.keys(this.keys).reduce(
|
||||
(obj, key) => ({ ...obj, [key]: jest.fn() }),
|
||||
(obj, key) => ({
|
||||
...obj,
|
||||
[key]: jest.fn(val => {
|
||||
this.hooks.state[key] = val;
|
||||
}),
|
||||
}),
|
||||
{},
|
||||
);
|
||||
}
|
||||
@@ -157,4 +174,13 @@ export class MockUseState {
|
||||
mockVal(key, val) {
|
||||
this.hooks.state[key].mockReturnValueOnce([val, this.setState[key]]);
|
||||
}
|
||||
|
||||
testGetter(key) {
|
||||
test(`${key} state getter should return useState passthrough`, () => {
|
||||
const testValue = 'some value';
|
||||
const useState = (val) => ({ useState: val });
|
||||
jest.spyOn(react, 'useState').mockImplementationOnce(useState);
|
||||
expect(this.hooks.state[key](testValue)).toEqual(useState(testValue));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
8
www/package-lock.json
generated
8
www/package-lock.json
generated
@@ -1173,9 +1173,9 @@
|
||||
"integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA=="
|
||||
},
|
||||
"@edx/brand": {
|
||||
"version": "npm:@edx/brand-openedx@1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/brand-openedx/-/brand-openedx-1.1.0.tgz",
|
||||
"integrity": "sha512-ne2ZKF1r0akkt0rEzCAQAk4cTDTI2GiWCpc+T7ldQpw9X57OnUB16dKsFNe40C9uEjL5h3Ps/ZsFM5dm4cIkEQ=="
|
||||
"version": "npm:@edx/brand-edx.org@2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@edx/brand-edx.org/-/brand-edx.org-2.0.3.tgz",
|
||||
"integrity": "sha512-QRmq2su1Xy+9GhY3NRZ+WdjtYWHmgfuKbVCW2skxgfgW9Q6kea8L6LrgigfrZtW+kQq/KdX2tVJcYBkB9xALtQ=="
|
||||
},
|
||||
"@edx/eslint-config": {
|
||||
"version": "2.0.0",
|
||||
@@ -2419,7 +2419,7 @@
|
||||
"integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA=="
|
||||
},
|
||||
"@edx/brand": {
|
||||
"version": "npm:@edx/brand@1.1.0",
|
||||
"version": "npm:@edx/brand-openedx@1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/brand-openedx/-/brand-openedx-1.1.0.tgz",
|
||||
"integrity": "sha512-ne2ZKF1r0akkt0rEzCAQAk4cTDTI2GiWCpc+T7ldQpw9X57OnUB16dKsFNe40C9uEjL5h3Ps/ZsFM5dm4cIkEQ=="
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
|
||||
"@edx/brand": "npm:@edx/brand-edx.org@^2.0.3",
|
||||
"@edx/frontend-build": "^9.1.1",
|
||||
"@edx/frontend-platform": "1.14.0",
|
||||
"@edx/frontend-lib-content-components": "file:..",
|
||||
|
||||
Reference in New Issue
Block a user