From 322cf794a96e8cf0593e883f38ed8caff2341a71 Mon Sep 17 00:00:00 2001 From: mahula Date: Mon, 17 Jul 2023 12:15:41 +0200 Subject: [PATCH 01/28] add contribution by link information to locaes --- frontend/src/locales/de.json | 2 +- frontend/src/locales/en.json | 2 +- frontend/src/locales/es.json | 2 +- frontend/src/locales/nl.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index c20339a6c..9d3dee531 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -29,7 +29,7 @@ "myContributions": "Meine Beiträge", "noOpenContributionLinkText": "Zur Zeit gibt es keine per Link erzeugte Schöpfungen.", "openContributionLinks": "Per Link erzeugte Schöpfungen", - "openContributionLinkText": "Die Gemeinschaft „{name}“ unterstützt aktuell {count} per Link erzeugte Schöpfungen:", + "openContributionLinkText": "Für Startguthaben oder ähnliche Zwecke kann die Gemeinschaft so genannte Schöpfungs-Links erstellen. Sie lösen automatische Schöpfungen aus, die dem Benutzer gut geschrieben werden.\nDie Gemeinschaft „{name}“ unterstützt aktuell {count} per Link erzeugte Schöpfungen:", "startNewsButton": "Benutzernamen eintragen", "submitContribution": "Schreiben" }, diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 5ba7b6d80..4ef319374 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -29,7 +29,7 @@ "myContributions": "My contributions", "noOpenContributionLinkText": "Currently there are no link-generated creations.", "openContributionLinks": "Creations generated by link", - "openContributionLinkText": "The \"{name}\" community currently supports {count} link-generated creations:", + "openContributionLinkText": "For starting credits or similar purposes, the community can create so-called creation links. They trigger automatic creations that are credited to the user.\nThe \"{name}\" community currently supports {count} link-generated creations:", "startNewsButton": "Enter username", "submitContribution": "Contribute" }, diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index 9c702619f..439fcca67 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -28,7 +28,7 @@ "myContributions": "Mis contribuciones al bien común", "noOpenContributionLinkText": "Actualmente no hay creaciones generadas por enlaces.", "openContributionLinks": "Creaciones generadas por enlace", - "openContributionLinkText": "La comunidad \"{name}\" admite actualmente {count} creaciones generadas por enlaces:", + "openContributionLinkText": "Para créditos iniciales o fines similares, la comunidad puede crear los llamados enlaces de creación. Éstos activan creaciones automáticas que se acreditan al usuario.\nLa comunidad \"{name}\" admite actualmente {count} creaciones generadas por enlaces:", "other-communities": "Otras comunidades", "startNewsButton": "Introducir nombre de usuario", "statistic": "Estadísticas", diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index 2e6217841..31cd778a1 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -28,7 +28,7 @@ "myContributions": "Mijn bijdragen voor het algemeen belang", "noOpenContributionLinkText": "Er zijn momenteel geen link-gegenereerde creaties.", "openContributionLinks": "Creaties gegenereerd door link", - "openContributionLinkText": "De community \"{name}\" ondersteunt momenteel {count} link-gegenereerde creaties:", + "openContributionLinkText": "Voor startcredits of soortgelijke doeleinden kan de community zogenaamde creatielinks maken. Deze activeren automatische creaties die worden gecrediteerd aan de gebruiker.\nDe community \"{name}\" ondersteunt momenteel {count} link-gegenereerde creaties:", "other-communities": "Verdere gemeenschappen", "startNewsButton": "Gebruikersnaam invoeren", "statistic": "Statistieken", From bed2b86ea58de86452465ec924973612de98b325 Mon Sep 17 00:00:00 2001 From: elweyn Date: Sun, 23 Jul 2023 11:09:39 +0200 Subject: [PATCH 02/28] Test guards with roles array for verfiyLogin. --- admin/src/router/guards.test.js | 61 +++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/admin/src/router/guards.test.js b/admin/src/router/guards.test.js index da4dd5969..19a1881ef 100644 --- a/admin/src/router/guards.test.js +++ b/admin/src/router/guards.test.js @@ -5,7 +5,7 @@ const storeCommitMock = jest.fn() const apolloQueryMock = jest.fn().mockResolvedValue({ data: { verifyLogin: { - isAdmin: true, + roles: ['ADMIN'], language: 'de', }, }, @@ -52,7 +52,12 @@ describe('navigation guards', () => { }) it('commits the moderator to the store', () => { - expect(storeCommitMock).toBeCalledWith('moderator', { isAdmin: true, language: 'de' }) + expect(storeCommitMock).toBeCalledWith('moderator', { + roles: ['ADMIN'], + isAdmin: true, + isModerator: false, + language: 'de', + }) }) it('redirects to /', () => { @@ -60,12 +65,49 @@ describe('navigation guards', () => { }) }) - describe('with valid token and not as admin', () => { + describe('with valid token and as moderator', () => { + beforeEach(async () => { + jest.clearAllMocks() + apolloQueryMock.mockResolvedValue({ + data: { + verifyLogin: { + roles: ['MODERATOR'], + language: 'de', + }, + }, + }) + await navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next) + }) + + it('commits the token to the store', () => { + expect(storeCommitMock).toBeCalledWith('token', 'valid-token') + }) + + it.skip('sets the locale', () => { + expect(i18nLocaleMock).toBeCalledWith('de') + }) + + it('commits the moderator to the store', () => { + expect(storeCommitMock).toBeCalledWith('moderator', { + roles: ['MODERATOR'], + isAdmin: false, + isModerator: true, + language: 'de', + }) + }) + + it('redirects to /', () => { + expect(next).toBeCalledWith({ path: '/' }) + }) + }) + + describe('with valid token and no roles', () => { beforeEach(() => { apolloQueryMock.mockResolvedValue({ data: { verifyLogin: { - isAdmin: false, + roles: [], + language: 'de', }, }, }) @@ -128,17 +170,24 @@ describe('navigation guards', () => { expect(next).toBeCalledWith({ path: '/not-found' }) }) - it('redirects to not found with token in store and not moderator', () => { + it('redirects to not found with token in store and not admin or moderator', () => { store.state.token = 'valid token' navGuard({ path: '/' }, {}, next) expect(next).toBeCalledWith({ path: '/not-found' }) }) - it('does not redirect with token in store and as moderator', () => { + it('does not redirect with token in store and as admin', () => { store.state.token = 'valid token' store.state.moderator = { isAdmin: true } navGuard({ path: '/' }, {}, next) expect(next).toBeCalledWith() }) + + it('does not redirect with token in store and as moderator', () => { + store.state.token = 'valid token' + store.state.moderator = { isModerator: true } + navGuard({ path: '/' }, {}, next) + expect(next).toBeCalledWith() + }) }) }) From 1a4c42ea266a041c892089d036b7cbeceeffc45b Mon Sep 17 00:00:00 2001 From: elweyn Date: Sun, 23 Jul 2023 11:10:12 +0200 Subject: [PATCH 03/28] Add roles check to the guards verifyLogin. --- admin/src/router/guards.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/admin/src/router/guards.js b/admin/src/router/guards.js index dd61e8657..3780c6ee1 100644 --- a/admin/src/router/guards.js +++ b/admin/src/router/guards.js @@ -13,8 +13,10 @@ const addNavigationGuards = (router, store, apollo, i18n) => { }) .then((result) => { const moderator = result.data.verifyLogin - if (moderator.isAdmin) { + if (moderator.roles.includes('ADMIN', 0) || moderator.roles.includes('MODERATOR', 0)) { i18n.locale = moderator.language + moderator.isAdmin = moderator.roles.includes('ADMIN', 0) + moderator.isModerator = moderator.roles.includes('MODERATOR', 0) store.commit('moderator', moderator) next({ path: '/' }) } else { @@ -35,7 +37,7 @@ const addNavigationGuards = (router, store, apollo, i18n) => { !CONFIG.DEBUG_DISABLE_AUTH && // we did not disabled the auth module for debug purposes (!store.state.token || // we do not have a token !store.state.moderator || // no moderator set in store - !store.state.moderator.isAdmin) && // user is no admin + !(store.state.moderator.isAdmin || store.state.moderator.isModerator)) && // user is no admin to.path !== '/not-found' && // we are not on `not-found` to.path !== '/logout' // we are not on `logout` ) { From 9f00b4e73a267cb664faaab60f70bae9715a3978 Mon Sep 17 00:00:00 2001 From: elweyn Date: Sun, 23 Jul 2023 11:11:13 +0200 Subject: [PATCH 04/28] Get roles property of verifyLogin query. --- admin/src/graphql/verifyLogin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/src/graphql/verifyLogin.js b/admin/src/graphql/verifyLogin.js index ea08bb5b1..ce9e96ffc 100644 --- a/admin/src/graphql/verifyLogin.js +++ b/admin/src/graphql/verifyLogin.js @@ -5,7 +5,7 @@ export const verifyLogin = gql` verifyLogin { firstName lastName - isAdmin + roles id language } From 489a043aa73dd588d4c04e49618dd7eab6f577c9 Mon Sep 17 00:00:00 2001 From: elweyn Date: Sun, 23 Jul 2023 16:09:58 +0200 Subject: [PATCH 05/28] Refactor backend findUsers query to find user roles. --- backend/src/graphql/resolver/UserResolver.ts | 4 +- .../src/graphql/resolver/util/findUsers.ts | 103 +++++++++++------- 2 files changed, 63 insertions(+), 44 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 7b64b548e..6408b9dda 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -655,9 +655,7 @@ export class UserResolver { const clientTimezoneOffset = getClientTimezoneOffset(context) const userFields = ['id', 'firstName', 'lastName', 'emailId', 'emailContact', 'deletedAt'] const [users, count] = await findUsers( - userFields.map((fieldName) => { - return 'user.' + fieldName - }), + userFields, query, filters ?? null, currentPage, diff --git a/backend/src/graphql/resolver/util/findUsers.ts b/backend/src/graphql/resolver/util/findUsers.ts index d01afb904..a7d7d719c 100644 --- a/backend/src/graphql/resolver/util/findUsers.ts +++ b/backend/src/graphql/resolver/util/findUsers.ts @@ -1,10 +1,24 @@ -import { getConnection, Brackets, IsNull, Not } from '@dbTools/typeorm' +import { IsNull, Not, Like } from '@dbTools/typeorm' import { User as DbUser } from '@entity/User' import { SearchUsersFilters } from '@arg/SearchUsersFilters' import { Order } from '@enum/Order' -import { LogError } from '@/server/LogError' +function likeQuery(searchCriteria: string) { + return Like(`%${searchCriteria}%`) +} + +function emailCheckedQuery(filters: SearchUsersFilters) { + return filters.byActivated ?? undefined +} + +function deletedAtQuery(filters: SearchUsersFilters | null) { + return filters?.byDeleted !== undefined && filters?.byDeleted !== null + ? filters.byDeleted + ? Not(IsNull()) + : IsNull() + : undefined +} export const findUsers = async ( select: string[], @@ -14,44 +28,51 @@ export const findUsers = async ( pageSize: number, order = Order.ASC, ): Promise<[DbUser[], number]> => { - const queryRunner = getConnection().createQueryRunner() - try { - await queryRunner.connect() - const query = queryRunner.manager - .createQueryBuilder(DbUser, 'user') - .select(select) - .withDeleted() - .leftJoinAndSelect('user.emailContact', 'emailContact') - .where( - new Brackets((qb) => { - qb.where( - 'user.firstName like :name or user.lastName like :lastName or emailContact.email like :email', - { - name: `%${searchCriteria}%`, - lastName: `%${searchCriteria}%`, - email: `%${searchCriteria}%`, - }, - ) - }), - ) - if (filters) { - if (filters.byActivated !== null) { - query.andWhere('emailContact.emailChecked = :value', { value: filters.byActivated }) - } - - if (filters.byDeleted !== null) { - query.andWhere({ deletedAt: filters.byDeleted ? Not(IsNull()) : IsNull() }) - } - } - - return await query - .orderBy({ 'user.id': order }) - .take(pageSize) - .skip((currentPage - 1) * pageSize) - .getManyAndCount() - } catch (err) { - throw new LogError('Unable to search users', err) - } finally { - await queryRunner.release() + const where = [ + { + firstName: likeQuery(searchCriteria), + deletedAt: deletedAtQuery(filters), + emailContact: filters + ? { + emailChecked: emailCheckedQuery(filters), + } + : undefined, + }, + { + lastName: likeQuery(searchCriteria), + deletedAt: deletedAtQuery(filters), + emailContact: filters + ? { + emailChecked: emailCheckedQuery(filters), + } + : undefined, + }, + { + emailContact: { + // ...(filters ?? emailChecked: filters.byActivated) + emailChecked: filters ? emailCheckedQuery(filters) : undefined, + email: likeQuery(searchCriteria), + }, + deletedAt: deletedAtQuery(filters), + }, + ] + const selectFind = Object.fromEntries(select.map((item) => [item, true])) + const relations = ['emailContact', 'userRoles'] + const orderFind = { + id: order, } + const take = pageSize + const skip = (currentPage - 1) * pageSize + const withDeleted = true + + const [users, count] = await DbUser.findAndCount({ + where, + withDeleted, + select: selectFind, + relations, + order: orderFind, + take, + skip, + }) + return [users, count] } From 06033775e748cb2272172423d8878ad8acfe6028 Mon Sep 17 00:00:00 2001 From: elweyn Date: Sun, 23 Jul 2023 16:42:03 +0200 Subject: [PATCH 06/28] Change user query to get roles array & setUserRole mutation. --- admin/src/graphql/searchUsers.js | 3 ++- admin/src/graphql/setUserRole.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/admin/src/graphql/searchUsers.js b/admin/src/graphql/searchUsers.js index 15fb8da32..315b2e1aa 100644 --- a/admin/src/graphql/searchUsers.js +++ b/admin/src/graphql/searchUsers.js @@ -26,7 +26,8 @@ export const searchUsers = gql` hasElopage emailConfirmationSend deletedAt - isAdmin + # isAdmin + roles } } } diff --git a/admin/src/graphql/setUserRole.js b/admin/src/graphql/setUserRole.js index 8cdcab396..8df86da6b 100644 --- a/admin/src/graphql/setUserRole.js +++ b/admin/src/graphql/setUserRole.js @@ -1,7 +1,7 @@ import gql from 'graphql-tag' export const setUserRole = gql` - mutation ($userId: Int!, $isAdmin: Boolean!) { - setUserRole(userId: $userId, isAdmin: $isAdmin) + mutation ($userId: Int!, $role: RoleNames!) { + setUserRole(userId: $userId, role: $role) } ` From 037178bbe1c0d3dbed06306caff1301fe95495b8 Mon Sep 17 00:00:00 2001 From: elweyn Date: Sun, 23 Jul 2023 16:42:32 +0200 Subject: [PATCH 07/28] Add moderator object. --- admin/src/locales/de.json | 1 + admin/src/locales/en.json | 1 + 2 files changed, 2 insertions(+) diff --git a/admin/src/locales/de.json b/admin/src/locales/de.json index 1f5e41fbe..d4209ce83 100644 --- a/admin/src/locales/de.json +++ b/admin/src/locales/de.json @@ -209,6 +209,7 @@ "selectLabel": "Rolle:", "selectRoles": { "admin": "Administrator", + "moderator": "Moderator", "user": "einfacher Nutzer" }, "successfullyChangedTo": "Nutzer ist jetzt „{role}“.", diff --git a/admin/src/locales/en.json b/admin/src/locales/en.json index 99c438365..35aacfa69 100644 --- a/admin/src/locales/en.json +++ b/admin/src/locales/en.json @@ -209,6 +209,7 @@ "selectLabel": "Role:", "selectRoles": { "admin": "administrator", + "moderator": "moderator", "user": "usual user" }, "successfullyChangedTo": "User is now \"{role}\".", From 8934e9205b8d03811ba2b50353b37e70bb4e83ed Mon Sep 17 00:00:00 2001 From: elweyn Date: Sun, 23 Jul 2023 16:43:26 +0200 Subject: [PATCH 08/28] Change of roles added to CreationConfirm. --- admin/src/pages/CreationConfirm.spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/admin/src/pages/CreationConfirm.spec.js b/admin/src/pages/CreationConfirm.spec.js index 9e1ddb4a4..008ffb0bc 100644 --- a/admin/src/pages/CreationConfirm.spec.js +++ b/admin/src/pages/CreationConfirm.spec.js @@ -28,7 +28,9 @@ const mocks = { moderator: { firstName: 'Peter', lastName: 'Lustig', - isAdmin: '2022-08-30T07:41:31.000Z', + isAdmin: true, + isModerator: false, + roles: ['ADMIN'], id: 263, language: 'de', }, From adae41d2d0c74e18e3f7af807407c6e77b196e82 Mon Sep 17 00:00:00 2001 From: elweyn Date: Sun, 23 Jul 2023 17:35:45 +0200 Subject: [PATCH 09/28] Add new roles to mock objects. --- admin/src/components/Tables/SearchUserTable.spec.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/admin/src/components/Tables/SearchUserTable.spec.js b/admin/src/components/Tables/SearchUserTable.spec.js index 20eeb9dad..d38520327 100644 --- a/admin/src/components/Tables/SearchUserTable.spec.js +++ b/admin/src/components/Tables/SearchUserTable.spec.js @@ -15,6 +15,7 @@ const propsData = { email: 'bibi@bloxberg.de', creation: [200, 400, 600], emailChecked: true, + roles: [], }, { userId: 2, @@ -23,6 +24,7 @@ const propsData = { email: 'benjamin@bluemchen.de', creation: [1000, 1000, 1000], emailChecked: true, + roles: [], }, { userId: 3, @@ -31,6 +33,7 @@ const propsData = { email: 'peter@lustig.de', creation: [0, 0, 0], emailChecked: true, + roles: ['ADMIN'], }, { userId: 4, @@ -39,6 +42,7 @@ const propsData = { email: 'new@user.ch', creation: [1000, 1000, 1000], emailChecked: false, + roles: [], }, ], fields: [ From e0b84b52a013dcfceb78df0bd5ce77419edca829 Mon Sep 17 00:00:00 2001 From: elweyn Date: Sun, 23 Jul 2023 17:36:21 +0200 Subject: [PATCH 10/28] Add moderator role selection of roles. --- .../components/ChangeUserRoleFormular.spec.js | 350 +++++++++++++++++- .../src/components/ChangeUserRoleFormular.vue | 33 +- 2 files changed, 364 insertions(+), 19 deletions(-) diff --git a/admin/src/components/ChangeUserRoleFormular.spec.js b/admin/src/components/ChangeUserRoleFormular.spec.js index 381d2ce43..21df772e3 100644 --- a/admin/src/components/ChangeUserRoleFormular.spec.js +++ b/admin/src/components/ChangeUserRoleFormular.spec.js @@ -21,6 +21,7 @@ const mocks = { moderator: { id: 0, name: 'test moderator', + roles: ['ADMIN'], }, }, }, @@ -45,7 +46,7 @@ describe('ChangeUserRoleFormular', () => { propsData = { item: { userId: 1, - isAdmin: null, + roles: ['USER'], }, } wrapper = Wrapper() @@ -61,7 +62,7 @@ describe('ChangeUserRoleFormular', () => { propsData = { item: { userId: 0, - isAdmin: null, + roles: ['USER'], }, } wrapper = Wrapper() @@ -88,7 +89,7 @@ describe('ChangeUserRoleFormular', () => { propsData = { item: { userId: 1, - isAdmin: null, + roles: ['USER'], }, } wrapper = Wrapper() @@ -126,7 +127,7 @@ describe('ChangeUserRoleFormular', () => { propsData = { item: { userId: 1, - isAdmin: null, + roles: ['USER'], }, } wrapper = Wrapper() @@ -149,7 +150,7 @@ describe('ChangeUserRoleFormular', () => { }) }) - describe('new role', () => { + describe('new role "MODERATOR"', () => { beforeEach(() => { rolesToSelect.at(1).setSelected() }) @@ -181,7 +182,190 @@ describe('ChangeUserRoleFormular', () => { mutation: setUserRole, variables: { userId: 1, - isAdmin: true, + // isAdmin: true, + role: 'moderator', + }, + }), + ) + }) + + it('emits "updateIsAdmin" with role moderator', () => { + expect(wrapper.emitted('updateIsAdmin')).toEqual( + expect.arrayContaining([ + expect.arrayContaining([ + { + userId: 1, + // isAdmin: true, + role: 'moderator', + }, + ]), + ]), + ) + }) + + it('toasts success message', () => { + expect(toastSuccessSpy).toBeCalledWith('userRole.successfullyChangedTo') + }) + }) + + describe('confirm role change with error', () => { + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + apolloMutateMock.mockRejectedValue({ message: 'Oh no!' }) + await wrapper.find('button').trigger('click') + await wrapper.vm.$nextTick() + }) + + it('toasts an error message', () => { + expect(toastErrorSpy).toBeCalledWith('Oh no!') + }) + }) + }) + }) + + describe('new role "ADMIN"', () => { + beforeEach(() => { + rolesToSelect.at(2).setSelected() + }) + + it('has "change_user_role" button enabled', () => { + expect(wrapper.find('button.btn.btn-danger').exists()).toBe(true) + expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe( + false, + ) + }) + + describe('clicking the "change_user_role" button', () => { + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + spy.mockImplementation(() => Promise.resolve(true)) + await wrapper.find('button').trigger('click') + await wrapper.vm.$nextTick() + }) + + it('calls the modal', () => { + expect(wrapper.emitted('showModal')) + expect(spy).toHaveBeenCalled() + }) + + describe('confirm role change with success', () => { + it('calls the API', () => { + expect(apolloMutateMock).toBeCalledWith( + expect.objectContaining({ + mutation: setUserRole, + variables: { + userId: 1, + // isAdmin: true, + role: 'admin', + }, + }), + ) + }) + + it('emits "updateIsAdmin" with role moderator', () => { + expect(wrapper.emitted('updateIsAdmin')).toEqual( + expect.arrayContaining([ + expect.arrayContaining([ + { + userId: 1, + // isAdmin: true, + role: 'admin', + }, + ]), + ]), + ) + }) + + it('toasts success message', () => { + expect(toastSuccessSpy).toBeCalledWith('userRole.successfullyChangedTo') + }) + }) + + describe('confirm role change with error', () => { + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + apolloMutateMock.mockRejectedValue({ message: 'Oh no!' }) + await wrapper.find('button').trigger('click') + await wrapper.vm.$nextTick() + }) + + it('toasts an error message', () => { + expect(toastErrorSpy).toBeCalledWith('Oh no!') + }) + }) + }) + }) + }) + }) + + describe('user has role "moderator"', () => { + beforeEach(() => { + apolloMutateMock.mockResolvedValue({ + data: { + setUserRole: null, + }, + }) + propsData = { + item: { + userId: 1, + // isAdmin: new Date(), + roles: ['MODERATOR'], + }, + } + wrapper = Wrapper() + rolesToSelect = wrapper.find('select.role-select').findAll('option') + }) + + it('has selected option set to "moderator"', () => { + expect(wrapper.find('select.role-select').element.value).toBe('moderator') + }) + + describe('change select to', () => { + describe('same role', () => { + it('has "change_user_role" button disabled', () => { + expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe(true) + }) + + it('does not call the API', () => { + rolesToSelect.at(1).setSelected() + // TODO: Fix this + expect(apolloMutateMock).not.toHaveBeenCalled() + }) + }) + + describe('new role "USER"', () => { + beforeEach(() => { + rolesToSelect.at(0).setSelected() + }) + + it('has "change_user_role" button enabled', () => { + expect(wrapper.find('button.btn.btn-danger').exists()).toBe(true) + expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe( + false, + ) + }) + + describe('clicking the "change_user_role" button', () => { + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + spy.mockImplementation(() => Promise.resolve(true)) + await wrapper.find('button').trigger('click') + await wrapper.vm.$nextTick() + }) + + it('calls the modal', () => { + expect(wrapper.emitted('showModal')) + expect(spy).toHaveBeenCalled() + }) + + describe('confirm role change with success', () => { + it('calls the API', () => { + expect(apolloMutateMock).toBeCalledWith( + expect.objectContaining({ + mutation: setUserRole, + variables: { + userId: 1, + role: 'user', }, }), ) @@ -193,7 +377,78 @@ describe('ChangeUserRoleFormular', () => { expect.arrayContaining([ { userId: 1, - isAdmin: expect.any(Date), + role: 'user', + }, + ]), + ]), + ) + }) + + it('toasts success message', () => { + expect(toastSuccessSpy).toBeCalledWith('userRole.successfullyChangedTo') + }) + }) + + describe('confirm role change with error', () => { + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + apolloMutateMock.mockRejectedValue({ message: 'Oh no!' }) + await wrapper.find('button').trigger('click') + await wrapper.vm.$nextTick() + }) + + it('toasts an error message', () => { + expect(toastErrorSpy).toBeCalledWith('Oh no!') + }) + }) + }) + }) + + describe('new role "ADMIN"', () => { + beforeEach(() => { + rolesToSelect.at(2).setSelected() + }) + + it('has "change_user_role" button enabled', () => { + expect(wrapper.find('button.btn.btn-danger').exists()).toBe(true) + expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe( + false, + ) + }) + + describe('clicking the "change_user_role" button', () => { + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + spy.mockImplementation(() => Promise.resolve(true)) + await wrapper.find('button').trigger('click') + await wrapper.vm.$nextTick() + }) + + it('calls the modal', () => { + expect(wrapper.emitted('showModal')) + expect(spy).toHaveBeenCalled() + }) + + describe('confirm role change with success', () => { + it('calls the API', () => { + expect(apolloMutateMock).toBeCalledWith( + expect.objectContaining({ + mutation: setUserRole, + variables: { + userId: 1, + role: 'admin', + }, + }), + ) + }) + + it('emits "updateIsAdmin"', () => { + expect(wrapper.emitted('updateIsAdmin')).toEqual( + expect.arrayContaining([ + expect.arrayContaining([ + { + userId: 1, + role: 'admin', }, ]), ]), @@ -232,7 +487,8 @@ describe('ChangeUserRoleFormular', () => { propsData = { item: { userId: 1, - isAdmin: new Date(), + // isAdmin: new Date(), + roles: ['ADMIN'], }, } wrapper = Wrapper() @@ -251,11 +507,12 @@ describe('ChangeUserRoleFormular', () => { it('does not call the API', () => { rolesToSelect.at(1).setSelected() + // TODO: Fix this expect(apolloMutateMock).not.toHaveBeenCalled() }) }) - describe('new role', () => { + describe('new role "USER"', () => { beforeEach(() => { rolesToSelect.at(0).setSelected() }) @@ -287,7 +544,7 @@ describe('ChangeUserRoleFormular', () => { mutation: setUserRole, variables: { userId: 1, - isAdmin: false, + role: 'user', }, }), ) @@ -299,7 +556,78 @@ describe('ChangeUserRoleFormular', () => { expect.arrayContaining([ { userId: 1, - isAdmin: null, + role: 'user', + }, + ]), + ]), + ) + }) + + it('toasts success message', () => { + expect(toastSuccessSpy).toBeCalledWith('userRole.successfullyChangedTo') + }) + }) + + describe('confirm role change with error', () => { + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + apolloMutateMock.mockRejectedValue({ message: 'Oh no!' }) + await wrapper.find('button').trigger('click') + await wrapper.vm.$nextTick() + }) + + it('toasts an error message', () => { + expect(toastErrorSpy).toBeCalledWith('Oh no!') + }) + }) + }) + }) + + describe('new role "MODERATOR"', () => { + beforeEach(() => { + rolesToSelect.at(1).setSelected() + }) + + it('has "change_user_role" button enabled', () => { + expect(wrapper.find('button.btn.btn-danger').exists()).toBe(true) + expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe( + false, + ) + }) + + describe('clicking the "change_user_role" button', () => { + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + spy.mockImplementation(() => Promise.resolve(true)) + await wrapper.find('button').trigger('click') + await wrapper.vm.$nextTick() + }) + + it('calls the modal', () => { + expect(wrapper.emitted('showModal')) + expect(spy).toHaveBeenCalled() + }) + + describe('confirm role change with success', () => { + it('calls the API', () => { + expect(apolloMutateMock).toBeCalledWith( + expect.objectContaining({ + mutation: setUserRole, + variables: { + userId: 1, + role: 'moderator', + }, + }), + ) + }) + + it('emits "updateIsAdmin"', () => { + expect(wrapper.emitted('updateIsAdmin')).toEqual( + expect.arrayContaining([ + expect.arrayContaining([ + { + userId: 1, + role: 'moderator', }, ]), ]), diff --git a/admin/src/components/ChangeUserRoleFormular.vue b/admin/src/components/ChangeUserRoleFormular.vue index 677a12f56..06cb9d946 100644 --- a/admin/src/components/ChangeUserRoleFormular.vue +++ b/admin/src/components/ChangeUserRoleFormular.vue @@ -26,6 +26,7 @@ import { setUserRole } from '../graphql/setUserRole' const rolesValues = { admin: 'admin', + moderator: 'moderator', user: 'user', } @@ -39,15 +40,24 @@ export default { }, data() { return { - currentRole: this.item.isAdmin ? rolesValues.admin : rolesValues.user, - roleSelected: this.item.isAdmin ? rolesValues.admin : rolesValues.user, + // currentRole: this.item.isAdmin ? rolesValues.admin : rolesValues.user, + currentRole: this.newFunction(), + // roleSelected: this.item.isAdmin ? rolesValues.admin : rolesValues.user, + roleSelected: this.newFunction(), roles: [ { value: rolesValues.user, text: this.$t('userRole.selectRoles.user') }, + { value: rolesValues.moderator, text: this.$t('userRole.selectRoles.moderator') }, { value: rolesValues.admin, text: this.$t('userRole.selectRoles.admin') }, ], } }, methods: { + newFunction() { + let userRole = rolesValues.user + if (this.item.roles.includes('ADMIN', 0)) userRole = rolesValues.admin + else if (this.item.roles.includes('MODERATOR', 0)) userRole = rolesValues.moderator + return userRole + }, showModal() { this.$bvModal .msgBoxConfirm( @@ -77,25 +87,32 @@ export default { }) }, setUserRole(newRole, oldRole) { + let role + switch (newRole) { + case rolesValues.admin: + case rolesValues.moderator: + case rolesValues.user: + role = newRole + break + default: + role = 'USER' + } this.$apollo .mutate({ mutation: setUserRole, variables: { userId: this.item.userId, - isAdmin: newRole === rolesValues.admin, + role, }, }) .then((result) => { this.$emit('updateIsAdmin', { userId: this.item.userId, - isAdmin: result.data.setUserRole, + role, }) this.toastSuccess( this.$t('userRole.successfullyChangedTo', { - role: - result.data.setUserRole !== null - ? this.$t('userRole.selectRoles.admin') - : this.$t('userRole.selectRoles.user'), + role: role.text, }), ) }) From 557ffed4ef93cd098bc31c95bc93174218fa1b3a Mon Sep 17 00:00:00 2001 From: elweyn Date: Sun, 23 Jul 2023 18:57:44 +0200 Subject: [PATCH 11/28] Add flushPromises. --- admin/package.json | 1 + admin/yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/admin/package.json b/admin/package.json index 8350e146a..039ddd02f 100644 --- a/admin/package.json +++ b/admin/package.json @@ -36,6 +36,7 @@ "date-fns": "^2.29.3", "dotenv-webpack": "^7.0.3", "express": "^4.17.1", + "flush-promises": "^1.0.2", "graphql": "^15.6.1", "identity-obj-proxy": "^3.0.0", "jest": "26.6.3", diff --git a/admin/yarn.lock b/admin/yarn.lock index c270b80bf..6f9a999ce 100644 --- a/admin/yarn.lock +++ b/admin/yarn.lock @@ -6423,6 +6423,11 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== +flush-promises@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flush-promises/-/flush-promises-1.0.2.tgz#4948fd58f15281fed79cbafc86293d5bb09b2ced" + integrity sha512-G0sYfLQERwKz4+4iOZYQEZVpOt9zQrlItIxQAAYAWpfby3gbHrx0osCHz5RLl/XoXevXk0xoN4hDFky/VV9TrA== + flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" From 19ba385e46790876f9dbc84b6917b60e7d993ee6 Mon Sep 17 00:00:00 2001 From: elweyn Date: Sun, 23 Jul 2023 18:58:12 +0200 Subject: [PATCH 12/28] Test UserQuery to get jest line check. --- admin/src/components/UserQuery.spec.js | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 admin/src/components/UserQuery.spec.js diff --git a/admin/src/components/UserQuery.spec.js b/admin/src/components/UserQuery.spec.js new file mode 100644 index 000000000..2d6a6c554 --- /dev/null +++ b/admin/src/components/UserQuery.spec.js @@ -0,0 +1,50 @@ +import { mount } from '@vue/test-utils' +import UserQuery from './UserQuery' +import flushPromises from 'flush-promises' + +const localVue = global.localVue + +const propsData = { + userId: 42, +} +const mocks = { + $t: jest.fn((t) => t), +} +describe('TransactionLinkList', () => { + let wrapper + + const Wrapper = () => { + return mount(UserQuery, { mocks, localVue, propsData }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('has div .input-group', () => { + expect(wrapper.find('div .input-group').exists()).toBe(true) + }) + + it('has .test-input-criteria', () => { + expect(wrapper.find('input.test-input-criteria').exists()).toBe(true) + }) + + describe('has', () => { + beforeEach(async () => { + await wrapper.find('input.test-input-criteria').setValue('Test2') + await wrapper.find('input.test-input-criteria').trigger('blur') + await flushPromises() + await wrapper.vm.$nextTick() + }) + + it('emits input', () => { + expect(wrapper.emitted('input')).toBeTruthy() + }) + + it('emits input with value "Test2"', () => { + expect(wrapper.emitted('input')).toEqual([['Test2']]) + }) + }) + }) +}) From 03ae59627d46c77fc2430368b844e82a946d9883 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 24 Jul 2023 10:00:15 +0200 Subject: [PATCH 13/28] Fix emit and role as uppercase --- .../components/ChangeUserRoleFormular.spec.js | 30 ++++++++----------- .../src/components/ChangeUserRoleFormular.vue | 25 +++++++--------- .../components/Tables/SearchUserTable.spec.js | 5 ++-- .../src/components/Tables/SearchUserTable.vue | 4 +-- admin/src/pages/UserSearch.spec.js | 18 +++++++---- admin/src/pages/UserSearch.vue | 4 +-- 6 files changed, 42 insertions(+), 44 deletions(-) diff --git a/admin/src/components/ChangeUserRoleFormular.spec.js b/admin/src/components/ChangeUserRoleFormular.spec.js index 21df772e3..51a558d60 100644 --- a/admin/src/components/ChangeUserRoleFormular.spec.js +++ b/admin/src/components/ChangeUserRoleFormular.spec.js @@ -182,8 +182,7 @@ describe('ChangeUserRoleFormular', () => { mutation: setUserRole, variables: { userId: 1, - // isAdmin: true, - role: 'moderator', + role: 'MODERATOR', }, }), ) @@ -195,8 +194,7 @@ describe('ChangeUserRoleFormular', () => { expect.arrayContaining([ { userId: 1, - // isAdmin: true, - role: 'moderator', + role: 'MODERATOR', }, ]), ]), @@ -255,8 +253,7 @@ describe('ChangeUserRoleFormular', () => { mutation: setUserRole, variables: { userId: 1, - // isAdmin: true, - role: 'admin', + role: 'ADMIN', }, }), ) @@ -268,8 +265,7 @@ describe('ChangeUserRoleFormular', () => { expect.arrayContaining([ { userId: 1, - // isAdmin: true, - role: 'admin', + role: 'ADMIN', }, ]), ]), @@ -308,7 +304,6 @@ describe('ChangeUserRoleFormular', () => { propsData = { item: { userId: 1, - // isAdmin: new Date(), roles: ['MODERATOR'], }, } @@ -365,7 +360,7 @@ describe('ChangeUserRoleFormular', () => { mutation: setUserRole, variables: { userId: 1, - role: 'user', + role: 'USER', }, }), ) @@ -377,7 +372,7 @@ describe('ChangeUserRoleFormular', () => { expect.arrayContaining([ { userId: 1, - role: 'user', + role: 'USER', }, ]), ]), @@ -436,7 +431,7 @@ describe('ChangeUserRoleFormular', () => { mutation: setUserRole, variables: { userId: 1, - role: 'admin', + role: 'ADMIN', }, }), ) @@ -448,7 +443,7 @@ describe('ChangeUserRoleFormular', () => { expect.arrayContaining([ { userId: 1, - role: 'admin', + role: 'ADMIN', }, ]), ]), @@ -487,7 +482,6 @@ describe('ChangeUserRoleFormular', () => { propsData = { item: { userId: 1, - // isAdmin: new Date(), roles: ['ADMIN'], }, } @@ -544,7 +538,7 @@ describe('ChangeUserRoleFormular', () => { mutation: setUserRole, variables: { userId: 1, - role: 'user', + role: 'USER', }, }), ) @@ -556,7 +550,7 @@ describe('ChangeUserRoleFormular', () => { expect.arrayContaining([ { userId: 1, - role: 'user', + role: 'USER', }, ]), ]), @@ -615,7 +609,7 @@ describe('ChangeUserRoleFormular', () => { mutation: setUserRole, variables: { userId: 1, - role: 'moderator', + role: 'MODERATOR', }, }), ) @@ -627,7 +621,7 @@ describe('ChangeUserRoleFormular', () => { expect.arrayContaining([ { userId: 1, - role: 'moderator', + role: 'MODERATOR', }, ]), ]), diff --git a/admin/src/components/ChangeUserRoleFormular.vue b/admin/src/components/ChangeUserRoleFormular.vue index 06cb9d946..840410931 100644 --- a/admin/src/components/ChangeUserRoleFormular.vue +++ b/admin/src/components/ChangeUserRoleFormular.vue @@ -64,8 +64,10 @@ export default { this.$t('overlay.changeUserRole.question', { username: `${this.item.firstName} ${this.item.lastName}`, newRole: - this.roleSelected === 'admin' + this.roleSelected === rolesValues.admin ? this.$t('userRole.selectRoles.admin') + : this.roleSelected === rolesValues.moderator + ? this.$t('userRole.selectRoles.moderator') : this.$t('userRole.selectRoles.user'), }), { @@ -87,32 +89,27 @@ export default { }) }, setUserRole(newRole, oldRole) { - let role - switch (newRole) { - case rolesValues.admin: - case rolesValues.moderator: - case rolesValues.user: - role = newRole - break - default: - role = 'USER' - } + const role = this.roles.find((role) => { + return role.value === newRole + }) + const roleText = role.text + const roleValue = role.value.toUpperCase() this.$apollo .mutate({ mutation: setUserRole, variables: { userId: this.item.userId, - role, + role: role.value.toUpperCase(), }, }) .then((result) => { this.$emit('updateIsAdmin', { userId: this.item.userId, - role, + role: roleValue, }) this.toastSuccess( this.$t('userRole.successfullyChangedTo', { - role: role.text, + role: roleText, }), ) }) diff --git a/admin/src/components/Tables/SearchUserTable.spec.js b/admin/src/components/Tables/SearchUserTable.spec.js index d38520327..a8037422f 100644 --- a/admin/src/components/Tables/SearchUserTable.spec.js +++ b/admin/src/components/Tables/SearchUserTable.spec.js @@ -102,12 +102,13 @@ describe('SearchUserTable', () => { beforeEach(async () => { await wrapper.find('div.change-user-role-formular').vm.$emit('updateIsAdmin', { userId: 1, - isAdmin: new Date(), + // isAdmin: new Date(), + role: 'ADMIN', }) }) it('emits updateIsAdmin', () => { - expect(wrapper.emitted('updateIsAdmin')).toEqual([[1, expect.any(Date)]]) + expect(wrapper.emitted('updateIsAdmin')).toEqual([[1, 'ADMIN']]) }) }) diff --git a/admin/src/components/Tables/SearchUserTable.vue b/admin/src/components/Tables/SearchUserTable.vue index 25d807c83..32f917e0e 100644 --- a/admin/src/components/Tables/SearchUserTable.vue +++ b/admin/src/components/Tables/SearchUserTable.vue @@ -127,8 +127,8 @@ export default { updateUserData(rowItem, newCreation) { rowItem.creation = newCreation }, - updateIsAdmin({ userId, isAdmin }) { - this.$emit('updateIsAdmin', userId, isAdmin) + updateIsAdmin({ userId, role }) { + this.$emit('updateIsAdmin', userId, role) }, updateDeletedAt({ userId, deletedAt }) { this.$emit('updateDeletedAt', userId, deletedAt) diff --git a/admin/src/pages/UserSearch.spec.js b/admin/src/pages/UserSearch.spec.js index 7979ed6d0..a35486720 100644 --- a/admin/src/pages/UserSearch.spec.js +++ b/admin/src/pages/UserSearch.spec.js @@ -16,6 +16,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({ email: 'new@user.ch', creation: [1000, 1000, 1000], emailChecked: false, + roles: [], deletedAt: null, }, { @@ -24,6 +25,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({ lastName: 'Lustig', email: 'peter@lustig.de', creation: [0, 0, 0], + roles: ['ADMIN'], emailChecked: true, deletedAt: null, }, @@ -33,6 +35,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({ lastName: 'Blümchen', email: 'benjamin@bluemchen.de', creation: [1000, 1000, 1000], + roles: ['USER'], emailChecked: true, deletedAt: new Date(), }, @@ -42,6 +45,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({ lastName: 'Bloxberg', email: 'bibi@bloxberg.de', creation: [200, 400, 600], + roles: ['USER'], emailChecked: true, deletedAt: null, }, @@ -212,10 +216,10 @@ describe('UserSearch', () => { it('updates user role to admin', async () => { await wrapper .findComponent({ name: 'SearchUserTable' }) - .vm.$emit('updateIsAdmin', userId, new Date()) - expect(wrapper.vm.searchResult.find((obj) => obj.userId === userId).isAdmin).toEqual( - expect.any(Date), - ) + .vm.$emit('updateIsAdmin', userId, 'ADMIN') + expect(wrapper.vm.searchResult.find((obj) => obj.userId === userId).roles).toEqual([ + 'ADMIN', + ]) }) }) @@ -223,8 +227,10 @@ describe('UserSearch', () => { it('updates user role to usual user', async () => { await wrapper .findComponent({ name: 'SearchUserTable' }) - .vm.$emit('updateIsAdmin', userId, null) - expect(wrapper.vm.searchResult.find((obj) => obj.userId === userId).isAdmin).toEqual(null) + .vm.$emit('updateIsAdmin', userId, 'USER') + expect(wrapper.vm.searchResult.find((obj) => obj.userId === userId).roles).toEqual([ + 'USER', + ]) }) }) }) diff --git a/admin/src/pages/UserSearch.vue b/admin/src/pages/UserSearch.vue index 95b9a6833..4f6c74720 100644 --- a/admin/src/pages/UserSearch.vue +++ b/admin/src/pages/UserSearch.vue @@ -101,8 +101,8 @@ export default { this.toastError(error.message) }) }, - updateIsAdmin(userId, isAdmin) { - this.searchResult.find((obj) => obj.userId === userId).isAdmin = isAdmin + updateIsAdmin(userId, role) { + this.searchResult.find((obj) => obj.userId === userId).roles = [role] }, updateDeletedAt(userId, deletedAt) { this.searchResult.find((obj) => obj.userId === userId).deletedAt = deletedAt From fdf40a6ec79ecd88964a2d20cd19afcbd51d4704 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 24 Jul 2023 14:46:55 +0200 Subject: [PATCH 14/28] remove flush promises --- admin/package.json | 1 - admin/src/components/UserQuery.spec.js | 7 ++----- admin/yarn.lock | 5 ----- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/admin/package.json b/admin/package.json index 039ddd02f..8350e146a 100644 --- a/admin/package.json +++ b/admin/package.json @@ -36,7 +36,6 @@ "date-fns": "^2.29.3", "dotenv-webpack": "^7.0.3", "express": "^4.17.1", - "flush-promises": "^1.0.2", "graphql": "^15.6.1", "identity-obj-proxy": "^3.0.0", "jest": "26.6.3", diff --git a/admin/src/components/UserQuery.spec.js b/admin/src/components/UserQuery.spec.js index 2d6a6c554..92fdd2413 100644 --- a/admin/src/components/UserQuery.spec.js +++ b/admin/src/components/UserQuery.spec.js @@ -1,6 +1,5 @@ import { mount } from '@vue/test-utils' import UserQuery from './UserQuery' -import flushPromises from 'flush-promises' const localVue = global.localVue @@ -30,12 +29,10 @@ describe('TransactionLinkList', () => { expect(wrapper.find('input.test-input-criteria').exists()).toBe(true) }) - describe('has', () => { + describe('set value', () => { beforeEach(async () => { + jest.clearAllMocks() await wrapper.find('input.test-input-criteria').setValue('Test2') - await wrapper.find('input.test-input-criteria').trigger('blur') - await flushPromises() - await wrapper.vm.$nextTick() }) it('emits input', () => { diff --git a/admin/yarn.lock b/admin/yarn.lock index 6f9a999ce..c270b80bf 100644 --- a/admin/yarn.lock +++ b/admin/yarn.lock @@ -6423,11 +6423,6 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== -flush-promises@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/flush-promises/-/flush-promises-1.0.2.tgz#4948fd58f15281fed79cbafc86293d5bb09b2ced" - integrity sha512-G0sYfLQERwKz4+4iOZYQEZVpOt9zQrlItIxQAAYAWpfby3gbHrx0osCHz5RLl/XoXevXk0xoN4hDFky/VV9TrA== - flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" From d82a994eebc93d72c2aa831db6254b50bf0a5a78 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 24 Jul 2023 14:58:43 +0200 Subject: [PATCH 15/28] remove isAdmin from guards, simplify logic --- admin/src/graphql/searchUsers.js | 1 - admin/src/router/guards.js | 6 ++---- admin/src/router/guards.test.js | 11 ++++------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/admin/src/graphql/searchUsers.js b/admin/src/graphql/searchUsers.js index 315b2e1aa..9f82452c3 100644 --- a/admin/src/graphql/searchUsers.js +++ b/admin/src/graphql/searchUsers.js @@ -26,7 +26,6 @@ export const searchUsers = gql` hasElopage emailConfirmationSend deletedAt - # isAdmin roles } } diff --git a/admin/src/router/guards.js b/admin/src/router/guards.js index 3780c6ee1..79e381478 100644 --- a/admin/src/router/guards.js +++ b/admin/src/router/guards.js @@ -13,10 +13,8 @@ const addNavigationGuards = (router, store, apollo, i18n) => { }) .then((result) => { const moderator = result.data.verifyLogin - if (moderator.roles.includes('ADMIN', 0) || moderator.roles.includes('MODERATOR', 0)) { + if (moderator.roles?.length) { i18n.locale = moderator.language - moderator.isAdmin = moderator.roles.includes('ADMIN', 0) - moderator.isModerator = moderator.roles.includes('MODERATOR', 0) store.commit('moderator', moderator) next({ path: '/' }) } else { @@ -37,7 +35,7 @@ const addNavigationGuards = (router, store, apollo, i18n) => { !CONFIG.DEBUG_DISABLE_AUTH && // we did not disabled the auth module for debug purposes (!store.state.token || // we do not have a token !store.state.moderator || // no moderator set in store - !(store.state.moderator.isAdmin || store.state.moderator.isModerator)) && // user is no admin + !store.state.moderator.roles.length) && // user is no admin to.path !== '/not-found' && // we are not on `not-found` to.path !== '/logout' // we are not on `logout` ) { diff --git a/admin/src/router/guards.test.js b/admin/src/router/guards.test.js index 19a1881ef..6d51fda6d 100644 --- a/admin/src/router/guards.test.js +++ b/admin/src/router/guards.test.js @@ -54,8 +54,6 @@ describe('navigation guards', () => { it('commits the moderator to the store', () => { expect(storeCommitMock).toBeCalledWith('moderator', { roles: ['ADMIN'], - isAdmin: true, - isModerator: false, language: 'de', }) }) @@ -90,8 +88,6 @@ describe('navigation guards', () => { it('commits the moderator to the store', () => { expect(storeCommitMock).toBeCalledWith('moderator', { roles: ['MODERATOR'], - isAdmin: false, - isModerator: true, language: 'de', }) }) @@ -103,6 +99,7 @@ describe('navigation guards', () => { describe('with valid token and no roles', () => { beforeEach(() => { + jest.clearAllMocks() apolloQueryMock.mockResolvedValue({ data: { verifyLogin: { @@ -119,7 +116,7 @@ describe('navigation guards', () => { }) it('does not commit the moderator to the store', () => { - expect(storeCommitMock).not.toBeCalledWith('moderator', { isAdmin: false }) + expect(storeCommitMock).not.toBeCalledWith('moderator') }) it('redirects to /not-found', async () => { @@ -178,14 +175,14 @@ describe('navigation guards', () => { it('does not redirect with token in store and as admin', () => { store.state.token = 'valid token' - store.state.moderator = { isAdmin: true } + store.state.moderator = { roles: ['ADMIN'] } navGuard({ path: '/' }, {}, next) expect(next).toBeCalledWith() }) it('does not redirect with token in store and as moderator', () => { store.state.token = 'valid token' - store.state.moderator = { isModerator: true } + store.state.moderator = { roles: ['MODERATOR'] } navGuard({ path: '/' }, {}, next) expect(next).toBeCalledWith() }) From a1d12c3c682b8250608302c1f804dbc23f87f7b1 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 24 Jul 2023 15:40:05 +0200 Subject: [PATCH 16/28] do not pass USER role by update role. Simplify logic --- .../components/ChangeUserRoleFormular.spec.js | 54 +++++++++---------- .../src/components/ChangeUserRoleFormular.vue | 43 ++++++++------- .../components/Tables/SearchUserTable.spec.js | 7 ++- .../src/components/Tables/SearchUserTable.vue | 6 +-- admin/src/pages/CreationConfirm.spec.js | 2 - admin/src/pages/UserSearch.spec.js | 12 ++--- admin/src/pages/UserSearch.vue | 6 +-- 7 files changed, 62 insertions(+), 68 deletions(-) diff --git a/admin/src/components/ChangeUserRoleFormular.spec.js b/admin/src/components/ChangeUserRoleFormular.spec.js index 51a558d60..42f859733 100644 --- a/admin/src/components/ChangeUserRoleFormular.spec.js +++ b/admin/src/components/ChangeUserRoleFormular.spec.js @@ -46,7 +46,7 @@ describe('ChangeUserRoleFormular', () => { propsData = { item: { userId: 1, - roles: ['USER'], + roles: [], }, } wrapper = Wrapper() @@ -62,7 +62,7 @@ describe('ChangeUserRoleFormular', () => { propsData = { item: { userId: 0, - roles: ['USER'], + roles: ['ADMIN'], }, } wrapper = Wrapper() @@ -89,7 +89,7 @@ describe('ChangeUserRoleFormular', () => { propsData = { item: { userId: 1, - roles: ['USER'], + roles: [], }, } wrapper = Wrapper() @@ -121,7 +121,7 @@ describe('ChangeUserRoleFormular', () => { beforeEach(() => { apolloMutateMock.mockResolvedValue({ data: { - setUserRole: new Date(), + setUserRole: 'ADMIN', }, }) propsData = { @@ -135,7 +135,7 @@ describe('ChangeUserRoleFormular', () => { }) it('has selected option set to "usual user"', () => { - expect(wrapper.find('select.role-select').element.value).toBe('user') + expect(wrapper.find('select.role-select').element.value).toBe('USER') }) describe('change select to', () => { @@ -188,13 +188,13 @@ describe('ChangeUserRoleFormular', () => { ) }) - it('emits "updateIsAdmin" with role moderator', () => { - expect(wrapper.emitted('updateIsAdmin')).toEqual( + it('emits "updateRoles" with role moderator', () => { + expect(wrapper.emitted('updateRoles')).toEqual( expect.arrayContaining([ expect.arrayContaining([ { userId: 1, - role: 'MODERATOR', + roles: ['MODERATOR'], }, ]), ]), @@ -259,13 +259,13 @@ describe('ChangeUserRoleFormular', () => { ) }) - it('emits "updateIsAdmin" with role moderator', () => { - expect(wrapper.emitted('updateIsAdmin')).toEqual( + it('emits "updateRoles" with role moderator', () => { + expect(wrapper.emitted('updateRoles')).toEqual( expect.arrayContaining([ expect.arrayContaining([ { userId: 1, - role: 'ADMIN', + roles: ['ADMIN'], }, ]), ]), @@ -296,6 +296,7 @@ describe('ChangeUserRoleFormular', () => { describe('user has role "moderator"', () => { beforeEach(() => { + jest.clearAllMocks() apolloMutateMock.mockResolvedValue({ data: { setUserRole: null, @@ -311,8 +312,8 @@ describe('ChangeUserRoleFormular', () => { rolesToSelect = wrapper.find('select.role-select').findAll('option') }) - it('has selected option set to "moderator"', () => { - expect(wrapper.find('select.role-select').element.value).toBe('moderator') + it('has selected option set to "MODERATOR"', () => { + expect(wrapper.find('select.role-select').element.value).toBe('MODERATOR') }) describe('change select to', () => { @@ -323,7 +324,6 @@ describe('ChangeUserRoleFormular', () => { it('does not call the API', () => { rolesToSelect.at(1).setSelected() - // TODO: Fix this expect(apolloMutateMock).not.toHaveBeenCalled() }) }) @@ -366,13 +366,13 @@ describe('ChangeUserRoleFormular', () => { ) }) - it('emits "updateIsAdmin"', () => { - expect(wrapper.emitted('updateIsAdmin')).toEqual( + it('emits "updateRoles"', () => { + expect(wrapper.emitted('updateRoles')).toEqual( expect.arrayContaining([ expect.arrayContaining([ { userId: 1, - role: 'USER', + roles: [], }, ]), ]), @@ -437,13 +437,13 @@ describe('ChangeUserRoleFormular', () => { ) }) - it('emits "updateIsAdmin"', () => { - expect(wrapper.emitted('updateIsAdmin')).toEqual( + it('emits "updateRoles"', () => { + expect(wrapper.emitted('updateRoles')).toEqual( expect.arrayContaining([ expect.arrayContaining([ { userId: 1, - role: 'ADMIN', + roles: ['ADMIN'], }, ]), ]), @@ -490,7 +490,7 @@ describe('ChangeUserRoleFormular', () => { }) it('has selected option set to "admin"', () => { - expect(wrapper.find('select.role-select').element.value).toBe('admin') + expect(wrapper.find('select.role-select').element.value).toBe('ADMIN') }) describe('change select to', () => { @@ -544,13 +544,13 @@ describe('ChangeUserRoleFormular', () => { ) }) - it('emits "updateIsAdmin"', () => { - expect(wrapper.emitted('updateIsAdmin')).toEqual( + it('emits "updateRoles"', () => { + expect(wrapper.emitted('updateRoles')).toEqual( expect.arrayContaining([ expect.arrayContaining([ { userId: 1, - role: 'USER', + roles: [], }, ]), ]), @@ -615,13 +615,13 @@ describe('ChangeUserRoleFormular', () => { ) }) - it('emits "updateIsAdmin"', () => { - expect(wrapper.emitted('updateIsAdmin')).toEqual( + it('emits "updateRoles"', () => { + expect(wrapper.emitted('updateRoles')).toEqual( expect.arrayContaining([ expect.arrayContaining([ { userId: 1, - role: 'MODERATOR', + roles: ['MODERATOR'], }, ]), ]), diff --git a/admin/src/components/ChangeUserRoleFormular.vue b/admin/src/components/ChangeUserRoleFormular.vue index 840410931..7f048d0e2 100644 --- a/admin/src/components/ChangeUserRoleFormular.vue +++ b/admin/src/components/ChangeUserRoleFormular.vue @@ -1,7 +1,10 @@