diff --git a/changelog/unreleased/change-message-handling b/changelog/unreleased/change-message-handling new file mode 100644 index 00000000000..384ad1108d5 --- /dev/null +++ b/changelog/unreleased/change-message-handling @@ -0,0 +1,8 @@ +Change: Message handling to pinia + +BREAKING CHANGE for developers: Messages are no longer stored in a vuex store but in pinia instead. This means to display a message in the UI, you need to use the new `useMessages` composable. + +For more details please see the linked PR down below. + +https://github.com/owncloud/web/pull/10309 +https://github.com/owncloud/web/issues/10210 diff --git a/packages/web-app-admin-settings/src/components/General/AppearanceSection.vue b/packages/web-app-admin-settings/src/components/General/AppearanceSection.vue index c064257c9f0..bd055b59027 100644 --- a/packages/web-app-admin-settings/src/components/General/AppearanceSection.vue +++ b/packages/web-app-admin-settings/src/components/General/AppearanceSection.vue @@ -65,7 +65,7 @@ export default defineComponent({ const logoInput: VNodeRef = ref(null) - const { actions: resetLogoActions } = useGeneralActionsResetLogo({ store }) + const { actions: resetLogoActions } = useGeneralActionsResetLogo() const { actions: uploadLogoActions, uploadImage } = useGeneralActionsUploadLogo({ imageInput: logoInput }) diff --git a/packages/web-app-admin-settings/src/components/Groups/ContextActions.vue b/packages/web-app-admin-settings/src/components/Groups/ContextActions.vue index da27eecda6b..3472522b68e 100644 --- a/packages/web-app-admin-settings/src/components/Groups/ContextActions.vue +++ b/packages/web-app-admin-settings/src/components/Groups/ContextActions.vue @@ -24,7 +24,7 @@ export default defineComponent({ setup(props) { const store = useStore() const { actions: showDetailsActions } = useActionsShowDetails() - const { actions: deleteActions } = useGroupActionsDelete({ store }) + const { actions: deleteActions } = useGroupActionsDelete() const { actions: editActions } = useGroupActionsEdit() const menuItemsPrimaryActions = computed(() => diff --git a/packages/web-app-admin-settings/src/components/Groups/CreateGroupModal.vue b/packages/web-app-admin-settings/src/components/Groups/CreateGroupModal.vue index da1bb2c796e..28a7936c37a 100644 --- a/packages/web-app-admin-settings/src/components/Groups/CreateGroupModal.vue +++ b/packages/web-app-admin-settings/src/components/Groups/CreateGroupModal.vue @@ -17,7 +17,7 @@ import { useGettext } from 'vue3-gettext' import { computed, defineComponent, ref, PropType, unref, watch } from 'vue' import { Group } from '@ownclouders/web-client/src/generated' -import { MaybeRef, Modal, useClientService, useEventBus, useStore } from '@ownclouders/web-pkg' +import { MaybeRef, Modal, useClientService, useEventBus, useMessages } from '@ownclouders/web-pkg' export default defineComponent({ name: 'CreateGroupModal', @@ -27,7 +27,7 @@ export default defineComponent({ emits: ['confirm', 'update:confirmDisabled'], setup(props, { emit, expose }) { const { $gettext } = useGettext() - const store = useStore() + const { showMessage, showErrorMessage } = useMessages() const eventBus = useEventBus() const clientService = useClientService() @@ -61,15 +61,13 @@ export default defineComponent({ try { const client = clientService.graphAuthenticated const response = await client.groups.createGroup(unref(group)) - store.dispatch('showMessage', { - title: $gettext('Group was created successfully') - }) + showMessage({ title: $gettext('Group was created successfully') }) eventBus.publish('app.admin-settings.groups.add', { ...response?.data, members: [] }) } catch (error) { console.error(error) - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('Failed to create group'), - error + errors: [error] }) } } diff --git a/packages/web-app-admin-settings/src/components/Users/AddToGroupsModal.vue b/packages/web-app-admin-settings/src/components/Users/AddToGroupsModal.vue index bf656e28494..9027c195f29 100644 --- a/packages/web-app-admin-settings/src/components/Users/AddToGroupsModal.vue +++ b/packages/web-app-admin-settings/src/components/Users/AddToGroupsModal.vue @@ -12,11 +12,11 @@ import { useGettext } from 'vue3-gettext' import { Group, User } from '@ownclouders/web-client/src/generated' import GroupSelect from './GroupSelect.vue' import { - useStore, useEventBus, useClientService, useConfigurationManager, - Modal + Modal, + useMessages } from '@ownclouders/web-pkg' export default defineComponent({ @@ -35,7 +35,7 @@ export default defineComponent({ }, emits: ['update:confirmDisabled'], setup(props, { emit, expose }) { - const store = useStore() + const { showMessage, showErrorMessage } = useMessages() const clientService = useClientService() const configurationManager = useConfigurationManager() const eventBus = useEventBus() @@ -75,7 +75,7 @@ export default defineComponent({ 'Group assignments already added', props.users.length * unref(selectedOptions).length ) - store.dispatch('showMessage', { title }) + showMessage({ title }) return } @@ -95,7 +95,7 @@ export default defineComponent({ { groupAssignmentCount: succeeded.length.toString() }, true ) - store.dispatch('showMessage', { title }) + showMessage({ title }) } const failed = results.filter((r) => r.status === 'rejected') @@ -114,7 +114,7 @@ export default defineComponent({ { groupAssignmentCount: failed.length.toString() }, true ) - store.dispatch('showErrorMessage', { + showErrorMessage({ title, errors: (failed as PromiseRejectedResult[]).map((f) => f.reason) }) diff --git a/packages/web-app-admin-settings/src/components/Users/ContextActions.vue b/packages/web-app-admin-settings/src/components/Users/ContextActions.vue index 367a3bd748e..0a42c5e81c2 100644 --- a/packages/web-app-admin-settings/src/components/Users/ContextActions.vue +++ b/packages/web-app-admin-settings/src/components/Users/ContextActions.vue @@ -33,7 +33,7 @@ export default defineComponent({ const { actions: showDetailsActions } = useActionsShowDetails() const { actions: editQuotaActions } = useUserActionsEditQuota() const { actions: userEditActions } = useUserActionsEdit() - const { actions: userDeleteActions } = useUserActionsDelete({ store }) + const { actions: userDeleteActions } = useUserActionsDelete() const menuItemsPrimaryActions = computed(() => [...unref(userEditActions), ...unref(userDeleteActions)].filter((item) => diff --git a/packages/web-app-admin-settings/src/components/Users/CreateUserModal.vue b/packages/web-app-admin-settings/src/components/Users/CreateUserModal.vue index 426c18117a2..0bf4eb496fc 100644 --- a/packages/web-app-admin-settings/src/components/Users/CreateUserModal.vue +++ b/packages/web-app-admin-settings/src/components/Users/CreateUserModal.vue @@ -47,14 +47,14 @@ import { useGettext } from 'vue3-gettext' import { computed, defineComponent, ref, unref, PropType, watch } from 'vue' import * as EmailValidator from 'email-validator' -import { Modal, useClientService, useEventBus, useStore } from '@ownclouders/web-pkg' +import { Modal, useClientService, useEventBus, useMessages } from '@ownclouders/web-pkg' export default defineComponent({ name: 'CreateUserModal', props: { modal: { type: Object as PropType, required: true } }, emits: ['confirm', 'update:confirmDisabled'], setup(props, { emit, expose }) { - const store = useStore() + const { showMessage, showErrorMessage } = useMessages() const eventBus = useEventBus() const clientService = useClientService() const { $gettext } = useGettext() @@ -111,15 +111,13 @@ export default defineComponent({ const { data } = await client.users.createUser(unref(user)) const { id: createdUserId } = data const { data: createdUser } = await client.users.getUser(createdUserId) - store.dispatch('showMessage', { - title: $gettext('User was created successfully') - }) + showMessage({ title: $gettext('User was created successfully') }) eventBus.publish('app.admin-settings.users.add', createdUser) } catch (error) { console.error(error) - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('Failed to create user'), - error + errors: [error] }) } } diff --git a/packages/web-app-admin-settings/src/components/Users/LoginModal.vue b/packages/web-app-admin-settings/src/components/Users/LoginModal.vue index 956a36dc2af..4fca3d1241f 100644 --- a/packages/web-app-admin-settings/src/components/Users/LoginModal.vue +++ b/packages/web-app-admin-settings/src/components/Users/LoginModal.vue @@ -17,7 +17,13 @@ import { computed, defineComponent, onMounted, PropType, ref, unref, watch } from 'vue' import { useGettext } from 'vue3-gettext' import { User } from '@ownclouders/web-client/src/generated' -import { useClientService, useStore, useEventBus, useUserStore, Modal } from '@ownclouders/web-pkg' +import { + useClientService, + useEventBus, + useUserStore, + Modal, + useMessages +} from '@ownclouders/web-pkg' type LoginOption = { label: string @@ -35,7 +41,7 @@ export default defineComponent({ }, emits: ['update:confirmDisabled'], setup(props, { emit, expose }) { - const store = useStore() + const { showMessage, showErrorMessage } = useMessages() const clientService = useClientService() const eventBus = useEventBus() const { $gettext, $ngettext } = useGettext() @@ -93,7 +99,7 @@ export default defineComponent({ { userCount: succeeded.length.toString() }, true ) - store.dispatch('showMessage', { title }) + showMessage({ title }) } const failed = results.filter((r) => r.status === 'rejected') @@ -112,7 +118,7 @@ export default defineComponent({ { userCount: failed.length.toString() }, true ) - store.dispatch('showErrorMessage', { + showErrorMessage({ title, errors: (failed as PromiseRejectedResult[]).map((f) => f.reason) }) diff --git a/packages/web-app-admin-settings/src/components/Users/RemoveFromGroupsModal.vue b/packages/web-app-admin-settings/src/components/Users/RemoveFromGroupsModal.vue index a56cf7e85ce..38a7a42b39e 100644 --- a/packages/web-app-admin-settings/src/components/Users/RemoveFromGroupsModal.vue +++ b/packages/web-app-admin-settings/src/components/Users/RemoveFromGroupsModal.vue @@ -11,7 +11,7 @@ import { defineComponent, PropType, ref, unref, watch } from 'vue' import { useGettext } from 'vue3-gettext' import { Group, User } from '@ownclouders/web-client/src/generated' import GroupSelect from './GroupSelect.vue' -import { useStore, useEventBus, useClientService, Modal } from '@ownclouders/web-pkg' +import { useEventBus, useClientService, Modal, useMessages } from '@ownclouders/web-pkg' export default defineComponent({ name: 'RemoveFromGroupsModal', @@ -29,7 +29,7 @@ export default defineComponent({ }, emits: ['update:confirmDisabled'], setup(props, { emit, expose }) { - const store = useStore() + const { showMessage, showErrorMessage } = useMessages() const clientService = useClientService() const eventBus = useEventBus() const { $gettext, $ngettext } = useGettext() @@ -68,7 +68,7 @@ export default defineComponent({ 'Group assignments already removed', props.users.length * unref(selectedOptions).length ) - store.dispatch('showMessage', { title }) + showMessage({ title }) return } @@ -88,7 +88,7 @@ export default defineComponent({ { groupAssignmentCount: succeeded.length.toString() }, true ) - store.dispatch('showMessage', { title }) + showMessage({ title }) } const failed = results.filter((r) => r.status === 'rejected') @@ -107,7 +107,7 @@ export default defineComponent({ { groupAssignmentCount: failed.length.toString() }, true ) - store.dispatch('showErrorMessage', { + showErrorMessage({ title, errors: (failed as PromiseRejectedResult[]).map((f) => f.reason) }) diff --git a/packages/web-app-admin-settings/src/composables/actions/general/useGeneralActionsResetLogo.ts b/packages/web-app-admin-settings/src/composables/actions/general/useGeneralActionsResetLogo.ts index e9c5f663589..f10f7b2b97b 100644 --- a/packages/web-app-admin-settings/src/composables/actions/general/useGeneralActionsResetLogo.ts +++ b/packages/web-app-admin-settings/src/composables/actions/general/useGeneralActionsResetLogo.ts @@ -1,11 +1,10 @@ -import { Store } from 'vuex' import { useGettext } from 'vue3-gettext' import { computed } from 'vue' -import { useAbility, useClientService, useRouter, useStore } from '@ownclouders/web-pkg' +import { useAbility, useClientService, useMessages, useRouter } from '@ownclouders/web-pkg' import { Action } from '@ownclouders/web-pkg' -export const useGeneralActionsResetLogo = ({ store }: { store?: Store }) => { - store = store || useStore() +export const useGeneralActionsResetLogo = () => { + const { showMessage, showErrorMessage } = useMessages() const { $gettext } = useGettext() const ability = useAbility() const clientService = useClientService() @@ -15,17 +14,15 @@ export const useGeneralActionsResetLogo = ({ store }: { store?: Store }) => try { const httpClient = clientService.httpAuthenticated await httpClient.delete('/branding/logo') - store.dispatch('showMessage', { - title: $gettext('Logo was reset successfully. Reloading page...') - }) + showMessage({ title: $gettext('Logo was reset successfully. Reloading page...') }) setTimeout(() => { router.go(0) }, 1000) } catch (e) { console.error(e) - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('Failed to reset logo'), - error: e + errors: [e] }) } } diff --git a/packages/web-app-admin-settings/src/composables/actions/general/useGeneralActionsUploadLogo.ts b/packages/web-app-admin-settings/src/composables/actions/general/useGeneralActionsUploadLogo.ts index ab4c052c0ef..cc1d726706e 100644 --- a/packages/web-app-admin-settings/src/composables/actions/general/useGeneralActionsUploadLogo.ts +++ b/packages/web-app-admin-settings/src/composables/actions/general/useGeneralActionsUploadLogo.ts @@ -1,18 +1,11 @@ -import { Store } from 'vuex' import { supportedLogoMimeTypes } from '../../../defaults' import { computed, VNodeRef, unref } from 'vue' -import { Action } from '@ownclouders/web-pkg' -import { useAbility, useClientService, useRouter, useStore } from '@ownclouders/web-pkg' +import { Action, useMessages } from '@ownclouders/web-pkg' +import { useAbility, useClientService, useRouter } from '@ownclouders/web-pkg' import { useGettext } from 'vue3-gettext' -export const useGeneralActionsUploadLogo = ({ - store, - imageInput -}: { - store?: Store - imageInput: VNodeRef -}) => { - store = store || useStore() +export const useGeneralActionsUploadLogo = ({ imageInput }: { imageInput: VNodeRef }) => { + const { showMessage, showErrorMessage } = useMessages() const { $gettext } = useGettext() const ability = useAbility() const clientService = useClientService() @@ -26,9 +19,7 @@ export const useGeneralActionsUploadLogo = ({ } if (!supportedLogoMimeTypes.includes(file.type)) { - return store.dispatch('showErrorMessage', { - title: $gettext('The file type is unsupported') - }) + return showErrorMessage({ title: $gettext('The file type is unsupported') }) } try { @@ -40,17 +31,15 @@ export const useGeneralActionsUploadLogo = ({ 'Content-Type': 'multipart/form-data' } }) - store.dispatch('showMessage', { - title: $gettext('Logo was uploaded successfully') - }) + showMessage({ title: $gettext('Logo was uploaded successfully') }) setTimeout(() => { router.go(0) }, 1000) } catch (e) { console.error(e) - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('Failed to upload logo'), - error: e + errors: [e] }) } } diff --git a/packages/web-app-admin-settings/src/composables/actions/groups/useGroupActionsDelete.ts b/packages/web-app-admin-settings/src/composables/actions/groups/useGroupActionsDelete.ts index 6055f227325..ccd7cd4a165 100644 --- a/packages/web-app-admin-settings/src/composables/actions/groups/useGroupActionsDelete.ts +++ b/packages/web-app-admin-settings/src/composables/actions/groups/useGroupActionsDelete.ts @@ -1,13 +1,12 @@ import { computed } from 'vue' -import { Store } from 'vuex' -import { eventBus, useModals } from '@ownclouders/web-pkg' -import { useClientService, useStore } from '@ownclouders/web-pkg' +import { eventBus, useMessages, useModals } from '@ownclouders/web-pkg' +import { useClientService } from '@ownclouders/web-pkg' import { GroupAction, GroupActionOptions } from '@ownclouders/web-pkg' import { useGettext } from 'vue3-gettext' import { Group } from '@ownclouders/web-client/src/generated' -export const useGroupActionsDelete = ({ store }: { store?: Store }) => { - store = store || useStore() +export const useGroupActionsDelete = () => { + const { showMessage, showErrorMessage } = useMessages() const { $gettext, $ngettext } = useGettext() const clientService = useClientService() const { dispatchModal } = useModals() @@ -29,7 +28,7 @@ export const useGroupActionsDelete = ({ store }: { store?: Store }) => { { groupCount: succeeded.length.toString() }, true ) - store.dispatch('showMessage', { title }) + showMessage({ title }) } const failed = results.filter((r) => r.status === 'rejected') @@ -46,7 +45,7 @@ export const useGroupActionsDelete = ({ store }: { store?: Store }) => { { groupCount: failed.length.toString() }, true ) - store.dispatch('showErrorMessage', { + showErrorMessage({ title, errors: (failed as PromiseRejectedResult[]).map((f) => f.reason) }) diff --git a/packages/web-app-admin-settings/src/composables/actions/users/useUserActionsDelete.ts b/packages/web-app-admin-settings/src/composables/actions/users/useUserActionsDelete.ts index 8dc8f3aadeb..5ee86c78f29 100644 --- a/packages/web-app-admin-settings/src/composables/actions/users/useUserActionsDelete.ts +++ b/packages/web-app-admin-settings/src/composables/actions/users/useUserActionsDelete.ts @@ -1,13 +1,17 @@ import { computed, unref } from 'vue' -import { Store } from 'vuex' -import { eventBus, useCapabilityDeleteUsersDisabled, useModals } from '@ownclouders/web-pkg' -import { useClientService, useStore } from '@ownclouders/web-pkg' +import { + eventBus, + useCapabilityDeleteUsersDisabled, + useMessages, + useModals +} from '@ownclouders/web-pkg' +import { useClientService } from '@ownclouders/web-pkg' import { UserAction, UserActionOptions } from '@ownclouders/web-pkg' import { useGettext } from 'vue3-gettext' import { User } from '@ownclouders/web-client/src/generated' -export const useUserActionsDelete = ({ store }: { store?: Store }) => { - store = store || useStore() +export const useUserActionsDelete = () => { + const { showMessage, showErrorMessage } = useMessages() const { $gettext, $ngettext } = useGettext() const clientService = useClientService() const { dispatchModal } = useModals() @@ -29,7 +33,7 @@ export const useUserActionsDelete = ({ store }: { store?: Store }) => { { userCount: succeeded.length.toString() }, true ) - store.dispatch('showMessage', { title }) + showMessage({ title }) } const failed = results.filter((r) => r.status === 'rejected') @@ -46,7 +50,7 @@ export const useUserActionsDelete = ({ store }: { store?: Store }) => { { userCount: failed.length.toString() }, true ) - store.dispatch('showErrorMessage', { + showErrorMessage({ title, errors: (failed as PromiseRejectedResult[]).map((f) => f.reason) }) diff --git a/packages/web-app-admin-settings/src/views/Groups.vue b/packages/web-app-admin-settings/src/views/Groups.vue index 05a03d8a181..e8bc9027bce 100644 --- a/packages/web-app-admin-settings/src/views/Groups.vue +++ b/packages/web-app-admin-settings/src/views/Groups.vue @@ -71,6 +71,7 @@ import { eventBus, queryItemAsString, useClientService, + useMessages, useRouteQuery, useSideBar, useStore @@ -94,6 +95,7 @@ export default defineComponent({ }, setup() { const store = useStore() + const { showErrorMessage } = useMessages() const groups = ref([]) const template = ref() const selectedGroups = ref([]) @@ -124,7 +126,7 @@ export default defineComponent({ }) }) - const { actions: deleteActions } = useGroupActionsDelete({ store }) + const { actions: deleteActions } = useGroupActionsDelete() const batchActions = computed(() => { return [...unref(deleteActions)].filter((item) => item.isEnabled({ resources: unref(selectedGroups) }) @@ -151,9 +153,9 @@ export default defineComponent({ return updatedGroup } catch (error) { console.error(error) - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('Failed to edit group'), - error + errors: [error] }) } } diff --git a/packages/web-app-admin-settings/src/views/Users.vue b/packages/web-app-admin-settings/src/views/Users.vue index 61d4a90e881..1c77fcccd59 100644 --- a/packages/web-app-admin-settings/src/views/Users.vue +++ b/packages/web-app-admin-settings/src/views/Users.vue @@ -163,7 +163,8 @@ import { useStore, SideBarPanel, SideBarPanelContext, - useUserStore + useUserStore, + useMessages } from '@ownclouders/web-pkg' import { computed, @@ -197,6 +198,7 @@ export default defineComponent({ const router = useRouter() const route = useRoute() const store = useStore() + const { showErrorMessage } = useMessages() const accessToken = useAccessToken({ store }) const clientService = useClientService() const configurationManager = useConfigurationManager() @@ -219,7 +221,7 @@ export default defineComponent({ const { actions: createUserActions } = useUserActionsCreateUser() const createUserAction = computed(() => unref(createUserActions)[0]) - const { actions: deleteActions } = useUserActionsDelete({ store }) + const { actions: deleteActions } = useUserActionsDelete() const { actions: removeFromGroupsActions } = useUserActionsRemoveFromGroups({ groups: writableGroups }) @@ -523,9 +525,9 @@ export default defineComponent({ return updatedUser } catch (error) { console.error(error) - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('Failed to edit user'), - error + errors: [error] }) } } diff --git a/packages/web-app-admin-settings/tests/unit/components/Groups/CreateGroupModal.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Groups/CreateGroupModal.spec.ts index d2b34e85ae7..25e8ce24ab6 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Groups/CreateGroupModal.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/components/Groups/CreateGroupModal.spec.ts @@ -10,7 +10,7 @@ import { } from 'web-test-helpers' import { mock } from 'jest-mock-extended' import { AxiosResponse } from 'axios' -import { Modal, eventBus } from '@ownclouders/web-pkg' +import { Modal, eventBus, useMessages } from '@ownclouders/web-pkg' describe('CreateGroupModal', () => { describe('computed method "isFormInvalid"', () => { @@ -60,18 +60,19 @@ describe('CreateGroupModal', () => { describe('method "onConfirm"', () => { it('should not create group if form is invalid', async () => { jest.spyOn(console, 'error').mockImplementation(() => undefined) - const { wrapper, storeOptions } = getWrapper() + const { wrapper } = getWrapper() const eventSpy = jest.spyOn(eventBus, 'publish') try { await wrapper.vm.onConfirm() } catch (error) {} - expect(storeOptions.actions.showMessage).not.toHaveBeenCalled() + const { showMessage } = useMessages() + expect(showMessage).not.toHaveBeenCalled() expect(eventSpy).not.toHaveBeenCalled() }) it('should create group on success', async () => { - const { wrapper, mocks, storeOptions } = getWrapper() + const { wrapper, mocks } = getWrapper() mocks.$clientService.graphAuthenticated.groups.getGroup.mockRejectedValueOnce(new Error('')) wrapper.vm.group.displayName = 'foo bar' @@ -84,14 +85,15 @@ describe('CreateGroupModal', () => { const eventSpy = jest.spyOn(eventBus, 'publish') await wrapper.vm.onConfirm() - expect(storeOptions.actions.showMessage).toHaveBeenCalled() + const { showMessage } = useMessages() + expect(showMessage).toHaveBeenCalled() expect(eventSpy).toHaveBeenCalled() }) it('should show message on error', async () => { jest.spyOn(console, 'error').mockImplementation(() => undefined) - const { wrapper, mocks, storeOptions } = getWrapper() + const { wrapper, mocks } = getWrapper() mocks.$clientService.graphAuthenticated.groups.getGroup.mockRejectedValue(new Error('')) wrapper.vm.group.displayName = 'foo bar' @@ -103,7 +105,8 @@ describe('CreateGroupModal', () => { const eventSpy = jest.spyOn(eventBus, 'publish') await wrapper.vm.onConfirm() - expect(storeOptions.actions.showErrorMessage).toHaveBeenCalled() + const { showErrorMessage } = useMessages() + expect(showErrorMessage).toHaveBeenCalled() expect(eventSpy).not.toHaveBeenCalled() }) }) diff --git a/packages/web-app-admin-settings/tests/unit/components/Users/AddToGroupsModal.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Users/AddToGroupsModal.spec.ts index 0585443c88e..a9bfe450282 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Users/AddToGroupsModal.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/components/Users/AddToGroupsModal.spec.ts @@ -1,15 +1,13 @@ import AddToGroupsModal from '../../../../src/components/Users/AddToGroupsModal.vue' import { - createStore, defaultComponentMocks, defaultPlugins, - defaultStoreMockOptions, mockAxiosResolve, shallowMount } from 'web-test-helpers' import { mock } from 'jest-mock-extended' import { Group, User } from '@ownclouders/web-client/src/generated' -import { Modal, eventBus } from '@ownclouders/web-pkg' +import { Modal, eventBus, useMessages } from '@ownclouders/web-pkg' describe('AddToGroupsModal', () => { it('renders the input', () => { @@ -21,7 +19,7 @@ describe('AddToGroupsModal', () => { it('adds all users to the given groups', async () => { const users = [mock({ memberOf: [] }), mock({ memberOf: [] })] const groups = [mock(), mock()] - const { wrapper, mocks, storeOptions } = getWrapper({ users, groups }) + const { wrapper, mocks } = getWrapper({ users, groups }) mocks.$clientService.graphAuthenticated.groups.addMember.mockResolvedValue(undefined) mocks.$clientService.graphAuthenticated.users.getUser.mockResolvedValue( mockAxiosResolve({ id: 'e3515ffb-d264-4dfc-8506-6c239f6673b5' }) @@ -31,7 +29,8 @@ describe('AddToGroupsModal', () => { const eventSpy = jest.spyOn(eventBus, 'publish') await wrapper.vm.onConfirm() - expect(storeOptions.actions.showMessage).toHaveBeenCalled() + const { showMessage } = useMessages() + expect(showMessage).toHaveBeenCalled() expect(eventSpy).toHaveBeenCalled() }) @@ -40,7 +39,7 @@ describe('AddToGroupsModal', () => { const users = [mock({ memberOf: [] }), mock({ memberOf: [] })] const groups = [mock(), mock()] - const { wrapper, mocks, storeOptions } = getWrapper({ users, groups }) + const { wrapper, mocks } = getWrapper({ users, groups }) mocks.$clientService.graphAuthenticated.groups.addMember.mockRejectedValue(new Error('')) mocks.$clientService.graphAuthenticated.users.getUser.mockRejectedValue(new Error('')) @@ -48,7 +47,8 @@ describe('AddToGroupsModal', () => { const eventSpy = jest.spyOn(eventBus, 'publish') await wrapper.vm.onConfirm() - expect(storeOptions.actions.showErrorMessage).toHaveBeenCalled() + const { showErrorMessage } = useMessages() + expect(showErrorMessage).toHaveBeenCalled() expect(eventSpy).not.toHaveBeenCalled() }) }) @@ -56,12 +56,9 @@ describe('AddToGroupsModal', () => { function getWrapper({ users = [mock()], groups = [mock()] } = {}) { const mocks = defaultComponentMocks() - const storeOptions = defaultStoreMockOptions - const store = createStore(storeOptions) return { mocks, - storeOptions, wrapper: shallowMount(AddToGroupsModal, { props: { modal: mock(), @@ -70,7 +67,7 @@ function getWrapper({ users = [mock()], groups = [mock()] } = {}) { }, global: { provide: mocks, - plugins: [...defaultPlugins(), store], + plugins: [...defaultPlugins()], stubs: { GroupSelect: true } } }) diff --git a/packages/web-app-admin-settings/tests/unit/components/Users/CreateUserModal.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Users/CreateUserModal.spec.ts index 971df49a7ec..add4090d547 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Users/CreateUserModal.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/components/Users/CreateUserModal.spec.ts @@ -1,16 +1,14 @@ import CreateUserModal from '../../../../src/components/Users/CreateUserModal.vue' import { - createStore, defaultComponentMocks, defaultPlugins, - defaultStoreMockOptions, mockAxiosReject, mockAxiosResolve, shallowMount } from 'web-test-helpers' import { mock } from 'jest-mock-extended' import { AxiosResponse } from 'axios' -import { Modal, eventBus } from '@ownclouders/web-pkg' +import { Modal, eventBus, useMessages } from '@ownclouders/web-pkg' describe('CreateUserModal', () => { describe('computed method "isFormInvalid"', () => { @@ -117,18 +115,19 @@ describe('CreateUserModal', () => { describe('method "onConfirm"', () => { it('should not create user if form is invalid', async () => { jest.spyOn(console, 'error').mockImplementation(() => undefined) - const { wrapper, storeOptions } = getWrapper() + const { wrapper } = getWrapper() const eventSpy = jest.spyOn(eventBus, 'publish') try { await wrapper.vm.onConfirm() } catch (error) {} - expect(storeOptions.actions.showMessage).not.toHaveBeenCalled() + const { showMessage } = useMessages() + expect(showMessage).not.toHaveBeenCalled() expect(eventSpy).not.toHaveBeenCalled() }) it('should create user on success', async () => { - const { wrapper, mocks, storeOptions } = getWrapper() + const { wrapper, mocks } = getWrapper() mocks.$clientService.graphAuthenticated.users.getUser.mockRejectedValueOnce(new Error('')) wrapper.vm.user.onPremisesSamAccountName = 'foo' @@ -150,14 +149,15 @@ describe('CreateUserModal', () => { const eventSpy = jest.spyOn(eventBus, 'publish') await wrapper.vm.onConfirm() - expect(storeOptions.actions.showMessage).toHaveBeenCalled() + const { showMessage } = useMessages() + expect(showMessage).toHaveBeenCalled() expect(eventSpy).toHaveBeenCalled() }) it('should show message on error', async () => { jest.spyOn(console, 'error').mockImplementation(() => undefined) - const { wrapper, mocks, storeOptions } = getWrapper() + const { wrapper, mocks } = getWrapper() mocks.$clientService.graphAuthenticated.users.getUser.mockRejectedValue(new Error('')) wrapper.vm.user.onPremisesSamAccountName = 'foo' @@ -175,7 +175,8 @@ describe('CreateUserModal', () => { const eventSpy = jest.spyOn(eventBus, 'publish') await wrapper.vm.onConfirm() - expect(storeOptions.actions.showErrorMessage).toHaveBeenCalled() + const { showErrorMessage } = useMessages() + expect(showErrorMessage).toHaveBeenCalled() expect(eventSpy).not.toHaveBeenCalled() }) }) @@ -183,12 +184,9 @@ describe('CreateUserModal', () => { function getWrapper() { const mocks = defaultComponentMocks() - const storeOptions = defaultStoreMockOptions - const store = createStore(storeOptions) return { mocks, - storeOptions, wrapper: shallowMount(CreateUserModal, { props: { modal: mock() @@ -196,7 +194,7 @@ function getWrapper() { global: { mocks, provide: mocks, - plugins: [...defaultPlugins(), store] + plugins: [...defaultPlugins()] } }) } diff --git a/packages/web-app-admin-settings/tests/unit/components/Users/LoginModal.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Users/LoginModal.spec.ts index 5b9e34cb61a..c474c276ea6 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Users/LoginModal.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/components/Users/LoginModal.spec.ts @@ -1,15 +1,13 @@ import LoginModal from '../../../../src/components/Users/LoginModal.vue' import { - createStore, defaultComponentMocks, defaultPlugins, - defaultStoreMockOptions, mockAxiosResolve, shallowMount } from 'web-test-helpers' import { mock } from 'jest-mock-extended' import { User } from '@ownclouders/web-client/src/generated' -import { Modal, eventBus } from '@ownclouders/web-pkg' +import { Modal, eventBus, useMessages } from '@ownclouders/web-pkg' describe('LoginModal', () => { it('renders the input including two options', () => { @@ -23,7 +21,7 @@ describe('LoginModal', () => { describe('method "onConfirm"', () => { it('updates the login for all given users', async () => { const users = [mock(), mock()] - const { wrapper, mocks, storeOptions } = getWrapper(users) + const { wrapper, mocks } = getWrapper(users) mocks.$clientService.graphAuthenticated.users.editUser.mockResolvedValue( mockAxiosResolve({ id: 'e3515ffb-d264-4dfc-8506-6c239f6673b5' }) ) @@ -34,7 +32,8 @@ describe('LoginModal', () => { const eventSpy = jest.spyOn(eventBus, 'publish') await wrapper.vm.onConfirm() - expect(storeOptions.actions.showMessage).toHaveBeenCalled() + const { showMessage } = useMessages() + expect(showMessage).toHaveBeenCalled() expect(eventSpy).toHaveBeenCalled() expect(mocks.$clientService.graphAuthenticated.users.editUser).toHaveBeenCalledTimes( users.length @@ -57,24 +56,22 @@ describe('LoginModal', () => { jest.spyOn(console, 'error').mockImplementation(() => undefined) const users = [mock(), mock()] - const { wrapper, mocks, storeOptions } = getWrapper(users) + const { wrapper, mocks } = getWrapper(users) mocks.$clientService.graphAuthenticated.users.editUser.mockRejectedValue(new Error('')) mocks.$clientService.graphAuthenticated.users.getUser.mockRejectedValue(new Error('')) await wrapper.vm.onConfirm() - expect(storeOptions.actions.showErrorMessage).toHaveBeenCalled() + const { showErrorMessage } = useMessages() + expect(showErrorMessage).toHaveBeenCalled() }) }) }) function getWrapper(users = [mock()]) { const mocks = defaultComponentMocks() - const storeOptions = defaultStoreMockOptions - const store = createStore(storeOptions) return { mocks, - storeOptions, wrapper: shallowMount(LoginModal, { props: { modal: mock(), @@ -82,7 +79,7 @@ function getWrapper(users = [mock()]) { }, global: { provide: mocks, - plugins: [...defaultPlugins(), store] + plugins: [...defaultPlugins()] } }) } diff --git a/packages/web-app-admin-settings/tests/unit/components/Users/RemoveFromGroupsModal.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Users/RemoveFromGroupsModal.spec.ts index fe29c754eb2..9dfaf30d60d 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Users/RemoveFromGroupsModal.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/components/Users/RemoveFromGroupsModal.spec.ts @@ -1,15 +1,13 @@ import RemoveFromGroupsModal from '../../../../src/components/Users/RemoveFromGroupsModal.vue' import { - createStore, defaultComponentMocks, defaultPlugins, - defaultStoreMockOptions, mockAxiosResolve, shallowMount } from 'web-test-helpers' import { mock } from 'jest-mock-extended' import { Group, User } from '@ownclouders/web-client/src/generated' -import { Modal, eventBus } from '@ownclouders/web-pkg' +import { Modal, eventBus, useMessages } from '@ownclouders/web-pkg' describe('RemoveFromGroupsModal', () => { it('renders the input', () => { @@ -24,7 +22,7 @@ describe('RemoveFromGroupsModal', () => { mock({ memberOf: [{ id: '1' }] }) ] const groups = [mock({ id: '1' })] - const { wrapper, mocks, storeOptions } = getWrapper({ users, groups }) + const { wrapper, mocks } = getWrapper({ users, groups }) mocks.$clientService.graphAuthenticated.groups.deleteMember.mockResolvedValue(undefined) mocks.$clientService.graphAuthenticated.users.getUser.mockResolvedValue( mockAxiosResolve({ id: 'e3515ffb-d264-4dfc-8506-6c239f6673b5' }) @@ -34,7 +32,8 @@ describe('RemoveFromGroupsModal', () => { const eventSpy = jest.spyOn(eventBus, 'publish') await wrapper.vm.onConfirm() - expect(storeOptions.actions.showMessage).toHaveBeenCalled() + const { showMessage } = useMessages() + expect(showMessage).toHaveBeenCalled() expect(eventSpy).toHaveBeenCalled() }) @@ -46,7 +45,7 @@ describe('RemoveFromGroupsModal', () => { mock({ memberOf: [{ id: '1' }] }) ] const groups = [mock({ id: '1' })] - const { wrapper, mocks, storeOptions } = getWrapper({ users, groups }) + const { wrapper, mocks } = getWrapper({ users, groups }) mocks.$clientService.graphAuthenticated.groups.deleteMember.mockRejectedValue(new Error('')) mocks.$clientService.graphAuthenticated.users.getUser.mockRejectedValue(new Error('')) @@ -54,7 +53,8 @@ describe('RemoveFromGroupsModal', () => { const eventSpy = jest.spyOn(eventBus, 'publish') await wrapper.vm.onConfirm() - expect(storeOptions.actions.showErrorMessage).toHaveBeenCalled() + const { showErrorMessage } = useMessages() + expect(showErrorMessage).toHaveBeenCalled() expect(eventSpy).not.toHaveBeenCalled() }) }) @@ -62,12 +62,9 @@ describe('RemoveFromGroupsModal', () => { function getWrapper({ users = [mock()], groups = [mock()] } = {}) { const mocks = defaultComponentMocks() - const storeOptions = defaultStoreMockOptions - const store = createStore(storeOptions) return { mocks, - storeOptions, wrapper: shallowMount(RemoveFromGroupsModal, { props: { modal: mock(), @@ -76,7 +73,7 @@ function getWrapper({ users = [mock()], groups = [mock()] } = {}) { }, global: { provide: mocks, - plugins: [...defaultPlugins(), store], + plugins: [...defaultPlugins()], stubs: { GroupSelect: true } } }) diff --git a/packages/web-app-admin-settings/tests/unit/composables/actions/general/useGeneralActionsResetLogo.spec.ts b/packages/web-app-admin-settings/tests/unit/composables/actions/general/useGeneralActionsResetLogo.spec.ts index 0fe5cd347ee..0b9e425ea89 100644 --- a/packages/web-app-admin-settings/tests/unit/composables/actions/general/useGeneralActionsResetLogo.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/composables/actions/general/useGeneralActionsResetLogo.spec.ts @@ -1,9 +1,8 @@ +import { useMessages } from '@ownclouders/web-pkg' import { useGeneralActionsResetLogo } from '../../../../../src/composables/actions/general/useGeneralActionsResetLogo' import { mock } from 'jest-mock-extended' import { unref } from 'vue' import { - createStore, - defaultStoreMockOptions, defaultComponentMocks, RouteLocation, mockAxiosResolve, @@ -17,12 +16,13 @@ describe('resetLogo', () => { describe('handler', () => { it('should show message on request success', () => { getWrapper({ - setup: async ({ actions }, { storeOptions, clientService, router }) => { + setup: async ({ actions }, { clientService, router }) => { clientService.httpAuthenticated.delete.mockImplementation(() => mockAxiosResolve()) await unref(actions)[0].handler() jest.runAllTimers() expect(router.go).toHaveBeenCalledTimes(1) - expect(storeOptions.actions.showMessage).toHaveBeenCalledTimes(1) + const { showMessage } = useMessages() + expect(showMessage).toHaveBeenCalledTimes(1) } }) }) @@ -30,12 +30,13 @@ describe('resetLogo', () => { it('should show message on request error', () => { jest.spyOn(console, 'error').mockImplementation(() => undefined) getWrapper({ - setup: async ({ actions }, { storeOptions, clientService, router }) => { + setup: async ({ actions }, { clientService, router }) => { clientService.httpAuthenticated.delete.mockRejectedValue(() => mockAxiosReject()) await unref(actions)[0].handler() jest.runAllTimers() expect(router.go).toHaveBeenCalledTimes(0) - expect(storeOptions.actions.showErrorMessage).toHaveBeenCalledTimes(1) + const { showErrorMessage } = useMessages() + expect(showErrorMessage).toHaveBeenCalledTimes(1) } }) }) @@ -48,33 +49,27 @@ function getWrapper({ setup: ( instance: ReturnType, { - storeOptions, clientService, router }: { - storeOptions: typeof defaultStoreMockOptions clientService: ReturnType['$clientService'] router: ReturnType['$router'] } ) => void }) { - const storeOptions = defaultStoreMockOptions - const store = createStore(storeOptions) const mocks = defaultComponentMocks({ currentRoute: mock({ name: 'admin-settings-general' }) }) return { wrapper: getComposableWrapper( () => { - const instance = useGeneralActionsResetLogo({ store }) + const instance = useGeneralActionsResetLogo() setup(instance, { - storeOptions, clientService: mocks.$clientService, router: mocks.$router }) }, { - store, mocks, provide: mocks } diff --git a/packages/web-app-admin-settings/tests/unit/composables/actions/general/useGeneralActionsUploadLogo.spec.ts b/packages/web-app-admin-settings/tests/unit/composables/actions/general/useGeneralActionsUploadLogo.spec.ts index b244b85b739..81683f82376 100644 --- a/packages/web-app-admin-settings/tests/unit/composables/actions/general/useGeneralActionsUploadLogo.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/composables/actions/general/useGeneralActionsUploadLogo.spec.ts @@ -1,9 +1,8 @@ +import { useMessages } from '@ownclouders/web-pkg' import { useGeneralActionsUploadLogo } from '../../../../../src/composables/actions/general/useGeneralActionsUploadLogo' import { mock } from 'jest-mock-extended' import { VNodeRef } from 'vue' import { - createStore, - defaultStoreMockOptions, defaultComponentMocks, RouteLocation, mockAxiosResolve, @@ -17,7 +16,7 @@ describe('uploadImage', () => { describe('method "uploadImage"', () => { it('should show message on request success', () => { getWrapper({ - setup: async ({ uploadImage }, { storeOptions, clientService, router }) => { + setup: async ({ uploadImage }, { clientService, router }) => { clientService.httpAuthenticated.post.mockImplementation(() => mockAxiosResolve()) await uploadImage({ currentTarget: { @@ -26,7 +25,8 @@ describe('uploadImage', () => { } as unknown as InputEvent) jest.runAllTimers() expect(router.go).toHaveBeenCalledTimes(1) - expect(storeOptions.actions.showMessage).toHaveBeenCalledTimes(1) + const { showMessage } = useMessages() + expect(showMessage).toHaveBeenCalledTimes(1) } }) }) @@ -34,7 +34,7 @@ describe('uploadImage', () => { it('should show message on request error', () => { jest.spyOn(console, 'error').mockImplementation(() => undefined) getWrapper({ - setup: async ({ uploadImage }, { storeOptions, clientService, router }) => { + setup: async ({ uploadImage }, { clientService, router }) => { clientService.httpAuthenticated.post.mockRejectedValue(() => mockAxiosReject()) await uploadImage({ currentTarget: { @@ -43,7 +43,8 @@ describe('uploadImage', () => { } as unknown as InputEvent) jest.runAllTimers() expect(router.go).toHaveBeenCalledTimes(0) - expect(storeOptions.actions.showErrorMessage).toHaveBeenCalledTimes(1) + const { showErrorMessage } = useMessages() + expect(showErrorMessage).toHaveBeenCalledTimes(1) } }) }) @@ -51,14 +52,15 @@ describe('uploadImage', () => { it('should show message on invalid mimeType', () => { jest.spyOn(console, 'error').mockImplementation(() => undefined) getWrapper({ - setup: async ({ uploadImage }, { storeOptions, clientService }) => { + setup: async ({ uploadImage }, { clientService }) => { await uploadImage({ currentTarget: { files: [{ name: 'text.txt', type: 'text/plain' }] } } as unknown as InputEvent) expect(clientService.httpAuthenticated.post).toHaveBeenCalledTimes(0) - expect(storeOptions.actions.showErrorMessage).toHaveBeenCalledTimes(1) + const { showErrorMessage } = useMessages() + expect(showErrorMessage).toHaveBeenCalledTimes(1) } }) }) @@ -72,19 +74,15 @@ function getWrapper({ instance: ReturnType, { imageInput, - storeOptions, clientService, router }: { imageInput: VNodeRef - storeOptions: typeof defaultStoreMockOptions clientService: ReturnType['$clientService'] router: ReturnType['$router'] } ) => void }) { - const storeOptions = defaultStoreMockOptions - const store = createStore(storeOptions) const mocks = defaultComponentMocks({ currentRoute: mock({ name: 'admin-settings-general' }) }) @@ -93,16 +91,14 @@ function getWrapper({ wrapper: getComposableWrapper( () => { const imageInput = mock() - const instance = useGeneralActionsUploadLogo({ store, imageInput }) + const instance = useGeneralActionsUploadLogo({ imageInput }) setup(instance, { imageInput, - storeOptions, clientService: mocks.$clientService, router: mocks.$router }) }, { - store, mocks, provide: mocks } diff --git a/packages/web-app-admin-settings/tests/unit/composables/actions/groups/useGroupActionsDelete.spec.ts b/packages/web-app-admin-settings/tests/unit/composables/actions/groups/useGroupActionsDelete.spec.ts index c0cf7e8c98c..d617bddc3b7 100644 --- a/packages/web-app-admin-settings/tests/unit/composables/actions/groups/useGroupActionsDelete.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/composables/actions/groups/useGroupActionsDelete.spec.ts @@ -83,7 +83,7 @@ function getWrapper({ return { wrapper: getComposableWrapper( () => { - const instance = useGroupActionsDelete({ store }) + const instance = useGroupActionsDelete() setup(instance, { storeOptions, clientService: mocks.$clientService }) }, { store, mocks, provide: mocks } diff --git a/packages/web-app-admin-settings/tests/unit/composables/actions/users/useUserActionsDelete.spec.ts b/packages/web-app-admin-settings/tests/unit/composables/actions/users/useUserActionsDelete.spec.ts index 04dc9590eb0..320716fb5ff 100644 --- a/packages/web-app-admin-settings/tests/unit/composables/actions/users/useUserActionsDelete.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/composables/actions/users/useUserActionsDelete.spec.ts @@ -79,7 +79,7 @@ function getWrapper({ return { wrapper: getComposableWrapper( () => { - const instance = useUserActionsDelete({ store }) + const instance = useUserActionsDelete() setup(instance, { storeOptions, clientService: mocks.$clientService }) }, { store, mocks, provide: mocks } diff --git a/packages/web-app-admin-settings/tests/unit/views/Groups.spec.ts b/packages/web-app-admin-settings/tests/unit/views/Groups.spec.ts index 668060b925e..d7a86c2c7c7 100644 --- a/packages/web-app-admin-settings/tests/unit/views/Groups.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/views/Groups.spec.ts @@ -1,7 +1,7 @@ import Groups from '../../../src/views/Groups.vue' import { mockAxiosResolve, mockAxiosReject } from 'web-test-helpers/src/mocks' import { mock, mockDeep } from 'jest-mock-extended' -import { ClientService, eventBus } from '@ownclouders/web-pkg' +import { ClientService, eventBus, useMessages } from '@ownclouders/web-pkg' import { createStore, defaultComponentMocks, @@ -53,10 +53,11 @@ describe('Groups view', () => { jest.spyOn(console, 'error').mockImplementation(() => undefined) const clientService = getClientServiceMock() clientService.graphAuthenticated.groups.editGroup.mockImplementation(() => mockAxiosReject()) - const { wrapper, storeOptions } = getWrapper({ clientService }) + const { wrapper } = getWrapper({ clientService }) await wrapper.vm.onEditGroup({}) - expect(storeOptions.actions.showErrorMessage).toHaveBeenCalled() + const { showErrorMessage } = useMessages() + expect(showErrorMessage).toHaveBeenCalled() }) }) diff --git a/packages/web-app-admin-settings/tests/unit/views/Users.spec.ts b/packages/web-app-admin-settings/tests/unit/views/Users.spec.ts index df0ab1e4a26..1bc28aab7e0 100644 --- a/packages/web-app-admin-settings/tests/unit/views/Users.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/views/Users.spec.ts @@ -4,7 +4,8 @@ import { UserAction, eventBus, useAppDefaults, - useConfigurationManager + useConfigurationManager, + useMessages } from '@ownclouders/web-pkg' import { mock, mockDeep } from 'jest-mock-extended' import { mockAxiosResolve, mockAxiosReject } from 'web-test-helpers/src/mocks' @@ -257,14 +258,15 @@ describe('Users view', () => { jest.spyOn(console, 'error').mockImplementation(() => undefined) const clientService = getClientService() clientService.graphAuthenticated.users.editUser.mockImplementation(() => mockAxiosReject()) - const { wrapper, storeOptions } = getMountedWrapper({ clientService }) + const { wrapper } = getMountedWrapper({ clientService }) await wrapper.vm.loadResourcesTask.last await wrapper.vm.onEditUser({ editUser: {} }) - expect(storeOptions.actions.showErrorMessage).toHaveBeenCalled() + const { showErrorMessage } = useMessages() + expect(showErrorMessage).toHaveBeenCalled() }) }) diff --git a/packages/web-app-external/src/App.vue b/packages/web-app-external/src/App.vue index 16457192ef8..f490d15bb24 100644 --- a/packages/web-app-external/src/App.vue +++ b/packages/web-app-external/src/App.vue @@ -34,6 +34,7 @@ import { isSameResource, queryItemAsString, useConfigurationManager, + useMessages, useRequest, useRouteQuery, useStore @@ -55,6 +56,7 @@ export default defineComponent({ setup(props, { emit }) { const language = useGettext() const store = useStore() + const { showErrorMessage } = useMessages() const configurationManager = useConfigurationManager() const { $gettext } = language @@ -80,19 +82,17 @@ export default defineComponent({ }) const errorPopup = (error) => { - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('An error occurred'), desc: error, - error + errors: [error] }) } const loadAppUrl = useTask(function* (signal, viewMode: string) { try { if (props.isReadOnly && viewMode === 'write') { - store.dispatch('showErrorMessage', { - title: $gettext('Cannot open file in edit mode as it is read-only') - }) + showErrorMessage({ title: $gettext('Cannot open file in edit mode as it is read-only') }) return } diff --git a/packages/web-app-files/src/HandleUpload.ts b/packages/web-app-files/src/HandleUpload.ts index fcdf316da34..856aa67cd36 100644 --- a/packages/web-app-files/src/HandleUpload.ts +++ b/packages/web-app-files/src/HandleUpload.ts @@ -10,7 +10,7 @@ import { RouteLocationNormalizedLoaded } from 'vue-router' import { Resource, SpaceResource } from '@ownclouders/web-client/src' import { urlJoin } from '@ownclouders/web-client/src/utils' import { ResourceConflict } from './helpers/resource' -import { UserStore, locationPublicLink } from '@ownclouders/web-pkg' +import { MessageStore, UserStore, locationPublicLink } from '@ownclouders/web-pkg' import { locationSpacesGeneric, UppyService, UppyResource } from '@ownclouders/web-pkg' import { isPersonalSpaceResource, isShareSpaceResource } from '@ownclouders/web-client/src/helpers' import { ClientService, queryItemAsString } from '@ownclouders/web-pkg' @@ -22,6 +22,7 @@ export interface HandleUploadOptions { route: Ref store: Store userStore: UserStore + messageStore: MessageStore uppyService: UppyService id?: string space?: SpaceResource @@ -51,6 +52,7 @@ export class HandleUpload extends BasePlugin { space: SpaceResource store: Store userStore: UserStore + messageStore: MessageStore uppyService: UppyService quotaCheckEnabled: boolean directoryTreeCreateEnabled: boolean @@ -69,6 +71,7 @@ export class HandleUpload extends BasePlugin { this.space = opts.space this.store = opts.store this.userStore = opts.userStore + this.messageStore = opts.messageStore this.uppyService = opts.uppyService this.quotaCheckEnabled = opts.quotaCheckEnabled ?? true @@ -246,7 +249,7 @@ export class HandleUpload extends BasePlugin { spaceName = $gettext('Personal') } - this.store.dispatch('showErrorMessage', { + this.messageStore.showErrorMessage({ title: $gettext('Not enough quota'), desc: $gettext( 'There is not enough quota on %{spaceName}, you need additional %{missingSpace} to upload these files', diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 73afe031460..1f5ba208a70 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -192,6 +192,7 @@ import { isLocationSpacesActive, useFileActions, useFileActionsCreateNewShortcut, + useMessages, useUserStore } from '@ownclouders/web-pkg' import { useActiveLocation } from '@ownclouders/web-pkg' @@ -265,6 +266,7 @@ export default defineComponent({ const clientService = useClientService() const store = useStore() const userStore = useUserStore() + const messageStore = useMessages() const route = useRoute() const language = useGettext() const hasSpaces = useCapabilitySpacesEnabled(store) @@ -281,6 +283,7 @@ export default defineComponent({ space: props.space, store, userStore, + messageStore, uppyService }) } diff --git a/packages/web-app-files/src/components/AppBar/CreateSpace.vue b/packages/web-app-files/src/components/AppBar/CreateSpace.vue index ca6248cbbcb..74a5eb9d58f 100644 --- a/packages/web-app-files/src/components/AppBar/CreateSpace.vue +++ b/packages/web-app-files/src/components/AppBar/CreateSpace.vue @@ -16,11 +16,18 @@ diff --git a/packages/web-runtime/src/container/bootstrap.ts b/packages/web-runtime/src/container/bootstrap.ts index 1c91db9f114..4be677a602e 100644 --- a/packages/web-runtime/src/container/bootstrap.ts +++ b/packages/web-runtime/src/container/bootstrap.ts @@ -8,7 +8,13 @@ import { loadTheme } from '../helpers/theme' import OwnCloud from 'owncloud-sdk' import { createGettext, GetTextOptions, Language } from 'vue3-gettext' import { getBackendVersion, getWebVersion } from './versions' -import { useModals, useThemeStore, useUserStore, UserStore } from '@ownclouders/web-pkg' +import { + useModals, + useThemeStore, + useUserStore, + UserStore, + useMessages +} from '@ownclouders/web-pkg' import { authService } from '../services/auth' import { ClientService, @@ -331,9 +337,10 @@ export const announceTheme = async ({ } export const announcePiniaStores = () => { + const messagesStore = useMessages() const modalStore = useModals() const userStore = useUserStore() - return { modalStore, userStore } + return { messagesStore, modalStore, userStore } } /** diff --git a/packages/web-runtime/src/layouts/Application.vue b/packages/web-runtime/src/layouts/Application.vue index 99bf9bdcd8d..9232d443abc 100644 --- a/packages/web-runtime/src/layouts/Application.vue +++ b/packages/web-runtime/src/layouts/Application.vue @@ -27,14 +27,14 @@
- +
diff --git a/packages/web-runtime/src/pages/account.vue b/packages/web-runtime/src/pages/account.vue index 21f6add3d79..1039fce23a5 100644 --- a/packages/web-runtime/src/pages/account.vue +++ b/packages/web-runtime/src/pages/account.vue @@ -149,6 +149,7 @@ import { useCapabilityGraphPersonalDataExport, useClientService, useGetMatchingSpace, + useMessages, useModals, useStore, useUserStore @@ -171,6 +172,7 @@ export default defineComponent({ }, setup() { const store = useStore() + const { showMessage, showErrorMessage } = useMessages() const userStore = useUserStore() const language = useGettext() const { $gettext } = language @@ -215,9 +217,9 @@ export default defineComponent({ valuesList.value = values || [] } catch (e) { console.error(e) - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('Unable to load account data…'), - error: e + errors: [e] }) valuesList.value = [] } @@ -235,9 +237,9 @@ export default defineComponent({ accountBundle.value = bundles?.find((b) => b.extension === 'ocis-accounts') } catch (e) { console.error(e) - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('Unable to load account data…'), - error: e + errors: [e] }) accountBundle.value = undefined } @@ -249,9 +251,9 @@ export default defineComponent({ graphUser.value = data } catch (e) { console.error(e) - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('Unable to load account data…'), - error: e + errors: [e] }) graphUser.value = undefined } @@ -345,14 +347,12 @@ export default defineComponent({ }) } - store.dispatch('showMessage', { - title: $gettext('Preference saved.') - }) + showMessage({ title: $gettext('Preference saved.') }) } catch (e) { console.error(e) - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('Unable to save preference…'), - error: e + errors: [e] }) } } @@ -364,14 +364,12 @@ export default defineComponent({ valueOptions: { boolValue: !option } }) disableEmailNotificationsValue.value = option - store.dispatch('showMessage', { - title: $gettext('Preference saved.') - }) + showMessage({ title: $gettext('Preference saved.') }) } catch (e) { console.error(e) - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('Unable to save preference…'), - error: e + errors: [e] }) } } @@ -380,14 +378,12 @@ export default defineComponent({ try { store.commit('Files/SET_FILE_WEB_DAV_DETAILS_VISIBILITY', option) viewOptionWebDavDetailsValue.value = option - store.dispatch('showMessage', { - title: $gettext('Preference saved.') - }) + showMessage({ title: $gettext('Preference saved.') }) } catch (e) { console.error(e) - store.dispatch('showErrorMessage', { + showErrorMessage({ title: $gettext('Unable to save preference…'), - error: e + errors: [e] }) } } diff --git a/packages/web-runtime/src/store/app.ts b/packages/web-runtime/src/store/app.ts deleted file mode 100644 index 146ea578a15..00000000000 --- a/packages/web-runtime/src/store/app.ts +++ /dev/null @@ -1,74 +0,0 @@ -const state = { - messages: [] -} - -const actions = { - showErrorMessage({ commit }, message) { - const getXRequestID = (error: any): string | null => { - /** - * x-request-id response headers might be very nested in ownCloud SDK, - * only remove records if you are sure they aren't valid anymore - */ - if (error.response?.res?.res?.headers?.['x-request-id']) { - return error.response.res.res.headers['x-request-id'] - } - if (error.response?.headers?.map?.['x-request-id']) { - return error.response.headers.map['x-request-id'] - } - if (error.response?.res?.headers?.['x-request-id']) { - return error.response.res.headers['x-request-id'] - } - if (error.response?.headers?.['x-request-id']) { - return error.response.headers['x-request-id'] - } - return null - } - - message.status = message.status || 'danger' - message.timeout = message.timeout || 0 - message.errors = message.error ? [message.error] : message.errors || [] - - const xRequestIds = message.errors - .map((error) => getXRequestID(error)) - .filter((xRequestId) => xRequestId !== null) - .map((item) => `X-Request-Id: ${item}`) - .join('\r\n') - - message.errorLogContent = xRequestIds - - commit('ENQUEUE_MESSAGE', message) - }, - showMessage({ commit }, message) { - commit('ENQUEUE_MESSAGE', message) - }, - deleteMessage(context, mId) { - context.commit('REMOVE_MESSAGE', mId) - } -} - -const mutations = { - ENQUEUE_MESSAGE(state, message) { - // set random id to improve iteration in v-for & lodash - if (!message.id) { - message.id = Math.random().toString(36).slice(2, -1) - } - - state.messages.push(message) - }, - REMOVE_MESSAGE(state, item) { - state.messages.splice(state.messages.indexOf(item), 1) - } -} - -const getters = { - activeMessages: (state) => { - return state.messages - } -} - -export default { - state, - actions, - mutations, - getters -} diff --git a/packages/web-runtime/src/store/index.ts b/packages/web-runtime/src/store/index.ts index 2c63a6f851d..b851c9eefb3 100644 --- a/packages/web-runtime/src/store/index.ts +++ b/packages/web-runtime/src/store/index.ts @@ -1,5 +1,4 @@ import ancestorMetaData from './ancestorMetaData' -import app from './app' import apps from './apps' import auth from './auth' import config from './config' @@ -20,7 +19,6 @@ const runtime = { export default { modules: { - app, apps, user, config, diff --git a/packages/web-runtime/tests/unit/components/MessageBar.spec.ts b/packages/web-runtime/tests/unit/components/MessageBar.spec.ts index a05f07cfb94..a6d1d847af3 100644 --- a/packages/web-runtime/tests/unit/components/MessageBar.spec.ts +++ b/packages/web-runtime/tests/unit/components/MessageBar.spec.ts @@ -1,10 +1,6 @@ +import { useMessages } from '@ownclouders/web-pkg' import MessageBar from 'web-runtime/src/components/MessageBar.vue' -import { - createStore, - defaultPlugins, - shallowMount, - defaultStoreMockOptions -} from 'web-test-helpers' +import { defaultPlugins, shallowMount } from 'web-test-helpers' const messages = [ { @@ -50,30 +46,22 @@ const selectors = { } describe('MessageBar component', () => { - const spyDeleteMessage = jest.spyOn((MessageBar as any).methods, 'deleteMessage') - const spyOcMessagesLimited = jest.spyOn((MessageBar as any).computed, '$_ocMessages_limited') - - afterEach(() => { - jest.clearAllMocks() - }) - describe('when there is an active message', () => { - const { wrapper } = getShallowWrapper([messages[0]]) - const notificationMessage = wrapper.findComponent(selectors.notificationMessage) - it('should set props in oc-notification-message component', () => { + const { wrapper } = getShallowWrapper([messages[0]]) + const notificationMessage = wrapper.findComponent(selectors.notificationMessage) + expect(notificationMessage.attributes().title).toEqual(messages[0].title) expect(notificationMessage.attributes().status).toEqual(messages[0].status) expect(notificationMessage.attributes().message).toEqual(messages[0].desc) }) - it('should call "deleteMessage" method on close event', () => { - expect(spyDeleteMessage).toHaveBeenCalledTimes(0) + it('should call "removeMessage" method on close event', () => { + const { wrapper } = getShallowWrapper([messages[0]]) + const messageStore = useMessages() + const notificationMessage = wrapper.findComponent(selectors.notificationMessage) ;(notificationMessage.vm as any).$emit('close') - expect(spyDeleteMessage).toHaveBeenCalledTimes(1) - expect(spyDeleteMessage).toHaveBeenCalledWith(messages[0]) - expect(wrapper.emitted().deleteMessage).toBeTruthy() - expect(wrapper.emitted().deleteMessage).toEqual([[messages[0]]]) + expect(messageStore.removeMessage).toHaveBeenCalledTimes(1) }) }) @@ -81,28 +69,17 @@ describe('MessageBar component', () => { it('should return only the first five messages', () => { const { wrapper } = getShallowWrapper(messages) - expect(spyOcMessagesLimited).toHaveBeenCalledTimes(1) - expect(wrapper.vm.$_ocMessages_limited).toHaveLength(5) + expect(wrapper.findAll(selectors.notificationMessage).length).toBe(5) }) }) }) -function getShallowWrapper(activeMessages = []) { - const storeOptions = defaultStoreMockOptions - storeOptions.getters.configuration.mockImplementation(() => ({ - options: { - topCenterNotifications: false - } - })) - const store = createStore(storeOptions) +function getShallowWrapper(messages = []) { return { wrapper: shallowMount(MessageBar, { - props: { - activeMessages - }, global: { renderStubDefaultSlot: true, - plugins: [...defaultPlugins(), store] + plugins: [...defaultPlugins({ piniaOptions: { messagesState: { messages } } })] } }) } diff --git a/packages/web-runtime/tests/unit/pages/account.spec.ts b/packages/web-runtime/tests/unit/pages/account.spec.ts index 50c522bbf5f..8ba9a565dcd 100644 --- a/packages/web-runtime/tests/unit/pages/account.spec.ts +++ b/packages/web-runtime/tests/unit/pages/account.spec.ts @@ -11,7 +11,7 @@ import { import { mock } from 'jest-mock-extended' import { SpaceResource } from '@ownclouders/web-client/src/helpers' import { AxiosResponse } from 'axios' -import { ConfigurationManager } from '@ownclouders/web-pkg' +import { ConfigurationManager, useMessages } from '@ownclouders/web-pkg' import { SettingsBundle, SettingsValue } from 'web-runtime/src/helpers/settings' import { User } from '@ownclouders/web-client/src/generated' @@ -233,7 +233,7 @@ describe('account page', () => { describe('Method "updateDisableEmailNotifications', () => { it('should show a message on success', async () => { - const { wrapper, mocks, storeOptions } = getWrapper() + const { wrapper, mocks } = getWrapper() await wrapper.vm.loadAccountBundleTask.last await wrapper.vm.loadValuesListTask.last @@ -241,12 +241,13 @@ describe('account page', () => { mocks.$clientService.httpAuthenticated.post.mockResolvedValueOnce(mockAxiosResolve({})) await wrapper.vm.updateDisableEmailNotifications(true) - expect(storeOptions.actions.showMessage).toHaveBeenCalled() + const { showMessage } = useMessages() + expect(showMessage).toHaveBeenCalled() }) it('should show a message on error', async () => { jest.spyOn(console, 'error').mockImplementation(() => undefined) - const { wrapper, mocks, storeOptions } = getWrapper() + const { wrapper, mocks } = getWrapper() await wrapper.vm.loadAccountBundleTask.last await wrapper.vm.loadValuesListTask.last @@ -254,13 +255,14 @@ describe('account page', () => { mocks.$clientService.httpAuthenticated.post.mockImplementation(() => mockAxiosReject('err')) await wrapper.vm.updateDisableEmailNotifications(true) - expect(storeOptions.actions.showErrorMessage).toHaveBeenCalled() + const { showErrorMessage } = useMessages() + expect(showErrorMessage).toHaveBeenCalled() }) }) describe('Method "updateSelectedLanguage', () => { it('should show a message on success', async () => { - const { wrapper, mocks, storeOptions } = getWrapper({}) + const { wrapper, mocks } = getWrapper({}) await wrapper.vm.loadAccountBundleTask.last await wrapper.vm.loadValuesListTask.last @@ -270,12 +272,13 @@ describe('account page', () => { mockAxiosResolve({}) ) await wrapper.vm.updateSelectedLanguage('en') - expect(storeOptions.actions.showMessage).toHaveBeenCalled() + const { showMessage } = useMessages() + expect(showMessage).toHaveBeenCalled() }) it('should show a message on error', async () => { jest.spyOn(console, 'error').mockImplementation(() => undefined) - const { wrapper, mocks, storeOptions } = getWrapper({}) + const { wrapper, mocks } = getWrapper({}) await wrapper.vm.loadAccountBundleTask.last await wrapper.vm.loadValuesListTask.last @@ -285,7 +288,8 @@ describe('account page', () => { mockAxiosReject('err') ) await wrapper.vm.updateSelectedLanguage('en') - expect(storeOptions.actions.showErrorMessage).toHaveBeenCalled() + const { showErrorMessage } = useMessages() + expect(showErrorMessage).toHaveBeenCalled() }) }) @@ -298,7 +302,8 @@ describe('account page', () => { await wrapper.vm.loadGraphUserTask.last await wrapper.vm.updateViewOptionsWebDavDetails(true) - expect(storeOptions.actions.showMessage).toHaveBeenCalled() + const { showMessage } = useMessages() + expect(showMessage).toHaveBeenCalled() expect( storeOptions.modules.Files.mutations.SET_FILE_WEB_DAV_DETAILS_VISIBILITY ).toHaveBeenCalled() diff --git a/packages/web-test-helpers/src/mocks/pinia.ts b/packages/web-test-helpers/src/mocks/pinia.ts index a4a3eddb40e..2a8974af7cb 100644 --- a/packages/web-test-helpers/src/mocks/pinia.ts +++ b/packages/web-test-helpers/src/mocks/pinia.ts @@ -1,7 +1,7 @@ import { createTestingPinia } from '@pinia/testing' import defaultTheme from '../../../web-runtime/themes/owncloud/theme.json' import { User } from '../../../web-client/src/generated' -import { Modal, WebThemeType } from '../../../web-pkg/src/composables/piniaStores' +import { Message, Modal, WebThemeType } from '../../../web-pkg/src/composables/piniaStores' import { mock } from 'jest-mock-extended' export { createTestingPinia } @@ -9,6 +9,7 @@ export { createTestingPinia } export type PiniaMockOptions = { stubActions?: boolean themeState?: { availableThemes?: WebThemeType[]; currentTheme?: WebThemeType } + messagesState?: { messages?: Message[] } modalsState?: { modals?: Modal[] } userState?: { user?: User } } @@ -16,6 +17,7 @@ export type PiniaMockOptions = { export function createMockStore({ stubActions = true, themeState = {}, + messagesState = {}, modalsState = {}, userState = {} }: PiniaMockOptions = {}) { @@ -33,6 +35,7 @@ export function createMockStore({ return createTestingPinia({ stubActions, initialState: { + messages: { messages: [], ...messagesState }, modals: { modals: [], ...modalsState diff --git a/packages/web-test-helpers/src/mocks/store/defaultStoreMockOptions.ts b/packages/web-test-helpers/src/mocks/store/defaultStoreMockOptions.ts index 28d4275021d..121e0723d7e 100644 --- a/packages/web-test-helpers/src/mocks/store/defaultStoreMockOptions.ts +++ b/packages/web-test-helpers/src/mocks/store/defaultStoreMockOptions.ts @@ -33,9 +33,6 @@ export const defaultStoreMockOptions = { } }, actions: { - showMessage: jest.fn(), - showErrorMessage: jest.fn(), - deleteNotification: jest.fn(), openNavigation: jest.fn(), closeNavigation: jest.fn() },