diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.test.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.test.tsx new file mode 100644 index 0000000000..4205282a15 --- /dev/null +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.test.tsx @@ -0,0 +1,289 @@ +import { fireEvent, screen, waitFor, within } from '@testing-library/react'; +import { testServerRoute, testServerSetup } from 'utils/testServer'; +import { ChangeRequestState, IChangeRequest } from '../changeRequest.types'; +import { render } from 'utils/testRenderer'; +import { ChangeRequestOverview } from './ChangeRequestOverview'; +import { + ADMIN, + APPLY_CHANGE_REQUEST, +} from 'component/providers/AccessProvider/permissions'; +import { Route, Routes } from 'react-router-dom'; + +const server = testServerSetup(); +const mockChangeRequest = ( + featureName: string, + state: ChangeRequestState, +): IChangeRequest => { + const result: IChangeRequest = { + id: 1, + environment: 'production', + state: state, + minApprovals: 1, + project: 'default', + createdBy: { + id: 1, + username: 'admin', + imageUrl: + 'https://gravatar.com/avatar/21232f297a57a5a743894a0e4a801fc3?size=42&default=retro', + }, + createdAt: new Date('2022-12-02T09:19:12.242Z'), + segments: [], + title: '', + features: [ + { + name: featureName, + changes: [ + { + id: 292, + action: 'addStrategy', + payload: { + name: 'default', + segments: [], + parameters: {}, + constraints: [], + }, + createdAt: new Date('2022-12-02T09:19:12.245Z'), + createdBy: { + id: 1, + username: 'admin', + imageUrl: + 'https://gravatar.com/avatar/21232f297a57a5a743894a0e4a801fc3?size=42&default=retro', + }, + }, + ], + }, + ], + approvals: [], + rejections: [], + comments: [], + }; + + if (state === 'Scheduled') { + result.schedule = { + scheduledAt: '2022-12-02T09:19:12.242Z', + status: 'pending', + }; + } + + return result; +}; +const pendingChangeRequest = (changeRequest: IChangeRequest) => + testServerRoute( + server, + '/api/admin/projects/default/change-requests/pending', + [changeRequest], + ); + +const changeRequest = (changeRequest: IChangeRequest) => + testServerRoute( + server, + '/api/admin/projects/default/change-requests/1', + changeRequest, + 'get', + ); + +const updateChangeRequestState = () => + testServerRoute( + server, + '/api/admin/projects/default/change-requests/1/state', + {}, + 'post', + ); +const changeRequestConfig = () => + testServerRoute( + server, + '/api/admin/projects/default/change-requests/config', + [ + { + environment: 'development', + type: 'development', + changeRequestEnabled: false, + }, + { + environment: 'production', + type: 'production', + changeRequestEnabled: true, + }, + ], + 'get', + ); + +const setupChangeRequest = (featureName: string, state: ChangeRequestState) => { + pendingChangeRequest(mockChangeRequest(featureName, state)); + changeRequest(mockChangeRequest(featureName, state)); +}; + +const uiConfig = () => { + testServerRoute(server, '/api/admin/ui-config', { + versionInfo: { + current: { oss: 'version', enterprise: 'version' }, + }, + flags: { + scheduledConfigurationChanges: true, + }, + }); +}; + +const user = () => { + testServerRoute(server, '/api/admin/user', { + user: { + isAPI: false, + id: 17, + name: 'Some User', + email: 'user@example.com', + imageUrl: + 'https://gravatar.com/avatar/8aa1132e102345f8c79322340e15340?size=42&default=retro', + seenAt: '2022-11-28T14:55:18.982Z', + loginAttempts: 0, + createdAt: '2022-11-23T13:31:17.061Z', + }, + permissions: [{ permission: ADMIN }], + feedback: [], + splash: {}, + }); +}; + +const setupHttpRoutes = () => { + uiConfig(); + changeRequestConfig(); + user(); + updateChangeRequestState(); +}; + +beforeEach(() => { + setupHttpRoutes(); +}); + +const Component = () => { + return ( + <> + + } + /> + + + ); +}; + +const featureName = 'feature1'; + +test('should allow scheduling of approved change request and show the schedule dialog', async () => { + setupChangeRequest(featureName, 'Approved'); + + render(, { + route: '/projects/default/change-requests/1', + permissions: [ + { + permission: APPLY_CHANGE_REQUEST, + project: 'default', + environment: 'production', + }, + ], + }); + + const applyOrScheduleButton = await screen.findByText( + 'Apply or schedule changes', + ); + await waitFor(() => expect(applyOrScheduleButton).toBeEnabled(), { + timeout: 3000, + }); + + fireEvent.click(applyOrScheduleButton); + + const scheduleChangesButton = await screen.findByRole('menuitem', { + name: 'Schedule changes', + }); + + fireEvent.click(scheduleChangesButton); + + await screen.findByRole('dialog', { name: 'Schedule changes' }); +}); + +test('should show a reschedule dialog when change request is scheduled and update schedule is selected', async () => { + setupChangeRequest(featureName, 'Scheduled'); + render(, { + route: '/projects/default/change-requests/1', + permissions: [ + { + permission: APPLY_CHANGE_REQUEST, + project: 'default', + environment: 'production', + }, + ], + }); + + const applyOrScheduleButton = await screen.findByText( + 'Apply or schedule changes', + ); + await waitFor(() => expect(applyOrScheduleButton).toBeEnabled(), { + timeout: 3000, + }); + fireEvent.click(applyOrScheduleButton); + + const scheduleChangesButton = await screen.findByRole('menuitem', { + name: 'Update schedule', + }); + + fireEvent.click(scheduleChangesButton); + + await screen.findByRole('dialog', { name: 'Update schedule' }); +}); + +test('should show an apply dialog when change request is scheduled and apply is selected', async () => { + setupChangeRequest(featureName, 'Scheduled'); + + render(, { + route: '/projects/default/change-requests/1', + permissions: [ + { + permission: APPLY_CHANGE_REQUEST, + project: 'default', + environment: 'production', + }, + ], + }); + + const applyOrScheduleButton = await screen.findByText( + 'Apply or schedule changes', + ); + await waitFor(() => expect(applyOrScheduleButton).toBeEnabled(), { + timeout: 3000, + }); + fireEvent.click(applyOrScheduleButton); + + const applyChangesButton = await screen.findByRole('menuitem', { + name: 'Apply changes', + }); + fireEvent.click(applyChangesButton); + + await screen.findByRole('dialog', { name: 'Apply changes' }); +}); + +test('should show a reject dialog when change request is scheduled and Reject Changes button is clicked', async () => { + setupChangeRequest(featureName, 'Scheduled'); + + render(, { + route: '/projects/default/change-requests/1', + permissions: [{ permission: ADMIN }], + }); + + const applyOrScheduleButton = await screen.findByText( + 'Apply or schedule changes', + ); + await waitFor(() => expect(applyOrScheduleButton).toBeEnabled(), { + timeout: 3000, + }); + + const buttons = await screen.findAllByRole('button'); + const rejectChangesButton = buttons[buttons.length - 1]; + expect( + within(rejectChangesButton).getByText('Reject changes'), + ).toBeInTheDocument(); + fireEvent.click(rejectChangesButton); + + await screen.findByRole('dialog', { + name: 'Reject changes', + }); +}); diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx index 43aac2d721..c814ff3264 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx @@ -186,13 +186,13 @@ export const ChangeRequestOverview: FC = () => { comment, }); setShowRejectDialog(false); - refetchChangeRequest(); - refetchChangeRequestOpen(); setToastData({ type: 'success', title: 'Success', text: 'Changes rejected', }); + refetchChangeRequest(); + refetchChangeRequestOpen(); } catch (error: unknown) { setToastApiError(formatUnknownError(error)); } diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/MultiActionButton/MultiActionButton.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/MultiActionButton/MultiActionButton.tsx index ba86906706..a414a29321 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/MultiActionButton/MultiActionButton.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/MultiActionButton/MultiActionButton.tsx @@ -102,7 +102,10 @@ export const MultiActionButton: FC<{ > {actions.map( ({ label, onSelect, icon }) => ( - + {icon} diff --git a/frontend/src/component/changeRequest/UpdateCount.tsx b/frontend/src/component/changeRequest/UpdateCount.tsx index 696d71cb98..408a310615 100644 --- a/frontend/src/component/changeRequest/UpdateCount.tsx +++ b/frontend/src/component/changeRequest/UpdateCount.tsx @@ -6,7 +6,7 @@ export const UpdateCount: FC<{ featuresCount: number; segmentsCount: number; }> = ({ featuresCount, segmentsCount }) => ( - + { formatApiPath(`api/admin/projects/${projectId}/change-requests/config`), fetcher, ); - return { data: data || [], loading: !error && !data,