mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-07-24 01:40:00 +00:00
492 lines
14 KiB
TypeScript
492 lines
14 KiB
TypeScript
/*
|
|
* 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 { 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 { 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,
|
|
checkEditOwnerButtonPermission,
|
|
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();
|
|
|
|
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();
|
|
},
|
|
});
|
|
|
|
base.beforeAll('Setup pre-requests', async ({ browser }) => {
|
|
const { apiContext, afterAction } = await performAdminLogin(browser);
|
|
|
|
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();
|
|
});
|
|
|
|
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('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 ({
|
|
adminPage,
|
|
}) => {
|
|
await redirectToHomePage(adminPage);
|
|
await settingClick(adminPage, GlobalSettingOptions.USERS);
|
|
await adminPage.waitForLoadState('networkidle');
|
|
await adminPage.waitForSelector('.user-list-table [data-testid="loader"]', {
|
|
state: 'detached',
|
|
});
|
|
await softDeleteUserProfilePage(
|
|
adminPage,
|
|
user.responseData.name,
|
|
user.responseData.displayName
|
|
);
|
|
|
|
await restoreUserProfilePage(adminPage, user.responseData.displayName);
|
|
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 dataConsumerPage.waitForLoadState('networkidle');
|
|
await dataConsumerPage.waitForSelector('[data-testid="loader"]', {
|
|
state: 'detached',
|
|
});
|
|
|
|
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-review-button"]')
|
|
).not.toBeVisible();
|
|
|
|
const hasAddOwnerButton = dataConsumerPage.locator(
|
|
'[data-testid="add-owner"]'
|
|
);
|
|
|
|
if (!hasAddOwnerButton) {
|
|
await checkEditOwnerButtonPermission(dataConsumerPage);
|
|
}
|
|
|
|
// 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);
|
|
});
|
|
});
|
|
|
|
test.describe('User Profile Feed Interactions', () => {
|
|
test('Should navigate to user profile from feed card avatar click', async ({
|
|
adminPage,
|
|
}) => {
|
|
await redirectToHomePage(adminPage);
|
|
const feedResponse = adminPage.waitForResponse(
|
|
'/api/v1/feed?type=Conversation'
|
|
);
|
|
|
|
await visitOwnProfilePage(adminPage);
|
|
await feedResponse;
|
|
|
|
await adminPage.waitForSelector('[data-testid="message-container"]');
|
|
const userDetailsResponse = adminPage.waitForResponse(
|
|
'/api/v1/users/name/*'
|
|
);
|
|
const userFeedResponse = adminPage.waitForResponse(
|
|
'/api/v1/feed?type=Conversation&filterType=OWNER_OR_FOLLOWS&userId=*'
|
|
);
|
|
|
|
const avatar = adminPage
|
|
.locator('[data-testid="message-container"]')
|
|
.first()
|
|
.locator('[data-testid="profile-avatar"]');
|
|
|
|
await avatar.hover();
|
|
await adminPage.waitForSelector('.ant-popover-card');
|
|
await adminPage.getByTestId('user-name').nth(1).click();
|
|
|
|
await userDetailsResponse;
|
|
await userFeedResponse;
|
|
const response = await userDetailsResponse;
|
|
const { fullyQualifiedName } = await response.json();
|
|
|
|
await expect(
|
|
adminPage.locator('[data-testid="user-display-name"]')
|
|
).toHaveText(fullyQualifiedName);
|
|
});
|
|
});
|