diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index 9846eed7566356ed1e5395def9b59388bb870958..8524b2ba36cdf19ebe818ece46fa79021042dc64 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -105,6 +105,7 @@ const GUIComponent = props => {
         onRequestCloseTelemetryModal,
         onSeeCommunity,
         onShare,
+        onStartSelectingFileUpload,
         onTelemetryModalCancel,
         onTelemetryModalOptIn,
         onTelemetryModalOptOut,
@@ -226,6 +227,7 @@ const GUIComponent = props => {
                     onProjectTelemetryEvent={onProjectTelemetryEvent}
                     onSeeCommunity={onSeeCommunity}
                     onShare={onShare}
+                    onStartSelectingFileUpload={onStartSelectingFileUpload}
                     onToggleLoginOpen={onToggleLoginOpen}
                 />
                 <Box className={styles.bodyWrapper}>
@@ -399,6 +401,7 @@ GUIComponent.propTypes = {
     onRequestCloseTelemetryModal: PropTypes.func,
     onSeeCommunity: PropTypes.func,
     onShare: PropTypes.func,
+    onStartSelectingFileUpload: PropTypes.func,
     onTabSelect: PropTypes.func,
     onTelemetryModalCancel: PropTypes.func,
     onTelemetryModalOptIn: PropTypes.func,
diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx
index 98bc128a92f11fe272d674513930e53f897d58bb..05a5294bc30eadfe41a723f7712bc4b3fcb0e837 100644
--- a/src/components/menu-bar/menu-bar.jsx
+++ b/src/components/menu-bar/menu-bar.jsx
@@ -17,7 +17,6 @@ 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';
@@ -392,22 +391,11 @@ class MenuBar extends React.Component {
                                         </MenuSection>
                                     )}
                                     <MenuSection>
-                                        <SBFileUploader
-                                            canSave={this.props.canSave}
-                                            userOwnsProject={this.props.userOwnsProject}
+                                        <MenuItem
+                                            onClick={this.props.onStartSelectingFileUpload}
                                         >
-                                            {(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>
+                                            {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)}
+                                        </MenuItem>
                                         <SB3Downloader>{(className, downloadProjectCallback) => (
                                             <MenuItem
                                                 className={className}
@@ -743,6 +731,7 @@ MenuBar.propTypes = {
     onRequestCloseLogin: PropTypes.func,
     onSeeCommunity: PropTypes.func,
     onShare: PropTypes.func,
+    onStartSelectingFileUpload: PropTypes.func,
     onToggleLoginOpen: PropTypes.func,
     projectTitle: PropTypes.string,
     renderLogin: PropTypes.func,
diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index 314c38893518b0a8e5a8bce6a847dc008a6ac5b6..c10df0fb3e9bf73c3ca60d342f8d4f155a6e61c0 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -27,6 +27,7 @@ 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';
@@ -181,6 +182,7 @@ const WrappedGui = compose(
     ProjectSaverHOC,
     vmListenerHOC,
     vmManagerHOC,
+    SBFileUploaderHOC,
     cloudManagerHOC
 )(ConnectedGUI);
 
diff --git a/src/containers/sb-file-uploader.jsx b/src/containers/sb-file-uploader.jsx
deleted file mode 100644
index 2019f719e978a9c485b540488f873cb8046644ee..0000000000000000000000000000000000000000
--- a/src/containers/sb-file-uploader.jsx
+++ /dev/null
@@ -1,226 +0,0 @@
-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));
diff --git a/src/lib/sb-file-uploader-hoc.jsx b/src/lib/sb-file-uploader-hoc.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..90dd1ff5382acc3b7cbff02aeae46292d5bf1e1f
--- /dev/null
+++ b/src/lib/sb-file-uploader-hoc.jsx
@@ -0,0 +1,241 @@
+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, [
+                'getProjectTitleFromFilename',
+                'handleStartSelectingFileUpload',
+                'setFileInput',
+                'handleChange',
+                'onload',
+                'resetFileInput'
+            ]);
+        }
+        componentWillMount () {
+            this.reader = new FileReader();
+            this.reader.onload = this.onload;
+            this.resetFileInput();
+        }
+        componentDidUpdate (prevProps) {
+            if (this.props.isLoadingUpload && !prevProps.isLoadingUpload) {
+                if (this.fileToUpload && this.reader) {
+                    this.reader.readAsArrayBuffer(this.fileToUpload);
+                } else {
+                    this.props.cancelFileUpload(this.props.loadingState);
+                }
+            }
+        }
+        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.resetFileInput();
+                }
+                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.onUpdateProjectTitle(uploadedProjectTitle);
+                        }
+                        this.resetFileInput();
+                    })
+                    .catch(error => {
+                        log.warn(error);
+                        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();
+                    });
+            }
+        }
+        handleStartSelectingFileUpload () {
+            // open filesystem browsing window
+            this.fileInput.click();
+        }
+        setFileInput (input) {
+            this.fileInput = input;
+        }
+        render () {
+            const fileInput = (
+                <input
+                    accept=".sb,.sb2,.sb3"
+                    ref={this.setFileInput}
+                    style={{display: 'none'}}
+                    type="file"
+                    onChange={this.handleChange}
+                />
+            );
+            const {
+                /* eslint-disable no-unused-vars */
+                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}
+                    />
+                    {fileInput}
+                </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()),
+        onLoadingFinished: (loadingState, success) => {
+            dispatch(onLoadedProject(loadingState, ownProps.canSave, success));
+            dispatch(closeLoadingProject());
+            dispatch(closeFileMenu());
+        },
+        onLoadingStarted: () => dispatch(openLoadingProject()),
+        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
+};
diff --git a/src/reducers/project-state.js b/src/reducers/project-state.js
index 91e51bbe48e6b8fa1c0cb1b254cbce7350f4a246..a26d7c97184cf4b93f2f1b3c7c4d50a56353454a 100644
--- a/src/reducers/project-state.js
+++ b/src/reducers/project-state.js
@@ -17,7 +17,7 @@ const START_AUTO_UPDATING = 'scratch-gui/project-state/START_AUTO_UPDATING';
 const START_CREATING_NEW = 'scratch-gui/project-state/START_CREATING_NEW';
 const START_ERROR = 'scratch-gui/project-state/START_ERROR';
 const START_FETCHING_NEW = 'scratch-gui/project-state/START_FETCHING_NEW';
