Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
joseivanlopez committed Apr 11, 2024
1 parent 57006bf commit d6a1aa5
Show file tree
Hide file tree
Showing 3 changed files with 300 additions and 17 deletions.
24 changes: 15 additions & 9 deletions web/src/components/storage/ProposalVolumes.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,6 @@ it("renders a button for the generic actions", async () => {
});

it("changes the volumes if reset action is used", async () => {
props.onChange = jest.fn();

const { user } = plainRender(<ProposalVolumes {...props} />);

const button = screen.getByRole("button", { name: "Actions" });
Expand All @@ -186,7 +184,6 @@ it("changes the volumes if reset action is used", async () => {

it("allows to add a volume if add action is used", async () => {
props.templates = [homeVolume];
props.onChange = jest.fn();

const { user } = plainRender(<ProposalVolumes {...props} />);

Expand All @@ -206,7 +203,6 @@ it("allows to add a volume if add action is used", async () => {

it("allows to cancel if add action is used", async () => {
props.templates = [homeVolume];
props.onChange = jest.fn();

const { user } = plainRender(<ProposalVolumes {...props} />);

Expand Down Expand Up @@ -255,8 +251,6 @@ describe("if there are volumes", () => {
});

it("allows deleting the volume", async () => {
props.onChange = jest.fn();

const { user } = plainRender(<ProposalVolumes {...props} />);

const [, body] = await screen.findAllByRole("rowgroup");
Expand All @@ -270,8 +264,6 @@ describe("if there are volumes", () => {
});

it("allows editing the volume", async () => {
props.onChange = jest.fn();

const { user } = plainRender(<ProposalVolumes {...props} />);

const [, body] = await screen.findAllByRole("rowgroup");
Expand All @@ -285,7 +277,21 @@ describe("if there are volumes", () => {
within(popup).getByText("Edit file system");
});

describe("and there is transactional Btrfs root volume", () => {
it("allows changing the location of the volume", async () => {
const { user } = plainRender(<ProposalVolumes {...props} />);

const [, body] = await screen.findAllByRole("rowgroup");
const row = within(body).getByRole("row", { name: "/home XFS At least 1 KiB Partition at installation disk" });
const actions = within(row).getByRole("button", { name: "Actions" });
await user.click(actions);
const locationAction = within(row).queryByRole("menuitem", { name: "Change location" });
await user.click(locationAction);

const popup = await screen.findByRole("dialog");
within(popup).getByText("Location for /home file system");
});

describe("and there is a transactional Btrfs root volume", () => {
beforeEach(() => {
props.volumes = [{ ...rootVolume, snapshots: true, transactional: true }];
});
Expand Down
18 changes: 10 additions & 8 deletions web/src/components/storage/VolumeLocationDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,16 @@ const RadioOption = ({ id, onChange, defaultChecked, children }) => {
* Renders a dialog that allows the user to change the location of a volume.
* @component
*
* @param {object} props
* @param {Volume} props.volume - Volume to edit.
* @param {StorageDevice[]} props.devices - Devices available for installation.
* @param {ProposalTarget} props.target - Installation target.
* @param {StorageDevice|undefined} props.targetDevice - Device selected for installation, if target is a disk.
* @param {boolean} [props.isOpen=false] - Whether the dialog is visible or not.
* @param {() => void} props.onCancel
* @param {(volume: Volume) => void} props.onAccept
* @typedef {object} VolumeLocationDialogProps
* @property {Volume} volume - Volume to edit.
* @property {StorageDevice[]} devices - Devices available for installation.
* @property {ProposalTarget} target - Installation target.
* @property {StorageDevice|undefined} targetDevice - Device selected for installation, if target is a disk.
* @property {boolean} [isOpen=false] - Whether the dialog is visible or not.
* @property {() => void} onCancel
* @property {(volume: Volume) => void} onAccept
*
* @param {VolumeLocationDialogProps} props
*/
export default function VolumeLocationDialog({
volume,
Expand Down
275 changes: 275 additions & 0 deletions web/src/components/storage/VolumeLocationDialog.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
/*
* Copyright (c) [2024] 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.
*/

// @ts-check

import React from "react";
import { screen, within } from "@testing-library/react";
import { plainRender } from "~/test-utils";
import VolumeLocationDialog from "~/components/storage/VolumeLocationDialog";

/**
* @typedef {import ("~/client/storage").StorageDevice} StorageDevice
* @typedef {import ("~/client/storage").Volume} Volume
* @typedef {import ("~/components/storage/VolumeLocationDialog").VolumeLocationDialogProps} VolumeLocationDialogProps
*/

/** @type {StorageDevice} */
const sda = {
sid: 59,
isDrive: true,
type: "disk",
description: "",
vendor: "Micron",
model: "Micron 1100 SATA",
driver: ["ahci", "mmcblk"],
bus: "IDE",
busId: "",
transport: "usb",
dellBOSS: false,
sdCard: true,
active: true,
name: "/dev/sda",
size: 1024,
recoverableSize: 0,
systems : [],
udevIds: ["ata-Micron_1100_SATA_512GB_12563", "scsi-0ATA_Micron_1100_SATA_512GB"],
udevPaths: ["pci-0000:00-12", "pci-0000:00-12-ata"],
};

/** @type {StorageDevice} */
const sdb = {
sid: 62,
isDrive: true,
type: "disk",
description: "",
vendor: "Samsung",
model: "Samsung Evo 8 Pro",
driver: ["ahci"],
bus: "IDE",
busId: "",
transport: "",
dellBOSS: false,
sdCard: false,
active: true,
name: "/dev/sdb",
size: 2048,
recoverableSize: 0,
systems : [],
udevIds: [],
udevPaths: ["pci-0000:00-19"]
};

/** @type {StorageDevice} */
const sdc = {
sid: 63,
isDrive: true,
type: "disk",
description: "",
vendor: "Samsung",
model: "Samsung Evo 8 Pro",
driver: ["ahci"],
bus: "IDE",
busId: "",
transport: "",
dellBOSS: false,
sdCard: false,
active: true,
name: "/dev/sdc",
size: 2048,
recoverableSize: 0,
systems : [],
udevIds: [],
udevPaths: ["pci-0000:00-19"]
};

/** @type {Volume} */
const volume = {
mountPath: "/",
target: "DEFAULT",
fsType: "Btrfs",
minSize: 1024,
maxSize: 2048,
autoSize: false,
snapshots: false,
transactional: false,
outline: {
required: true,
fsTypes: ["Btrfs", "Ext4"],
supportAutoSize: true,
snapshotsConfigurable: true,
snapshotsAffectSizes: true,
sizeRelevantVolumes: [],
adjustByRam: false
}
};

/** @type {VolumeLocationDialogProps} */
let props;

describe("VolumeLocationDialog", () => {
beforeEach(() => {
props = {
isOpen: true,
volume,
devices: [sda, sdb, sdc],
target: "DISK",
targetDevice: sda,
onCancel: jest.fn(),
onAccept: jest.fn()
};
});

const automaticOption = () => screen.queryByRole("radio", { name: "Automatic" });
const selectDiskOption = () => screen.queryByRole("radio", { name: "Select a disk" });
const diskSelector = () => screen.queryByRole("combobox", { name: /choose a disk/i });
const lvmSelector = () => screen.queryByRole("checkbox", { name: /dedicated lvm/i });

it("offers an option to use the installation disk", () => {
plainRender(<VolumeLocationDialog {...props} />);
expect(automaticOption()).toBeInTheDocument();
});

it("offers an option to selected a disk", () => {
plainRender(<VolumeLocationDialog {...props} />);
expect(selectDiskOption()).toBeInTheDocument();
expect(diskSelector()).toBeInTheDocument();
expect(lvmSelector()).toBeInTheDocument();
});

describe("if the current value is set to use the installation disk", () => {
beforeEach(() => {
props.volume.target = "DEFAULT";
props.targetDevice = sda;
});

it("selects 'Automatic' option by default", () => {
plainRender(<VolumeLocationDialog {...props} />);
expect(automaticOption()).toBeChecked();
expect(selectDiskOption()).not.toBeChecked();
expect(diskSelector()).toBeDisabled();
expect(lvmSelector()).toBeDisabled();
});
});

describe("if the current value is set to use a selected disk", () => {
beforeEach(() => {
props.volume.target = "NEW_PARTITION";
props.targetDevice = sda;
});

it("selects 'Select a disk' option by default", () => {
plainRender(<VolumeLocationDialog {...props} />);
expect(automaticOption()).not.toBeChecked();
expect(selectDiskOption()).toBeChecked();
expect(diskSelector()).toBeEnabled();
expect(lvmSelector()).toBeEnabled();
expect(lvmSelector()).not.toBeChecked();
});
});

describe("if the current value is set to use a selected disk for a dedicated LVM", () => {
beforeEach(() => {
props.volume.target = "NEW_VG";
props.targetDevice = sda;
});

it("selects 'Select a disk' option and check LVM by default", () => {
plainRender(<VolumeLocationDialog {...props} />);
expect(automaticOption()).not.toBeChecked();
expect(selectDiskOption()).toBeChecked();
expect(diskSelector()).toBeEnabled();
expect(lvmSelector()).toBeEnabled();
expect(lvmSelector()).toBeChecked();
});
});

it("does not call onAccept on cancel", async () => {
const { user } = plainRender(<VolumeLocationDialog {...props} />);
const cancel = screen.getByRole("button", { name: "Cancel" });

await user.click(cancel);

expect(props.onAccept).not.toHaveBeenCalled();
});

describe("if the 'Automatic' option is selected", () => {
beforeEach(() => {
props.volume.target = "NEW_PARTITION";
props.volume.targetDevice = sda;
});

it("calls onAccept with the selected options on accept", async () => {
const { user } = plainRender(<VolumeLocationDialog {...props} />);

await user.click(automaticOption());

const accept = screen.getByRole("button", { name: "Confirm" });
await user.click(accept);

expect(props.onAccept).toHaveBeenCalledWith(expect.objectContaining(
{ target: "DEFAULT", targetDevice: undefined }
));
});
});

describe("if the 'Select a disk' option is selected", () => {
beforeEach(() => {
props.volume.target = "DEFAULT";
props.volume.targetDevice = undefined;
});

it("calls onAccept with the selected options on accept", async () => {
const { user } = plainRender(<VolumeLocationDialog {...props} />);

await user.click(selectDiskOption());
const selector = diskSelector();
const sdbOption = within(selector).getByRole("option", { name: /sdb/ });
await user.selectOptions(selector, sdbOption);

const accept = screen.getByRole("button", { name: "Confirm" });
await user.click(accept);

expect(props.onAccept).toHaveBeenCalledWith(expect.objectContaining(
{ target: "NEW_PARTITION", targetDevice: sdb }
));
});

describe("and dedicated LVM is checked", () => {
it("calls onAccept with the selected options on accept", async () => {
const { user } = plainRender(<VolumeLocationDialog {...props} />);

await user.click(selectDiskOption());
const selector = diskSelector();
const sdbOption = within(selector).getByRole("option", { name: /sdb/ });
await user.selectOptions(selector, sdbOption);
await user.click(lvmSelector());

const accept = screen.getByRole("button", { name: "Confirm" });
await user.click(accept);

expect(props.onAccept).toHaveBeenCalledWith(expect.objectContaining(
{ target: "NEW_VG", targetDevice: sdb }
));
});
});
});
});

0 comments on commit d6a1aa5

Please sign in to comment.