✨(frontend) use user abilities to show or not the features
use the abilities to show or not the Teams / Mailbox features as well as the object creation button
This commit is contained in:
committed by
BEY Quentin
parent
6be1b63277
commit
6123e11dd4
@@ -4,6 +4,7 @@ import { render, screen } from '@testing-library/react';
|
|||||||
import { AppWrapper } from '@/tests/utils';
|
import { AppWrapper } from '@/tests/utils';
|
||||||
|
|
||||||
import { MainLayout } from '../MainLayout';
|
import { MainLayout } from '../MainLayout';
|
||||||
|
import { useAuthStore } from '../auth';
|
||||||
import { useConfigStore } from '../config';
|
import { useConfigStore } from '../config';
|
||||||
|
|
||||||
jest.mock('next/navigation', () => ({
|
jest.mock('next/navigation', () => ({
|
||||||
@@ -19,6 +20,19 @@ describe('MainLayout', () => {
|
|||||||
useConfigStore.setState({
|
useConfigStore.setState({
|
||||||
config: { RELEASE: '1.0.0', FEATURES: { TEAMS: true }, LANGUAGES: [] },
|
config: { RELEASE: '1.0.0', FEATURES: { TEAMS: true }, LANGUAGES: [] },
|
||||||
});
|
});
|
||||||
|
useAuthStore.setState({
|
||||||
|
authenticated: true,
|
||||||
|
userData: {
|
||||||
|
id: '1',
|
||||||
|
email: 'test@example.com',
|
||||||
|
name: 'Test User',
|
||||||
|
abilities: {
|
||||||
|
contacts: { can_view: true },
|
||||||
|
teams: { can_view: true },
|
||||||
|
mailboxes: { can_view: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
render(<MainLayout />, { wrapper: AppWrapper });
|
render(<MainLayout />, { wrapper: AppWrapper });
|
||||||
|
|
||||||
@@ -35,10 +49,57 @@ describe('MainLayout', () => {
|
|||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('checks menu rendering with no abilities', () => {
|
||||||
|
useConfigStore.setState({
|
||||||
|
config: { RELEASE: '1.0.0', FEATURES: { TEAMS: true }, LANGUAGES: [] },
|
||||||
|
});
|
||||||
|
useAuthStore.setState({
|
||||||
|
authenticated: true,
|
||||||
|
userData: {
|
||||||
|
id: '1',
|
||||||
|
email: 'test@example.com',
|
||||||
|
name: 'Test User',
|
||||||
|
abilities: {
|
||||||
|
contacts: { can_view: false },
|
||||||
|
teams: { can_view: false },
|
||||||
|
mailboxes: { can_view: false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<MainLayout />, { wrapper: AppWrapper });
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.queryByRole('button', {
|
||||||
|
// Changé de getByRole à queryByRole
|
||||||
|
name: /Teams button/i,
|
||||||
|
}),
|
||||||
|
).not.toBeInTheDocument(); //
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.queryByRole('button', {
|
||||||
|
name: /Mail Domains button/i,
|
||||||
|
}),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it('checks menu rendering without team feature', () => {
|
it('checks menu rendering without team feature', () => {
|
||||||
useConfigStore.setState({
|
useConfigStore.setState({
|
||||||
config: { RELEASE: '1.0.0', FEATURES: { TEAMS: false }, LANGUAGES: [] },
|
config: { RELEASE: '1.0.0', FEATURES: { TEAMS: false }, LANGUAGES: [] },
|
||||||
});
|
});
|
||||||
|
useAuthStore.setState({
|
||||||
|
authenticated: true,
|
||||||
|
userData: {
|
||||||
|
id: '1',
|
||||||
|
email: 'test@example.com',
|
||||||
|
name: 'Test User',
|
||||||
|
abilities: {
|
||||||
|
contacts: { can_view: true },
|
||||||
|
teams: { can_view: true },
|
||||||
|
mailboxes: { can_view: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
render(<MainLayout />, { wrapper: AppWrapper });
|
render(<MainLayout />, { wrapper: AppWrapper });
|
||||||
|
|
||||||
|
|||||||
@@ -9,4 +9,14 @@ export interface User {
|
|||||||
id: string;
|
id: string;
|
||||||
email: string;
|
email: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
abilities?: {
|
||||||
|
mailboxes: UserAbilities;
|
||||||
|
contacts: UserAbilities;
|
||||||
|
teams: UserAbilities;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UserAbilities = {
|
||||||
|
can_view?: boolean;
|
||||||
|
can_create?: boolean;
|
||||||
|
};
|
||||||
|
|||||||
@@ -35,7 +35,11 @@ describe('getMailDomainAccesses', () => {
|
|||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
role: Role.VIEWER,
|
role: Role.VIEWER,
|
||||||
user: { id: '12', name: 'username2', email: 'user2@test.com' },
|
user: {
|
||||||
|
id: '12',
|
||||||
|
name: 'username2',
|
||||||
|
email: 'user2@test.com',
|
||||||
|
},
|
||||||
can_set_role_to: [Role.VIEWER],
|
can_set_role_to: [Role.VIEWER],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import IconGroup from '@/assets/icons/icon-group.svg';
|
import IconGroup from '@/assets/icons/icon-group.svg';
|
||||||
import { Box } from '@/components/';
|
import { Box } from '@/components/';
|
||||||
|
import { useAuthStore } from '@/core/auth';
|
||||||
import useCunninghamTheme from '@/cunningham/useCunninghamTheme';
|
import useCunninghamTheme from '@/cunningham/useCunninghamTheme';
|
||||||
|
|
||||||
import MenuItem from './MenuItems';
|
import MenuItem from './MenuItems';
|
||||||
@@ -10,6 +10,9 @@ import IconMailDomains from './assets/icon-mails.svg';
|
|||||||
|
|
||||||
export const Menu = () => {
|
export const Menu = () => {
|
||||||
const { colorsTokens } = useCunninghamTheme();
|
const { colorsTokens } = useCunninghamTheme();
|
||||||
|
const { userData } = useAuthStore();
|
||||||
|
|
||||||
|
console.log(userData);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -22,17 +25,21 @@ export const Menu = () => {
|
|||||||
$margin="none"
|
$margin="none"
|
||||||
>
|
>
|
||||||
<Box $padding={{ top: 'large' }} $direction="column" $gap="0.8rem">
|
<Box $padding={{ top: 'large' }} $direction="column" $gap="0.8rem">
|
||||||
<MenuItem
|
{userData?.abilities?.teams.can_view && (
|
||||||
Icon={IconGroup}
|
<MenuItem
|
||||||
label={t('Teams')}
|
Icon={IconGroup}
|
||||||
href="/teams"
|
label={t('Teams')}
|
||||||
alias={['/teams']}
|
href="/teams"
|
||||||
/>
|
alias={['/teams']}
|
||||||
<MenuItem
|
/>
|
||||||
Icon={IconMailDomains}
|
)}
|
||||||
label={t('Mail Domains')}
|
{userData?.abilities?.mailboxes.can_view && (
|
||||||
href="/mail-domains"
|
<MenuItem
|
||||||
/>
|
Icon={IconMailDomains}
|
||||||
|
label={t('Mail Domains')}
|
||||||
|
href="/mail-domains"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import type { ReactElement } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { Box } from '@/components';
|
import { Box, Text } from '@/components';
|
||||||
|
import { useAuthStore } from '@/core/auth';
|
||||||
import { MailDomainsLayout } from '@/features/mail-domains/domains';
|
import { MailDomainsLayout } from '@/features/mail-domains/domains';
|
||||||
import { NextPageWithLayout } from '@/types/next';
|
import { NextPageWithLayout } from '@/types/next';
|
||||||
|
|
||||||
@@ -14,14 +15,18 @@ const StyledButton = styled(Button)`
|
|||||||
|
|
||||||
const Page: NextPageWithLayout = () => {
|
const Page: NextPageWithLayout = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { userData } = useAuthStore();
|
||||||
|
const can_create = userData?.abilities?.mailboxes.can_create;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box $align="center" $justify="center" $height="inherit">
|
<Box $align="center" $justify="center" $height="inherit">
|
||||||
<StyledButton onClick={() => void router.push('/mail-domains/add')}>
|
{can_create && (
|
||||||
{t('Add a mail domain')}
|
<StyledButton onClick={() => void router.push('/mail-domains/add')}>
|
||||||
</StyledButton>
|
{t('Add a mail domain')}
|
||||||
|
</StyledButton>
|
||||||
|
)}
|
||||||
|
{!can_create && <Text>{t('Click on mailbox to view details')}</Text>}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { Button } from '@openfun/cunningham-react';
|
import { Button } from '@openfun/cunningham-react';
|
||||||
import { useRouter as useNavigate } from 'next/navigation';
|
import { useRouter as useNavigate } from 'next/navigation';
|
||||||
import type { ReactElement } from 'react';
|
import { ReactElement } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { Box } from '@/components';
|
import { Box, Text } from '@/components';
|
||||||
|
import { useAuthStore } from '@/core/auth';
|
||||||
import { TeamLayout } from '@/features/teams/team-management';
|
import { TeamLayout } from '@/features/teams/team-management';
|
||||||
import { NextPageWithLayout } from '@/types/next';
|
import { NextPageWithLayout } from '@/types/next';
|
||||||
|
|
||||||
@@ -15,12 +16,17 @@ const StyledButton = styled(Button)`
|
|||||||
const Page: NextPageWithLayout = () => {
|
const Page: NextPageWithLayout = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useNavigate();
|
const router = useNavigate();
|
||||||
|
const { userData } = useAuthStore();
|
||||||
|
const can_create = userData?.abilities?.teams.can_create ?? false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box $align="center" $justify="center" $height="inherit">
|
<Box $align="center" $justify="center" $height="inherit">
|
||||||
<StyledButton onClick={() => void router.push('/teams/create')}>
|
{can_create && (
|
||||||
{t('Create a new team')}
|
<StyledButton onClick={() => void router.push('/teams/create')}>
|
||||||
</StyledButton>
|
{t('Create a new team')}
|
||||||
|
</StyledButton>
|
||||||
|
)}
|
||||||
|
{!can_create && <Text>{t('Click on team to view details')}</Text>}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ test.describe('Menu', () => {
|
|||||||
name: 'Teams',
|
name: 'Teams',
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
expectedUrl: '/teams/',
|
expectedUrl: '/teams/',
|
||||||
expectedText: 'Create a new team',
|
expectedText: 'Teams',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Mail Domains',
|
name: 'Mail Domains',
|
||||||
@@ -62,6 +62,94 @@ test.describe('Menu', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test(`it checks that the menu is not displaying when no abilities`, async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await page.route('**/api/v1.0/users/me/', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
json: {
|
||||||
|
id: '52de4dcf-5ca0-4b7f-9841-3a18e8cb6a95',
|
||||||
|
email: 'user@chromium.e2e',
|
||||||
|
language: 'en-us',
|
||||||
|
name: 'E2E Chromium',
|
||||||
|
timezone: 'UTC',
|
||||||
|
is_device: false,
|
||||||
|
is_staff: false,
|
||||||
|
abilities: {
|
||||||
|
contacts: {
|
||||||
|
can_view: true,
|
||||||
|
can_create: true,
|
||||||
|
},
|
||||||
|
teams: {
|
||||||
|
can_view: true,
|
||||||
|
can_create: false,
|
||||||
|
},
|
||||||
|
mailboxes: {
|
||||||
|
can_view: true,
|
||||||
|
can_create: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const menu = page.locator('menu').first();
|
||||||
|
|
||||||
|
let buttonMenu = menu.getByLabel(`Teams button`);
|
||||||
|
await buttonMenu.click();
|
||||||
|
await expect(
|
||||||
|
page.getByText('Click on team to view details').first(),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
buttonMenu = menu.getByLabel(`Mail Domains`);
|
||||||
|
await buttonMenu.click();
|
||||||
|
await expect(
|
||||||
|
page.getByText('Click on mailbox to view details').first(),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test(`it checks that the menu is not displaying when all abilities`, async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await page.route('**/api/v1.0/users/me/', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
json: {
|
||||||
|
id: '52de4dcf-5ca0-4b7f-9841-3a18e8cb6a95',
|
||||||
|
email: 'user@chromium.e2e',
|
||||||
|
language: 'en-us',
|
||||||
|
name: 'E2E ChromiumMM',
|
||||||
|
timezone: 'UTC',
|
||||||
|
is_device: false,
|
||||||
|
is_staff: false,
|
||||||
|
abilities: {
|
||||||
|
contacts: {
|
||||||
|
can_view: true,
|
||||||
|
can_create: true,
|
||||||
|
},
|
||||||
|
teams: {
|
||||||
|
can_view: true,
|
||||||
|
can_create: true,
|
||||||
|
},
|
||||||
|
mailboxes: {
|
||||||
|
can_view: true,
|
||||||
|
can_create: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const menu = page.locator('menu').first();
|
||||||
|
|
||||||
|
let buttonMenu = menu.getByLabel(`Teams button`);
|
||||||
|
await buttonMenu.click();
|
||||||
|
await expect(page.getByText('Create a new team').first()).toBeVisible();
|
||||||
|
|
||||||
|
buttonMenu = menu.getByLabel(`Mail Domains`);
|
||||||
|
await buttonMenu.click();
|
||||||
|
await expect(page.getByText('Add a mail domain').first()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
test(`it checks that the sub menu is still highlighted`, async ({
|
test(`it checks that the sub menu is still highlighted`, async ({
|
||||||
page,
|
page,
|
||||||
browserName,
|
browserName,
|
||||||
|
|||||||
Reference in New Issue
Block a user