Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

playwright: migrate permission spec to playwright #17795

Merged
merged 12 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
/*
* 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 { Page, test as base } from '@playwright/test';
import { Operation } from 'fast-json-patch';
import { PolicyClass } from '../../support/access-control/PoliciesClass';
import { RolesClass } from '../../support/access-control/RolesClass';
import { TableClass } from '../../support/entity/TableClass';
import { UserClass } from '../../support/user/UserClass';
import { performAdminLogin } from '../../utils/admin';
import { getApiContext, redirectToHomePage, uuid } from '../../utils/common';
import { validateViewPermissions } from '../../utils/permission';

const policy = new PolicyClass();
const policy2 = new PolicyClass();
const role = new RolesClass();
const role2 = new RolesClass();
const user = new UserClass();
const table = new TableClass();

const viewPermissionsData = [
{
title: 'ViewBasic, ViewSampleData & ViewQueries permission',
data: {
patch: [
{ op: 'add', path: '/rules/0/operations/1', value: 'ViewSampleData' },
{ op: 'add', path: '/rules/0/operations/2', value: 'ViewQueries' },
],
permission: { viewSampleData: true, viewQueries: true },
},
},
{
title: 'ViewBasic, ViewSampleData, ViewQueries & ViewTests permission',
data: {
patch: [{ op: 'add', path: '/rules/0/operations/3', value: 'ViewTests' }],
permission: {
viewSampleData: true,
viewQueries: true,
viewTests: true,
},
},
},
{
title: 'EditDisplayName permission',
data: {
patch: [
{ op: 'add', path: '/rules/0/operations/4', value: 'EditDisplayName' },
],
permission: {
viewSampleData: true,
viewQueries: true,
viewTests: true,
editDisplayName: true,
},
},
},
];

const test = base.extend<{
adminPage: Page;
userPage: Page;
}>({
adminPage: async ({ browser }, use) => {
const { page } = await performAdminLogin(browser);
await use(page);
await page.close();
},
userPage: async ({ browser }, use) => {
const page = await browser.newPage();
await user.login(page);
await use(page);
await page.close();
},
});

test.beforeAll(async ({ browser }) => {
const { apiContext, afterAction } = await performAdminLogin(browser);
await user.create(apiContext);
const policyResponse = await policy.create(apiContext, [
{
name: `pw-permission-rule-${uuid()}`,
resources: ['All'],
operations: ['ViewBasic'],
effect: 'allow',
},
]);
const policyResponse2 = await policy2.create(apiContext, [
{
name: `pw-permission-rule-${uuid()}`,
resources: ['All'],
operations: ['EditOwners'],
effect: 'deny',
},
]);
await table.create(apiContext);
await table.createTestCase(apiContext);
await table.createQuery(apiContext);
const roleResponse = await role.create(apiContext, [
policyResponse.fullyQualifiedName,
]);
const roleResponse2 = await role2.create(apiContext, [
policyResponse2.fullyQualifiedName,
]);
await user.patch({
apiContext,
patchData: [
{
op: 'replace',
path: '/roles',
value: [
{
id: roleResponse.id,
type: 'role',
name: roleResponse.name,
},
{
id: roleResponse2.id,
type: 'role',
name: roleResponse2.name,
},
],
},
],
});
await afterAction();
});

test.afterAll(async ({ browser }) => {
const { apiContext, afterAction } = await performAdminLogin(browser);
await user.delete(apiContext);
await role.delete(apiContext);
await policy.delete(apiContext);
await table.delete(apiContext);
await afterAction();
});

test('Permissions', async ({ userPage, adminPage }) => {
test.slow();

await redirectToHomePage(userPage);

await test.step('ViewBasic permission', async () => {
await table.visitEntityPage(userPage);
await userPage.waitForSelector('[data-testid="loader"]', {
state: 'detached',
});
await validateViewPermissions(userPage);
});

for (const viewPermission of viewPermissionsData) {
await test.step(viewPermission.title, async () => {
const { apiContext, afterAction } = await getApiContext(adminPage);
await policy.patch(apiContext, viewPermission.data.patch as Operation[]);
await afterAction();
await redirectToHomePage(userPage);
await userPage.reload();
const permissionResponse = userPage.waitForResponse(
`/api/v1/permissions/table/name/${encodeURIComponent(
table.entityResponseData?.['fullyQualifiedName']
)}`
);
await table.visitEntityPage(userPage);
await permissionResponse;
await userPage.waitForSelector('[data-testid="loader"]', {
state: 'detached',
});
await validateViewPermissions(userPage, viewPermission.data.permission);
});
}

await test.step('EditQuery permission', async () => {
const { apiContext, afterAction } = await getApiContext(adminPage);
await policy.patch(apiContext, [
{
op: 'add',
path: '/rules/1',
value: {
name: `pw-edit-query-rule-${uuid()}`,
resources: ['query'],
operations: ['ViewAll', 'EditAll'],
effect: 'allow',
},
},
{ op: 'add', path: '/rules/0/operations/5', value: 'EditQueries' },
]);
await afterAction();
await redirectToHomePage(userPage);
await userPage.reload();
const permissionResponse = userPage.waitForResponse(
`/api/v1/permissions/table/name/${encodeURIComponent(
table.entityResponseData?.['fullyQualifiedName']
)}`
);
await table.visitEntityPage(userPage);
await permissionResponse;
await userPage.waitForSelector('[data-testid="loader"]', {
state: 'detached',
});
const queryListResponse = userPage.waitForResponse(
'/api/v1/search/query?q=*&index=query_search_index*'
);
await userPage.click('[data-testid="table_queries"]');
await queryListResponse;
await userPage.click('[data-testid="query-btn"]');
await userPage.click('[data-menu-id*="edit-query"]');
await userPage.locator('.CodeMirror-line').click();
await userPage.keyboard.type('updated');
const saveQueryResponse = userPage.waitForResponse('/api/v1/queries/*');
await userPage.click('[data-testid="save-query-btn"]');
await saveQueryResponse;
});

await test.step('EditTest permission', async () => {
const testCaseName = table.testCasesResponseData[0]?.['name'];
const { apiContext, afterAction } = await getApiContext(adminPage);
await policy.patch(apiContext, [
{ op: 'add', path: '/rules/1/operations/6', value: 'EditTests' },
{
op: 'add',
path: '/rules/2',
value: {
name: `cy-edit-test-case-rule-${uuid()}`,
resources: ['testCase'],
operations: ['ViewAll', 'EditAll'],
effect: 'allow',
},
},
]);
await afterAction();
await redirectToHomePage(userPage);
await userPage.reload();
const permissionResponse = userPage.waitForResponse(
`/api/v1/permissions/table/name/${encodeURIComponent(
table.entityResponseData?.['fullyQualifiedName']
)}`
);
await table.visitEntityPage(userPage);
await permissionResponse;
await userPage.waitForSelector('[data-testid="loader"]', {
state: 'detached',
});

await userPage.getByTestId('profiler').click();
const testCaseResponse = userPage.waitForResponse(
'/api/v1/dataQuality/testCases/search/list?fields=*'
);
await userPage
.getByTestId('profiler-tab-left-panel')
.getByText('Data Quality')
.click();
await testCaseResponse;

await userPage.getByTestId(`edit-${testCaseName}`).click();
await userPage.locator('#tableTestForm_displayName').clear();
await userPage.fill('#tableTestForm_displayName', 'Update_display_name');
const saveTestResponse = userPage.waitForResponse(
'/api/v1/dataQuality/testCases/*'
);
await userPage.locator('.ant-modal-footer').getByText('Submit').click();
await saveTestResponse;
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -63,27 +63,6 @@ entities.forEach((EntityClass) => {

await user.create(apiContext);

const dataConsumerRoleResponse = await apiContext.get(
'/api/v1/roles/name/DataConsumer'
);

const dataConsumerRole = await dataConsumerRoleResponse.json();

await user.patch({
apiContext,
patchData: [
{
op: 'add',
path: '/roles/0',
value: {
id: dataConsumerRole.id,
type: 'role',
name: dataConsumerRole.name,
},
},
],
});

await EntityDataClass.preRequisitesForTests(apiContext);
await entity.create(apiContext);
await afterAction();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { JWT_EXPIRY_TIME_MAP } from '../constant/login';
import { AdminClass } from '../support/user/AdminClass';
import { getApiContext } from '../utils/common';
import { updateJWTTokenExpiryTime } from '../utils/login';
import { removeOrganizationPolicyAndRole } from '../utils/team';
const adminFile = 'playwright/.auth/admin.json';

setup('authenticate as admin', async ({ page }) => {
Expand All @@ -25,6 +26,7 @@ setup('authenticate as admin', async ({ page }) => {
await page.waitForURL('**/my-data');
const { apiContext, afterAction } = await getApiContext(page);
await updateJWTTokenExpiryTime(apiContext, JWT_EXPIRY_TIME_MAP['4 hours']);
await removeOrganizationPolicyAndRole(apiContext);
await afterAction();
await admin.logout(page);
await page.waitForURL('**/signin');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* limitations under the License.
*/
import { APIRequestContext } from '@playwright/test';
import { Operation } from 'fast-json-patch';
import { uuid } from '../../utils/common';

