diff --git a/web/src/components/storage/ProposalDeviceSection.jsx b/web/src/components/storage/ProposalDeviceSection.jsx index 1b8a42e349..6b9ed801f5 100644 --- a/web/src/components/storage/ProposalDeviceSection.jsx +++ b/web/src/components/storage/ProposalDeviceSection.jsx @@ -20,11 +20,19 @@ */ import React, { useState } from "react"; -import { Button, Form, Skeleton } from "@patternfly/react-core"; +import { + Button, + Form, + Skeleton, + Switch, + ToggleGroup, ToggleGroupItem, + Tooltip +} from "@patternfly/react-core"; import { _ } from "~/i18n"; +import { Icon } from "~/components/layout"; import { If, Section, Popup } from "~/components/core"; -import { DeviceSelector } from "~/components/storage"; +import { DeviceList, DeviceSelector } from "~/components/storage"; import { deviceLabel } from '~/components/storage/utils'; import { noop } from "~/utils"; @@ -167,6 +175,201 @@ const InstallationDeviceField = ({ ); }; +/** + * Form for configuring the system volume group. + * @component + * + * @param {object} props + * @param {string} props.id - Form ID. + * @param {ProposalSettings} props.settings - Settings used for calculating a proposal. + * @param {StorageDevice[]} [props.devices=[]] - Available storage devices. + * @param {onSubmitFn} [props.onSubmit=noop] - On submit callback. + * @param {onValidateFn} [props.onValidate=noop] - On validate callback. + * + * @callback onSubmitFn + * @param {string[]} devices - Name of the selected devices. + * + * @callback onValidateFn + * @param {boolean} valid + */ +const LVMSettingsForm = ({ + id, + settings, + devices = [], + onSubmit: onSubmitProp = noop, + onValidate = noop +}) => { + const [vgDevices, setVgDevices] = useState(settings.systemVGDevices); + const [isBootDeviceSelected, setIsBootDeviceSelected] = useState(settings.systemVGDevices.length === 0); + const [editedDevices, setEditedDevices] = useState(false); + + const selectBootDevice = () => { + setIsBootDeviceSelected(true); + onValidate(true); + }; + + const selectCustomDevices = () => { + setIsBootDeviceSelected(false); + const { bootDevice } = settings; + const customDevices = (vgDevices.length === 0 && !editedDevices) ? [bootDevice] : vgDevices; + setVgDevices(customDevices); + onValidate(customDevices.length > 0); + }; + + const onChangeDevices = (selection) => { + const selectedDevices = devices.filter(d => selection.includes(d.sid)).map(d => d.name); + setVgDevices(selectedDevices); + setEditedDevices(true); + onValidate(devices.length > 0); + }; + + const onSubmit = (e) => { + e.preventDefault(); + const customDevices = isBootDeviceSelected ? [] : vgDevices; + onSubmitProp(customDevices); + }; + + const BootDevice = () => { + const bootDevice = devices.find(d => d.name === settings.bootDevice); + + // FIXME: In this case, should be a "readOnly" selector. + return ; + }; + + return ( +
+
+ {_("Devices for creating the volume group")} + + + + +
+ } + else={ + vgDevices?.includes(d.name))} + devices={devices} + onChange={onChangeDevices} + /> + } + /> + + ); +}; + +/** + * Allows to select LVM and configure the system volume group. + * @component + * + * @param {object} props + * @param {ProposalSettings} props.settings - Settings used for calculating a proposal. + * @param {StorageDevice[]} [props.devices=[]] - Available storage devices. + * @param {boolean} [props.isChecked=false] - Whether LVM is selected. + * @param {boolean} [props.isLoading=false] - Whether to show the selector as loading. + * @param {onChangeFn} [props.onChange=noop] - On change callback. + * + * @callback onChangeFn + * @param {boolean} lvm + */ +const LVMField = ({ + settings, + devices = [], + isChecked: isCheckedProp = false, + isLoading = false, + onChange: onChangeProp = noop +}) => { + const [isChecked, setIsChecked] = useState(isCheckedProp); + const [isFormOpen, setIsFormOpen] = useState(false); + const [isFormValid, setIsFormValid] = useState(true); + + const onChange = (_, value) => { + setIsChecked(value); + onChangeProp({ lvm: value, vgDevices: [] }); + }; + + const openForm = () => setIsFormOpen(true); + + const closeForm = () => setIsFormOpen(false); + + const onValidateForm = (valid) => setIsFormValid(valid); + + const onSubmitForm = (vgDevices) => { + closeForm(); + onChangeProp({ vgDevices }); + }; + + const description = _("Configuration of the system volume group. All the file systems will be \ +created in a logical volume of the system volume group."); + + const LVMSettingsButton = () => { + return ( + + + + ); + }; + + if (isLoading) return ; + + return ( +
+ + } /> + + + + + {_("Accept")} + + + + +
+ ); +}; + /** * Section for editing the selected device * @component @@ -195,6 +398,14 @@ export default function ProposalDeviceSection({ } }; + const changeLVM = ({ lvm, vgDevices }) => { + const settings = {}; + if (lvm !== undefined) settings.lvm = lvm; + if (vgDevices !== undefined) settings.systemVGDevices = vgDevices; + + onChange(settings); + }; + const Description = () => ( + ); } diff --git a/web/src/components/storage/ProposalDeviceSection.test.jsx b/web/src/components/storage/ProposalDeviceSection.test.jsx index e90db6a054..27bb058910 100644 --- a/web/src/components/storage/ProposalDeviceSection.test.jsx +++ b/web/src/components/storage/ProposalDeviceSection.test.jsx @@ -66,6 +66,53 @@ const sdb = { udevPaths: ["pci-0000:00-19"] }; +const vda = { + sid: "59", + type: "disk", + vendor: "Micron", + model: "Micron 1100 SATA", + driver: ["ahci", "mmcblk"], + bus: "IDE", + transport: "usb", + dellBOSS: false, + sdCard: true, + active: true, + name: "/dev/vda", + size: 1024, + systems: ["Windows", "openSUSE Leap 15.2"], + udevIds: ["ata-Micron_1100_SATA_512GB_12563", "scsi-0ATA_Micron_1100_SATA_512GB"], + udevPaths: ["pci-0000:00-12", "pci-0000:00-12-ata"], + partitionTable: { type: "gpt", partitions: [] } +}; + +const md0 = { + sid: "62", + type: "md", + level: "raid0", + uuid: "12345:abcde", + members: ["/dev/vdb"], + active: true, + name: "/dev/md0", + size: 2048, + systems: [], + udevIds: [], + udevPaths: [] +}; + +const md1 = { + sid: "63", + type: "md", + level: "raid0", + uuid: "12345:abcde", + members: ["/dev/vdc"], + active: true, + name: "/dev/md1", + size: 4096, + systems: [], + udevIds: [], + udevPaths: [] +}; + const props = { settings: { bootDevice: "/dev/sda", @@ -76,107 +123,274 @@ const props = { }; describe("ProposalDeviceSection", () => { - describe("when set as loading", () => { - beforeEach(() => { - props.isLoading = true; - }); + describe("Installation device field", () => { + describe("when set as loading", () => { + beforeEach(() => { + props.isLoading = true; + }); - describe("and selected device is not defined yet", () => { + describe("and selected device is not defined yet", () => { + beforeEach(() => { + props.settings = { bootDevice: undefined }; + }); + + it("renders a loading hint", () => { + plainRender(); + screen.getByText("Loading selected device"); + }); + }); + }); + describe("when installation device is not selected yet", () => { beforeEach(() => { - props.settings = { bootDevice: undefined }; + props.settings = { bootDevice: "" }; }); - it("renders a loading hint", () => { - plainRender(); - screen.getByText("Loading selected device"); + it("uses a 'No device selected yet' text for the selection button", async () => { + const { user } = plainRender(); + const button = screen.getByRole("button", { name: "No device selected yet" }); + + await user.click(button); + + screen.getByRole("dialog", { name: "Installation device" }); }); }); - }); - describe("when installation device is not selected yet", () => { - beforeEach(() => { - props.settings = { bootDevice: "" }; + describe("when installation device is selected", () => { + beforeEach(() => { + props.settings = { bootDevice: "/dev/sda" }; + }); + + it("uses its name as part of the text for the selection button", async () => { + const { user } = plainRender(); + const button = screen.getByRole("button", { name: /\/dev\/sda/ }); + + await user.click(button); + + screen.getByRole("dialog", { name: "Installation device" }); + }); }); - it("uses a 'No device selected yet' text for the selection button", async () => { + it("allows changing the selected device", async () => { const { user } = plainRender(); - const button = screen.getByRole("button", { name: "No device selected yet" }); + const button = screen.getByRole("button", { name: "/dev/sda, 1 KiB" }); await user.click(button); - screen.getByRole("dialog", { name: "Installation device" }); + const selector = await screen.findByRole("dialog", { name: "Installation device" }); + const sdbOption = within(selector).getByRole("radio", { name: /sdb/ }); + const accept = within(selector).getByRole("button", { name: "Accept" }); + + await user.click(sdbOption); + await user.click(accept); + + expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); + expect(props.onChange).toHaveBeenCalledWith({ bootDevice: sdb.name }); }); - }); - describe("when installation device is selected", () => { - beforeEach(() => { - props.settings = { bootDevice: "/dev/sda" }; + it("allows canceling a device selection", async () => { + const { user } = plainRender(); + const button = screen.getByRole("button", { name: "/dev/sda, 1 KiB" }); + + await user.click(button); + + const selector = await screen.findByRole("dialog", { name: "Installation device" }); + const sdbOption = within(selector).getByRole("radio", { name: /sdb/ }); + const cancel = within(selector).getByRole("button", { name: "Cancel" }); + + await user.click(sdbOption); + await user.click(cancel); + + expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); + expect(props.onChange).not.toHaveBeenCalled(); }); - it("uses its name as part of the text for the selection button", async () => { + it("does not trigger the onChange callback when selection actually did not change", async () => { const { user } = plainRender(); - const button = screen.getByRole("button", { name: /\/dev\/sda/ }); + const button = screen.getByRole("button", { name: "/dev/sda, 1 KiB" }); await user.click(button); - screen.getByRole("dialog", { name: "Installation device" }); + const selector = await screen.findByRole("dialog", { name: "Installation device" }); + const sdaOption = within(selector).getByRole("radio", { name: /sda/ }); + const sdbOption = within(selector).getByRole("radio", { name: /sdb/ }); + const accept = within(selector).getByRole("button", { name: "Accept" }); + + // User selects a different device + await user.click(sdbOption); + // but then goes back to the selected device + await user.click(sdaOption); + // and clicks on Accept button + await user.click(accept); + + expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); + // There is no reason for triggering the onChange callback + expect(props.onChange).not.toHaveBeenCalled(); }); }); - it("allows changing the selected device", async () => { - const { user } = plainRender(); - const button = screen.getByRole("button", { name: "/dev/sda, 1 KiB" }); + describe("LVM field", () => { + describe("if LVM setting is not set yet", () => { + beforeEach(() => { + props.settings = {}; + }); - await user.click(button); + it("does not render the LVM switch", () => { + plainRender(); - const selector = await screen.findByRole("dialog", { name: "Installation device" }); - const sdbOption = within(selector).getByRole("radio", { name: /sdb/ }); - const accept = within(selector).getByRole("button", { name: "Accept" }); + expect(screen.queryByLabelText(/Use logical volume/)).toBeNull(); + }); + }); - await user.click(sdbOption); - await user.click(accept); + describe("if LVM setting is set", () => { + beforeEach(() => { + props.settings = { lvm: false }; + }); - expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); - expect(props.onChange).toHaveBeenCalledWith({ bootDevice: sdb.name }); - }); + it("renders the LVM switch", () => { + plainRender(); + + screen.getByRole("checkbox", { name: /Use logical volume/ }); + }); + }); - it("allows canceling a device selection", async () => { - const { user } = plainRender(); - const button = screen.getByRole("button", { name: "/dev/sda, 1 KiB" }); + describe("if LVM is set to true", () => { + beforeEach(() => { + props.availableDevices = [vda, md0, md1]; + props.settings = { bootDevice: "/dev/vda", lvm: true, systemVGDevices: [] }; + props.onChange = jest.fn(); + }); - await user.click(button); + it("renders the LVM switch as selected", () => { + plainRender(); - const selector = await screen.findByRole("dialog", { name: "Installation device" }); - const sdbOption = within(selector).getByRole("radio", { name: /sdb/ }); - const cancel = within(selector).getByRole("button", { name: "Cancel" }); + const checkbox = screen.getByRole("checkbox", { name: /Use logical volume/ }); + expect(checkbox).toBeChecked(); + }); - await user.click(sdbOption); - await user.click(cancel); + it("renders a button for changing the LVM settings", () => { + plainRender(); - expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); - expect(props.onChange).not.toHaveBeenCalled(); - }); + screen.getByRole("button", { name: /LVM settings/ }); + }); + + it("changes the selection on click", async () => { + const { user } = plainRender(); + + const checkbox = screen.getByRole("checkbox", { name: /Use logical volume/ }); + await user.click(checkbox); + + expect(checkbox).not.toBeChecked(); + expect(props.onChange).toHaveBeenCalled(); + }); + + describe("and user clicks on LVM settings", () => { + it("opens the LVM settings dialog", async () => { + const { user } = plainRender(); + const settingsButton = screen.getByRole("button", { name: /LVM settings/ }); + + await user.click(settingsButton); + + const popup = await screen.findByRole("dialog"); + within(popup).getByText("System Volume Group"); + }); + + it("allows selecting either installation device or custom devices", async () => { + const { user } = plainRender(); + const settingsButton = screen.getByRole("button", { name: /LVM settings/ }); + + await user.click(settingsButton); + + const popup = await screen.findByRole("dialog"); + screen.getByText("System Volume Group"); + + within(popup).getByRole("button", { name: "Installation device" }); + within(popup).getByRole("button", { name: "Custom devices" }); + }); + + it("allows to set the installation device as system volume group", async () => { + const { user } = plainRender(); + const settingsButton = screen.getByRole("button", { name: /LVM settings/ }); + + await user.click(settingsButton); - it("does not trigger the onChange callback when selection actually did not change", async () => { - const { user } = plainRender(); - const button = screen.getByRole("button", { name: "/dev/sda, 1 KiB" }); + const popup = await screen.findByRole("dialog"); + screen.getByText("System Volume Group"); - await user.click(button); + const bootDeviceButton = within(popup).getByRole("button", { name: "Installation device" }); + const customDevicesButton = within(popup).getByRole("button", { name: "Custom devices" }); + const acceptButton = within(popup).getByRole("button", { name: "Accept" }); - const selector = await screen.findByRole("dialog", { name: "Installation device" }); - const sdaOption = within(selector).getByRole("radio", { name: /sda/ }); - const sdbOption = within(selector).getByRole("radio", { name: /sdb/ }); - const accept = within(selector).getByRole("button", { name: "Accept" }); + await user.click(customDevicesButton); + await user.click(bootDeviceButton); + await user.click(acceptButton); - // User selects a different device - await user.click(sdbOption); - // but then goes back to the selected device - await user.click(sdaOption); - // and clicks on Accept button - await user.click(accept); + expect(props.onChange).toHaveBeenCalledWith( + expect.objectContaining({ systemVGDevices: [] }) + ); + }); - expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); - // There is no reason for triggering the onChange callback - expect(props.onChange).not.toHaveBeenCalled(); + it("allows customize the system volume group", async () => { + const { user } = plainRender(); + const settingsButton = screen.getByRole("button", { name: /LVM settings/ }); + + await user.click(settingsButton); + + const popup = await screen.findByRole("dialog"); + screen.getByText("System Volume Group"); + + const customDevicesButton = within(popup).getByRole("button", { name: "Custom devices" }); + const acceptButton = within(popup).getByRole("button", { name: "Accept" }); + + await user.click(customDevicesButton); + + const vdaOption = within(popup).getByRole("row", { name: /vda/ }); + const md0Option = within(popup).getByRole("row", { name: /md0/ }); + const md1Option = within(popup).getByRole("row", { name: /md1/ }); + + // unselect the boot devices + await user.click(vdaOption); + + await user.click(md0Option); + await user.click(md1Option); + + await user.click(acceptButton); + + expect(props.onChange).toHaveBeenCalledWith( + expect.objectContaining({ systemVGDevices: ["/dev/md0", "/dev/md1"] }) + ); + }); + }); + }); + + describe("if LVM is set to false", () => { + beforeEach(() => { + props.settings = { lvm: false }; + props.onChange = jest.fn(); + }); + + it("renders the LVM switch as not selected", () => { + plainRender(); + + const checkbox = screen.getByRole("checkbox", { name: /Use logical volume/ }); + expect(checkbox).not.toBeChecked(); + }); + + it("does not render a button for changing the LVM settings", () => { + plainRender(); + + const button = screen.queryByRole("button", { name: /LVM settings/ }); + expect(button).toBeNull(); + }); + + it("changes the selection on click", async () => { + const { user } = plainRender(); + + const checkbox = screen.getByRole("checkbox", { name: /Use logical volume/ }); + await user.click(checkbox); + + expect(checkbox).toBeChecked(); + expect(props.onChange).toHaveBeenCalled(); + }); + }); }); }); diff --git a/web/src/components/storage/ProposalSettingsSection.jsx b/web/src/components/storage/ProposalSettingsSection.jsx index 9dedef6fd0..1a8812d949 100644 --- a/web/src/components/storage/ProposalSettingsSection.jsx +++ b/web/src/components/storage/ProposalSettingsSection.jsx @@ -20,15 +20,10 @@ */ import React, { useEffect, useState } from "react"; -import { - Form, Skeleton, Switch, Checkbox, - ToggleGroup, ToggleGroupItem, - Tooltip -} from "@patternfly/react-core"; +import { Checkbox, Form, Skeleton, Switch, Tooltip } from "@patternfly/react-core"; import { _ } from "~/i18n"; import { If, PasswordAndConfirmationInput, Section, Popup } from "~/components/core"; -import { DeviceList, DeviceSelector } from "~/components/storage"; import { Icon } from "~/components/layout"; import { noop } from "~/utils"; @@ -38,201 +33,6 @@ import { noop } from "~/utils"; * @typedef {import ("~/client/storage").ProposalManager.Volume} Volume */ -/** - * Form for configuring the system volume group. - * @component - * - * @param {object} props - * @param {string} props.id - Form ID. - * @param {ProposalSettings} props.settings - Settings used for calculating a proposal. - * @param {StorageDevice[]} [props.devices=[]] - Available storage devices. - * @param {onSubmitFn} [props.onSubmit=noop] - On submit callback. - * @param {onValidateFn} [props.onValidate=noop] - On validate callback. - * - * @callback onSubmitFn - * @param {string[]} devices - Name of the selected devices. - * - * @callback onValidateFn - * @param {boolean} valid - */ -const LVMSettingsForm = ({ - id, - settings, - devices = [], - onSubmit: onSubmitProp = noop, - onValidate = noop -}) => { - const [vgDevices, setVgDevices] = useState(settings.systemVGDevices); - const [isBootDeviceSelected, setIsBootDeviceSelected] = useState(settings.systemVGDevices.length === 0); - const [editedDevices, setEditedDevices] = useState(false); - - const selectBootDevice = () => { - setIsBootDeviceSelected(true); - onValidate(true); - }; - - const selectCustomDevices = () => { - setIsBootDeviceSelected(false); - const { bootDevice } = settings; - const customDevices = (vgDevices.length === 0 && !editedDevices) ? [bootDevice] : vgDevices; - setVgDevices(customDevices); - onValidate(customDevices.length > 0); - }; - - const onChangeDevices = (selection) => { - const selectedDevices = devices.filter(d => selection.includes(d.sid)).map(d => d.name); - setVgDevices(selectedDevices); - setEditedDevices(true); - onValidate(devices.length > 0); - }; - - const onSubmit = (e) => { - e.preventDefault(); - const customDevices = isBootDeviceSelected ? [] : vgDevices; - onSubmitProp(customDevices); - }; - - const BootDevice = () => { - const bootDevice = devices.find(d => d.name === settings.bootDevice); - - // FIXME: In this case, should be a "readOnly" selector. - return ; - }; - - return ( -
-
- {_("Devices for creating the volume group")} - - - - -
- } - else={ - vgDevices?.includes(d.name))} - devices={devices} - onChange={onChangeDevices} - /> - } - /> - - ); -}; - -/** - * Allows to select LVM and configure the system volume group. - * @component - * - * @param {object} props - * @param {ProposalSettings} props.settings - Settings used for calculating a proposal. - * @param {StorageDevice[]} [props.devices=[]] - Available storage devices. - * @param {boolean} [props.isChecked=false] - Whether LVM is selected. - * @param {boolean} [props.isLoading=false] - Whether to show the selector as loading. - * @param {onChangeFn} [props.onChange=noop] - On change callback. - * - * @callback onChangeFn - * @param {boolean} lvm - */ -const LVMField = ({ - settings, - devices = [], - isChecked: isCheckedProp = false, - isLoading = false, - onChange: onChangeProp = noop -}) => { - const [isChecked, setIsChecked] = useState(isCheckedProp); - const [isFormOpen, setIsFormOpen] = useState(false); - const [isFormValid, setIsFormValid] = useState(true); - - const onChange = (_, value) => { - setIsChecked(value); - onChangeProp({ lvm: value, vgDevices: [] }); - }; - - const openForm = () => setIsFormOpen(true); - - const closeForm = () => setIsFormOpen(false); - - const onValidateForm = (valid) => setIsFormValid(valid); - - const onSubmitForm = (vgDevices) => { - closeForm(); - onChangeProp({ vgDevices }); - }; - - const description = _("Configuration of the system volume group. All the file systems will be \ -created in a logical volume of the system volume group."); - - const LVMSettingsButton = () => { - return ( - - - - ); - }; - - if (isLoading) return ; - - return ( -
- - } /> - - - - - {_("Accept")} - - - - -
- ); -}; - /** * Form for configuring the encryption password. * @component @@ -422,7 +222,6 @@ const EncryptionField = ({ * * @param {object} props * @param {ProposalSettings} props.settings - * @param {StorageDevice[]} [props.availableDevices=[]] * @param {String[]} [props.encryptionMethods=[]] * @param {onChangeFn} [props.onChange=noop] * @@ -431,18 +230,9 @@ const EncryptionField = ({ */ export default function ProposalSettingsSection({ settings, - availableDevices = [], encryptionMethods = [], onChange = noop }) { - const changeLVM = ({ lvm, vgDevices }) => { - const settings = {}; - if (lvm !== undefined) settings.lvm = lvm; - if (vgDevices !== undefined) settings.systemVGDevices = vgDevices; - - onChange(settings); - }; - const changeEncryption = ({ password, method }) => { onChange({ encryptionPassword: password, encryptionMethod: method }); }; @@ -452,13 +242,6 @@ export default function ProposalSettingsSection({ return ( <>
- { let props; -const vda = { - sid: "59", - type: "disk", - vendor: "Micron", - model: "Micron 1100 SATA", - driver: ["ahci", "mmcblk"], - bus: "IDE", - transport: "usb", - dellBOSS: false, - sdCard: true, - active: true, - name: "/dev/vda", - size: 1024, - systems: ["Windows", "openSUSE Leap 15.2"], - udevIds: ["ata-Micron_1100_SATA_512GB_12563", "scsi-0ATA_Micron_1100_SATA_512GB"], - udevPaths: ["pci-0000:00-12", "pci-0000:00-12-ata"], - partitionTable: { type: "gpt", partitions: [] } -}; - -const md0 = { - sid: "62", - type: "md", - level: "raid0", - uuid: "12345:abcde", - members: ["/dev/vdb"], - active: true, - name: "/dev/md0", - size: 2048, - systems: [], - udevIds: [], - udevPaths: [] -}; - -const md1 = { - sid: "63", - type: "md", - level: "raid0", - uuid: "12345:abcde", - members: ["/dev/vdc"], - active: true, - name: "/dev/md1", - size: 4096, - systems: [], - udevIds: [], - udevPaths: [] -}; - beforeEach(() => { props = {}; }); -describe("LVM field", () => { - describe("if LVM setting is not set yet", () => { - beforeEach(() => { - props.settings = {}; - }); - - it("does not render the LVM switch", () => { - plainRender(); - - expect(screen.queryByLabelText(/Use logical volume/)).toBeNull(); - }); - }); - - describe("if LVM setting is set", () => { - beforeEach(() => { - props.settings = { lvm: false }; - }); - - it("renders the LVM switch", () => { - plainRender(); - - screen.getByRole("checkbox", { name: /Use logical volume/ }); - }); - }); - - describe("if LVM is set to true", () => { - beforeEach(() => { - props.availableDevices = [vda, md0, md1]; - props.settings = { bootDevice: "/dev/vda", lvm: true, systemVGDevices: [] }; - props.onChange = jest.fn(); - }); - - it("renders the LVM switch as selected", () => { - plainRender(); - - const checkbox = screen.getByRole("checkbox", { name: /Use logical volume/ }); - expect(checkbox).toBeChecked(); - }); - - it("renders a button for changing the LVM settings", () => { - plainRender(); - - screen.getByRole("button", { name: /LVM settings/ }); - }); - - it("changes the selection on click", async () => { - const { user } = plainRender(); - - const checkbox = screen.getByRole("checkbox", { name: /Use logical volume/ }); - await user.click(checkbox); - - expect(checkbox).not.toBeChecked(); - expect(props.onChange).toHaveBeenCalled(); - }); - - describe("and user clicks on LVM settings", () => { - it("opens the LVM settings dialog", async () => { - const { user } = plainRender(); - const settingsButton = screen.getByRole("button", { name: /LVM settings/ }); - - await user.click(settingsButton); - - const popup = await screen.findByRole("dialog"); - within(popup).getByText("System Volume Group"); - }); - - it("allows selecting either installation device or custom devices", async () => { - const { user } = plainRender(); - const settingsButton = screen.getByRole("button", { name: /LVM settings/ }); - - await user.click(settingsButton); - - const popup = await screen.findByRole("dialog"); - screen.getByText("System Volume Group"); - - within(popup).getByRole("button", { name: "Installation device" }); - within(popup).getByRole("button", { name: "Custom devices" }); - }); - - it("allows to set the installation device as system volume group", async () => { - const { user } = plainRender(); - const settingsButton = screen.getByRole("button", { name: /LVM settings/ }); - - await user.click(settingsButton); - - const popup = await screen.findByRole("dialog"); - screen.getByText("System Volume Group"); - - const bootDeviceButton = within(popup).getByRole("button", { name: "Installation device" }); - const customDevicesButton = within(popup).getByRole("button", { name: "Custom devices" }); - const acceptButton = within(popup).getByRole("button", { name: "Accept" }); - - await user.click(customDevicesButton); - await user.click(bootDeviceButton); - await user.click(acceptButton); - - expect(props.onChange).toHaveBeenCalledWith( - expect.objectContaining({ systemVGDevices: [] }) - ); - }); - - it("allows customize the system volume group", async () => { - const { user } = plainRender(); - const settingsButton = screen.getByRole("button", { name: /LVM settings/ }); - - await user.click(settingsButton); - - const popup = await screen.findByRole("dialog"); - screen.getByText("System Volume Group"); - - const customDevicesButton = within(popup).getByRole("button", { name: "Custom devices" }); - const acceptButton = within(popup).getByRole("button", { name: "Accept" }); - - await user.click(customDevicesButton); - - const vdaOption = within(popup).getByRole("row", { name: /vda/ }); - const md0Option = within(popup).getByRole("row", { name: /md0/ }); - const md1Option = within(popup).getByRole("row", { name: /md1/ }); - - // unselect the boot devices - await user.click(vdaOption); - - await user.click(md0Option); - await user.click(md1Option); - - await user.click(acceptButton); - - expect(props.onChange).toHaveBeenCalledWith( - expect.objectContaining({ systemVGDevices: ["/dev/md0", "/dev/md1"] }) - ); - }); - }); - }); - - describe("if LVM is set to false", () => { - beforeEach(() => { - props.settings = { lvm: false }; - props.onChange = jest.fn(); - }); - - it("renders the LVM switch as not selected", () => { - plainRender(); - - const checkbox = screen.getByRole("checkbox", { name: /Use logical volume/ }); - expect(checkbox).not.toBeChecked(); - }); - - it("does not render a button for changing the LVM settings", () => { - plainRender(); - - const button = screen.queryByRole("button", { name: /LVM settings/ }); - expect(button).toBeNull(); - }); - - it("changes the selection on click", async () => { - const { user } = plainRender(); - - const checkbox = screen.getByRole("checkbox", { name: /Use logical volume/ }); - await user.click(checkbox); - - expect(checkbox).toBeChecked(); - expect(props.onChange).toHaveBeenCalled(); - }); - }); -}); - describe("Encryption field", () => { describe("if encryption password setting is not set yet", () => { beforeEach(() => {