diff --git a/CHANGELOG.md b/CHANGELOG.md index 93a58e4..531ba87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to ## [Unreleased] +### Added + +🔧Runtime config for the frontend #345 + ## [1.0.1] - 2024-08-19 ### Fixed diff --git a/src/frontend/apps/desk/.env.development b/src/frontend/apps/desk/.env.development index 4cb4d49..45ee232 100644 --- a/src/frontend/apps/desk/.env.development +++ b/src/frontend/apps/desk/.env.development @@ -1,2 +1 @@ NEXT_PUBLIC_API_ORIGIN=http://localhost:8071 -NEXT_PUBLIC_FEATURE_TEAM=true diff --git a/src/frontend/apps/desk/.env.test b/src/frontend/apps/desk/.env.test index 026733c..9a4d514 100644 --- a/src/frontend/apps/desk/.env.test +++ b/src/frontend/apps/desk/.env.test @@ -1,2 +1 @@ NEXT_PUBLIC_API_ORIGIN=http://test.jest -NEXT_PUBLIC_FEATURE_TEAM=true diff --git a/src/frontend/apps/desk/src/__tests__/pages.test.tsx b/src/frontend/apps/desk/src/__tests__/pages.test.tsx index 28d66c5..1713238 100644 --- a/src/frontend/apps/desk/src/__tests__/pages.test.tsx +++ b/src/frontend/apps/desk/src/__tests__/pages.test.tsx @@ -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(, { wrapper: AppWrapper }); - expect( - screen.getByRole('button', { - name: /Create a new team/i, - }), - ).toBeInTheDocument(); + expect(mockedPush).toHaveBeenCalledWith('/teams/'); + }); + + it('checks Page rendering without team feature', () => { + useConfigStore.setState({ + config: { FEATURES: { TEAMS: false }, LANGUAGES: [] }, + }); + + render(, { wrapper: AppWrapper }); + + expect(mockedPush).toHaveBeenCalledWith('/mail-domains/'); }); }); diff --git a/src/frontend/apps/desk/src/core/MainLayout.tsx b/src/frontend/apps/desk/src/core/MainLayout.tsx index 88b6ccf..3b90e64 100644 --- a/src/frontend/apps/desk/src/core/MainLayout.tsx +++ b/src/frontend/apps/desk/src/core/MainLayout.tsx @@ -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 (
- {process.env.NEXT_PUBLIC_FEATURE_TEAM === 'true' && } + {config?.FEATURES.TEAMS && } ({ ...jest.requireActual('next/navigation'), @@ -11,7 +12,11 @@ jest.mock('next/navigation', () => ({ })); describe('MainLayout', () => { - it('checks menu rendering', () => { + it('checks menu rendering with team feature', () => { + useConfigStore.setState({ + config: { FEATURES: { TEAMS: true }, LANGUAGES: [] }, + }); + render(, { wrapper: AppWrapper }); expect( @@ -28,8 +33,6 @@ describe('MainLayout', () => { }); it('checks menu rendering without team feature', () => { - process.env.NEXT_PUBLIC_FEATURE_TEAM = 'false'; - render(, { wrapper: AppWrapper }); expect( diff --git a/src/frontend/apps/desk/src/features/mail-domains/components/panel/Panel.tsx b/src/frontend/apps/desk/src/features/mail-domains/components/panel/Panel.tsx index 10b6082..44e67d2 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/components/panel/Panel.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/components/panel/Panel.tsx @@ -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'; @@ -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); @@ -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, }; diff --git a/src/frontend/apps/desk/src/features/menu/Menu.tsx b/src/frontend/apps/desk/src/features/menu/Menu.tsx index c921646..d8a622e 100644 --- a/src/frontend/apps/desk/src/features/menu/Menu.tsx +++ b/src/frontend/apps/desk/src/features/menu/Menu.tsx @@ -25,7 +25,7 @@ export const Menu = () => { { useEffect(() => { config?.FEATURES.TEAMS ? router.push('/teams/') - : router.push('mail-domains/'); + : router.push('/mail-domains/'); }, [config?.FEATURES.TEAMS, router]); return null; diff --git a/src/frontend/apps/e2e/__tests__/app-desk/404.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/404.spec.ts index 4e4fa70..494676b 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/404.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/404.spec.ts @@ -24,6 +24,6 @@ test.describe('404', () => { page, }) => { await page.getByText('Back to home page').click(); - await expect(page).toHaveURL('/'); + await expect(page).toHaveURL('/teams/'); }); }); diff --git a/src/frontend/apps/e2e/__tests__/app-desk/config.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/config.spec.ts new file mode 100644 index 0000000..b6d2937 --- /dev/null +++ b/src/frontend/apps/e2e/__tests__/app-desk/config.spec.ts @@ -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(); + }); +}); diff --git a/src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts index 4cad5c4..5c753fc 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts @@ -12,7 +12,7 @@ test.describe('Menu', () => { { name: 'Teams', isDefault: true, - expectedUrl: '', + expectedUrl: '/teams/', expectedText: 'Create a new team', }, {