diff --git a/admin/package.json b/admin/package.json index 0cbf57f5a..d499117af 100644 --- a/admin/package.json +++ b/admin/package.json @@ -36,6 +36,7 @@ "graphql": "^15.6.1", "identity-obj-proxy": "^3.0.0", "jest": "26.6.3", + "portal-vue": "^2.1.7", "regenerator-runtime": "^0.13.9", "stats-webpack-plugin": "^0.7.0", "vue": "^2.6.11", @@ -43,7 +44,6 @@ "vue-i18n": "^8.26.5", "vue-jest": "^3.0.7", "vue-router": "^3.5.3", - "vue-toasted": "^1.1.28", "vuex": "^3.6.2", "vuex-persistedstate": "^4.1.0" }, diff --git a/admin/src/components/ConfirmRegisterMailFormular.spec.js b/admin/src/components/ConfirmRegisterMailFormular.spec.js index 78f5791dc..e9e21b31e 100644 --- a/admin/src/components/ConfirmRegisterMailFormular.spec.js +++ b/admin/src/components/ConfirmRegisterMailFormular.spec.js @@ -1,21 +1,17 @@ import { mount } from '@vue/test-utils' import ConfirmRegisterMailFormular from './ConfirmRegisterMailFormular.vue' +import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup' + const localVue = global.localVue const apolloMutateMock = jest.fn().mockResolvedValue() -const toastSuccessMock = jest.fn() -const toastErrorMock = jest.fn() const mocks = { $t: jest.fn((t) => t), $apollo: { mutate: apolloMutateMock, }, - $toasted: { - success: toastSuccessMock, - error: toastErrorMock, - }, } const propsData = { @@ -54,7 +50,7 @@ describe('ConfirmRegisterMailFormular', () => { }) it('toasts a success message', () => { - expect(toastSuccessMock).toBeCalledWith('unregister_mail.success') + expect(toastSuccessSpy).toBeCalledWith('unregister_mail.success') }) }) @@ -66,7 +62,7 @@ describe('ConfirmRegisterMailFormular', () => { }) it('toasts an error message', () => { - expect(toastErrorMock).toBeCalledWith('unregister_mail.error') + expect(toastErrorSpy).toBeCalledWith('unregister_mail.error') }) }) }) diff --git a/admin/src/components/ConfirmRegisterMailFormular.vue b/admin/src/components/ConfirmRegisterMailFormular.vue index 067e95c67..1b72f55d0 100644 --- a/admin/src/components/ConfirmRegisterMailFormular.vue +++ b/admin/src/components/ConfirmRegisterMailFormular.vue @@ -48,10 +48,10 @@ export default { }, }) .then(() => { - this.$toasted.success(this.$t('unregister_mail.success', { email: this.email })) + this.toastSuccess(this.$t('unregister_mail.success', { email: this.email })) }) .catch((error) => { - this.$toasted.error(this.$t('unregister_mail.error', { message: error.message })) + this.toastError(this.$t('unregister_mail.error', { message: error.message })) }) }, }, diff --git a/admin/src/components/CreationFormular.spec.js b/admin/src/components/CreationFormular.spec.js index cfc23fa26..083b7ca67 100644 --- a/admin/src/components/CreationFormular.spec.js +++ b/admin/src/components/CreationFormular.spec.js @@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils' import CreationFormular from './CreationFormular.vue' import { createPendingCreation } from '../graphql/createPendingCreation' import { createPendingCreations } from '../graphql/createPendingCreations' +import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup' const localVue = global.localVue @@ -11,8 +12,6 @@ const apolloMutateMock = jest.fn().mockResolvedValue({ }, }) const stateCommitMock = jest.fn() -const toastedErrorMock = jest.fn() -const toastedSuccessMock = jest.fn() const mocks = { $t: jest.fn((t, options) => (options ? [t, options] : t)), @@ -32,10 +31,6 @@ const mocks = { }, }, }, - $toasted: { - error: toastedErrorMock, - success: toastedSuccessMock, - }, } const propsData = { @@ -140,7 +135,7 @@ describe('CreationFormular', () => { }) it('toasts a success message', () => { - expect(toastedSuccessMock).toBeCalledWith([ + expect(toastSuccessSpy).toBeCalledWith([ 'creation_form.toasted', { email: 'benjamin@bluemchen.de', value: '90' }, ]) @@ -162,7 +157,7 @@ describe('CreationFormular', () => { }) it('toasts an error message', () => { - expect(toastedErrorMock).toBeCalledWith('Ouch!') + expect(toastErrorSpy).toBeCalledWith('Ouch!') }) }) @@ -292,7 +287,7 @@ describe('CreationFormular', () => { }) it('toast success message', () => { - expect(toastedSuccessMock).toBeCalled() + expect(toastSuccessSpy).toBeCalled() }) it('store commit openCreationPlus', () => { @@ -426,13 +421,14 @@ describe('CreationFormular', () => { expect(stateCommitMock).toBeCalledWith('openCreationsPlus', 0) }) - it('toasts two errors', () => { - expect(toastedErrorMock).toBeCalledWith( - 'Could not created PendingCreation for bob@baumeister.de', - ) - expect(toastedErrorMock).toBeCalledWith( - 'Could not created PendingCreation for bibi@bloxberg.de', - ) + it('emits remove all bookmarks', () => { + expect(wrapper.emitted('remove-all-bookmark')).toBeTruthy() + }) + + it('emits toast failed creations with two emails', () => { + expect(wrapper.emitted('toast-failed-creations')).toEqual([ + [['bob@baumeister.de', 'bibi@bloxberg.de']], + ]) }) }) @@ -454,7 +450,7 @@ describe('CreationFormular', () => { }) it('toasts an error message', () => { - expect(toastedErrorMock).toBeCalledWith('Oh no!') + expect(toastErrorSpy).toBeCalledWith('Oh no!') }) }) }) diff --git a/admin/src/components/CreationFormular.vue b/admin/src/components/CreationFormular.vue index 34df13e11..cd4de5fd6 100644 --- a/admin/src/components/CreationFormular.vue +++ b/admin/src/components/CreationFormular.vue @@ -166,20 +166,21 @@ export default { fetchPolicy: 'no-cache', }) .then((result) => { + const failedCreations = [] this.$store.commit( 'openCreationsPlus', result.data.createPendingCreations.successfulCreation.length, ) if (result.data.createPendingCreations.failedCreation.length > 0) { - result.data.createPendingCreations.failedCreation.forEach((failed) => { - // TODO: Please localize this error message - this.$toasted.error('Could not created PendingCreation for ' + failed) + result.data.createPendingCreations.failedCreation.forEach((email) => { + failedCreations.push(email) }) } this.$emit('remove-all-bookmark') + this.$emit('toast-failed-creations', failedCreations) }) .catch((error) => { - this.$toasted.error(error.message) + this.toastError(error.message) }) } else if (this.type === 'singleCreation') { submitObj = { @@ -196,19 +197,19 @@ export default { }) .then((result) => { this.$emit('update-user-data', this.item, result.data.createPendingCreation) - this.$toasted.success( + this.$store.commit('openCreationsPlus', 1) + this.toastSuccess( this.$t('creation_form.toasted', { value: this.value, email: this.item.email, }), ) - this.$store.commit('openCreationsPlus', 1) // what is this? Tests says that this.text is not reseted this.$refs.creationForm.reset() this.value = 0 }) .catch((error) => { - this.$toasted.error(error.message) + this.toastError(error.message) this.$refs.creationForm.reset() this.value = 0 }) diff --git a/admin/src/components/CreationTransactionListFormular.spec.js b/admin/src/components/CreationTransactionListFormular.spec.js index 88cda89ee..7331184b7 100644 --- a/admin/src/components/CreationTransactionListFormular.spec.js +++ b/admin/src/components/CreationTransactionListFormular.spec.js @@ -1,5 +1,6 @@ import { mount } from '@vue/test-utils' import CreationTransactionListFormular from './CreationTransactionListFormular.vue' +import { toastErrorSpy } from '../../test/testSetup' const localVue = global.localVue @@ -50,17 +51,12 @@ const apolloQueryMock = jest.fn().mockResolvedValue({ }, }) -const toastedErrorMock = jest.fn() - const mocks = { $d: jest.fn((t) => t), $t: jest.fn((t) => t), $apollo: { query: apolloQueryMock, }, - $toasted: { - error: toastedErrorMock, - }, } const propsData = { @@ -109,7 +105,7 @@ describe('CreationTransactionListFormular', () => { }) it('toast error', () => { - expect(toastedErrorMock).toBeCalledWith('OUCH!') + expect(toastErrorSpy).toBeCalledWith('OUCH!') }) }) }) diff --git a/admin/src/components/CreationTransactionListFormular.vue b/admin/src/components/CreationTransactionListFormular.vue index 627647534..f4fbe7c8b 100644 --- a/admin/src/components/CreationTransactionListFormular.vue +++ b/admin/src/components/CreationTransactionListFormular.vue @@ -62,7 +62,7 @@ export default { this.items = result.data.transactionList.transactions.filter((t) => t.type === 'creation') }) .catch((error) => { - this.$toasted.error(error.message) + this.toastError(error.message) }) }, }, diff --git a/admin/src/components/DeletedUserFormular.spec.js b/admin/src/components/DeletedUserFormular.spec.js index bad97c1d7..5c41831e5 100644 --- a/admin/src/components/DeletedUserFormular.spec.js +++ b/admin/src/components/DeletedUserFormular.spec.js @@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils' import DeletedUserFormular from './DeletedUserFormular.vue' import { deleteUser } from '../graphql/deleteUser' import { unDeleteUser } from '../graphql/unDeleteUser' +import { toastErrorSpy } from '../../test/testSetup' const localVue = global.localVue @@ -13,9 +14,6 @@ const apolloMutateMock = jest.fn().mockResolvedValue({ }, }) -const toastedErrorMock = jest.fn() -const toastedSuccessMock = jest.fn() - const mocks = { $t: jest.fn((t) => t), $apollo: { @@ -29,10 +27,6 @@ const mocks = { }, }, }, - $toasted: { - error: toastedErrorMock, - success: toastedSuccessMock, - }, } const propsData = { @@ -118,10 +112,6 @@ describe('DeletedUserFormular', () => { ) }) - it('toasts a success message', () => { - expect(toastedSuccessMock).toBeCalledWith('user_deleted') - }) - it('emits update deleted At', () => { expect(wrapper.emitted('updateDeletedAt')).toEqual( expect.arrayContaining([ @@ -147,7 +137,7 @@ describe('DeletedUserFormular', () => { }) it('toasts an error message', () => { - expect(toastedErrorMock).toBeCalledWith('Oh no!') + expect(toastErrorSpy).toBeCalledWith('Oh no!') }) }) @@ -215,10 +205,6 @@ describe('DeletedUserFormular', () => { ) }) - it('toasts a success message', () => { - expect(toastedSuccessMock).toBeCalledWith('user_recovered') - }) - it('emits update deleted At', () => { expect(wrapper.emitted('updateDeletedAt')).toEqual( expect.arrayContaining([ @@ -244,7 +230,7 @@ describe('DeletedUserFormular', () => { }) it('toasts an error message', () => { - expect(toastedErrorMock).toBeCalledWith('Oh no!') + expect(toastErrorSpy).toBeCalledWith('Oh no!') }) }) diff --git a/admin/src/components/DeletedUserFormular.vue b/admin/src/components/DeletedUserFormular.vue index b840fdb23..03359d9f9 100644 --- a/admin/src/components/DeletedUserFormular.vue +++ b/admin/src/components/DeletedUserFormular.vue @@ -45,7 +45,6 @@ export default { }, }) .then((result) => { - this.$toasted.success(this.$t('user_deleted')) this.$emit('updateDeletedAt', { userId: this.item.userId, deletedAt: result.data.deleteUser, @@ -53,7 +52,7 @@ export default { this.checked = false }) .catch((error) => { - this.$toasted.error(error.message) + this.toastError(error.message) }) }, unDeleteUser() { @@ -65,7 +64,7 @@ export default { }, }) .then((result) => { - this.$toasted.success(this.$t('user_recovered')) + this.toastSuccess(this.$t('user_recovered')) this.$emit('updateDeletedAt', { userId: this.item.userId, deletedAt: result.data.unDeleteUser, @@ -73,7 +72,7 @@ export default { this.checked = false }) .catch((error) => { - this.$toasted.error(error.message) + this.toastError(error.message) }) }, }, diff --git a/admin/src/components/EditCreationFormular.spec.js b/admin/src/components/EditCreationFormular.spec.js index 84d3e26d3..f5c7fb0fe 100644 --- a/admin/src/components/EditCreationFormular.spec.js +++ b/admin/src/components/EditCreationFormular.spec.js @@ -1,5 +1,6 @@ import { mount } from '@vue/test-utils' import EditCreationFormular from './EditCreationFormular.vue' +import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup' const localVue = global.localVue @@ -16,8 +17,6 @@ const apolloMutateMock = jest.fn().mockResolvedValue({ }) const stateCommitMock = jest.fn() -const toastedErrorMock = jest.fn() -const toastedSuccessMock = jest.fn() const mocks = { $t: jest.fn((t) => t), @@ -37,10 +36,6 @@ const mocks = { }, commit: stateCommitMock, }, - $toasted: { - error: toastedErrorMock, - success: toastedSuccessMock, - }, } const now = new Date(Date.now()) @@ -142,7 +137,7 @@ describe('EditCreationFormular', () => { }) it('toasts a success message', () => { - expect(toastedSuccessMock).toBeCalledWith('creation_form.toasted_update') + expect(toastSuccessSpy).toBeCalledWith('creation_form.toasted_update') }) }) @@ -155,7 +150,7 @@ describe('EditCreationFormular', () => { }) it('toasts an error message', () => { - expect(toastedErrorMock).toBeCalledWith('Oh no!') + expect(toastErrorSpy).toBeCalledWith('Oh no!') }) }) }) diff --git a/admin/src/components/EditCreationFormular.vue b/admin/src/components/EditCreationFormular.vue index 650b00410..82b444154 100644 --- a/admin/src/components/EditCreationFormular.vue +++ b/admin/src/components/EditCreationFormular.vue @@ -132,7 +132,7 @@ export default { moderator: Number(result.data.updatePendingCreation.moderator), row: this.row, }) - this.$toasted.success( + this.toastSuccess( this.$t('creation_form.toasted_update', { value: this.value, email: this.item.email, @@ -144,7 +144,7 @@ export default { this.value = 0 }) .catch((error) => { - this.$toasted.error(error.message) + this.toastError(error.message) // das creation Formular reseten this.$refs.updateCreationForm.reset() // Den geschöpften Wert auf o setzen diff --git a/admin/src/components/Tables/SearchUserTable.spec.js b/admin/src/components/Tables/SearchUserTable.spec.js index 9e1ce5e52..eb87357cc 100644 --- a/admin/src/components/Tables/SearchUserTable.spec.js +++ b/admin/src/components/Tables/SearchUserTable.spec.js @@ -73,10 +73,6 @@ const mocks = { }, }, }, - $toasted: { - error: jest.fn(), - success: jest.fn(), - }, } describe('SearchUserTable', () => { diff --git a/admin/src/locales/de.json b/admin/src/locales/de.json index e5f4bf4ca..4c85db8db 100644 --- a/admin/src/locales/de.json +++ b/admin/src/locales/de.json @@ -5,6 +5,7 @@ "confirmed": "bestätigt", "creation": "Schöpfung", "creation_form": { + "creation_failed": "Ausstehende Schöpfung für {email} konnte nicht erzeugt werden.", "creation_for": "Aktives Grundeinkommen für", "enter_text": "Text eintragen", "form": "Schöpfungsformular", @@ -28,6 +29,7 @@ "delete_user": "Nutzer löschen", "details": "Details", "edit": "Bearbeiten", + "error": "Fehler", "e_mail": "E-Mail", "firstname": "Vorname", "gradido_admin_footer": "Gradido Akademie Adminkonsole", @@ -68,6 +70,7 @@ "remove_all": "alle Nutzer entfernen", "save": "Speichern", "status": "Status", + "success": "Erfolg", "text": "Text", "transaction": "Transaktion", "transactionlist": { diff --git a/admin/src/locales/en.json b/admin/src/locales/en.json index d772d638f..b7a7b5013 100644 --- a/admin/src/locales/en.json +++ b/admin/src/locales/en.json @@ -5,6 +5,7 @@ "confirmed": "confirmed", "creation": "Creation", "creation_form": { + "creation_failed": "Could not create pending creation for {email}", "creation_for": "Active Basic Income for", "enter_text": "Enter text", "form": "Creation form", @@ -28,6 +29,7 @@ "delete_user": "Delete user", "details": "Details", "edit": "Edit", + "error": "Error", "e_mail": "E-mail", "firstname": "Firstname", "gradido_admin_footer": "Gradido Academy Admin Console", @@ -68,6 +70,7 @@ "remove_all": "Remove all users", "save": "Speichern", "status": "Status", + "success": "Success", "text": "Text", "transaction": "Transaction", "transactionlist": { diff --git a/admin/src/main.js b/admin/src/main.js index f4a8dfb3c..f6c021a53 100644 --- a/admin/src/main.js +++ b/admin/src/main.js @@ -13,31 +13,24 @@ import i18n from './i18n' import VueApollo from 'vue-apollo' +import PortalVue from 'portal-vue' + import { BootstrapVue, IconsPlugin } from 'bootstrap-vue' import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css' -import Toasted from 'vue-toasted' +import { toasters } from './mixins/toaster' import { apolloProvider } from './plugins/apolloProvider' +Vue.use(PortalVue) Vue.use(BootstrapVue) Vue.use(IconsPlugin) Vue.use(VueApollo) -Vue.use(Toasted, { - position: 'top-center', - duration: 5000, - fullWidth: true, - action: { - text: 'x', - onClick: (e, toastObject) => { - toastObject.goAway(0) - }, - }, -}) +Vue.mixin(toasters) addNavigationGuards(router, store, apolloProvider.defaultClient, i18n) diff --git a/admin/src/mixins/toaster.js b/admin/src/mixins/toaster.js new file mode 100644 index 000000000..9f79b91e8 --- /dev/null +++ b/admin/src/mixins/toaster.js @@ -0,0 +1,30 @@ +export const toasters = { + methods: { + toastSuccess(message) { + this.toast(message, { + title: this.$t('success'), + variant: 'success', + }) + }, + toastError(message) { + this.toast(message, { + title: this.$t('error'), + variant: 'danger', + }) + }, + toast(message, options) { + // for unit tests, check that replace is present + if (message.replace) message = message.replace(/^GraphQL error: /, '') + this.$bvToast.toast(message, { + autoHideDelay: 5000, + appendToast: true, + solid: true, + toaster: 'b-toaster-top-right', + headerClass: 'gdd-toaster-title', + bodyClass: 'gdd-toaster-body', + toastClass: 'gdd-toaster', + ...options, + }) + }, + }, +} diff --git a/admin/src/pages/Creation.spec.js b/admin/src/pages/Creation.spec.js index 81d556e9b..f9a4ed506 100644 --- a/admin/src/pages/Creation.spec.js +++ b/admin/src/pages/Creation.spec.js @@ -1,5 +1,6 @@ import { mount } from '@vue/test-utils' import Creation from './Creation.vue' +import { toastErrorSpy } from '../../test/testSetup' const localVue = global.localVue @@ -29,18 +30,14 @@ const apolloQueryMock = jest.fn().mockResolvedValue({ }, }) -const toastErrorMock = jest.fn() const storeCommitMock = jest.fn() const mocks = { - $t: jest.fn((t) => t), + $t: jest.fn((t, options) => (options ? [t, options] : t)), $d: jest.fn((d) => d), $apollo: { query: apolloQueryMock, }, - $toasted: { - error: toastErrorMock, - }, $store: { commit: storeCommitMock, state: { @@ -236,6 +233,25 @@ describe('Creation', () => { }) }) + describe('failed creations', () => { + beforeEach(async () => { + await wrapper + .findComponent({ name: 'CreationFormular' }) + .vm.$emit('toast-failed-creations', ['bibi@bloxberg.de', 'benjamin@bluemchen.de']) + }) + + it('toasts two error messages', () => { + expect(toastErrorSpy).toBeCalledWith([ + 'creation_form.creation_failed', + { email: 'bibi@bloxberg.de' }, + ]) + expect(toastErrorSpy).toBeCalledWith([ + 'creation_form.creation_failed', + { email: 'benjamin@bluemchen.de' }, + ]) + }) + }) + describe('watchers', () => { beforeEach(() => { jest.clearAllMocks() @@ -298,7 +314,7 @@ describe('Creation', () => { }) it('toasts an error message', () => { - expect(toastErrorMock).toBeCalledWith('Ouch') + expect(toastErrorSpy).toBeCalledWith('Ouch') }) }) }) diff --git a/admin/src/pages/Creation.vue b/admin/src/pages/Creation.vue index 16678cad8..a5966ee68 100644 --- a/admin/src/pages/Creation.vue +++ b/admin/src/pages/Creation.vue @@ -56,6 +56,7 @@ :creation="creation" :items="itemsMassCreation" @remove-all-bookmark="removeAllBookmarks" + @toast-failed-creations="toastFailedCreations" /> @@ -118,7 +119,7 @@ export default { } }) .catch((error) => { - this.$toasted.error(error.message) + this.toastError(error.message) }) }, pushItem(selectedItem) { @@ -144,6 +145,11 @@ export default { this.$store.commit('setUserSelectedInMassCreation', []) this.getUsers() }, + toastFailedCreations(failedCreations) { + failedCreations.forEach((email) => + this.toastError(this.$t('creation_form.creation_failed', { email })), + ) + }, }, computed: { Searchfields() { diff --git a/admin/src/pages/CreationConfirm.spec.js b/admin/src/pages/CreationConfirm.spec.js index f0412678b..6df60378c 100644 --- a/admin/src/pages/CreationConfirm.spec.js +++ b/admin/src/pages/CreationConfirm.spec.js @@ -2,12 +2,11 @@ import { mount } from '@vue/test-utils' import CreationConfirm from './CreationConfirm.vue' import { deletePendingCreation } from '../graphql/deletePendingCreation' import { confirmPendingCreation } from '../graphql/confirmPendingCreation' +import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup' const localVue = global.localVue const storeCommitMock = jest.fn() -const toastedErrorMock = jest.fn() -const toastedSuccessMock = jest.fn() const apolloQueryMock = jest.fn().mockResolvedValue({ data: { getPendingCreations: [ @@ -47,10 +46,6 @@ const mocks = { query: apolloQueryMock, mutate: apolloMutateMock, }, - $toasted: { - error: toastedErrorMock, - success: toastedSuccessMock, - }, } describe('CreationConfirm', () => { @@ -101,7 +96,7 @@ describe('CreationConfirm', () => { }) it('toasts a success message', () => { - expect(toastedSuccessMock).toBeCalledWith('creation_form.toasted_delete') + expect(toastSuccessSpy).toBeCalledWith('creation_form.toasted_delete') }) }) @@ -112,7 +107,7 @@ describe('CreationConfirm', () => { }) it('toasts an error message', () => { - expect(toastedErrorMock).toBeCalledWith('Ouchhh!') + expect(toastErrorSpy).toBeCalledWith('Ouchhh!') }) }) @@ -158,7 +153,7 @@ describe('CreationConfirm', () => { }) it('toasts a success message', () => { - expect(toastedSuccessMock).toBeCalledWith('creation_form.toasted_created') + expect(toastSuccessSpy).toBeCalledWith('creation_form.toasted_created') }) it('has 1 item left in the table', () => { @@ -173,7 +168,7 @@ describe('CreationConfirm', () => { }) it('toasts an error message', () => { - expect(toastedErrorMock).toBeCalledWith('Ouchhh!') + expect(toastErrorSpy).toBeCalledWith('Ouchhh!') }) }) }) @@ -189,7 +184,7 @@ describe('CreationConfirm', () => { }) it('toast an error message', () => { - expect(toastedErrorMock).toBeCalledWith('Ouch!') + expect(toastErrorSpy).toBeCalledWith('Ouch!') }) }) }) diff --git a/admin/src/pages/CreationConfirm.vue b/admin/src/pages/CreationConfirm.vue index 54580c366..26928fb67 100644 --- a/admin/src/pages/CreationConfirm.vue +++ b/admin/src/pages/CreationConfirm.vue @@ -43,10 +43,10 @@ export default { }) .then((result) => { this.updatePendingCreations(item.id) - this.$toasted.success(this.$t('creation_form.toasted_delete')) + this.toastSuccess(this.$t('creation_form.toasted_delete')) }) .catch((error) => { - this.$toasted.error(error.message) + this.toastError(error.message) }) }, confirmCreation() { @@ -60,11 +60,11 @@ export default { .then((result) => { this.overlay = false this.updatePendingCreations(this.item.id) - this.$toasted.success(this.$t('creation_form.toasted_created')) + this.toastSuccess(this.$t('creation_form.toasted_created')) }) .catch((error) => { this.overlay = false - this.$toasted.error(error.message) + this.toastError(error.message) }) }, getPendingCreations() { @@ -79,7 +79,7 @@ export default { this.$store.commit('setOpenCreations', result.data.getPendingCreations.length) }) .catch((error) => { - this.$toasted.error(error.message) + this.toastError(error.message) }) }, updatePendingCreations(id) { diff --git a/admin/src/pages/UserSearch.spec.js b/admin/src/pages/UserSearch.spec.js index ab2bd722f..bd18965ac 100644 --- a/admin/src/pages/UserSearch.spec.js +++ b/admin/src/pages/UserSearch.spec.js @@ -1,5 +1,6 @@ import { mount } from '@vue/test-utils' import UserSearch from './UserSearch.vue' +import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup' const localVue = global.localVue @@ -15,6 +16,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({ email: 'bibi@bloxberg.de', creation: [200, 400, 600], emailChecked: true, + deletedAt: null, }, { userId: 2, @@ -23,6 +25,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({ email: 'benjamin@bluemchen.de', creation: [1000, 1000, 1000], emailChecked: true, + deletedAt: null, }, { userId: 3, @@ -31,6 +34,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({ email: 'peter@lustig.de', creation: [0, 0, 0], emailChecked: true, + deletedAt: null, }, { userId: 4, @@ -39,23 +43,19 @@ const apolloQueryMock = jest.fn().mockResolvedValue({ email: 'new@user.ch', creation: [1000, 1000, 1000], emailChecked: false, + deletedAt: null, }, ], }, }, }) -const toastErrorMock = jest.fn() - const mocks = { $t: jest.fn((t) => t), $d: jest.fn((d) => String(d)), $apollo: { query: apolloQueryMock, }, - $toasted: { - error: toastErrorMock, - }, } describe('UserSearch', () => { @@ -187,6 +187,21 @@ describe('UserSearch', () => { }) }) + describe('delete user', () => { + const now = new Date() + beforeEach(async () => { + wrapper.findComponent({ name: 'SearchUserTable' }).vm.$emit('updateDeletedAt', 4, now) + }) + + it('marks the user as deleted', () => { + expect(wrapper.vm.searchResult.find((obj) => obj.userId === 4).deletedAt).toEqual(now) + }) + + it('toasts a success message', () => { + expect(toastSuccessSpy).toBeCalledWith('user_deleted') + }) + }) + describe('apollo returns error', () => { beforeEach(() => { apolloQueryMock.mockRejectedValue({ @@ -196,7 +211,7 @@ describe('UserSearch', () => { }) it('toasts an error message', () => { - expect(toastErrorMock).toBeCalledWith('Ouch') + expect(toastErrorSpy).toBeCalledWith('Ouch') }) }) }) diff --git a/admin/src/pages/UserSearch.vue b/admin/src/pages/UserSearch.vue index edd64445c..ea49bf805 100644 --- a/admin/src/pages/UserSearch.vue +++ b/admin/src/pages/UserSearch.vue @@ -94,11 +94,12 @@ export default { this.searchResult = result.data.searchUsers.userList }) .catch((error) => { - this.$toasted.error(error.message) + this.toastError(error.message) }) }, updateDeletedAt(userId, deletedAt) { this.searchResult.find((obj) => obj.userId === userId).deletedAt = deletedAt + this.toastSuccess(this.$t('user_deleted')) }, }, watch: { diff --git a/admin/test/testSetup.js b/admin/test/testSetup.js index caaa3c19c..df3a025da 100644 --- a/admin/test/testSetup.js +++ b/admin/test/testSetup.js @@ -5,11 +5,18 @@ import { BootstrapVue, IconsPlugin } from 'bootstrap-vue' // without this async calls are not working import 'regenerator-runtime' +import { toasters } from '../src/mixins/toaster' + +export const toastErrorSpy = jest.spyOn(toasters.methods, 'toastError') +export const toastSuccessSpy = jest.spyOn(toasters.methods, 'toastSuccess') + global.localVue = createLocalVue() global.localVue.use(BootstrapVue) global.localVue.use(IconsPlugin) +global.localVue.mixin(toasters) + // throw errors for vue warnings to force the programmers to take care about warnings Vue.config.warnHandler = (w) => { throw new Error(w) diff --git a/admin/yarn.lock b/admin/yarn.lock index aff8f0d0b..4e5d587e5 100644 --- a/admin/yarn.lock +++ b/admin/yarn.lock @@ -12512,11 +12512,6 @@ vue-template-es2015-compiler@^1.6.0, vue-template-es2015-compiler@^1.9.0: resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== -vue-toasted@^1.1.28: - version "1.1.28" - resolved "https://registry.yarnpkg.com/vue-toasted/-/vue-toasted-1.1.28.tgz#dbabb83acc89f7a9e8765815e491d79f0dc65c26" - integrity sha512-UUzr5LX51UbbiROSGZ49GOgSzFxaMHK6L00JV8fir/CYNJCpIIvNZ5YmS4Qc8Y2+Z/4VVYRpeQL2UO0G800Raw== - vue@^2.6.11: version "2.6.14" resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"