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 = () =>
; const WrappedComponent = projectSaverHOC(Component); const mounted = mount( ); 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 = () =>
; const WrappedComponent = projectSaverHOC(Component); const mounted = mount( ); 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 = () =>
; const WrappedComponent = projectSaverHOC(Component); const mounted = mount( ); 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 = () =>
; const WrappedComponent = projectSaverHOC(Component); const mounted = mount( ); 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 = () =>
; const WrappedComponent = projectSaverHOC(Component); const mounted = mount( ); 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 = () =>
; 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( ); 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 = () =>
; 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( ); 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 = () =>
; 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( ); 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 = () =>
; 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( ); 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 = () =>
; 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( ); 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 = () =>
; const WrappedComponent = projectSaverHOC(Component); const mounted = mount( ); mounted.setProps({ isManualUpdating: true, isUpdating: true }); expect(mockedShowSavingAlert).toHaveBeenCalled(); }); test('if project is changed, it should autosave after interval', () => { const Component = () =>
; const WrappedComponent = projectSaverHOC(Component); const mockedAutoUpdate = jest.fn(() => Promise.resolve()); const mounted = mount( ); 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 = () =>
; const WrappedComponent = projectSaverHOC(Component); const mockedAutoUpdate = jest.fn(() => Promise.resolve()); const mounted = mount( ); 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 = () =>
; const WrappedComponent = projectSaverHOC(Component); const mockedAutoUpdate = jest.fn(() => Promise.resolve()); const mounted = mount( ); 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 = () =>
; const WrappedComponent = projectSaverHOC(Component); WrappedComponent.WrappedComponent.prototype.storeProject = mockedStoreProject; const mounted = mount( ); 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 = () =>
; const WrappedComponent = projectSaverHOC(Component); WrappedComponent.WrappedComponent.prototype.storeProject = mockedStoreProject; const mounted = mount( ); mounted.setProps({ isRemixing: false }); expect(mockedOnRemixing).toHaveBeenCalledWith(false); }); test('uses onSetProjectThumbnailer on mount/unmount', () => { const Component = () =>
; const WrappedComponent = projectSaverHOC(Component); const setThumb = jest.fn(); const mounted = mount( ); // 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 = () =>
; const WrappedComponent = projectSaverHOC(Component); const setSaver = jest.fn(); const mounted = mount( ); // 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); }); });