Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ test.describe("Roles & Permissions room settings tab", () => {
// Change the role of Alice to Moderator (50)
await combobox.selectOption("Moderator");
await expect(combobox).toHaveValue("50");

// Should display a modal to warn that we are demoting the only admin user
const modal = await page.locator(".mx_Dialog", {
hasText: "Warning",
});
await expect(modal).toBeVisible();
// Click on the continue button in the modal
await modal.getByRole("button", { name: "Continue" }).click();

const respPromise = page.waitForRequest("**/state/**");
await applyButton.click();
await respPromise;
Expand Down
28 changes: 27 additions & 1 deletion src/components/views/settings/PowerLevelSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
import PowerSelector from "../elements/PowerSelector";
import { _t } from "../../../languageHandler";
import SettingsFieldset from "./SettingsFieldset";
import Modal from "../../../Modal";
import QuestionDialog from "../dialogs/QuestionDialog";

/**
* Display in a fieldset, the power level of the users and allow to change them.
Expand Down Expand Up @@ -77,6 +79,13 @@ export function PowerLevelSelector({
// No user to display, we return the children into fragment to convert it to JSX.Element type
if (!users.length) return <>{children}</>;

// check at least one admin in the list
const roomHasAtLeastOneAdmin = (usersLevels: Record<string, number>): boolean => {
const userLevelValues = Object.values(usersLevels);
// At least one user as the pL 100 which means he is admin
return userLevelValues.some((uL) => uL === 100);
};

return (
<SettingsFieldset legend={title}>
{users.map((userId) => {
Expand All @@ -96,7 +105,24 @@ export function PowerLevelSelector({
disabled={!canChange}
label={userId}
key={userId}
onChange={(value) => setCurrentPowerLevel({ value, userId })}
onChange={async (value) => {
const userLevelsTmp = Object.assign({}, userLevels);
userLevelsTmp[userId] = value;
if (!roomHasAtLeastOneAdmin(userLevelsTmp)) {
const { finished } = Modal.createDialog(QuestionDialog, {
title: _t("common|warning"),
description: <div>{_t("user_info|demote_self_confirm_room")}</div>,
button: _t("action|continue"),
});
const [confirmed] = await finished;
if (!confirmed) {
// if cancel, we reput initial value
setCurrentPowerLevel({ value: userLevels[userId], userId });
return;
}
}
setCurrentPowerLevel({ value, userId });
}}
/>
);
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ describe("PowerLevelSelector", () => {

it("should be able to change the power level of the current user", async () => {
const onClick = jest.fn();
renderPLS({ onClick });
const userLevels = {
[currentUser]: 100,
"@alice:server.org": 100,
"@bob:server.org": 0,
};
renderPLS({ userLevels, onClick });

// Until the power level is changed, the apply button should be disabled
// compound button is using aria-disabled instead of the disabled attribute, we can't toBeDisabled on it
Expand Down Expand Up @@ -107,4 +112,58 @@ describe("PowerLevelSelector", () => {

expect(screen.getByText("empty label")).toBeInTheDocument();
});

it("should display modal warning if user is last admin", async () => {
const onClick = jest.fn();

renderPLS({ onClick });

// Until the power level is changed, the apply button should be disabled
// compound button is using aria-disabled instead of the disabled attribute, we can't toBeDisabled on it
expect(screen.getByRole("button", { name: "Apply" })).toHaveAttribute("aria-disabled", "true");

const select = screen.getByRole("combobox", { name: currentUser });
// Sanity check
expect(select).toHaveValue("100");

// Change current user power level to 50
await userEvent.selectOptions(select, "50");

// modal should appear because only admin in the room
expect(screen.findByText("WARNING")).toBeTruthy();

await userEvent.click(screen.getByRole("button", { name: "Continue" }));

expect(select).toHaveValue("50");
// After the user level changes, the apply button should be enabled
expect(screen.getByRole("button", { name: "Apply" })).toHaveAttribute("aria-disabled", "false");

// Click on Apply should call onClick with the new power level
await userEvent.click(screen.getByRole("button", { name: "Apply" }));
expect(onClick).toHaveBeenCalledWith(50, currentUser);
});

it("should display modal warning if user is last admin and return to initial value if user cancel", async () => {
const onClick = jest.fn();

renderPLS({ onClick });

// Until the power level is changed, the apply button should be disabled
// compound button is using aria-disabled instead of the disabled attribute, we can't toBeDisabled on it
expect(screen.getByRole("button", { name: "Apply" })).toHaveAttribute("aria-disabled", "true");

const select = screen.getByRole("combobox", { name: currentUser });
// Sanity check
expect(select).toHaveValue("100");

// Change current user power level to 50
await userEvent.selectOptions(select, "50");

// modal should appear because only admin in the room
expect(screen.findByText("WARNING")).toBeTruthy();

await userEvent.click(screen.getByRole("button", { name: "Cancel" }));
// the power level should be back to initial value
expect(select).toHaveValue("100");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ describe("RolesRoomSettingsTab", () => {
content: {
users: {
[cli.getUserId()!]: 100,
// needs at least one remaning admin in the room if we want to demote our user
// otherwise another modal will be displayed
["@admin:server"]: 100,
},
},
});
Expand Down
Loading