mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-25 17:37:57 +00:00
migrated user spec to playwright (#17652)
* migrated user spec to playwright * optimization around making dataStewardUser from userClass * changes as per comments (cherry picked from commit d74cbed6e6f59b56ec11238788df368229b5eb3f)
This commit is contained in:
parent
4e7b8c7167
commit
c61fe37220
@ -1,391 +0,0 @@
|
||||
/*
|
||||
* 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, displayName: string) => {
|
||||
cy.get('[data-testid="loader"]').should('not.exist');
|
||||
// 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(`"${displayName}" 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);
|
||||
|
||||
cy.get('[data-testid="loader"]').should('not.exist');
|
||||
// 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/restore', 'restoreUser');
|
||||
cy.get('.ant-modal-footer > .ant-btn-primary').click();
|
||||
verifyResponseStatusCode('@restoreUser', 200);
|
||||
toastNotification('User restored successfully');
|
||||
};
|
||||
|
||||
export const permanentDeleteUser = (username: string, displayName: string) => {
|
||||
interceptURL('GET', '/api/v1/users?*', 'getUsers');
|
||||
interceptURL('GET', '/api/v1/users/name/*', 'getUser');
|
||||
verifyResponseStatusCode('@getUsers', 200);
|
||||
verifyResponseStatusCode('@getUser', 200);
|
||||
cy.get('[data-testid="loader"]').should('not.exist');
|
||||
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"]').click();
|
||||
verifyResponseStatusCode('@hardDeleteUser', 200);
|
||||
|
||||
toastNotification(`"${displayName}" 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 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 | string) => {
|
||||
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 as number),
|
||||
`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) => {
|
||||
interceptURL(
|
||||
'GET',
|
||||
'/api/v1/feed?*type=Conversation*',
|
||||
'ActivityFeedConversation'
|
||||
);
|
||||
|
||||
cy.get('[data-testid="edit-displayName"]').should('be.visible');
|
||||
verifyResponseStatusCode('@ActivityFeedConversation', 200); // wait for the feed to load
|
||||
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="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(`[data-testid="${teamName}-link"]`)
|
||||
.scrollIntoView()
|
||||
.should('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().click();
|
||||
editTeams(teamName);
|
||||
|
||||
// edit description
|
||||
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);
|
||||
|
||||
cy.get('[data-testid="loader"]').should('not.exist');
|
||||
|
||||
// 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);
|
||||
};
|
||||
|
||||
export const checkNoPermissionPlaceholder = (permission = false) => {
|
||||
cy.get('[data-testid="permission-error-placeholder"]').should(
|
||||
permission ? 'not.be.visible' : 'be.visible'
|
||||
);
|
||||
if (!permission) {
|
||||
cy.get('[data-testid="permission-error-placeholder"]').should(
|
||||
'contain',
|
||||
'You don’t have access, please check with the admin to get permissions'
|
||||
);
|
||||
}
|
||||
};
|
@ -1,336 +0,0 @@
|
||||
/*
|
||||
* 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 { interceptURL, verifyResponseStatusCode } from '../../common/common';
|
||||
import UsersTestClass from '../../common/Entities/UserClass';
|
||||
import { visitEntityDetailsPage } from '../../common/Utils/Entity';
|
||||
import { getToken } from '../../common/Utils/LocalStorage';
|
||||
import {
|
||||
addOwner,
|
||||
generateRandomUser,
|
||||
removeOwner,
|
||||
} from '../../common/Utils/Owner';
|
||||
import {
|
||||
cleanupPolicies,
|
||||
createRoleViaREST,
|
||||
DATA_CONSUMER_ROLE,
|
||||
DATA_STEWARD_ROLE,
|
||||
} from '../../common/Utils/Policy';
|
||||
import {
|
||||
addUser,
|
||||
editRole,
|
||||
generateToken,
|
||||
resetPassword,
|
||||
revokeToken,
|
||||
updateDetails,
|
||||
updateExpiration,
|
||||
} from '../../common/Utils/Users';
|
||||
import {
|
||||
BASE_URL,
|
||||
DELETE_ENTITY,
|
||||
GLOBAL_SETTING_PERMISSIONS,
|
||||
ID,
|
||||
uuid,
|
||||
} from '../../constants/constants';
|
||||
import { EntityType, SidebarItem } from '../../constants/Entity.interface';
|
||||
import {
|
||||
GlobalSettingOptions,
|
||||
SETTINGS_OPTIONS_PATH,
|
||||
SETTING_CUSTOM_PROPERTIES_PATH,
|
||||
} from '../../constants/settings.constant';
|
||||
|
||||
const entity = new UsersTestClass();
|
||||
const expirationTime = {
|
||||
oneday: '1',
|
||||
sevendays: '7',
|
||||
onemonth: '30',
|
||||
twomonths: '60',
|
||||
threemonths: '90',
|
||||
};
|
||||
const name = `usercttest${uuid()}`;
|
||||
const owner = generateRandomUser();
|
||||
let userId = '';
|
||||
const ownerName = `${owner.firstName}${owner.lastName}`;
|
||||
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', { tags: 'Settings' }, () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
createRoleViaREST({ token });
|
||||
|
||||
// Create a new user
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `/api/v1/users/signup`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
body: owner,
|
||||
}).then((response) => {
|
||||
userId = response.body.id;
|
||||
});
|
||||
});
|
||||
});
|
||||
after(() => {
|
||||
cy.login();
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
|
||||
cleanupPolicies({ token });
|
||||
|
||||
// Delete created user
|
||||
cy.request({
|
||||
method: 'DELETE',
|
||||
url: `/api/v1/users/${userId}?hardDelete=true&recursive=false`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Update own admin details', () => {
|
||||
cy.login();
|
||||
updateDetails({
|
||||
...user,
|
||||
isAdmin: true,
|
||||
role: 'Admin',
|
||||
});
|
||||
});
|
||||
|
||||
it('Create Data Consumer User', () => {
|
||||
cy.login();
|
||||
entity.visitUserListPage();
|
||||
addUser({ ...user, role: DATA_CONSUMER_ROLE.name });
|
||||
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);
|
||||
entity.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.sidebarClick(SidebarItem.GLOSSARY);
|
||||
cy.clickOnLogo();
|
||||
|
||||
// Check CRUD for Tags
|
||||
cy.sidebarClick(SidebarItem.TAGS);
|
||||
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.login(user.email, user.newPassword);
|
||||
|
||||
Object.values(ID).forEach((id) => {
|
||||
if (id?.api) {
|
||||
interceptURL('GET', id.api, 'getTabDetails');
|
||||
}
|
||||
// Navigate to settings and respective tab page
|
||||
cy.settingClick(id.testid);
|
||||
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 === GlobalSettingOptions.METADATA) {
|
||||
cy.settingClick(id.testid);
|
||||
} else {
|
||||
cy.sidebarClick(SidebarItem.SETTINGS);
|
||||
let paths = SETTINGS_OPTIONS_PATH[id.testid];
|
||||
|
||||
if (id.isCustomProperty) {
|
||||
paths = SETTING_CUSTOM_PROPERTIES_PATH[id.testid];
|
||||
}
|
||||
cy.get(`[data-testid="${paths[0]}"]`).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();
|
||||
entity.visitUserListPage();
|
||||
editRole(user.name, DATA_STEWARD_ROLE.name);
|
||||
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);
|
||||
entity.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);
|
||||
entity.visitUserListPage();
|
||||
Object.values(expirationTime).forEach((expiry) => {
|
||||
updateExpiration(expiry);
|
||||
});
|
||||
});
|
||||
|
||||
it('Data Steward operations for settings page', () => {
|
||||
cy.login(user.email, user.newStewardPassword);
|
||||
|
||||
Object.values(ID).forEach((id) => {
|
||||
if (id?.api) {
|
||||
interceptURL('GET', id.api, 'getTabDetails');
|
||||
}
|
||||
// Navigate to settings and respective tab page
|
||||
cy.settingClick(id.testid);
|
||||
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 === GlobalSettingOptions.METADATA) {
|
||||
cy.settingClick(id.testid);
|
||||
} else {
|
||||
cy.sidebarClick(SidebarItem.SETTINGS);
|
||||
|
||||
let paths = SETTINGS_OPTIONS_PATH[id.testid];
|
||||
|
||||
if (id.isCustomProperty) {
|
||||
paths = SETTING_CUSTOM_PROPERTIES_PATH[id.testid];
|
||||
}
|
||||
cy.get(`[data-testid="${paths[0]}"]`).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();
|
||||
entity.visitUserListPage();
|
||||
entity.softDeleteUser(user.name, user.updatedDisplayName);
|
||||
});
|
||||
|
||||
it('Admin Restore soft deleted user', () => {
|
||||
cy.login();
|
||||
entity.visitUserListPage();
|
||||
entity.restoreSoftDeletedUser(user.name, user.updatedDisplayName);
|
||||
});
|
||||
|
||||
it('Admin Permanent Delete User', () => {
|
||||
cy.login();
|
||||
entity.visitUserListPage();
|
||||
entity.permanentDeleteUser(user.name, user.updatedDisplayName);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
@ -10,7 +10,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { PolicyRulesType } from '../support/access-control/PoliciesClass';
|
||||
import { uuid } from '../utils/common';
|
||||
import { GlobalSettingOptions } from './settings';
|
||||
|
||||
export const DEFAULT_POLICIES = {
|
||||
dataConsumerPolicy: 'Data Consumer Policy',
|
||||
@ -43,3 +45,128 @@ export const NEW_RULE_NAME = `New / Rule-test-${uuid()}`;
|
||||
export const NEW_RULE_DESCRIPTION = `This is ${NEW_RULE_NAME} description`;
|
||||
|
||||
export const UPDATED_RULE_NAME = `New-Rule-test-${uuid()}-updated`;
|
||||
|
||||
export const DATA_STEWARD_RULES: PolicyRulesType[] = [
|
||||
{
|
||||
name: 'DataStewardRole',
|
||||
resources: ['All'],
|
||||
operations: [
|
||||
'EditDescription',
|
||||
'EditDisplayName',
|
||||
'EditLineage',
|
||||
'EditOwners',
|
||||
'EditTags',
|
||||
'ViewAll',
|
||||
],
|
||||
effect: 'allow',
|
||||
},
|
||||
];
|
||||
|
||||
export const GLOBAL_SETTING_PERMISSIONS: Record<
|
||||
string,
|
||||
{ testid: GlobalSettingOptions; isCustomProperty?: boolean }
|
||||
> = {
|
||||
metadata: {
|
||||
testid: GlobalSettingOptions.METADATA,
|
||||
},
|
||||
customAttributesDatabase: {
|
||||
testid: GlobalSettingOptions.DATABASES,
|
||||
isCustomProperty: true,
|
||||
},
|
||||
customAttributesDatabaseSchema: {
|
||||
testid: GlobalSettingOptions.DATABASE_SCHEMA,
|
||||
isCustomProperty: true,
|
||||
},
|
||||
customAttributesStoredProcedure: {
|
||||
testid: GlobalSettingOptions.STORED_PROCEDURES,
|
||||
isCustomProperty: true,
|
||||
},
|
||||
customAttributesTable: {
|
||||
testid: GlobalSettingOptions.TABLES,
|
||||
isCustomProperty: true,
|
||||
},
|
||||
customAttributesTopics: {
|
||||
testid: GlobalSettingOptions.TOPICS,
|
||||
isCustomProperty: true,
|
||||
},
|
||||
customAttributesDashboards: {
|
||||
testid: GlobalSettingOptions.DASHBOARDS,
|
||||
isCustomProperty: true,
|
||||
},
|
||||
customAttributesPipelines: {
|
||||
testid: GlobalSettingOptions.PIPELINES,
|
||||
isCustomProperty: true,
|
||||
},
|
||||
customAttributesMlModels: {
|
||||
testid: GlobalSettingOptions.MLMODELS,
|
||||
isCustomProperty: true,
|
||||
},
|
||||
customAttributesSearchIndex: {
|
||||
testid: GlobalSettingOptions.SEARCH_INDEXES,
|
||||
isCustomProperty: true,
|
||||
},
|
||||
customAttributesGlossaryTerm: {
|
||||
testid: GlobalSettingOptions.GLOSSARY_TERM,
|
||||
isCustomProperty: true,
|
||||
},
|
||||
customAttributesAPICollection: {
|
||||
testid: GlobalSettingOptions.API_COLLECTIONS,
|
||||
isCustomProperty: true,
|
||||
},
|
||||
customAttributesAPIEndpoint: {
|
||||
testid: GlobalSettingOptions.API_ENDPOINTS,
|
||||
isCustomProperty: true,
|
||||
},
|
||||
bots: {
|
||||
testid: GlobalSettingOptions.BOTS,
|
||||
},
|
||||
};
|
||||
export const SETTING_PAGE_ENTITY_PERMISSION: Record<
|
||||
string,
|
||||
{ testid: GlobalSettingOptions; button: string; api?: string }
|
||||
> = {
|
||||
teams: {
|
||||
testid: GlobalSettingOptions.TEAMS,
|
||||
button: 'add-team',
|
||||
},
|
||||
users: {
|
||||
testid: GlobalSettingOptions.USERS,
|
||||
button: 'add-user',
|
||||
api: '/api/v1/users?*',
|
||||
},
|
||||
admins: {
|
||||
testid: GlobalSettingOptions.ADMINS,
|
||||
button: 'add-user',
|
||||
api: '/api/v1/users?*',
|
||||
},
|
||||
databases: {
|
||||
testid: GlobalSettingOptions.DATABASES,
|
||||
button: 'add-service-button',
|
||||
api: '/api/v1/services/databaseServices?*',
|
||||
},
|
||||
messaging: {
|
||||
testid: GlobalSettingOptions.MESSAGING,
|
||||
button: 'add-service-button',
|
||||
api: '/api/v1/services/messagingServices?*',
|
||||
},
|
||||
dashboard: {
|
||||
testid: GlobalSettingOptions.DASHBOARDS,
|
||||
button: 'add-service-button',
|
||||
api: '/api/v1/services/dashboardServices?*',
|
||||
},
|
||||
pipelines: {
|
||||
testid: GlobalSettingOptions.PIPELINES,
|
||||
button: 'add-service-button',
|
||||
api: '/api/v1/services/pipelineServices?*',
|
||||
},
|
||||
mlmodels: {
|
||||
testid: GlobalSettingOptions.MLMODELS,
|
||||
button: 'add-service-button',
|
||||
api: '/api/v1/services/mlmodelServices?*',
|
||||
},
|
||||
storage: {
|
||||
testid: GlobalSettingOptions.STORAGES,
|
||||
button: 'add-service-button',
|
||||
api: '/api/v1/services/storageServices?*',
|
||||
},
|
||||
};
|
||||
|
@ -26,6 +26,52 @@ export const SERVICE_TYPE = {
|
||||
ApiService: GlobalSettingOptions.APIS,
|
||||
};
|
||||
|
||||
export const SERVICE_CATEGORIES = {
|
||||
DATABASE_SERVICES: 'databaseServices',
|
||||
MESSAGING_SERVICES: 'messagingServices',
|
||||
PIPELINE_SERVICES: 'pipelineServices',
|
||||
DASHBOARD_SERVICES: 'dashboardServices',
|
||||
ML_MODEL_SERVICES: 'mlmodelServices',
|
||||
STORAGE_SERVICES: 'storageServices',
|
||||
METADATA_SERVICES: 'metadataServices',
|
||||
SEARCH_SERVICES: 'searchServices',
|
||||
};
|
||||
|
||||
export const VISIT_SERVICE_PAGE_DETAILS = {
|
||||
[SERVICE_TYPE.Database]: {
|
||||
settingsMenuId: GlobalSettingOptions.DATABASES,
|
||||
serviceCategory: SERVICE_CATEGORIES.DATABASE_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.Messaging]: {
|
||||
settingsMenuId: GlobalSettingOptions.MESSAGING,
|
||||
serviceCategory: SERVICE_CATEGORIES.MESSAGING_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.Dashboard]: {
|
||||
settingsMenuId: GlobalSettingOptions.DASHBOARDS,
|
||||
serviceCategory: SERVICE_CATEGORIES.DASHBOARD_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.Pipeline]: {
|
||||
settingsMenuId: GlobalSettingOptions.PIPELINES,
|
||||
serviceCategory: SERVICE_CATEGORIES.PIPELINE_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.MLModels]: {
|
||||
settingsMenuId: GlobalSettingOptions.MLMODELS,
|
||||
serviceCategory: SERVICE_CATEGORIES.ML_MODEL_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.Storage]: {
|
||||
settingsMenuId: GlobalSettingOptions.STORAGES,
|
||||
serviceCategory: SERVICE_CATEGORIES.STORAGE_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.Search]: {
|
||||
settingsMenuId: GlobalSettingOptions.SEARCH,
|
||||
serviceCategory: SERVICE_CATEGORIES.SEARCH_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.Metadata]: {
|
||||
settingsMenuId: GlobalSettingOptions.METADATA,
|
||||
serviceCategory: SERVICE_CATEGORIES.METADATA_SERVICES,
|
||||
},
|
||||
};
|
||||
|
||||
const uniqueID = uuid();
|
||||
|
||||
export const REDSHIFT = {
|
||||
|
@ -30,7 +30,7 @@ import {
|
||||
redirectToHomePage,
|
||||
toastNotification,
|
||||
uuid,
|
||||
visitUserProfilePage,
|
||||
visitOwnProfilePage,
|
||||
} from '../../utils/common';
|
||||
import { addOwner, updateDescription } from '../../utils/entity';
|
||||
import { clickOnLogo } from '../../utils/sidebar';
|
||||
@ -169,7 +169,7 @@ test.describe('Activity feed', () => {
|
||||
}) => {
|
||||
await redirectToHomePage(page);
|
||||
|
||||
await visitUserProfilePage(page);
|
||||
await visitOwnProfilePage(page);
|
||||
|
||||
const secondFeedConversation = page
|
||||
.locator('#center-container [data-testid="message-container"]')
|
||||
@ -479,7 +479,7 @@ test.describe('Activity feed', () => {
|
||||
});
|
||||
});
|
||||
|
||||
base.describe('Activity feed with Data Steward User', () => {
|
||||
base.describe('Activity feed with Data Consumer User', () => {
|
||||
base.slow(true);
|
||||
|
||||
const id = uuid();
|
||||
|
@ -10,46 +10,430 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import test from '@playwright/test';
|
||||
import { expect, Page, test as base } from '@playwright/test';
|
||||
import { DATA_STEWARD_RULES } from '../../constant/permission';
|
||||
import { GlobalSettingOptions } from '../../constant/settings';
|
||||
import { SidebarItem } from '../../constant/sidebar';
|
||||
import { PolicyClass } from '../../support/access-control/PoliciesClass';
|
||||
import { RolesClass } from '../../support/access-control/RolesClass';
|
||||
import { EntityTypeEndpoint } from '../../support/entity/Entity.interface';
|
||||
import { TableClass } from '../../support/entity/TableClass';
|
||||
import { UserClass } from '../../support/user/UserClass';
|
||||
import { createNewPage, redirectToHomePage } from '../../utils/common';
|
||||
import { settingClick } from '../../utils/sidebar';
|
||||
import { performAdminLogin } from '../../utils/admin';
|
||||
import {
|
||||
redirectToHomePage,
|
||||
uuid,
|
||||
visitOwnProfilePage,
|
||||
} from '../../utils/common';
|
||||
import { addOwner } from '../../utils/entity';
|
||||
import { settingClick, sidebarClick } from '../../utils/sidebar';
|
||||
import {
|
||||
addUser,
|
||||
checkDataConsumerPermissions,
|
||||
checkStewardPermissions,
|
||||
checkStewardServicesPermissions,
|
||||
generateToken,
|
||||
hardDeleteUserProfilePage,
|
||||
permanentDeleteUser,
|
||||
resetPassword,
|
||||
restoreUser,
|
||||
restoreUserProfilePage,
|
||||
revokeToken,
|
||||
settingPageOperationPermissionCheck,
|
||||
softDeleteUser,
|
||||
softDeleteUserProfilePage,
|
||||
updateExpiration,
|
||||
updateUserDetails,
|
||||
visitUserListPage,
|
||||
visitUserProfilePage,
|
||||
} from '../../utils/user';
|
||||
|
||||
const userName = `pw-user-${uuid()}`;
|
||||
const expirationTime = [1, 7, 30, 60, 90];
|
||||
|
||||
const updatedUserDetails = {
|
||||
name: userName,
|
||||
email: `${userName}@gmail.com`,
|
||||
updatedDisplayName: `Edited${uuid()}`,
|
||||
teamName: 'Applications',
|
||||
updatedDescription: `This is updated description ${uuid()}`,
|
||||
password: `User@${uuid()}`,
|
||||
newPassword: `NewUser@${uuid()}`,
|
||||
};
|
||||
|
||||
const adminUser = new UserClass();
|
||||
const dataConsumerUser = new UserClass();
|
||||
const dataStewardUser = new UserClass();
|
||||
const user = new UserClass();
|
||||
const user2 = new UserClass();
|
||||
const tableEntity = new TableClass();
|
||||
const tableEntity2 = new TableClass();
|
||||
const policy = new PolicyClass();
|
||||
const role = new RolesClass();
|
||||
|
||||
// use the admin user to login
|
||||
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||
const test = base.extend<{
|
||||
adminPage: Page;
|
||||
dataConsumerPage: Page;
|
||||
dataStewardPage: Page;
|
||||
}>({
|
||||
adminPage: async ({ browser }, use) => {
|
||||
const adminPage = await browser.newPage();
|
||||
await adminUser.login(adminPage);
|
||||
await use(adminPage);
|
||||
await adminPage.close();
|
||||
},
|
||||
dataConsumerPage: async ({ browser }, use) => {
|
||||
const page = await browser.newPage();
|
||||
await dataConsumerUser.login(page);
|
||||
await use(page);
|
||||
await page.close();
|
||||
},
|
||||
dataStewardPage: async ({ browser }, use) => {
|
||||
const page = await browser.newPage();
|
||||
await dataStewardUser.login(page);
|
||||
await use(page);
|
||||
await page.close();
|
||||
},
|
||||
});
|
||||
|
||||
test.describe('User with different Roles', () => {
|
||||
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await createNewPage(browser);
|
||||
base.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
|
||||
await user.create(apiContext);
|
||||
await adminUser.create(apiContext);
|
||||
await adminUser.setAdminRole(apiContext);
|
||||
await dataConsumerUser.create(apiContext);
|
||||
await dataStewardUser.create(apiContext);
|
||||
await dataStewardUser.setDataStewardRole(apiContext);
|
||||
await user.create(apiContext);
|
||||
await user2.create(apiContext);
|
||||
await tableEntity.create(apiContext);
|
||||
await tableEntity2.create(apiContext);
|
||||
await policy.create(apiContext, DATA_STEWARD_RULES);
|
||||
await role.create(apiContext, [policy.responseData.name]);
|
||||
|
||||
await afterAction();
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
base.afterAll('Cleanup', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
await adminUser.delete(apiContext);
|
||||
await dataConsumerUser.delete(apiContext);
|
||||
await dataStewardUser.delete(apiContext);
|
||||
await tableEntity.delete(apiContext);
|
||||
await tableEntity2.delete(apiContext);
|
||||
await policy.delete(apiContext);
|
||||
await role.delete(apiContext);
|
||||
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.describe('User with Admin Roles', () => {
|
||||
test.slow(true);
|
||||
|
||||
test('Update own admin details', async ({ adminPage }) => {
|
||||
await redirectToHomePage(adminPage);
|
||||
|
||||
await updateUserDetails(adminPage, {
|
||||
...updatedUserDetails,
|
||||
isAdmin: true,
|
||||
role: 'Admin',
|
||||
});
|
||||
});
|
||||
|
||||
test.beforeEach('Visit user list page', async ({ page }) => {
|
||||
await redirectToHomePage(page);
|
||||
test('Create and Delete user', async ({ adminPage }) => {
|
||||
await redirectToHomePage(adminPage);
|
||||
await visitUserListPage(adminPage);
|
||||
|
||||
await addUser(adminPage, {
|
||||
...updatedUserDetails,
|
||||
role: role.responseData.displayName,
|
||||
});
|
||||
|
||||
await visitUserProfilePage(adminPage, updatedUserDetails.name);
|
||||
|
||||
await visitUserListPage(adminPage);
|
||||
|
||||
await permanentDeleteUser(
|
||||
adminPage,
|
||||
updatedUserDetails.name,
|
||||
updatedUserDetails.name,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test('Admin soft & hard delete and restore user', async ({ adminPage }) => {
|
||||
await redirectToHomePage(adminPage);
|
||||
await visitUserListPage(adminPage);
|
||||
await softDeleteUser(
|
||||
adminPage,
|
||||
user2.responseData.name,
|
||||
user2.responseData.displayName
|
||||
);
|
||||
|
||||
await restoreUser(
|
||||
adminPage,
|
||||
user2.responseData.name,
|
||||
user2.responseData.displayName
|
||||
);
|
||||
|
||||
await permanentDeleteUser(
|
||||
adminPage,
|
||||
user2.responseData.name,
|
||||
user2.responseData.displayName
|
||||
);
|
||||
});
|
||||
|
||||
test('Admin soft & hard delete and restore user from profile page', async ({
|
||||
page,
|
||||
adminPage,
|
||||
}) => {
|
||||
await settingClick(page, GlobalSettingOptions.USERS);
|
||||
await redirectToHomePage(adminPage);
|
||||
await settingClick(adminPage, GlobalSettingOptions.USERS);
|
||||
await softDeleteUserProfilePage(
|
||||
page,
|
||||
adminPage,
|
||||
user.responseData.name,
|
||||
user.responseData.displayName
|
||||
);
|
||||
|
||||
await restoreUserProfilePage(page, user.responseData.fullyQualifiedName);
|
||||
await hardDeleteUserProfilePage(page, user.responseData.displayName);
|
||||
await restoreUserProfilePage(
|
||||
adminPage,
|
||||
user.responseData.fullyQualifiedName
|
||||
);
|
||||
await hardDeleteUserProfilePage(adminPage, user.responseData.displayName);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('User with Data Consumer Roles', () => {
|
||||
test.slow(true);
|
||||
|
||||
test('Token generation & revocation for Data Consumer', async ({
|
||||
dataConsumerPage,
|
||||
}) => {
|
||||
await redirectToHomePage(dataConsumerPage);
|
||||
await visitOwnProfilePage(dataConsumerPage);
|
||||
|
||||
await dataConsumerPage.getByTestId('access-token').click();
|
||||
await generateToken(dataConsumerPage);
|
||||
await revokeToken(dataConsumerPage);
|
||||
});
|
||||
|
||||
test(`Update token expiration for Data Consumer`, async ({
|
||||
dataConsumerPage,
|
||||
}) => {
|
||||
await redirectToHomePage(dataConsumerPage);
|
||||
await visitOwnProfilePage(dataConsumerPage);
|
||||
|
||||
await dataConsumerPage.getByTestId('access-token').click();
|
||||
|
||||
await expect(
|
||||
dataConsumerPage.locator('[data-testid="no-token"]')
|
||||
).toBeVisible();
|
||||
|
||||
await dataConsumerPage.click('[data-testid="auth-mechanism"] > span');
|
||||
|
||||
for (const expiry of expirationTime) {
|
||||
await updateExpiration(dataConsumerPage, expiry);
|
||||
}
|
||||
});
|
||||
|
||||
test('User should have only view permission for glossary and tags for Data Consumer', async ({
|
||||
dataConsumerPage,
|
||||
}) => {
|
||||
await redirectToHomePage(dataConsumerPage);
|
||||
|
||||
// Check CRUD for Glossary
|
||||
await sidebarClick(dataConsumerPage, SidebarItem.GLOSSARY);
|
||||
|
||||
await expect(
|
||||
dataConsumerPage.locator('[data-testid="add-glossary"]')
|
||||
).not.toBeVisible();
|
||||
|
||||
await expect(
|
||||
dataConsumerPage.locator('[data-testid="add-new-tag-button-header"]')
|
||||
).not.toBeVisible();
|
||||
|
||||
await expect(
|
||||
dataConsumerPage.locator('[data-testid="manage-button"]')
|
||||
).not.toBeVisible();
|
||||
|
||||
// Glossary Term Table Action column
|
||||
await expect(dataConsumerPage.getByText('Actions')).not.toBeVisible();
|
||||
|
||||
// right panel
|
||||
await expect(
|
||||
dataConsumerPage.locator('[data-testid="add-domain"]')
|
||||
).not.toBeVisible();
|
||||
await expect(
|
||||
dataConsumerPage.locator('[data-testid="edit-owner"]')
|
||||
).not.toBeVisible();
|
||||
await expect(
|
||||
dataConsumerPage.locator('[data-testid="edit-review-button"]')
|
||||
).not.toBeVisible();
|
||||
|
||||
// Check CRUD for Tags
|
||||
await sidebarClick(dataConsumerPage, SidebarItem.TAGS);
|
||||
|
||||
await expect(
|
||||
dataConsumerPage.locator('[data-testid="add-classification"]')
|
||||
).not.toBeVisible();
|
||||
|
||||
await expect(
|
||||
dataConsumerPage.locator('[data-testid="add-new-tag-button"]')
|
||||
).not.toBeVisible();
|
||||
|
||||
await expect(
|
||||
dataConsumerPage.locator('[data-testid="manage-button"]')
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('Operations for settings page for Data Consumer', async ({
|
||||
dataConsumerPage,
|
||||
}) => {
|
||||
await settingPageOperationPermissionCheck(dataConsumerPage);
|
||||
});
|
||||
|
||||
test('Permissions for table details page for Data Consumer', async ({
|
||||
adminPage,
|
||||
dataConsumerPage,
|
||||
}) => {
|
||||
await redirectToHomePage(adminPage);
|
||||
|
||||
await tableEntity.visitEntityPage(adminPage);
|
||||
|
||||
await addOwner({
|
||||
page: adminPage,
|
||||
owner: user.responseData.displayName,
|
||||
type: 'Users',
|
||||
endpoint: EntityTypeEndpoint.Table,
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
|
||||
await tableEntity.visitEntityPage(dataConsumerPage);
|
||||
|
||||
await checkDataConsumerPermissions(dataConsumerPage);
|
||||
});
|
||||
|
||||
test('Update user details for Data Consumer', async ({
|
||||
dataConsumerPage,
|
||||
}) => {
|
||||
await redirectToHomePage(dataConsumerPage);
|
||||
|
||||
await updateUserDetails(dataConsumerPage, {
|
||||
...updatedUserDetails,
|
||||
isAdmin: false,
|
||||
});
|
||||
});
|
||||
|
||||
test('Reset Password for Data Consumer', async ({ dataConsumerPage }) => {
|
||||
await redirectToHomePage(dataConsumerPage);
|
||||
|
||||
await resetPassword(
|
||||
dataConsumerPage,
|
||||
dataConsumerUser.data.password,
|
||||
updatedUserDetails.password,
|
||||
updatedUserDetails.newPassword
|
||||
);
|
||||
|
||||
await dataConsumerUser.logout(dataConsumerPage);
|
||||
|
||||
await dataConsumerUser.login(
|
||||
dataConsumerPage,
|
||||
dataConsumerUser.data.email,
|
||||
updatedUserDetails.newPassword
|
||||
);
|
||||
|
||||
await visitOwnProfilePage(dataConsumerPage);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('User with Data Steward Roles', () => {
|
||||
test.slow(true);
|
||||
|
||||
test('Update user details for Data Steward', async ({ dataStewardPage }) => {
|
||||
await redirectToHomePage(dataStewardPage);
|
||||
|
||||
await updateUserDetails(dataStewardPage, {
|
||||
...updatedUserDetails,
|
||||
isAdmin: false,
|
||||
});
|
||||
});
|
||||
|
||||
test('Token generation & revocation for Data Steward', async ({
|
||||
dataStewardPage,
|
||||
}) => {
|
||||
await redirectToHomePage(dataStewardPage);
|
||||
await visitOwnProfilePage(dataStewardPage);
|
||||
|
||||
await dataStewardPage.getByTestId('access-token').click();
|
||||
await generateToken(dataStewardPage);
|
||||
await revokeToken(dataStewardPage);
|
||||
});
|
||||
|
||||
test('Update token expiration for Data Steward', async ({
|
||||
dataStewardPage,
|
||||
}) => {
|
||||
await redirectToHomePage(dataStewardPage);
|
||||
await visitOwnProfilePage(dataStewardPage);
|
||||
|
||||
await dataStewardPage.getByTestId('access-token').click();
|
||||
|
||||
await expect(
|
||||
dataStewardPage.locator('[data-testid="no-token"]')
|
||||
).toBeVisible();
|
||||
|
||||
await dataStewardPage.click('[data-testid="auth-mechanism"] > span');
|
||||
|
||||
for (const expiry of expirationTime) {
|
||||
await updateExpiration(dataStewardPage, expiry);
|
||||
}
|
||||
});
|
||||
|
||||
test('Operations for settings page for Data Steward', async ({
|
||||
dataStewardPage,
|
||||
}) => {
|
||||
await settingPageOperationPermissionCheck(dataStewardPage);
|
||||
});
|
||||
|
||||
test('Check permissions for Data Steward', async ({
|
||||
adminPage,
|
||||
dataStewardPage,
|
||||
}) => {
|
||||
await redirectToHomePage(adminPage);
|
||||
|
||||
await checkStewardServicesPermissions(dataStewardPage);
|
||||
|
||||
await tableEntity2.visitEntityPage(adminPage);
|
||||
|
||||
await addOwner({
|
||||
page: adminPage,
|
||||
owner: user.responseData.displayName,
|
||||
type: 'Users',
|
||||
endpoint: EntityTypeEndpoint.Table,
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
|
||||
await tableEntity2.visitEntityPage(dataStewardPage);
|
||||
|
||||
await checkStewardPermissions(dataStewardPage);
|
||||
});
|
||||
|
||||
test('Reset Password for Data Steward', async ({ dataStewardPage }) => {
|
||||
await redirectToHomePage(dataStewardPage);
|
||||
|
||||
await resetPassword(
|
||||
dataStewardPage,
|
||||
dataStewardUser.data.password,
|
||||
updatedUserDetails.password,
|
||||
updatedUserDetails.newPassword
|
||||
);
|
||||
|
||||
await dataStewardUser.logout(dataStewardPage);
|
||||
|
||||
await dataStewardUser.login(
|
||||
dataStewardPage,
|
||||
dataStewardUser.data.email,
|
||||
updatedUserDetails.newPassword
|
||||
);
|
||||
|
||||
await visitOwnProfilePage(dataStewardPage);
|
||||
});
|
||||
});
|
||||
|
@ -12,7 +12,11 @@
|
||||
*/
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { generateRandomUsername } from '../../utils/common';
|
||||
import { DATA_STEWARD_RULES } from '../../constant/permission';
|
||||
import { generateRandomUsername, uuid } from '../../utils/common';
|
||||
import { PolicyClass } from '../access-control/PoliciesClass';
|
||||
import { RolesClass } from '../access-control/RolesClass';
|
||||
import { TeamClass } from '../team/TeamClass';
|
||||
|
||||
type ResponseDataType = {
|
||||
name: string;
|
||||
@ -29,10 +33,15 @@ type UserData = {
|
||||
password: string;
|
||||
};
|
||||
|
||||
const dataStewardPolicy = new PolicyClass();
|
||||
const dataStewardRoles = new RolesClass();
|
||||
let dataStewardTeam: TeamClass;
|
||||
|
||||
export class UserClass {
|
||||
data: UserData;
|
||||
|
||||
responseData: ResponseDataType;
|
||||
isUserDataSteward = false;
|
||||
|
||||
constructor(data?: UserData) {
|
||||
this.data = data ? data : generateRandomUsername();
|
||||
@ -85,7 +94,33 @@ export class UserClass {
|
||||
});
|
||||
}
|
||||
|
||||
async setDataStewardRole(apiContext: APIRequestContext) {
|
||||
this.isUserDataSteward = true;
|
||||
const id = uuid();
|
||||
await dataStewardPolicy.create(apiContext, DATA_STEWARD_RULES);
|
||||
await dataStewardRoles.create(apiContext, [
|
||||
dataStewardPolicy.responseData.name,
|
||||
]);
|
||||
dataStewardTeam = new TeamClass({
|
||||
name: `PW%data_steward_team-${id}`,
|
||||
displayName: `PW Data Steward Team ${id}`,
|
||||
description: 'playwright data steward team description',
|
||||
teamType: 'Group',
|
||||
users: [this.responseData.id],
|
||||
defaultRoles: dataStewardRoles.responseData.id
|
||||
? [dataStewardRoles.responseData.id]
|
||||
: [],
|
||||
});
|
||||
await dataStewardTeam.create(apiContext);
|
||||
}
|
||||
|
||||
async delete(apiContext: APIRequestContext) {
|
||||
if (this.isUserDataSteward) {
|
||||
await dataStewardPolicy.delete(apiContext);
|
||||
await dataStewardRoles.delete(apiContext);
|
||||
await dataStewardTeam.delete(apiContext);
|
||||
}
|
||||
|
||||
const response = await apiContext.delete(
|
||||
`/api/v1/users/${this.responseData.id}?recursive=false&hardDelete=true`
|
||||
);
|
||||
|
@ -127,7 +127,7 @@ export const clickOutside = async (page: Page) => {
|
||||
await page.mouse.move(1280, 0); // moving out side left menu bar to avoid random failure due to left menu bar
|
||||
};
|
||||
|
||||
export const visitUserProfilePage = async (page: Page) => {
|
||||
export const visitOwnProfilePage = async (page: Page) => {
|
||||
await page.locator('[data-testid="dropdown-profile"] svg').click();
|
||||
await page.waitForSelector('[role="menu"].profile-dropdown', {
|
||||
state: 'visible',
|
||||
|
@ -12,7 +12,7 @@
|
||||
*/
|
||||
import { expect, Page } from '@playwright/test';
|
||||
import { GlobalSettingOptions } from '../constant/settings';
|
||||
import { visitUserProfilePage } from './common';
|
||||
import { visitOwnProfilePage } from './common';
|
||||
import { settingClick } from './sidebar';
|
||||
|
||||
export const navigateToCustomizeLandingPage = async (
|
||||
@ -95,7 +95,7 @@ export const setUserDefaultPersona = async (
|
||||
page: Page,
|
||||
personaName: string
|
||||
) => {
|
||||
await visitUserProfilePage(page);
|
||||
await visitOwnProfilePage(page);
|
||||
|
||||
await page
|
||||
.locator(
|
||||
|
@ -11,11 +11,38 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { expect, Page } from '@playwright/test';
|
||||
import { GlobalSettingOptions } from '../constant/settings';
|
||||
import { expect, Page, Response } from '@playwright/test';
|
||||
import {
|
||||
customFormatDateTime,
|
||||
getEpochMillisForFutureDays,
|
||||
} from '../../src/utils/date-time/DateTimeUtils';
|
||||
import {
|
||||
GLOBAL_SETTING_PERMISSIONS,
|
||||
SETTING_PAGE_ENTITY_PERMISSION,
|
||||
} from '../constant/permission';
|
||||
import { VISIT_SERVICE_PAGE_DETAILS } from '../constant/service';
|
||||
import {
|
||||
GlobalSettingOptions,
|
||||
SETTINGS_OPTIONS_PATH,
|
||||
SETTING_CUSTOM_PROPERTIES_PATH,
|
||||
} from '../constant/settings';
|
||||
import { SidebarItem } from '../constant/sidebar';
|
||||
import { UserClass } from '../support/user/UserClass';
|
||||
import { getAuthContext, getToken, toastNotification } from './common';
|
||||
import { settingClick } from './sidebar';
|
||||
import {
|
||||
descriptionBox,
|
||||
getAuthContext,
|
||||
getToken,
|
||||
redirectToHomePage,
|
||||
toastNotification,
|
||||
visitOwnProfilePage,
|
||||
} from './common';
|
||||
import { settingClick, sidebarClick } from './sidebar';
|
||||
|
||||
export const visitUserListPage = async (page: Page) => {
|
||||
const fetchUsers = page.waitForResponse('/api/v1/users?*');
|
||||
await settingClick(page, GlobalSettingOptions.USERS);
|
||||
await fetchUsers;
|
||||
};
|
||||
|
||||
export const performUserLogin = async (browser, user: UserClass) => {
|
||||
const page = await browser.newPage();
|
||||
@ -168,3 +195,562 @@ export const hardDeleteUserProfilePage = async (
|
||||
|
||||
await toastNotification(page, /deleted successfully!/);
|
||||
};
|
||||
|
||||
export const editDisplayName = async (page: Page, editedUserName: string) => {
|
||||
await page.click('[data-testid="edit-displayName"]');
|
||||
await page.fill('[data-testid="displayName"]', '');
|
||||
await page.type('[data-testid="displayName"]', editedUserName);
|
||||
|
||||
const saveResponse = page.waitForResponse('/api/v1/users/*');
|
||||
await page.click('[data-testid="inline-save-btn"]');
|
||||
await saveResponse;
|
||||
|
||||
// Verify the updated display name
|
||||
const userName = await page.textContent('[data-testid="user-name"]');
|
||||
|
||||
expect(userName).toContain(editedUserName);
|
||||
};
|
||||
|
||||
export const editTeams = async (page: Page, teamName: string) => {
|
||||
await page.click('[data-testid="edit-teams-button"]');
|
||||
await page.click('.ant-select-selection-item-remove > .anticon');
|
||||
|
||||
await page.click('[data-testid="team-select"]');
|
||||
await page.type('[data-testid="team-select"]', teamName);
|
||||
|
||||
// Click the team from the dropdown
|
||||
await page.click('.filter-node > .ant-select-tree-node-content-wrapper');
|
||||
|
||||
const updateTeamResponse = page.waitForResponse('/api/v1/users/*');
|
||||
await page.click('[data-testid="inline-save-btn"]');
|
||||
await updateTeamResponse;
|
||||
|
||||
// Verify the new team link is visible
|
||||
await expect(page.locator(`[data-testid="${teamName}-link"]`)).toBeVisible();
|
||||
};
|
||||
|
||||
export const editDescription = async (
|
||||
page: Page,
|
||||
updatedDescription: string
|
||||
) => {
|
||||
await page.click('[data-testid="edit-description"]');
|
||||
|
||||
// Clear and type the new description
|
||||
await page.locator(descriptionBox).fill(updatedDescription);
|
||||
|
||||
const updateDescription = page.waitForResponse('/api/v1/users/*');
|
||||
await page.click('[data-testid="save"]');
|
||||
await updateDescription;
|
||||
|
||||
await page.click('.ant-collapse-expand-icon > .anticon > svg');
|
||||
|
||||
// Verify the updated description
|
||||
const description = page.locator(
|
||||
'[data-testid="asset-description-container"] .toastui-editor-contents > p'
|
||||
);
|
||||
|
||||
await expect(description).toContainText(updatedDescription);
|
||||
};
|
||||
|
||||
export const handleAdminUpdateDetails = async (
|
||||
page: Page,
|
||||
editedUserName: string,
|
||||
updatedDescription: string,
|
||||
teamName: string,
|
||||
role?: string
|
||||
) => {
|
||||
const feedResponse = page.waitForResponse('/api/v1/feed?type=Conversation');
|
||||
await visitOwnProfilePage(page);
|
||||
await feedResponse;
|
||||
|
||||
// edit displayName
|
||||
await editDisplayName(page, editedUserName);
|
||||
|
||||
// edit teams
|
||||
await page.click('.ant-collapse-expand-icon > .anticon > svg');
|
||||
await editTeams(page, teamName);
|
||||
|
||||
// edit description
|
||||
await editDescription(page, updatedDescription);
|
||||
|
||||
await page.click('.ant-collapse-expand-icon > .anticon > svg');
|
||||
|
||||
// verify role for the user
|
||||
const chipContainer = page.locator(
|
||||
'[data-testid="user-profile-roles"] [data-testid="chip-container"]'
|
||||
);
|
||||
|
||||
await expect(chipContainer).toContainText(role ?? '');
|
||||
};
|
||||
|
||||
export const handleUserUpdateDetails = async (
|
||||
page: Page,
|
||||
editedUserName: string,
|
||||
updatedDescription: string
|
||||
) => {
|
||||
const feedResponse = page.waitForResponse(
|
||||
'/api/v1/feed?type=Conversation&filterType=OWNER_OR_FOLLOWS&userId=*'
|
||||
);
|
||||
await visitOwnProfilePage(page);
|
||||
await feedResponse;
|
||||
|
||||
// edit displayName
|
||||
await editDisplayName(page, editedUserName);
|
||||
|
||||
// edit description
|
||||
await page.click('.ant-collapse-expand-icon > .anticon > svg');
|
||||
await editDescription(page, updatedDescription);
|
||||
};
|
||||
|
||||
export const updateUserDetails = async (
|
||||
page: Page,
|
||||
{
|
||||
updatedDisplayName,
|
||||
updatedDescription,
|
||||
isAdmin,
|
||||
teamName,
|
||||
role,
|
||||
}: {
|
||||
updatedDisplayName: string;
|
||||
updatedDescription: string;
|
||||
teamName: string;
|
||||
isAdmin?: boolean;
|
||||
role?: string;
|
||||
}
|
||||
) => {
|
||||
if (isAdmin) {
|
||||
await handleAdminUpdateDetails(
|
||||
page,
|
||||
updatedDisplayName,
|
||||
updatedDescription,
|
||||
teamName,
|
||||
role
|
||||
);
|
||||
} else {
|
||||
await handleUserUpdateDetails(page, updatedDisplayName, updatedDescription);
|
||||
}
|
||||
};
|
||||
|
||||
export const softDeleteUser = async (
|
||||
page: Page,
|
||||
username: string,
|
||||
displayName: string
|
||||
) => {
|
||||
// Wait for the loader to disappear
|
||||
await page.waitForSelector('[data-testid="loader"]', { state: 'hidden' });
|
||||
|
||||
const searchResponse = page.waitForResponse(
|
||||
'/api/v1/search/query?q=**&from=0&size=*&index=*'
|
||||
);
|
||||
await page.fill('[data-testid="searchbar"]', username);
|
||||
await searchResponse;
|
||||
|
||||
// Click on delete button
|
||||
await page.click(`[data-testid="delete-user-btn-${username}"]`);
|
||||
// Soft deleting the user
|
||||
await page.click('[data-testid="soft-delete"]');
|
||||
await page.fill('[data-testid="confirmation-text-input"]', 'DELETE');
|
||||
|
||||
const fetchUpdatedUsers = page.waitForResponse('/api/v1/users/*');
|
||||
const deleteResponse = page.waitForResponse(
|
||||
'/api/v1/users/*?hardDelete=false&recursive=false'
|
||||
);
|
||||
await page.click('[data-testid="confirm-button"]');
|
||||
await deleteResponse;
|
||||
await fetchUpdatedUsers;
|
||||
|
||||
await toastNotification(page, `"${displayName}" deleted successfully!`);
|
||||
|
||||
// Search soft deleted user in non-deleted mode
|
||||
const searchSoftDeletedUserResponse = page.waitForResponse(
|
||||
'/api/v1/search/query*'
|
||||
);
|
||||
await page.fill('[data-testid="searchbar"]', username);
|
||||
await searchSoftDeletedUserResponse;
|
||||
|
||||
// Verify the search error placeholder is visible
|
||||
const searchErrorPlaceholder = page.locator(
|
||||
'[data-testid="search-error-placeholder"]'
|
||||
);
|
||||
|
||||
await expect(searchErrorPlaceholder).toBeVisible();
|
||||
};
|
||||
|
||||
export const restoreUser = async (
|
||||
page: Page,
|
||||
username: string,
|
||||
editedUserName: string
|
||||
) => {
|
||||
// Click on deleted user toggle
|
||||
await page.click('[data-testid="show-deleted"]');
|
||||
|
||||
const searchUsers = page.waitForResponse('/api/v1/search/query*');
|
||||
await page.fill('[data-testid="searchbar"]', username);
|
||||
await searchUsers;
|
||||
|
||||
// Click on restore user button
|
||||
await page.click(`[data-testid="restore-user-btn-${username}"]`);
|
||||
|
||||
// Verify the modal content
|
||||
const modalContent = page.locator('.ant-modal-body > p');
|
||||
|
||||
await expect(modalContent).toContainText(
|
||||
`Are you sure you want to restore ${editedUserName}?`
|
||||
);
|
||||
|
||||
// Click the confirm button in the modal
|
||||
const restoreUserResponse = page.waitForResponse('/api/v1/users/restore');
|
||||
await page.click('.ant-modal-footer > .ant-btn-primary');
|
||||
await restoreUserResponse;
|
||||
|
||||
await toastNotification(page, 'User restored successfully');
|
||||
};
|
||||
|
||||
export const permanentDeleteUser = async (
|
||||
page: Page,
|
||||
username: string,
|
||||
displayName: string,
|
||||
isUserSoftDeleted = true
|
||||
) => {
|
||||
if (isUserSoftDeleted) {
|
||||
// Click on deleted user toggle to off it
|
||||
await page.click('[data-testid="show-deleted"]');
|
||||
}
|
||||
|
||||
// Search the user
|
||||
const searchUserResponse = page.waitForResponse('/api/v1/search/query*');
|
||||
await page.fill('[data-testid="searchbar"]', username);
|
||||
await searchUserResponse;
|
||||
|
||||
// Click on delete user button
|
||||
await page.click(`[data-testid="delete-user-btn-${username}"]`);
|
||||
|
||||
// Click on hard delete
|
||||
await page.click('[data-testid="hard-delete"]');
|
||||
await page.fill('[data-testid="confirmation-text-input"]', 'DELETE');
|
||||
|
||||
const hardDeleteUserResponse = page.waitForResponse(
|
||||
'api/v1/users/*?hardDelete=true&recursive=false'
|
||||
);
|
||||
await page.click('[data-testid="confirm-button"]');
|
||||
await hardDeleteUserResponse;
|
||||
|
||||
await toastNotification(page, `"${displayName}" deleted successfully!`);
|
||||
|
||||
// Search the user again
|
||||
const searchUserAfterDeleteResponse = page.waitForResponse(
|
||||
'/api/v1/search/query*'
|
||||
);
|
||||
await page.fill('[data-testid="searchbar"]', username);
|
||||
|
||||
await searchUserAfterDeleteResponse;
|
||||
|
||||
// Verify the search error placeholder is visible
|
||||
const searchErrorPlaceholder = page.locator(
|
||||
'[data-testid="search-error-placeholder"]'
|
||||
);
|
||||
|
||||
await expect(searchErrorPlaceholder).toBeVisible();
|
||||
};
|
||||
|
||||
export const generateToken = async (page: Page) => {
|
||||
await expect(page.locator('[data-testid="no-token"]')).toBeVisible();
|
||||
|
||||
await page.click('[data-testid="auth-mechanism"] > span');
|
||||
|
||||
await page.click('[data-testid="token-expiry"]');
|
||||
|
||||
await page.locator('[title="1 hr"] div').click();
|
||||
|
||||
await expect(page.locator('[data-testid="token-expiry"]')).toBeVisible();
|
||||
|
||||
const generateToken = page.waitForResponse('/api/v1/users/security/token');
|
||||
await page.click('[data-testid="save-edit"]');
|
||||
await generateToken;
|
||||
};
|
||||
|
||||
export const revokeToken = async (page: Page) => {
|
||||
await page.click('[data-testid="revoke-button"]');
|
||||
|
||||
await expect(page.locator('[data-testid="body-text"]')).toContainText(
|
||||
'Are you sure you want to revoke access for Personal Access Token?'
|
||||
);
|
||||
|
||||
await page.click('[data-testid="save-button"]');
|
||||
|
||||
await expect(page.locator('[data-testid="revoke-button"]')).not.toBeVisible();
|
||||
};
|
||||
|
||||
export const updateExpiration = async (page: Page, expiry: number | string) => {
|
||||
await page.click('[data-testid="token-expiry"]');
|
||||
await page.click(`text=${expiry} days`);
|
||||
|
||||
const expiryDate = customFormatDateTime(
|
||||
getEpochMillisForFutureDays(expiry as number),
|
||||
`ccc d'th' MMMM, yyyy`
|
||||
);
|
||||
|
||||
await page.click('[data-testid="save-edit"]');
|
||||
|
||||
await expect(
|
||||
page.locator('[data-testid="center-panel"] [data-testid="revoke-button"]')
|
||||
).toBeVisible();
|
||||
|
||||
await expect(page.locator('[data-testid="token-expiry"]')).toContainText(
|
||||
`Expires on ${expiryDate}`
|
||||
);
|
||||
|
||||
await revokeToken(page);
|
||||
};
|
||||
|
||||
export const checkDataConsumerPermissions = async (page: Page) => {
|
||||
// check Add domain permission
|
||||
await expect(page.locator('[data-testid="add-domain"]')).not.toBeVisible();
|
||||
await expect(
|
||||
page.locator('[data-testid="edit-displayName-button"]')
|
||||
).not.toBeVisible();
|
||||
|
||||
// Check edit owner permission
|
||||
await expect(page.locator('[data-testid="edit-owner"]')).not.toBeVisible();
|
||||
|
||||
// Check edit description permission
|
||||
await expect(page.locator('[data-testid="edit-description"]')).toBeVisible();
|
||||
|
||||
// Check edit tier permission
|
||||
await expect(page.locator('[data-testid="edit-tier"]')).toBeVisible();
|
||||
|
||||
// Check right panel add tags button
|
||||
await expect(
|
||||
page.locator(
|
||||
'[data-testid="entity-right-panel"] [data-testid="tags-container"] [data-testid="entity-tags"] .tag-chip-add-button'
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
// Check right panel add glossary term button
|
||||
await expect(
|
||||
page.locator(
|
||||
'[data-testid="entity-right-panel"] [data-testid="glossary-container"] [data-testid="entity-tags"] .tag-chip-add-button'
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
if (process.env.PLAYWRIGHT_IS_OSS) {
|
||||
await expect(
|
||||
page.locator('[data-testid="manage-button"]')
|
||||
).not.toBeVisible();
|
||||
} else {
|
||||
await expect(page.locator('[data-testid="manage-button"]')).toBeVisible();
|
||||
|
||||
await page.click('[data-testid="manage-button"]');
|
||||
|
||||
await expect(page.locator('[data-testid="export-button"]')).toBeVisible();
|
||||
await expect(
|
||||
page.locator('[data-testid="import-button"]')
|
||||
).not.toBeVisible();
|
||||
await expect(
|
||||
page.locator('[data-testid="announcement-button"]')
|
||||
).not.toBeVisible();
|
||||
await expect(
|
||||
page.locator('[data-testid="delete-button"]')
|
||||
).not.toBeVisible();
|
||||
}
|
||||
|
||||
await page.click('[data-testid="lineage"] > .ant-space-item');
|
||||
|
||||
await expect(page.locator('[data-testid="edit-lineage"]')).toBeDisabled();
|
||||
};
|
||||
|
||||
export const checkStewardServicesPermissions = async (page: Page) => {
|
||||
// Click on the sidebar item for Explore
|
||||
await sidebarClick(page, SidebarItem.EXPLORE);
|
||||
|
||||
// Iterate through the service page details and check for the add service button
|
||||
for (const service of Object.values(VISIT_SERVICE_PAGE_DETAILS)) {
|
||||
await settingClick(page, service.settingsMenuId);
|
||||
|
||||
await expect(
|
||||
page.locator('[data-testid="add-service-button"] > span')
|
||||
).not.toBeVisible();
|
||||
}
|
||||
|
||||
// Click on the sidebar item for Explore again
|
||||
await sidebarClick(page, SidebarItem.EXPLORE);
|
||||
|
||||
// Perform search actions
|
||||
await page.click('[data-testid="search-dropdown-Data Assets"]');
|
||||
await page.locator('[data-testid="table-checkbox"]').scrollIntoViewIfNeeded();
|
||||
await page.click('[data-testid="table-checkbox"]');
|
||||
|
||||
const getSearchResultResponse = page.waitForResponse(
|
||||
'/api/v1/search/query?q=*'
|
||||
);
|
||||
await page.click('[data-testid="update-btn"]');
|
||||
|
||||
await getSearchResultResponse;
|
||||
|
||||
// Click on the entity link in the drawer title
|
||||
await page.click(
|
||||
'.ant-drawer-title > [data-testid="entity-link"] > .ant-typography'
|
||||
);
|
||||
|
||||
// Check if the edit tier button is visible
|
||||
await expect(page.locator('[data-testid="edit-tier"]')).toBeVisible();
|
||||
};
|
||||
|
||||
export const checkStewardPermissions = async (page: Page) => {
|
||||
// Check Add domain permission
|
||||
await expect(page.locator('[data-testid="add-domain"]')).not.toBeVisible();
|
||||
|
||||
await expect(
|
||||
page
|
||||
.getByRole('cell', { name: 'user_id' })
|
||||
.getByTestId('edit-displayName-button')
|
||||
).toBeVisible();
|
||||
|
||||
// Check edit owner permission
|
||||
await expect(page.locator('[data-testid="edit-owner"]')).toBeVisible();
|
||||
|
||||
// Check edit description permission
|
||||
await expect(page.locator('[data-testid="edit-description"]')).toBeVisible();
|
||||
|
||||
// Check edit tier permission
|
||||
await expect(page.locator('[data-testid="edit-tier"]')).toBeVisible();
|
||||
|
||||
// Check right panel add tags button
|
||||
await expect(
|
||||
page.locator(
|
||||
'[data-testid="entity-right-panel"] [data-testid="tags-container"] [data-testid="entity-tags"] .tag-chip-add-button'
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
// Check right panel add glossary term button
|
||||
await expect(
|
||||
page.locator(
|
||||
'[data-testid="entity-right-panel"] [data-testid="glossary-container"] [data-testid="entity-tags"] .tag-chip-add-button'
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
// Check manage button
|
||||
await expect(page.locator('[data-testid="manage-button"]')).toBeVisible();
|
||||
|
||||
// Click on lineage item
|
||||
await page.click('[data-testid="lineage"] > .ant-space-item');
|
||||
|
||||
// Check if edit lineage button is enabled
|
||||
await expect(page.locator('[data-testid="edit-lineage"]')).toBeEnabled();
|
||||
};
|
||||
|
||||
export const addUser = async (page: Page, { name, email, password, role }) => {
|
||||
await page.click('[data-testid="add-user"]');
|
||||
|
||||
await page.fill('[data-testid="email"]', email);
|
||||
|
||||
await page.fill('[data-testid="displayName"]', name);
|
||||
|
||||
await page.fill(descriptionBox, 'Adding new user');
|
||||
|
||||
await page.click(':nth-child(2) > .ant-radio > .ant-radio-input');
|
||||
await page.fill('#password', password);
|
||||
await page.fill('#confirmPassword', password);
|
||||
|
||||
await page.click('[data-testid="roles-dropdown"] > .ant-select-selector');
|
||||
await page.type(
|
||||
'[data-testid="roles-dropdown"] > .ant-select-selector',
|
||||
role
|
||||
);
|
||||
await page.click('.ant-select-item-option-content');
|
||||
await page.click('[data-testid="roles-dropdown"] > .ant-select-selector');
|
||||
|
||||
const saveResponse = page.waitForResponse('/api/v1/users');
|
||||
await page.click('[data-testid="save-user"]');
|
||||
await saveResponse;
|
||||
|
||||
expect((await saveResponse).status()).toBe(201);
|
||||
};
|
||||
|
||||
const resetPasswordModal = async (
|
||||
page: Page,
|
||||
oldPassword: string,
|
||||
newPassword: string,
|
||||
isOldPasswordCorrect = true
|
||||
) => {
|
||||
await page.fill('[data-testid="input-oldPassword"]', oldPassword);
|
||||
await page.fill('[data-testid="input-newPassword"]', newPassword);
|
||||
await page.fill('[data-testid="input-confirm-newPassword"]', newPassword);
|
||||
|
||||
const saveResetPasswordResponse = page.waitForResponse(
|
||||
'/api/v1/users/changePassword'
|
||||
);
|
||||
await page.click(
|
||||
'.ant-modal-footer > .ant-btn-primary:has-text("Update Password")'
|
||||
);
|
||||
|
||||
await saveResetPasswordResponse;
|
||||
|
||||
await toastNotification(
|
||||
page,
|
||||
isOldPasswordCorrect
|
||||
? 'Password updated successfully.'
|
||||
: 'Old Password is not correct'
|
||||
);
|
||||
};
|
||||
|
||||
export const resetPassword = async (
|
||||
page: Page,
|
||||
oldCorrectPassword: string,
|
||||
oldWrongPassword: string,
|
||||
newPassword: string
|
||||
) => {
|
||||
await visitOwnProfilePage(page);
|
||||
|
||||
await page.click('[data-testid="change-password-button"]');
|
||||
|
||||
await expect(page.locator('.ant-modal-wrap')).toBeVisible();
|
||||
|
||||
// Try with the wrong old password should throw an error
|
||||
await resetPasswordModal(page, oldWrongPassword, newPassword, false);
|
||||
|
||||
// Try with the Correct old password should reset the password
|
||||
await resetPasswordModal(page, oldCorrectPassword, newPassword);
|
||||
};
|
||||
|
||||
export const expectSettingEntityNotVisible = async (
|
||||
page: Page,
|
||||
path: string[]
|
||||
) => {
|
||||
await expect(page.getByTestId(path[0])).not.toBeVisible();
|
||||
};
|
||||
|
||||
// Check the permissions for the settings page for DataSteward and DataConsumer
|
||||
export const settingPageOperationPermissionCheck = async (page: Page) => {
|
||||
await redirectToHomePage(page);
|
||||
|
||||
for (const id of Object.values(SETTING_PAGE_ENTITY_PERMISSION)) {
|
||||
let apiResponse: Promise<Response> | undefined;
|
||||
if (id?.api) {
|
||||
apiResponse = page.waitForResponse(id.api);
|
||||
}
|
||||
// Navigate to settings and respective tab page
|
||||
await settingClick(page, id.testid);
|
||||
if (id?.api && apiResponse) {
|
||||
await apiResponse;
|
||||
}
|
||||
|
||||
await expect(page.locator('.ant-skeleton-button')).not.toBeVisible();
|
||||
await expect(page.getByTestId(id.button)).not.toBeVisible();
|
||||
}
|
||||
|
||||
for (const id of Object.values(GLOBAL_SETTING_PERMISSIONS)) {
|
||||
if (id.testid === GlobalSettingOptions.METADATA) {
|
||||
await settingClick(page, id.testid);
|
||||
} else {
|
||||
await sidebarClick(page, SidebarItem.SETTINGS);
|
||||
let paths = SETTINGS_OPTIONS_PATH[id.testid];
|
||||
|
||||
if (id.isCustomProperty) {
|
||||
paths = SETTING_CUSTOM_PROPERTIES_PATH[id.testid];
|
||||
}
|
||||
|
||||
await expectSettingEntityNotVisible(page, paths);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user