diff --git a/src/store/callViewStore.spec.js b/src/store/callViewStore.spec.js deleted file mode 100644 index 000a33d7b42b..000000000000 --- a/src/store/callViewStore.spec.js +++ /dev/null @@ -1,233 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -import { createLocalVue } from '@vue/test-utils' -import { cloneDeep } from 'lodash' -import Vuex from 'vuex' - -import storeConfig from './storeConfig.js' -import { CONVERSATION } from '../constants.js' - -describe('callViewStore', () => { - let localVue = null - let store = null - - beforeEach(() => { - localVue = createLocalVue() - localVue.use(Vuex) - - const testStoreConfig = cloneDeep(storeConfig) - - // remove participant store to avoid participant interaction - testStoreConfig.modules.participantsStore = {} - - // eslint-disable-next-line import/no-named-as-default-member - store = new Vuex.Store(testStoreConfig) - - // to fully reset the state between tests, clear the storage - localStorage.clear() - - // and reset all mocks - jest.clearAllMocks() - }) - - describe('call view mode and presentation', () => { - test('restores grid state when joining call (true)', () => { - localStorage.getItem.mockReturnValueOnce('true') - - store.dispatch('joinCall', { token: 'XXTOKENXX' }) - - expect(localStorage.getItem).toHaveBeenCalled() - expect(localStorage.getItem.mock.calls[0][0]).toEqual(expect.stringMatching(/callprefs-XXTOKENXX-isgrid$/)) - - expect(store.getters.isGrid).toBe(true) - expect(store.getters.isStripeOpen).toBe(true) - }) - - test('restores grid state when joining call (false)', () => { - localStorage.getItem.mockReturnValueOnce('false') - - store.dispatch('joinCall', { token: 'XXTOKENXX' }) - - expect(localStorage.getItem).toHaveBeenCalled() - expect(localStorage.getItem.mock.calls[0][0]).toEqual(expect.stringMatching(/callprefs-XXTOKENXX-isgrid$/)) - - expect(store.getters.isGrid).toBe(false) - expect(store.getters.isStripeOpen).toBe(true) - }) - - /** - * @param {number} conversationType The type of the conversation - * @param {boolean} state Whether or not the grid is shown - */ - function testDefaultGridState(conversationType, state) { - localStorage.getItem.mockReturnValueOnce(null) - - // using commit instead of dispatch because the action - // also processes participants - store.commit('addConversation', { - token: 'XXTOKENXX', - type: conversationType, - }) - store.dispatch('joinCall', { token: 'XXTOKENXX' }) - - expect(localStorage.getItem).toHaveBeenCalled() - expect(localStorage.getItem.mock.calls[0][0]).toEqual(expect.stringMatching(/callprefs-XXTOKENXX-isgrid$/)) - - expect(store.getters.isGrid).toBe(state) - expect(store.getters.isStripeOpen).toBe(true) - } - - test('sets default grid state when joining call in group conversation', () => { - testDefaultGridState(CONVERSATION.TYPE.GROUP, true) - }) - - test('sets default grid state when joining call in public conversation', () => { - testDefaultGridState(CONVERSATION.TYPE.PUBLIC, true) - }) - - test('sets default grid state when joining call in one to one conversation', () => { - testDefaultGridState(CONVERSATION.TYPE.ONE_TO_ONE, false) - }) - - test('switching call view mode saves in local storage', () => { - store.dispatch('updateToken', 'XXTOKENXX') - - store.dispatch('setCallViewMode', { - isGrid: true, - isStripeOpen: false, - }) - - expect(store.getters.isGrid).toEqual(true) - expect(store.getters.isStripeOpen).toEqual(false) - - expect(localStorage.setItem).toHaveBeenCalled() - expect(localStorage.setItem.mock.calls[0][0]).toEqual(expect.stringMatching(/callprefs-XXTOKENXX-isgrid$/)) - expect(localStorage.setItem.mock.calls[0][1]).toBe(true) - - store.dispatch('setCallViewMode', { - isGrid: false, - isStripeOpen: true, - }) - - expect(store.getters.isGrid).toEqual(false) - expect(store.getters.isStripeOpen).toEqual(true) - - expect(localStorage.setItem).toHaveBeenCalled() - expect(localStorage.setItem.mock.calls[1][0]).toEqual(expect.stringMatching(/callprefs-XXTOKENXX-isgrid$/)) - expect(localStorage.setItem.mock.calls[1][1]).toBe(false) - }) - - test('start presentation switches off grid view and restores when it ends', () => { - [{ - isGrid: true, - isStripeOpen: true, - }, { - isGrid: false, - isStripeOpen: false, - }].forEach((testState) => { - store.dispatch('setCallViewMode', testState) - - store.dispatch('startPresentation') - - expect(store.getters.isGrid).toEqual(false) - expect(store.getters.isStripeOpen).toEqual(false) - - store.dispatch('stopPresentation') - - expect(store.getters.isGrid).toEqual(testState.isGrid) - expect(store.getters.isStripeOpen).toEqual(testState.isStripeOpen) - }) - }) - - test('switching modes during presentation does not resets it after it ends', () => { - store.dispatch('setCallViewMode', { - isGrid: true, - isStripeOpen: true, - }) - - store.dispatch('startPresentation') - - // switch during presentation - store.dispatch('setCallViewMode', { - isGrid: true, - isStripeOpen: true, - }) - - store.dispatch('stopPresentation') - - // state kept, not restored - expect(store.getters.isGrid).toEqual(true) - expect(store.getters.isStripeOpen).toEqual(true) - }) - - test('starting presentation twice does not mess up remembered state', () => { - store.dispatch('setCallViewMode', { - isGrid: true, - isStripeOpen: true, - }) - - expect(store.getters.presentationStarted).toBe(false) - - store.dispatch('startPresentation') - - expect(store.getters.presentationStarted).toBe(true) - - // switch during presentation - store.dispatch('setCallViewMode', { - isGrid: true, - isStripeOpen: true, - }) - - store.dispatch('startPresentation') - - // state kept - expect(store.getters.isGrid).toEqual(true) - expect(store.getters.isStripeOpen).toEqual(true) - - expect(store.getters.presentationStarted).toBe(true) - - store.dispatch('stopPresentation') - - expect(store.getters.presentationStarted).toBe(false) - - // state kept, not restored - expect(store.getters.isGrid).toEqual(true) - expect(store.getters.isStripeOpen).toEqual(true) - }) - - test('stopping presentation twice does not mess up remembered state', () => { - store.dispatch('setCallViewMode', { - isGrid: true, - isStripeOpen: true, - }) - - expect(store.getters.presentationStarted).toBe(false) - - store.dispatch('startPresentation') - - expect(store.getters.presentationStarted).toBe(true) - - store.dispatch('stopPresentation') - - expect(store.getters.presentationStarted).toBe(false) - - expect(store.getters.isGrid).toEqual(true) - expect(store.getters.isStripeOpen).toEqual(true) - - store.dispatch('setCallViewMode', { - isGrid: false, - isStripeOpen: false, - }) - - store.dispatch('stopPresentation') - - expect(store.getters.presentationStarted).toBe(false) - - // state kept, not reset - expect(store.getters.isGrid).toEqual(false) - expect(store.getters.isStripeOpen).toEqual(false) - }) - }) -}) diff --git a/src/stores/__tests__/callView.spec.js b/src/stores/__tests__/callView.spec.js new file mode 100644 index 000000000000..96a7198f0841 --- /dev/null +++ b/src/stores/__tests__/callView.spec.js @@ -0,0 +1,252 @@ +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import { setActivePinia, createPinia } from 'pinia' + +import { CONVERSATION } from '../../constants.js' +import BrowserStorage from '../../services/BrowserStorage.js' +import vuexStore from '../../store/index.js' +import { useCallViewStore } from '../callView.js' + +jest.mock('../../services/BrowserStorage.js', () => ({ + getItem: jest.fn().mockReturnValue(null), + setItem: jest.fn(), +})) + +describe('callViewStore', () => { + const TOKEN = 'XXTOKENXX' + const BROWSER_STORAGE_KEY = 'callprefs-XXTOKENXX-isgrid' + const PEER_ID = 'peer-id' + let callViewStore + + beforeEach(() => { + setActivePinia(createPinia()) + callViewStore = useCallViewStore() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('call view mode and presentation', () => { + /** + * @param {number} type The type of the conversation + * @param {boolean} state Whether the grid is shown + * @param {string|null} browserStorageState Whether preference is set in BrowserStorage + */ + function testDefaultGridState(type, state, browserStorageState = null) { + // Arrange + BrowserStorage.getItem.mockReturnValueOnce(browserStorageState) + // using commit instead of dispatch because the action also processes participants + vuexStore.commit('addConversation', { token: TOKEN, type }) + + // Act + callViewStore.handleJoinCall({ token: TOKEN }) + + // Assert + expect(BrowserStorage.getItem).toHaveBeenCalledWith(BROWSER_STORAGE_KEY) + expect(callViewStore.isGrid).toBe(state) + expect(callViewStore.isStripeOpen).toBeTruthy() + } + + it('restores grid state from BrowserStorage when joining call (true)', () => { + // Arrange + testDefaultGridState(CONVERSATION.TYPE.GROUP, true, 'true') + }) + + it('restores grid state from BrowserStorage when joining call (false)', () => { + testDefaultGridState(CONVERSATION.TYPE.GROUP, false, 'false') + }) + + it('sets default grid state when joining call in group conversation', () => { + testDefaultGridState(CONVERSATION.TYPE.GROUP, true) + }) + + it('sets default grid state when joining call in public conversation', () => { + testDefaultGridState(CONVERSATION.TYPE.PUBLIC, true) + }) + + it('sets default grid state when joining call in one to one conversation', () => { + testDefaultGridState(CONVERSATION.TYPE.ONE_TO_ONE, false) + }) + + it('switching call view mode saves in local storage', () => { + vuexStore.dispatch('updateToken', TOKEN) + + callViewStore.setCallViewMode({ + isGrid: true, + isStripeOpen: false, + }) + expect(callViewStore.isGrid).toBeTruthy() + expect(callViewStore.isStripeOpen).toBeFalsy() + expect(BrowserStorage.setItem).toHaveBeenCalledWith(BROWSER_STORAGE_KEY, true) + + callViewStore.setCallViewMode({ + isGrid: false, + isStripeOpen: true, + }) + expect(callViewStore.isGrid).toBeFalsy() + expect(callViewStore.isStripeOpen).toBeTruthy() + expect(BrowserStorage.setItem).toHaveBeenCalledWith(BROWSER_STORAGE_KEY, false) + }) + + it('start presentation switches off grid view and restores when it ends', () => { + [{ + isGrid: true, + isStripeOpen: true, + }, { + isGrid: false, + isStripeOpen: false, + }].forEach((testState) => { + callViewStore.setCallViewMode(testState) + + callViewStore.startPresentation() + expect(callViewStore.isGrid).toBeFalsy() + expect(callViewStore.isStripeOpen).toBeFalsy() + + callViewStore.stopPresentation() + expect(callViewStore.isGrid).toEqual(testState.isGrid) + expect(callViewStore.isStripeOpen).toEqual(testState.isStripeOpen) + }) + }) + + it('switching modes during presentation does not resets it after it ends', () => { + callViewStore.setCallViewMode({ + isGrid: true, + isStripeOpen: true, + }) + callViewStore.startPresentation() + + // switch during presentation + callViewStore.setCallViewMode({ + isGrid: true, + isStripeOpen: true, + }) + callViewStore.stopPresentation() + + // state kept, not restored + expect(callViewStore.isGrid).toBeTruthy() + expect(callViewStore.isStripeOpen).toBeTruthy() + }) + + it('starting presentation twice does not mess up remembered state', () => { + callViewStore.setCallViewMode({ + isGrid: true, + isStripeOpen: true, + }) + expect(callViewStore.presentationStarted).toBeFalsy() + + callViewStore.startPresentation() + expect(callViewStore.presentationStarted).toBeTruthy() + + // switch during presentation + callViewStore.setCallViewMode({ + isGrid: true, + isStripeOpen: true, + }) + callViewStore.startPresentation() + // state kept + expect(callViewStore.presentationStarted).toBeTruthy() + expect(callViewStore.isGrid).toBeTruthy() + expect(callViewStore.isStripeOpen).toBeTruthy() + + callViewStore.stopPresentation() + expect(callViewStore.presentationStarted).toBeFalsy() + // state kept, not restored + expect(callViewStore.isGrid).toBeTruthy() + expect(callViewStore.isStripeOpen).toBeTruthy() + }) + + it('stopping presentation twice does not mess up remembered state', () => { + callViewStore.setCallViewMode({ + isGrid: true, + isStripeOpen: true, + }) + expect(callViewStore.presentationStarted).toBeFalsy() + + callViewStore.startPresentation() + expect(callViewStore.presentationStarted).toBeTruthy() + + callViewStore.stopPresentation() + expect(callViewStore.presentationStarted).toBeFalsy() + expect(callViewStore.isGrid).toBeTruthy() + expect(callViewStore.isStripeOpen).toBeTruthy() + + callViewStore.setCallViewMode({ + isGrid: false, + isStripeOpen: false, + }) + callViewStore.stopPresentation() + expect(callViewStore.presentationStarted).toBeFalsy() + // state kept, not reset + expect(callViewStore.isGrid).toBeFalsy() + expect(callViewStore.isStripeOpen).toBeFalsy() + }) + + it('does not change last state if provided nothing', () => { + expect(callViewStore.lastIsGrid).toEqual(null) + expect(callViewStore.lastIsStripeOpen).toEqual(null) + callViewStore.setCallViewMode({ + isGrid: true, + isStripeOpen: false, + }) + expect(callViewStore.lastIsGrid).toBeFalsy() + expect(callViewStore.lastIsStripeOpen).toBeTruthy() + + callViewStore.setCallViewMode({ + clearLast: false, + }) + expect(callViewStore.lastIsGrid).toBeFalsy() + expect(callViewStore.lastIsStripeOpen).toBeTruthy() + }) + }) + + describe('other actions', () => { + it('sets value on forceCallView', () => { + expect(callViewStore.forceCallView).toBeFalsy() + callViewStore.setForceCallView(true) + expect(callViewStore.forceCallView).toBeTruthy() + }) + + it('sets value on isViewerOverlay', () => { + expect(callViewStore.isViewerOverlay).toBeFalsy() + callViewStore.setIsViewerOverlay(true) + expect(callViewStore.isViewerOverlay).toBeTruthy() + }) + + it('sets value on isEmptyCallView', () => { + expect(callViewStore.isEmptyCallView).toBeTruthy() + callViewStore.setIsEmptyCallView(false) + expect(callViewStore.isEmptyCallView).toBeFalsy() + }) + + it('sets value on selectedVideoPeerId', () => { + callViewStore.setSelectedVideoPeerId(PEER_ID) + expect(callViewStore.selectedVideoPeerId).toBe(PEER_ID) + }) + + it('sets timeout if timestamp is lesser than 10 seconds', () => { + callViewStore.setCallHasJustEnded(Date.now() / 1000 - 3) + expect(callViewStore.callHasJustEnded).toBeTruthy() + }) + + it('does not set timeout if timestamp is bigger than 10 seconds', () => { + callViewStore.setCallHasJustEnded(Date.now() / 1000 - 15) + expect(callViewStore.callHasJustEnded).toBeFalsy() + }) + + it('resets callHasJustEnded after passed time', () => { + // Arrange + jest.useFakeTimers() + callViewStore.setCallHasJustEnded(Date.now() / 1000 - 2) + expect(callViewStore.callHasJustEnded).toBeTruthy() + // Skip 4 seconds + jest.advanceTimersByTime(4000) + expect(callViewStore.callHasJustEnded).toBeTruthy() + // Skip remaining 4 seconds + jest.advanceTimersByTime(4000) + expect(callViewStore.callHasJustEnded).toBeFalsy() + }) + }) +})