diff --git a/web/src/components/overview/StorageSection.jsx b/web/src/components/overview/StorageSection.jsx index de10992e41..e5f31648d8 100644 --- a/web/src/components/overview/StorageSection.jsx +++ b/web/src/components/overview/StorageSection.jsx @@ -74,7 +74,10 @@ export default function StorageSection ({ showErrors }) { dispatch({ type: "UPDATE_STATUS", payload: { status } }); }; - cancellablePromise(client.storage.getStatus()).then(updateStatus); + cancellablePromise(client.storage.getStatus()).then((result) => { + console.log("getStatus", result); + updateStatus(result); + }); return client.storage.onStatusChange(updateStatus); }, [client.storage, cancellablePromise]); @@ -93,6 +96,7 @@ export default function StorageSection ({ showErrors }) { const errors = showErrors ? state.errors : []; const SectionContent = () => { + console.log("State", state); if (state.busy || !state.proposal) return ; return ( @@ -111,7 +115,9 @@ export default function StorageSection ({ showErrors }) { return (
- +
+ +
); } diff --git a/web/src/components/overview/StorageSection.test.jsx b/web/src/components/overview/StorageSection.test.jsx new file mode 100644 index 0000000000..cd28d1d5ae --- /dev/null +++ b/web/src/components/overview/StorageSection.test.jsx @@ -0,0 +1,178 @@ +/* + * Copyright (c) [2022] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact SUSE LLC. + * + * To contact SUSE LLC about this file by physical or electronic mail, you may + * find current contact information at www.suse.com. + */ + +import React from "react"; +import { act, screen, waitFor } from "@testing-library/react"; +import { installerRender, createCallbackMock } from "@/test-utils"; +import { createClient } from "@client"; +import { BUSY, IDLE } from "@client/status"; +import { StorageSection } from "@components/overview"; + +const mockUseNavigate = jest.fn(); + +jest.mock("@client"); +jest.mock("@components/core/InstallerSkeleton", () => () => "Loading storage"); +jest.mock("react-router-dom", () => ({ + ...jest.requireActual("react-router-dom"), + useNavigate: () => mockUseNavigate +})); + +let status = IDLE; +let proposal = { + availableDevices: [ + { id: "/dev/sda", label: "/dev/sda, 500 GiB" }, + { id: "/dev/sdb", label: "/dev/sdb, 650 GiB" } + ], + candidateDevices: ["/dev/sda"], + lvm: false +}; +let errors = []; + +beforeEach(() => { + createClient.mockImplementation(() => { + return { + storage: { + getProposal: jest.fn().mockResolvedValue(proposal), + getStatus: jest.fn().mockResolvedValue(status), + getValidationErrors: jest.fn().mockResolvedValue(errors), + onStatusChange: () => jest.fn() + }, + }; + }); +}); + +describe("when there is no proposal yet", () => { + beforeEach(() => { + proposal = undefined; + }); + + it("does not render a link a for editing storage settings", async () => { + installerRender(); + + await waitFor(() => expect(screen.queryByRole("button", { name: "Edit storage settings" })).not.toBeInTheDocument()); + }); +}); + +describe("but storage service is busy", () => { + beforeEach(() => { + status = IDLE; + errors = [{ message: "Fake error" }]; + }); + + it("does not render a link a for editing storage settings", async () => { + installerRender(); + + await waitFor(() => expect(screen.queryByRole("button", { name: "Edit storage settings" })).not.toBeInTheDocument()); + }); + + it("does not render errors", async () => { + installerRender(); + + await waitFor(() => expect(screen.queryByText("Fake error")).not.toBeInTheDocument()); + }); +}); + +// describe("when the user selects another disk", () => { +// it("changes the selected disk", async () => { +// calculateStorageProposalFn = jest.fn().mockResolvedValue(0); + +// const { user } = installerRender(); +// const button = await screen.findByRole("button", { name: "/dev/sda, 500 GiB" }); +// await user.click(button); + +// const targetSelector = await screen.findByLabelText("Device to install into"); +// await user.selectOptions(targetSelector, ["/dev/sdb"]); +// await user.click(screen.getByRole("button", { name: "Confirm" })); + +// await screen.findByRole("button", { name: "/dev/sdb, 650 GiB" }); +// expect(calculateStorageProposalFn).toHaveBeenCalledWith({ +// candidateDevices: ["/dev/sdb"] +// }); +// }); +// }); + +// describe("when the storage proposal changes", () => { +// let callbacks; + +// beforeEach(() => { +// const [mockedFn, callbacksList] = createCallbackMock(); +// callbacks = callbacksList; +// onStorageProposalChangeFn = mockedFn; +// }); + +// it("updates the proposal", async () => { +// installerRender(); +// await screen.findByRole("button", { name: "/dev/sda, 500 GiB" }); + +// const [cb] = callbacks; +// act(() => { +// cb("/dev/sdb"); +// }); +// await screen.findByRole("button", { name: "/dev/sdb, 650 GiB" }); +// }); +// }); + +// describe("when the storage actions change", () => { +// let callbacks; + +// beforeEach(() => { +// const [mockedFn, callbacksList] = createCallbackMock(); +// callbacks = callbacksList; +// onActionsChangeFn = mockedFn; +// }); + +// it("updates the proposal", async () => { +// installerRender(); +// await screen.findByText("Mount /dev/sda1 as root"); + +// const [cb] = callbacks; +// act(() => { +// cb([{ text: "Mount /dev/sdb1 as root", subvol: false }]); +// }); +// await screen.findByText("Mount /dev/sdb1 as root"); +// }); +// }); + +// describe("when showError is set to true", () => { +// beforeEach(() => { +// getValidationErrorsFn.mockResolvedValue([{ message: "Could not make a proposal" }]); +// }); + +// it("displays the list of errors", async () => { +// installerRender(); +// await waitFor(() => { +// expect(screen.queryByText(/Could not make a proposal/)).toBeInTheDocument(); +// }); +// }); + +// it("refreshes the list of errors when they change", async () => { +// let callback; +// onValidationChangeFn.mockImplementation(cb => { callback = cb }); + +// installerRender(); +// act(() => { +// callback([{ message: "Could not find a suitable device" }]); +// }); +// await waitFor(() => { +// expect(screen.queryByText(/Could not find a suitable device/)).toBeInTheDocument(); +// }); +// }); +// }); diff --git a/web/src/components/storage/Storage.test.jsx b/web/src/components/storage/Storage.test.jsx deleted file mode 100644 index df8f5bccfb..0000000000 --- a/web/src/components/storage/Storage.test.jsx +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) [2022] SUSE LLC - * - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, contact SUSE LLC. - * - * To contact SUSE LLC about this file by physical or electronic mail, you may - * find current contact information at www.suse.com. - */ - -import React from "react"; -import { act, screen, waitFor } from "@testing-library/react"; -import { installerRender, createCallbackMock } from "@/test-utils"; -import { createClient } from "@client"; -import { IDLE } from "@client/status"; -import { Storage } from "@components/storage"; - -jest.mock("@client"); -jest.mock("@components/core/InstallerSkeleton", () => () => "Loading storage"); - -let proposalSettings; -let storageActions; - -let onActionsChangeFn = jest.fn(); -let onStorageProposalChangeFn = jest.fn(); -let calculateStorageProposalFn; -const getStatusFn = jest.fn().mockResolvedValue(IDLE); -const getValidationErrorsFn = jest.fn().mockResolvedValue([]); -const onValidationChangeFn = jest.fn(); - -const storageMock = { - getStorageProposal: () => Promise.resolve(proposalSettings), - getStorageActions: () => Promise.resolve(storageActions) -}; - -beforeEach(() => { - storageActions = [{ text: "Mount /dev/sda1 as root", subvol: false, delete: false }]; - proposalSettings = { - availableDevices: [ - { id: "/dev/sda", label: "/dev/sda, 500 GiB" }, - { id: "/dev/sdb", label: "/dev/sdb, 650 GiB" } - ], - candidateDevices: ["/dev/sda"], - lvm: false - }; - createClient.mockImplementation(() => { - return { - storage: { - ...storageMock, - calculateStorageProposal: calculateStorageProposalFn, - onActionsChange: onActionsChangeFn, - onStorageProposalChange: onStorageProposalChangeFn, - getStatus: getStatusFn.mockResolvedValue(IDLE), - onStatusChange: jest.fn(), - getValidationErrors: getValidationErrorsFn, - onValidationChange: onValidationChangeFn - }, - manager: { - getStatus: jest.fn().mockResolvedValue(IDLE), - onStatusChange: jest.fn() - } - }; - }); -}); - -describe("when the user selects another disk", () => { - it("changes the selected disk", async () => { - calculateStorageProposalFn = jest.fn().mockResolvedValue(0); - - const { user } = installerRender(); - const button = await screen.findByRole("button", { name: "/dev/sda, 500 GiB" }); - await user.click(button); - - const targetSelector = await screen.findByLabelText("Device to install into"); - await user.selectOptions(targetSelector, ["/dev/sdb"]); - await user.click(screen.getByRole("button", { name: "Confirm" })); - - await screen.findByRole("button", { name: "/dev/sdb, 650 GiB" }); - expect(calculateStorageProposalFn).toHaveBeenCalledWith({ - candidateDevices: ["/dev/sdb"] - }); - }); -}); - -describe("when the storage proposal changes", () => { - let callbacks; - - beforeEach(() => { - const [mockedFn, callbacksList] = createCallbackMock(); - callbacks = callbacksList; - onStorageProposalChangeFn = mockedFn; - }); - - it("updates the proposal", async () => { - installerRender(); - await screen.findByRole("button", { name: "/dev/sda, 500 GiB" }); - - const [cb] = callbacks; - act(() => { - cb("/dev/sdb"); - }); - await screen.findByRole("button", { name: "/dev/sdb, 650 GiB" }); - }); -}); - -describe("when the storage actions change", () => { - let callbacks; - - beforeEach(() => { - const [mockedFn, callbacksList] = createCallbackMock(); - callbacks = callbacksList; - onActionsChangeFn = mockedFn; - }); - - it("updates the proposal", async () => { - installerRender(); - await screen.findByText("Mount /dev/sda1 as root"); - - const [cb] = callbacks; - act(() => { - cb([{ text: "Mount /dev/sdb1 as root", subvol: false }]); - }); - await screen.findByText("Mount /dev/sdb1 as root"); - }); -}); - -describe("when showError is set to true", () => { - beforeEach(() => { - getValidationErrorsFn.mockResolvedValue([{ message: "Could not make a proposal" }]); - }); - - it("displays the list of errors", async () => { - installerRender(); - await waitFor(() => { - expect(screen.queryByText(/Could not make a proposal/)).toBeInTheDocument(); - }); - }); - - it("refreshes the list of errors when they change", async () => { - let callback; - onValidationChangeFn.mockImplementation(cb => { callback = cb }); - - installerRender(); - act(() => { - callback([{ message: "Could not find a suitable device" }]); - }); - await waitFor(() => { - expect(screen.queryByText(/Could not find a suitable device/)).toBeInTheDocument(); - }); - }); -});