type ResponseDataType = {
Expand Down Expand Up @@ -55,6 +56,22 @@ export class PolicyClass {
return data;
}

async patch(apiContext: APIRequestContext, patchData: Operation[]) {
const response = await apiContext.patch(
`/api/v1/policies/${this.responseData.id}`,
{
data: patchData,
headers: {
'Content-Type': 'application/json-patch+json',
},
}
);
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`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export class TableClass extends EntityClass {
testSuiteResponseData: unknown;
testSuitePipelineResponseData: unknown[] = [];
testCasesResponseData: unknown[] = [];
queryResponseData: unknown[] = [];

constructor(name?: string) {
super(EntityTypeEndpoint.Table);
Expand Down Expand Up @@ -172,6 +173,25 @@ export class TableClass extends EntityClass {
});
}

async createQuery(apiContext: APIRequestContext, queryText?: string) {
const queryResponse = await apiContext.post('/api/v1/queries', {
data: {
query:
queryText ??
`select * from ${this.entityResponseData?.['fullyQualifiedName']}`,
queryUsedIn: [{ id: this.entityResponseData?.['id'], type: 'table' }],
queryDate: Date.now(),
service: this.serviceResponseData?.['name'],
},
});

const query = await queryResponse.json();

this.queryResponseData.push(query);

return query;
}

async createTestSuiteAndPipelines(
apiContext: APIRequestContext,
testSuite?: TestSuiteData
Expand Down
Loading
Loading