From 406345e257b38b15d0e755c4c79e8bbec25904b1 Mon Sep 17 00:00:00 2001
From: Harsh Vador <58542468+harsh-vador@users.noreply.github.com>
Date: Mon, 15 Jan 2024 17:23:10 +0530
Subject: [PATCH] Add & Organise Cypress User spec (#14624)
* add user spec
* organize user spec
* change test description
* cover different user roles
* change max password length
* code refactor
* fix failing update admin details test
---------
Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
---
.../ui/cypress/common/Entities/UserClass.ts | 151 +++++++
.../ui/cypress/common/Utils/Users.ts | 382 ++++++++++++++++++
.../resources/ui/cypress/common/common.js | 134 ------
.../ui/cypress/constants/constants.js | 70 ++++
.../ui/cypress/constants/service.constants.js | 4 +
.../ui/cypress/e2e/Pages/Users.spec.js | 227 -----------
.../ui/cypress/e2e/Pages/Users.spec.ts | 292 +++++++++++++
.../resources/ui/cypress/support/commands.js | 12 +-
.../resources/ui/cypress/support/index.ts | 6 +-
.../ui/src/constants/regex.constants.ts | 2 +-
.../src/pages/UserListPage/UserListPageV1.tsx | 4 +-
11 files changed, 914 insertions(+), 370 deletions(-)
create mode 100644 openmetadata-ui/src/main/resources/ui/cypress/common/Entities/UserClass.ts
create mode 100644 openmetadata-ui/src/main/resources/ui/cypress/common/Utils/Users.ts
delete mode 100644 openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Users.spec.js
create mode 100644 openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Users.spec.ts
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/Entities/UserClass.ts b/openmetadata-ui/src/main/resources/ui/cypress/common/Entities/UserClass.ts
new file mode 100644
index 00000000000..0ee0c622a6e
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/cypress/common/Entities/UserClass.ts
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2022 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+ descriptionBox,
+ interceptURL,
+ verifyResponseStatusCode,
+} from '../../common/common';
+import { VISIT_SERVICE_PAGE_DETAILS } from '../../constants/service.constants';
+import {
+ permanentDeleteUser,
+ restoreUser,
+ softDeleteUser,
+} from '../Utils/Users';
+
+class UsersTestClass {
+ protected name: string;
+
+ public getName() {
+ return this.name;
+ }
+
+ visitUserListPage() {
+ cy.get('[data-testid="app-bar-item-settings"]')
+ .should('exist')
+ .should('be.visible')
+ .click();
+ interceptURL('GET', '/api/v1/users?*', 'getUsers');
+ cy.get('[data-testid="settings-left-panel"]').contains('Users').click();
+ }
+
+ softDeleteUser(name) {
+ interceptURL('GET', '/api/v1/users?*', 'getUsers');
+ verifyResponseStatusCode('@getUsers', 200);
+ softDeleteUser(name);
+ }
+
+ restoreSoftDeletedUser(name, editedName) {
+ restoreUser(name, editedName);
+ }
+
+ permanentDeleteUser(name) {
+ permanentDeleteUser(name);
+ cy.logout();
+ }
+
+ checkConsumerPermissions() {
+ // check Add domain permission
+ cy.get('[data-testid="add-domain"]').should('not.be.exist');
+ cy.get('[data-testid="edit-displayName-button"]').should('not.be.exist');
+ // check edit owner permission
+ cy.get('[data-testid="edit-owner"]').should('not.be.exist');
+ // check edit description permission
+ cy.get('[data-testid="edit-description"]').should('be.exist');
+ // check edit tier permission
+ cy.get('[data-testid="edit-tier"]').should('be.exist');
+ // check add tags button
+ cy.get(
+ ':nth-child(2) > [data-testid="tags-container"] > [data-testid="entity-tags"] > .m-t-xss > .ant-tag'
+ ).should('be.exist');
+ // check add glossary term button
+ cy.get(
+ ':nth-child(3) > [data-testid="glossary-container"] > [data-testid="entity-tags"] > .m-t-xss > .ant-tag'
+ ).should('be.exist');
+ // check edit tier permission
+ cy.get('[data-testid="manage-button"]').should('not.be.exist');
+ cy.get('[data-testid="lineage"] > .ant-space-item').click();
+ cy.get('[data-testid="edit-lineage"]').should('be.disabled');
+ }
+
+ checkStewardServicesPermissions() {
+ cy.get('[data-testid="app-bar-item-explore"]').click();
+ Object.values(VISIT_SERVICE_PAGE_DETAILS).forEach((service) => {
+ cy.get('[data-testid="app-bar-item-settings"]').click();
+ cy.get(`[data-menu-id*="${service.settingsMenuId}"]`).click();
+ cy.get('[data-testid="add-service-button"] > span').should('not.exist');
+ });
+ cy.get('[data-testid="app-bar-item-explore"]').click();
+ cy.get('[data-testid="tables-tab"] > .ant-space').click();
+ cy.get(
+ '.ant-drawer-title > [data-testid="entity-link"] > .ant-typography'
+ ).click();
+ cy.get('[data-testid="edit-tier"]').should('be.visible');
+ }
+
+ checkStewardPermissions() {
+ // check Add domain permission
+ cy.get('[data-testid="add-domain"]').should('not.be.exist');
+ cy.get('[data-testid="edit-displayName-button"]').should('be.exist');
+ // check edit owner permission
+ cy.get('[data-testid="edit-owner"]').should('be.exist');
+ // check edit description permission
+ cy.get('[data-testid="edit-description"]').should('be.exist');
+ // check edit tier permission
+ cy.get('[data-testid="edit-tier"]').should('be.exist');
+ // check add tags button
+ cy.get(
+ ':nth-child(2) > [data-testid="tags-container"] > [data-testid="entity-tags"] > .m-t-xss > .ant-tag'
+ ).should('be.exist');
+ // check add glossary term button
+ cy.get(
+ ':nth-child(3) > [data-testid="glossary-container"] > [data-testid="entity-tags"] > .m-t-xss > .ant-tag'
+ ).should('be.exist');
+ // check edit tier permission
+ cy.get('[data-testid="manage-button"]').should('be.exist');
+ cy.get('[data-testid="lineage"] > .ant-space-item').click();
+ cy.get('[data-testid="edit-lineage"]').should('be.enabled');
+ }
+
+ restoreAdminDetails() {
+ cy.get('[data-testid="dropdown-profile"]').click({ force: true });
+ cy.get('[data-testid="user-name"] > .ant-typography').click({
+ force: true,
+ });
+ cy.get('[data-testid="edit-displayName"]').should('be.visible');
+ cy.get('[data-testid="edit-displayName"]').click();
+ cy.get('[data-testid="displayName"]').clear();
+ interceptURL('PATCH', '/api/v1/users/*', 'updateName');
+ cy.get('[data-testid="inline-save-btn"]').click();
+ cy.get('[data-testid="edit-displayName"]').scrollIntoView();
+ verifyResponseStatusCode('@updateName', 200);
+
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').click();
+ cy.get('[data-testid="edit-teams-button"]').click();
+ interceptURL('PATCH', '/api/v1/users/*', 'updateTeam');
+ cy.get('.ant-select-selection-item-remove > .anticon').click();
+ cy.get('[data-testid="inline-save-btn"]').click();
+ verifyResponseStatusCode('@updateTeam', 200);
+
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').click();
+ cy.get('[data-testid="edit-description"]').click();
+ cy.get(descriptionBox).clear();
+ interceptURL('PATCH', '/api/v1/users/*', 'patchDescription');
+ cy.get('[data-testid="save"]').should('be.visible').click();
+ verifyResponseStatusCode('@patchDescription', 200);
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').scrollIntoView();
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').click();
+ }
+}
+
+export default UsersTestClass;
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/Utils/Users.ts b/openmetadata-ui/src/main/resources/ui/cypress/common/Utils/Users.ts
new file mode 100644
index 00000000000..85adf015c8d
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/cypress/common/Utils/Users.ts
@@ -0,0 +1,382 @@
+/*
+ * Copyright 2024 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+ customFormatDateTime,
+ getEpochMillisForFutureDays,
+} from '../../../src/utils/date-time/DateTimeUtils';
+import {
+ descriptionBox,
+ interceptURL,
+ toastNotification,
+ verifyResponseStatusCode,
+} from '../common';
+
+export const addUser = ({
+ name,
+ email,
+ password,
+ role,
+}: {
+ name: string;
+ email: string;
+ password: string;
+ role: string;
+}) => {
+ cy.get('[data-testid="add-user"]').click();
+
+ cy.get('[data-testid="email"]')
+ .scrollIntoView()
+ .should('exist')
+ .should('be.visible')
+ .type(email);
+ cy.get('[data-testid="displayName"]')
+ .should('exist')
+ .should('be.visible')
+ .type(name);
+ cy.get(descriptionBox)
+ .should('exist')
+ .should('be.visible')
+ .type('Adding user');
+ cy.get(':nth-child(2) > .ant-radio > .ant-radio-input').click();
+ cy.get('#password').type(password);
+ cy.get('#confirmPassword').type(password);
+ cy.get('[data-testid="roles-dropdown"] > .ant-select-selector')
+ .click()
+ .type(role);
+ cy.get('.ant-select-item-option-content').click();
+ cy.get('[data-testid="roles-dropdown"] > .ant-select-selector').click();
+ interceptURL('POST', ' /api/v1/users', 'add-user');
+ cy.get('[data-testid="save-user"]').scrollIntoView().click();
+ verifyResponseStatusCode('@add-user', 201);
+ interceptURL('GET', '/api/v1/users?*', 'getUsers');
+
+ verifyResponseStatusCode('@getUsers', 200);
+};
+
+export const visitProfileSection = () => {
+ interceptURL('GET', '/api/v1/users?*', 'getUsers');
+ verifyResponseStatusCode('@getUsers', 200);
+ cy.get('[data-testid="dropdown-profile"]').click({ force: true });
+ cy.get('[data-testid="user-name"] > .ant-typography').click({
+ force: true,
+ });
+ cy.get('[data-testid="access-token"] > .ant-space-item').click();
+};
+export const softDeleteUser = (username: string) => {
+ // Search the created user
+ interceptURL(
+ 'GET',
+ '/api/v1/search/query?q=**&from=0&size=*&index=*',
+ 'searchUser'
+ );
+ cy.get('[data-testid="searchbar"]').type(username);
+
+ verifyResponseStatusCode('@searchUser', 200);
+
+ // Click on delete button
+ cy.get(`[data-testid="delete-user-btn-${username}"]`);
+ cy.get(':nth-child(4) > .ant-space > .ant-space-item > .ant-btn').click();
+ // Soft deleting the user
+ cy.get('[data-testid="soft-delete"]').click();
+ cy.get('[data-testid="confirmation-text-input"]').type('DELETE');
+
+ interceptURL(
+ 'DELETE',
+ '/api/v1/users/*?hardDelete=false&recursive=false',
+ 'softdeleteUser'
+ );
+ interceptURL('GET', '/api/v1/users*', 'userDeleted');
+ cy.get('[data-testid="confirm-button"]').click();
+ verifyResponseStatusCode('@softdeleteUser', 200);
+ verifyResponseStatusCode('@userDeleted', 200);
+
+ toastNotification('User deleted successfully!');
+
+ interceptURL('GET', '/api/v1/search/query*', 'searchUser');
+
+ // Verifying the deleted user
+ cy.get('[data-testid="searchbar"]').scrollIntoView().clear().type(username);
+
+ verifyResponseStatusCode('@searchUser', 200);
+};
+
+export const restoreUser = (username: string, editedUserName: string) => {
+ interceptURL('GET', '/api/v1/users?*', 'getUsers');
+
+ verifyResponseStatusCode('@getUsers', 200);
+ // Click on deleted user toggle
+ cy.get('[data-testid="show-deleted"]').click();
+ interceptURL('GET', '/api/v1/search/query*', 'searchUser');
+ verifyResponseStatusCode('@getUsers', 200);
+
+ cy.get('[data-testid="searchbar"]').type(username);
+
+ verifyResponseStatusCode('@searchUser', 200);
+
+ cy.get(`[data-testid="restore-user-btn-${username}"]`).click();
+ cy.get('.ant-modal-body > p').should(
+ 'contain',
+ `Are you sure you want to restore ${editedUserName}?`
+ );
+ interceptURL('PUT', '/api/v1/users', 'restoreUser');
+ cy.get('.ant-modal-footer > .ant-btn-primary').click();
+ verifyResponseStatusCode('@restoreUser', 200);
+ toastNotification('User restored successfully');
+};
+
+export const permanentDeleteUser = (username: string) => {
+ interceptURL('GET', '/api/v1/users?*', 'getUsers');
+ interceptURL('GET', '/api/v1/users/name/*', 'getUser');
+ verifyResponseStatusCode('@getUsers', 200);
+ verifyResponseStatusCode('@getUser', 200);
+ interceptURL('GET', '/api/v1/search/query*', 'searchUser');
+ cy.get('[data-testid="searchbar"]').type(username);
+ verifyResponseStatusCode('@searchUser', 200);
+ cy.get(`[data-testid="delete-user-btn-${username}"]`).click();
+ cy.get('[data-testid="hard-delete"]').click();
+ cy.get('[data-testid="confirmation-text-input"]').type('DELETE');
+ interceptURL(
+ 'DELETE',
+ 'api/v1/users/*?hardDelete=true&recursive=false',
+ 'hardDeleteUser'
+ );
+ cy.get('[data-testid="confirm-button"]')
+ .should('exist')
+ .should('be.visible')
+ .click();
+ verifyResponseStatusCode('@hardDeleteUser', 200);
+
+ toastNotification('User deleted successfully!');
+
+ interceptURL(
+ 'GET',
+ 'api/v1/search/query?q=**&from=0&size=15&index=user_search_index',
+ 'searchUser'
+ );
+
+ cy.get('[data-testid="searchbar"]').type(username);
+ verifyResponseStatusCode('@searchUser', 200);
+
+ cy.get('[data-testid="search-error-placeholder"]').should('be.exist');
+};
+export const visitUserListPage = () => {
+ cy.get('[data-testid="app-bar-item-settings"]')
+ .should('exist')
+ .should('be.visible')
+ .click();
+ interceptURL('GET', '/api/v1/users?*', 'getUsers');
+ cy.get('[data-testid="settings-left-panel"]').contains('Users').click();
+};
+
+export const generateToken = () => {
+ cy.get('[data-testid="no-token"]').should('be.visible');
+ cy.get('[data-testid="auth-mechanism"] > span').click();
+ cy.get('[data-testid="token-expiry"]').should('be.visible').click();
+ cy.contains('1 hr').should('exist').should('be.visible').click();
+ cy.get('[data-testid="token-expiry"]').should('be.visible');
+ cy.get('[data-testid="save-edit"]').should('be.visible').click();
+};
+
+export const revokeToken = () => {
+ cy.get('[data-testid="revoke-button"]').should('be.visible').click();
+ cy.get('[data-testid="body-text"]').should(
+ 'contain',
+ 'Are you sure you want to revoke access for Personal Access Token?'
+ );
+ cy.get('[data-testid="save-button"]').click();
+ cy.get('[data-testid="revoke-button"]').should('not.exist');
+};
+
+export const updateExpiration = (expiry: number) => {
+ cy.get('[data-testid="dropdown-profile"]').click();
+ cy.get('[data-testid="user-name"] > .ant-typography').click({
+ force: true,
+ });
+ cy.get('[data-testid="access-token"] > .ant-space-item').click();
+ cy.get('[data-testid="no-token"]').should('be.visible');
+ cy.get('[data-testid="auth-mechanism"] > span').click();
+
+ cy.get('[data-testid="token-expiry"]').click();
+ cy.contains(`${expiry} days`).click();
+ const expiryDate = customFormatDateTime(
+ getEpochMillisForFutureDays(expiry),
+ `ccc d'th' MMMM, yyyy`
+ );
+ cy.get('[data-testid="save-edit"]').click();
+ cy.get('[data-testid="center-panel"]')
+ .find('[data-testid="revoke-button"]')
+ .should('be.visible');
+ cy.get('[data-testid="token-expiry"]')
+ .invoke('text')
+ .should('contain', `Expires on ${expiryDate}`);
+ cy.get('[data-testid="token-expiry"]').click();
+ revokeToken();
+};
+
+export const editDisplayName = (editedUserName: string) => {
+ cy.get('[data-testid="edit-displayName"]').should('be.visible');
+ cy.get('[data-testid="edit-displayName"]').click();
+ cy.get('[data-testid="displayName"]').clear();
+ cy.get('[data-testid="displayName"]').type(editedUserName);
+ interceptURL('PATCH', '/api/v1/users/*', 'updateName');
+ cy.get('[data-testid="inline-save-btn"]').click();
+ cy.get('[data-testid="edit-displayName"]').scrollIntoView();
+ cy.get('[data-testid="user-name"]').should('contain', editedUserName);
+};
+
+export const editDescription = (updatedDescription: string) => {
+ cy.get('[data-testid="edit-description"]').click();
+ cy.get(descriptionBox).clear().type(updatedDescription);
+ interceptURL('PATCH', '/api/v1/users/*', 'patchDescription');
+ cy.get('[data-testid="save"]').should('be.visible').click();
+ verifyResponseStatusCode('@patchDescription', 200);
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').scrollIntoView();
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').click();
+ cy.get(
+ ':nth-child(2) > :nth-child(1) > [data-testid="viewer-container"] > [data-testid="markdown-parser"] > :nth-child(1) > .toastui-editor-contents > p'
+ ).should('contain', updatedDescription);
+};
+
+export const editTeams = (teamName: string) => {
+ cy.get('[data-testid="edit-teams-button"]').click();
+ cy.get('.ant-select-selection-item-remove > .anticon').click();
+ cy.get('[data-testid="team-select"]').click();
+ cy.get('[data-testid="team-select"]').type(teamName);
+ interceptURL('PATCH', '/api/v1/users/*', 'updateTeams');
+ cy.get('.filter-node > .ant-select-tree-node-content-wrapper').click();
+ cy.get('[data-testid="inline-save-btn"]').click({ timeout: 10000 });
+ verifyResponseStatusCode('@updateTeams', 200);
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').scrollIntoView();
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').click();
+ cy.get(`[data-testid="${teamName}"]`).should('exist').and('be.visible');
+};
+
+export const handleUserUpdateDetails = (
+ editedUserName: string,
+ updatedDescription: string
+) => {
+ cy.get('[data-testid="dropdown-profile"]').click({ force: true });
+ cy.get('[data-testid="user-name"] > .ant-typography').click({
+ force: true,
+ });
+ // edit displayName
+ editDisplayName(editedUserName);
+ // edit description
+ cy.wait(500);
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').scrollIntoView();
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').click();
+ editDescription(updatedDescription);
+
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').scrollIntoView();
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').click();
+};
+
+export const handleAdminUpdateDetails = (
+ editedUserName: string,
+ updatedDescription: string,
+ teamName: string,
+ role?: string
+) => {
+ // edit displayName
+ cy.get('[data-testid="dropdown-profile"]').click({ force: true });
+ cy.get('[data-testid="user-name"] > .ant-typography').click({
+ force: true,
+ });
+ editDisplayName(editedUserName);
+
+ // edit teams
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').scrollIntoView();
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').click();
+ editTeams(teamName);
+
+ // edit description
+ cy.wait(500);
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').scrollIntoView();
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').click();
+ editDescription(updatedDescription);
+
+ // edit roles
+ cy.get(`[data-testid="chip-container"]`).should('contain', role);
+};
+
+export const updateDetails = ({
+ updatedDisplayName,
+ updatedDescription,
+ isAdmin,
+ teamName,
+ role,
+}: {
+ email: string;
+ password: string;
+ updatedDisplayName: string;
+ updatedDescription: string;
+ teamName: string;
+ isAdmin?: boolean;
+ role?: string;
+}) => {
+ isAdmin
+ ? handleAdminUpdateDetails(
+ updatedDisplayName,
+ updatedDescription,
+ teamName,
+ role
+ )
+ : handleUserUpdateDetails(updatedDisplayName, updatedDescription);
+};
+
+export const resetPassword = (password: string, newPassword: string) => {
+ cy.get('[data-testid="dropdown-profile"]').click({ force: true });
+ cy.get('[data-testid="user-name"] > .ant-typography').click({
+ force: true,
+ });
+ cy.clickOutside();
+ cy.get('[data-testid="change-password-button"]').click();
+ cy.get('.ant-modal-wrap').should('be.visible');
+ cy.get('[data-testid="input-oldPassword"]').clear().type(password);
+ cy.get('[data-testid="input-newPassword"]').clear().type(newPassword);
+ cy.get('[data-testid="input-confirm-newPassword"]').clear().type(newPassword);
+ interceptURL('PUT', '/api/v1/users/changePassword', 'changePassword');
+ cy.get('.ant-modal-footer > .ant-btn-primary')
+ .contains('Update Password')
+ .click();
+ verifyResponseStatusCode('@changePassword', 200);
+ toastNotification('Password updated successfully.');
+};
+
+export const editRole = (username: string, role: string) => {
+ interceptURL('GET', '/api/v1/users?*', 'getUsers');
+ verifyResponseStatusCode('@getUsers', 200);
+
+ // Search the created user
+ interceptURL(
+ 'GET',
+ '/api/v1/search/query?q=**&from=0&size=*&index=*',
+ 'searchUser'
+ );
+ cy.get('[data-testid="searchbar"]').type(username);
+ verifyResponseStatusCode('@searchUser', 200);
+ cy.get(`[data-testid=${username}]`).click();
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').scrollIntoView();
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').click();
+ cy.get('[data-testid="edit-roles-button"]').click();
+ cy.get('.ant-select-selection-item-remove > .anticon').click();
+ cy.get('[data-testid="inline-edit-container"] #select-role')
+ .click()
+ .type(role);
+ cy.get('.ant-select-item-option-content').contains(role).click();
+ interceptURL('PATCH', `/api/v1/users/*`, 'updateRole');
+ cy.get('[data-testid="inline-save-btn"]').click();
+ verifyResponseStatusCode('@updateRole', 200);
+ cy.get('.ant-collapse-expand-icon > .anticon > svg').scrollIntoView();
+ cy.get(`[data-testid=chip-container]`).should('contain', role);
+};
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js
index 235dfe0a666..08d46bc7ea6 100644
--- a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js
+++ b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js
@@ -657,140 +657,6 @@ export const addNewTagToEntity = (entityObj, term) => {
}
};
-export const addUser = (username, email) => {
- cy.get('[data-testid="email"]')
- .scrollIntoView()
- .should('exist')
- .should('be.visible')
- .type(email);
- cy.get('[data-testid="displayName"]')
- .should('exist')
- .should('be.visible')
- .type(username);
- cy.get(descriptionBox)
- .should('exist')
- .should('be.visible')
- .type('Adding user');
- interceptURL('GET', ' /api/v1/users/generateRandomPwd', 'generatePassword');
- cy.get('[data-testid="password-generator"]').should('be.visible').click();
- interceptURL('POST', ' /api/v1/users', 'add-user');
- verifyResponseStatusCode('@generatePassword', 200);
- cy.get('[data-testid="save-user"]').scrollIntoView().click();
- verifyResponseStatusCode('@add-user', 201);
-};
-
-export const softDeleteUser = (username, isAdmin) => {
- // Search the created user
- interceptURL(
- 'GET',
- '/api/v1/search/query?q=**&from=0&size=*&index=*',
- 'searchUser'
- );
- cy.get('[data-testid="searchbar"]').type(username);
-
- verifyResponseStatusCode('@searchUser', 200);
-
- // Click on delete button
- cy.get(`[data-testid="delete-user-btn-${username}"]`).click();
-
- // Soft deleting the user
- cy.get('[data-testid="soft-delete"]').click();
- cy.get('[data-testid="confirmation-text-input"]').type('DELETE');
-
- interceptURL(
- 'DELETE',
- '/api/v1/users/*?hardDelete=false&recursive=false',
- 'softdeleteUser'
- );
- interceptURL('GET', '/api/v1/users*', 'userDeleted');
- cy.get('[data-testid="confirm-button"]').click();
- verifyResponseStatusCode('@softdeleteUser', 200);
- verifyResponseStatusCode('@userDeleted', 200);
-
- toastNotification('User deleted successfully!');
-
- interceptURL('GET', '/api/v1/search/query*', 'searchUser');
-
- // Verifying the deleted user
- cy.get('[data-testid="searchbar"]').scrollIntoView().clear().type(username);
-
- verifyResponseStatusCode('@searchUser', 200);
- cy.get('[data-testid="search-error-placeholder"]').should('be.visible');
-};
-
-export const restoreUser = (username) => {
- // Click on deleted user toggle
- interceptURL('GET', '/api/v1/users*', 'deletedUser');
- cy.get('[data-testid="show-deleted"]').click();
- verifyResponseStatusCode('@deletedUser', 200);
- interceptURL(
- 'GET',
- '/api/v1/search/query?q=**&from=0&size=*&index=*',
- 'searchUser'
- );
- cy.get('[data-testid="searchbar"]').type(username);
-
- verifyResponseStatusCode('@searchUser', 200);
-
- cy.get(`[data-testid="restore-user-btn-${username}"]`).click();
- cy.get('.ant-modal-body > p').should(
- 'contain',
- `Are you sure you want to restore ${username}?`
- );
- interceptURL('PUT', '/api/v1/users', 'restoreUser');
- cy.get('.ant-modal-footer > .ant-btn-primary').click();
- verifyResponseStatusCode('@restoreUser', 200);
- toastNotification('User restored successfully');
-
- // Verifying the restored user
- cy.get('[data-testid="show-deleted"]').click();
-
- interceptURL('GET', '/api/v1/search/query*', 'searchUser');
- cy.get('[data-testid="searchbar"]').type(username);
- verifyResponseStatusCode('@searchUser', 200);
- cy.get(`[data-testid=${username}]`).should('exist');
-};
-
-export const deleteSoftDeletedUser = (username) => {
- interceptURL('GET', '/api/v1/users?*', 'getUsers');
-
- cy.get('.ant-switch-handle').should('exist').should('be.visible').click();
-
- verifyResponseStatusCode('@getUsers', 200);
-
- cy.get(`[data-testid="delete-user-btn-${username}"]`)
- .should('exist')
- .should('be.visible')
- .click();
- cy.get('[data-testid="confirmation-text-input"]').type('DELETE');
- interceptURL(
- 'DELETE',
- 'api/v1/users/*?hardDelete=true&recursive=false',
- 'hardDeleteUser'
- );
- cy.get('[data-testid="confirm-button"]')
- .should('exist')
- .should('be.visible')
- .click();
- verifyResponseStatusCode('@hardDeleteUser', 200);
-
- toastNotification('User deleted successfully!');
-
- interceptURL(
- 'GET',
- 'api/v1/search/query?q=**&from=0&size=15&index=user_search_index',
- 'searchUser'
- );
-
- cy.get('[data-testid="searchbar"]')
- .should('exist')
- .should('be.visible')
- .type(username);
- verifyResponseStatusCode('@searchUser', 200);
-
- cy.get('[data-testid="search-error-placeholder"]').should('be.visible');
-};
-
export const toastNotification = (msg, closeToast = true) => {
cy.get('.Toastify__toast-body').should('contain.text', msg);
cy.wait(200);
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js b/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js
index 3b638f72f62..36faef86787 100644
--- a/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js
+++ b/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js
@@ -740,3 +740,73 @@ export const DOMAIN_3 = {
},
],
};
+export const GLOBAL_SETTING_PERMISSIONS = {
+ metadata: {
+ testid: '[data-menu-id*="metadata"]',
+ },
+ customAttributesTable: {
+ testid: '[data-menu-id*="tables"]',
+ },
+ customAttributesTopics: {
+ testid: '[data-menu-id*="topics"]',
+ },
+ customAttributesDashboards: {
+ testid: '[data-menu-id*="customAttributes.dashboards"]',
+ },
+ customAttributesPipelines: {
+ testid: '[data-menu-id*="customAttributes.pipelines"]',
+ },
+ customAttributesMlModels: {
+ testid: '[data-menu-id*="customAttributes.mlModels"]',
+ },
+ bots: {
+ testid: '[data-menu-id*="bots"]',
+ },
+};
+export const ID = {
+ teams: {
+ testid: '[data-menu-id*="teams"]',
+ button: 'add-team',
+ api: '/api/v1/teams/name/Organization?*',
+ },
+ users: {
+ testid: '[data-menu-id*="users"]',
+ button: 'add-user',
+ api: '/api/v1/users?*',
+ },
+ admins: {
+ testid: '[data-menu-id*="admins"]',
+ button: 'add-user',
+ api: '/api/v1/users?*',
+ },
+ databases: {
+ testid: '[data-menu-id*="databases"]',
+ button: 'add-service-button',
+ api: '/api/v1/services/databaseServices?*',
+ },
+ messaging: {
+ testid: '[data-menu-id*="messaging"]',
+ button: 'add-service-button',
+ api: '/api/v1/services/messagingServices?*',
+ },
+ dashboard: {
+ testid: '[data-menu-id*="services.dashboards"]',
+ button: 'add-service-button',
+ api: '/api/v1/services/dashboardServices?*',
+ },
+ pipelines: {
+ testid: '[data-menu-id*="services.pipelines"]',
+ button: 'add-service-button',
+ api: '/api/v1/services/pipelineServices?*',
+ },
+ mlmodels: {
+ testid: '[data-menu-id*="services.mlmodels"]',
+ button: 'add-service-button',
+ api: '/api/v1/services/mlmodelServices?*',
+ },
+ storage: {
+ testid: '[data-menu-id*="services.storages"]',
+ button: 'add-service-button',
+ api: '/api/v1/services/storageServices?*',
+ },
+};
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/constants/service.constants.js b/openmetadata-ui/src/main/resources/ui/cypress/constants/service.constants.js
index a5ec97989c2..9aec7a6c4a9 100644
--- a/openmetadata-ui/src/main/resources/ui/cypress/constants/service.constants.js
+++ b/openmetadata-ui/src/main/resources/ui/cypress/constants/service.constants.js
@@ -69,4 +69,8 @@ export const VISIT_SERVICE_PAGE_DETAILS = {
settingsMenuId: 'services.search',
serviceCategory: SERVICE_CATEGORIES.SEARCH_SERVICES,
},
+ [SERVICE_TYPE.Metadata]: {
+ settingsMenuId: 'services.metadata',
+ serviceCategory: SERVICE_CATEGORIES.METADATA_SERVICES,
+ },
};
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Users.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Users.spec.js
deleted file mode 100644
index d5cbb08c2dd..00000000000
--- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Users.spec.js
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright 2022 Collate.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// eslint-disable-next-line spaced-comment
-///
-import {
- customFormatDateTime,
- getEpochMillisForFutureDays,
-} from '../../../src/utils/date-time/DateTimeUtils';
-import {
- addUser,
- deleteSoftDeletedUser,
- interceptURL,
- restoreUser,
- softDeleteUser,
- uuid,
- verifyResponseStatusCode,
-} from '../../common/common';
-
-const userName = `Usercttest${uuid()}`;
-const userEmail = `${userName}@gmail.com`;
-
-const adminName = `Admincttest${uuid()}`;
-const adminEmail = `${adminName}@gmail.com`;
-
-const searchBotText = 'bot';
-
-const expirationTime = {
- oneday: '1',
- sevendays: '7',
- onemonth: '30',
- twomonths: '60',
- threemonths: '90',
-};
-
-const revokeToken = () => {
- // Click on revoke button
- cy.get('[data-testid="revoke-button"]').should('be.visible').click();
- // Verify the revoke text
- cy.get('[data-testid="body-text"]').should(
- 'contain',
- 'Are you sure you want to revoke access for Personal Access Token?'
- );
- // Click on confirm button
- cy.get('[data-testid="save-button"]').click();
- // Verify the revoke is successful
- cy.get('[data-testid="revoke-button"]').should('not.exist');
-};
-
-describe('Users flow should work properly', () => {
- beforeEach(() => {
- cy.login();
-
- cy.get('[data-testid="app-bar-item-settings"]')
- .should('exist')
- .should('be.visible')
- .click();
- interceptURL('GET', '/api/v1/users?*', 'getUsers');
- cy.get('[data-testid="settings-left-panel"]').contains('Users').click();
- });
-
- it('Add new User', () => {
- // Clicking on Add user button
- cy.get('[data-testid="add-user"]').click();
-
- addUser(userName, userEmail);
- verifyResponseStatusCode('@getUsers', 200);
- });
-
- it('Soft delete user', () => {
- softDeleteUser(userName);
- });
-
- it('Restore soft deleted user', () => {
- restoreUser(userName);
- });
-
- it('Permanently Delete Soft Deleted User', () => {
- softDeleteUser(userName);
- deleteSoftDeletedUser(userName);
- });
-
- it('Search for bot user', () => {
- interceptURL(
- 'GET',
- `/api/v1/search/query?q=*${searchBotText}***isBot:false&from=0&size=25&index=user_search_index`,
- 'searchUser'
- );
- cy.get('[data-testid="searchbar"]')
- .should('exist')
- .should('be.visible')
- .type(searchBotText);
-
- verifyResponseStatusCode('@searchUser', 200);
- });
-});
-
-describe('Admin flow should work properly', () => {
- beforeEach(() => {
- cy.login();
-
- cy.get('[data-testid="app-bar-item-settings"]')
- .should('exist')
- .should('be.visible')
- .click();
- interceptURL('GET', '/api/v1/users?*isAdmin=true*', 'getAdmins');
- cy.get('.ant-menu-title-content')
- .contains('Admins')
- .should('exist')
- .should('be.visible')
- .click();
- });
-
- it('Add admin user', () => {
- // Clicking on add user button
- cy.get('[data-testid="add-user"]').click();
-
- // Setting the user to admin before adding user
- cy.get('[data-testid="admin"]')
- .scrollIntoView()
- .should('exist')
- .should('be.visible')
- .click();
-
- addUser(adminName, adminEmail);
- verifyResponseStatusCode('@getAdmins', 200);
-
- // Validate if user is added in the User tab
- interceptURL(
- 'GET',
- '/api/v1/search/query?q=**&from=0&size=*&index=*',
- 'searchUser'
- );
- cy.get('[data-testid="searchbar"]')
- .should('exist')
- .should('be.visible')
- .type(adminName);
- verifyResponseStatusCode('@searchUser', 200);
- cy.get('.ant-table-tbody ').should('contain', adminName);
- });
-
- it('Soft delete admin', () => {
- softDeleteUser(adminName, true);
- });
-
- it('Restore soft deleted admin', () => {
- restoreUser(adminName);
- });
-
- it('Permanently Delete Soft Deleted admin', () => {
- softDeleteUser(adminName, true);
- deleteSoftDeletedUser(adminName);
- });
-
- describe('Personal Access Token flow should work properly', () => {
- beforeEach(() => {
- cy.login();
- });
-
- it('Token should be generated and revoked', function () {
- // Enter profile section
- cy.get('.username').click();
- cy.get('[data-testid="user-name"] > .ant-typography').click();
- cy.get('[data-testid="access-token"] > .ant-space-item').click();
- cy.get('[data-testid="access-token"] > .ant-space-item').should(
- 'be.visible'
- );
-
- // generate token
- cy.get('[data-testid="no-token"]').should('be.visible');
- cy.get('[data-testid="auth-mechanism"] > span').click();
- cy.get('[data-testid="token-expiry"]').should('be.visible').click();
- cy.contains('1 hr').should('exist').should('be.visible').click();
- cy.get('[data-testid="token-expiry"]').should('be.visible');
- cy.get('[data-testid="save-edit"]').should('be.visible').click();
-
- // revoke token
- cy.get('[data-testid="revoke-button"] > span').should('be.visible');
- cy.get('[data-testid="revoke-button"] > span').click();
- cy.get('[data-testid="save-button"] > span').click();
- cy.get(':nth-child(1) > .ant-row > .ant-form-item-label > label').should(
- 'be.visible'
- );
- });
-
- Object.values(expirationTime).forEach((expiry) => {
- it(`Update token expiration for ${expiry} days`, () => {
- cy.get('.username').click();
- cy.get('[data-testid="user-name"] > .ant-typography').click();
- cy.get('[data-testid="access-token"] > .ant-space-item').click();
- cy.get('[data-testid="access-token"] > .ant-space-item').should(
- 'be.visible'
- );
- cy.get('[data-testid="no-token"]').should('be.visible');
- cy.get('[data-testid="auth-mechanism"] > span').click();
-
- cy.get('[data-testid="token-expiry"]').click();
- // Select the expiration period
- cy.contains(`${expiry} days`).click();
- // Save the updated date
- const expiryDate = customFormatDateTime(
- getEpochMillisForFutureDays(expiry),
- `ccc d'th' MMMM, yyyy`
- );
- cy.get('[data-testid="save-edit"]').click();
- cy.get('[data-testid="center-panel"]')
- .find('[data-testid="revoke-button"]')
- .should('be.visible');
- // Verify the expiry time
- cy.get('[data-testid="token-expiry"]')
- .invoke('text')
- .should('contain', `Expires on ${expiryDate}`);
- cy.get('[data-testid="token-expiry"]').click();
- revokeToken();
- });
- });
- });
-});
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Users.spec.ts b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Users.spec.ts
new file mode 100644
index 00000000000..e6313e514e3
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Users.spec.ts
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2024 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// eslint-disable-next-line spaced-comment
+import UsersTestClass from '../../common/Entities/UserClass';
+import { visitEntityDetailsPage } from '../../common/Utils/Entity';
+import { addOwner, removeOwner } from '../../common/Utils/Owner';
+import {
+ addUser,
+ editRole,
+ generateToken,
+ resetPassword,
+ revokeToken,
+ updateDetails,
+ updateExpiration,
+ visitUserListPage,
+} from '../../common/Utils/Users';
+import { interceptURL, verifyResponseStatusCode } from '../../common/common';
+import { EntityType } from '../../constants/Entity.interface';
+
+import {
+ BASE_URL,
+ DELETE_ENTITY,
+ GLOBAL_SETTING_PERMISSIONS,
+ ID,
+ uuid,
+} from '../../constants/constants';
+import { NAVBAR_DETAILS } from '../../constants/redirections.constants';
+const entity = new UsersTestClass();
+const expirationTime = {
+ oneday: '1',
+ sevendays: '7',
+ onemonth: '30',
+ twomonths: '60',
+ threemonths: '90',
+};
+const name = `Usercttest${uuid()}`;
+const glossary = NAVBAR_DETAILS.glossary;
+const tag = NAVBAR_DETAILS.tags;
+const ownerName = 'Aaron Warren';
+const user = {
+ name: name,
+ email: `${name}@gmail.com`,
+ password: `User@${uuid()}`,
+ updatedDisplayName: `Edited${uuid()}`,
+ newPassword: `NewUser@${uuid()}`,
+ teamName: 'Applications',
+ updatedDescription: 'This is updated description',
+ newStewardPassword: `StewUser@${uuid()}`,
+};
+
+describe('User with different Roles', () => {
+ it('Update own admin details', () => {
+ cy.login();
+ updateDetails({
+ ...user,
+ isAdmin: true,
+ role: 'Admin',
+ });
+ });
+
+ it('Create Data Consumer User', () => {
+ cy.login();
+ visitUserListPage();
+ addUser({ ...user, role: 'Data Consumer' });
+ cy.logout();
+ });
+
+ it('Reset Data Consumer Password', () => {
+ cy.login(user.email, user.password);
+
+ resetPassword(user.password, user.newPassword);
+ cy.logout();
+
+ cy.login(user.email, user.newPassword);
+ });
+
+ it('Token generation & revocation', () => {
+ cy.login(user.email, user.newPassword);
+ cy.get('[data-testid="dropdown-profile"]').click({ force: true });
+ cy.get('[data-testid="user-name"] > .ant-typography', {
+ timeout: 10000,
+ }).click();
+ cy.get('[data-testid="access-token"]').click();
+ generateToken();
+ revokeToken();
+ });
+
+ it(`Update token expiration`, () => {
+ cy.login(user.email, user.newPassword);
+ visitUserListPage();
+ Object.values(expirationTime).forEach((expiry) => {
+ updateExpiration(expiry);
+ });
+ });
+
+ it('Data Consumer user should have only view permission for glossary and tags', () => {
+ Cypress.session.clearAllSavedSessions();
+ cy.storeSession(user.email, user.newPassword);
+ cy.goToHomePage();
+ cy.url().should('eq', `${BASE_URL}/my-data`);
+
+ // Check CRUD for Glossary
+ cy.get(glossary.testid)
+ .should('be.visible')
+ .click({ animationDistanceThreshold: 10 });
+ if (glossary.subMenu) {
+ cy.get(glossary.subMenu).should('be.visible').click({ force: true });
+ }
+ cy.clickOutside();
+
+ cy.clickOnLogo();
+
+ // Check CRUD for Tags
+ cy.get(tag.testid)
+ .should('be.visible')
+ .click({ animationDistanceThreshold: 10 });
+ if (tag.subMenu) {
+ cy.get(tag.subMenu).should('be.visible').click({ force: true });
+ }
+ cy.get('body').click();
+ cy.wait(200);
+ cy.get('[data-testid="add-new-tag-button"]').should('not.exist');
+
+ cy.get('[data-testid="manage-button"]').should('not.exist');
+ });
+
+ it('Data Consumer operations for settings page', () => {
+ cy.storeSession(user.email, user.newPassword);
+ cy.goToHomePage();
+ cy.url().should('eq', `${BASE_URL}/my-data`);
+ // Navigate to settings
+ cy.get(NAVBAR_DETAILS.settings.testid).should('be.visible').click();
+ Object.values(ID).forEach((id) => {
+ if (id?.api) {
+ interceptURL('GET', id.api, 'getTabDetails');
+ }
+ cy.get(id.testid).should('be.visible').click();
+ if (id?.api) {
+ verifyResponseStatusCode('@getTabDetails', 200);
+ }
+ cy.get(`[data-testid="${id.button}"]`).should('not.be.exist');
+ });
+
+ Object.values(GLOBAL_SETTING_PERMISSIONS).forEach((id) => {
+ if (id.testid === '[data-menu-id*="metadata"]') {
+ cy.get(id.testid).should('be.visible').click();
+ } else {
+ cy.get(id.testid).should('not.be.exist');
+ }
+ });
+ });
+
+ it('Data Consumer permissions for table details page', () => {
+ cy.login();
+ visitEntityDetailsPage({
+ term: DELETE_ENTITY.table.term,
+ serviceName: DELETE_ENTITY.table.serviceName,
+ entity: EntityType.Table,
+ });
+ addOwner(ownerName);
+ cy.logout();
+ cy.login(user.email, user.newPassword);
+ visitEntityDetailsPage({
+ term: DELETE_ENTITY.table.term,
+ serviceName: DELETE_ENTITY.table.serviceName,
+ entity: EntityType.Table,
+ });
+ entity.checkConsumerPermissions();
+ });
+
+ it('Update Data Consumer details', () => {
+ cy.login(user.email, user.newPassword);
+ updateDetails({ ...user, isAdmin: false });
+ });
+
+ it('Update Data Steward details', () => {
+ // change role from consumer to steward
+ cy.login();
+ visitUserListPage();
+ editRole(user.name, 'Data Steward');
+ cy.logout();
+ // login to steward user
+ cy.login(user.email, user.newPassword);
+ updateDetails({ ...user, isAdmin: false });
+ cy.logout();
+ });
+
+ it('Reset Data Steward Password', () => {
+ cy.login(user.email, user.newPassword);
+ resetPassword(user.newPassword, user.newStewardPassword);
+ cy.logout();
+ cy.login(user.email, user.newStewardPassword);
+ });
+
+ it('Token generation & revocation for Data Steward', () => {
+ cy.login(user.email, user.newStewardPassword);
+ visitUserListPage();
+ cy.get('[data-testid="dropdown-profile"]').click({ force: true });
+ cy.get('[data-testid="user-name"] > .ant-typography').click({
+ force: true,
+ });
+ cy.get('[data-testid="access-token"] > .ant-space-item').click();
+ generateToken();
+ revokeToken();
+ });
+
+ it(`Update token expiration for Data Steward`, () => {
+ cy.login(user.email, user.newStewardPassword);
+ visitUserListPage();
+ Object.values(expirationTime).forEach((expiry) => {
+ updateExpiration(expiry);
+ });
+ });
+
+ it('Data Steward operations for settings page', () => {
+ cy.login(user.email, user.newStewardPassword);
+ cy.url().should('eq', `${BASE_URL}/my-data`);
+ // Navigate to settings
+ cy.get(NAVBAR_DETAILS.settings.testid).should('be.visible').click();
+ Object.values(ID).forEach((id) => {
+ if (id?.api) {
+ interceptURL('GET', id.api, 'getTabDetails');
+ }
+ cy.get(id.testid).should('be.visible').click();
+ if (id?.api) {
+ verifyResponseStatusCode('@getTabDetails', 200);
+ }
+ cy.get(`[data-testid="${id.button}"]`).should('not.be.exist');
+ });
+
+ Object.values(GLOBAL_SETTING_PERMISSIONS).forEach((id) => {
+ if (id.testid === '[data-menu-id*="metadata"]') {
+ cy.get(id.testid).should('be.visible').click();
+ } else {
+ cy.get(id.testid).should('not.be.exist');
+ }
+ });
+ });
+
+ it('Check Data Steward permissions', () => {
+ cy.login(user.email, user.newStewardPassword);
+ entity.checkStewardServicesPermissions();
+ cy.goToHomePage();
+ visitEntityDetailsPage({
+ term: DELETE_ENTITY.table.term,
+ serviceName: DELETE_ENTITY.table.serviceName,
+ entity: EntityType.Table,
+ });
+ entity.checkStewardPermissions();
+ cy.logout();
+ });
+
+ it('Admin Soft delete user', () => {
+ cy.login();
+ visitUserListPage();
+ entity.softDeleteUser(user.name);
+ });
+
+ it('Admin Restore soft deleted user', () => {
+ cy.login();
+ visitUserListPage();
+ entity.restoreSoftDeletedUser(user.name, user.updatedDisplayName);
+ });
+
+ it('Admin Permanent Delete User', () => {
+ cy.login();
+ visitUserListPage();
+ entity.permanentDeleteUser(user.name);
+ });
+
+ it('Restore Admin Details', () => {
+ cy.login();
+ entity.restoreAdminDetails();
+ cy.goToHomePage();
+ visitEntityDetailsPage({
+ term: DELETE_ENTITY.table.term,
+ serviceName: DELETE_ENTITY.table.serviceName,
+ entity: EntityType.Table,
+ });
+ removeOwner(ownerName);
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/support/commands.js b/openmetadata-ui/src/main/resources/ui/cypress/support/commands.js
index 1c86b80c631..40311feb3a1 100644
--- a/openmetadata-ui/src/main/resources/ui/cypress/support/commands.js
+++ b/openmetadata-ui/src/main/resources/ui/cypress/support/commands.js
@@ -130,10 +130,13 @@ Cypress.Commands.add('storeSession', (username, password) => {
});
});
-Cypress.Commands.add('login', () => {
- cy.storeSession(LOGIN.username, LOGIN.password);
- cy.goToHomePage();
-});
+Cypress.Commands.add(
+ 'login',
+ (username = LOGIN.username, password = LOGIN.password) => {
+ cy.storeSession(username, password);
+ cy.goToHomePage();
+ }
+);
Cypress.Commands.add('clickOutside', function () {
return cy.get('body').click(0, 0); // 0,0 here are the x and y coordinates
@@ -149,4 +152,5 @@ Cypress.Commands.add('logout', () => {
verifyResponseStatusCode('@logoutUser', 200);
cy.url().should('eq', `${BASE_URL}/signin`);
+ Cypress.session.clearAllSavedSessions();
});
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/support/index.ts b/openmetadata-ui/src/main/resources/ui/cypress/support/index.ts
index 1fcb5f8a9af..0b7157eb2f4 100644
--- a/openmetadata-ui/src/main/resources/ui/cypress/support/index.ts
+++ b/openmetadata-ui/src/main/resources/ui/cypress/support/index.ts
@@ -20,7 +20,11 @@ declare global {
* Custom command to select DOM element by data-cy attribute.
* @example cy.login()
*/
- login(): void;
+ login(email?: string, password?: string): void;
+ logout(): void;
+ goToHomePage(doNotNavigate: boolean): void;
+ clickOnLogo(): void;
+ clickOutside(): void;
}
}
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts
index f1e8f36be76..8ad1d63fe45 100644
--- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts
@@ -32,7 +32,7 @@ export const delimiterRegex = /[\\[\]\\()\\;\\,\\|\\{}\\``\\/\\<>\\^]/g;
export const nameWithSpace = /\s/g;
export const passwordRegex =
- /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[^\w\d\s:])([^\s]){8,16}$/g;
+ /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[^\w\d\s:])([^\s]){8,56}$/g;
export const allowedNameRegEx = /[`!@#$%^&*()+=[\]{};:"\\|,.<>/?~]/;
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/UserListPage/UserListPageV1.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/UserListPage/UserListPageV1.tsx
index 0168a6e11d5..11a925b94d4 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/UserListPage/UserListPageV1.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/UserListPage/UserListPageV1.tsx
@@ -324,9 +324,7 @@ const UserListPageV1 = () => {
disabled={!isAdminUser}
icon={