Skip to content
Snippets Groups Projects
Unverified Commit e34d5ed6 authored by Benjamin Wheeler's avatar Benjamin Wheeler Committed by GitHub
Browse files

Merge pull request #5437 from LLK/revert-4809-refactor-file-uploader

Revert "refactor SB* file uploader into top-level HOC"
parents c368de81 af4ad818
No related branches found
No related tags found
No related merge requests found
......@@ -105,7 +105,6 @@ const GUIComponent = props => {
onRequestCloseTelemetryModal,
onSeeCommunity,
onShare,
onStartSelectingFileUpload,
onTelemetryModalCancel,
onTelemetryModalOptIn,
onTelemetryModalOptOut,
......@@ -227,7 +226,6 @@ const GUIComponent = props => {
onProjectTelemetryEvent={onProjectTelemetryEvent}
onSeeCommunity={onSeeCommunity}
onShare={onShare}
onStartSelectingFileUpload={onStartSelectingFileUpload}
onToggleLoginOpen={onToggleLoginOpen}
/>
<Box className={styles.bodyWrapper}>
......@@ -401,7 +399,6 @@ GUIComponent.propTypes = {
onRequestCloseTelemetryModal: PropTypes.func,
onSeeCommunity: PropTypes.func,
onShare: PropTypes.func,
onStartSelectingFileUpload: PropTypes.func,
onTabSelect: PropTypes.func,
onTelemetryModalCancel: PropTypes.func,
onTelemetryModalOptIn: PropTypes.func,
......
......@@ -17,6 +17,7 @@ import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx';
import Divider from '../divider/divider.jsx';
import LanguageSelector from '../../containers/language-selector.jsx';
import SaveStatus from './save-status.jsx';
import SBFileUploader from '../../containers/sb-file-uploader.jsx';
import ProjectWatcher from '../../containers/project-watcher.jsx';
import MenuBarMenu from './menu-bar-menu.jsx';
import {MenuItem, MenuSection} from '../menu/menu.jsx';
......@@ -391,11 +392,22 @@ class MenuBar extends React.Component {
</MenuSection>
)}
<MenuSection>
<MenuItem
onClick={this.props.onStartSelectingFileUpload}
<SBFileUploader
canSave={this.props.canSave}
userOwnsProject={this.props.userOwnsProject}
>
{this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)}
</MenuItem>
{(className, renderFileInput, handleLoadProject) => (
<MenuItem
className={className}
onClick={handleLoadProject}
>
{/* eslint-disable max-len */}
{this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)}
{/* eslint-enable max-len */}
{renderFileInput()}
</MenuItem>
)}
</SBFileUploader>
<SB3Downloader>{(className, downloadProjectCallback) => (
<MenuItem
className={className}
......@@ -731,7 +743,6 @@ MenuBar.propTypes = {
onRequestCloseLogin: PropTypes.func,
onSeeCommunity: PropTypes.func,
onShare: PropTypes.func,
onStartSelectingFileUpload: PropTypes.func,
onToggleLoginOpen: PropTypes.func,
projectTitle: PropTypes.string,
renderLogin: PropTypes.func,
......
......@@ -27,7 +27,6 @@ import {
import FontLoaderHOC from '../lib/font-loader-hoc.jsx';
import LocalizationHOC from '../lib/localization-hoc.jsx';
import SBFileUploaderHOC from '../lib/sb-file-uploader-hoc.jsx';
import ProjectFetcherHOC from '../lib/project-fetcher-hoc.jsx';
import TitledHOC from '../lib/titled-hoc.jsx';
import ProjectSaverHOC from '../lib/project-saver-hoc.jsx';
......@@ -182,7 +181,6 @@ const WrappedGui = compose(
ProjectSaverHOC,
vmListenerHOC,
vmManagerHOC,
SBFileUploaderHOC,
cloudManagerHOC
)(ConnectedGUI);
......
import bindAll from 'lodash.bindall';
import PropTypes from 'prop-types';
import React from 'react';
import {connect} from 'react-redux';
import {defineMessages, injectIntl, intlShape} from 'react-intl';
import {setProjectTitle} from '../reducers/project-title';
import log from '../lib/log';
import sharedMessages from '../lib/shared-messages';
import {
LoadingStates,
getIsLoadingUpload,
getIsShowingWithoutId,
onLoadedProject,
requestProjectUpload
} from '../reducers/project-state';
import {
openLoadingProject,
closeLoadingProject
} from '../reducers/modals';
import {
closeFileMenu
} from '../reducers/menus';
/**
* SBFileUploader component passes a file input, load handler and props to its child.
* It expects this child to be a function with the signature
* function (renderFileInput, handleLoadProject) {}
* The component can then be used to attach project loading functionality
* to any other component:
*
* <SBFileUploader>{(className, renderFileInput, handleLoadProject) => (
* <MyCoolComponent
* className={className}
* onClick={handleLoadProject}
* >
* {renderFileInput()}
* </MyCoolComponent>
* )}</SBFileUploader>
*/
const messages = defineMessages({
loadError: {
id: 'gui.projectLoader.loadError',
defaultMessage: 'The project file that was selected failed to load.',
description: 'An error that displays when a local project file fails to load.'
}
});
class SBFileUploader extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'getProjectTitleFromFilename',
'renderFileInput',
'setFileInput',
'handleChange',
'handleClick',
'onload',
'resetFileInput'
]);
}
componentWillMount () {
this.reader = new FileReader();
this.reader.onload = this.onload;
this.resetFileInput();
}
componentDidUpdate (prevProps) {
if (this.props.isLoadingUpload && !prevProps.isLoadingUpload && this.fileToUpload && this.reader) {
this.reader.readAsArrayBuffer(this.fileToUpload);
}
}
componentWillUnmount () {
this.reader = null;
this.resetFileInput();
}
resetFileInput () {
this.fileToUpload = null;
if (this.fileInput) {
this.fileInput.value = null;
}
}
getProjectTitleFromFilename (fileInputFilename) {
if (!fileInputFilename) return '';
// only parse title with valid scratch project extensions
// (.sb, .sb2, and .sb3)
const matches = fileInputFilename.match(/^(.*)\.sb[23]?$/);
if (!matches) return '';
return matches[1].substring(0, 100); // truncate project title to max 100 chars
}
// called when user has finished selecting a file to upload
handleChange (e) {
const {
intl,
isShowingWithoutId,
loadingState,
projectChanged,
userOwnsProject
} = this.props;
const thisFileInput = e.target;
if (thisFileInput.files) { // Don't attempt to load if no file was selected
this.fileToUpload = thisFileInput.files[0];
// If user owns the project, or user has changed the project,
// we must confirm with the user that they really intend to replace it.
// (If they don't own the project and haven't changed it, no need to confirm.)
let uploadAllowed = true;
if (userOwnsProject || (projectChanged && isShowingWithoutId)) {
uploadAllowed = confirm( // eslint-disable-line no-alert
intl.formatMessage(sharedMessages.replaceProjectWarning)
);
}
if (uploadAllowed) {
this.props.requestProjectUpload(loadingState);
} else {
this.props.closeFileMenu();
}
}
}
// called when file upload raw data is available in the reader
onload () {
if (this.reader) {
this.props.onLoadingStarted();
const filename = this.fileToUpload && this.fileToUpload.name;
this.props.vm.loadProject(this.reader.result)
.then(() => {
this.props.onLoadingFinished(this.props.loadingState, true);
// Reset the file input after project is loaded
// This is necessary in case the user wants to reload a project
if (filename) {
const uploadedProjectTitle = this.getProjectTitleFromFilename(filename);
this.props.onReceivedProjectTitle(uploadedProjectTitle);
}
this.resetFileInput();
})
.catch(error => {
log.warn(error);
alert(this.props.intl.formatMessage(messages.loadError)); // eslint-disable-line no-alert
this.props.onLoadingFinished(this.props.loadingState, false);
// Reset the file input after project is loaded
// This is necessary in case the user wants to reload a project
this.resetFileInput();
});
}
}
handleClick () {
// open filesystem browsing window
this.fileInput.click();
}
setFileInput (input) {
this.fileInput = input;
}
renderFileInput () {
return (
<input
accept=".sb,.sb2,.sb3"
ref={this.setFileInput}
style={{display: 'none'}}
type="file"
onChange={this.handleChange}
/>
);
}
render () {
return this.props.children(this.props.className, this.renderFileInput, this.handleClick);
}
}
SBFileUploader.propTypes = {
canSave: PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
children: PropTypes.func,
className: PropTypes.string,
closeFileMenu: PropTypes.func,
intl: intlShape.isRequired,
isLoadingUpload: PropTypes.bool,
isShowingWithoutId: PropTypes.bool,
loadingState: PropTypes.oneOf(LoadingStates),
onLoadingFinished: PropTypes.func,
onLoadingStarted: PropTypes.func,
projectChanged: PropTypes.bool,
requestProjectUpload: PropTypes.func,
onReceivedProjectTitle: PropTypes.func,
userOwnsProject: PropTypes.bool,
vm: PropTypes.shape({
loadProject: PropTypes.func
})
};
SBFileUploader.defaultProps = {
className: ''
};
const mapStateToProps = state => {
const loadingState = state.scratchGui.projectState.loadingState;
return {
isLoadingUpload: getIsLoadingUpload(loadingState),
isShowingWithoutId: getIsShowingWithoutId(loadingState),
loadingState: loadingState,
projectChanged: state.scratchGui.projectChanged,
vm: state.scratchGui.vm
};
};
const mapDispatchToProps = (dispatch, ownProps) => ({
closeFileMenu: () => dispatch(closeFileMenu()),
onLoadingFinished: (loadingState, success) => {
dispatch(onLoadedProject(loadingState, ownProps.canSave, success));
dispatch(closeLoadingProject());
dispatch(closeFileMenu());
},
requestProjectUpload: loadingState => dispatch(requestProjectUpload(loadingState)),
onLoadingStarted: () => dispatch(openLoadingProject()),
onReceivedProjectTitle: title => dispatch(setProjectTitle(title))
});
// Allow incoming props to override redux-provided props. Used to mock in tests.
const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(
{}, stateProps, dispatchProps, ownProps
);
export default connect(
mapStateToProps,
mapDispatchToProps,
mergeProps
)(injectIntl(SBFileUploader));
import bindAll from 'lodash.bindall';
import React from 'react';
import PropTypes from 'prop-types';
import {defineMessages, intlShape, injectIntl} from 'react-intl';
import {connect} from 'react-redux';
import log from '../lib/log';
import sharedMessages from './shared-messages';
import {
LoadingStates,
getIsLoadingUpload,
getIsShowingWithoutId,
onLoadedProject,
requestProjectUpload
} from '../reducers/project-state';
import {
openLoadingProject,
closeLoadingProject
} from '../reducers/modals';
import {
closeFileMenu
} from '../reducers/menus';
const messages = defineMessages({
loadError: {
id: 'gui.projectLoader.loadError',
defaultMessage: 'The project file that was selected failed to load.',
description: 'An error that displays when a local project file fails to load.'
}
});
/**
* Higher Order Component to provide behavior for loading local project files into editor.
* @param {React.Component} WrappedComponent the component to add project file loading functionality to
* @returns {React.Component} WrappedComponent with project file loading functionality added
*
* <SBFileUploaderHOC>
* <WrappedComponent />
* </SBFileUploaderHOC>
*/
const SBFileUploaderHOC = function (WrappedComponent) {
class SBFileUploaderComponent extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'createFileObjects',
'getProjectTitleFromFilename',
'handleFinishedLoadingUpload',
'handleStartSelectingFileUpload',
'handleChange',
'onload',
'removeFileObjects'
]);
}
componentDidUpdate (prevProps) {
if (this.props.isLoadingUpload && !prevProps.isLoadingUpload) {
this.handleFinishedLoadingUpload(); // cue step 5 below
}
}
componentWillUnmount () {
this.removeFileObjects();
}
// step 1: this is where the upload process begins
handleStartSelectingFileUpload () {
this.createFileObjects(); // go to step 2
}
// step 2: create a FileReader and an <input> element, and issue a
// pseudo-click to it. That will open the file chooser dialog.
createFileObjects () {
// redo step 7, in case it got skipped last time and its objects are
// still in memory
this.removeFileObjects();
// create fileReader
this.fileReader = new FileReader();
this.fileReader.onload = this.onload;
// create <input> element and add it to DOM
this.inputElement = document.createElement('input');
this.inputElement.accept = '.sb,.sb2,.sb3';
this.inputElement.style = 'display: none;';
this.inputElement.type = 'file';
this.inputElement.onchange = this.handleChange; // connects to step 3
document.body.appendChild(this.inputElement);
// simulate a click to open file chooser dialog
this.inputElement.click();
}
// step 3: user has picked a file using the file chooser dialog.
// We don't actually load the file here, we only decide whether to do so.
handleChange (e) {
const {
intl,
isShowingWithoutId,
loadingState,
projectChanged,
userOwnsProject
} = this.props;
const thisFileInput = e.target;
if (thisFileInput.files) { // Don't attempt to load if no file was selected
this.fileToUpload = thisFileInput.files[0];
// If user owns the project, or user has changed the project,
// we must confirm with the user that they really intend to
// replace it. (If they don't own the project and haven't
// changed it, no need to confirm.)
let uploadAllowed = true;
if (userOwnsProject || (projectChanged && isShowingWithoutId)) {
uploadAllowed = confirm( // eslint-disable-line no-alert
intl.formatMessage(sharedMessages.replaceProjectWarning)
);
}
if (uploadAllowed) {
// cues step 4
this.props.requestProjectUpload(loadingState);
} else {
// skips ahead to step 7
this.removeFileObjects();
}
this.props.closeFileMenu();
}
}
// step 4 is below, in mapDispatchToProps
// step 5: called from componentDidUpdate when project state shows
// that project data has finished "uploading" into the browser
handleFinishedLoadingUpload () {
if (this.fileToUpload && this.fileReader) {
// begin to read data from the file. When finished,
// cues step 6 using the reader's onload callback
this.fileReader.readAsArrayBuffer(this.fileToUpload);
} else {
this.props.cancelFileUpload(this.props.loadingState);
// skip ahead to step 7
this.removeFileObjects();
}
}
// used in step 6 below
getProjectTitleFromFilename (fileInputFilename) {
if (!fileInputFilename) return '';
// only parse title with valid scratch project extensions
// (.sb, .sb2, and .sb3)
const matches = fileInputFilename.match(/^(.*)\.sb[23]?$/);
if (!matches) return '';
return matches[1].substring(0, 100); // truncate project title to max 100 chars
}
// step 6: attached as a handler on our FileReader object; called when
// file upload raw data is available in the reader
onload () {
if (this.fileReader) {
this.props.onLoadingStarted();
const filename = this.fileToUpload && this.fileToUpload.name;
this.props.vm.loadProject(this.fileReader.result)
.then(() => {
this.props.onLoadingFinished(this.props.loadingState, true);
if (filename) {
const uploadedProjectTitle = this.getProjectTitleFromFilename(filename);
this.props.onUpdateProjectTitle(uploadedProjectTitle);
}
})
.catch(error => {
log.warn(error);
this.props.intl.formatMessage(messages.loadError);
this.props.onLoadingFinished(this.props.loadingState, false);
})
.then(() => {
// go back to step 7: whether project loading succeeded
// or failed, reset file objects
this.removeFileObjects();
});
}
}
// step 7: remove the <input> element from the DOM and clear reader and
// fileToUpload reference, so those objects can be garbage collected
removeFileObjects () {
if (this.inputElement) {
this.inputElement.value = null;
document.body.removeChild(this.inputElement);
}
this.inputElement = null;
this.fileReader = null;
this.fileToUpload = null;
}
render () {
const {
/* eslint-disable no-unused-vars */
cancelFileUpload,
closeFileMenu: closeFileMenuProp,
isLoadingUpload,
isShowingWithoutId,
loadingState,
onLoadingFinished,
onLoadingStarted,
projectChanged,
requestProjectUpload: requestProjectUploadProp,
userOwnsProject,
/* eslint-enable no-unused-vars */
...componentProps
} = this.props;
return (
<React.Fragment>
<WrappedComponent
onStartSelectingFileUpload={this.handleStartSelectingFileUpload}
{...componentProps}
/>
</React.Fragment>
);
}
}
SBFileUploaderComponent.propTypes = {
canSave: PropTypes.bool,
cancelFileUpload: PropTypes.func,
closeFileMenu: PropTypes.func,
intl: intlShape.isRequired,
isLoadingUpload: PropTypes.bool,
isShowingWithoutId: PropTypes.bool,
loadingState: PropTypes.oneOf(LoadingStates),
onLoadingFinished: PropTypes.func,
onLoadingStarted: PropTypes.func,
onUpdateProjectTitle: PropTypes.func,
projectChanged: PropTypes.bool,
requestProjectUpload: PropTypes.func,
userOwnsProject: PropTypes.bool,
vm: PropTypes.shape({
loadProject: PropTypes.func
})
};
const mapStateToProps = (state, ownProps) => {
const loadingState = state.scratchGui.projectState.loadingState;
const user = state.session && state.session.session && state.session.session.user;
return {
isLoadingUpload: getIsLoadingUpload(loadingState),
isShowingWithoutId: getIsShowingWithoutId(loadingState),
loadingState: loadingState,
projectChanged: state.scratchGui.projectChanged,
userOwnsProject: ownProps.authorUsername && user &&
(ownProps.authorUsername === user.username),
vm: state.scratchGui.vm
};
};
const mapDispatchToProps = (dispatch, ownProps) => ({
cancelFileUpload: loadingState => dispatch(onLoadedProject(loadingState, false, false)),
closeFileMenu: () => dispatch(closeFileMenu()),
// transition project state from loading to regular, and close
// loading screen and file menu
onLoadingFinished: (loadingState, success) => {
dispatch(onLoadedProject(loadingState, ownProps.canSave, success));
dispatch(closeLoadingProject());
dispatch(closeFileMenu());
},
// show project loading screen
onLoadingStarted: () => dispatch(openLoadingProject()),
// step 4: transition the project state so we're ready to handle the new
// project data. When this is done, the project state transition will be
// noticed by componentDidUpdate()
requestProjectUpload: loadingState => dispatch(requestProjectUpload(loadingState))
});
// Allow incoming props to override redux-provided props. Used to mock in tests.
const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(
{}, stateProps, dispatchProps, ownProps
);
return injectIntl(connect(
mapStateToProps,
mapDispatchToProps,
mergeProps
)(SBFileUploaderComponent));
};
export {
SBFileUploaderHOC as default
};
......@@ -75,7 +75,6 @@ describe('Menu bar settings', () => {
test('User is not warned before uploading project file over a fresh project', async () => {
await loadUri(uri);
await clickText('File');
await clickText('Load from your computer');
const input = await findByXpath('//input[@accept=".sb,.sb2,.sb3"]');
await input.sendKeys(path.resolve(__dirname, '../fixtures/project1.sb3'));
// No replace alert since no changes were made
......@@ -90,7 +89,6 @@ describe('Menu bar settings', () => {
await clickText('delete', scope.spriteTile);
await clickText('File');
await clickText('Load from your computer');
const input = await findByXpath('//input[@accept=".sb,.sb2,.sb3"]');
await input.sendKeys(path.resolve(__dirname, '../fixtures/project1.sb3'));
await driver.switchTo().alert()
......
import React from 'react';
import {shallowWithIntl} from '../../helpers/intl-helpers.jsx';
import configureStore from 'redux-mock-store';
import SBFileUploader from '../../../src/containers/sb-file-uploader';
import {LoadingState} from '../../../src/reducers/project-state';
jest.mock('react-ga'); // must mock this entire library, or lib/analytics causes error
describe('SBFileUploader Container', () => {
const mockStore = configureStore();
let onLoadingFinished;
let onLoadingStarted;
let store;
// Wrap this in a function so it gets test specific states and can be reused.
const getContainer = function () {
return (
<SBFileUploader
onLoadingFinished={onLoadingFinished}
onLoadingStarted={onLoadingStarted}
>
{(renderFileInput, loadProject) => (
<div
onClick={loadProject}
/>
)}
</SBFileUploader>
);
};
beforeEach(() => {
store = mockStore({
scratchGui: {
projectState: {
loadingState: LoadingState.SHOWING_WITH_ID
},
vm: {}
}
});
onLoadingFinished = jest.fn();
onLoadingStarted = jest.fn();
});
test('correctly sets title with .sb3 filename', () => {
const wrapper = shallowWithIntl(getContainer(), {context: {store}});
const instance = wrapper
.dive() // unwrap redux Connect(InjectIntl(SBFileUploader))
.dive() // unwrap InjectIntl(SBFileUploader)
.instance(); // SBFileUploader
const projectName = instance.getProjectTitleFromFilename('my project is great.sb3');
expect(projectName).toBe('my project is great');
});
test('correctly sets title with .sb2 filename', () => {
const wrapper = shallowWithIntl(getContainer(), {context: {store}});
const instance = wrapper
.dive() // unwrap redux Connect(InjectIntl(SBFileUploader))
.dive() // unwrap InjectIntl(SBFileUploader)
.instance(); // SBFileUploader
const projectName = instance.getProjectTitleFromFilename('my project is great.sb2');
expect(projectName).toBe('my project is great');
});
test('correctly sets title with .sb filename', () => {
const wrapper = shallowWithIntl(getContainer(), {context: {store}});
const instance = wrapper
.dive() // unwrap redux Connect(InjectIntl(SBFileUploader))
.dive() // unwrap InjectIntl(SBFileUploader)
.instance(); // SBFileUploader
const projectName = instance.getProjectTitleFromFilename('my project is great.sb');
expect(projectName).toBe('my project is great');
});
test('sets blank title with filename with no extension', () => {
const wrapper = shallowWithIntl(getContainer(), {context: {store}});
const instance = wrapper
.dive() // unwrap redux Connect(InjectIntl(SBFileUploader))
.dive() // unwrap InjectIntl(SBFileUploader)
.instance(); // SBFileUploader
const projectName = instance.getProjectTitleFromFilename('my project is great');
expect(projectName).toBe('');
});
});
import 'web-audio-test-api';
import React from 'react';
import configureStore from 'redux-mock-store';
import {mountWithIntl, shallowWithIntl} from '../../helpers/intl-helpers.jsx';
import {LoadingState} from '../../../src/reducers/project-state';
import VM from 'scratch-vm';
import SBFileUploaderHOC from '../../../src/lib/sb-file-uploader-hoc.jsx';
describe('SBFileUploaderHOC', () => {
const mockStore = configureStore();
let store;
let vm;
// Wrap this in a function so it gets test specific states and can be reused.
const getContainer = function () {
const Component = () => <div />;
return SBFileUploaderHOC(Component);
};
const shallowMountWithContext = component => (
shallowWithIntl(component, {context: {store}})
);
const unwrappedInstance = () => {
const WrappedComponent = getContainer();
// default starting state: looking at a project you created, not logged in
const wrapper = shallowMountWithContext(
<WrappedComponent
projectChanged
canSave={false}
cancelFileUpload={jest.fn()}
closeFileMenu={jest.fn()}
requestProjectUpload={jest.fn()}
userOwnsProject={false}
vm={vm}
onLoadingFinished={jest.fn()}
onLoadingStarted={jest.fn()}
onUpdateProjectTitle={jest.fn()}
/>
);
return wrapper
.dive() // unwrap intl
.dive() // unwrap redux Connect(SBFileUploaderComponent)
.instance(); // SBFileUploaderComponent
};
beforeEach(() => {
vm = new VM();
store = mockStore({
scratchGui: {
projectState: {
loadingState: LoadingState.SHOWING_WITHOUT_ID
},
vm: {}
},
locales: {
locale: 'en'
}
});
});
test('correctly sets title with .sb3 filename', () => {
const projectName = unwrappedInstance().getProjectTitleFromFilename('my project is great.sb3');
expect(projectName).toBe('my project is great');
});
test('correctly sets title with .sb2 filename', () => {
const projectName = unwrappedInstance().getProjectTitleFromFilename('my project is great.sb2');
expect(projectName).toBe('my project is great');
});
test('correctly sets title with .sb filename', () => {
const projectName = unwrappedInstance().getProjectTitleFromFilename('my project is great.sb');
expect(projectName).toBe('my project is great');
});
test('sets blank title with filename with no extension', () => {
const projectName = unwrappedInstance().getProjectTitleFromFilename('my project is great');
expect(projectName).toBe('');
});
test('if isLoadingUpload becomes true, without fileToUpload set, will call cancelFileUpload', () => {
const mockedCancelFileUpload = jest.fn();
const WrappedComponent = getContainer();
const mounted = mountWithIntl(
<WrappedComponent
projectChanged
canSave={false}
cancelFileUpload={mockedCancelFileUpload}
closeFileMenu={jest.fn()}
isLoadingUpload={false}
requestProjectUpload={jest.fn()}
store={store}
userOwnsProject={false}
vm={vm}
onLoadingFinished={jest.fn()}
onLoadingStarted={jest.fn()}
onUpdateProjectTitle={jest.fn()}
/>
);
mounted.setProps({
isLoadingUpload: true
});
expect(mockedCancelFileUpload).toHaveBeenCalled();
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment