1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-27 01:19:00 +02:00
unleash.unleash/frontend/src/component/changeRequest/ChangeRequestPermissions.test.tsx
Nuno Góis 38bd50dc8a
refactor: introduce a highlight reusable component (#8643)
Follow-up to: https://github.com/Unleash/unleash/pull/8642

Introduces a reusable `Highlight` component that leverages the Context
API pattern, enabling highlight effects to be triggered from anywhere in
the application.

This update refactors the existing highlight effect in the event
timeline to use the new Highlight component and extends the
functionality to include the Unleash AI experiment, triggered by its
entry in the "New in Unleash" section.
2024-11-05 09:21:19 +00:00

383 lines
12 KiB
TypeScript

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<IPermission>) => {
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<string> }>,
) => {
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 }) => (
<SWRConfig
value={{
provider: () => new Map(),
isVisible() {
return true;
},
}}
>
<UIProviderContainer>
<AccessProvider>
<MemoryRouter initialEntries={[path]}>
<ThemeProvider>
<AnnouncerProvider>
<StickyProvider>
<HighlightProvider>
<Routes>
<Route
path={pathTemplate}
element={children}
/>
</Routes>
</HighlightProvider>
</StickyProvider>
</AnnouncerProvider>
</ThemeProvider>
</MemoryRouter>
</AccessProvider>
</UIProviderContainer>
</SWRConfig>
);
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(
<UnleashUiSetup
pathTemplate='/projects/:projectId/features/:featureId/*'
path={`/projects/${project}/features/${featureName}`}
>
<FeatureView />
</UnleashUiSetup>,
);
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(
<UnleashUiSetup
pathTemplate='/projects/:projectId/features/:featureId/*'
path={`/projects/${project}/features/${featureName}`}
>
<FeatureView />
</UnleashUiSetup>,
);
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(
<UnleashUiSetup
pathTemplate='/projects/:projectId/features/:featureId/*'
path={`/projects/${project}/features/${featureName}`}
>
<FeatureView />
</UnleashUiSetup>,
);
await openEnvironments(['development', 'production', 'custom']);
await strategiesAreDisplayed('UserIDs', 'Standard');
await deleteButtonsInactiveInChangeRequestEnv();
await copyButtonsActiveInOtherEnv();
});