remix and save as a copy functionality

made remixing, saving as copy states; made project state naming more consistent

changed meaning of canCreateNew to true creation

fixed project-saver-hoc test

removed Remix option from standalone player menu; renamed canSaveAsCopy to canCreateCopy

added parenthesis missing from rebase

fixed create new test
This commit is contained in:
Ben Wheeler committed 2018-10-24 11:29:35 -04:00
1 parent 4fd0019d5d
commit 80f4e01d47
6 files changed
+298 -157

No files matched your search

+5 -5
View File
@@ -66,7 +66,7 @@ const GUIComponent = props => {
canCreateNew,
canRemix,
canSave,
canSaveAsCopy,
canCreateCopy,
canShare,
children,
connectionModalVisible,
@@ -178,10 +178,10 @@ const GUIComponent = props => {
) : null}
<MenuBar
accountNavOpen={accountNavOpen}
canCreateCopy={canCreateCopy}
canCreateNew={canCreateNew}
canRemix={canRemix}
canSave={canSave}
canSaveAsCopy={canSaveAsCopy}
canShare={canShare}
className={styles.menuBarPosition}
enableCommunity={enableCommunity}
@@ -328,10 +328,10 @@ GUIComponent.propTypes = {
}),
basePath: PropTypes.string,
blocksTabVisible: PropTypes.bool,
canCreateCopy: PropTypes.bool,
canCreateNew: PropTypes.bool,
canRemix: PropTypes.bool,
canSave: PropTypes.bool,
canSaveAsCopy: PropTypes.bool,
canShare: PropTypes.bool,
cardsVisible: PropTypes.bool,
children: PropTypes.node,
@@ -373,10 +373,10 @@ GUIComponent.defaultProps = {
visible: false
},
basePath: './',
canCreateNew: true,
canCreateNew: false,
canRemix: false,
canSave: false,
canSaveAsCopy: false,
canCreateCopy: false,
canShare: false,
onUpdateProjectTitle: () => {},
showComingSoon: false,
+45 -27
View File
@@ -26,7 +26,9 @@ import {
getIsUpdating,
getIsShowingProject,
requestNewProject,
saveProject
remixProject,
updateProject,
saveProjectAsCopy
} from '../../reducers/project-state';
import {
openAccountMenu,
@@ -130,7 +132,9 @@ class MenuBar extends React.Component {
super(props);
bindAll(this, [
'handleClickNew',
'handleClickRemix',
'handleClickSave',
'handleClickSaveAsCopy',
'handleCloseFileMenuAndThen',
'handleLanguageMouseUp',
'handleRestoreOption',
@@ -145,16 +149,23 @@ class MenuBar extends React.Component {
}
}
handleClickNew () {
// if canSave===true, it's safe to replace current project, since we will auto-save first
const readyToReplaceProject =
this.props.canSave || confirm('Replace contents of the current project?'); // eslint-disable-line no-alert
// if canCreateNew===true, it's safe to replace current project, since we will auto-save first.
// else confirm first.
const readyToReplaceProject = this.props.canCreateNew ||
confirm('Replace contents of the current project?'); // eslint-disable-line no-alert
if (readyToReplaceProject) {
this.props.onClickNew(this.props.canSave);
this.props.onClickNew(this.props.canCreateNew);
}
}
handleClickRemix () {
this.props.onClickRemix();
}
handleClickSave () {
this.props.onClickSave();
}
handleClickSaveAsCopy () {
this.props.onClickSaveAsCopy();
}
handleRestoreOption (restoreFun) {
return () => {
restoreFun();
@@ -209,13 +220,20 @@ class MenuBar extends React.Component {
id="gui.menuBar.saveNow"
/>
);
const saveAsCopyMessage = (
const createCopyMessage = (
<FormattedMessage
defaultMessage="Save as a copy"
description="Menu bar item for saving as a copy"
id="gui.menuBar.saveAsCopy"
/>
);
const remixMessage = (
<FormattedMessage
defaultMessage="Remix"
description="Menu bar item for remixing"
id="gui.menuBar.remix"
/>
);
const newProjectMessage = (
<FormattedMessage
defaultMessage="New"
@@ -291,21 +309,12 @@ class MenuBar extends React.Component {
place={this.props.isRtl ? 'left' : 'right'}
onRequestClose={this.props.onRequestCloseFile}
>
{this.props.canCreateNew ? (
<MenuItem
isRtl={this.props.isRtl}
onClick={this.handleClickNew}
>
{newProjectMessage}
</MenuItem>
) : (this.props.showComingSoon ? (
<MenuItemTooltip
id="new"
isRtl={this.props.isRtl}
>
<MenuItem>{newProjectMessage}</MenuItem>
</MenuItemTooltip>
) : [])}
<MenuItem
isRtl={this.props.isRtl}
onClick={this.handleClickNew}
>
{newProjectMessage}
</MenuItem>
<MenuSection>
{this.props.canSave ? (
<MenuItem onClick={this.handleClickSave}>
@@ -319,18 +328,23 @@ class MenuBar extends React.Component {
<MenuItem>{saveNowMessage}</MenuItem>
</MenuItemTooltip>
) : [])}
{this.props.canSaveAsCopy ? (
{this.props.canCreateCopy ? (
<MenuItem onClick={this.handleClickSaveAsCopy}>
{saveAsCopyMessage}
{createCopyMessage}
</MenuItem>
) : (this.props.showComingSoon ? (
<MenuItemTooltip
id="copy"
isRtl={this.props.isRtl}
>
<MenuItem>{saveAsCopyMessage}</MenuItem>
<MenuItem>{createCopyMessage}</MenuItem>
</MenuItemTooltip>
) : [])}
{this.props.canRemix ? (
<MenuItem onClick={this.handleClickRemix}>
{remixMessage}
</MenuItem>
) : []}
</MenuSection>
<MenuSection>
<SBFileUploader onUpdateProjectTitle={this.props.onUpdateProjectTitle}>
@@ -627,10 +641,10 @@ class MenuBar extends React.Component {
MenuBar.propTypes = {
accountMenuOpen: PropTypes.bool,
canCreateCopy: PropTypes.bool,
canCreateNew: PropTypes.bool,
canRemix: PropTypes.bool,
canSave: PropTypes.bool,
canSaveAsCopy: PropTypes.bool,
canShare: PropTypes.bool,
className: PropTypes.string,
editMenuOpen: PropTypes.bool,
@@ -648,7 +662,9 @@ MenuBar.propTypes = {
onClickLanguage: PropTypes.func,
onClickLogin: PropTypes.func,
onClickNew: PropTypes.func,
onClickRemix: PropTypes.func,
onClickSave: PropTypes.func,
onClickSaveAsCopy: PropTypes.func,
onLogOut: PropTypes.func,
onOpenRegistration: PropTypes.func,
onOpenTipLibrary: PropTypes.func,
@@ -700,8 +716,10 @@ const mapDispatchToProps = dispatch => ({
onRequestCloseLanguage: () => dispatch(closeLanguageMenu()),
onClickLogin: () => dispatch(openLoginMenu()),
onRequestCloseLogin: () => dispatch(closeLoginMenu()),
onClickNew: canSave => dispatch(requestNewProject(canSave)),
onClickSave: () => dispatch(saveProject()),
onClickNew: canCreateNew => dispatch(requestNewProject(canCreateNew)),
onClickRemix: () => dispatch(remixProject()),
onClickSave: () => dispatch(updateProject()),
onClickSaveAsCopy: () => dispatch(saveProjectAsCopy()),
onSeeCommunity: () => dispatch(setPlayer(true))
});
+35 -29
View File
@@ -7,14 +7,14 @@ import storage from '../lib/storage';
import {
LoadingStates,
createProject,
doneCreatingProject,
doneUpdatingProject,
getIsCreating,
getIsShowingProject,
getIsShowingWithoutId,
getIsUpdating,
onCreated,
onUpdated,
onError,
saveProject
projectError,
updateProject
} from '../reducers/project-state';
/**
@@ -33,34 +33,39 @@ const ProjectSaverHOC = function (WrappedComponent) {
this.storeProject(this.props.reduxProjectId)
.then(() => {
// there is nothing we expect to find in response that we need to check here
this.props.onUpdated(this.props.loadingState);
this.props.onUpdatedProject(this.props.loadingState);
})
.catch(err => {
// NOTE: should throw up a notice for user
this.props.onError(`Saving the project failed with error: ${err}`);
this.props.onProjectError(`Saving the project failed with error: ${err}`);
});
}
// TODO: distinguish between creating new, remixing, and saving as a copy
if (this.props.isCreating && !prevProps.isCreating) {
this.storeProject()
.then(response => {
this.props.onCreated(response.id.toString());
this.props.onCreatedProject(response.id.toString(), this.props.loadingState);
})
.catch(err => {
// NOTE: should throw up a notice for user
this.props.onError(`Creating a new project failed with error: ${err}`);
this.props.onProjectError(`Creating a new project failed with error: ${err}`);
});
}
// if this is the first time we're able to create this project on the server, create it!
const showingCreateable = this.props.canSave && this.props.isShowingWithoutId;
const prevShowingCreateable = prevProps.canSave && prevProps.isShowingWithoutId;
// check if the project state, and user capabilities, have changed so as to indicate
// that we should create or update project
//
// if we're newly able to create this project on the server, create it!
const showingCreateable = this.props.canCreateNew && this.props.isShowingWithoutId;
const prevShowingCreateable = prevProps.canCreateNew && prevProps.isShowingWithoutId;
if (showingCreateable && !prevShowingCreateable) {
this.props.createProject();
this.props.onCreateProject();
} else {
// if we're newly *able* to save this project, save it!
// if we're newly able to save this project, save it!
const showingSaveable = this.props.canSave && this.props.isShowingWithId;
const becameAbleToSave = this.props.canSave && !prevProps.canSave;
if (showingSaveable && becameAbleToSave) {
this.props.saveProject();
this.props.onUpdateProject();
}
}
}
@@ -89,17 +94,17 @@ const ProjectSaverHOC = function (WrappedComponent) {
render () {
const {
/* eslint-disable no-unused-vars */
createProject: createProjectProp,
isCreating: isCreatingProp,
isShowingWithId: isShowingWithIdProp,
isShowingWithoutId: isShowingWithoutIdProp,
isUpdating: isUpdatingProp,
loadingState,
onCreated: onCreatedProp,
onError: onErrorProp,
onUpdated: onUpdatedProp,
onCreatedProject: onCreatedProjectProp,
onCreateProject: onCreateProjectProp,
onProjectError: onProjectErrorProp,
onUpdatedProject: onUpdatedProjectProp,
onUpdateProject: onUpdateProjectProp,
reduxProjectId,
saveProject: saveProjectProp,
/* eslint-enable no-unused-vars */
...componentProps
} = this.props;
@@ -111,18 +116,19 @@ const ProjectSaverHOC = function (WrappedComponent) {
}
}
ProjectSaverComponent.propTypes = {
canCreateNew: PropTypes.bool,
canSave: PropTypes.bool,
createProject: PropTypes.func,
isCreating: PropTypes.bool,
isShowingWithId: PropTypes.bool,
isShowingWithoutId: PropTypes.bool,
isUpdating: PropTypes.bool,
loadingState: PropTypes.oneOf(LoadingStates),
onCreated: PropTypes.func,
onError: PropTypes.func,
onUpdated: PropTypes.func,
onCreateProject: PropTypes.func,
onCreatedProject: PropTypes.func,
onProjectError: PropTypes.func,
onUpdateProject: PropTypes.func,
onUpdatedProject: PropTypes.func,
reduxProjectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
saveProject: PropTypes.func,
vm: PropTypes.instanceOf(VM).isRequired
};
const mapStateToProps = state => {
@@ -138,11 +144,11 @@ const ProjectSaverHOC = function (WrappedComponent) {
};
};
const mapDispatchToProps = dispatch => ({
createProject: () => dispatch(createProject()),
onCreated: projectId => dispatch(onCreated(projectId)),
onUpdated: (projectId, loadingState) => dispatch(onUpdated(projectId, loadingState)),
onError: error => dispatch(onError(error)),
saveProject: () => dispatch(saveProject())
onCreatedProject: (projectId, loadingState) => dispatch(doneCreatingProject(projectId, loadingState)),
onCreateProject: () => dispatch(createProject()),
onProjectError: error => dispatch(projectError(error)),
onUpdateProject: () => dispatch(updateProject()),
onUpdatedProject: (projectId, loadingState) => dispatch(doneUpdatingProject(projectId, loadingState))
});
// Allow incoming props to override redux-provided props. Used to mock in tests.
const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(
+125 -52
View File
@@ -1,20 +1,24 @@
import keyMirror from 'keymirror';
const START_CREATING_NEW = 'scratch-gui/project-state/START_CREATING_NEW';
const DONE_CREATING_COPY = 'scratch-gui/project-state/DONE_CREATING_COPY';
const DONE_CREATING_NEW = 'scratch-gui/project-state/DONE_CREATING_NEW';
const DONE_FETCHING_WITH_ID = 'scratch-gui/project-state/DONE_FETCHING_WITH_ID';
const DONE_FETCHING_DEFAULT = 'scratch-gui/project-state/DONE_FETCHING_DEFAULT';
const DONE_FETCHING_WITH_ID = 'scratch-gui/project-state/DONE_FETCHING_WITH_ID';
const DONE_LOADING_VM_TO_SAVE = 'scratch-gui/project-state/DONE_LOADING_VM_TO_SAVE';
const DONE_LOADING_VM_WITH_ID = 'scratch-gui/project-state/DONE_LOADING_VM_WITH_ID';
const DONE_LOADING_VM_WITHOUT_ID = 'scratch-gui/project-state/DONE_LOADING_VM_WITHOUT_ID';
const DONE_LOADING_VM_TO_SAVE = 'scratch-gui/project-state/DONE_LOADING_VM_TO_SAVE';
const DONE_SAVING_WITH_ID = 'scratch-gui/project-state/DONE_SAVING_WITH_ID';
const DONE_SAVING_WITH_ID_BEFORE_NEW = 'scratch-gui/project-state/DONE_SAVING_WITH_ID_BEFORE_NEW';
const GO_TO_ERROR_STATE = 'scratch-gui/project-state/GO_TO_ERROR_STATE';
const DONE_REMIXING = 'scratch-gui/project-state/DONE_REMIXING';
const DONE_UPDATING = 'scratch-gui/project-state/DONE_UPDATING';
const DONE_UPDATING_BEFORE_NEW = 'scratch-gui/project-state/DONE_UPDATING_BEFORE_NEW';
const SET_PROJECT_ID = 'scratch-gui/project-state/SET_PROJECT_ID';
const START_CREATING_COPY = 'scratch-gui/project-state/START_CREATING_COPY';
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_SAVING = 'scratch-gui/project-state/START_SAVING';
const START_SAVING_BEFORE_CREATING_NEW = 'scratch-gui/project-state/START_SAVING_BEFORE_CREATING_NEW';
const START_REMIXING = 'scratch-gui/project-state/START_REMIXING';
const START_UPDATING = 'scratch-gui/project-state/START_UPDATING';
const START_UPDATING_BEFORE_CREATING_NEW = 'scratch-gui/project-state/START_UPDATING_BEFORE_CREATING_NEW';
const defaultProjectId = '0'; // hardcoded id of default project
@@ -26,10 +30,12 @@ const LoadingState = keyMirror({
LOADING_VM_WITH_ID: null,
LOADING_VM_FILE_UPLOAD: null,
LOADING_VM_NEW_DEFAULT: null,
REMIXING: null,
SHOWING_WITH_ID: null,
SHOWING_WITHOUT_ID: null,
SAVING_WITH_ID: null,
SAVING_WITH_ID_BEFORE_NEW: null,
CREATING_COPY: null,
UPDATING: null,
UPDATING_BEFORE_NEW: null,
CREATING_NEW: null
});
@@ -49,11 +55,13 @@ const getIsLoadingWithId = loadingState => (
loadingState === LoadingState.LOADING_VM_NEW_DEFAULT
);
const getIsCreating = loadingState => (
loadingState === LoadingState.CREATING_NEW
loadingState === LoadingState.CREATING_NEW ||
loadingState === LoadingState.REMIXING ||
loadingState === LoadingState.CREATING_COPY
);
const getIsUpdating = loadingState => (
loadingState === LoadingState.SAVING_WITH_ID ||
loadingState === LoadingState.SAVING_WITH_ID_BEFORE_NEW
loadingState === LoadingState.UPDATING ||
loadingState === LoadingState.UPDATING_BEFORE_NEW
);
const getIsShowingProject = loadingState => (
loadingState === LoadingState.SHOWING_WITH_ID ||
@@ -86,7 +94,7 @@ const reducer = function (state, action) {
if (state.loadingState === LoadingState.CREATING_NEW) {
return Object.assign({}, state, {
loadingState: LoadingState.SHOWING_WITH_ID,
projectId: action.id
projectId: action.projectId
});
}
return state;
@@ -124,19 +132,39 @@ const reducer = function (state, action) {
case DONE_LOADING_VM_TO_SAVE:
if (state.loadingState === LoadingState.LOADING_VM_FILE_UPLOAD) {
return Object.assign({}, state, {
loadingState: LoadingState.SAVING_WITH_ID
loadingState: LoadingState.UPDATING
});
}
return state;
case DONE_SAVING_WITH_ID:
if (state.loadingState === LoadingState.SAVING_WITH_ID) {
case DONE_REMIXING:
// We need to set project id since we just created new project on the server.
// No need to load, we should have data already in vm.
if (state.loadingState === LoadingState.REMIXING) {
return Object.assign({}, state, {
loadingState: LoadingState.SHOWING_WITH_ID,
projectId: action.projectId
});
}
return state;
case DONE_CREATING_COPY:
// We need to set project id since we just created new project on the server.
// No need to load, we should have data already in vm.
if (state.loadingState === LoadingState.CREATING_COPY) {
return Object.assign({}, state, {
loadingState: LoadingState.SHOWING_WITH_ID,
projectId: action.projectId
});
}
return state;
case DONE_UPDATING:
if (state.loadingState === LoadingState.UPDATING) {
return Object.assign({}, state, {
loadingState: LoadingState.SHOWING_WITH_ID
});
}
return state;
case DONE_SAVING_WITH_ID_BEFORE_NEW:
if (state.loadingState === LoadingState.SAVING_WITH_ID_BEFORE_NEW) {
case DONE_UPDATING_BEFORE_NEW:
if (state.loadingState === LoadingState.UPDATING_BEFORE_NEW) {
return Object.assign({}, state, {
loadingState: LoadingState.FETCHING_NEW_DEFAULT,
projectId: defaultProjectId
@@ -145,7 +173,7 @@ const reducer = function (state, action) {
return state;
case SET_PROJECT_ID:
// if setting the default project id, specifically fetch that project
if (action.id === defaultProjectId) {
if (action.projectId === defaultProjectId) {
return Object.assign({}, state, {
loadingState: LoadingState.FETCHING_NEW_DEFAULT,
projectId: defaultProjectId
@@ -154,16 +182,16 @@ const reducer = function (state, action) {
// if we were already showing a project, and a different projectId is set, only fetch that project if
// projectId has changed. This prevents re-fetching projects unnecessarily.
if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
if (state.projectId !== action.id) {
if (state.projectId !== action.projectId) {
return Object.assign({}, state, {
loadingState: LoadingState.FETCHING_WITH_ID,
projectId: action.id
projectId: action.projectId
});
}
} else { // allow any other states to transition to fetching project
return Object.assign({}, state, {
loadingState: LoadingState.FETCHING_WITH_ID,
projectId: action.id
projectId: action.projectId
});
}
return state;
@@ -197,30 +225,48 @@ const reducer = function (state, action) {
});
}
return state;
case START_SAVING:
case START_REMIXING:
// do not set projectId to null, because we nay reference it in creating project
if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
return Object.assign({}, state, {
loadingState: LoadingState.SAVING_WITH_ID
loadingState: LoadingState.REMIXING
});
}
return state;
case START_SAVING_BEFORE_CREATING_NEW:
case START_UPDATING:
if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
return Object.assign({}, state, {
loadingState: LoadingState.SAVING_WITH_ID_BEFORE_NEW
loadingState: LoadingState.UPDATING
});
}
return state;
case GO_TO_ERROR_STATE:
case START_CREATING_COPY:
// do not set projectId to null, because we nay reference it in creating project
if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
return Object.assign({}, state, {
loadingState: LoadingState.CREATING_COPY
});
}
return state;
case START_UPDATING_BEFORE_CREATING_NEW:
if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
return Object.assign({}, state, {
loadingState: LoadingState.UPDATING_BEFORE_NEW
});
}
return state;
case START_ERROR:
// NOTE: we should introduce handling in components for showing ERROR state
if ([
LoadingState.CREATING_NEW,
LoadingState.FETCHING_NEW_DEFAULT,
LoadingState.FETCHING_WITH_ID,
LoadingState.LOADING_VM_NEW_DEFAULT,
LoadingState.LOADING_VM_WITH_ID,
LoadingState.FETCHING_WITH_ID,
LoadingState.FETCHING_NEW_DEFAULT,
LoadingState.SAVING_WITH_ID,
LoadingState.SAVING_WITH_ID_BEFORE_NEW,
LoadingState.CREATING_NEW
LoadingState.REMIXING,
LoadingState.CREATING_COPY,
LoadingState.UPDATING_BEFORE_NEW,
LoadingState.UPDATING
].includes(state.loadingState)) {
return Object.assign({}, state, {
loadingState: LoadingState.ERROR,
@@ -237,10 +283,27 @@ const createProject = () => ({
type: START_CREATING_NEW
});
const onCreated = id => ({
type: DONE_CREATING_NEW,
id: id
});
const doneCreatingProject = (id, loadingState) => {
switch (loadingState) {
case LoadingState.CREATING_NEW:
return {
type: DONE_CREATING_NEW,
projectId: id
};
case LoadingState.CREATING_COPY:
return {
type: DONE_CREATING_COPY,
projectId: id
};
case LoadingState.REMIXING:
return {
type: DONE_REMIXING,
projectId: id
};
default:
break;
}
};
const onFetchedProjectData = (projectData, loadingState) => {
switch (loadingState) {
@@ -283,15 +346,15 @@ const onLoadedProject = (loadingState, canSave) => {
}
};
const onUpdated = loadingState => {
const doneUpdatingProject = loadingState => {
switch (loadingState) {
case LoadingState.SAVING_WITH_ID:
case LoadingState.UPDATING:
return {
type: DONE_SAVING_WITH_ID
type: DONE_UPDATING
};
case LoadingState.SAVING_WITH_ID_BEFORE_NEW:
case LoadingState.UPDATING_BEFORE_NEW:
return {
type: DONE_SAVING_WITH_ID_BEFORE_NEW
type: DONE_UPDATING_BEFORE_NEW
};
default:
break;
@@ -299,17 +362,17 @@ const onUpdated = loadingState => {
};
const onError = error => ({
type: GO_TO_ERROR_STATE,
type: START_ERROR,
error: error
});
const setProjectId = id => ({
type: SET_PROJECT_ID,
id: id
projectId: id
});
const requestNewProject = canSave => {
if (canSave) return {type: START_SAVING_BEFORE_CREATING_NEW};
const requestNewProject = canCreateNew => {
if (canCreateNew) return {type: START_UPDATING_BEFORE_CREATING_NEW};
return {type: START_FETCHING_NEW};
};
@@ -317,8 +380,16 @@ const onProjectUploadStarted = () => ({
type: START_LOADING_VM_FILE_UPLOAD
});
const saveProject = () => ({
type: START_SAVING
const updateProject = () => ({
type: START_UPDATING
});
const saveProjectAsCopy = () => ({
type: START_CREATING_COPY
});
const remixProject = () => ({
type: START_REMIXING
});
export {
@@ -337,13 +408,15 @@ export {
getIsShowingProject,
getIsShowingWithId,
getIsShowingWithoutId,
onCreated,
onError,
doneCreatingProject,
projectError,
onFetchedProjectData,
onLoadedProject,
onProjectUploadStarted,
onUpdated,
doneUpdatingProject,
requestNewProject,
saveProject,
remixProject,
updateProject,
saveProjectAsCopy,
setProjectId
};
@@ -2,15 +2,17 @@
import projectStateReducer from '../../../src/reducers/project-state';
import {
LoadingState,
onCreated,
onError,
doneCreatingProject,
doneUpdatingProject,
onFetchedProjectData,
onLoadedProject,
onProjectUploadStarted,
onUpdated,
projectError,
remixProject,
requestNewProject,
saveProject,
setProjectId
saveProjectAsCopy,
setProjectId,
updateProject
} from '../../../src/reducers/project-state';
test('initialState', () => {
@@ -23,28 +25,50 @@ test('initialState', () => {
expect(projectStateReducer(defaultState, {type: 'anything'}).loadingState).toBe(LoadingState.NOT_LOADED);
});
test('onCreated with projectId type string shows project with that id', () => {
test('doneCreatingProject for new project with projectId type string shows project with that id', () => {
const initialState = {
projectId: null,
loadingState: LoadingState.CREATING_NEW
};
const action = onCreated('100');
const action = doneCreatingProject('100', initialState.loadingState);
const resultState = projectStateReducer(initialState, action);
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
expect(resultState.projectId).toBe('100');
});
test('onCreated with projectId type number shows project with id of type number', () => {
test('doneCreatingProject for new project with projectId type number shows project with id of type number', () => {
const initialState = {
projectId: null,
loadingState: LoadingState.CREATING_NEW
};
const action = onCreated(100);
const action = doneCreatingProject(100, initialState.loadingState);
const resultState = projectStateReducer(initialState, action);
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
expect(resultState.projectId).toBe(100);
});
test('doneCreatingProject for remix shows project with that id', () => {
const initialState = {
projectId: null,
loadingState: LoadingState.REMIXING
};
const action = doneCreatingProject('100', initialState.loadingState);
const resultState = projectStateReducer(initialState, action);
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
expect(resultState.projectId).toBe('100');
});
test('doneCreatingProject for save as copy shows project with that id', () => {
const initialState = {
projectId: null,
loadingState: LoadingState.CREATING_COPY
};
const action = doneCreatingProject('100', initialState.loadingState);
const resultState = projectStateReducer(initialState, action);
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
expect(resultState.projectId).toBe('100');
});
test('onFetchedProjectData with id loads project data into vm', () => {
const initialState = {
projectData: null,
@@ -82,7 +106,7 @@ test('onLoadedProject upload, with canSave true, prepares to save', () => {
};
const action = onLoadedProject(initialState.loadingState, true);
const resultState = projectStateReducer(initialState, action);
expect(resultState.loadingState).toBe(LoadingState.SAVING_WITH_ID);
expect(resultState.loadingState).toBe(LoadingState.UPDATING);
});
test('onLoadedProject with id shows with id', () => {
@@ -112,20 +136,20 @@ test('onLoadedProject new, to save shows without id', () => {
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITHOUT_ID);
});
test('onUpdated with id shows with id', () => {
test('doneUpdatingProject with id shows with id', () => {
const initialState = {
loadingState: LoadingState.SAVING_WITH_ID
loadingState: LoadingState.UPDATING
};
const action = onUpdated(initialState.loadingState);
const action = doneUpdatingProject(initialState.loadingState);
const resultState = projectStateReducer(initialState, action);
expect(resultState.loadingState).toBe(LoadingState.SHOWING_WITH_ID);
});
test('onUpdated with id, before new, fetches default project', () => {
test('doneUpdatingProject with id, before new, fetches default project', () => {
const initialState = {
loadingState: LoadingState.SAVING_WITH_ID_BEFORE_NEW
loadingState: LoadingState.UPDATING_BEFORE_NEW
};
const action = onUpdated(initialState.loadingState);
const action = doneUpdatingProject(initialState.loadingState);
const resultState = projectStateReducer(initialState, action);
expect(resultState.loadingState).toBe(LoadingState.FETCHING_NEW_DEFAULT);
});
@@ -163,7 +187,7 @@ test('setProjectId, with same id as before, but not same type, should fetch beca
expect(resultState.projectId).toBe(100);
});
test('requestNewProject, when can\'t save, should fetch default project without id', () => {
test('requestNewProject, when can\'t create new, should fetch default project without id', () => {
const initialState = {
loadingState: LoadingState.SHOWING_WITHOUT_ID
};
@@ -172,13 +196,13 @@ test('requestNewProject, when can\'t save, should fetch default project without
expect(resultState.loadingState).toBe(LoadingState.FETCHING_NEW_DEFAULT);
});
test('requestNewProject, when can save, should save and prepare to fetch default project', () => {
test('requestNewProject, when can create new, should save and prepare to fetch default project', () => {
const initialState = {
loadingState: LoadingState.SHOWING_WITH_ID
};
const action = requestNewProject(true);
const resultState = projectStateReducer(initialState, action);
expect(resultState.loadingState).toBe(LoadingState.SAVING_WITH_ID_BEFORE_NEW);
expect(resultState.loadingState).toBe(LoadingState.UPDATING_BEFORE_NEW);
});
test('onProjectUploadStarted when project not loaded should load', () => {
@@ -208,24 +232,44 @@ test('onProjectUploadStarted when showing project without id should load', () =>
expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_FILE_UPLOAD);
});
test('saveProject should prepare to save', () => {
test('updateProject should prepare to update', () => {
const initialState = {
loadingState: LoadingState.SHOWING_WITH_ID
};
const action = saveProject();
const action = updateProject();
const resultState = projectStateReducer(initialState, action);
expect(resultState.loadingState).toBe(LoadingState.SAVING_WITH_ID);
expect(resultState.loadingState).toBe(LoadingState.UPDATING);
});
test('onError from various states should show error', () => {
test('saveProjectAsCopy should prepare to save as a copy', () => {
const initialState = {
loadingState: LoadingState.SHOWING_WITH_ID
};
const action = saveProjectAsCopy();
const resultState = projectStateReducer(initialState, action);
expect(resultState.loadingState).toBe(LoadingState.CREATING_COPY);
});
test('remixProject should prepare to remix', () => {
const initialState = {
loadingState: LoadingState.SHOWING_WITH_ID
};
const action = remixProject();
const resultState = projectStateReducer(initialState, action);
expect(resultState.loadingState).toBe(LoadingState.REMIXING);
});
test('projectError from various states should show error', () => {
const startStates = [
LoadingState.CREATING_NEW,
LoadingState.FETCHING_NEW_DEFAULT,
LoadingState.FETCHING_WITH_ID,
LoadingState.LOADING_VM_NEW_DEFAULT,
LoadingState.LOADING_VM_WITH_ID,
LoadingState.FETCHING_WITH_ID,
LoadingState.FETCHING_NEW_DEFAULT,
LoadingState.SAVING_WITH_ID,
LoadingState.SAVING_WITH_ID_BEFORE_NEW,
LoadingState.CREATING_NEW
LoadingState.REMIXING,
LoadingState.CREATING_COPY,
LoadingState.UPDATING_BEFORE_NEW,
LoadingState.UPDATING
];
for (const startState of startStates) {
const initialState = {
@@ -239,7 +283,7 @@ test('onError from various states should show error', () => {
}
});
test('onError from showing project should show error', () => {
test('projectError from showing project should show error', () => {
const initialState = {
error: null,
loadingState: LoadingState.FETCHING_WITH_ID
+15 -15
View File
@@ -23,7 +23,7 @@ describe('projectSaverHOC', () => {
});
test('if canSave becomes true when showing a project with an id, project will be saved', () => {
const mockedSaveProject = jest.fn();
const mockedUpdateProject = jest.fn();
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mounted = mount(
@@ -34,15 +34,15 @@ describe('projectSaverHOC', () => {
isShowingWithoutId={false}
isUpdating={false}
loadingState={LoadingState.SHOWING_WITH_ID}
saveProject={mockedSaveProject}
store={store}
vm={vm}
onUpdateProject={mockedUpdateProject}
/>
);
mounted.setProps({
canSave: true
});
expect(mockedSaveProject).toHaveBeenCalled();
expect(mockedUpdateProject).toHaveBeenCalled();
});
test('if canSave is alreatdy true and we show a project with an id, project will NOT be saved', () => {
@@ -57,9 +57,9 @@ describe('projectSaverHOC', () => {
isShowingWithoutId={false}
isUpdating={false}
loadingState={LoadingState.LOADING_VM_WITH_ID}
saveProject={mockedSaveProject}
store={store}
vm={vm}
onUpdateProject={mockedSaveProject}
/>
);
mounted.setProps({
@@ -78,13 +78,13 @@ describe('projectSaverHOC', () => {
<WrappedComponent
isShowingWithoutId
canSave={false}
createProject={mockedCreateProject}
isCreating={false}
isShowingWithId={false}
isUpdating={false}
loadingState={LoadingState.LOADING_VM_NEW_DEFAULT}
store={store}
vm={vm}
onCreateProject={mockedCreateProject}
/>
);
mounted.setProps({
@@ -94,37 +94,36 @@ describe('projectSaverHOC', () => {
expect(mockedCreateProject).not.toHaveBeenCalled();
});
test('if canSave becomes true when showing a project without an id, project will be created', () => {
test('if canCreateNew becomes true when showing a project without an id, project will be created', () => {
const mockedCreateProject = jest.fn();
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mounted = mount(
<WrappedComponent
isShowingWithoutId
canSave={false}
createProject={mockedCreateProject}
canCreateNew={false}
isCreating={false}
isShowingWithId={false}
isUpdating={false}
loadingState={LoadingState.SHOWING_WITHOUT_ID}
store={store}
vm={vm}
onCreateProject={mockedCreateProject}
/>
);
mounted.setProps({
canSave: true
canCreateNew: true
});
expect(mockedCreateProject).toHaveBeenCalled();
});
test('if canSave is true and we transition to showing new project, project will be created', () => {
test('if canCreateNew is true and we transition to showing new project, project will be created', () => {
const mockedCreateProject = jest.fn();
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mounted = mount(
<WrappedComponent
canSave
createProject={mockedCreateProject}
canCreateNew
isCreating={false}
isShowingWithId={false}
isShowingWithoutId={false}
@@ -132,6 +131,7 @@ describe('projectSaverHOC', () => {
loadingState={LoadingState.LOADING_VM_NEW_DEFAULT}
store={store}
vm={vm}
onCreateProject={mockedCreateProject}
/>
);
mounted.setProps({
@@ -184,7 +184,7 @@ describe('projectSaverHOC', () => {
);
mounted.setProps({
isUpdating: true,
loadingState: LoadingState.SAVING_WITH_ID
loadingState: LoadingState.UPDATING
});
expect(vm.saveProjectSb3).toHaveBeenCalled();
});
@@ -200,7 +200,7 @@ describe('projectSaverHOC', () => {
isCreating={false}
isShowingWithId={false}
isShowingWithoutId={false}
loadingState={LoadingState.SAVING_WITH_ID}
loadingState={LoadingState.UPDATING}
reduxProjectId={'100'}
store={store}
vm={vm}
@@ -208,7 +208,7 @@ describe('projectSaverHOC', () => {
);
mounted.setProps({
isUpdating: true,
loadingState: LoadingState.SAVING_WITH_ID,
loadingState: LoadingState.UPDATING,
reduxProjectId: '99' // random change to force a re-render and componentDidUpdate
});
expect(vm.saveProjectSb3).not.toHaveBeenCalled();