easy-scratch/test/unit/util/project-saver-hoc.test.jsx

492 lines
18 KiB
JavaScript

import 'web-audio-test-api';
import React from 'react';
import configureStore from 'redux-mock-store';
import {mount} from 'enzyme';
import {LoadingState} from '../../../src/reducers/project-state';
import VM from 'scratch-vm';
import projectSaverHOC from '../../../src/lib/project-saver-hoc.jsx';
describe('projectSaverHOC', () => {
const mockStore = configureStore();
let store;
let vm;
beforeEach(() => {
store = mockStore({
scratchGui: {
projectChanged: false,
projectState: {},
projectTitle: 'Scratch Project',
timeout: {
autoSaveTimeoutId: null
}
},
locales: {
locale: 'en'
}
});
vm = new VM();
jest.useFakeTimers();
});
test('if canSave becomes true when showing a project with an id, project will be saved', () => {
const mockedUpdateProject = jest.fn();
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mounted = mount(
<WrappedComponent
isShowingWithId
canSave={false}
isCreatingNew={false}
isShowingSaveable={false} // set explicitly because it relies on ownProps.canSave
isShowingWithoutId={false}
isUpdating={false}
loadingState={LoadingState.SHOWING_WITH_ID}
store={store}
vm={vm}
onAutoUpdateProject={mockedUpdateProject}
/>
);
mounted.setProps({
canSave: true,
isShowingSaveable: true
});
expect(mockedUpdateProject).toHaveBeenCalled();
});
test('if canSave is alreatdy 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);
const mounted = mount(
<WrappedComponent
canSave
isCreatingNew={false}
isShowingWithId={false}
isShowingWithoutId={false}
isUpdating={false}
loadingState={LoadingState.LOADING_VM_WITH_ID}
store={store}
vm={vm}
onAutoUpdateProject={mockedSaveProject}
/>
);
mounted.setProps({
canSave: true,
isShowingWithId: true,
loadingState: LoadingState.SHOWING_WITH_ID
});
expect(mockedSaveProject).not.toHaveBeenCalled();
});
test('if canSave is false when showing a project without an id, project will NOT be created', () => {
const mockedCreateProject = jest.fn();
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mounted = mount(
<WrappedComponent
isShowingWithoutId
canSave={false}
isCreatingNew={false}
isShowingWithId={false}
isUpdating={false}
loadingState={LoadingState.LOADING_VM_NEW_DEFAULT}
store={store}
vm={vm}
onCreateProject={mockedCreateProject}
/>
);
mounted.setProps({
isShowingWithoutId: true,
loadingState: LoadingState.SHOWING_WITHOUT_ID
});
expect(mockedCreateProject).not.toHaveBeenCalled();
});
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
canCreateNew={false}
isCreatingNew={false}
isShowingWithId={false}
isUpdating={false}
loadingState={LoadingState.SHOWING_WITHOUT_ID}
store={store}
vm={vm}
onCreateProject={mockedCreateProject}
/>
);
mounted.setProps({
canCreateNew: true
});
expect(mockedCreateProject).toHaveBeenCalled();
});
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
canCreateNew
isCreatingNew={false}
isShowingWithId={false}
isShowingWithoutId={false}
isUpdating={false}
loadingState={LoadingState.LOADING_VM_NEW_DEFAULT}
store={store}
vm={vm}
onCreateProject={mockedCreateProject}
/>
);
mounted.setProps({
isShowingWithoutId: true,
loadingState: LoadingState.SHOWING_WITHOUT_ID
});
expect(mockedCreateProject).toHaveBeenCalled();
});
test('if we enter creating new state, vm project should be requested', () => {
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mockedStoreProject = jest.fn(() => Promise.resolve());
// The first wrapper is redux's Connect HOC
WrappedComponent.WrappedComponent.prototype.storeProject = mockedStoreProject;
const mounted = mount(
<WrappedComponent
canSave
isCreatingCopy={false}
isCreatingNew={false}
isRemixing={false}
isShowingWithId={false}
isShowingWithoutId={false}
isUpdating={false}
loadingState={LoadingState.LOADING_VM_NEW_DEFAULT}
reduxProjectId={'100'}
store={store}
vm={vm}
/>
);
mounted.setProps({
isCreatingNew: true,
loadingState: LoadingState.CREATING_NEW
});
expect(mockedStoreProject).toHaveBeenCalled();
});
test('if we enter remixing state, vm project should be requested, and alert should show', () => {
const mockedShowCreatingRemixAlert = jest.fn();
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mockedStoreProject = jest.fn(() => Promise.resolve());
// The first wrapper is redux's Connect HOC
WrappedComponent.WrappedComponent.prototype.storeProject = mockedStoreProject;
const mounted = mount(
<WrappedComponent
canSave
isCreatingCopy={false}
isCreatingNew={false}
isRemixing={false}
isShowingWithId={false}
isShowingWithoutId={false}
isUpdating={false}
loadingState={LoadingState.SHOWING_WITH_ID}
reduxProjectId={'100'}
store={store}
vm={vm}
onShowCreatingRemixAlert={mockedShowCreatingRemixAlert}
/>
);
mounted.setProps({
isRemixing: true,
loadingState: LoadingState.REMIXING
});
expect(mockedStoreProject).toHaveBeenCalled();
expect(mockedShowCreatingRemixAlert).toHaveBeenCalled();
});
test('if we enter creating copy state, vm project should be requested, and alert should show', () => {
const mockedShowCreatingCopyAlert = jest.fn();
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mockedStoreProject = jest.fn(() => Promise.resolve());
// The first wrapper is redux's Connect HOC
WrappedComponent.WrappedComponent.prototype.storeProject = mockedStoreProject;
const mounted = mount(
<WrappedComponent
canSave
isCreatingCopy={false}
isCreatingNew={false}
isRemixing={false}
isShowingWithId={false}
isShowingWithoutId={false}
isUpdating={false}
loadingState={LoadingState.SHOWING_WITH_ID}
reduxProjectId={'100'}
store={store}
vm={vm}
onShowCreatingCopyAlert={mockedShowCreatingCopyAlert}
/>
);
mounted.setProps({
isCreatingCopy: true,
loadingState: LoadingState.CREATING_COPY
});
expect(mockedStoreProject).toHaveBeenCalled();
expect(mockedShowCreatingCopyAlert).toHaveBeenCalled();
});
test('if we enter updating/saving state, vm project should be requested', () => {
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mockedStoreProject = jest.fn(() => Promise.resolve());
// The first wrapper is redux's Connect HOC
WrappedComponent.WrappedComponent.prototype.storeProject = mockedStoreProject;
const mounted = mount(
<WrappedComponent
canSave
isCreatingNew={false}
isShowingWithId={false}
isShowingWithoutId={false}
isUpdating={false}
loadingState={LoadingState.LOADING_VM_WITH_ID}
reduxProjectId={'100'}
store={store}
vm={vm}
/>
);
mounted.setProps({
isUpdating: true,
loadingState: LoadingState.MANUAL_UPDATING
});
expect(mockedStoreProject).toHaveBeenCalled();
});
test('if we are already in updating/saving state, vm project ' +
'should NOT requested, alert should NOT show', () => {
const mockedShowCreatingAlert = jest.fn();
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mockedStoreProject = jest.fn(() => Promise.resolve());
// The first wrapper is redux's Connect HOC
WrappedComponent.WrappedComponent.prototype.storeProject = mockedStoreProject;
const mounted = mount(
<WrappedComponent
canSave
isUpdating
isCreatingNew={false}
isShowingWithId={false}
isShowingWithoutId={false}
loadingState={LoadingState.MANUAL_UPDATING}
reduxProjectId={'100'}
store={store}
vm={vm}
onShowCreatingAlert={mockedShowCreatingAlert}
/>
);
mounted.setProps({
isUpdating: true,
loadingState: LoadingState.AUTO_UPDATING,
reduxProjectId: '99' // random change to force a re-render and componentDidUpdate
});
expect(mockedStoreProject).not.toHaveBeenCalled();
expect(mockedShowCreatingAlert).not.toHaveBeenCalled();
});
test('if user saves, inline saving alert should show', () => {
const mockedShowSavingAlert = jest.fn();
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mounted = mount(
<WrappedComponent
canSave
isShowingWithoutId
canCreateNew={false}
isCreatingNew={false}
isManualUpdating={false}
isShowingWithId={false}
isUpdating={false}
loadingState={LoadingState.SHOWING_WITH_ID}
store={store}
vm={vm}
onShowSavingAlert={mockedShowSavingAlert}
/>
);
mounted.setProps({
isManualUpdating: true,
isUpdating: true
});
expect(mockedShowSavingAlert).toHaveBeenCalled();
});
test('if project is changed, it should autosave after interval', () => {
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mockedAutoUpdate = jest.fn(() => Promise.resolve());
const mounted = mount(
<WrappedComponent
canSave
isShowingSaveable
isShowingWithId
loadingState={LoadingState.SHOWING_WITH_ID}
store={store}
vm={vm}
onAutoUpdateProject={mockedAutoUpdate}
/>
);
mounted.setProps({
projectChanged: true
});
// Fast-forward until all timers have been executed
jest.runAllTimers();
expect(mockedAutoUpdate).toHaveBeenCalled();
});
test('if project is changed several times in a row, it should only autosave once', () => {
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mockedAutoUpdate = jest.fn(() => Promise.resolve());
const mounted = mount(
<WrappedComponent
canSave
isShowingSaveable
isShowingWithId
loadingState={LoadingState.SHOWING_WITH_ID}
store={store}
vm={vm}
onAutoUpdateProject={mockedAutoUpdate}
/>
);
mounted.setProps({
projectChanged: true,
reduxProjectTitle: 'a'
});
mounted.setProps({
projectChanged: true,
reduxProjectTitle: 'b'
});
mounted.setProps({
projectChanged: true,
reduxProjectTitle: 'c'
});
// Fast-forward until all timers have been executed
jest.runAllTimers();
expect(mockedAutoUpdate).toHaveBeenCalledTimes(1);
});
test('if project is not changed, it should not autosave after interval', () => {
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const mockedAutoUpdate = jest.fn(() => Promise.resolve());
const mounted = mount(
<WrappedComponent
canSave
isShowingSaveable
isShowingWithId
loadingState={LoadingState.SHOWING_WITH_ID}
store={store}
vm={vm}
onAutoUpdateProject={mockedAutoUpdate}
/>
);
mounted.setProps({
projectChanged: false
});
// Fast-forward until all timers have been executed
jest.runAllTimers();
expect(mockedAutoUpdate).not.toHaveBeenCalled();
});
test('when starting to remix, onRemixing should be called with param true', () => {
const mockedOnRemixing = jest.fn();
const mockedStoreProject = jest.fn(() => Promise.resolve());
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
WrappedComponent.WrappedComponent.prototype.storeProject = mockedStoreProject;
const mounted = mount(
<WrappedComponent
isRemixing={false}
store={store}
vm={vm}
onRemixing={mockedOnRemixing}
/>
);
mounted.setProps({
isRemixing: true
});
expect(mockedOnRemixing).toHaveBeenCalledWith(true);
});
test('when starting to remix, onRemixing should be called with param false', () => {
const mockedOnRemixing = jest.fn();
const mockedStoreProject = jest.fn(() => Promise.resolve());
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
WrappedComponent.WrappedComponent.prototype.storeProject = mockedStoreProject;
const mounted = mount(
<WrappedComponent
isRemixing
store={store}
vm={vm}
onRemixing={mockedOnRemixing}
/>
);
mounted.setProps({
isRemixing: false
});
expect(mockedOnRemixing).toHaveBeenCalledWith(false);
});
test('uses onSetProjectThumbnailer on mount/unmount', () => {
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const setThumb = jest.fn();
const mounted = mount(
<WrappedComponent
store={store}
vm={vm}
onSetProjectThumbnailer={setThumb}
/>
);
// Set project thumbnailer should be called on mount
expect(setThumb).toHaveBeenCalledTimes(1);
// And it should not pass that function on to wrapped element
expect(mounted.find(Component).props().onSetProjectThumbnailer).toBeUndefined();
// Unmounting should call it again with null
mounted.unmount();
expect(setThumb).toHaveBeenCalledTimes(2);
expect(setThumb.mock.calls[1][0]).toBe(null);
});
test('uses onSetProjectSaver on mount/unmount', () => {
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const setSaver = jest.fn();
const mounted = mount(
<WrappedComponent
store={store}
vm={vm}
onSetProjectSaver={setSaver}
/>
);
// Set project saver should be called on mount
expect(setSaver).toHaveBeenCalledTimes(1);
// And it should not pass that function on to wrapped element
expect(mounted.find(Component).props().onSetProjectSaver).toBeUndefined();
// Unmounting should call it again with null
mounted.unmount();
expect(setSaver).toHaveBeenCalledTimes(2);
expect(setSaver.mock.calls[1][0]).toBe(null);
});
});