import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { MemoryRouter, Routes, Route } from 'react-router-dom'; import { FeatureView } from '../feature/FeatureView/FeatureView'; import { ThemeProvider } from 'themes/ThemeProvider'; import { AccessProvider } from '../providers/AccessProvider/AccessProvider'; import { AnnouncerProvider } from '../common/Announcer/AnnouncerProvider/AnnouncerProvider'; import { testServerRoute, testServerSetup } from '../../utils/testServer'; import { UIProviderContainer } from '../providers/UIProvider/UIProviderContainer'; import type React from 'react'; import type { FC } from 'react'; import type { IPermission } from '../../interfaces/user'; import { SWRConfig } from 'swr'; import type { ProjectMode } from '../project/Project/hooks/useProjectEnterpriseSettingsForm'; import { StickyProvider } from 'component/common/Sticky/StickyProvider'; import { HighlightProvider } from 'component/common/Highlight/HighlightProvider'; const server = testServerSetup(); const projectWithCollaborationMode = (mode: ProjectMode) => testServerRoute(server, '/api/admin/projects/default/overview', { mode }); const changeRequestsEnabledIn = ( env: 'development' | 'production' | 'custom', ) => testServerRoute( server, '/api/admin/projects/default/change-requests/config', [ { environment: 'development', type: 'development', requiredApprovals: null, changeRequestEnabled: env === 'development', }, { environment: 'production', type: 'production', requiredApprovals: 1, changeRequestEnabled: env === 'production', }, { environment: 'custom', type: 'production', requiredApprovals: null, changeRequestEnabled: env === 'custom', }, ], ); const uiConfigForEnterprise = () => testServerRoute(server, '/api/admin/ui-config', { environment: 'Open Source', flags: { changeRequests: true, }, versionInfo: { current: { oss: '4.18.0-beta.5', enterprise: '4.17.0-beta.1' }, }, disablePasswordAuth: false, }); const setupOtherRoutes = (feature: string) => { testServerRoute( server, 'api/admin/projects/default/change-requests/pending', [], ); testServerRoute(server, `api/admin/client-metrics/features/${feature}`, { version: 1, maturity: 'stable', featureName: feature, lastHourUsage: [], seenApplications: [], }); testServerRoute(server, `api/admin/features/${feature}/tags`, { version: 1, tags: [], }); testServerRoute(server, `api/admin/tag-types`, { tagTypes: [], version: 1, }); testServerRoute(server, `api/admin/tags/simple`, { version: 1, tags: [], }); testServerRoute(server, `api/admin/strategies`, { version: 1, strategies: [ { displayName: 'Standard', name: 'default', editable: false, description: 'The standard strategy is strictly on / off for your entire userbase.', parameters: [], deprecated: false, }, { displayName: 'UserIDs', name: 'userWithId', editable: false, description: 'Enable the feature for a specific set of userIds.', parameters: [ { name: 'userIds', type: 'list', description: '', required: false, }, ], deprecated: false, }, ], }); }; const userHasPermissions = (permissions: Array) => { testServerRoute(server, 'api/admin/user', { user: { isAPI: false, id: 2, name: 'Test', email: 'test@getunleash.ai', imageUrl: 'https://gravatar.com/avatar/e55646b526ff342ff8b43721f0cbdd8e?size=42&default=retro', seenAt: '2022-11-29T08:21:52.581Z', loginAttempts: 0, createdAt: '2022-11-21T10:10:33.074Z', }, permissions, feedback: [], splash: {}, }); }; const userIsMemberOfProjects = (projects: string[]) => { userHasPermissions( projects.map((project) => ({ project, environment: 'irrelevant', permission: 'irrelevant', })), ); }; const featureEnvironments = ( feature: string, environments: Array<{ name: string; strategies: Array }>, ) => { testServerRoute(server, `/api/admin/projects/default/features/${feature}`, { environments: environments.map((env) => ({ name: env.name, enabled: false, type: 'production', sortOrder: 1, strategies: env.strategies.map((strategy) => ({ name: strategy, id: Math.random(), constraints: [], parameters: [], sortOrder: 1, })), })), name: feature, impressionData: false, description: '', project: 'default', stale: false, variants: [], createdAt: '2022-11-14T08:16:33.338Z', lastSeenAt: null, type: 'release', archived: false, children: [], dependencies: [], }); }; const UnleashUiSetup: FC<{ path: string; pathTemplate: string; children?: React.ReactNode; }> = ({ children, path, pathTemplate }) => ( new Map(), isVisible() { return true; }, }} > ); const strategiesAreDisplayed = async ( firstStrategy: string, secondStrategy: string, ) => { await screen.findByText(firstStrategy); await screen.findByText(secondStrategy); }; const getDeleteButtons = async () => { const removeMenus = screen.getAllByTestId(`STRATEGY_REMOVE_MENU_BTN`); const deleteButtons: HTMLElement[] = []; await Promise.all( removeMenus.map(async (menu) => { fireEvent.click(menu); const removeButton = await screen.findAllByTestId( 'STRATEGY_FORM_REMOVE_ID', ); deleteButtons.push(...removeButton); }), ); return deleteButtons; }; const deleteButtonsActiveInChangeRequestEnv = async () => { const deleteButtons = await getDeleteButtons(); expect(deleteButtons.length).toBe(3); // wait for change request config to be loaded await waitFor(() => { // production const productionStrategyDeleteButton = deleteButtons[1]; expect(productionStrategyDeleteButton).not.toHaveClass('Mui-disabled'); }); await waitFor(() => { // custom env const customEnvStrategyDeleteButton = deleteButtons[2]; expect(customEnvStrategyDeleteButton).toHaveClass('Mui-disabled'); }); }; const deleteButtonsInactiveInChangeRequestEnv = async () => { const deleteButtons = await getDeleteButtons(); expect(deleteButtons.length).toBe(3); // wait for change request config to be loaded await waitFor(() => { // production const productionStrategyDeleteButton = deleteButtons[1]; expect(productionStrategyDeleteButton).toHaveClass('Mui-disabled'); }); await waitFor(() => { // custom env const customEnvStrategyDeleteButton = deleteButtons[2]; expect(customEnvStrategyDeleteButton).toHaveClass('Mui-disabled'); }); }; const copyButtonsActiveInOtherEnv = async () => { const copyButtons = await screen.findAllByTestId('STRATEGY_FORM_COPY_ID'); expect(copyButtons.length).toBe(2); // production const productionStrategyCopyButton = copyButtons[0]; expect(productionStrategyCopyButton).toBeDisabled(); // custom env const customEnvStrategyCopyButton = copyButtons[1]; expect(customEnvStrategyCopyButton).not.toBeDisabled(); }; const openEnvironments = async (envNames: string[]) => { for (const env of envNames) { (await screen.findAllByText(env))[1].click(); } }; test('open mode + non-project member can perform basic change request actions', async () => { const project = 'default'; const featureName = 'test'; featureEnvironments(featureName, [ { name: 'development', strategies: [] }, { name: 'production', strategies: ['userWithId'] }, { name: 'custom', strategies: ['default'] }, ]); userIsMemberOfProjects([]); changeRequestsEnabledIn('production'); projectWithCollaborationMode('open'); uiConfigForEnterprise(); setupOtherRoutes(featureName); render( , ); await openEnvironments(['development', 'production', 'custom']); await strategiesAreDisplayed('UserIDs', 'Standard'); await deleteButtonsActiveInChangeRequestEnv(); await copyButtonsActiveInOtherEnv(); }); test('protected mode + project member can perform basic change request actions', async () => { const project = 'default'; const featureName = 'test'; featureEnvironments(featureName, [ { name: 'development', strategies: [] }, { name: 'production', strategies: ['userWithId'] }, { name: 'custom', strategies: ['default'] }, ]); userIsMemberOfProjects([project]); changeRequestsEnabledIn('production'); projectWithCollaborationMode('protected'); uiConfigForEnterprise(); setupOtherRoutes(featureName); render( , ); await openEnvironments(['development', 'production', 'custom']); await strategiesAreDisplayed('UserIDs', 'Standard'); await deleteButtonsActiveInChangeRequestEnv(); await copyButtonsActiveInOtherEnv(); }); test.skip('protected mode + non-project member cannot perform basic change request actions', async () => { const project = 'default'; const featureName = 'test'; featureEnvironments(featureName, [ { name: 'development', strategies: [] }, { name: 'production', strategies: ['userWithId'] }, { name: 'custom', strategies: ['default'] }, ]); userIsMemberOfProjects([]); changeRequestsEnabledIn('production'); projectWithCollaborationMode('protected'); uiConfigForEnterprise(); setupOtherRoutes(featureName); render( , ); await openEnvironments(['development', 'production', 'custom']); await strategiesAreDisplayed('UserIDs', 'Standard'); await deleteButtonsInactiveInChangeRequestEnv(); await copyButtonsActiveInOtherEnv(); });