-
Notifications
You must be signed in to change notification settings - Fork 107
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add workflow actions impl * Resolve comments * Remove unnecessary mock fn
- Loading branch information
1 parent
c7c4b1b
commit 944d9a6
Showing
16 changed files
with
359 additions
and
98 deletions.
There are no files selected for viewing
14 changes: 12 additions & 2 deletions
14
src/views/workflow-actions/__fixtures__/workflow-actions-config.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,30 @@ | ||
import { MdHighlightOff, MdPowerSettingsNew } from 'react-icons/md'; | ||
|
||
import { type CancelWorkflowResponse } from '@/route-handlers/cancel-workflow/cancel-workflow.types'; | ||
import { type TerminateWorkflowResponse } from '@/route-handlers/terminate-workflow/terminate-workflow.types'; | ||
|
||
import { type WorkflowAction } from '../workflow-actions.types'; | ||
|
||
export const mockWorkflowActionsConfig = [ | ||
export const mockWorkflowActionsConfig: [ | ||
WorkflowAction<CancelWorkflowResponse>, | ||
WorkflowAction<TerminateWorkflowResponse>, | ||
] = [ | ||
{ | ||
id: 'cancel', | ||
label: 'Mock cancel', | ||
subtitle: 'Mock cancel a workflow execution', | ||
icon: MdHighlightOff, | ||
getIsEnabled: () => true, | ||
apiRoute: 'cancel', | ||
getSuccessMessage: () => 'Mock cancel notification', | ||
}, | ||
{ | ||
id: 'terminate', | ||
label: 'Mock terminate', | ||
subtitle: 'Mock terminate a workflow execution', | ||
icon: MdPowerSettingsNew, | ||
getIsEnabled: () => false, | ||
apiRoute: 'terminate', | ||
getSuccessMessage: () => 'Mock terminate notification', | ||
}, | ||
] as const satisfies Array<WorkflowAction>; | ||
] as const; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
...-actions/workflow-actions-modal-content/__tests__/workflow-actions-modal-content.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { HttpResponse } from 'msw'; | ||
|
||
import { render, screen, userEvent } from '@/test-utils/rtl'; | ||
|
||
import { type CancelWorkflowResponse } from '@/route-handlers/cancel-workflow/cancel-workflow.types'; | ||
import { mockWorkflowDetailsParams } from '@/views/workflow-page/__fixtures__/workflow-details-params'; | ||
|
||
import { mockWorkflowActionsConfig } from '../../__fixtures__/workflow-actions-config'; | ||
import WorkflowActionsModalContent from '../workflow-actions-modal-content'; | ||
|
||
const mockEnqueue = jest.fn(); | ||
const mockDequeue = jest.fn(); | ||
jest.mock('baseui/snackbar', () => ({ | ||
...jest.requireActual('baseui/snackbar'), | ||
useSnackbar: () => ({ | ||
enqueue: mockEnqueue, | ||
dequeue: mockDequeue, | ||
}), | ||
})); | ||
|
||
describe(WorkflowActionsModalContent.name, () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('renders the modal content as expected', async () => { | ||
setup({}); | ||
|
||
expect(await screen.findAllByText('Mock cancel workflow')).toHaveLength(2); | ||
expect( | ||
screen.getByText('Mock cancel a workflow execution') | ||
).toBeInTheDocument(); | ||
expect( | ||
screen.getByRole('button', { name: 'Mock cancel workflow' }) | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it('calls onCloseModal when the Go Back button is clicked', async () => { | ||
const { user, mockOnClose } = setup({}); | ||
|
||
const goBackButton = await screen.findByText('Go back'); | ||
await user.click(goBackButton); | ||
|
||
expect(mockOnClose).toHaveBeenCalled(); | ||
}); | ||
|
||
it('calls mockCancelWorkflow, sends toast, and closes modal when the action button is clicked', async () => { | ||
const { user, mockOnClose } = setup({}); | ||
|
||
const cancelButton = await screen.findByRole('button', { | ||
name: 'Mock cancel workflow', | ||
}); | ||
await user.click(cancelButton); | ||
|
||
expect(mockEnqueue).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
message: 'Mock cancel notification', | ||
}) | ||
); | ||
expect(mockOnClose).toHaveBeenCalled(); | ||
}); | ||
|
||
it('Displays banner when the action button is clicked and action fails', async () => { | ||
const { user, mockOnClose } = setup({ error: true }); | ||
|
||
const cancelButton = await screen.findByRole('button', { | ||
name: 'Mock cancel workflow', | ||
}); | ||
await user.click(cancelButton); | ||
|
||
expect( | ||
await screen.findByText('Failed to cancel workflow') | ||
).toBeInTheDocument(); | ||
expect(mockOnClose).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
function setup({ error }: { error?: boolean }) { | ||
const user = userEvent.setup(); | ||
const mockOnClose = jest.fn(); | ||
|
||
render( | ||
<WorkflowActionsModalContent | ||
action={mockWorkflowActionsConfig[0]} | ||
params={{ ...mockWorkflowDetailsParams }} | ||
onCloseModal={mockOnClose} | ||
/>, | ||
{ | ||
endpointsMocks: [ | ||
{ | ||
path: '/api/domains/:domain/:cluster/workflows/:workflowId/:runId/cancel', | ||
httpMethod: 'POST', | ||
mockOnce: false, | ||
httpResolver: () => { | ||
if (error) { | ||
return HttpResponse.json( | ||
{ message: 'Failed to cancel workflow' }, | ||
{ status: 500 } | ||
); | ||
} | ||
return HttpResponse.json({} satisfies CancelWorkflowResponse); | ||
}, | ||
}, | ||
], | ||
} | ||
); | ||
|
||
return { user, mockOnClose }; | ||
} |
27 changes: 27 additions & 0 deletions
27
.../workflow-actions/workflow-actions-modal-content/workflow-actions-modal-content.styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { type Theme, withStyle } from 'baseui'; | ||
import { type BannerOverrides } from 'baseui/banner'; | ||
import { ModalBody, ModalFooter, ModalHeader } from 'baseui/modal'; | ||
|
||
export const styled = { | ||
ModalHeader: withStyle(ModalHeader, ({ $theme }: { $theme: Theme }) => ({ | ||
marginTop: $theme.sizing.scale850, | ||
})), | ||
ModalBody: withStyle(ModalBody, ({ $theme }: { $theme: Theme }) => ({ | ||
marginBottom: $theme.sizing.scale800, | ||
})), | ||
ModalFooter: withStyle(ModalFooter, { | ||
display: 'flex', | ||
justifyContent: 'space-between', | ||
}), | ||
}; | ||
|
||
export const overrides = { | ||
banner: { | ||
Root: { | ||
style: { | ||
marginLeft: 0, | ||
marginRight: 0, | ||
}, | ||
}, | ||
} satisfies BannerOverrides, | ||
}; |
103 changes: 103 additions & 0 deletions
103
src/views/workflow-actions/workflow-actions-modal-content/workflow-actions-modal-content.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { useMutation, useQueryClient } from '@tanstack/react-query'; | ||
import { Banner, HIERARCHY, KIND as BANNER_KIND } from 'baseui/banner'; | ||
import { KIND as BUTTON_KIND, SIZE } from 'baseui/button'; | ||
import { ModalButton } from 'baseui/modal'; | ||
import { useSnackbar } from 'baseui/snackbar'; | ||
import { MdCheckCircle, MdErrorOutline } from 'react-icons/md'; | ||
|
||
import request from '@/utils/request'; | ||
import { type RequestError } from '@/utils/request/request-error'; | ||
|
||
import { type WorkflowActionInputParams } from '../workflow-actions.types'; | ||
|
||
import { overrides, styled } from './workflow-actions-modal-content.styles'; | ||
import { type Props } from './workflow-actions-modal-content.types'; | ||
|
||
export default function WorkflowActionsModalContent<R>({ | ||
action, | ||
params, | ||
onCloseModal, | ||
}: Props<R>) { | ||
const queryClient = useQueryClient(); | ||
const { enqueue, dequeue } = useSnackbar(); | ||
const { mutate, isPending, error } = useMutation< | ||
R, | ||
RequestError, | ||
WorkflowActionInputParams | ||
>( | ||
{ | ||
mutationFn: ({ | ||
domain, | ||
cluster, | ||
workflowId, | ||
runId, | ||
}: WorkflowActionInputParams) => | ||
request( | ||
`/api/domains/${domain}/${cluster}/workflows/${workflowId}/${runId}/${action.apiRoute}`, | ||
{ | ||
method: 'POST', | ||
body: JSON.stringify({ | ||
// TODO: pass the input here when implementing extended workflow actions | ||
}), | ||
} | ||
).then((res) => res.json() as R), | ||
onSuccess: (result, params) => { | ||
const { | ||
// TODO: input, | ||
...workflowDetailsParams | ||
} = params; | ||
|
||
queryClient.invalidateQueries({ | ||
queryKey: ['describe_workflow', workflowDetailsParams], | ||
}); | ||
|
||
onCloseModal(); | ||
enqueue({ | ||
message: action.getSuccessMessage(result, params), | ||
startEnhancer: MdCheckCircle, | ||
actionMessage: 'OK', | ||
actionOnClick: () => dequeue(), | ||
}); | ||
}, | ||
}, | ||
queryClient | ||
); | ||
|
||
return ( | ||
<> | ||
<styled.ModalHeader>{action.label} workflow</styled.ModalHeader> | ||
<styled.ModalBody> | ||
{action.subtitle} | ||
{error && ( | ||
<Banner | ||
hierarchy={HIERARCHY.low} | ||
kind={BANNER_KIND.negative} | ||
overrides={overrides.banner} | ||
artwork={{ | ||
icon: MdErrorOutline, | ||
}} | ||
> | ||
{error.message} | ||
</Banner> | ||
)} | ||
</styled.ModalBody> | ||
<styled.ModalFooter> | ||
<ModalButton | ||
size={SIZE.compact} | ||
kind={BUTTON_KIND.secondary} | ||
onClick={onCloseModal} | ||
> | ||
Go back | ||
</ModalButton> | ||
<ModalButton | ||
size={SIZE.compact} | ||
kind={BUTTON_KIND.primary} | ||
onClick={() => mutate(params)} | ||
isLoading={isPending} | ||
> | ||
{action.label} workflow | ||
</ModalButton> | ||
</styled.ModalFooter> | ||
</> | ||
); | ||
} |
10 changes: 10 additions & 0 deletions
10
...s/workflow-actions/workflow-actions-modal-content/workflow-actions-modal-content.types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { | ||
type WorkflowAction, | ||
type WorkflowActionInputParams, | ||
} from '../workflow-actions.types'; | ||
|
||
export type Props<R> = { | ||
action: WorkflowAction<R>; | ||
params: WorkflowActionInputParams; | ||
onCloseModal: () => void; | ||
}; |
Oops, something went wrong.