-const START_LOADING_VM_FILE_UPLOAD = 'scratch-gui/project-state/START_LOADING_FILE_UPLOAD';
+const START_LOADING_VM_FILE_UPLOAD = 'scratch-gui/project-state/START_LOADING_VM_FILE_UPLOAD';
 const START_MANUAL_UPDATING = 'scratch-gui/project-state/START_MANUAL_UPDATING';
 const START_REMIXING = 'scratch-gui/project-state/START_REMIXING';
 const START_UPDATING_BEFORE_CREATING_COPY = 'scratch-gui/project-state/START_UPDATING_BEFORE_CREATING_COPY';
@@ -435,10 +435,21 @@ const onLoadedProject = (loadingState, canSave, success) => {
         default:
             return;
         }
+    } else {
+        switch (loadingState) {
+        case LoadingState.LOADING_VM_WITH_ID:
+        case LoadingState.LOADING_VM_FILE_UPLOAD:
+            return {
+                type: RETURN_TO_SHOWING
+            };
+        case LoadingState.LOADING_VM_NEW_DEFAULT:
+            return {
+                type: START_ERROR
+            };
+        default:
+            return;
+        }
     }
-    return {
-        type: RETURN_TO_SHOWING
-    };
 };
 
 const doneUpdatingProject = loadingState => {
diff --git a/test/unit/containers/sb-file-uploader.test.jsx b/test/unit/containers/sb-file-uploader.test.jsx
deleted file mode 100644
index c757114b4ca7e387c78e7477931cf31a04c91a9a..0000000000000000000000000000000000000000
--- a/test/unit/containers/sb-file-uploader.test.jsx
+++ /dev/null
@@ -1,84 +0,0 @@
-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('');
-    });
-});
diff --git a/test/unit/reducers/project-state-reducer.test.js b/test/unit/reducers/project-state-reducer.test.js
index 2683c5beec159d4a08a86b003f15905071b82648..6938085a3456da6c8c74d5646d32e6976f5951f9 100644
--- a/test/unit/reducers/project-state-reducer.test.js
+++ b/test/unit/reducers/project-state-reducer.test.js
@@ -92,16 +92,49 @@ test('onFetchedProjectData new loads project data into vm', () => {
     expect(resultState.projectData).toBe('1010101');
 });
 
