-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨(frontend) add mail domain access management
- move files: separate mail domains feature into domains, mailboxe and access management sub features. - pages/mail-domains/[slug].tsx becomes pages/mail-domains/[slug]/index.tsx to add a pages/mail-domains/[slug]/accesses.tsx to manage accesses. - access management view is ready to use get, patch and delete requests once backend is ready. - update translations and component tests. - reduce gap between mail domains feature logo and mail domain name in top banner
- Loading branch information
1 parent
232ea97
commit 631e63c
Showing
65 changed files
with
2,781 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule secrets
updated
from 2572ba to 49b591
109 changes: 109 additions & 0 deletions
109
.../features/mail-domains/access-management/api/__tests__/useDeleteMailDomainAccess.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { renderHook, waitFor } from '@testing-library/react'; | ||
import fetchMock from 'fetch-mock'; | ||
|
||
import { APIError } from '@/api'; | ||
import { AppWrapper } from '@/tests/utils'; | ||
|
||
import { | ||
deleteMailDomainAccess, | ||
useDeleteMailDomainAccess, | ||
} from '../useDeleteMailDomainAccess'; | ||
|
||
describe('deleteMailDomainAccess', () => { | ||
afterEach(() => { | ||
fetchMock.restore(); | ||
}); | ||
|
||
it('deletes the access successfully', async () => { | ||
fetchMock.deleteOnce('end:/mail-domains/example-slug/accesses/1-1-1-1-1/', { | ||
status: 204, // No content status | ||
}); | ||
|
||
await deleteMailDomainAccess({ | ||
slug: 'example-slug', | ||
accessId: '1-1-1-1-1', | ||
}); | ||
|
||
expect(fetchMock.calls()).toHaveLength(1); | ||
expect(fetchMock.lastUrl()).toContain( | ||
'/mail-domains/example-slug/accesses/1-1-1-1-1/', | ||
); | ||
}); | ||
|
||
it('throws an error when the API call fails', async () => { | ||
fetchMock.deleteOnce('end:/mail-domains/example-slug/accesses/1-1-1-1-1/', { | ||
status: 500, | ||
body: { cause: ['Internal server error'] }, | ||
}); | ||
|
||
await expect( | ||
deleteMailDomainAccess({ | ||
slug: 'example-slug', | ||
accessId: '1-1-1-1-1', | ||
}), | ||
).rejects.toThrow(APIError); | ||
expect(fetchMock.calls()).toHaveLength(1); | ||
}); | ||
}); | ||
|
||
describe('useDeleteMailDomainAccess', () => { | ||
afterEach(() => { | ||
fetchMock.restore(); | ||
}); | ||
|
||
it('deletes the access and calls onSuccess callback', async () => { | ||
fetchMock.deleteOnce('end:/mail-domains/example-slug/accesses/1-1-1-1-1/', { | ||
status: 204, // No content status | ||
}); | ||
|
||
const onSuccess = jest.fn(); | ||
|
||
const { result } = renderHook( | ||
() => useDeleteMailDomainAccess({ onSuccess }), | ||
{ | ||
wrapper: AppWrapper, | ||
}, | ||
); | ||
|
||
result.current.mutate({ | ||
slug: 'example-slug', | ||
accessId: '1-1-1-1-1', | ||
}); | ||
|
||
await waitFor(() => expect(fetchMock.calls()).toHaveLength(1)); | ||
await waitFor(() => | ||
expect(onSuccess).toHaveBeenCalledWith( | ||
undefined, | ||
{ slug: 'example-slug', accessId: '1-1-1-1-1' }, | ||
undefined, | ||
), | ||
); | ||
expect(fetchMock.lastUrl()).toContain( | ||
'/mail-domains/example-slug/accesses/1-1-1-1-1/', | ||
); | ||
}); | ||
|
||
it('calls onError when the API fails', async () => { | ||
fetchMock.deleteOnce('end:/mail-domains/example-slug/accesses/1-1-1-1-1/', { | ||
status: 500, | ||
body: { cause: ['Internal server error'] }, | ||
}); | ||
|
||
const onError = jest.fn(); | ||
|
||
const { result } = renderHook( | ||
() => useDeleteMailDomainAccess({ onError }), | ||
{ | ||
wrapper: AppWrapper, | ||
}, | ||
); | ||
|
||
result.current.mutate({ | ||
slug: 'example-slug', | ||
accessId: '1-1-1-1-1', | ||
}); | ||
|
||
await waitFor(() => expect(fetchMock.calls()).toHaveLength(1)); | ||
await waitFor(() => expect(onError).toHaveBeenCalled()); | ||
}); | ||
}); |
133 changes: 133 additions & 0 deletions
133
.../src/features/mail-domains/access-management/api/__tests__/useMailDomainAccesses.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import { renderHook, waitFor } from '@testing-library/react'; | ||
import fetchMock from 'fetch-mock'; | ||
|
||
import { APIError } from '@/api'; | ||
import { AppWrapper } from '@/tests/utils'; | ||
|
||
import { Role } from '../../../domains'; | ||
import { Access } from '../../types'; | ||
import { | ||
getMailDomainAccesses, | ||
useMailDomainAccesses, | ||
} from '../useMailDomainAccesses'; | ||
|
||
const mockAccess: Access = { | ||
id: '1-1-1-1-1', | ||
role: Role.ADMIN, | ||
user: { | ||
id: '2-1-1-1-1', | ||
name: 'username1', | ||
email: 'user1@test.com', | ||
}, | ||
can_set_role_to: [Role.VIEWER, Role.ADMIN], | ||
}; | ||
|
||
describe('getMailDomainAccesses', () => { | ||
afterEach(() => { | ||
fetchMock.restore(); | ||
}); | ||
|
||
it('fetches the list of accesses successfully', async () => { | ||
const mockResponse = { | ||
count: 2, | ||
results: [ | ||
mockAccess, | ||
{ | ||
id: '2', | ||
role: Role.VIEWER, | ||
user: { id: '12', name: 'username2', email: 'user2@test.com' }, | ||
can_set_role_to: [Role.VIEWER], | ||
}, | ||
], | ||
}; | ||
|
||
fetchMock.getOnce('end:/mail-domains/example-slug/accesses/?page=1', { | ||
status: 200, | ||
body: mockResponse, | ||
}); | ||
|
||
const result = await getMailDomainAccesses({ | ||
page: 1, | ||
slug: 'example-slug', | ||
}); | ||
|
||
expect(result).toEqual(mockResponse); | ||
expect(fetchMock.calls()).toHaveLength(1); | ||
expect(fetchMock.lastUrl()).toContain( | ||
'/mail-domains/example-slug/accesses/?page=1', | ||
); | ||
}); | ||
|
||
it('throws an error when the API call fails', async () => { | ||
fetchMock.getOnce('end:/mail-domains/example-slug/accesses/?page=1', { | ||
status: 500, | ||
body: { cause: ['Internal server error'] }, | ||
}); | ||
|
||
await expect( | ||
getMailDomainAccesses({ page: 1, slug: 'example-slug' }), | ||
).rejects.toThrow(APIError); | ||
expect(fetchMock.calls()).toHaveLength(1); | ||
}); | ||
}); | ||
|
||
describe('useMailDomainAccesses', () => { | ||
afterEach(() => { | ||
fetchMock.restore(); | ||
}); | ||
|
||
it('fetches and returns the accesses data using the hook', async () => { | ||
const mockResponse = { | ||
count: 2, | ||
results: [ | ||
mockAccess, | ||
{ | ||
id: '2', | ||
role: Role.VIEWER, | ||
user: { id: '12', name: 'username2', email: 'user2@test.com' }, | ||
can_set_role_to: [Role.VIEWER], | ||
}, | ||
], | ||
}; | ||
|
||
fetchMock.getOnce('end:/mail-domains/example-slug/accesses/?page=1', { | ||
status: 200, | ||
body: mockResponse, | ||
}); | ||
|
||
const { result } = renderHook( | ||
() => useMailDomainAccesses({ page: 1, slug: 'example-slug' }), | ||
{ | ||
wrapper: AppWrapper, | ||
}, | ||
); | ||
|
||
await waitFor(() => result.current.isSuccess); | ||
|
||
await waitFor(() => expect(result.current.data).toEqual(mockResponse)); | ||
expect(fetchMock.calls()).toHaveLength(1); | ||
expect(fetchMock.lastUrl()).toContain( | ||
'/mail-domains/example-slug/accesses/?page=1', | ||
); | ||
}); | ||
|
||
it('handles an API error properly with the hook', async () => { | ||
fetchMock.getOnce('end:/mail-domains/example-slug/accesses/?page=1', { | ||
status: 500, | ||
body: { cause: ['Internal server error'] }, | ||
}); | ||
|
||
const { result } = renderHook( | ||
() => useMailDomainAccesses({ page: 1, slug: 'example-slug' }), | ||
{ | ||
wrapper: AppWrapper, | ||
}, | ||
); | ||
|
||
await waitFor(() => result.current.isError); | ||
|
||
await waitFor(() => expect(result.current.error).toBeInstanceOf(APIError)); | ||
expect(result.current.error?.message).toBe('Failed to get the accesses'); | ||
expect(fetchMock.calls()).toHaveLength(1); | ||
}); | ||
}); |
139 changes: 139 additions & 0 deletions
139
.../features/mail-domains/access-management/api/__tests__/useUpdateMailDomainAccess.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import { renderHook, waitFor } from '@testing-library/react'; | ||
import fetchMock from 'fetch-mock'; | ||
|
||
import { APIError } from '@/api'; | ||
import { AppWrapper } from '@/tests/utils'; | ||
|
||
import { Role } from '../../../domains'; | ||
import { Access } from '../../types'; | ||
import { | ||
updateMailDomainAccess, | ||
useUpdateMailDomainAccess, | ||
} from '../useUpdateMailDomainAccess'; | ||
|
||
const mockAccess: Access = { | ||
id: '1-1-1-1-1', | ||
role: Role.ADMIN, | ||
user: { | ||
id: '2-1-1-1-1', | ||
name: 'username1', | ||
email: 'user1@test.com', | ||
}, | ||
can_set_role_to: [Role.VIEWER, Role.ADMIN], | ||
}; | ||
|
||
describe('updateMailDomainAccess', () => { | ||
afterEach(() => { | ||
fetchMock.restore(); | ||
}); | ||
|
||
it('updates the access role successfully', async () => { | ||
const mockResponse = { | ||
...mockAccess, | ||
role: Role.VIEWER, | ||
}; | ||
|
||
fetchMock.patchOnce('end:/mail-domains/example-slug/accesses/1-1-1-1-1/', { | ||
status: 200, | ||
body: mockResponse, | ||
}); | ||
|
||
const result = await updateMailDomainAccess({ | ||
slug: 'example-slug', | ||
accessId: '1-1-1-1-1', | ||
role: Role.VIEWER, | ||
}); | ||
|
||
expect(result).toEqual(mockResponse); | ||
expect(fetchMock.calls()).toHaveLength(1); | ||
expect(fetchMock.lastUrl()).toContain( | ||
'/mail-domains/example-slug/accesses/1-1-1-1-1/', | ||
); | ||
}); | ||
|
||
it('throws an error when the API call fails', async () => { | ||
fetchMock.patchOnce('end:/mail-domains/example-slug/accesses/1-1-1-1-1/', { | ||
status: 500, | ||
body: { cause: ['Internal server error'] }, | ||
}); | ||
|
||
await expect( | ||
updateMailDomainAccess({ | ||
slug: 'example-slug', | ||
accessId: '1-1-1-1-1', | ||
role: Role.VIEWER, | ||
}), | ||
).rejects.toThrow(APIError); | ||
expect(fetchMock.calls()).toHaveLength(1); | ||
}); | ||
}); | ||
|
||
describe('useUpdateMailDomainAccess', () => { | ||
afterEach(() => { | ||
fetchMock.restore(); | ||
}); | ||
|
||
it('updates the role and calls onSuccess callback', async () => { | ||
const mockResponse = { | ||
...mockAccess, | ||
role: Role.VIEWER, | ||
}; | ||
|
||
fetchMock.patchOnce('end:/mail-domains/example-slug/accesses/1-1-1-1-1/', { | ||
status: 200, | ||
body: mockResponse, | ||
}); | ||
|
||
const onSuccess = jest.fn(); | ||
|
||
const { result } = renderHook( | ||
() => useUpdateMailDomainAccess({ onSuccess }), | ||
{ | ||
wrapper: AppWrapper, | ||
}, | ||
); | ||
|
||
result.current.mutate({ | ||
slug: 'example-slug', | ||
accessId: '1-1-1-1-1', | ||
role: Role.VIEWER, | ||
}); | ||
|
||
await waitFor(() => expect(fetchMock.calls()).toHaveLength(1)); | ||
await waitFor(() => | ||
expect(onSuccess).toHaveBeenCalledWith( | ||
mockResponse, // data | ||
{ slug: 'example-slug', accessId: '1-1-1-1-1', role: Role.VIEWER }, // variables | ||
undefined, // context | ||
), | ||
); | ||
expect(fetchMock.lastUrl()).toContain( | ||
'/mail-domains/example-slug/accesses/1-1-1-1-1/', | ||
); | ||
}); | ||
|
||
it('calls onError when the API fails', async () => { | ||
fetchMock.patchOnce('end:/mail-domains/example-slug/accesses/1-1-1-1-1/', { | ||
status: 500, | ||
body: { cause: ['Internal server error'] }, | ||
}); | ||
|
||
const onError = jest.fn(); | ||
|
||
const { result } = renderHook( | ||
() => useUpdateMailDomainAccess({ onError }), | ||
{ | ||
wrapper: AppWrapper, | ||
}, | ||
); | ||
|
||
result.current.mutate({ | ||
slug: 'example-slug', | ||
accessId: '1-1-1-1-1', | ||
role: Role.VIEWER, | ||
}); | ||
|
||
await waitFor(() => expect(fetchMock.calls()).toHaveLength(1)); | ||
await waitFor(() => expect(onError).toHaveBeenCalled()); | ||
}); | ||
}); |
3 changes: 3 additions & 0 deletions
3
src/frontend/apps/desk/src/features/mail-domains/access-management/api/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './useMailDomainAccesses'; | ||
export * from './useUpdateMailDomainAccess'; | ||
export * from './useDeleteMailDomainAccess'; |
Oops, something went wrong.