Skip to content

Commit

Permalink
⚗️(frontend) show username on AccountDropDown
Browse files Browse the repository at this point in the history
- show username instead of "My account"
  • Loading branch information
daproclaima committed Sep 19, 2024
1 parent 232ea97 commit 1f75ae9
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to

- ✨(domains) add endpoint to list and retrieve domain accesses @sdemagny #404
- 🍱(dev) embark dimail-api as container by @mjeammet #366
- ⚗️(frontend) show username on AccountDropDown #412

## [1.1.0] - 2024-09-10

Expand Down
2 changes: 1 addition & 1 deletion secrets
36 changes: 21 additions & 15 deletions src/frontend/apps/desk/src/features/header/AccountDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,38 @@ import { useAuthStore } from '@/core/auth';

export const AccountDropdown = () => {
const { t } = useTranslation();
const { logout } = useAuthStore();
const { userData, logout } = useAuthStore();

const userName = userData?.name || t('No Username');
return (
<DropButton
aria-label={t('My account')}
aria-label={userName}
button={
<Box $flex $direction="row" $align="center">
<Text $theme="primary">{t('My account')}</Text>
<Text $theme="primary">{userName}</Text>
<Text className="material-icons" $theme="primary" aria-hidden="true">
arrow_drop_down
</Text>
</Box>
}
>
<Button
onClick={logout}
color="primary-text"
icon={
<span className="material-icons" aria-hidden="true">
logout
</span>
}
aria-label={t('Logout')}
>
<Text $weight="normal">{t('Logout')}</Text>
</Button>
<Box $css="display: flex; direction: column; gap: 0.5rem">
<Box>
<Button
onClick={logout}
key="logout"
color="primary-text"
icon={
<span className="material-icons" aria-hidden="true">
logout
</span>
}
aria-label={t('Logout')}
>
<Text $weight="normal">{t('Logout')}</Text>
</Button>
</Box>
</Box>
</DropButton>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { fireEvent, render, screen } from '@testing-library/react';

import { useAuthStore } from '@/core/auth';
import { AppWrapper } from '@/tests/utils';

import { AccountDropdown } from '../AccountDropdown';

jest.mock('@/core/auth', () => ({
useAuthStore: jest.fn(),
}));

describe('AccountDropdown', () => {
const mockLogout = jest.fn();
const mockUserData = {
id: '1',
email: '[email protected]',
name: 'Test User',
};

const renderAccountDropdown = () =>
render(<AccountDropdown />, { wrapper: AppWrapper });

beforeEach(() => {
jest.clearAllMocks();
(useAuthStore as unknown as jest.Mock).mockReturnValue({
userData: mockUserData,
logout: mockLogout,
});
});

it('renders the user name correctly', async () => {
renderAccountDropdown();

expect(await screen.findByText('Test User')).toBeInTheDocument();
});

it('renders "No Username" when userData name is missing', () => {
(useAuthStore as unknown as jest.Mock).mockReturnValue({
userData: { id: '1', email: '[email protected]' }, // No name property
logout: mockLogout,
});
renderAccountDropdown();
expect(screen.getByText('No Username')).toBeInTheDocument();
});

it('opens the dropdown and shows logout button when clicked', async () => {
renderAccountDropdown();

const dropButton = await screen.findByText('Test User');
fireEvent.click(dropButton);

expect(screen.getByText('Logout')).toBeInTheDocument();
expect(screen.getByLabelText('Logout')).toBeInTheDocument();
});

it('calls logout function when logout button is clicked', async () => {
renderAccountDropdown();

// Open the dropdown first
const dropButton = await screen.findByText('Test User');
fireEvent.click(dropButton);

// Click the logout button
const logoutButton = screen.getByLabelText('Logout');
fireEvent.click(logoutButton);

expect(mockLogout).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { render, screen, waitFor } from '@testing-library/react';
import { useRouter as useNavigate } from 'next/navigation';
import { useRouter } from 'next/router';

import { AccessesContent } from '@/features/mail-domains/access-management';
import { Role, useMailDomain } from '@/features/mail-domains/domains';
import MailDomainAccessesPage from '@/pages/mail-domains/[slug]/accesses';
import { AppWrapper } from '@/tests/utils';

jest.mock('next/navigation', () => ({
useRouter: jest.fn(),
}));

jest.mock('next/router', () => ({
useRouter: jest.fn(),
}));

jest.mock('@/features/mail-domains/domains/api/useMailDomain', () => ({
useMailDomain: jest.fn(),
}));

jest.mock(
'@/features/mail-domains/access-management/components/AccessesContent',
() => ({
AccessesContent: jest.fn(() => <div>AccessContent</div>),
}),
);

describe('MailDomainAccessesPage', () => {
const mockRouterReplace = jest.fn();
const mockNavigate = { replace: mockRouterReplace };
const mockRouter = {
query: { slug: 'example-slug' },
};

(useRouter as jest.Mock).mockReturnValue(mockRouter);
(useNavigate as jest.Mock).mockReturnValue(mockNavigate);

beforeEach(() => {
jest.clearAllMocks();
(useRouter as jest.Mock).mockReturnValue(mockRouter);
(useNavigate as jest.Mock).mockReturnValue(mockNavigate);
});

const renderPage = () => {
render(<MailDomainAccessesPage />, { wrapper: AppWrapper });
};

it('renders loader while loading', () => {
(useMailDomain as jest.Mock).mockReturnValue({
isLoading: true,
data: null,
error: null,
isError: false,
});

renderPage();

expect(screen.getByRole('status')).toBeInTheDocument();
});

it('renders error message when there is an error', () => {
(useMailDomain as jest.Mock).mockReturnValue({
isLoading: false,
data: null,
error: { cause: ['Error loading domain data'] },
isError: true,
});

renderPage();

expect(screen.getByText('Error loading domain data')).toBeInTheDocument();
});

it('redirects to 404 page if the domain is not found', async () => {
(useMailDomain as jest.Mock).mockReturnValue({
isLoading: false,
data: null,
error: { status: 404 },
isError: true,
});

renderPage();

await waitFor(() => {
expect(mockRouterReplace).toHaveBeenCalledWith('/404');
});
});

it('renders the AccessesContent when data is available', async () => {
const mockMailDomain = {
id: '1-1-1-1-1',
name: 'example.com',
slug: 'example-com',
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
abilities: {
get: true,
patch: true,
put: true,
post: true,
delete: true,
manage_accesses: true,
},
};

(useMailDomain as jest.Mock).mockReturnValue({
isLoading: false,
data: mockMailDomain,
error: null,
isError: false,
});

renderPage();

await waitFor(() => {
expect(screen.getByText('AccessContent')).toBeInTheDocument();
});

await waitFor(() => {
expect(AccessesContent).toHaveBeenCalledWith(
{
mailDomain: mockMailDomain,
currentRole: Role.OWNER,
},
{}, // adding this empty object is necessary to load jest context and that AccessContent is a mock
);
});
});

it('throws an error when slug is invalid', () => {
// used to not add expected error to jest logs
console.error = jest.fn(() => {});

(useRouter as jest.Mock).mockReturnValue({
query: { slug: ['invalid-array-slug-in-array'] },
});

expect(() => renderPage()).toThrow('Invalid mail domain slug');
});
});
2 changes: 1 addition & 1 deletion src/frontend/apps/desk/src/i18n/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@
"Member icon": "Icône de membre",
"Member {{name}} added to the team": "Membre {{name}} ajouté au groupe",
"More info?": "Plus d'infos ?",
"My account": "Mon compte",
"Names": "Noms",
"New name...": "Nouveau nom...",
"No Username": "Aucun nom d'utilisateur",
"No domains exist.": "Aucun domaine existant.",
"No mail box was created with this mail domain.": "Aucune boîte mail n'a été créée avec ce nom de domaine.",
"Nothing exceptional, no special privileges related to a .gouv.fr.": "Rien d'exceptionnel, pas de privilèges spéciaux liés à un .gouv.fr.",
Expand Down
10 changes: 6 additions & 4 deletions src/frontend/apps/e2e/__tests__/app-desk/header.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ test.beforeEach(async ({ page, browserName }) => {
});

test.describe('Header', () => {
test('checks all the elements are visible', async ({ page }) => {
test('checks all the elements are visible', async ({ page, browserName }) => {
const header = page.locator('header').first();

await expect(header.getByAltText('Marianne Logo')).toBeVisible();
Expand Down Expand Up @@ -37,13 +37,15 @@ test.describe('Header', () => {
).toBeVisible();

await expect(header.getByRole('combobox').getByText('EN')).toBeVisible();
await expect(header.getByText('My account')).toBeVisible();
await expect(
header.getByText(new RegExp(`E2E ${browserName}`, 'i')),
).toBeVisible();
});

test('checks logout button', async ({ page }) => {
test('checks logout button', async ({ page, browserName }) => {
await page
.getByRole('button', {
name: 'My account',
name: new RegExp(`E2E ${browserName}`, 'i'),
})
.click();

Expand Down

0 comments on commit 1f75ae9

Please sign in to comment.