Skip to content

Commit

Permalink
feat: move network discovery forms to sidepanel (#5285)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jay-Topher committed Jan 16, 2024
1 parent 114d228 commit 70bb415
Show file tree
Hide file tree
Showing 13 changed files with 467 additions and 152 deletions.
163 changes: 163 additions & 0 deletions src/app/networkDiscovery/components/NetworkForm/NetworkForm.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import configureStore from "redux-mock-store";

import type { NetworkDiscoverySidePanelContent } from "../../views/constants";
import { NetworkDiscoverySidePanelViews } from "../../views/constants";

import NetworkForm from "./NetworkForm";

import type { Discovery } from "@/app/store/discovery/types";
import type { RootState } from "@/app/store/root/types";
import {
NodeStatus,
NodeStatusCode,
TestStatusStatus,
} from "@/app/store/types/node";
import { callId, enableCallIdMocks } from "@/testing/callId-mock";
import {
discovery as discoveryFactory,
domain as domainFactory,
device as deviceFactory,
machine as machineFactory,
testStatus as testStatusFactory,
modelRef as modelRefFactory,
discoveryState as discoveryStateFactory,
deviceState as deviceStateFactory,
domainState as domainStateFactory,
machineState as machineStateFactory,
subnetState as subnetStateFactory,
vlanState as vlanStateFactory,
rootState as rootStateFactory,
machineStateList as machineStateListFactory,
machineStateListGroup as machineStateListGroupFactory,
} from "@/testing/factories";
import { renderWithBrowserRouter, screen } from "@/testing/utils";

const mockStore = configureStore<RootState, {}>();
enableCallIdMocks();

let state: RootState;
let discovery: Discovery;

beforeEach(() => {
const machines = [
machineFactory({
actions: [],
architecture: "amd64/generic",
cpu_count: 4,
cpu_test_status: testStatusFactory({
status: TestStatusStatus.RUNNING,
}),
distro_series: "bionic",
domain: modelRefFactory({
name: "example",
}),
extra_macs: [],
fqdn: "koala.example",
hostname: "koala",
ip_addresses: [],
memory: 8,
memory_test_status: testStatusFactory({
status: TestStatusStatus.PASSED,
}),
network_test_status: testStatusFactory({
status: TestStatusStatus.PASSED,
}),
osystem: "ubuntu",
owner: "admin",
permissions: ["edit", "delete"],
physical_disk_count: 1,
pool: modelRefFactory(),
pxe_mac: "00:11:22:33:44:55",
spaces: [],
status: NodeStatus.DEPLOYED,
status_code: NodeStatusCode.DEPLOYED,
status_message: "",
storage: 8,
storage_test_status: testStatusFactory({
status: TestStatusStatus.PASSED,
}),
testing_status: TestStatusStatus.PASSED,
system_id: "abc123",
zone: modelRefFactory(),
}),
];
discovery = discoveryFactory({
ip: "1.2.3.4",
mac_address: "aa:bb:cc",
subnet: 9,
vlan: 8,
});
state = rootStateFactory({
device: deviceStateFactory({
loaded: true,
items: [deviceFactory({ system_id: "abc123", fqdn: "abc123.example" })],
}),
discovery: discoveryStateFactory({
loaded: true,
items: [discovery],
}),
domain: domainStateFactory({
loaded: true,
items: [domainFactory({ name: "local" })],
}),
machine: machineStateFactory({
loaded: true,
items: machines,
lists: {
[callId]: machineStateListFactory({
loaded: true,
groups: [
machineStateListGroupFactory({
items: [machines[0].system_id],
name: "Deployed",
}),
],
}),
},
}),
subnet: subnetStateFactory({ loaded: true }),
vlan: vlanStateFactory({ loaded: true }),
});
});

afterAll(() => {
vi.restoreAllMocks();
});

it("renders the clear discovery form when the sidepanel view is provided", () => {
const sidePanelContent: NetworkDiscoverySidePanelContent = {
view: NetworkDiscoverySidePanelViews.CLEAR_ALL_DISCOVERIES,
};
renderWithBrowserRouter(
<NetworkForm
setSidePanelContent={vi.fn()}
sidePanelContent={sidePanelContent}
/>
);

expect(
screen.getByRole("form", { name: "Clear all discoveries" })
).toBeInTheDocument();
});

it("renders the add discovery form given the sidepanel view", () => {
const store = mockStore(state);
const sidePanelContent: NetworkDiscoverySidePanelContent = {
view: NetworkDiscoverySidePanelViews.ADD_DISCOVERY,
extras: {
discovery,
},
};

renderWithBrowserRouter(
<NetworkForm
setSidePanelContent={vi.fn()}
sidePanelContent={sidePanelContent}
/>,
{ store }
);

expect(
screen.getByRole("form", { name: "Add discovery" })
).toBeInTheDocument();
});
49 changes: 49 additions & 0 deletions src/app/networkDiscovery/components/NetworkForm/NetworkForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useCallback } from "react";

