Users screen UI revision (#1961)
* Changed Users screen UI to selector based delete, table layout changes, updated testcafe permissions test to reflect new delete User sequence, fixed ListUsers checkbox permission issue
This commit is contained in:
@@ -699,6 +699,7 @@ const TableWrapper = ({
|
||||
inputProps={{
|
||||
"aria-label": "secondary checkbox",
|
||||
}}
|
||||
className="TableCheckbox"
|
||||
checked={isSelected}
|
||||
onChange={onSelect}
|
||||
onClick={(e) => {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { DialogContentText } from "@mui/material";
|
||||
import { User } from "./types";
|
||||
import { setErrorSnackMessage } from "../../../actions";
|
||||
import useApi from "../Common/Hooks/useApi";
|
||||
import ConfirmDialog from "../Common/ModalWrapper/ConfirmDialog";
|
||||
@@ -28,14 +27,14 @@ import { encodeURLString } from "../../../common/utils";
|
||||
interface IDeleteUserProps {
|
||||
closeDeleteModalAndRefresh: (refresh: boolean) => void;
|
||||
deleteOpen: boolean;
|
||||
selectedUser: User | null;
|
||||
selectedUsers: string[] | null;
|
||||
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||
}
|
||||
|
||||
const DeleteUser = ({
|
||||
closeDeleteModalAndRefresh,
|
||||
deleteOpen,
|
||||
selectedUser,
|
||||
selectedUsers,
|
||||
setErrorSnackMessage,
|
||||
}: IDeleteUserProps) => {
|
||||
const onDelSuccess = () => closeDeleteModalAndRefresh(true);
|
||||
@@ -44,23 +43,34 @@ const DeleteUser = ({
|
||||
|
||||
const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);
|
||||
|
||||
if (!selectedUser) {
|
||||
const userLoggedIn = localStorage.getItem("userLoggedIn") || "";
|
||||
|
||||
if (!selectedUsers) {
|
||||
return null;
|
||||
}
|
||||
const renderUsers = selectedUsers.map((user) => (
|
||||
<div key={user}>
|
||||
<b>{user}</b>
|
||||
</div>
|
||||
));
|
||||
|
||||
const onConfirmDelete = () => {
|
||||
invokeDeleteApi(
|
||||
"DELETE",
|
||||
`/api/v1/user/${encodeURLString(selectedUser.accessKey)}`,
|
||||
{
|
||||
id: selectedUser.id,
|
||||
for (let user of selectedUsers) {
|
||||
if (user === userLoggedIn) {
|
||||
setErrorSnackMessage({
|
||||
errorMessage: "Cannot delete currently logged in user",
|
||||
detailedError: `Cannot delete currently logged in user ${userLoggedIn}`,
|
||||
});
|
||||
closeDeleteModalAndRefresh(true);
|
||||
} else {
|
||||
invokeDeleteApi("DELETE", `/api/v1/user/${encodeURLString(user)}`);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfirmDialog
|
||||
title={`Delete User`}
|
||||
title={`Delete User${selectedUsers.length > 1 ? "s" : ""}`}
|
||||
confirmText={"Delete"}
|
||||
isOpen={deleteOpen}
|
||||
titleIcon={<ConfirmDeleteIcon />}
|
||||
@@ -69,8 +79,9 @@ const DeleteUser = ({
|
||||
onClose={onClose}
|
||||
confirmationContent={
|
||||
<DialogContentText>
|
||||
Are you sure you want to delete user <br />
|
||||
<b>{selectedUser.accessKey}</b>?
|
||||
Are you sure you want to delete the following {selectedUsers.length}{" "}
|
||||
user{selectedUsers.length > 1 ? "s?" : "?"}
|
||||
<b>{renderUsers}</b>
|
||||
</DialogContentText>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -26,7 +26,7 @@ import { ConfirmDeleteIcon } from "../../../icons";
|
||||
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
|
||||
import { encodeURLString } from "../../../common/utils";
|
||||
|
||||
interface IDeleteUserProps {
|
||||
interface IDeleteUserStringProps {
|
||||
closeDeleteModalAndRefresh: (refresh: boolean) => void;
|
||||
deleteOpen: boolean;
|
||||
userName: string;
|
||||
@@ -38,7 +38,7 @@ const DeleteUserModal = ({
|
||||
deleteOpen,
|
||||
userName,
|
||||
setErrorSnackMessage,
|
||||
}: IDeleteUserProps) => {
|
||||
}: IDeleteUserStringProps) => {
|
||||
const onDelSuccess = () => {
|
||||
history.push(IAM_PAGES.USERS);
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ import api from "../../../common/api";
|
||||
import { Grid, LinearProgress } from "@mui/material";
|
||||
import { User, UsersList } from "./types";
|
||||
import { usersSort } from "../../../utils/sortFunctions";
|
||||
import { GroupsIcon, AddIcon, UsersIcon } from "../../../icons";
|
||||
import { GroupsIcon, AddIcon, DeleteIcon, UsersIcon } from "../../../icons";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
@@ -35,7 +35,7 @@ import { ErrorResponseHandler } from "../../../common/types";
|
||||
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import { decodeURLString, encodeURLString } from "../../../common/utils";
|
||||
import { encodeURLString } from "../../../common/utils";
|
||||
import HelpBox from "../../../common/HelpBox";
|
||||
import AButton from "../Common/AButton/AButton";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
@@ -54,9 +54,6 @@ import {
|
||||
SecureComponent,
|
||||
} from "../../../common/SecureComponent";
|
||||
|
||||
const SetPolicy = withSuspense(
|
||||
React.lazy(() => import("../Policies/SetPolicy"))
|
||||
);
|
||||
const DeleteUser = withSuspense(React.lazy(() => import("./DeleteUser")));
|
||||
const AddToGroup = withSuspense(React.lazy(() => import("./BulkAddToGroup")));
|
||||
|
||||
@@ -83,20 +80,14 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
const [records, setRecords] = useState<User[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [selectedUser, setSelectedUser] = useState<User | null>(null);
|
||||
const [addGroupOpen, setAddGroupOpen] = useState<boolean>(false);
|
||||
const [filter, setFilter] = useState<string>("");
|
||||
const [checkedUsers, setCheckedUsers] = useState<string[]>([]);
|
||||
const [policyOpen, setPolicyOpen] = useState<boolean>(false);
|
||||
|
||||
const displayListUsers = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_LIST_USERS,
|
||||
]);
|
||||
|
||||
const deleteUser = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_DELETE_USER,
|
||||
]);
|
||||
|
||||
const viewUser = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_GET_USER,
|
||||
]);
|
||||
@@ -105,11 +96,16 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
IAM_SCOPES.ADMIN_ADD_USER_TO_GROUP,
|
||||
]);
|
||||
|
||||
const deleteUser = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_DELETE_USER,
|
||||
]);
|
||||
|
||||
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
||||
setDeleteOpen(false);
|
||||
if (refresh) {
|
||||
setLoading(true);
|
||||
}
|
||||
setCheckedUsers([]);
|
||||
};
|
||||
|
||||
const closeAddGroupBulk = (unCheckAll: boolean = false) => {
|
||||
@@ -145,9 +141,7 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
);
|
||||
|
||||
const selectionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const targetD = e.target;
|
||||
const value = targetD.value;
|
||||
const checked = targetD.checked;
|
||||
const { target: { value = "", checked = false } = {} } = e;
|
||||
|
||||
let elements: string[] = [...checkedUsers]; // We clone the checkedUsers array
|
||||
|
||||
@@ -170,15 +164,6 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const deleteAction = (selectionElement: any): void => {
|
||||
setDeleteOpen(true);
|
||||
setSelectedUser(selectionElement);
|
||||
};
|
||||
|
||||
const userLoggedIn = decodeURLString(
|
||||
localStorage.getItem("userLoggedIn") || ""
|
||||
);
|
||||
|
||||
const tableActions = [
|
||||
{
|
||||
type: "view",
|
||||
@@ -186,30 +171,18 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
disableButtonFunction: () => !viewUser,
|
||||
},
|
||||
{
|
||||
type: "delete",
|
||||
onClick: deleteAction,
|
||||
disableButtonFunction: (topValue: any) =>
|
||||
topValue === userLoggedIn || !deleteUser,
|
||||
type: "edit",
|
||||
onClick: viewAction,
|
||||
disableButtonFunction: () => !viewUser,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{policyOpen && (
|
||||
<SetPolicy
|
||||
open={policyOpen}
|
||||
selectedUser={selectedUser}
|
||||
selectedGroup={null}
|
||||
closeModalAndRefresh={() => {
|
||||
setPolicyOpen(false);
|
||||
setLoading(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{deleteOpen && (
|
||||
<DeleteUser
|
||||
deleteOpen={deleteOpen}
|
||||
selectedUser={selectedUser}
|
||||
selectedUsers={checkedUsers}
|
||||
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
||||
closeDeleteModalAndRefresh(refresh);
|
||||
}}
|
||||
@@ -233,6 +206,25 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
overrideClass={classes.searchField}
|
||||
value={filter}
|
||||
/>
|
||||
<SecureComponent
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
scopes={[IAM_SCOPES.ADMIN_DELETE_USER]}
|
||||
matchAll
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<RBIconButton
|
||||
tooltip={"Delete Selected"}
|
||||
onClick={() => {
|
||||
setDeleteOpen(true);
|
||||
}}
|
||||
text={"Delete Selected"}
|
||||
icon={<DeleteIcon />}
|
||||
color="secondary"
|
||||
disabled={checkedUsers.length === 0}
|
||||
variant={"outlined"}
|
||||
aria-label="delete-selected-users"
|
||||
/>
|
||||
</SecureComponent>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.ADMIN_ADD_USER_TO_GROUP]}
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
@@ -280,7 +272,7 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
<Fragment>
|
||||
{records.length > 0 && (
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.tableBlock}>
|
||||
<Grid item xs={12} className={classes.tableBlock} marginBottom={"15px"}>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.ADMIN_LIST_USERS]}
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
@@ -291,7 +283,11 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
columns={[
|
||||
{ label: "Access Key", elementKey: "accessKey" },
|
||||
]}
|
||||
onSelect={addUserToGroup ? selectionChanged : undefined}
|
||||
onSelect={
|
||||
addUserToGroup || deleteUser
|
||||
? selectionChanged
|
||||
: undefined
|
||||
}
|
||||
selectedItems={checkedUsers}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
@@ -300,28 +296,30 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
/>
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
<Grid item xs={12} marginTop={"25px"}>
|
||||
<HelpBox
|
||||
<HelpBox
|
||||
title={"Users"}
|
||||
iconComponent={<UsersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A MinIO user consists of a unique access key (username)
|
||||
and corresponding secret key (password). Clients must
|
||||
authenticate their identity by specifying both a valid
|
||||
access key (username) and the corresponding secret key
|
||||
(password) of an existing MinIO user.
|
||||
<br />
|
||||
<br />
|
||||
Each user can have one or more assigned policies that
|
||||
explicitly list the actions and resources to which that
|
||||
user has access. Users can also inherit policies from
|
||||
the groups in which they have membership.
|
||||
<br />
|
||||
<br />
|
||||
A MinIO user consists of a unique access key (username) and
|
||||
corresponding secret key (password). Clients must authenticate their
|
||||
identity by specifying both a valid access key (username) and the
|
||||
corresponding secret key (password) of an existing MinIO user.
|
||||
<br />
|
||||
Groups provide a simplified method for managing shared permissions among users with common access patterns and workloads.
|
||||
<br />
|
||||
<br />
|
||||
Users inherit access permissions to data and resources through the groups they belong to.
|
||||
<br />
|
||||
MinIO uses Policy-Based Access Control (PBAC) to define the authorized actions and resources to which an authenticated user has access. Each policy describes one or more actions and conditions that outline the permissions of a user or group of users.
|
||||
<br />
|
||||
<br />
|
||||
Each user can access only those resources and operations which are explicitly granted by the built-in role. MinIO denies access to any other resource or action by default.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/baremetal/monitoring/bucket-notifications/bucket-notifications.html?ref=con"
|
||||
href="https://docs.min.io/minio/k8s/tutorials/user-management.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
@@ -331,7 +329,6 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{records.length === 0 && (
|
||||
@@ -339,7 +336,7 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
alignItems={"start"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
@@ -347,46 +344,51 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
iconComponent={<UsersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A MinIO user consists of a unique access key (username)
|
||||
and corresponding secret key (password). Clients must
|
||||
authenticate their identity by specifying both a valid
|
||||
access key (username) and the corresponding secret key
|
||||
(password) of an existing MinIO user.
|
||||
<br />
|
||||
<br />
|
||||
Each user can have one or more assigned policies that
|
||||
explicitly list the actions and resources to which that
|
||||
user has access. Users can also inherit policies from
|
||||
the groups in which they have membership.
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.ADMIN_CREATE_USER,
|
||||
IAM_SCOPES.ADMIN_LIST_USER_POLICIES,
|
||||
IAM_SCOPES.ADMIN_LIST_GROUPS,
|
||||
]}
|
||||
matchAll
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
>
|
||||
<Fragment>
|
||||
<br />
|
||||
<br />
|
||||
To get started,{" "}
|
||||
<AButton
|
||||
onClick={() => {
|
||||
history.push(`${IAM_PAGES.USER_ADD}`);
|
||||
}}
|
||||
>
|
||||
Create a User
|
||||
</AButton>
|
||||
.
|
||||
</Fragment>
|
||||
A MinIO user consists of a unique access key (username) and
|
||||
corresponding secret key (password). Clients must authenticate their
|
||||
identity by specifying both a valid access key (username) and the
|
||||
corresponding secret key (password) of an existing MinIO user.
|
||||
<br />
|
||||
Groups provide a simplified method for managing shared permissions among users with common access patterns and workloads.
|
||||
<br />
|
||||
<br />
|
||||
Users inherit access permissions to data and resources through the groups they belong to.
|
||||
<br />
|
||||
MinIO uses Policy-Based Access Control (PBAC) to define the authorized actions and resources to which an authenticated user has access. Each policy describes one or more actions and conditions that outline the permissions of a user or group of users.
|
||||
<br />
|
||||
<br />
|
||||
Each user can access only those resources and operations which are explicitly granted by the built-in role. MinIO denies access to any other resource or action by default.
|
||||
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.ADMIN_CREATE_USER,
|
||||
IAM_SCOPES.ADMIN_LIST_USER_POLICIES,
|
||||
IAM_SCOPES.ADMIN_LIST_GROUPS,
|
||||
]}
|
||||
matchAll
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
>
|
||||
<br />
|
||||
<br />
|
||||
To get started,{" "}
|
||||
<AButton
|
||||
onClick={() => {
|
||||
history.push(`${IAM_PAGES.USER_ADD}`);
|
||||
}}
|
||||
>
|
||||
Create a User
|
||||
</AButton>
|
||||
.
|
||||
</SecureComponent>
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
|
||||
</Fragment>
|
||||
)}
|
||||
</PageLayout>
|
||||
|
||||
125
portal-ui/src/screens/Console/Users/UsersHelpBox.tsx
Normal file
125
portal-ui/src/screens/Console/Users/UsersHelpBox.tsx
Normal file
@@ -0,0 +1,125 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React from "react";
|
||||
import { Box } from "@mui/material";
|
||||
import {
|
||||
HelpIconFilled,
|
||||
UsersIcon,
|
||||
ChangeAccessPolicyIcon,
|
||||
GroupsIcon,
|
||||
} from "../../../icons";
|
||||
|
||||
const FeatureItem = ({
|
||||
icon,
|
||||
description,
|
||||
}: {
|
||||
icon: any;
|
||||
description: string;
|
||||
}) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
"& .min-icon": {
|
||||
marginRight: "10px",
|
||||
height: "23px",
|
||||
width: "23px",
|
||||
marginBottom: "10px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{icon}{" "}
|
||||
<div style={{ fontSize: "14px", fontStyle: "italic", color: "#5E5E5E" }}>
|
||||
{description}
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
const UsersHelpBox = () => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1,
|
||||
border: "1px solid #eaeaea",
|
||||
borderRadius: "2px",
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
padding: "20px",
|
||||
marginTop: {
|
||||
xs: "0px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
fontWeight: 600,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginBottom: "16px",
|
||||
|
||||
"& .min-icon": {
|
||||
height: "21px",
|
||||
width: "21px",
|
||||
marginRight: "15px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<HelpIconFilled />
|
||||
<div>Learn more about the Users feature</div>
|
||||
</Box>
|
||||
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
}}
|
||||
>
|
||||
<FeatureItem icon={<UsersIcon />} description={`Create Users`} />
|
||||
<Box sx={{ fontSize: "14px", marginBottom: "15px" }}>
|
||||
A MinIO user consists of a unique access key (username) and
|
||||
corresponding secret key (password). Clients must authenticate their
|
||||
identity by specifying both a valid access key (username) and the
|
||||
corresponding secret key (password) of an existing MinIO user.
|
||||
<br />
|
||||
</Box>
|
||||
<FeatureItem icon={<GroupsIcon />} description={`Manage Groups`} />
|
||||
<Box sx={{ fontSize: "14px", marginBottom: "15px" }}>
|
||||
Groups provide a simplified method for managing shared permissions among users with common access patterns and workloads.
|
||||
<br />
|
||||
<br />
|
||||
Users inherit access permissions to data and resources through the groups they belong to.
|
||||
<br />
|
||||
</Box>
|
||||
<FeatureItem
|
||||
icon={<ChangeAccessPolicyIcon />}
|
||||
description={`Assign Policies`}
|
||||
/>
|
||||
<Box sx={{ fontSize: "14px", marginBottom: "15px" }}>
|
||||
MinIO uses Policy-Based Access Control (PBAC) to define the authorized actions and resources to which an authenticated user has access. Each policy describes one or more actions and conditions that outline the permissions of a user or group of users.
|
||||
<br />
|
||||
<br />
|
||||
Each user can access only those resources and operations which are explicitly granted by the built-in role. MinIO denies access to any other resource or action by default.
|
||||
<br />
|
||||
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersHelpBox;
|
||||
@@ -26,9 +26,10 @@ const userListItem = Selector(".ReactVirtualized__Table__rowColumn").withText(
|
||||
);
|
||||
|
||||
const userDeleteIconButton = userListItem
|
||||
.nextSibling()
|
||||
.child("button")
|
||||
.withAttribute("aria-label", "delete");
|
||||
.child("checkbox")
|
||||
.withAttribute("aria-label", "secondary checkbox");
|
||||
|
||||
const userCheckbox = Selector(".TableCheckbox");
|
||||
|
||||
fixture("For user with Users permissions")
|
||||
.page("http://localhost:9090")
|
||||
@@ -78,12 +79,15 @@ test("Users table exists", async (t) => {
|
||||
|
||||
test("Created User can be viewed and deleted", async (t) => {
|
||||
const userListItemExists = userListItem.exists;
|
||||
const deleteSelectedButton =
|
||||
Selector("button:enabled").withExactText("Delete Selected");
|
||||
await t
|
||||
.navigateTo(usersPageUrl)
|
||||
.typeText(elements.searchResourceInput, constants.TEST_USER_NAME)
|
||||
.expect(userListItemExists)
|
||||
.ok()
|
||||
.click(userDeleteIconButton)
|
||||
.click(userCheckbox)
|
||||
.click(deleteSelectedButton)
|
||||
.click(elements.deleteButton)
|
||||
.expect(userListItemExists)
|
||||
.notOk();
|
||||
|
||||
Reference in New Issue
Block a user