mirror of
https://github.com/Unleash/unleash.git
synced 2025-12-09 20:04:11 +01:00
**Upgrade to React v18 for Unleash v6. Here's why I think it's a good time to do it:** - Command Bar project: We've begun work on the command bar project, and there's a fantastic library we want to use. However, it requires React v18 support. - Straightforward Upgrade: I took a look at the upgrade guide https://react.dev/blog/2022/03/08/react-18-upgrade-guide and it seems fairly straightforward. In fact, I was able to get React v18 running with minimal changes in just 10 minutes! - Dropping IE Support: React v18 no longer supports Internet Explorer (IE), which is no longer supported by Microsoft as of June 15, 2022. Upgrading to v18 in v6 would be a good way to align with this change. TS updates: * FC children has to be explicit: https://stackoverflow.com/questions/71788254/react-18-typescript-children-fc * forcing version 18 types in resolutions: https://sentry.io/answers/type-is-not-assignable-to-type-reactnode/ Test updates: * fixing SWR issue that we have always had but it manifests more in new React (https://github.com/vercel/swr/issues/2373) --------- Co-authored-by: kwasniew <kwasniewski.mateusz@gmail.com>
372 lines
9.5 KiB
TypeScript
372 lines
9.5 KiB
TypeScript
import { fireEvent, screen } from '@testing-library/react';
|
|
import { render } from 'utils/testRenderer';
|
|
import FeatureOverviewMetaData from './FeatureOverviewMetaData';
|
|
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
|
import { Route, Routes } from 'react-router-dom';
|
|
import type { IDependency, IFeatureToggle } from 'interfaces/featureToggle';
|
|
import ToastRenderer from 'component/common/ToastRenderer/ToastRenderer';
|
|
import userEvent from '@testing-library/user-event';
|
|
|
|
const server = testServerSetup();
|
|
|
|
const setupApi = () => {
|
|
testServerRoute(server, '/api/admin/ui-config', {
|
|
versionInfo: {
|
|
current: { oss: 'irrelevant', enterprise: 'some value' },
|
|
},
|
|
});
|
|
testServerRoute(
|
|
server,
|
|
'/api/admin/projects/default/features/feature/parents',
|
|
['some_parent'],
|
|
);
|
|
testServerRoute(
|
|
server,
|
|
'/api/admin/projects/default/features/feature/dependencies',
|
|
{},
|
|
'delete',
|
|
200,
|
|
);
|
|
testServerRoute(server, '/api/admin/projects/default/dependencies', false);
|
|
};
|
|
|
|
const setupOssWithExistingDependencies = () => {
|
|
testServerRoute(server, '/api/admin/ui-config', {
|
|
versionInfo: {
|
|
current: { oss: 'some value' },
|
|
},
|
|
});
|
|
testServerRoute(server, '/api/admin/projects/default/dependencies', true);
|
|
};
|
|
|
|
const setupChangeRequestApi = () => {
|
|
testServerRoute(
|
|
server,
|
|
'/api/admin/projects/default/change-requests/config',
|
|
[
|
|
{
|
|
environment: 'development',
|
|
type: 'development',
|
|
requiredApprovals: null,
|
|
changeRequestEnabled: true,
|
|
},
|
|
],
|
|
);
|
|
testServerRoute(
|
|
server,
|
|
'/api/admin/projects/default/change-requests/pending',
|
|
[],
|
|
);
|
|
testServerRoute(
|
|
server,
|
|
'/api/admin/projects/default/environments/development/change-requests',
|
|
{},
|
|
'post',
|
|
200,
|
|
);
|
|
};
|
|
|
|
const setupFeatureApi = (feature: IFeatureToggle) => {
|
|
testServerRoute(
|
|
server,
|
|
'/api/admin/projects/default/features/feature',
|
|
feature,
|
|
);
|
|
};
|
|
|
|
beforeEach(() => {
|
|
setupApi();
|
|
});
|
|
|
|
const route = '/projects/default/features/feature';
|
|
|
|
test('show dependency dialogue', async () => {
|
|
setupFeatureApi(feature);
|
|
render(
|
|
<Routes>
|
|
<Route
|
|
path={'/projects/:projectId/features/:featureId'}
|
|
element={<FeatureOverviewMetaData />}
|
|
/>
|
|
</Routes>,
|
|
{
|
|
route,
|
|
permissions: [
|
|
{ permission: 'UPDATE_FEATURE_DEPENDENCY', project: 'default' },
|
|
],
|
|
},
|
|
);
|
|
|
|
const addParentButton = await screen.findByText('Add parent feature');
|
|
|
|
addParentButton.click();
|
|
|
|
await screen.findByText('Add parent feature dependency');
|
|
});
|
|
|
|
test('show dependency dialogue for OSS with dependencies', async () => {
|
|
setupOssWithExistingDependencies();
|
|
setupFeatureApi({
|
|
name: 'feature',
|
|
project: 'default',
|
|
dependencies: [] as Array<{ feature: string }>,
|
|
children: [] as string[],
|
|
} as IFeatureToggle);
|
|
render(
|
|
<Routes>
|
|
<Route
|
|
path={'/projects/:projectId/features/:featureId'}
|
|
element={<FeatureOverviewMetaData />}
|
|
/>
|
|
</Routes>,
|
|
{
|
|
route,
|
|
permissions: [
|
|
{ permission: 'UPDATE_FEATURE_DEPENDENCY', project: 'default' },
|
|
],
|
|
},
|
|
);
|
|
|
|
const addParentButton = await screen.findByText('Add parent feature');
|
|
|
|
addParentButton.click();
|
|
|
|
await screen.findByText('Add parent feature dependency');
|
|
});
|
|
|
|
test('show child', async () => {
|
|
setupFeatureApi({
|
|
name: 'feature',
|
|
project: 'default',
|
|
dependencies: [] as Array<{ feature: string }>,
|
|
children: ['some_child'],
|
|
} as IFeatureToggle);
|
|
render(
|
|
<Routes>
|
|
<Route
|
|
path={'/projects/:projectId/features/:featureId'}
|
|
element={<FeatureOverviewMetaData />}
|
|
/>
|
|
</Routes>,
|
|
{ route },
|
|
);
|
|
|
|
await screen.findByText('Children:');
|
|
await screen.findByText('1 feature');
|
|
});
|
|
|
|
test('show children', async () => {
|
|
setupFeatureApi({
|
|
name: 'feature',
|
|
project: 'default',
|
|
dependencies: [] as Array<{ feature: string }>,
|
|
children: ['some_child', 'some_other_child'],
|
|
} as IFeatureToggle);
|
|
render(
|
|
<Routes>
|
|
<Route
|
|
path={'/projects/:projectId/features/:featureId'}
|
|
element={<FeatureOverviewMetaData />}
|
|
/>
|
|
</Routes>,
|
|
{ route },
|
|
);
|
|
|
|
await screen.findByText('Children:');
|
|
await screen.findByText('2 features');
|
|
});
|
|
|
|
const feature = {
|
|
name: 'feature',
|
|
project: 'default',
|
|
dependencies: [] as IDependency[],
|
|
children: [] as string[],
|
|
} as IFeatureToggle;
|
|
|
|
test('delete dependency', async () => {
|
|
setupFeatureApi({
|
|
...feature,
|
|
dependencies: [{ feature: 'some_parent' }],
|
|
});
|
|
render(
|
|
<>
|
|
<ToastRenderer />
|
|
<Routes>
|
|
<Route
|
|
path={'/projects/:projectId/features/:featureId'}
|
|
element={<FeatureOverviewMetaData />}
|
|
/>
|
|
</Routes>
|
|
</>,
|
|
{
|
|
route,
|
|
permissions: [
|
|
{ permission: 'UPDATE_FEATURE_DEPENDENCY', project: 'default' },
|
|
],
|
|
},
|
|
);
|
|
|
|
await screen.findByText('Dependency:');
|
|
await screen.findByText('some_parent');
|
|
|
|
const actionsButton = screen.getByRole('button', {
|
|
name: /Dependency actions/i,
|
|
});
|
|
fireEvent.click(actionsButton);
|
|
|
|
const deleteButton = await screen.findByText('Delete');
|
|
fireEvent.click(deleteButton);
|
|
|
|
await screen.findByText('Dependency removed');
|
|
});
|
|
|
|
test('delete dependency with change request', async () => {
|
|
setupChangeRequestApi();
|
|
setupFeatureApi({
|
|
...feature,
|
|
dependencies: [{ feature: 'some_parent' }],
|
|
});
|
|
render(
|
|
<>
|
|
<ToastRenderer />
|
|
<Routes>
|
|
<Route
|
|
path={'/projects/:projectId/features/:featureId'}
|
|
element={<FeatureOverviewMetaData />}
|
|
/>
|
|
</Routes>
|
|
</>,
|
|
{
|
|
route,
|
|
permissions: [
|
|
/* deliberately no permissions */
|
|
],
|
|
},
|
|
);
|
|
|
|
await screen.findByText('Dependency:');
|
|
await screen.findByText('some_parent');
|
|
|
|
const actionsButton = await screen.findByRole('button', {
|
|
name: /Dependency actions/i,
|
|
});
|
|
fireEvent.click(actionsButton);
|
|
|
|
const deleteButton = await screen.findByText('Delete');
|
|
fireEvent.click(deleteButton);
|
|
|
|
await screen.findByText('Change added to a draft');
|
|
});
|
|
|
|
test('edit dependency', async () => {
|
|
setupFeatureApi({
|
|
...feature,
|
|
dependencies: [{ feature: 'some_parent', enabled: false }],
|
|
});
|
|
render(
|
|
<Routes>
|
|
<Route
|
|
path={'/projects/:projectId/features/:featureId'}
|
|
element={<FeatureOverviewMetaData />}
|
|
/>
|
|
</Routes>,
|
|
{
|
|
route,
|
|
permissions: [
|
|
{ permission: 'UPDATE_FEATURE_DEPENDENCY', project: 'default' },
|
|
],
|
|
},
|
|
);
|
|
|
|
await screen.findByText('Dependency:');
|
|
await screen.findByText('some_parent');
|
|
await screen.findByText('Dependency value:');
|
|
await screen.findByText('disabled');
|
|
|
|
const actionsButton = await screen.findByRole('button', {
|
|
name: /Dependency actions/i,
|
|
});
|
|
fireEvent.click(actionsButton);
|
|
|
|
const editButton = await screen.findByText('Edit');
|
|
fireEvent.click(editButton);
|
|
|
|
await screen.findByText('Add parent feature dependency');
|
|
});
|
|
|
|
test('show variant dependencies', async () => {
|
|
setupFeatureApi({
|
|
...feature,
|
|
dependencies: [
|
|
{
|
|
feature: 'some_parent',
|
|
enabled: true,
|
|
variants: ['variantA', 'variantB'],
|
|
},
|
|
],
|
|
});
|
|
render(
|
|
<Routes>
|
|
<Route
|
|
path={'/projects/:projectId/features/:featureId'}
|
|
element={<FeatureOverviewMetaData />}
|
|
/>
|
|
</Routes>,
|
|
{ route },
|
|
);
|
|
|
|
const variants = await screen.findByText('2 variants');
|
|
|
|
await userEvent.hover(variants);
|
|
|
|
await screen.findByText('variantA');
|
|
await screen.findByText('variantB');
|
|
});
|
|
|
|
test('show variant dependency', async () => {
|
|
setupFeatureApi({
|
|
...feature,
|
|
dependencies: [
|
|
{
|
|
feature: 'some_parent',
|
|
enabled: true,
|
|
variants: ['variantA'],
|
|
},
|
|
],
|
|
});
|
|
render(
|
|
<Routes>
|
|
<Route
|
|
path={'/projects/:projectId/features/:featureId'}
|
|
element={<FeatureOverviewMetaData />}
|
|
/>
|
|
</Routes>,
|
|
{ route },
|
|
);
|
|
|
|
await screen.findByText('variantA');
|
|
});
|
|
|
|
test('show disabled dependency', async () => {
|
|
setupFeatureApi({
|
|
...feature,
|
|
dependencies: [
|
|
{
|
|
feature: 'some_parent',
|
|
enabled: false,
|
|
},
|
|
],
|
|
});
|
|
render(
|
|
<Routes>
|
|
<Route
|
|
path={'/projects/:projectId/features/:featureId'}
|
|
element={<FeatureOverviewMetaData />}
|
|
/>
|
|
</Routes>,
|
|
{ route },
|
|
);
|
|
|
|
await screen.findByText('disabled');
|
|
});
|