mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-17 03:38:18 +00:00
Migrate: tags spec to playwright (#17758)
This commit is contained in:
parent
1c90eaaf3d
commit
ae9de3057d
@ -1,379 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
addNewTagToEntity,
|
||||
descriptionBox,
|
||||
interceptURL,
|
||||
verifyResponseStatusCode,
|
||||
} from '../../common/common';
|
||||
import {
|
||||
deleteClassification,
|
||||
submitForm,
|
||||
validateForm,
|
||||
visitClassificationPage,
|
||||
} from '../../common/TagUtils';
|
||||
import { visitEntityDetailsPage } from '../../common/Utils/Entity';
|
||||
import { assignTags, removeTags } from '../../common/Utils/Tags';
|
||||
import {
|
||||
DELETE_TERM,
|
||||
NEW_CLASSIFICATION,
|
||||
NEW_TAG,
|
||||
SEARCH_ENTITY_TABLE,
|
||||
} from '../../constants/constants';
|
||||
import { EntityType } from '../../constants/Entity.interface';
|
||||
|
||||
const permanentDeleteModal = (entity) => {
|
||||
cy.get('[data-testid="delete-confirmation-modal"]')
|
||||
.should('exist')
|
||||
.then(() => {
|
||||
cy.get('[role="dialog"]').should('be.visible');
|
||||
cy.get('[data-testid="modal-header"]').should('be.visible');
|
||||
});
|
||||
cy.get('[data-testid="modal-header"]')
|
||||
.should('be.visible')
|
||||
.should('contain', `Delete ${entity}`);
|
||||
cy.get('[data-testid="confirmation-text-input"]')
|
||||
.should('be.visible')
|
||||
.type(DELETE_TERM);
|
||||
|
||||
cy.get('[data-testid="confirm-button"]')
|
||||
.should('be.visible')
|
||||
.should('not.disabled')
|
||||
.click();
|
||||
};
|
||||
|
||||
describe('Classification Page', { tags: 'Governance' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
interceptURL(
|
||||
'GET',
|
||||
`/api/v1/tags?fields=usageCount&parent=${NEW_CLASSIFICATION.name}&limit=10`,
|
||||
'getTagList'
|
||||
);
|
||||
interceptURL('GET', `/api/v1/permissions/classification/*`, 'permissions');
|
||||
interceptURL(
|
||||
'GET',
|
||||
`/api/v1/search/query?q=*%20AND%20disabled:false&index=tag_search_index*`,
|
||||
'suggestTag'
|
||||
);
|
||||
visitClassificationPage();
|
||||
});
|
||||
|
||||
it('Should render basic elements on page', () => {
|
||||
cy.get('[data-testid="add-classification"]').should('be.visible');
|
||||
cy.get('[data-testid="add-new-tag-button"]').should('be.visible');
|
||||
cy.get('[data-testid="manage-button"]').should('be.visible');
|
||||
cy.get('[data-testid="description-container"]').should('be.visible');
|
||||
cy.get('[data-testid="table"]').should('be.visible');
|
||||
|
||||
cy.get('.ant-table-thead > tr > .ant-table-cell')
|
||||
.eq(0)
|
||||
.contains('Tag')
|
||||
.should('be.visible');
|
||||
cy.get('.ant-table-thead > tr > .ant-table-cell')
|
||||
.eq(1)
|
||||
.contains('Display Name')
|
||||
.should('be.visible');
|
||||
cy.get('.ant-table-thead > tr > .ant-table-cell')
|
||||
.eq(2)
|
||||
.contains('Description')
|
||||
.should('be.visible');
|
||||
cy.get('.ant-table-thead > tr > .ant-table-cell')
|
||||
.eq(3)
|
||||
.contains('Actions')
|
||||
.should('be.visible');
|
||||
|
||||
cy.get('.activeCategory > .tag-category')
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
cy.get('.activeCategory > .tag-category')
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.then((heading) => {
|
||||
expect(text).to.equal(heading);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Create classification with validation checks', () => {
|
||||
interceptURL('POST', 'api/v1/classifications', 'createTagCategory');
|
||||
cy.get('[data-testid="add-classification"]').should('be.visible').click();
|
||||
cy.get('[data-testid="modal-container"]')
|
||||
.should('exist')
|
||||
.then(() => {
|
||||
cy.get('[role="dialog"]').should('be.visible');
|
||||
});
|
||||
|
||||
// validation should work
|
||||
validateForm();
|
||||
|
||||
cy.get('[data-testid="name"]')
|
||||
.should('be.visible')
|
||||
.clear()
|
||||
.type(NEW_CLASSIFICATION.name);
|
||||
cy.get('[data-testid="displayName"]')
|
||||
.should('be.visible')
|
||||
.type(NEW_CLASSIFICATION.displayName);
|
||||
cy.get(descriptionBox)
|
||||
.should('be.visible')
|
||||
.type(NEW_CLASSIFICATION.description);
|
||||
cy.get('[data-testid="mutually-exclusive-button"]')
|
||||
.scrollIntoView()
|
||||
.should('be.visible')
|
||||
.click();
|
||||
|
||||
submitForm();
|
||||
|
||||
verifyResponseStatusCode('@createTagCategory', 201);
|
||||
cy.get('[data-testid="modal-container"]').should('not.exist');
|
||||
cy.get('[data-testid="data-summary-container"]')
|
||||
.should('be.visible')
|
||||
.and('contain', NEW_CLASSIFICATION.displayName);
|
||||
});
|
||||
|
||||
it('Create tag with validation checks', () => {
|
||||
cy.get('[data-testid="data-summary-container"]')
|
||||
.contains(NEW_CLASSIFICATION.displayName)
|
||||
.should('be.visible')
|
||||
.as('newCategory');
|
||||
|
||||
cy.get('@newCategory')
|
||||
.click()
|
||||
.parent()
|
||||
.should('have.class', 'activeCategory');
|
||||
cy.get('[data-testid="add-new-tag-button"]').should('be.visible').click();
|
||||
cy.get('[data-testid="modal-container"]')
|
||||
.should('exist')
|
||||
.then(() => {
|
||||
cy.get('[role="dialog"]').should('be.visible');
|
||||
});
|
||||
|
||||
// validation should work
|
||||
validateForm();
|
||||
|
||||
cy.get('[data-testid="name"]')
|
||||
.should('be.visible')
|
||||
.clear()
|
||||
.type(NEW_TAG.name);
|
||||
cy.get('[data-testid="displayName"]')
|
||||
.should('be.visible')
|
||||
.type(NEW_TAG.displayName);
|
||||
cy.get(descriptionBox).should('be.visible').type(NEW_TAG.description);
|
||||
|
||||
cy.get('[data-testid="icon-url"]').scrollIntoView().type(NEW_TAG.icon);
|
||||
cy.get('[data-testid="tags_color-color-input"]')
|
||||
.scrollIntoView()
|
||||
.type(NEW_TAG.color);
|
||||
|
||||
interceptURL('POST', '/api/v1/tags', 'createTag');
|
||||
submitForm();
|
||||
|
||||
verifyResponseStatusCode('@createTag', 201);
|
||||
|
||||
cy.get('[data-testid="table"]').should('contain', NEW_TAG.name);
|
||||
});
|
||||
|
||||
it(`Assign tag to table ${SEARCH_ENTITY_TABLE.table_3.displayName}`, () => {
|
||||
const entity = SEARCH_ENTITY_TABLE.table_3;
|
||||
visitEntityDetailsPage({
|
||||
term: entity.term,
|
||||
serviceName: entity.serviceName,
|
||||
entity: entity.entity,
|
||||
});
|
||||
addNewTagToEntity(NEW_TAG);
|
||||
});
|
||||
|
||||
it('Assign tag to DatabaseSchema', () => {
|
||||
interceptURL(
|
||||
'GET',
|
||||
'/api/v1/permissions/databaseSchema/name/*',
|
||||
'permissions'
|
||||
);
|
||||
interceptURL('PUT', '/api/v1/feed/tasks/*/resolve', 'taskResolve');
|
||||
interceptURL(
|
||||
'GET',
|
||||
'/api/v1/databaseSchemas/name/*',
|
||||
'databaseSchemasPage'
|
||||
);
|
||||
interceptURL('PATCH', '/api/v1/databaseSchemas/*', 'addTags');
|
||||
|
||||
const entity = SEARCH_ENTITY_TABLE.table_3;
|
||||
const tag = 'PII.Sensitive';
|
||||
|
||||
visitEntityDetailsPage({
|
||||
term: entity.term,
|
||||
serviceName: entity.serviceName,
|
||||
entity: entity.entity,
|
||||
});
|
||||
|
||||
cy.get('[data-testid="breadcrumb-link"]')
|
||||
.should('be.visible')
|
||||
.contains(entity.schemaName)
|
||||
.click();
|
||||
|
||||
verifyResponseStatusCode('@databaseSchemasPage', 200);
|
||||
verifyResponseStatusCode('@permissions', 200);
|
||||
|
||||
assignTags(tag, EntityType.DatabaseSchema);
|
||||
|
||||
removeTags(tag, EntityType.DatabaseSchema);
|
||||
});
|
||||
|
||||
it('Assign tag using Task & Suggestion flow to DatabaseSchema', () => {
|
||||
interceptURL(
|
||||
'GET',
|
||||
'/api/v1/permissions/databaseSchema/name/*',
|
||||
'permissions'
|
||||
);
|
||||
interceptURL('PUT', '/api/v1/feed/tasks/*/resolve', 'taskResolve');
|
||||
interceptURL(
|
||||
'GET',
|
||||
'/api/v1/databaseSchemas/name/*',
|
||||
'databaseSchemasPage'
|
||||
);
|
||||
|
||||
const entity = SEARCH_ENTITY_TABLE.table_2;
|
||||
const tag = 'Personal';
|
||||
const assignee = 'admin';
|
||||
|
||||
visitEntityDetailsPage({
|
||||
term: entity.term,
|
||||
serviceName: entity.serviceName,
|
||||
entity: entity.entity,
|
||||
});
|
||||
|
||||
cy.get('[data-testid="breadcrumb-link"]')
|
||||
.should('be.visible')
|
||||
.contains(entity.schemaName)
|
||||
.click();
|
||||
|
||||
verifyResponseStatusCode('@databaseSchemasPage', 200);
|
||||
verifyResponseStatusCode('@permissions', 200);
|
||||
|
||||
// Create task to add tags
|
||||
interceptURL('POST', '/api/v1/feed', 'taskCreated');
|
||||
cy.get('[data-testid="request-entity-tags"]').should('exist').click();
|
||||
|
||||
// set assignees for task
|
||||
cy.get(
|
||||
'[data-testid="select-assignee"] > .ant-select-selector > .ant-select-selection-overflow'
|
||||
)
|
||||
.click()
|
||||
.type(assignee);
|
||||
cy.get(`[data-testid="${assignee}"]`).scrollIntoView().click();
|
||||
|
||||
// click outside the select box
|
||||
cy.clickOutside();
|
||||
|
||||
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('[data-testid="submit-tag-request"]').click();
|
||||
verifyResponseStatusCode('@taskCreated', 201);
|
||||
|
||||
// Accept the tag suggestion which is created
|
||||
cy.get('.ant-btn-compact-first-item').contains('Accept Suggestion').click();
|
||||
|
||||
verifyResponseStatusCode('@taskResolve', 200);
|
||||
verifyResponseStatusCode('@databaseSchemasPage', 200);
|
||||
cy.get('[data-testid="table"]').click();
|
||||
|
||||
cy.reload();
|
||||
verifyResponseStatusCode('@databaseSchemasPage', 200);
|
||||
|
||||
cy.get('[data-testid="tags-container"]').scrollIntoView().contains(tag);
|
||||
|
||||
cy.get('[data-testid="edit-button"]').click();
|
||||
|
||||
// Remove all added tags
|
||||
cy.get('[data-testid="remove-tags"]').click({ multiple: true });
|
||||
|
||||
interceptURL('PATCH', '/api/v1/databaseSchemas/*', 'removeTags');
|
||||
cy.get('[data-testid="saveAssociatedTag"]').scrollIntoView().click();
|
||||
verifyResponseStatusCode('@removeTags', 200);
|
||||
});
|
||||
|
||||
it('Should have correct tag usage count and redirection should work', () => {
|
||||
cy.get('[data-testid="data-summary-container"]')
|
||||
.contains(NEW_CLASSIFICATION.displayName)
|
||||
.should('be.visible')
|
||||
.as('newCategory');
|
||||
|
||||
cy.get('@newCategory')
|
||||
.click()
|
||||
.parent()
|
||||
.should('have.class', 'activeCategory');
|
||||
|
||||
verifyResponseStatusCode('@permissions', 200);
|
||||
cy.get('[data-testid="entity-header-display-name"]')
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
// Get the text of the first menu item
|
||||
if (text !== NEW_CLASSIFICATION.displayName) {
|
||||
verifyResponseStatusCode('@getTags', 200);
|
||||
}
|
||||
});
|
||||
|
||||
cy.get('[data-testid="usage-count"]').should('be.visible').as('count');
|
||||
cy.get('@count')
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
expect(text).to.equal('1');
|
||||
});
|
||||
|
||||
interceptURL(
|
||||
'GET',
|
||||
'api/v1/search/query?q=&index=**',
|
||||
'getEntityDetailsPage'
|
||||
);
|
||||
cy.get('@count').click();
|
||||
verifyResponseStatusCode('@getEntityDetailsPage', 200);
|
||||
});
|
||||
|
||||
it('Remove tag', () => {
|
||||
interceptURL(
|
||||
'DELETE',
|
||||
'/api/v1/tags/*?recursive=true&hardDelete=true',
|
||||
'deleteTag'
|
||||
);
|
||||
cy.get('[data-testid="data-summary-container"]')
|
||||
.contains(NEW_CLASSIFICATION.displayName)
|
||||
.click()
|
||||
.parent()
|
||||
.should('have.class', 'activeCategory');
|
||||
|
||||
verifyResponseStatusCode('@permissions', 200);
|
||||
|
||||
cy.get('[data-testid="table"]').should('contain', NEW_TAG.name);
|
||||
|
||||
cy.get('[data-testid="table"]').find('[data-testid="delete-tag"]').click();
|
||||
cy.wait(500); // adding manual wait to open modal, as it depends on click not an api.
|
||||
permanentDeleteModal(NEW_TAG.name);
|
||||
|
||||
verifyResponseStatusCode('@deleteTag', 200);
|
||||
cy.wait(500);
|
||||
cy.get('[data-testid="table"]')
|
||||
.contains(NEW_TAG.name)
|
||||
.should('not.be.exist');
|
||||
});
|
||||
|
||||
it('Remove classification', () => {
|
||||
deleteClassification(NEW_CLASSIFICATION);
|
||||
});
|
||||
});
|
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Copyright 2024 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { expect, Page, test } from '@playwright/test';
|
||||
import { SidebarItem } from '../../constant/sidebar';
|
||||
import { TableClass } from '../../support/entity/TableClass';
|
||||
import {
|
||||
clickOutside,
|
||||
createNewPage,
|
||||
descriptionBox,
|
||||
redirectToHomePage,
|
||||
uuid,
|
||||
} from '../../utils/common';
|
||||
import { sidebarClick } from '../../utils/sidebar';
|
||||
import { submitForm, validateForm } from '../../utils/tag';
|
||||
|
||||
const NEW_CLASSIFICATION = {
|
||||
name: `PlaywrightClassification-${uuid()}`,
|
||||
displayName: `PlaywrightClassification-${uuid()}`,
|
||||
description: 'This is the PlaywrightClassification',
|
||||
};
|
||||
const NEW_TAG = {
|
||||
name: `PlaywrightTag-${uuid()}`,
|
||||
displayName: `PlaywrightTag-${uuid()}`,
|
||||
renamedName: `PlaywrightTag-${uuid()}`,
|
||||
description: 'This is the PlaywrightTag',
|
||||
color: '#FF5733',
|
||||
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAF8AAACFCAMAAAAKN9SOAAAAA1BMVEXmGSCqexgYAAAAI0lEQVRoge3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAHgaMeAAAUWJHZ4AAAAASUVORK5CYII=',
|
||||
};
|
||||
const tagFqn = `${NEW_CLASSIFICATION.name}.${NEW_TAG.name}`;
|
||||
|
||||
const permanentDeleteModal = async (page: Page, entity: string) => {
|
||||
await page.waitForSelector('.ant-modal-content', {
|
||||
state: 'visible',
|
||||
});
|
||||
|
||||
await expect(page.locator('.ant-modal-content')).toBeVisible();
|
||||
|
||||
await expect(page.locator('[data-testid="modal-header"]')).toContainText(
|
||||
`Delete ${entity}`
|
||||
);
|
||||
|
||||
await page.fill('[data-testid="confirmation-text-input"]', 'DELETE');
|
||||
await page.click('[data-testid="confirm-button"]');
|
||||
};
|
||||
|
||||
test.describe.configure({ mode: 'serial' });
|
||||
|
||||
// use the admin user to login
|
||||
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||
|
||||
const table = new TableClass();
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await createNewPage(browser);
|
||||
await table.create(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.afterAll(async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await createNewPage(browser);
|
||||
await table.delete(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await redirectToHomePage(page);
|
||||
});
|
||||
|
||||
test('Classification Page', async ({ page }) => {
|
||||
await test.step('Should render basic elements on page', async () => {
|
||||
const getTags = page.waitForResponse('/api/v1/tags*');
|
||||
await sidebarClick(page, SidebarItem.TAGS);
|
||||
await getTags;
|
||||
|
||||
await expect(
|
||||
page.locator('[data-testid="add-classification"]')
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.locator('[data-testid="add-new-tag-button"]')
|
||||
).toBeVisible();
|
||||
await expect(page.locator('[data-testid="manage-button"]')).toBeVisible();
|
||||
await expect(
|
||||
page.locator('[data-testid="description-container"]')
|
||||
).toBeVisible();
|
||||
await expect(page.locator('[data-testid="table"]')).toBeVisible();
|
||||
|
||||
const headers = await page
|
||||
.locator('.ant-table-thead > tr > .ant-table-cell')
|
||||
.allTextContents();
|
||||
|
||||
expect(headers).toEqual(['Tag', 'Display Name', 'Description', 'Actions']);
|
||||
});
|
||||
|
||||
await test.step('Create classification with validation checks', async () => {
|
||||
await page.click('[data-testid="add-classification"]');
|
||||
await page.waitForSelector('.ant-modal-content', {
|
||||
state: 'visible',
|
||||
});
|
||||
|
||||
await expect(page.locator('.ant-modal-content')).toBeVisible();
|
||||
|
||||
await validateForm(page);
|
||||
|
||||
await page.fill('[data-testid="name"]', NEW_CLASSIFICATION.name);
|
||||
await page.fill(
|
||||
'[data-testid="displayName"]',
|
||||
NEW_CLASSIFICATION.displayName
|
||||
);
|
||||
await page.fill(descriptionBox, NEW_CLASSIFICATION.description);
|
||||
await page.click('[data-testid="mutually-exclusive-button"]');
|
||||
|
||||
const createTagCategoryResponse = page.waitForResponse(
|
||||
'api/v1/classifications'
|
||||
);
|
||||
await submitForm(page);
|
||||
await createTagCategoryResponse;
|
||||
|
||||
await expect(
|
||||
page.locator('[data-testid="modal-container"]')
|
||||
).not.toBeVisible();
|
||||
await expect(
|
||||
page.locator('[data-testid="data-summary-container"]')
|
||||
).toContainText(NEW_CLASSIFICATION.displayName);
|
||||
});
|
||||
|
||||
await test.step('Create tag with validation checks', async () => {
|
||||
await page.click(`text=${NEW_CLASSIFICATION.displayName}`);
|
||||
|
||||
await expect(page.locator('.activeCategory')).toContainText(
|
||||
NEW_CLASSIFICATION.displayName
|
||||
);
|
||||
|
||||
await page.click('[data-testid="add-new-tag-button"]');
|
||||
|
||||
await page.waitForSelector('.ant-modal-content', {
|
||||
state: 'visible',
|
||||
});
|
||||
|
||||
await expect(page.locator('.ant-modal-content')).toBeVisible();
|
||||
|
||||
await validateForm(page);
|
||||
|
||||
await page.fill('[data-testid="name"]', NEW_TAG.name);
|
||||
await page.fill('[data-testid="displayName"]', NEW_TAG.displayName);
|
||||
await page.fill(descriptionBox, NEW_TAG.description);
|
||||
await page.fill('[data-testid="icon-url"]', NEW_TAG.icon);
|
||||
await page.fill('[data-testid="tags_color-color-input"]', NEW_TAG.color);
|
||||
|
||||
const createTagResponse = page.waitForResponse('api/v1/tags');
|
||||
await submitForm(page);
|
||||
await createTagResponse;
|
||||
|
||||
await expect(page.locator('[data-testid="table"]')).toContainText(
|
||||
NEW_TAG.name
|
||||
);
|
||||
});
|
||||
|
||||
await test.step(`Assign tag to table`, async () => {
|
||||
await table.visitEntityPage(page);
|
||||
const { name, displayName } = NEW_TAG;
|
||||
|
||||
await page.click(
|
||||
'[data-testid="classification-tags-0"] [data-testid="entity-tags"] [data-testid="add-tag"]'
|
||||
);
|
||||
await page.fill('[data-testid="tag-selector"] input', name);
|
||||
await page.click(`[data-testid="tag-${tagFqn}"]`);
|
||||
|
||||
await expect(
|
||||
page.locator('[data-testid="tag-selector"] > .ant-select-selector')
|
||||
).toContainText(displayName);
|
||||
|
||||
const saveAssociatedTag = page.waitForResponse(
|
||||
(response) =>
|
||||
response.request().method() === 'PATCH' &&
|
||||
response
|
||||
.url()
|
||||
.includes(`/api/v1/tables/${table.entityResponseData?.['id']}`)
|
||||
);
|
||||
await page.click('[data-testid="saveAssociatedTag"]');
|
||||
await saveAssociatedTag;
|
||||
|
||||
await page.waitForSelector('.ant-select-dropdown', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
await expect(
|
||||
page
|
||||
.getByRole('row', { name: 'user_id numeric Unique' })
|
||||
.getByTestId('tags-container')
|
||||
).toContainText(displayName);
|
||||
|
||||
await expect(
|
||||
page.locator(
|
||||
'[data-testid="classification-tags-0"] [data-testid="tags-container"] [data-testid="icon"]'
|
||||
)
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step(
|
||||
'Assign tag using Task & Suggestion flow to DatabaseSchema',
|
||||
async () => {
|
||||
const entity = table.schema;
|
||||
const tag = 'Personal';
|
||||
const assignee = 'admin';
|
||||
|
||||
const databaseSchemaPage = page.waitForResponse(
|
||||
'api/v1/databaseSchemas/name/*'
|
||||
);
|
||||
const permissions = page.waitForResponse(
|
||||
'api/v1/permissions/databaseSchema/name/*'
|
||||
);
|
||||
await page.click(
|
||||
`[data-testid="breadcrumb-link"]:has-text("${entity.name}")`
|
||||
);
|
||||
|
||||
await databaseSchemaPage;
|
||||
await permissions;
|
||||
|
||||
await page.click('[data-testid="request-entity-tags"]');
|
||||
|
||||
await page.click('[data-testid="select-assignee"]');
|
||||
const assigneeResponse = page.waitForResponse(
|
||||
'/api/v1/search/suggest?q=*&index=user_search_index*team_search_index*'
|
||||
);
|
||||
await page.keyboard.type(assignee);
|
||||
await page.click(`[data-testid="${assignee}"]`);
|
||||
await assigneeResponse;
|
||||
|
||||
await clickOutside(page);
|
||||
|
||||
const suggestTag = page.waitForResponse(
|
||||
'api/v1/search/query?q=*%20AND%20disabled:false&index=tag_search_index*'
|
||||
);
|
||||
await page.click('[data-testid="tag-selector"]');
|
||||
await page.keyboard.type(tag);
|
||||
await suggestTag;
|
||||
await page.click('[data-testid="tag-PersonalData.Personal"]');
|
||||
|
||||
await page.click('[data-testid="tags-label"]');
|
||||
const taskCreated = page.waitForResponse(
|
||||
(response) =>
|
||||
response.request().method() === 'POST' &&
|
||||
response.url().includes('api/v1/feed')
|
||||
);
|
||||
await page.click('[data-testid="submit-tag-request"]');
|
||||
await taskCreated;
|
||||
|
||||
const acceptSuggestion = page.waitForResponse(
|
||||
(response) =>
|
||||
response.request().method() === 'PUT' &&
|
||||
response.url().includes('/api/v1/feed/tasks/') &&
|
||||
response.url().includes('/resolve')
|
||||
);
|
||||
await page.click(
|
||||
'.ant-btn-compact-first-item:has-text("Accept Suggestion")'
|
||||
);
|
||||
await acceptSuggestion;
|
||||
await page.click('[data-testid="table"]');
|
||||
|
||||
const databaseSchemasPage = page.waitForResponse(
|
||||
'api/v1/databaseSchemas/name/*'
|
||||
);
|
||||
await page.reload();
|
||||
await databaseSchemasPage;
|
||||
|
||||
await expect(
|
||||
page.locator('[data-testid="tags-container"]')
|
||||
).toContainText(tag);
|
||||
|
||||
await page.click('[data-testid="edit-button"]');
|
||||
|
||||
await page.click('[data-testid="remove-tags"]');
|
||||
|
||||
const removeTags = page.waitForResponse(
|
||||
(response) =>
|
||||
response.request().method() === 'PATCH' &&
|
||||
response.url().includes('/api/v1/databaseSchemas/')
|
||||
);
|
||||
await page.click('[data-testid="saveAssociatedTag"]');
|
||||
await removeTags;
|
||||
}
|
||||
);
|
||||
|
||||
await test.step(
|
||||
'Should have correct tag usage count and redirection should work',
|
||||
async () => {
|
||||
const getTags = page.waitForResponse('/api/v1/tags*');
|
||||
await sidebarClick(page, SidebarItem.TAGS);
|
||||
await getTags;
|
||||
await page
|
||||
.locator(`[data-testid="side-panel-classification"]`)
|
||||
.filter({ hasText: NEW_CLASSIFICATION.displayName })
|
||||
.click();
|
||||
|
||||
await expect(page.locator('.activeCategory')).toContainText(
|
||||
NEW_CLASSIFICATION.displayName
|
||||
);
|
||||
|
||||
const count = await page
|
||||
.locator('[data-testid="usage-count"]')
|
||||
.textContent();
|
||||
|
||||
expect(count).toBe('1');
|
||||
|
||||
const getEntityDetailsPage = page.waitForResponse(
|
||||
'api/v1/search/query?q=&index=**'
|
||||
);
|
||||
await page.click('[data-testid="usage-count"]');
|
||||
await getEntityDetailsPage;
|
||||
}
|
||||
);
|
||||
|
||||
await test.step('Delete tag', async () => {
|
||||
const getTags = page.waitForResponse('/api/v1/tags*');
|
||||
await sidebarClick(page, SidebarItem.TAGS);
|
||||
await getTags;
|
||||
await page
|
||||
.locator(`[data-testid="side-panel-classification"]`)
|
||||
.filter({ hasText: NEW_CLASSIFICATION.displayName })
|
||||
.click();
|
||||
|
||||
await expect(page.locator('.activeCategory')).toContainText(
|
||||
NEW_CLASSIFICATION.displayName
|
||||
);
|
||||
|
||||
await expect(page.locator('[data-testid="table"]')).toContainText(
|
||||
NEW_TAG.name
|
||||
);
|
||||
|
||||
await page.click('[data-testid="table"] [data-testid="delete-tag"]');
|
||||
await page.waitForTimeout(500); // adding manual wait to open modal, as it depends on click not an api.
|
||||
const deleteTag = page.waitForResponse(
|
||||
(response) =>
|
||||
response.request().method() === 'DELETE' &&
|
||||
response.url().includes('/api/v1/tags/')
|
||||
);
|
||||
await permanentDeleteModal(page, NEW_TAG.name);
|
||||
await deleteTag;
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await expect(page.locator('[data-testid="table"]')).not.toContainText(
|
||||
NEW_TAG.name
|
||||
);
|
||||
});
|
||||
|
||||
await test.step('Remove classification', async () => {
|
||||
await expect(page.getByTestId('entity-header-display-name')).toContainText(
|
||||
NEW_CLASSIFICATION.displayName
|
||||
);
|
||||
|
||||
await page.click('[data-testid="manage-button"]');
|
||||
|
||||
await page.click('[data-testid="delete-button"]');
|
||||
|
||||
await page.click('[data-testid="hard-delete-option"]');
|
||||
await page.fill('[data-testid="confirmation-text-input"]', 'DELETE');
|
||||
|
||||
const deleteClassification = page.waitForResponse(
|
||||
(response) =>
|
||||
response.request().method() === 'DELETE' &&
|
||||
response.url().includes('/api/v1/classifications/')
|
||||
);
|
||||
await page.click('[data-testid="confirm-button"]');
|
||||
await deleteClassification;
|
||||
|
||||
await expect(
|
||||
page
|
||||
.locator('[data-testid="data-summary-container"]')
|
||||
.filter({ hasText: NEW_CLASSIFICATION.name })
|
||||
).not.toBeVisible();
|
||||
});
|
||||
});
|
@ -10,11 +10,21 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Page } from '@playwright/test';
|
||||
import { expect, Page } from '@playwright/test';
|
||||
import { SidebarItem } from '../constant/sidebar';
|
||||
import { redirectToHomePage } from './common';
|
||||
import {
|
||||
NAME_MIN_MAX_LENGTH_VALIDATION_ERROR,
|
||||
NAME_VALIDATION_ERROR,
|
||||
redirectToHomePage,
|
||||
} from './common';
|
||||
import { sidebarClick } from './sidebar';
|
||||
|
||||
export const TAG_INVALID_NAMES = {
|
||||
MIN_LENGTH: 'c',
|
||||
MAX_LENGTH: 'a87439625b1c2d3e4f5061728394a5b6c7d8e90a1b2c3d4e5f67890ab',
|
||||
WITH_SPECIAL_CHARS: '!@#$%^&*()',
|
||||
};
|
||||
|
||||
export const visitClassificationPage = async (
|
||||
page: Page,
|
||||
classificationName: string
|
||||
@ -27,3 +37,53 @@ export const visitClassificationPage = async (
|
||||
await classificationResponse;
|
||||
await page.getByRole('menuitem', { name: classificationName }).click();
|
||||
};
|
||||
|
||||
export async function submitForm(page: Page) {
|
||||
await page.locator('button[type="submit"]').scrollIntoViewIfNeeded();
|
||||
await page.locator('button[type="submit"]').click();
|
||||
}
|
||||
|
||||
export async function validateForm(page: Page) {
|
||||
// submit form without any data to trigger validation
|
||||
await submitForm(page);
|
||||
|
||||
// error messages
|
||||
await expect(page.locator('#tags_name_help')).toBeVisible();
|
||||
await expect(page.locator('#tags_name_help')).toContainText(
|
||||
'Name is required'
|
||||
);
|
||||
|
||||
await expect(page.locator('#tags_description_help')).toBeVisible();
|
||||
await expect(page.locator('#tags_description_help')).toContainText(
|
||||
'Description is required'
|
||||
);
|
||||
|
||||
// validation should work for invalid names
|
||||
|
||||
// min length validation
|
||||
await page.locator('[data-testid="name"]').scrollIntoViewIfNeeded();
|
||||
await page.locator('[data-testid="name"]').clear();
|
||||
await page.locator('[data-testid="name"]').fill(TAG_INVALID_NAMES.MIN_LENGTH);
|
||||
|
||||
await expect(page.locator('#tags_name_help')).toContainText(
|
||||
NAME_MIN_MAX_LENGTH_VALIDATION_ERROR
|
||||
);
|
||||
|
||||
// max length validation
|
||||
await page.locator('[data-testid="name"]').clear();
|
||||
await page.locator('[data-testid="name"]').fill(TAG_INVALID_NAMES.MAX_LENGTH);
|
||||
|
||||
await expect(page.locator('#tags_name_help')).toContainText(
|
||||
NAME_MIN_MAX_LENGTH_VALIDATION_ERROR
|
||||
);
|
||||
|
||||
// with special char validation
|
||||
await page.locator('[data-testid="name"]').clear();
|
||||
await page
|
||||
.locator('[data-testid="name"]')
|
||||
.fill(TAG_INVALID_NAMES.WITH_SPECIAL_CHARS);
|
||||
|
||||
await expect(page.locator('#tags_name_help')).toContainText(
|
||||
NAME_VALIDATION_ERROR
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user