import type { SidePanelContentTypes } from "@/app/base/side-panel-context";
import DiscoveryAddForm from "@/app/networkDiscovery/views/DiscoveryAddForm";
import DiscoveryDeleteForm from "@/app/networkDiscovery/views/DiscoveryDeleteForm";
import ClearAllForm from "@/app/networkDiscovery/views/NetworkDiscoveryHeader/ClearAllForm";
import { NetworkDiscoverySidePanelViews } from "@/app/networkDiscovery/views/constants";

type Props = SidePanelContentTypes & {};

const NetworkForm = ({ sidePanelContent, setSidePanelContent }: Props) => {
const clearSidePanelContent = useCallback(
() => setSidePanelContent(null),
[setSidePanelContent]
);

if (!sidePanelContent) return null;
const discovery =
sidePanelContent.extras && "discovery" in sidePanelContent.extras
? sidePanelContent.extras.discovery
: null;

switch (sidePanelContent.view) {
case NetworkDiscoverySidePanelViews.ADD_DISCOVERY: {
if (!discovery) return null;
return (
<DiscoveryAddForm
discovery={discovery}
onClose={clearSidePanelContent}
/>
);
}
case NetworkDiscoverySidePanelViews.CLEAR_ALL_DISCOVERIES:
return <ClearAllForm closeForm={clearSidePanelContent} />;
case NetworkDiscoverySidePanelViews.DELETE_DISCOVERY: {
if (!discovery) return null;
return (
<DiscoveryDeleteForm
discovery={discovery}
onClose={clearSidePanelContent}
/>
);
}
default:
return null;
}
};

export default NetworkForm;
1 change: 1 addition & 0 deletions src/app/networkDiscovery/components/NetworkForm/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./NetworkForm";
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import configureStore from "redux-mock-store";
import { NetworkDiscoverySidePanelViews } from "../constants";

import DiscoveriesList, {
Labels as DiscoveriesListLabels,
} from "./DiscoveriesList";

import * as sidePanelHooks from "@/app/base/side-panel-context";
import * as query from "@/app/store/machine/utils/query";
import type { RootState } from "@/app/store/root/types";
import {
Expand Down Expand Up @@ -35,12 +36,18 @@ import {
renderWithBrowserRouter,
} from "@/testing/utils";

const mockStore = configureStore<RootState, {}>();
const route = "/network-discovery";
describe("DiscoveriesList", () => {
let state: RootState;

const setSidePanelContent = vi.fn();
beforeEach(() => {
vi.spyOn(sidePanelHooks, "useSidePanel").mockReturnValue({
setSidePanelContent,
sidePanelContent: null,
setSidePanelSize: vi.fn(),
sidePanelSize: "regular",
});
vi.spyOn(query, "generateCallId").mockReturnValueOnce("123456");
const machines = [
machineFactory({
Expand Down Expand Up @@ -170,30 +177,26 @@ describe("DiscoveriesList", () => {
).not.toBeInTheDocument();
});

it("can display the add form", async () => {
it("can trigger the add form sidepanel", async () => {
renderWithBrowserRouter(<DiscoveriesList />, {
route: route,
state,
});
const row = screen.getByRole("row", { name: "my-discovery-test" });
expect(
screen.queryByRole("form", { name: "Add discovery" })
).not.toBeInTheDocument();
await userEvent.click(
within(within(row).getByTestId("row-menu")).getByRole("button")
);
await userEvent.click(
screen.getByRole("button", { name: DiscoveriesListLabels.AddDiscovery })
);

await vi.waitFor(() =>
expect(
screen.getByRole("form", { name: /Add discovery/ })
).toBeInTheDocument()
expect(setSidePanelContent).toHaveBeenCalledWith(
expect.objectContaining({
view: NetworkDiscoverySidePanelViews.ADD_DISCOVERY,
})
);
});

it("can display the delete form", async () => {
it("can trigger the delete form sidepanel", async () => {
renderWithBrowserRouter(<DiscoveriesList />, {
route: route,
state,
Expand All @@ -209,39 +212,10 @@ describe("DiscoveriesList", () => {
})
);

expect(
screen.getByText(
'Are you sure you want to delete discovery "my-discovery-test"?'
)
).toBeInTheDocument();

expect(screen.getByRole("button", { name: "Delete" })).toBeInTheDocument();
});

it("can delete a discovery", async () => {
const store = mockStore(state);
renderWithBrowserRouter(<DiscoveriesList />, {
route: route,
store,
});
const row = screen.getByRole("row", { name: "my-discovery-test" });

// Open the action menu.
await userEvent.click(
within(within(row).getByTestId("row-menu")).getByRole("button")
);

// Click on the delete link.
await userEvent.click(
screen.getByRole("button", {
name: DiscoveriesListLabels.DeleteDiscovery,
expect(setSidePanelContent).toHaveBeenCalledWith(
expect.objectContaining({
view: NetworkDiscoverySidePanelViews.DELETE_DISCOVERY,
})
);

// Click on the confirm button.
await userEvent.click(screen.getByRole("button", { name: "Delete" }));
expect(
store.getActions().some((action) => action.type === "discovery/delete")
).toBe(true);
});
});
Loading

0 comments on commit 70bb415

Please sign in to comment.