Skip to content

Commit

Permalink
♻️(frontend) replace env NEXT_PUBLIC_FEATURE_TEAM
Browse files Browse the repository at this point in the history
NEXT_PUBLIC_FEATURE_TEAM is a buid-time env
variable, it is not easy to overload it per
environment.
We will use the config endpoint to get the
feature flag at runtime.
To do so, we are using the ConfigStore.
  • Loading branch information
AntoLC committed Aug 20, 2024
1 parent 92876ab commit c539658
Show file tree
Hide file tree
Showing 11 changed files with 110 additions and 18 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ and this project adheres to

## [Unreleased]

### Added

🔧Runtime config for the frontend #345

## [1.0.1] - 2024-08-19

### Fixed
Expand Down
1 change: 0 additions & 1 deletion src/frontend/apps/desk/.env.development
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
NEXT_PUBLIC_API_ORIGIN=http://localhost:8071
NEXT_PUBLIC_FEATURE_TEAM=true
1 change: 0 additions & 1 deletion src/frontend/apps/desk/.env.test
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
NEXT_PUBLIC_API_ORIGIN=http://test.jest
NEXT_PUBLIC_FEATURE_TEAM=true
34 changes: 26 additions & 8 deletions src/frontend/apps/desk/src/__tests__/pages.test.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import { render } from '@testing-library/react';

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

import Page from '../pages';

const mockedPush = jest.fn();
const mockedUseRouter = jest.fn().mockReturnValue({
push: mockedPush,
});

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

describe('Page', () => {
it('checks Page rendering', () => {
afterEach(() => jest.clearAllMocks());

it('checks Page rendering with team feature', () => {
useConfigStore.setState({
config: { FEATURES: { TEAMS: true }, LANGUAGES: [] },
});

render(<Page />, { wrapper: AppWrapper });

expect(mockedPush).toHaveBeenCalledWith('/teams/');
});

it('checks Page rendering without team feature', () => {
useConfigStore.setState({
config: { FEATURES: { TEAMS: false }, LANGUAGES: [] },
});

render(<Page />, { wrapper: AppWrapper });

expect(
screen.getByRole('button', {
name: /Create a new team/i,
}),
).toBeInTheDocument();
expect(mockedPush).toHaveBeenCalledWith('/mail-domains/');
});
});
6 changes: 5 additions & 1 deletion src/frontend/apps/desk/src/core/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import { Footer } from '@/features/footer/Footer';
import { HEADER_HEIGHT, Header } from '@/features/header';
import { Menu } from '@/features/menu';

import { useConfigStore } from './config';

export function MainLayout({ children }: PropsWithChildren) {
const { config } = useConfigStore();

return (
<Box>
<Box $height="100vh">
<Header />
<Box $css="flex: 1;" $direction="row">
{process.env.NEXT_PUBLIC_FEATURE_TEAM === 'true' && <Menu />}
{config?.FEATURES.TEAMS && <Menu />}
<Box
as="main"
$height={`calc(100vh - ${HEADER_HEIGHT})`}
Expand Down
9 changes: 6 additions & 3 deletions src/frontend/apps/desk/src/core/__tests__/MainLayout.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ import { render, screen } from '@testing-library/react';
import { AppWrapper } from '@/tests/utils';

import { MainLayout } from '../MainLayout';
import { useConfigStore } from '../config';

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

describe('MainLayout', () => {
it('checks menu rendering', () => {
it('checks menu rendering with team feature', () => {
useConfigStore.setState({
config: { FEATURES: { TEAMS: true }, LANGUAGES: [] },
});

render(<MainLayout />, { wrapper: AppWrapper });

expect(
Expand All @@ -28,8 +33,6 @@ describe('MainLayout', () => {
});

it('checks menu rendering without team feature', () => {
process.env.NEXT_PUBLIC_FEATURE_TEAM = 'false';

render(<MainLayout />, { wrapper: AppWrapper });

expect(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next';

import IconOpenClose from '@/assets/icons/icon-open-close.svg';
import { Box, BoxButton, Text } from '@/components';
import { useConfigStore } from '@/core/';
import { useCunninghamTheme } from '@/cunningham';

import { ItemList } from './ItemList';
Expand All @@ -11,6 +12,7 @@ import { PanelActions } from './PanelActions';
export const Panel = () => {
const { t } = useTranslation();
const { colorsTokens } = useCunninghamTheme();
const { config } = useConfigStore();

const [isOpen, setIsOpen] = useState(true);

Expand All @@ -20,7 +22,7 @@ export const Panel = () => {
$minWidth: '0',
};

const styleNoTeam = process.env.NEXT_PUBLIC_FEATURE_TEAM !== 'true' && {
const styleNoTeam = !config?.FEATURES.TEAMS && {
$display: 'none',
tabIndex: -1,
};
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/apps/desk/src/features/menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const Menu = () => {
<MenuItem
Icon={IconGroup}
label={t('Teams')}
href="/"
href="/teams"
alias={['/teams']}
/>
<MenuItem
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/apps/desk/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const Page: NextPageWithLayout = () => {
useEffect(() => {
config?.FEATURES.TEAMS
? router.push('/teams/')
: router.push('mail-domains/');
: router.push('/mail-domains/');
}, [config?.FEATURES.TEAMS, router]);

return null;
Expand Down
63 changes: 63 additions & 0 deletions src/frontend/apps/e2e/__tests__/app-desk/config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { expect, test } from '@playwright/test';

import { keyCloakSignIn } from './common';

test.describe('Config', () => {
test.beforeEach(async ({ page, browserName }) => {
await page.goto('/');
await keyCloakSignIn(page, browserName);
});

test('it checks the config api is called', async ({ page }) => {
const responsePromise = page.waitForResponse(
(response) =>
response.url().includes('/config/') && response.status() === 200,
);

const response = await responsePromise;
expect(response.ok()).toBeTruthy();

expect(await response.json()).toStrictEqual({
LANGUAGES: [
['en-us', 'English'],
['fr-fr', 'French'],
],
FEATURES: { TEAMS: true },
});
});

test('it checks that the config can deactivate the feature "teams"', async ({
page,
}) => {
await page.route('**/api/v1.0/config/', async (route) => {
const request = route.request();
if (request.method().includes('GET')) {
await route.fulfill({
json: {
LANGUAGES: [
['en-us', 'English'],
['fr-fr', 'French'],
],
FEATURES: { TEAMS: false },
},
});
} else {
await route.continue();
}
});

await expect(page.locator('menu')).toBeHidden();

await expect(
page.getByRole('button', {
name: 'Create a new team',
}),
).toBeHidden();

await expect(
page.getByRole('button', {
name: 'Add your mail domain',
}),
).toBeVisible();
});
});
2 changes: 1 addition & 1 deletion src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ test.describe('Menu', () => {
{
name: 'Teams',
isDefault: true,
expectedUrl: '',
expectedUrl: '/teams/',
expectedText: 'Create a new team',
},
{
Expand Down

0 comments on commit c539658

Please sign in to comment.