diff --git a/backend/src/apis/humhub/ExportUsers.ts b/backend/src/apis/humhub/ExportUsers.ts index 24beab223..e2aa3e0cf 100644 --- a/backend/src/apis/humhub/ExportUsers.ts +++ b/backend/src/apis/humhub/ExportUsers.ts @@ -26,7 +26,7 @@ function getUsersPage(page: number, limit: number): Promise<[User[], number]> { /** * @param client - * @returns user map indices with email + * @returns user map indices with username */ async function loadUsersFromHumHub(client: HumHubClient): Promise> { const start = new Date().getTime() @@ -42,7 +42,7 @@ async function loadUsersFromHumHub(client: HumHubClient): Promise { // deleted users have empty emails if (user.account.email) { - humhubUsers.set(user.account.email.trim(), user) + humhubUsers.set(user.account.username, user) } else { skippedUsersCount++ } @@ -52,6 +52,7 @@ async function loadUsersFromHumHub(client: HumHubClient): Promise[] = [] - users.forEach((user: User) => promises.push(syncUser(user, humhubUsers))) - const executedActions = await Promise.all(promises) - executedActions.forEach((executedAction: ExecutedHumhubAction) => { - executedHumhubActionsCount[executedAction as number]++ - }) - // using process.stdout.write here so that carriage-return is working analog to c - // printf("\rchecked user: %d/%d", dbUserCount, totalUsers); - process.stdout.write(`checked user: ${dbUserCount}/${totalUsers}\r`) + try { + const [users, totalUsers] = await getUsersPage(page, USER_BULK_SIZE) + dbUserCount += users.length + userCount = users.length + page++ + const promises: Promise[] = [] + users.forEach((user: User) => promises.push(syncUser(user, humhubUsers))) + const executedActions = await Promise.all(promises) + executedActions.forEach((executedAction: ExecutedHumhubAction) => { + executedHumhubActionsCount[executedAction as number]++ + }) + // using process.stdout.write here so that carriage-return is working analog to c + // printf("\rchecked user: %d/%d", dbUserCount, totalUsers); + process.stdout.write(`checked user: ${dbUserCount}/${totalUsers}\r`) + } catch (e) { + process.stdout.write('\n') + throw e + } } while (userCount === USER_BULK_SIZE) + process.stdout.write('\n') await con.destroy() const elapsed = new Date().getTime() - start @@ -114,12 +121,13 @@ async function main() { updatedCount: executedHumhubActionsCount[ExecutedHumhubAction.UPDATE], skippedCount: executedHumhubActionsCount[ExecutedHumhubAction.SKIP], deletedCount: executedHumhubActionsCount[ExecutedHumhubAction.DELETE], + validationErrorCount: executedHumhubActionsCount[ExecutedHumhubAction.VALIDATION_ERROR], }) } main().catch((e) => { // eslint-disable-next-line no-console - console.error(e) + console.error(JSON.stringify(e, null, 2)) // eslint-disable-next-line n/no-process-exit process.exit(1) }) diff --git a/backend/src/apis/humhub/__mocks__/syncUser.ts b/backend/src/apis/humhub/__mocks__/syncUser.ts index bc29336c2..3e4b832f5 100644 --- a/backend/src/apis/humhub/__mocks__/syncUser.ts +++ b/backend/src/apis/humhub/__mocks__/syncUser.ts @@ -2,6 +2,7 @@ import { User } from 'database' import { isHumhubUserIdenticalToDbUser } from '@/apis/humhub/compareHumhubUserDbUser' import { GetUser } from '@/apis/humhub/model/GetUser' +import { PostUser } from '@/apis/humhub/model/PostUser' export enum ExecutedHumhubAction { UPDATE, @@ -26,7 +27,8 @@ export async function syncUser( user: User, humhubUsers: Map, ): Promise { - const humhubUser = humhubUsers.get(user.emailContact.email.trim()) + const postUser = new PostUser(user) + const humhubUser = humhubUsers.get(postUser.account.username) if (humhubUser) { if (!user.humhubAllowed) { return Promise.resolve(ExecutedHumhubAction.DELETE) diff --git a/backend/src/apis/humhub/compareHumhubUserDbUser.test.ts b/backend/src/apis/humhub/compareHumhubUserDbUser.test.ts index cc636d17a..a3d6c7044 100644 --- a/backend/src/apis/humhub/compareHumhubUserDbUser.test.ts +++ b/backend/src/apis/humhub/compareHumhubUserDbUser.test.ts @@ -1,4 +1,7 @@ /* eslint-disable prettier/prettier */ +// eslint-disable-next-line import/no-unassigned-import +import 'reflect-metadata' +import { PublishNameType } from '@/graphql/enum/PublishNameType' import { communityDbUser } from '@/util/communityUser' import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser' @@ -13,6 +16,7 @@ describe('isHumhubUserIdenticalToDbUser', () => { defaultUser.alias = 'alias' defaultUser.emailContact.email = 'email@gmail.com' defaultUser.language = 'en' + defaultUser.gradidoID = 'gradidoID' }) it('Should return true because humhubUser was created from entity user', () => { @@ -21,6 +25,20 @@ describe('isHumhubUserIdenticalToDbUser', () => { expect(result).toBe(true) }) + it('Should return false, because last name differ because of publish name type', () => { + const humhubUser = new GetUser(defaultUser, 1) + defaultUser.humhubPublishName = PublishNameType.PUBLISH_NAME_FIRST + const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser) + expect(result).toBe(false) + }) + + it('Should return true, even if alias is empty', () => { + defaultUser.alias = '' + const humhubUser = new GetUser(defaultUser, 1) + const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser) + expect(result).toBe(true) + }) + it('Should return false because first name differ', () => { const humhubUser = new GetUser(defaultUser, 1) humhubUser.profile.firstname = 'changed first name' diff --git a/backend/src/apis/humhub/syncUser.test.ts b/backend/src/apis/humhub/syncUser.test.ts index 857e3c911..21e76ee9e 100644 --- a/backend/src/apis/humhub/syncUser.test.ts +++ b/backend/src/apis/humhub/syncUser.test.ts @@ -27,7 +27,8 @@ describe('syncUser function', () => { it('When humhubUser exists and user.humhubAllowed is false, should return DELETE action', async () => { const humhubUsers = new Map() - humhubUsers.set(defaultUser.emailContact.email, new GetUser(defaultUser, 1)) + const humhubUser = new GetUser(defaultUser, 1) + humhubUsers.set(humhubUser.account.username, humhubUser) defaultUser.humhubAllowed = false const result = await syncUser(defaultUser, humhubUsers) @@ -38,8 +39,8 @@ describe('syncUser function', () => { it('When humhubUser exists and user.humhubAllowed is true and there are changes in user data, should return UPDATE action', async () => { const humhubUsers = new Map() const humhubUser = new GetUser(defaultUser, 1) + humhubUsers.set(humhubUser.account.username, humhubUser) humhubUser.account.username = 'test username' - humhubUsers.set(defaultUser.emailContact.email, humhubUser) defaultUser.humhubAllowed = true const result = await syncUser(defaultUser, humhubUsers) @@ -50,7 +51,7 @@ describe('syncUser function', () => { it('When humhubUser exists and user.humhubAllowed is true and there are no changes in user data, should return SKIP action', async () => { const humhubUsers = new Map() const humhubUser = new GetUser(defaultUser, 1) - humhubUsers.set(defaultUser.emailContact.email, humhubUser) + humhubUsers.set(humhubUser.account.username, humhubUser) defaultUser.humhubAllowed = true const result = await syncUser(defaultUser, humhubUsers) diff --git a/backend/src/apis/humhub/syncUser.ts b/backend/src/apis/humhub/syncUser.ts index e2b52db00..9a5711906 100644 --- a/backend/src/apis/humhub/syncUser.ts +++ b/backend/src/apis/humhub/syncUser.ts @@ -1,6 +1,7 @@ import { User } from 'database' import { LogError } from '@/server/LogError' +import { backendLogger as logger } from '@/server/logger' import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser' import { HumHubClient } from './HumHubClient' @@ -12,7 +13,22 @@ export enum ExecutedHumhubAction { CREATE, SKIP, DELETE, + VALIDATION_ERROR, } + +// todo: replace with full validation (schema) +function isValid(postUser: PostUser, userId: number): boolean { + if (postUser.profile.firstname.length > 20) { + logger.error('firstname too long for humhub, for user with id:', userId) + return false + } + if (postUser.profile.lastname.length > 20) { + logger.error('lastname too long for humhub, for user with id:', userId) + return false + } + return true +} + /** * Trigger action according to conditions * | User exist on humhub | export to humhub allowed | changes in user data | ACTION @@ -21,9 +37,8 @@ export enum ExecutedHumhubAction { * | true | true | false | SKIP * | false | false | ignored | SKIP * | false | true | ignored | CREATE - * @param user - * @param humHubClient - * @param humhubUsers + * @param user user entity + * @param humhubUsers user map indices with username * @returns */ export async function syncUser( @@ -31,7 +46,10 @@ export async function syncUser( humhubUsers: Map, ): Promise { const postUser = new PostUser(user) - const humhubUser = humhubUsers.get(user.emailContact.email.trim()) + if (!isValid(postUser, user.id)) { + return ExecutedHumhubAction.VALIDATION_ERROR + } + const humhubUser = humhubUsers.get(postUser.account.username) const humHubClient = HumHubClient.getInstance() if (!humHubClient) { throw new LogError('Error creating humhub client') diff --git a/backend/src/data/PublishName.logic.ts b/backend/src/data/PublishName.logic.ts index 31b0ca17a..69696086d 100644 --- a/backend/src/data/PublishName.logic.ts +++ b/backend/src/data/PublishName.logic.ts @@ -35,12 +35,16 @@ export class PublishNameLogic { * @returns user.firstName for PUBLISH_NAME_FIRST, PUBLISH_NAME_FIRST_INITIAL or PUBLISH_NAME_FULL */ public getFirstName(publishNameType: PublishNameType): string { + let firstName = '' + if (this.user && typeof this.user.firstName === 'string') { + firstName = this.user.firstName + } return [ PublishNameType.PUBLISH_NAME_FIRST, PublishNameType.PUBLISH_NAME_FIRST_INITIAL, PublishNameType.PUBLISH_NAME_FULL, ].includes(publishNameType) - ? this.user.firstName + ? firstName.slice(0, 20) : '' } @@ -51,10 +55,14 @@ export class PublishNameLogic { * first initial from user.lastName for PUBLISH_NAME_FIRST_INITIAL */ public getLastName(publishNameType: PublishNameType): string { + let lastName = '' + if (this.user && typeof this.user.lastName === 'string') { + lastName = this.user.lastName + } return publishNameType === PublishNameType.PUBLISH_NAME_FULL - ? this.user.lastName - : publishNameType === PublishNameType.PUBLISH_NAME_FIRST_INITIAL - ? this.user.lastName.charAt(0) + ? lastName.slice(0, 20) + : publishNameType === PublishNameType.PUBLISH_NAME_FIRST_INITIAL && lastName.length > 0 + ? lastName.charAt(0) : '' } @@ -85,7 +93,7 @@ export class PublishNameLogic { ? this.getUsernameFromAlias() : this.isUsernameFromInitials(publishNameType) ? this.getUsernameFromInitials() - : (this.getFirstName(publishNameType) + ' ' + this.getLastName(publishNameType)).trim() + : `(${this.getFirstName(publishNameType)} ${this.getLastName(publishNameType)}`.trim() } public getUsernameFromInitials(): string { diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index e89fbf80a..b393020fa 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -39,6 +39,7 @@ import { UpdateUserInfosArgs } from '@arg/UpdateUserInfosArgs' import { OptInType } from '@enum/OptInType' import { Order } from '@enum/Order' import { PasswordEncryptionType } from '@enum/PasswordEncryptionType' +import { PublishNameType } from '@enum/PublishNameType' import { UserContactType } from '@enum/UserContactType' import { SearchAdminUsersResult } from '@model/AdminUser' // import { Location } from '@model/Location' @@ -56,6 +57,7 @@ import { subscribe } from '@/apis/KlicktippController' import { encode } from '@/auth/JWT' import { RIGHTS } from '@/auth/RIGHTS' import { CONFIG } from '@/config' +import { PublishNameLogic } from '@/data/PublishName.logic' import { sendAccountActivationEmail, sendAccountMultiRegistrationEmail, @@ -248,12 +250,12 @@ export class UserResolver { try { const result = await humhubUserPromise user.humhubAllowed = result?.result?.account.status === 1 - if (user.humhubAllowed) { + if (user.humhubAllowed && result?.result?.account?.username) { let spaceId = null if (projectBranding) { spaceId = projectBranding.spaceId } - void syncHumhub(null, dbUser, spaceId) + await syncHumhub(null, dbUser, result.result.account.username, spaceId) } } catch (e) { logger.error("couldn't reach out to humhub, disable for now", e) @@ -450,7 +452,11 @@ export class UserResolver { if (projectBranding) { spaceId = projectBranding.spaceId } - void syncHumhub(null, dbUser, spaceId) + try { + await syncHumhub(null, dbUser, dbUser.gradidoID, spaceId) + } catch (e) { + logger.error("createUser: couldn't reach out to humhub, disable for now", e) + } } if (redeemCode) { @@ -661,6 +667,10 @@ export class UserResolver { ) const user = getUser(context) const updateUserInGMS = compareGmsRelevantUserSettings(user, updateUserInfosArgs) + const publishNameLogic = new PublishNameLogic(user) + const oldHumhubUsername = publishNameLogic.getUserIdentifier( + user.humhubPublishName as PublishNameType, + ) // try { if (firstName) { @@ -766,7 +776,7 @@ export class UserResolver { } try { if (CONFIG.HUMHUB_ACTIVE) { - await syncHumhub(updateUserInfosArgs, user) + await syncHumhub(updateUserInfosArgs, user, oldHumhubUsername) } } catch (e) { logger.error('error sync user with humhub', e) @@ -841,7 +851,7 @@ export class UserResolver { } const humhubUserAccount = new HumhubAccount(dbUser) const autoLoginUrlPromise = humhubClient.createAutoLoginUrl(humhubUserAccount.username, project) - const humhubUser = await syncHumhub(null, dbUser) + const humhubUser = await syncHumhub(null, dbUser, humhubUserAccount.username) if (!humhubUser) { throw new LogError("user don't exist (any longer) on humhub and couldn't be created") } diff --git a/backend/src/graphql/resolver/util/syncHumhub.test.ts b/backend/src/graphql/resolver/util/syncHumhub.test.ts index 1f2153d40..594f5fdfd 100644 --- a/backend/src/graphql/resolver/util/syncHumhub.test.ts +++ b/backend/src/graphql/resolver/util/syncHumhub.test.ts @@ -31,7 +31,7 @@ describe('syncHumhub', () => { }) it('Should not sync if no relevant changes', async () => { - await syncHumhub(mockUpdateUserInfosArg, new User()) + await syncHumhub(mockUpdateUserInfosArg, new User(), 'username') expect(HumHubClient.getInstance).not.toBeCalled() // language logging from some other place expect(logger.debug).toBeCalledTimes(5) @@ -41,7 +41,7 @@ describe('syncHumhub', () => { it('Should retrieve user from humhub and sync if relevant changes', async () => { mockUpdateUserInfosArg.firstName = 'New' // Relevant changes mockUser.firstName = 'New' - await syncHumhub(mockUpdateUserInfosArg, mockUser) + await syncHumhub(mockUpdateUserInfosArg, mockUser, 'username') expect(logger.debug).toHaveBeenCalledTimes(8) // Four language logging calls, two debug calls in function, one for not syncing expect(logger.info).toHaveBeenLastCalledWith('finished sync user with humhub', { localId: mockUser.id, diff --git a/backend/src/graphql/resolver/util/syncHumhub.ts b/backend/src/graphql/resolver/util/syncHumhub.ts index 57896399e..46dfcfcff 100644 --- a/backend/src/graphql/resolver/util/syncHumhub.ts +++ b/backend/src/graphql/resolver/util/syncHumhub.ts @@ -2,10 +2,9 @@ import { User } from 'database' import { HumHubClient } from '@/apis/humhub/HumHubClient' import { GetUser } from '@/apis/humhub/model/GetUser' +import { PostUser } from '@/apis/humhub/model/PostUser' import { ExecutedHumhubAction, syncUser } from '@/apis/humhub/syncUser' -import { PublishNameLogic } from '@/data/PublishName.logic' import { UpdateUserInfosArgs } from '@/graphql/arg/UpdateUserInfosArgs' -import { PublishNameType } from '@/graphql/enum/PublishNameType' import { backendLogger as logger } from '@/server/logger' /** @@ -17,6 +16,7 @@ import { backendLogger as logger } from '@/server/logger' export async function syncHumhub( updateUserInfosArg: UpdateUserInfosArgs | null, user: User, + oldHumhubUsername: string, spaceId?: number | null, ): Promise { // check for humhub relevant changes @@ -38,15 +38,13 @@ export async function syncHumhub( return } logger.debug('retrieve user from humhub') - const userNameLogic = new PublishNameLogic(user) - const username = userNameLogic.getUserIdentifier(user.humhubPublishName as PublishNameType) - let humhubUser = await humhubClient.userByUsername(username) + let humhubUser = await humhubClient.userByUsername(oldHumhubUsername) if (!humhubUser) { humhubUser = await humhubClient.userByEmail(user.emailContact.email) } const humhubUsers = new Map() if (humhubUser) { - humhubUsers.set(user.emailContact.email, humhubUser) + humhubUsers.set(humhubUser.account.username, humhubUser) } logger.debug('update user at humhub') const result = await syncUser(user, humhubUsers) @@ -63,7 +61,8 @@ export async function syncHumhub( logger.debug(`user added to space ${spaceId}`) } if (result !== ExecutedHumhubAction.SKIP) { - return await humhubClient.userByUsername(username) + const getUser = new PostUser(user) + return await humhubClient.userByUsername(getUser.account.username) } return humhubUser } diff --git a/backend/test/testSetup.ts b/backend/test/testSetup.ts index 8cf4ff084..74021f0a3 100644 --- a/backend/test/testSetup.ts +++ b/backend/test/testSetup.ts @@ -6,6 +6,8 @@ import { backendLogger as logger } from '@/server/logger' CONFIG.EMAIL = true CONFIG.EMAIL_TEST_MODUS = false +CONFIG.HUMHUB_ACTIVE = false +CONFIG.GMS_ACTIVE = false jest.setTimeout(1000000) diff --git a/frontend/src/components/Contributions/ContributionList.vue b/frontend/src/components/Contributions/ContributionList.vue index 889bc8fd2..e7fbd750d 100644 --- a/frontend/src/components/Contributions/ContributionList.vue +++ b/frontend/src/components/Contributions/ContributionList.vue @@ -67,6 +67,7 @@ const props = defineProps({ }) const emit = defineEmits([ + 'close-all-open-collapse', 'update-list-contributions', 'update-contribution-form', 'delete-contribution', diff --git a/frontend/src/components/Contributions/ContributionListItem.spec.js b/frontend/src/components/Contributions/ContributionListItem.spec.js index eb5fb1247..300076060 100644 --- a/frontend/src/components/Contributions/ContributionListItem.spec.js +++ b/frontend/src/components/Contributions/ContributionListItem.spec.js @@ -14,6 +14,7 @@ vi.mock('@vue/apollo-composable', () => ({ onResult: vi.fn(), onError: vi.fn(), load: vi.fn(), + refetch: vi.fn(), })), useMutation: vi.fn(() => ({ mutate: vi.fn(), diff --git a/frontend/src/components/Contributions/ContributionListItem.vue b/frontend/src/components/Contributions/ContributionListItem.vue index a08af2e4c..2b0e2b0a1 100644 --- a/frontend/src/components/Contributions/ContributionListItem.vue +++ b/frontend/src/components/Contributions/ContributionListItem.vue @@ -246,7 +246,7 @@ function deleteContribution(item) { } } -const { onResult, onError, load } = useLazyQuery(listContributionMessages, { +const { onResult, onError, load, refetch } = useLazyQuery(listContributionMessages, { contributionId: props.contributionId, }) @@ -254,9 +254,15 @@ function getListContributionMessages(closeCollapse = true) { if (closeCollapse) { emit('close-all-open-collapse') } - load(listContributionMessages, { + const variables = { contributionId: props.contributionId, - }) + } + // load works only once and return false on second call + if (!load(listContributionMessages, variables)) { + // update list data every time getListContributionMessages is called + // because it could be added new messages + refetch(variables) + } } onResult((resultValue) => { diff --git a/frontend/src/components/GddTransactionList.spec.js b/frontend/src/components/GddTransactionList.spec.js index c865c782d..416010301 100644 --- a/frontend/src/components/GddTransactionList.spec.js +++ b/frontend/src/components/GddTransactionList.spec.js @@ -95,6 +95,9 @@ describe('GddTransactionList', () => { }) describe('timestamp property', () => { + beforeEach(async () => { + await wrapper.setProps({ timestamp: new Date().getTime() }) + }) it('emits update-transactions when timestamp changes', async () => { await wrapper.setProps({ timestamp: 0 }) expect(wrapper.emitted('update-transactions')).toBeTruthy() diff --git a/frontend/src/components/GddTransactionList.vue b/frontend/src/components/GddTransactionList.vue index 8396f8d52..b4d188374 100644 --- a/frontend/src/components/GddTransactionList.vue +++ b/frontend/src/components/GddTransactionList.vue @@ -101,7 +101,7 @@ export default { this.updateTransactions() }, timestamp: { - immediate: true, + immediate: false, handler: 'updateTransactions', }, }, diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 65f7e9418..edd034344 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -256,8 +256,8 @@ export const communityStatistics = gql` ` export const searchAdminUsers = gql` - query { - searchAdminUsers { + query ($pageSize: Int = 25, $currentPage: Int = 1, $order: Order = ASC) { + searchAdminUsers(pageSize: $pageSize, currentPage: $currentPage, order: $order) { userCount userList { firstName diff --git a/frontend/src/graphql/transactions.graphql b/frontend/src/graphql/transactions.graphql new file mode 100644 index 000000000..eff0abbb5 --- /dev/null +++ b/frontend/src/graphql/transactions.graphql @@ -0,0 +1,56 @@ +fragment balanceFields on Balance { + balance + balanceGDT + count + linkCount +} + +fragment transactionFields on Transaction { + id + typeId + amount + balance + previousBalance + balanceDate + memo + linkedUser { + firstName + lastName + communityUuid + communityName + gradidoID + alias + } + decay { + decay + start + end + duration + } + linkId +} + +query transactionsQuery($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC) { + transactionList(currentPage: $currentPage, pageSize: $pageSize, order: $order) { + balance { + ...balanceFields + } + transactions { + ...transactionFields + } + } +} + +query transactionsUserCountQuery($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC) { + transactionList(currentPage: $currentPage, pageSize: $pageSize, order: $order) { + balance { + ...balanceFields + } + transactions { + ...transactionFields + } + } + communityStatistics { + totalUsers + } +} \ No newline at end of file diff --git a/frontend/src/layouts/DashboardLayout.spec.js b/frontend/src/layouts/DashboardLayout.spec.js index 0906793ff..cf6680d0b 100644 --- a/frontend/src/layouts/DashboardLayout.spec.js +++ b/frontend/src/layouts/DashboardLayout.spec.js @@ -5,6 +5,7 @@ import DashboardLayout from './DashboardLayout' import { createStore } from 'vuex' import { createRouter, createWebHistory } from 'vue-router' import routes from '@/routes/routes' +import { useQuery } from '@vue/apollo-composable' const toastErrorSpy = vi.fn() @@ -14,18 +15,35 @@ vi.mock('@/composables/useToast', () => ({ }), })) -const mockQueryFn = vi.fn() const mockRefetchFn = vi.fn() const mockMutateFn = vi.fn() +let onErrorHandler +let onResultHandler const mockQueryResult = ref(null) +const loading = ref(false) vi.mock('@vue/apollo-composable', () => ({ - useLazyQuery: vi.fn(() => ({ - load: mockQueryFn, + useQuery: vi.fn(() => ({ refetch: mockRefetchFn, result: mockQueryResult, - onResult: vi.fn(), - onError: vi.fn(), + onResult: (handler) => { + onResultHandler = handler + }, + onError: (handler) => { + onErrorHandler = handler + }, + loading, + })), + useLazyQuery: vi.fn(() => ({ + refetch: mockRefetchFn, + result: mockQueryResult, + onResult: (handler) => { + onResultHandler = handler + }, + onError: (handler) => { + onErrorHandler = handler + }, + loading, })), useMutation: vi.fn(() => ({ mutate: mockMutateFn, @@ -103,17 +121,6 @@ describe('DashboardLayout', () => { beforeEach(() => { vi.useFakeTimers() - mockQueryFn.mockResolvedValue({ - communityStatistics: { - totalUsers: 3113, - activeUsers: 1057, - deletedUsers: 35, - totalGradidoCreated: '4083774.05000000000000000000', - totalGradidoDecayed: '-1062639.13634129622923372197', - totalGradidoAvailable: '2513565.869444365732411569', - totalGradidoUnbookedDecayed: '-500474.6738366222166261272', - }, - }) wrapper = createWrapper() }) @@ -135,31 +142,32 @@ describe('DashboardLayout', () => { describe('after a timeout', () => { beforeEach(async () => { vi.advanceTimersByTime(1500) + loading.value = false await nextTick() }) describe('update transactions', () => { beforeEach(async () => { - mockQueryResult.value = { - transactionList: { - balance: { - balanceGDT: '100', - count: 4, - linkCount: 8, - balance: '1450', + onResultHandler({ + data: { + transactionList: { + balance: { + balanceGDT: '100', + count: 4, + linkCount: 8, + balance: '1450', + }, + transactions: ['transaction1', 'transaction2', 'transaction3', 'transaction4'], }, - transactions: ['transaction1', 'transaction2', 'transaction3', 'transaction4'], }, - } - - mockQueryFn.mockResolvedValue(mockQueryResult.value) + }) await wrapper.vm.updateTransactions({ currentPage: 2, pageSize: 5 }) await nextTick() // Ensure all promises are resolved }) it('load call to the API', () => { - expect(mockQueryFn).toHaveBeenCalled() + expect(useQuery).toHaveBeenCalled() }) it('updates balance', () => { @@ -190,7 +198,7 @@ describe('DashboardLayout', () => { describe('update transactions returns error', () => { beforeEach(async () => { - mockQueryFn.mockRejectedValue(new Error('Ouch!')) + wrapper.vm.skeleton = false await wrapper .findComponent({ ref: 'router-view' }) .vm.$emit('update-transactions', { currentPage: 2, pageSize: 5 }) @@ -202,6 +210,7 @@ describe('DashboardLayout', () => { }) it('toasts the error message', () => { + onErrorHandler({ message: 'Ouch!' }) expect(toastErrorSpy).toHaveBeenCalledWith('Ouch!') }) }) diff --git a/frontend/src/layouts/DashboardLayout.vue b/frontend/src/layouts/DashboardLayout.vue index 450e3c3fa..07b856428 100755 --- a/frontend/src/layouts/DashboardLayout.vue +++ b/frontend/src/layouts/DashboardLayout.vue @@ -187,11 +187,10 @@