-test('onLoadedProject upload, with canSave false, shows without id', () => {
+// onLoadedProject: LOADING_VM_WITH_ID
+
+test('onLoadedProject (LOADING_VM_WITH_ID, true, true) shows with id', () => {
     const initialState = {
-        loadingState: LoadingState.LOADING_VM_FILE_UPLOAD
+        loadingState: LoadingState.LOADING_VM_WITH_ID
     };
-    const action = onLoadedProject(initialState.loadingState, false, true);
+    const action = onLoadedProject(initialState.loadingState, true, true);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
+});
+
+test('onLoadedProject (LOADING_VM_WITH_ID, false, true) shows with id', () => {
+    const initialState = {
+        loadingState: LoadingState.LOADING_VM_WITH_ID
+    };
+    const action = onLoadedProject(initialState.loadingState, true, true);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
+});
+
+test('onLoadedProject (LOADING_VM_WITH_ID, false, false), with project id, shows with id', () => {
+    const initialState = {
+        loadingState: LoadingState.LOADING_VM_WITH_ID,
+        projectId: '12345'
+    };
+    const action = onLoadedProject(initialState.loadingState, false, false);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
+});
+
+test('onLoadedProject (LOADING_VM_WITH_ID, false, false), with no project id, shows without id', () => {
+    const initialState = {
+        loadingState: LoadingState.LOADING_VM_WITH_ID,
+        projectId: null
+    };
+    const action = onLoadedProject(initialState.loadingState, false, false);
     const resultState = projectStateReducer(initialState, action);
     expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITHOUT_ID);
 });
 
-test('onLoadedProject upload, with canSave true, prepares to save', () => {
+// onLoadedProject: LOADING_VM_FILE_UPLOAD
+
+test('onLoadedProject(LOADING_VM_FILE_UPLOAD, true, true) prepares to save', () => {
     const initialState = {
         loadingState: LoadingState.LOADING_VM_FILE_UPLOAD
     };
@@ -110,25 +143,38 @@ test('onLoadedProject upload, with canSave true, prepares to save', () => {
     expect(resultState.loadingState).toBe(LoadingState.AUTO_UPDATING);
 });
 
-test('onLoadedProject with id shows with id', () => {
+test('onLoadedProject (LOADING_VM_FILE_UPLOAD, false, true) shows without id', () => {
     const initialState = {
-        loadingState: LoadingState.LOADING_VM_WITH_ID
+        loadingState: LoadingState.LOADING_VM_FILE_UPLOAD
     };
-    const action = onLoadedProject(initialState.loadingState, true, true);
+    const action = onLoadedProject(initialState.loadingState, false, true);
+    const resultState = projectStateReducer(initialState, action);
+    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITHOUT_ID);
+});
+
+test('onLoadedProject (LOADING_VM_FILE_UPLOAD, false, false), with project id, shows with id', () => {
+    const initialState = {
+        loadingState: LoadingState.LOADING_VM_FILE_UPLOAD,
+        projectId: '12345'
+    };
+    const action = onLoadedProject(initialState.loadingState, false, false);
     const resultState = projectStateReducer(initialState, action);
     expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
 });
 
-test('onLoadedProject new shows without id', () => {
+test('onLoadedProject (LOADING_VM_FILE_UPLOAD, false, false), with no project id, shows without id', () => {
     const initialState = {
-        loadingState: LoadingState.LOADING_VM_NEW_DEFAULT
+        loadingState: LoadingState.LOADING_VM_FILE_UPLOAD,
+        projectId: null
     };
-    const action = onLoadedProject(initialState.loadingState, false, true);
+    const action = onLoadedProject(initialState.loadingState, false, false);
     const resultState = projectStateReducer(initialState, action);
     expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITHOUT_ID);
 });
 
-test('onLoadedProject new, to save shows without id', () => {
+// onLoadedProject: LOADING_VM_NEW_DEFAULT
+
+test('onLoadedProject (LOADING_VM_NEW_DEFAULT, true, true) shows without id', () => {
     const initialState = {
         loadingState: LoadingState.LOADING_VM_NEW_DEFAULT
     };
@@ -137,26 +183,26 @@ test('onLoadedProject new, to save shows without id', () => {
     expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITHOUT_ID);
 });
 
-test('onLoadedProject with success false, no project id, shows without id', () => {
+test('onLoadedProject (LOADING_VM_NEW_DEFAULT, false, true) shows without id', () => {
     const initialState = {
-        loadingState: LoadingState.LOADING_VM_WITH_ID,
-        projectId: null
+        loadingState: LoadingState.LOADING_VM_NEW_DEFAULT
     };
-    const action = onLoadedProject(initialState.loadingState, false, false);
+    const action = onLoadedProject(initialState.loadingState, false, true);
     const resultState = projectStateReducer(initialState, action);
     expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITHOUT_ID);
 });
 
-test('onLoadedProject with success false, valid project id, shows with id', () => {
+test('onLoadedProject (LOADING_VM_NEW_DEFAULT, false, false) shows error', () => {
     const initialState = {
-        loadingState: LoadingState.LOADING_VM_WITH_ID,
-        projectId: '12345'
+        loadingState: LoadingState.LOADING_VM_NEW_DEFAULT
     };
     const action = onLoadedProject(initialState.loadingState, false, false);
     const resultState = projectStateReducer(initialState, action);
-    expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
+    expect(resultState.loadingState).toBe(LoadingState.ERROR);
 });
 
+// doneUpdatingProject
+
 test('doneUpdatingProject with id shows with id', () => {
     const initialState = {
         loadingState: LoadingState.MANUAL_UPDATING
diff --git a/test/unit/util/project-saver-hoc.test.jsx b/test/unit/util/project-saver-hoc.test.jsx
index 0ec3190bf773fdf74aa6cc39c671b27e0df32beb..f5ac7b680e669c758c6af021643be10fc4d35692 100644
--- a/test/unit/util/project-saver-hoc.test.jsx
+++ b/test/unit/util/project-saver-hoc.test.jsx
@@ -56,7 +56,7 @@ describe('projectSaverHOC', () => {
         expect(mockedUpdateProject).toHaveBeenCalled();
     });
 
-    test('if canSave is alreatdy true and we show a project with an id, project will NOT be saved', () => {
+    test('if canSave is already true and we show a project with an id, project will NOT be saved', () => {
         const mockedSaveProject = jest.fn();
         const Component = () => <div />;
         const WrappedComponent = projectSaverHOC(Component);
diff --git a/test/unit/util/sb-file-uploader-hoc.test.jsx b/test/unit/util/sb-file-uploader-hoc.test.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..c200157c984d5afaf1ff384da292a0b417985210
--- /dev/null
+++ b/test/unit/util/sb-file-uploader-hoc.test.jsx
@@ -0,0 +1,108 @@
+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();
+    });
+});