diff --git a/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/FeatureChange.test.tsx b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/FeatureChange.test.tsx new file mode 100644 index 000000000000..af3b4e904be5 --- /dev/null +++ b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/FeatureChange.test.tsx @@ -0,0 +1,135 @@ +import React from 'react'; +import { render } from 'utils/testRenderer'; +import { screen } from '@testing-library/react'; +import { FeatureChange } from './FeatureChange'; +import { + ChangeRequestState, + IChangeRequestFeature, + IFeatureChange, +} from 'component/changeRequest/changeRequest.types'; + +describe('Schedule conflicts', () => { + const change = { + id: 15, + action: 'deleteStrategy' as const, + payload: { + id: 'b3ac8595-8ad3-419e-aa18-4d82f2b6bc4c', + name: 'flexibleRollout', + }, + createdAt: new Date(), + createdBy: { + id: 1, + username: 'admin', + imageUrl: '', + }, + scheduleConflicts: { + changeRequests: [ + { + id: 73, + }, + { + id: 80, + title: 'Adjust rollout percentage', + }, + ], + }, + }; + + const feature = (change: IFeatureChange): IChangeRequestFeature => ({ + name: 'conflict-test', + changes: [change], + }); + + const changeRequest = + (feature: IChangeRequestFeature) => (state: ChangeRequestState) => ({ + id: 1, + state, + title: '', + project: 'default', + environment: 'default', + minApprovals: 1, + createdBy: { id: 1, username: 'user1', imageUrl: '' }, + createdAt: new Date(), + features: [feature], + segments: [], + approvals: [], + rejections: [], + comments: [], + }); + + it.each(['Draft', 'Scheduled', 'In review', 'Approved'])( + 'should show schedule conflicts (when they exist) for change request in the %s state', + async (changeRequestState) => { + const flag = feature(change); + render( + , + ); + + const alert = await screen.findByRole('alert'); + + expect( + alert.textContent!.startsWith('Potential conflict'), + ).toBeTruthy(); + + const links = await screen.findAllByRole('link'); + + expect(links).toHaveLength( + change.scheduleConflicts.changeRequests.length, + ); + + const [link1, link2] = links; + + expect(link1).toHaveTextContent('#73'); + expect(link1).toHaveAccessibleDescription('Change request 73'); + expect(link1).toHaveAttribute( + 'href', + `/projects/default/change-requests/73`, + ); + + expect(link2).toHaveTextContent('#80 (Adjust rollout percentage)'); + expect(link2).toHaveAccessibleDescription('Change request 80'); + expect(link2).toHaveAttribute( + 'href', + `/projects/default/change-requests/80`, + ); + }, + ); + + it.each(['Draft', 'Scheduled', 'In review', 'Approved'])( + 'should not show schedule conflicts when they do not exist for change request in the %s state', + async (changeRequestState) => { + const { scheduleConflicts, ...changeWithNoScheduleConflicts } = + change; + + const flag = feature(changeWithNoScheduleConflicts); + + render( + , + ); + + const links = screen.queryByRole('link'); + + expect(links).toBe(null); + + const alert = screen.queryByRole('alert'); + + expect(alert).toBe(null); + }, + ); +}); diff --git a/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/FeatureChange.tsx b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/FeatureChange.tsx index eab19b99afce..1cc1800d42c8 100644 --- a/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/FeatureChange.tsx +++ b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/FeatureChange.tsx @@ -13,6 +13,7 @@ import { VariantPatch } from './VariantPatch/VariantPatch'; import { EnvironmentStrategyExecutionOrder } from './EnvironmentStrategyExecutionOrder/EnvironmentStrategyExecutionOrder'; import { ArchiveFeatureChange } from './ArchiveFeatureChange'; import { DependencyChange } from './DependencyChange'; +import { Link } from 'react-router-dom'; const StyledSingleChangeBox = styled(Box, { shouldForwardProp: (prop: string) => !prop.startsWith('$'), @@ -56,6 +57,15 @@ const StyledAlert = styled(Alert)(({ theme }) => ({ }, })); +const InlineList = styled('ul')(({ theme }) => ({ + display: 'inline', + padding: 0, + li: { display: 'inline' }, + 'li + li::before': { + content: '", "', + }, +})); + export const FeatureChange: FC<{ actions: ReactNode; index: number; @@ -71,9 +81,12 @@ export const FeatureChange: FC<{ return ( + + Potential conflict! This change would + create conflicts with the following scheduled change + request(s):{' '} + + {( + change.scheduleConflicts ?? { + changeRequests: [], + } + ).changeRequests.map(({ id, title }) => { + const text = title + ? `#${id} (${title})` + : `#${id}`; + return ( +
  • + + {text} + +
  • + ); + })} + . +
    + + } + /> + ({ padding: theme.spacing(3) })}> {(change.action === 'addDependency' || change.action === 'deleteDependency') && ( diff --git a/frontend/src/component/changeRequest/changeRequest.types.ts b/frontend/src/component/changeRequest/changeRequest.types.ts index e79a42fddb0c..0a077282e830 100644 --- a/frontend/src/component/changeRequest/changeRequest.types.ts +++ b/frontend/src/component/changeRequest/changeRequest.types.ts @@ -66,6 +66,9 @@ export interface IChangeRequestChangeBase { conflict?: string; createdBy?: Pick; createdAt?: Date; + scheduleConflicts?: { + changeRequests: { id: number; title?: string }[]; + }; } export type ChangeRequestState =