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>
This commit is contained in:
Harsh Vador 2024-01-15 17:23:10 +05:30 committed by GitHub
parent c87b6d60fa
commit 406345e257
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 914 additions and 370 deletions

View File

@ -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;

View File

@ -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);
};

View File

@ -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);

View File

@ -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?*',
},
};

View File

@ -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,
},
};

View File

@ -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
/// <reference types="cypress" />
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();
});
});
});
});

View File

@ -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);
});
});

View File

@ -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();
});

View File

@ -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;
}
}
}

View File

@ -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 = /[`!@#$%^&*()+=[\]{};:"\\|,.<>/?~]/;

View File

@ -324,9 +324,7 @@ const UserListPageV1 = () => {
disabled={!isAdminUser}
icon={
<IconDelete
data-testid={`delete-user-btn-${
record.displayName || record.name
}`}
data-testid={`delete-user-btn-${record.name}`}
name={t('label.delete')}
width="16px"
/>