diff --git a/admin-panel/app/[lang]/users/[authId]/page.test.tsx b/admin-panel/app/[lang]/users/[authId]/page.test.tsx new file mode 100644 index 0000000..1c86ca1 --- /dev/null +++ b/admin-panel/app/[lang]/users/[authId]/page.test.tsx @@ -0,0 +1,211 @@ +import { + fireEvent, screen, +} from '@testing-library/react' +import { + describe, it, expect, vi, beforeEach, Mock, +} from 'vitest' +import Page from 'app/[lang]/users/[authId]/page' +import { render } from 'vitest.setup' +import { + useDeleteApiV1UsersByAuthIdConsentedAppsAndAppIdMutation, + useDeleteApiV1UsersByAuthIdEmailMfaMutation, + useDeleteApiV1UsersByAuthIdLockedIpsMutation, + useDeleteApiV1UsersByAuthIdMutation, + useDeleteApiV1UsersByAuthIdOtpMfaMutation, + useDeleteApiV1UsersByAuthIdSmsMfaMutation, + useGetApiV1RolesQuery, + useGetApiV1UsersByAuthIdConsentedAppsQuery, + useGetApiV1UsersByAuthIdLockedIpsQuery, + useGetApiV1UsersByAuthIdQuery, + usePostApiV1UsersByAuthIdEmailMfaMutation, + usePostApiV1UsersByAuthIdOtpMfaMutation, + usePostApiV1UsersByAuthIdSmsMfaMutation, + usePostApiV1UsersByAuthIdVerifyEmailMutation, + usePutApiV1UsersByAuthIdMutation, +} from 'services/auth/api' +import { users } from 'tests/userMock' +import { roles } from 'tests/roleMock' + +const mockNav = { + authId: '3ed71b1e-fd0c-444b-b653-7e78731d4865', + push: vi.fn(), +} + +vi.mock( + 'next/navigation', + () => ({ + useParams: vi.fn(() => ({ authId: mockNav.authId })), + useRouter: vi.fn(() => ({ push: mockNav.push })), + }), +) + +vi.mock( + 'services/auth/api', + () => ({ + useDeleteApiV1UsersByAuthIdConsentedAppsAndAppIdMutation: vi.fn(), + useDeleteApiV1UsersByAuthIdEmailMfaMutation: vi.fn(), + useDeleteApiV1UsersByAuthIdLockedIpsMutation: vi.fn(), + useDeleteApiV1UsersByAuthIdMutation: vi.fn(), + useDeleteApiV1UsersByAuthIdOtpMfaMutation: vi.fn(), + useDeleteApiV1UsersByAuthIdSmsMfaMutation: vi.fn(), + useGetApiV1RolesQuery: vi.fn(), + useGetApiV1UsersByAuthIdConsentedAppsQuery: vi.fn(), + useGetApiV1UsersByAuthIdLockedIpsQuery: vi.fn(), + useGetApiV1UsersByAuthIdQuery: vi.fn(), + usePostApiV1UsersByAuthIdEmailMfaMutation: vi.fn(), + usePostApiV1UsersByAuthIdOtpMfaMutation: vi.fn(), + usePostApiV1UsersByAuthIdSmsMfaMutation: vi.fn(), + usePostApiV1UsersByAuthIdVerifyEmailMutation: vi.fn(), + usePutApiV1UsersByAuthIdMutation: vi.fn(), + }), +) + +vi.mock( + 'signals', + () => ({ + configSignal: { + value: { + ENABLE_NAMES: true, + SUPPORTED_LOCALES: ['en', 'fr'], + ACCOUNT_LOCKOUT_THRESHOLD: 2, + }, + subscribe: () => () => {}, + }, + userInfoSignal: { + value: { authId: '3ed71b1e-fd0c-444b-b653-7e78731d4865' }, + subscribe: () => () => {}, + }, + errorSignal: { + value: '', + subscribe: () => () => {}, + }, + }), +) + +const mockUpdate = vi.fn() +const mockDelete = vi.fn() +const mockDeleteConsent = vi.fn() +const mockDeleteIps = vi.fn() +const mockResendVerifyEmail = vi.fn() +const mockEnrollEmailMfa = vi.fn() +const mockEnrollOtpMfa = vi.fn() +const mockEnrollSmsMfa = vi.fn() +const mockUnenrollEmailMfa = vi.fn() +const mockUnenrollSmsMfa = vi.fn() +const mockUnenrollOtpMfa = vi.fn() + +describe( + 'user', + () => { + beforeEach(() => { + (useGetApiV1UsersByAuthIdQuery as Mock).mockReturnValue({ data: { user: users[0] } }); + (useGetApiV1RolesQuery as Mock).mockReturnValue({ data: { roles } }); + (useGetApiV1UsersByAuthIdConsentedAppsQuery as Mock).mockReturnValue({ + data: { + consentedApps: [{ + appId: 1, appName: 'test app', + }], + }, + }); + (useGetApiV1UsersByAuthIdLockedIpsQuery as Mock).mockReturnValue({ data: { lockedIPs: ['1.1.1.1'] } }); + (usePutApiV1UsersByAuthIdMutation as Mock).mockReturnValue([mockUpdate, { isLoading: false }]); + (useDeleteApiV1UsersByAuthIdMutation as Mock).mockReturnValue([mockDelete, { isLoading: false }]); + (useDeleteApiV1UsersByAuthIdConsentedAppsAndAppIdMutation as Mock) + .mockReturnValue([mockDeleteConsent, { isLoading: false }]); + (useDeleteApiV1UsersByAuthIdLockedIpsMutation as Mock).mockReturnValue([mockDeleteIps, { isLoading: false }]); + (usePostApiV1UsersByAuthIdVerifyEmailMutation as Mock) + .mockReturnValue([mockResendVerifyEmail, { isLoading: false }]); + (usePostApiV1UsersByAuthIdEmailMfaMutation as Mock).mockReturnValue([mockEnrollEmailMfa, { isLoading: false }]); + (usePostApiV1UsersByAuthIdSmsMfaMutation as Mock).mockReturnValue([mockEnrollSmsMfa, { isLoading: false }]); + (usePostApiV1UsersByAuthIdOtpMfaMutation as Mock).mockReturnValue([mockEnrollOtpMfa, { isLoading: false }]); + (useDeleteApiV1UsersByAuthIdEmailMfaMutation as Mock) + .mockReturnValue([mockUnenrollEmailMfa, { isLoading: false }]); + (useDeleteApiV1UsersByAuthIdOtpMfaMutation as Mock).mockReturnValue([mockUnenrollOtpMfa, { isLoading: false }]); + (useDeleteApiV1UsersByAuthIdSmsMfaMutation as Mock).mockReturnValue([mockUnenrollSmsMfa, { isLoading: false }]) + }) + + it( + 'render user', + async () => { + render() + + const localeOptions = screen.queryAllByTestId('localeOption') + expect(localeOptions.length).toBe(2) + + const lockedIpBadges = screen.queryAllByTestId('lockedIpBadge') + expect(lockedIpBadges.length).toBe(1) + expect(lockedIpBadges[0]?.innerHTML).toContain('1.1.1.1') + + const roleInputs = screen.queryAllByTestId('roleInput') as HTMLInputElement[] + expect(roleInputs.length).toBe(2) + expect(roleInputs[0]?.checked).toBeFalsy() + expect(roleInputs[1]?.checked).toBeFalsy() + + const firstNameInput = screen.queryByTestId('firstNameInput') as HTMLInputElement + expect(firstNameInput?.value).toBe(users[0].firstName) + + const lastNameInput = screen.queryByTestId('lastNameInput') as HTMLInputElement + expect(lastNameInput?.value).toBe(users[0].lastName) + }, + ) + + it( + 'update user', + async () => { + render() + + const localeSelect = screen.queryByTestId('localeSelect') as HTMLSelectElement + fireEvent.change( + localeSelect, + { target: { value: 'fr' } }, + ) + + const roleInputs = screen.queryAllByTestId('roleInput') as HTMLInputElement[] + fireEvent.click(roleInputs[0]) + + const firstNameInput = screen.queryByTestId('firstNameInput') as HTMLInputElement + fireEvent.change( + firstNameInput, + { target: { value: 'new firstname' } }, + ) + + const lastNameInput = screen.queryByTestId('lastNameInput') as HTMLInputElement + fireEvent.change( + lastNameInput, + { target: { value: 'new lastname' } }, + ) + + const saveBtn = screen.queryByTestId('saveButton') as HTMLButtonElement + expect(saveBtn?.disabled).toBeFalsy() + fireEvent.click(saveBtn) + + expect(mockUpdate).toHaveBeenLastCalledWith({ + authId: '3ed71b1e-fd0c-444b-b653-7e78731d4865', + putUserReq: { + locale: 'fr', + firstName: 'new firstname', + lastName: 'new lastname', + roles: ['super_admin'], + }, + }) + }, + ) + + it( + 'delete user', + async () => { + render() + + const deleteBtn = screen.queryByTestId('deleteButton') as HTMLButtonElement + expect(screen.queryByRole('dialog')).not.toBeInTheDocument() + + fireEvent.click(deleteBtn) + expect(screen.queryByRole('dialog')).toBeInTheDocument() + + fireEvent.click(screen.queryByTestId('confirmButton') as HTMLButtonElement) + + expect(mockDelete).toHaveBeenLastCalledWith({ authId: '3ed71b1e-fd0c-444b-b653-7e78731d4865' }) + }, + ) + }, +) diff --git a/admin-panel/app/[lang]/users/[authId]/page.tsx b/admin-panel/app/[lang]/users/[authId]/page.tsx index 9f823e5..57e44d7 100644 --- a/admin-panel/app/[lang]/users/[authId]/page.tsx +++ b/admin-panel/app/[lang]/users/[authId]/page.tsx @@ -389,6 +389,7 @@ const Page = () => { {configs.SUPPORTED_LOCALES.length > 1 && (