-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
443 additions
and
34 deletions.
There are no files selected for viewing
129 changes: 129 additions & 0 deletions
129
src/components/DeleteEntityPanel/DeleteEntityPanel.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,129 @@ | ||
import { screen } from "@testing-library/react"; | ||
import userEvent from "@testing-library/user-event"; | ||
import { vi } from "vitest"; | ||
|
||
import { renderComponent } from "test/utils"; | ||
|
||
import DeleteEntityPanel from "./DeleteEntityPanel"; | ||
import { Label } from "./types"; | ||
|
||
test("should render correctly for 1 entity", async () => { | ||
renderComponent( | ||
<DeleteEntityPanel | ||
entity="test" | ||
count={1} | ||
close={vi.fn()} | ||
onDelete={vi.fn()} | ||
isDeletePending={false} | ||
/>, | ||
); | ||
expect(screen.getByText("Delete 1 test")).toBeInTheDocument(); | ||
expect( | ||
screen.getByText("Are you sure you want to delete 1 test?", { | ||
exact: false, | ||
}), | ||
).toBeInTheDocument(); | ||
expect( | ||
screen.getByText( | ||
"The deletion of tests is irreversible and might adversely affect your system.", | ||
{ exact: false }, | ||
), | ||
).toBeInTheDocument(); | ||
const textBoxes = screen.getAllByRole("textbox"); | ||
expect(textBoxes).toHaveLength(1); | ||
expect(screen.getByRole("button", { name: Label.DELETE })).toBeDisabled(); | ||
expect( | ||
screen.getByRole("button", { name: Label.CANCEL }), | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
test("should render correctly for multiple entities", async () => { | ||
renderComponent( | ||
<DeleteEntityPanel | ||
entity="test" | ||
count={2} | ||
close={vi.fn()} | ||
onDelete={vi.fn()} | ||
isDeletePending={false} | ||
/>, | ||
); | ||
expect(screen.getByText("Delete 2 tests")).toBeInTheDocument(); | ||
expect( | ||
screen.getByText("Are you sure you want to delete 2 tests?", { | ||
exact: false, | ||
}), | ||
).toBeInTheDocument(); | ||
expect( | ||
screen.getByText( | ||
"The deletion of tests is irreversible and might adversely affect your system.", | ||
{ exact: false }, | ||
), | ||
).toBeInTheDocument(); | ||
const textBoxes = screen.getAllByRole("textbox"); | ||
expect(textBoxes).toHaveLength(1); | ||
expect(screen.getByRole("button", { name: Label.DELETE })).toBeDisabled(); | ||
expect( | ||
screen.getByRole("button", { name: Label.CANCEL }), | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
test("should enable the delete button when the confirmation message is correct", async () => { | ||
renderComponent( | ||
<DeleteEntityPanel | ||
entity="test" | ||
count={1} | ||
close={vi.fn()} | ||
onDelete={vi.fn()} | ||
isDeletePending={false} | ||
/>, | ||
); | ||
expect(screen.getByRole("button", { name: Label.DELETE })).toBeDisabled(); | ||
await userEvent.type(screen.getByRole("textbox"), "remove 1 test"); | ||
expect(screen.getByRole("button", { name: Label.DELETE })).toBeEnabled(); | ||
}); | ||
|
||
test("should disable the delete button when the confirmation message is incorrect", async () => { | ||
renderComponent( | ||
<DeleteEntityPanel | ||
entity="test" | ||
count={2} | ||
close={vi.fn()} | ||
onDelete={vi.fn()} | ||
isDeletePending={false} | ||
/>, | ||
); | ||
expect(screen.getByRole("button", { name: Label.DELETE })).toBeDisabled(); | ||
await userEvent.type(screen.getByRole("textbox"), "remove 1 test"); | ||
expect(screen.getByRole("button", { name: Label.DELETE })).toBeDisabled(); | ||
}); | ||
|
||
test("should handle delete when the delete button is clicked", async () => { | ||
const handleDelete = vi.fn(); | ||
renderComponent( | ||
<DeleteEntityPanel | ||
entity="test" | ||
count={1} | ||
close={vi.fn()} | ||
onDelete={handleDelete} | ||
isDeletePending={false} | ||
/>, | ||
); | ||
await userEvent.type(screen.getByRole("textbox"), "remove 1 test"); | ||
await userEvent.click(screen.getByRole("button", { name: Label.DELETE })); | ||
expect(handleDelete).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
test("should handle close when the cancel button is clicked", async () => { | ||
const handleClose = vi.fn(); | ||
renderComponent( | ||
<DeleteEntityPanel | ||
entity="test" | ||
count={1} | ||
close={handleClose} | ||
onDelete={vi.fn()} | ||
isDeletePending={false} | ||
/>, | ||
); | ||
await userEvent.click(screen.getByRole("button", { name: Label.CANCEL })); | ||
expect(handleClose).toHaveBeenCalledTimes(1); | ||
}); |
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,70 @@ | ||
import { Col, Row, FormikField, Icon } from "@canonical/react-components"; | ||
import * as Yup from "yup"; | ||
|
||
import FormPanel from "components/FormPanel"; | ||
|
||
import type { Props, FormFields } from "./types"; | ||
import { Label } from "./types"; | ||
|
||
const DeleteEntityPanel = ({ | ||
entity, | ||
count, | ||
close, | ||
onDelete, | ||
isDeletePending, | ||
}: Props) => { | ||
const entityCount = `${count} ${entity}${count !== 1 ? "s" : ""}`; | ||
const confirmationMessage = `remove ${entityCount}`; | ||
|
||
const schema = Yup.object().shape({ | ||
confirmationMessage: Yup.string().oneOf( | ||
[confirmationMessage], | ||
Label.CONFIRMATION_MESSAGE_ERROR, | ||
), | ||
}); | ||
|
||
return ( | ||
<FormPanel<FormFields> | ||
title={ | ||
<span className="p-heading--4 panel-form-navigation__current-title"> | ||
Delete {entityCount} | ||
</span> | ||
} | ||
close={close} | ||
submitLabel={ | ||
<> | ||
<Icon name="delete" className="is-light" /> {Label.DELETE} | ||
</> | ||
} | ||
validationSchema={schema} | ||
isSaving={isDeletePending} | ||
onSubmit={onDelete} | ||
initialValues={{ confirmationMessage: "" }} | ||
submitButtonAppearance="negative" | ||
> | ||
<Row> | ||
<Col size={12}> | ||
<p> | ||
Are you sure you want to delete {entityCount}?<br /> | ||
The deletion of {entity}s is irreversible and might adversely affect | ||
your system. | ||
</p> | ||
</Col> | ||
<Col size={12}> | ||
<FormikField | ||
label={ | ||
<Col size={12}> | ||
Type <b>{confirmationMessage}</b> to confirm. | ||
</Col> | ||
} | ||
name="confirmationMessage" | ||
type="text" | ||
takeFocus | ||
/> | ||
</Col> | ||
</Row> | ||
</FormPanel> | ||
); | ||
}; | ||
|
||
export default DeleteEntityPanel; |
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,2 @@ | ||
export { default } from "./DeleteEntityPanel"; | ||
export { Label as DeleteEntityPanelLabel } from "./types"; |
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,17 @@ | ||
export type FormFields = { | ||
confirmationMessage: string; | ||
}; | ||
|
||
export type Props = { | ||
entity: string; | ||
count: number; | ||
close: () => void; | ||
onDelete: () => Promise<void>; | ||
isDeletePending: boolean; | ||
}; | ||
|
||
export enum Label { | ||
CANCEL = "Cancel", | ||
DELETE = "Delete", | ||
CONFIRMATION_MESSAGE_ERROR = "Wrong confirmation message", | ||
} |
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,23 @@ | ||
import { screen } from "@testing-library/react"; | ||
import { vi } from "vitest"; | ||
|
||
import { renderComponent } from "test/utils"; | ||
|
||
import FormPanel from "./FormPanel"; | ||
|
||
test("displays a form", async () => { | ||
renderComponent( | ||
<FormPanel<{ name: string }> | ||
close={vi.fn()} | ||
initialValues={{ name: "" }} | ||
onSubmit={vi.fn()} | ||
submitLabel="Submit!" | ||
title="panel title" | ||
> | ||
Form content | ||
</FormPanel>, | ||
); | ||
expect(screen.getByText("panel title")).toBeInTheDocument(); | ||
expect(screen.getByText("Form content")).toBeInTheDocument(); | ||
expect(screen.getByRole("button", { name: "Submit!" })).toBeInTheDocument(); | ||
}); |
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,17 @@ | ||
import type { FormikValues } from "formik"; | ||
|
||
import Panel from "components/Panel"; | ||
import PanelForm from "components/PanelForm"; | ||
import { SidePanelLabelledById } from "consts"; | ||
|
||
import { type Props } from "./types"; | ||
|
||
const FormPanel = <F extends FormikValues>({ title, ...props }: Props<F>) => { | ||
return ( | ||
<Panel title={title} titleId={SidePanelLabelledById}> | ||
<PanelForm<F> {...props} /> | ||
</Panel> | ||
); | ||
}; | ||
|
||
export default FormPanel; |
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,2 @@ | ||
export { default } from "./FormPanel"; | ||
export * from "./types"; |
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,8 @@ | ||
import type { FormikValues } from "formik"; | ||
|
||
import type { PanelProps } from "components/Panel"; | ||
import type { Props as PanelFormProps } from "components/PanelForm"; | ||
|
||
export type Props<F extends FormikValues> = { | ||
title: PanelProps["title"]; | ||
} & PanelFormProps<F>; |
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
56 changes: 56 additions & 0 deletions
56
src/components/pages/Groups/DeleteGroupsPanel/DeleteGroupsPanel.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,56 @@ | ||
import { screen } from "@testing-library/react"; | ||
import userEvent from "@testing-library/user-event"; | ||
import { setupServer } from "msw/node"; | ||
import { vi } from "vitest"; | ||
|
||
import { | ||
getDeleteGroupsItemMockHandler, | ||
getDeleteGroupsItemMockHandler400, | ||
getDeleteGroupsItemResponseMock400, | ||
} from "api/groups/groups.msw"; | ||
import { DeleteEntityPanelLabel } from "components/DeleteEntityPanel"; | ||
import { renderComponent, hasToast } from "test/utils"; | ||
|
||
import DeleteGroupsPanel from "./DeleteGroupsPanel"; | ||
import { Label as DeleteGroupsPanelLabel } from "./types"; | ||
|
||
const mockApiServer = setupServer(getDeleteGroupsItemMockHandler()); | ||
|
||
beforeAll(() => { | ||
mockApiServer.listen(); | ||
}); | ||
|
||
afterEach(() => { | ||
mockApiServer.resetHandlers(); | ||
}); | ||
|
||
afterAll(() => { | ||
mockApiServer.close(); | ||
}); | ||
|
||
test("should delete groups", async () => { | ||
renderComponent( | ||
<DeleteGroupsPanel groups={["group1", "group2"]} close={vi.fn()} />, | ||
); | ||
const textBoxes = screen.getAllByRole("textbox"); | ||
expect(textBoxes).toHaveLength(1); | ||
const confirmationMessageTextBox = textBoxes[0]; | ||
await userEvent.type(confirmationMessageTextBox, "remove 2 groups"); | ||
await userEvent.click(screen.getByText(DeleteEntityPanelLabel.DELETE)); | ||
await hasToast(DeleteGroupsPanelLabel.DEELTE_SUCCESS_MESSAGE, "positive"); | ||
}); | ||
|
||
test("should handle errors when deleting groups", async () => { | ||
mockApiServer.use( | ||
getDeleteGroupsItemMockHandler400( | ||
getDeleteGroupsItemResponseMock400({ message: "Can't remove group" }), | ||
), | ||
); | ||
renderComponent(<DeleteGroupsPanel groups={["group1"]} close={vi.fn()} />); | ||
const textBoxes = screen.getAllByRole("textbox"); | ||
expect(textBoxes).toHaveLength(1); | ||
const confirmationMessageTextBox = textBoxes[0]; | ||
await userEvent.type(confirmationMessageTextBox, "remove 1 group"); | ||
await userEvent.click(screen.getByText(DeleteEntityPanelLabel.DELETE)); | ||
await hasToast(DeleteGroupsPanelLabel.DELETE_ERROR_MESSAGE, "negative"); | ||
}); |
Oops, something went wrong.