mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-26 18:06:03 +00:00
migrated task cypress spec in Activity feed playwright (#17618)
This commit is contained in:
parent
9245c8965f
commit
a79a9032ce
@ -1,386 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 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 {
|
|
||||||
interceptURL,
|
|
||||||
toastNotification,
|
|
||||||
verifyResponseStatusCode,
|
|
||||||
} from '../../common/common';
|
|
||||||
import {
|
|
||||||
createEntityTable,
|
|
||||||
deleteUserEntity,
|
|
||||||
hardDeleteService,
|
|
||||||
} from '../../common/EntityUtils';
|
|
||||||
import {
|
|
||||||
createAndUpdateDescriptionTask,
|
|
||||||
createDescriptionTask,
|
|
||||||
editAssignee,
|
|
||||||
verifyTaskDetails,
|
|
||||||
} from '../../common/TaskUtils';
|
|
||||||
import { visitEntityDetailsPage } from '../../common/Utils/Entity';
|
|
||||||
import { getToken } from '../../common/Utils/LocalStorage';
|
|
||||||
import { addOwner } from '../../common/Utils/Owner';
|
|
||||||
import { uuid } from '../../constants/constants';
|
|
||||||
import { EntityType } from '../../constants/Entity.interface';
|
|
||||||
import {
|
|
||||||
DATABASE_SERVICE,
|
|
||||||
USER_DETAILS,
|
|
||||||
USER_NAME,
|
|
||||||
} from '../../constants/EntityConstant';
|
|
||||||
import { SERVICE_CATEGORIES } from '../../constants/service.constants';
|
|
||||||
|
|
||||||
const ENTITY_TABLE = {
|
|
||||||
term: DATABASE_SERVICE.entity.name,
|
|
||||||
displayName: DATABASE_SERVICE.entity.name,
|
|
||||||
entity: EntityType.Table,
|
|
||||||
serviceName: DATABASE_SERVICE.service.name,
|
|
||||||
schemaName: DATABASE_SERVICE.schema.name,
|
|
||||||
entityType: 'Table',
|
|
||||||
};
|
|
||||||
|
|
||||||
const POLICY_DETAILS = {
|
|
||||||
name: `cy-data-viewAll-policy-${uuid()}`,
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
name: 'viewRuleAllowed',
|
|
||||||
resources: ['All'],
|
|
||||||
operations: ['ViewAll'],
|
|
||||||
effect: 'allow',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
effect: 'deny',
|
|
||||||
name: 'editNotAllowed',
|
|
||||||
operations: ['EditAll'],
|
|
||||||
resources: ['All'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const ROLE_DETAILS = {
|
|
||||||
name: `cy-data-viewAll-role-${uuid()}`,
|
|
||||||
policies: [POLICY_DETAILS.name],
|
|
||||||
};
|
|
||||||
|
|
||||||
const TEAM_DETAILS = {
|
|
||||||
name: 'viewAllTeam',
|
|
||||||
displayName: 'viewAllTeam',
|
|
||||||
teamType: 'Group',
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Task flow should work', { tags: 'DataAssets' }, () => {
|
|
||||||
const data = {
|
|
||||||
user: { id: '' },
|
|
||||||
policy: { id: '' },
|
|
||||||
role: { id: '' },
|
|
||||||
team: { id: '' },
|
|
||||||
};
|
|
||||||
|
|
||||||
before(() => {
|
|
||||||
cy.login();
|
|
||||||
cy.getAllLocalStorage().then((storageData) => {
|
|
||||||
const token = getToken(storageData);
|
|
||||||
|
|
||||||
createEntityTable({
|
|
||||||
token,
|
|
||||||
...DATABASE_SERVICE,
|
|
||||||
tables: [DATABASE_SERVICE.entity],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create ViewAll Policy
|
|
||||||
cy.request({
|
|
||||||
method: 'POST',
|
|
||||||
url: `/api/v1/policies`,
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
body: POLICY_DETAILS,
|
|
||||||
}).then((policyResponse) => {
|
|
||||||
data.policy = policyResponse.body;
|
|
||||||
|
|
||||||
// Create ViewAll Role
|
|
||||||
cy.request({
|
|
||||||
method: 'POST',
|
|
||||||
url: `/api/v1/roles`,
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
body: ROLE_DETAILS,
|
|
||||||
}).then((roleResponse) => {
|
|
||||||
data.role = roleResponse.body;
|
|
||||||
|
|
||||||
// Create a new user
|
|
||||||
cy.request({
|
|
||||||
method: 'POST',
|
|
||||||
url: `/api/v1/users/signup`,
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
body: USER_DETAILS,
|
|
||||||
}).then((userResponse) => {
|
|
||||||
data.user = userResponse.body;
|
|
||||||
|
|
||||||
// create team
|
|
||||||
cy.request({
|
|
||||||
method: 'GET',
|
|
||||||
url: `/api/v1/teams/name/Organization`,
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
}).then((teamResponse) => {
|
|
||||||
cy.request({
|
|
||||||
method: 'POST',
|
|
||||||
url: `/api/v1/teams`,
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
body: {
|
|
||||||
...TEAM_DETAILS,
|
|
||||||
parents: [teamResponse.body.id],
|
|
||||||
users: [userResponse.body.id],
|
|
||||||
defaultRoles: [roleResponse.body.id],
|
|
||||||
},
|
|
||||||
}).then((teamResponse) => {
|
|
||||||
data.team = teamResponse.body;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
after(() => {
|
|
||||||
cy.login();
|
|
||||||
cy.getAllLocalStorage().then((storageData) => {
|
|
||||||
const token = getToken(storageData);
|
|
||||||
|
|
||||||
hardDeleteService({
|
|
||||||
token,
|
|
||||||
serviceFqn: ENTITY_TABLE.serviceName,
|
|
||||||
serviceType: SERVICE_CATEGORIES.DATABASE_SERVICES,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clean up for the created data
|
|
||||||
deleteUserEntity({ token, id: data.user.id });
|
|
||||||
|
|
||||||
cy.request({
|
|
||||||
method: 'DELETE',
|
|
||||||
url: `/api/v1/teams/${data.team.id}?hardDelete=true&recursive=true`,
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.request({
|
|
||||||
method: 'DELETE',
|
|
||||||
url: `/api/v1/policies/${data.policy.id}?hardDelete=true&recursive=true`,
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.request({
|
|
||||||
method: 'DELETE',
|
|
||||||
url: `/api/v1/roles/${data.role.id}?hardDelete=true&recursive=true`,
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.login();
|
|
||||||
interceptURL('GET', '/api/v1/permissions/*/name/*', 'entityPermission');
|
|
||||||
interceptURL('GET', '/api/v1/feed/count?entityLink=*', 'entityFeed');
|
|
||||||
interceptURL('GET', '/api/v1/search/suggest?q=*', 'suggestApi');
|
|
||||||
interceptURL('PUT', '/api/v1/feed/tasks/*/resolve', 'taskResolve');
|
|
||||||
interceptURL(
|
|
||||||
'GET',
|
|
||||||
`/api/v1/search/query?q=*%20AND%20disabled:false&index=tag_search_index*`,
|
|
||||||
'suggestTag'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const assignee = 'adam.matthews2';
|
|
||||||
const tag = 'Personal';
|
|
||||||
|
|
||||||
const createTagTask = (value) => {
|
|
||||||
interceptURL('POST', 'api/v1/feed', 'createTask');
|
|
||||||
|
|
||||||
cy.get('#title').should(
|
|
||||||
'have.value',
|
|
||||||
value.tagCount > 0
|
|
||||||
? `Update tags for table ${value.term}`
|
|
||||||
: `Request tags for table ${value.term}`
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.get('[data-testid="select-assignee"] > .ant-select-selector').type(
|
|
||||||
assignee
|
|
||||||
);
|
|
||||||
// select value from dropdown
|
|
||||||
verifyResponseStatusCode('@suggestApi', 200);
|
|
||||||
|
|
||||||
cy.get(`[data-testid="${assignee}"]`).trigger('mouseover').trigger('click');
|
|
||||||
|
|
||||||
cy.clickOutside();
|
|
||||||
if (value.tagCount > 0) {
|
|
||||||
cy.get('[data-testid="tag-selector"]')
|
|
||||||
.find('[data-testid="remove-tags"]')
|
|
||||||
.each(($btn) => {
|
|
||||||
cy.wrap($btn).click();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cy.get('[data-testid="tag-selector"]').click().type(tag);
|
|
||||||
|
|
||||||
verifyResponseStatusCode('@suggestTag', 200);
|
|
||||||
cy.get('[data-testid="tag-PersonalData.Personal"]').click();
|
|
||||||
|
|
||||||
cy.get('[data-testid="tags-label"]').click();
|
|
||||||
|
|
||||||
cy.get('button[type="submit"]').click();
|
|
||||||
verifyResponseStatusCode('@createTask', 201);
|
|
||||||
toastNotification('Task created successfully.');
|
|
||||||
|
|
||||||
// verify the task details
|
|
||||||
verifyTaskDetails(
|
|
||||||
value.tagCount > 0
|
|
||||||
? /#(\d+) Request to update tags for/
|
|
||||||
: /#(\d+) Request tags for/
|
|
||||||
);
|
|
||||||
|
|
||||||
// edit task assignees
|
|
||||||
editAssignee();
|
|
||||||
|
|
||||||
// Accept the description suggestion which is created
|
|
||||||
cy.get('.ant-btn-compact-first-item').contains('Accept Suggestion').click();
|
|
||||||
|
|
||||||
verifyResponseStatusCode('@taskResolve', 200);
|
|
||||||
|
|
||||||
toastNotification('Task resolved successfully');
|
|
||||||
|
|
||||||
verifyResponseStatusCode('@entityFeed', 200);
|
|
||||||
};
|
|
||||||
|
|
||||||
it('Task flow for table description', () => {
|
|
||||||
interceptURL(
|
|
||||||
'GET',
|
|
||||||
`/api/v1/${ENTITY_TABLE.entity}/name/*`,
|
|
||||||
'getEntityDetails'
|
|
||||||
);
|
|
||||||
|
|
||||||
visitEntityDetailsPage({
|
|
||||||
term: ENTITY_TABLE.term,
|
|
||||||
serviceName: ENTITY_TABLE.serviceName,
|
|
||||||
entity: ENTITY_TABLE.entity,
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get('[data-testid="request-description"]').click();
|
|
||||||
|
|
||||||
cy.wait('@getEntityDetails').then((res) => {
|
|
||||||
const entity = res.response.body;
|
|
||||||
|
|
||||||
// create description task
|
|
||||||
|
|
||||||
createAndUpdateDescriptionTask({
|
|
||||||
...ENTITY_TABLE,
|
|
||||||
term: entity.displayName ?? entity.name,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Task flow for table tags', () => {
|
|
||||||
interceptURL(
|
|
||||||
'GET',
|
|
||||||
`/api/v1/${ENTITY_TABLE.entity}/name/*`,
|
|
||||||
'getEntityDetails'
|
|
||||||
);
|
|
||||||
|
|
||||||
visitEntityDetailsPage({
|
|
||||||
term: ENTITY_TABLE.term,
|
|
||||||
serviceName: ENTITY_TABLE.serviceName,
|
|
||||||
entity: ENTITY_TABLE.entity,
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get('[data-testid="request-entity-tags"]').click();
|
|
||||||
|
|
||||||
cy.wait('@getEntityDetails').then((res) => {
|
|
||||||
const entity = res.response.body;
|
|
||||||
|
|
||||||
// create tag task
|
|
||||||
createTagTask({
|
|
||||||
...ENTITY_TABLE,
|
|
||||||
term: entity.displayName ?? entity.name,
|
|
||||||
tagCount: entity.tags.length ?? 0,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Assignee field should not be disabled for owned entity tasks', () => {
|
|
||||||
interceptURL(
|
|
||||||
'GET',
|
|
||||||
`/api/v1/${ENTITY_TABLE.entity}/name/*`,
|
|
||||||
'getEntityDetails'
|
|
||||||
);
|
|
||||||
|
|
||||||
visitEntityDetailsPage({
|
|
||||||
term: ENTITY_TABLE.term,
|
|
||||||
serviceName: ENTITY_TABLE.serviceName,
|
|
||||||
entity: ENTITY_TABLE.entity,
|
|
||||||
});
|
|
||||||
|
|
||||||
addOwner('Adam Rodriguez');
|
|
||||||
|
|
||||||
cy.get('[data-testid="request-description"]').click();
|
|
||||||
|
|
||||||
cy.wait('@getEntityDetails').then((res) => {
|
|
||||||
const entity = res.response.body;
|
|
||||||
|
|
||||||
createDescriptionTask({
|
|
||||||
...ENTITY_TABLE,
|
|
||||||
assignee: USER_NAME,
|
|
||||||
term: entity.displayName ?? entity.name,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should throw error for not having edit permission for viewAll user`, () => {
|
|
||||||
// logout for the admin user
|
|
||||||
cy.logout();
|
|
||||||
|
|
||||||
// login to viewAll user
|
|
||||||
cy.login(USER_DETAILS.email, USER_DETAILS.password);
|
|
||||||
|
|
||||||
interceptURL(
|
|
||||||
'GET',
|
|
||||||
`/api/v1/${ENTITY_TABLE.entity}/name/*`,
|
|
||||||
'getEntityDetails'
|
|
||||||
);
|
|
||||||
|
|
||||||
visitEntityDetailsPage({
|
|
||||||
term: ENTITY_TABLE.term,
|
|
||||||
serviceName: ENTITY_TABLE.serviceName,
|
|
||||||
entity: ENTITY_TABLE.entity,
|
|
||||||
});
|
|
||||||
|
|
||||||
interceptURL(
|
|
||||||
'GET',
|
|
||||||
'/api/v1/feed?entityLink=*type=Conversation*',
|
|
||||||
'entityFeed'
|
|
||||||
);
|
|
||||||
interceptURL('GET', '/api/v1/feed?entityLink=*type=Task*', 'taskFeed');
|
|
||||||
cy.get('[data-testid="activity_feed"]').click();
|
|
||||||
verifyResponseStatusCode('@entityFeed', 200);
|
|
||||||
|
|
||||||
cy.get('[data-menu-id*="tasks"]').click();
|
|
||||||
verifyResponseStatusCode('@taskFeed', 200);
|
|
||||||
|
|
||||||
// verify the task details
|
|
||||||
verifyTaskDetails(
|
|
||||||
/#(\d+) Request to update description for/,
|
|
||||||
USER_DETAILS.firstName
|
|
||||||
);
|
|
||||||
|
|
||||||
// Accept the description suggestion which is created
|
|
||||||
cy.get('.ant-btn-compact-first-item').contains('Accept Suggestion').click();
|
|
||||||
|
|
||||||
verifyResponseStatusCode('@taskResolve', 403);
|
|
||||||
|
|
||||||
toastNotification(
|
|
||||||
`Principal: CatalogPrincipal{name='${USER_NAME}'} operation EditDescription denied by role ${ROLE_DETAILS.name}, policy ${POLICY_DETAILS.name}, rule editNotAllowed`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@ -11,7 +11,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { expect, Page, test as base } from '@playwright/test';
|
import { expect, Page, test as base } from '@playwright/test';
|
||||||
|
import {
|
||||||
|
PolicyClass,
|
||||||
|
PolicyRulesType,
|
||||||
|
} 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 { TableClass } from '../../support/entity/TableClass';
|
||||||
|
import { TeamClass } from '../../support/team/TeamClass';
|
||||||
import { UserClass } from '../../support/user/UserClass';
|
import { UserClass } from '../../support/user/UserClass';
|
||||||
import {
|
import {
|
||||||
checkDescriptionInEditModal,
|
checkDescriptionInEditModal,
|
||||||
@ -22,9 +29,10 @@ import {
|
|||||||
descriptionBox,
|
descriptionBox,
|
||||||
redirectToHomePage,
|
redirectToHomePage,
|
||||||
toastNotification,
|
toastNotification,
|
||||||
|
uuid,
|
||||||
visitUserProfilePage,
|
visitUserProfilePage,
|
||||||
} from '../../utils/common';
|
} from '../../utils/common';
|
||||||
import { updateDescription } from '../../utils/entity';
|
import { addOwner, updateDescription } from '../../utils/entity';
|
||||||
import { clickOnLogo } from '../../utils/sidebar';
|
import { clickOnLogo } from '../../utils/sidebar';
|
||||||
import {
|
import {
|
||||||
checkTaskCount,
|
checkTaskCount,
|
||||||
@ -38,6 +46,7 @@ import { performUserLogin } from '../../utils/user';
|
|||||||
const entity = new TableClass();
|
const entity = new TableClass();
|
||||||
const entity2 = new TableClass();
|
const entity2 = new TableClass();
|
||||||
const entity3 = new TableClass();
|
const entity3 = new TableClass();
|
||||||
|
const entity4 = new TableClass();
|
||||||
const user1 = new UserClass();
|
const user1 = new UserClass();
|
||||||
const user2 = new UserClass();
|
const user2 = new UserClass();
|
||||||
const adminUser = new UserClass();
|
const adminUser = new UserClass();
|
||||||
@ -61,7 +70,9 @@ test.describe('Activity feed', () => {
|
|||||||
await entity.create(apiContext);
|
await entity.create(apiContext);
|
||||||
await entity2.create(apiContext);
|
await entity2.create(apiContext);
|
||||||
await entity3.create(apiContext);
|
await entity3.create(apiContext);
|
||||||
|
await entity4.create(apiContext);
|
||||||
await user1.create(apiContext);
|
await user1.create(apiContext);
|
||||||
|
await user2.create(apiContext);
|
||||||
|
|
||||||
await afterAction();
|
await afterAction();
|
||||||
});
|
});
|
||||||
@ -71,7 +82,9 @@ test.describe('Activity feed', () => {
|
|||||||
await entity.delete(apiContext);
|
await entity.delete(apiContext);
|
||||||
await entity2.delete(apiContext);
|
await entity2.delete(apiContext);
|
||||||
await entity3.delete(apiContext);
|
await entity3.delete(apiContext);
|
||||||
|
await entity4.delete(apiContext);
|
||||||
await user1.delete(apiContext);
|
await user1.delete(apiContext);
|
||||||
|
await user2.delete(apiContext);
|
||||||
await adminUser.delete(apiContext);
|
await adminUser.delete(apiContext);
|
||||||
|
|
||||||
await afterAction();
|
await afterAction();
|
||||||
@ -245,22 +258,22 @@ test.describe('Activity feed', () => {
|
|||||||
|
|
||||||
test('Update Description Task on Columns', async ({ page }) => {
|
test('Update Description Task on Columns', async ({ page }) => {
|
||||||
const firstTaskValue: TaskDetails = {
|
const firstTaskValue: TaskDetails = {
|
||||||
term: entity.entity.name,
|
term: entity4.entity.name,
|
||||||
assignee: user1.responseData.name,
|
assignee: user1.responseData.name,
|
||||||
description: 'Column Description 1',
|
description: 'Column Description 1',
|
||||||
columnName: entity.entity.columns[0].name,
|
columnName: entity4.entity.columns[0].name,
|
||||||
oldDescription: entity.entity.columns[0].description,
|
oldDescription: entity4.entity.columns[0].description,
|
||||||
};
|
};
|
||||||
const secondTaskValue: TaskDetails = {
|
const secondTaskValue: TaskDetails = {
|
||||||
...firstTaskValue,
|
...firstTaskValue,
|
||||||
description: 'Column Description 2',
|
description: 'Column Description 2',
|
||||||
columnName: entity.entity.columns[1].name,
|
columnName: entity4.entity.columns[1].name,
|
||||||
oldDescription: entity.entity.columns[1].description,
|
oldDescription: entity4.entity.columns[1].description,
|
||||||
};
|
};
|
||||||
|
|
||||||
await redirectToHomePage(page);
|
await redirectToHomePage(page);
|
||||||
|
|
||||||
await entity.visitEntityPage(page);
|
await entity4.visitEntityPage(page);
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByRole('cell', { name: 'The ID of the store. This' })
|
.getByRole('cell', { name: 'The ID of the store. This' })
|
||||||
@ -370,7 +383,7 @@ test.describe('Activity feed', () => {
|
|||||||
await checkTaskCount(page, 0, 1);
|
await checkTaskCount(page, 0, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Open and Closed Task tab', async ({ page }) => {
|
test('Open and Closed Task Tab', async ({ page }) => {
|
||||||
const value: TaskDetails = {
|
const value: TaskDetails = {
|
||||||
term: entity3.entity.name,
|
term: entity3.entity.name,
|
||||||
assignee: user1.responseData.name,
|
assignee: user1.responseData.name,
|
||||||
@ -439,18 +452,79 @@ test.describe('Activity feed', () => {
|
|||||||
'Closing the task with comment'
|
'Closing the task with comment'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Assignee field should not be disabled for owned entity tasks', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const value: TaskDetails = {
|
||||||
|
term: entity4.entity.name,
|
||||||
|
assignee: user1.responseData.name,
|
||||||
|
};
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
|
||||||
|
await entity4.visitEntityPage(page);
|
||||||
|
|
||||||
|
await addOwner({
|
||||||
|
page,
|
||||||
|
owner: user2.responseData.displayName,
|
||||||
|
type: 'Users',
|
||||||
|
endpoint: EntityTypeEndpoint.Table,
|
||||||
|
dataTestId: 'data-assets-header',
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.getByTestId('request-description').click();
|
||||||
|
|
||||||
|
// create description task
|
||||||
|
await createDescriptionTask(page, value);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
base.describe('Activity feed with Data Steward User', () => {
|
base.describe('Activity feed with Data Steward User', () => {
|
||||||
base.slow(true);
|
base.slow(true);
|
||||||
|
|
||||||
|
const id = uuid();
|
||||||
|
const rules: PolicyRulesType[] = [
|
||||||
|
{
|
||||||
|
name: 'viewRuleAllowed',
|
||||||
|
resources: ['All'],
|
||||||
|
operations: ['ViewAll'],
|
||||||
|
effect: 'allow',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
effect: 'deny',
|
||||||
|
name: 'editNotAllowed',
|
||||||
|
operations: ['EditAll'],
|
||||||
|
resources: ['All'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const viewAllUser = new UserClass();
|
||||||
|
const viewAllPolicy = new PolicyClass();
|
||||||
|
const viewAllRoles = new RolesClass();
|
||||||
|
let viewAllTeam: TeamClass;
|
||||||
|
|
||||||
base.beforeAll('Setup pre-requests', async ({ browser }) => {
|
base.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||||
const { afterAction, apiContext } = await performAdminLogin(browser);
|
const { afterAction, apiContext } = await performAdminLogin(browser);
|
||||||
|
|
||||||
await entity.create(apiContext);
|
await entity.create(apiContext);
|
||||||
await entity2.create(apiContext);
|
await entity2.create(apiContext);
|
||||||
|
await entity3.create(apiContext);
|
||||||
await user1.create(apiContext);
|
await user1.create(apiContext);
|
||||||
await user2.create(apiContext);
|
await user2.create(apiContext);
|
||||||
|
await viewAllUser.create(apiContext);
|
||||||
|
await viewAllPolicy.create(apiContext, rules);
|
||||||
|
await viewAllRoles.create(apiContext, [viewAllPolicy.responseData.name]);
|
||||||
|
viewAllTeam = new TeamClass({
|
||||||
|
name: `PW%team-${id}`,
|
||||||
|
displayName: `PW Team ${id}`,
|
||||||
|
description: 'playwright team description',
|
||||||
|
teamType: 'Group',
|
||||||
|
users: [viewAllUser.responseData.id],
|
||||||
|
defaultRoles: viewAllRoles.responseData.id
|
||||||
|
? [viewAllRoles.responseData.id]
|
||||||
|
: [],
|
||||||
|
});
|
||||||
|
await viewAllTeam.create(apiContext);
|
||||||
|
|
||||||
await afterAction();
|
await afterAction();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -458,8 +532,13 @@ base.describe('Activity feed with Data Steward User', () => {
|
|||||||
const { afterAction, apiContext } = await performAdminLogin(browser);
|
const { afterAction, apiContext } = await performAdminLogin(browser);
|
||||||
await entity.delete(apiContext);
|
await entity.delete(apiContext);
|
||||||
await entity2.delete(apiContext);
|
await entity2.delete(apiContext);
|
||||||
|
await entity3.delete(apiContext);
|
||||||
await user1.delete(apiContext);
|
await user1.delete(apiContext);
|
||||||
await user2.delete(apiContext);
|
await user2.delete(apiContext);
|
||||||
|
await viewAllUser.delete(apiContext);
|
||||||
|
await viewAllPolicy.delete(apiContext);
|
||||||
|
await viewAllRoles.delete(apiContext);
|
||||||
|
await viewAllTeam.delete(apiContext);
|
||||||
|
|
||||||
await afterAction();
|
await afterAction();
|
||||||
});
|
});
|
||||||
@ -749,4 +828,53 @@ base.describe('Activity feed with Data Steward User', () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
base(
|
||||||
|
'Accepting task should throw error for not having edit permission',
|
||||||
|
async ({ browser }) => {
|
||||||
|
const { page: page1, afterAction: afterActionUser1 } =
|
||||||
|
await performUserLogin(browser, user1);
|
||||||
|
const { page: page2, afterAction: afterActionUser2 } =
|
||||||
|
await performUserLogin(browser, viewAllUser);
|
||||||
|
|
||||||
|
const value: TaskDetails = {
|
||||||
|
term: entity3.entity.name,
|
||||||
|
assignee: viewAllUser.responseData.name,
|
||||||
|
};
|
||||||
|
|
||||||
|
await base.step('Create and Assign Task to user 3', async () => {
|
||||||
|
await redirectToHomePage(page1);
|
||||||
|
await entity3.visitEntityPage(page1);
|
||||||
|
|
||||||
|
await page1.getByTestId('request-description').click();
|
||||||
|
|
||||||
|
await createDescriptionTask(page1, value);
|
||||||
|
|
||||||
|
await afterActionUser1();
|
||||||
|
});
|
||||||
|
|
||||||
|
await base.step(
|
||||||
|
'Accept Task By user 2 should throw error for since it has only viewAll permission',
|
||||||
|
async () => {
|
||||||
|
await redirectToHomePage(page2);
|
||||||
|
|
||||||
|
await entity3.visitEntityPage(page2);
|
||||||
|
|
||||||
|
await page2.getByTestId('activity_feed').click();
|
||||||
|
|
||||||
|
await page2.getByRole('menuitem', { name: 'Tasks' }).click();
|
||||||
|
|
||||||
|
await page2.getByText('Accept Suggestion').click();
|
||||||
|
|
||||||
|
await toastNotification(
|
||||||
|
page2,
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
`Principal: CatalogPrincipal{name='${viewAllUser.responseData.name}'} operation EditDescription denied by role ${viewAllRoles.responseData.name}, policy ${viewAllPolicy.responseData.name}, rule editNotAllowed`
|
||||||
|
);
|
||||||
|
|
||||||
|
await afterActionUser2();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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 { APIRequestContext } from '@playwright/test';
|
||||||
|
import { uuid } from '../../utils/common';
|
||||||
|
|
||||||
|
type ResponseDataType = {
|
||||||
|
name: string;
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
id?: string;
|
||||||
|
fullyQualifiedName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PolicyRulesType = {
|
||||||
|
name: string;
|
||||||
|
resources: string[];
|
||||||
|
operations: string[];
|
||||||
|
effect: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class PolicyClass {
|
||||||
|
id = uuid();
|
||||||
|
data: ResponseDataType;
|
||||||
|
responseData: ResponseDataType;
|
||||||
|
|
||||||
|
constructor(data?: ResponseDataType) {
|
||||||
|
this.data = data ?? {
|
||||||
|
name: `PW%Policy-${this.id}`,
|
||||||
|
displayName: `PW Policy ${this.id}`,
|
||||||
|
description: 'playwright for policy description',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
return this.responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(apiContext: APIRequestContext, rules: PolicyRulesType[]) {
|
||||||
|
const response = await apiContext.post('/api/v1/policies', {
|
||||||
|
data: { ...this.data, rules },
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
this.responseData = data;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(apiContext: APIRequestContext) {
|
||||||
|
const response = await apiContext.delete(
|
||||||
|
`/api/v1/policies/${this.responseData.id}?hardDelete=true&recursive=true`
|
||||||
|
);
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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 { APIRequestContext } from '@playwright/test';
|
||||||
|
import { uuid } from '../../utils/common';
|
||||||
|
|
||||||
|
type ResponseDataType = {
|
||||||
|
name: string;
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
id?: string;
|
||||||
|
fullyQualifiedName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class RolesClass {
|
||||||
|
id = uuid();
|
||||||
|
data: ResponseDataType;
|
||||||
|
responseData: ResponseDataType;
|
||||||
|
|
||||||
|
constructor(data?: ResponseDataType) {
|
||||||
|
this.data = data ?? {
|
||||||
|
name: `PW%Roles-${this.id}`,
|
||||||
|
displayName: `PW Roles ${this.id}`,
|
||||||
|
description: 'playwright for roles description',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
return this.responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(apiContext: APIRequestContext, policies: string[]) {
|
||||||
|
const response = await apiContext.post('/api/v1/roles', {
|
||||||
|
data: { ...this.data, policies },
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
this.responseData = data;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(apiContext: APIRequestContext) {
|
||||||
|
const response = await apiContext.delete(
|
||||||
|
`/api/v1/roles/${this.responseData.id}?hardDelete=true&recursive=true`
|
||||||
|
);
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ type ResponseDataType = {
|
|||||||
id?: string;
|
id?: string;
|
||||||
fullyQualifiedName?: string;
|
fullyQualifiedName?: string;
|
||||||
users?: string[];
|
users?: string[];
|
||||||
|
defaultRoles?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export class TeamClass {
|
export class TeamClass {
|
||||||
|
@ -110,7 +110,7 @@ export const toastNotification = async (
|
|||||||
) => {
|
) => {
|
||||||
await expect(page.getByRole('alert').first()).toHaveText(message);
|
await expect(page.getByRole('alert').first()).toHaveText(message);
|
||||||
|
|
||||||
await page.getByLabel('close').first().click();
|
await page.getByLabel('close', { exact: true }).first().click();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clickOutside = async (page: Page) => {
|
export const clickOutside = async (page: Page) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user