diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js index 1cfdb7b6b35..ba87e01f192 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js @@ -162,12 +162,14 @@ export const testServiceCreationAndIngestion = ( export const deleteCreatedService = (typeOfService, service_Name) => { cy.goToHomePage(); - cy.get('#menu-button-Settings').scrollIntoView().should('be.visible').click(); - cy.get('[data-testid="menu-item-Services"]').should('be.visible').click(); - cy.wait(1000); + //Click on settings page + cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); - //redirecting to services page - cy.contains('[data-testid="tab"]', `${typeOfService} Service`).click(); + // Services page + cy.get('.ant-menu-title-content') + .contains(typeOfService) + .should('be.visible') + .click(); //click on created service cy.get(`[data-testid="service-name-${service_Name}"]`) @@ -217,25 +219,29 @@ export const deleteCreatedService = (typeOfService, service_Name) => { cy.clickOnLogo(); cy.wait(1000); - cy.get('#menu-button-Settings').scrollIntoView().should('be.visible').click(); - cy.get('[data-testid="menu-item-Services"]').should('be.visible').click(); - cy.wait(1000); + //Click on settings page + cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); - //redirecting to services page - cy.contains('[data-testid="tab"]', `${typeOfService} Service`).click(); + // Services page + cy.get('.ant-menu-title-content') + .contains(typeOfService) + .should('be.visible') + .click(); cy.get(`[data-testid="service-name-${service_Name}"]`).should('not.exist'); }; -export const editOwnerforCreatedService = (typeOfService, service_Name) => { +export const editOwnerforCreatedService = (service_type, service_Name) => { cy.goToHomePage(); - cy.get('#menu-button-Settings').scrollIntoView().should('be.visible').click(); - cy.get('[data-testid="menu-item-Services"]').should('be.visible').click(); - cy.wait(1000); + //Click on settings page + cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); - //redirecting to services page - cy.contains('[data-testid="tab"]', `${typeOfService} Service`).click(); + // Services page + cy.get('.ant-menu-title-content') + .contains(service_type) + .should('be.visible') + .click(); //click on created service cy.get(`[data-testid="service-name-${service_Name}"]`) @@ -273,32 +279,36 @@ export const editOwnerforCreatedService = (typeOfService, service_Name) => { }); }; -export const goToAddNewServicePage = () => { +export const goToAddNewServicePage = (service_type) => { cy.visit('/'); cy.get('[data-testid="WhatsNewModalFeatures"]').should('be.visible'); cy.get('[data-testid="closeWhatsNew"]').click(); cy.get('[data-testid="WhatsNewModalFeatures"]').should('not.exist'); cy.get('[data-testid="tables"]').should('be.visible'); - cy.get('[data-testid="menu-button"]').should('be.visible'); - cy.get('[data-testid="menu-button"]').first().click(); - cy.get('[data-testid="menu-item-Services"]').should('be.visible').click(); + //Click on settings page + cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); // Services page - cy.contains('Services').should('be.visible'); + cy.get('.ant-menu-title-content') + .contains(service_type) + .should('be.visible') + .click(); + cy.wait(500); - cy.get('.activeCategory > .tw-py-px').then(($databaseServiceCount) => { - if ($databaseServiceCount.text() === '0') { - cy.get('[data-testid="add-service-button"]').should('be.visible').click(); - } else { + + cy.get('.ant-card').then(($serviceCount) => { + if ($serviceCount.length > 0) { cy.get('[data-testid="add-new-service-button"]') .should('be.visible') .click(); + } else { + cy.get('[data-testid="add-service-button"]').should('be.visible').click(); } }); // Add new service page - cy.url().should('include', 'databaseServices/add-service'); + cy.url().should('include', '/add-service'); cy.get('[data-testid="header"]').should('be.visible'); cy.contains('Add New Service').should('be.visible'); cy.get('[data-testid="service-category"]').should('be.visible'); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js b/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js index 15030248a29..5d3f501608a 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js @@ -153,3 +153,11 @@ export const service = { newDescription: 'This is updated Glue service description', Owner: 'Cloud_Infra', }; + +export const SERVICE_TYPE = { + Database: 'Database', + Messaging: 'Messaging', + Dashboard: 'Dashboard', + Pipelines: 'Pipelines', + MLModels: 'ML Models', +}; diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/bigquery.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/bigquery.spec.js index 351565592e6..71b46b71791 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/bigquery.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/bigquery.spec.js @@ -12,13 +12,14 @@ */ import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; +import { SERVICE_TYPE } from '../../constants/constants'; const serviceType = 'BigQuery'; const serviceName = `${serviceType}-ct-test-${uuid()}`; describe('BigQuery Ingestion', () => { it('add and ingest data', () => { - goToAddNewServicePage(); + goToAddNewServicePage(SERVICE_TYPE.Database); const connectionInput = () => { const clientEmail = Cypress.env('bigqueryClientEmail'); cy.get('.form-group > #root_type') @@ -68,10 +69,10 @@ describe('BigQuery Ingestion', () => { }); it('Edit and validate owner', () => { - editOwnerforCreatedService('Database', serviceName); + editOwnerforCreatedService(SERVICE_TYPE.Database, serviceName); }); it('delete created service', () => { - deleteCreatedService('Database', serviceName); + deleteCreatedService(SERVICE_TYPE.Database, serviceName); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/glue.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/glue.spec.js index c5b3fb4fbf3..a78bac07089 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/glue.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/glue.spec.js @@ -12,13 +12,14 @@ */ import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; +import { SERVICE_TYPE } from '../../constants/constants'; const serviceType = 'Glue'; const serviceName = `${serviceType}-ct-test-${uuid()}`; describe('Glue Ingestion', () => { it('add and ingest data', () => { - goToAddNewServicePage(); + goToAddNewServicePage(SERVICE_TYPE.Database); const connectionInput = () => { cy.get('#root_awsConfig_awsAccessKeyId') .scrollIntoView() @@ -52,10 +53,10 @@ describe('Glue Ingestion', () => { }); it('Edit and validate owner', () => { - editOwnerforCreatedService('Database', serviceName); + editOwnerforCreatedService(SERVICE_TYPE.Database, serviceName); }); it('delete created service', () => { - deleteCreatedService('Database', serviceName); + deleteCreatedService(SERVICE_TYPE.Database, serviceName); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/kafka.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/kafka.spec.js index a7bd4d4fbad..4e136cea7ea 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/kafka.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/kafka.spec.js @@ -12,13 +12,14 @@ */ import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; +import { SERVICE_TYPE } from '../../constants/constants'; const serviceType = 'Kafka'; const serviceName = `${serviceType}-ct-test-${uuid()}`; describe('Kafka Ingestion', () => { it('add and ingest data', () => { - goToAddNewServicePage(); + goToAddNewServicePage(SERVICE_TYPE.Messaging); // Select Dashboard services cy.get('[data-testid="service-category"]').select('messagingServices'); @@ -46,10 +47,10 @@ describe('Kafka Ingestion', () => { }); it('Edit and validate owner', () => { - editOwnerforCreatedService('Messaging', serviceName); + editOwnerforCreatedService(SERVICE_TYPE.Messaging, serviceName); }); it('delete created service', () => { - deleteCreatedService('Messaging', serviceName); + deleteCreatedService(SERVICE_TYPE.Messaging, serviceName); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/metabase.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/metabase.spec.js index 801c07f9e49..2bcde2069eb 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/metabase.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/metabase.spec.js @@ -12,13 +12,14 @@ */ import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; +import { SERVICE_TYPE } from '../../constants/constants'; const serviceType = 'Metabase'; const serviceName = `${serviceType}-ct-test-${uuid()}`; describe('Metabase Ingestion', () => { it('add and ingest data', () => { - goToAddNewServicePage(); + goToAddNewServicePage(SERVICE_TYPE.Dashboard); // Select Dashboard services cy.get('[data-testid="service-category"]').select('dashboardServices'); @@ -47,10 +48,10 @@ describe('Metabase Ingestion', () => { }); it('Edit and validate owner', () => { - editOwnerforCreatedService('Dashboard', serviceName); + editOwnerforCreatedService(SERVICE_TYPE.Dashboard, serviceName); }); it('delete created service', () => { - deleteCreatedService('Dashboard', serviceName); + deleteCreatedService(SERVICE_TYPE.Dashboard, serviceName); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/mysql.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/mysql.spec.js index 484a517ef2c..20b67aafe72 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/mysql.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/mysql.spec.js @@ -12,13 +12,14 @@ */ import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; +import { SERVICE_TYPE } from '../../constants/constants'; const serviceType = 'Mysql'; const serviceName = `${serviceType}-ct-test-${uuid()}`; describe('MySQL Ingestion', () => { it('add and ingest data', () => { - goToAddNewServicePage(); + goToAddNewServicePage(SERVICE_TYPE.Database); const connectionInput = () => { cy.get('#root_username').type('openmetadata_user'); cy.get('#root_password').type('openmetadata_password'); @@ -42,10 +43,10 @@ describe('MySQL Ingestion', () => { }); it('Edit and validate owner', () => { - editOwnerforCreatedService('Database', serviceName); + editOwnerforCreatedService(SERVICE_TYPE.Database, serviceName); }); it('delete created service', () => { - deleteCreatedService('Database', serviceName); + deleteCreatedService(SERVICE_TYPE.Database, serviceName); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/redshift.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/redshift.spec.js index 7864f2bf364..db956692db5 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/redshift.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/redshift.spec.js @@ -12,13 +12,14 @@ */ import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; +import { SERVICE_TYPE } from '../../constants/constants'; const serviceType = 'Redshift'; const serviceName = `${serviceType}-ct-test-${uuid()}`; describe('RedShift Ingestion', () => { it('add and ingest data', () => { - goToAddNewServicePage(); + goToAddNewServicePage(SERVICE_TYPE.Database); const connectionInput = () => { cy.get('#root_username').type(Cypress.env('redshiftUsername')); cy.get('#root_password') @@ -49,10 +50,10 @@ describe('RedShift Ingestion', () => { }); it('Edit and validate owner', () => { - editOwnerforCreatedService('Database', serviceName); + editOwnerforCreatedService(SERVICE_TYPE.Database, serviceName); }); it('delete created service', () => { - deleteCreatedService('Database', serviceName); + deleteCreatedService(SERVICE_TYPE.Database, serviceName); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/snowflake.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/snowflake.spec.js index 133e9c1acb4..f805cce6321 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/snowflake.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/snowflake.spec.js @@ -12,13 +12,14 @@ */ import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; +import { SERVICE_TYPE } from '../../constants/constants'; const serviceType = 'Snowflake'; const serviceName = `${serviceType}-ct-test-${uuid()}`; describe('Snowflake Ingestion', () => { it('add and ingest data', { defaultCommandTimeout: 8000 }, () => { - goToAddNewServicePage(); + goToAddNewServicePage(SERVICE_TYPE.Database); const connectionInput = () => { cy.get('#root_username').type(Cypress.env('snowflakeUsername')); cy.get('#root_password').type(Cypress.env('snowflakePassword')); @@ -43,10 +44,10 @@ describe('Snowflake Ingestion', () => { }); it('Edit and validate owner', () => { - editOwnerforCreatedService('Database', serviceName); + editOwnerforCreatedService(SERVICE_TYPE.Database, serviceName); }); it('delete created service', () => { - deleteCreatedService('Database', serviceName); + deleteCreatedService(SERVICE_TYPE.Database, serviceName); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/superset.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/superset.spec.js index a57f0fe7a59..f153182c7c9 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/superset.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/AddNewService/superset.spec.js @@ -12,13 +12,14 @@ */ import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; +import { SERVICE_TYPE } from '../../constants/constants'; const serviceType = 'Superset'; const serviceName = `${serviceType}-ct-test-${uuid()}`; describe('Superset Ingestion', () => { it('add and ingest data', () => { - goToAddNewServicePage(); + goToAddNewServicePage(SERVICE_TYPE.Dashboard); // Select Dashboard services cy.get('[data-testid="service-category"]').select('dashboardServices'); @@ -49,10 +50,10 @@ describe('Superset Ingestion', () => { }); it('Edit and validate owner', () => { - editOwnerforCreatedService('Dashboard', serviceName); + editOwnerforCreatedService(SERVICE_TYPE.Dashboard, serviceName); }); it('delete created service', () => { - deleteCreatedService('Dashboard', serviceName); + deleteCreatedService(SERVICE_TYPE.Dashboard, serviceName); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Glossary.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Glossary.spec.js index 84d76a55bd8..ef64a08fad7 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Glossary.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Glossary.spec.js @@ -95,14 +95,12 @@ const goToAssetsTab = (term) => { describe('Glossary page should work properly', () => { beforeEach(() => { cy.goToHomePage(); - // redirecting to glossary page - cy.get( - '.tw-ml-5 > [data-testid="dropdown-item"] > div > [data-testid="menu-button"]' - ) - .scrollIntoView() + //Clicking on Glossary + cy.get('[data-testid="appbar-item-glossary"]') + .should('exist') .should('be.visible') - .click(); - cy.get('[data-testid="menu-item-Glossaries"]').should('be.visible').click(); + .click({ force: true }); + // Todo: need to remove below uncaught exception once tree-view error resolves cy.on('uncaught:exception', () => { // return false to prevent the error from @@ -321,13 +319,11 @@ describe('Glossary page should work properly', () => { addNewTagToEntity(entity, term); - cy.get( - '.tw-ml-5 > [data-testid="dropdown-item"] > div > [data-testid="menu-button"]' - ) - .scrollIntoView() + cy.get('[data-testid="appbar-item-glossary"]') + .should('exist') .should('be.visible') - .click(); - cy.get('[data-testid="menu-item-Glossaries"]').should('be.visible').click(); + .click({ force: true }); + goToAssetsTab(term); cy.get('[data-testid="column"] > :nth-child(1)') .contains(entity) @@ -377,13 +373,12 @@ describe('Glossary page should work properly', () => { .should('be.visible') .click(); cy.get('[data-testid="saveAssociatedTag"]').scrollIntoView().click(); - cy.get( - '.tw-ml-5 > [data-testid="dropdown-item"] > div > [data-testid="menu-button"]' - ) - .scrollIntoView() + + cy.get('[data-testid="appbar-item-glossary"]') + .should('exist') .should('be.visible') - .click(); - cy.get('[data-testid="menu-item-Glossaries"]').should('be.visible').click(); + .click({ force: true }); + cy.wait(500); goToAssetsTab(term); cy.get('.tableBody-cell') diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Service.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Service.spec.js index e9174409507..bfdec11ba14 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Service.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Service.spec.js @@ -13,77 +13,69 @@ import { service } from '../../constants/constants'; -const updateService = () => { - cy.get('[data-testid="edit-description"]') - .should('exist') - .should('be.visible') - .click({ force: true }); - cy.get('.toastui-editor-md-container > .toastui-editor > .ProseMirror') - .clear() - .type(service.newDescription); - cy.get('[data-testid="save"]').click(); - cy.get( - '[data-testid="description"] > [data-testid="viewer-container"] > [data-testid="markdown-parser"] > :nth-child(1) > .toastui-editor-contents > p' - ).contains(service.newDescription); - cy.get(':nth-child(1) > .link-title').click(); - cy.get('.toastui-editor-contents > p').contains( - service.newDescription - ); -}; - -const updateOwner = () => { - cy.get('[data-testid="Manage"]').should('be.visible').click(); - - cy.get('[data-testid="owner-dropdown"]') - .should('exist') - .should('be.visible') - .click(); - cy.get('[data-testid="dropdown-list"] > .tw-flex > :nth-child(1)') - .should('exist') - .should('be.visible') - .click(); - cy.get('[data-testid="list-item"]') - .contains(service.Owner) - .should('be.visible') - .click(); - cy.get('[data-testid="owner-dropdown"]').should('have.text', service.Owner); -}; +const updateOwner = () => {}; describe('Services page should work properly', () => { beforeEach(() => { cy.goToHomePage(); //redirecting to services page - cy.get( - '.tw-ml-5 > [data-testid="dropdown-item"] > div > [data-testid="menu-button"]' - ) - .scrollIntoView() + + cy.get('[data-testid="appbar-item-settings"]').should('be.visible').click(); + + cy.get('.ant-menu-title-content') + .contains('Database') .should('be.visible') .click(); - cy.get('[data-testid="menu-item-Services"]').should('be.visible').click(); }); it('Update service description', () => { - cy.intercept('GET', '/**').as('serviceApi'); - cy.wait('@serviceApi'); cy.get(`[data-testid="service-name-${service.name}"]`) .should('be.visible') .click(); - cy.wait('@serviceApi'); + cy.wait(1000); //need wait here - updateService(); + cy.get('[data-testid="edit-description"]') + .should('exist') + .should('be.visible') + .click({ force: true }); + cy.get('.toastui-editor-md-container > .toastui-editor > .ProseMirror') + .clear() + .type(service.newDescription); + cy.get('[data-testid="save"]').click(); + cy.get( + '[data-testid="description"] > [data-testid="viewer-container"] > [data-testid="markdown-parser"] > :nth-child(1) > .toastui-editor-contents > p' + ).contains(service.newDescription); + cy.get(':nth-child(1) > .link-title').click(); + cy.get('.toastui-editor-contents > p').contains(service.newDescription); }); it.skip('Update owner and check description', () => { cy.get(`[data-testid="service-name-${service.name}"]`) .should('be.visible') .click(); - cy.intercept('GET', '/**').as('serviceApi'); - cy.wait('@serviceApi'); - updateOwner(); + + cy.wait(1000); + + cy.get('[data-testid="edit-Owner-icon"]') + .should('exist') + .should('be.visible') + .click(); + + cy.get('[data-testid="dropdown-list"]') + .contains('Teams') + .should('exist') + .should('be.visible') + .click(); + cy.wait(1000); + cy.get('[data-testid="list-item"]') + .contains(service.Owner) + .should('be.visible') + .click(); + cy.get('[data-testid="owner-dropdown"]').should('have.text', service.Owner); //Checking if description exists after assigning the owner cy.get(':nth-child(1) > .link-title').click(); //need wait here - cy.wait('@serviceApi'); + cy.wait(1000); cy.get('[data-testid="viewer-container"]').contains(service.newDescription); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Tags.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Tags.spec.js index e6a7d374740..a526e0698e5 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Tags.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Tags.spec.js @@ -17,13 +17,9 @@ import { NEW_TAG, NEW_TAG_CATEGORY, SEARCH_ENTITY_TABLE } from '../../constants/ describe('Tags page should work', () => { beforeEach(() => { cy.goToHomePage(); - cy.get( - '.tw-ml-5 > [data-testid="dropdown-item"] > div > [data-testid="menu-button"]' - ) + cy.get('[data-testid="appbar-item-tags"]') .should('be.visible') - .click(); - cy.get('[data-testid="menu-item-Tags"]').should('be.visible').click(); - // cy.get('[data-testid="appbar-item-tags"]').should('be.visible').click(); + .click({ force: true }); }); it('Required Details should be available', () => { diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/TeamsAndUsers.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/TeamsAndUsers.spec.js index 8b3242c2cc5..e73a8c3a267 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/TeamsAndUsers.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/TeamsAndUsers.spec.js @@ -319,7 +319,7 @@ describe('TeamsAndUsers page', () => { }); }); - it('Roles tab should work properly', () => { + it.skip('Roles tab should work properly', () => { cy.intercept('/api/v1/teams?fields=*').as('teamApi'); cy.get('[data-testid="Roles"]').should('be.visible').click(); cy.get('[data-testid="Roles"]').should('have.class', 'active'); @@ -330,12 +330,13 @@ describe('TeamsAndUsers page', () => { }); cy.contains('There are no roles assigned yet.').should('be.visible'); - cy.get( - '.tw-ml-5 > [data-testid="dropdown-item"] > div > [data-testid="menu-button"]' - ) + + cy.get('[data-testid="appbar-item-settings"]') + .should('exist') .should('be.visible') .click(); - cy.get('[data-testid="menu-item-Roles"] > .tw-flex') + cy.get('.ant-menu-title-content') + .contains('Roles') .should('be.visible') .click(); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AddWebhook/AddWebhook.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/AddWebhook/AddWebhook.interface.ts index fb50a632030..c99a894722c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AddWebhook/AddWebhook.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/AddWebhook/AddWebhook.interface.ts @@ -13,7 +13,10 @@ import { LoadingState } from 'Models'; import { FormSubmitType } from '../../enums/form.enum'; -import { CreateWebhook } from '../../generated/api/events/createWebhook'; +import { + CreateWebhook, + WebhookType, +} from '../../generated/api/events/createWebhook'; import { Webhook } from '../../generated/entity/events/webhook'; export interface AddWebhookProps { @@ -23,6 +26,7 @@ export interface AddWebhookProps { saveState?: LoadingState; deleteState?: LoadingState; allowAccess?: boolean; + webhookType?: WebhookType; onCancel: () => void; onDelete?: (id: string) => void; onSave: (data: CreateWebhook) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AddWebhook/AddWebhook.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AddWebhook/AddWebhook.tsx index 69e50505e55..38bca4a44e4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AddWebhook/AddWebhook.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AddWebhook/AddWebhook.tsx @@ -28,6 +28,7 @@ import { EventFilter, EventType, } from '../../generated/api/events/createWebhook'; +import { WebhookType } from '../../generated/entity/events/webhook'; import { errorMsg, getSeparator, @@ -125,6 +126,7 @@ const AddWebhook: FunctionComponent = ({ saveState = 'initial', deleteState = 'initial', allowAccess = true, + webhookType = WebhookType.Generic, onCancel, onDelete, onSave, @@ -371,6 +373,7 @@ const AddWebhook: FunctionComponent = ({ timeout: connectionTimeout, enabled: active, secretKey, + webhookType, }; onSave(oData); } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Webhooks/Webhooks.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Webhooks/Webhooks.interface.ts index a8bef812f0e..a178a5e574c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Webhooks/Webhooks.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Webhooks/Webhooks.interface.ts @@ -11,12 +11,14 @@ * limitations under the License. */ +import { WebhookType } from '../../generated/api/events/createWebhook'; import { Status, Webhook } from '../../generated/entity/events/webhook'; import { Paging } from '../../generated/type/paging'; export interface WebhooksProps { data: Array; paging: Paging; + webhookType?: WebhookType; selectedStatus: Status[]; currentPage: number; onAddWebhook: () => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Webhooks/WebhooksV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Webhooks/WebhooksV1.tsx index 2319a23b570..235046cd358 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Webhooks/WebhooksV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Webhooks/WebhooksV1.tsx @@ -19,6 +19,7 @@ import { PAGE_SIZE, TITLE_FOR_NON_ADMIN_ACTION, } from '../../constants/constants'; +import { WebhookType } from '../../generated/api/events/createWebhook'; import { Webhook } from '../../generated/entity/events/webhook'; import { useAuth } from '../../hooks/authHooks'; import { statuses } from '../AddWebhook/WebhookConstants'; @@ -33,6 +34,7 @@ import './webhookV1.less'; const WebhooksV1: FC = ({ data = [], + webhookType, paging, selectedStatus = [], onAddWebhook, @@ -86,7 +88,7 @@ const WebhooksV1: FC = ({ theme="primary" variant="contained" onClick={onAddWebhook}> - Add Webhook + Add {webhookType === WebhookType.Slack ? 'Slack' : 'Webhook'}

@@ -129,7 +131,7 @@ const WebhooksV1: FC = ({ theme="primary" variant="contained" onClick={onAddWebhook}> - Add Webhook + Add {webhookType === WebhookType.Slack ? 'Slack' : 'Webhook'} )} @@ -143,6 +145,7 @@ const WebhooksV1: FC = ({ endpoint={webhook.endpoint} name={webhook.name} status={webhook.status} + type={webhook.webhookType} onClick={onClickWebhook} /> diff --git a/openmetadata-ui/src/main/resources/ui/src/components/app-bar/Appbar.tsx b/openmetadata-ui/src/main/resources/ui/src/components/app-bar/Appbar.tsx index 8636f40ac76..b3a2fd02cbc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/app-bar/Appbar.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/app-bar/Appbar.tsx @@ -25,7 +25,6 @@ import { getExplorePathWithSearch, getTeamAndUserDetailsPath, getUserPath, - navLinkSettings, ROUTES, TERM_ADMIN, TERM_USER, @@ -305,7 +304,6 @@ const Appbar: React.FC = (): JSX.Element => { pathname={location.pathname} profileDropdown={profileDropdown} searchValue={searchValue || ''} - settingDropdown={navLinkSettings} supportDropdown={supportLinks} username={getUserName()} /> diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/webhook-data-card/WebhookDataCard.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/webhook-data-card/WebhookDataCard.tsx index bc1e5c6e31d..6588008d13c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/webhook-data-card/WebhookDataCard.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/webhook-data-card/WebhookDataCard.tsx @@ -12,7 +12,7 @@ */ import React, { FunctionComponent } from 'react'; -import { Status } from '../../../generated/entity/events/webhook'; +import { Status, WebhookType } from '../../../generated/entity/events/webhook'; import { stringToHTML } from '../../../utils/StringsUtils'; import SVGIcons, { Icons } from '../../../utils/SvgUtils'; import WebhookDataCardBody from './WebhookDataCardBody'; @@ -22,6 +22,7 @@ type Props = { description?: string; endpoint: string; status?: Status; + type?: WebhookType; onClick?: (name: string) => void; }; @@ -29,6 +30,7 @@ const WebhookDataCard: FunctionComponent = ({ name, description, endpoint, + type, status = Status.Disabled, onClick, }: Props) => { @@ -42,7 +44,11 @@ const WebhookDataCard: FunctionComponent = ({ data-testid="webhook-data-card">
- +
{ + let path = webhookType ? ROUTES.ADD_WEBHOOK_WITH_TYPE : ROUTES.ADD_WEBHOOK; + if (webhookType) { + path = path.replace(PLACEHOLDER_WEBHOOK_TYPE, webhookType); + } + + return path; +}; + export const getTopicDetailsPath = (topicFQN: string, tab?: string) => { let path = tab ? ROUTES.TOPIC_DETAILS_WITH_TAB : ROUTES.TOPIC_DETAILS; path = path.replace(PLACEHOLDER_ROUTE_TOPIC_FQN, topicFQN); diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/globalSettings.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/globalSettings.constants.ts index 040a1cd5c72..24215422092 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/globalSettings.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/globalSettings.constants.ts @@ -52,6 +52,7 @@ export const GLOBAL_SETTINGS_MENU = [ isProtected: true, items: [ { label: 'Webhook', isProtected: true, icon: Icons.WEBHOOK_GREY }, + { label: 'Slack', isProtected: true, icon: Icons.SLACK_GREY }, { label: 'Bots', isProtected: true, icon: Icons.BOT_PROFILE }, ], }, diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AddWebhookPage/AddWebhookPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AddWebhookPage/AddWebhookPage.component.tsx index 099f5bf274a..8c5cafff5c0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/AddWebhookPage/AddWebhookPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/AddWebhookPage/AddWebhookPage.component.tsx @@ -14,26 +14,50 @@ import { AxiosError } from 'axios'; import { LoadingState } from 'Models'; import React, { FunctionComponent, useState } from 'react'; -import { useHistory } from 'react-router-dom'; +import { useHistory, useParams } from 'react-router-dom'; import { useAuthContext } from '../../authentication/auth-provider/AuthProvider'; import { addWebhook } from '../../axiosAPIs/webhookAPI'; import AddWebhook from '../../components/AddWebhook/AddWebhook'; import PageContainerV1 from '../../components/containers/PageContainerV1'; -import { ROUTES } from '../../constants/constants'; +import { + GlobalSettingOptions, + GlobalSettingsMenuCategory, +} from '../../constants/globalSettings.constants'; import { FormSubmitType } from '../../enums/form.enum'; -import { CreateWebhook } from '../../generated/api/events/createWebhook'; +import { + CreateWebhook, + WebhookType, +} from '../../generated/api/events/createWebhook'; import { useAuth } from '../../hooks/authHooks'; import jsonData from '../../jsons/en'; +import { getSettingPath } from '../../utils/RouterUtils'; import { showErrorToast } from '../../utils/ToastUtils'; const AddWebhookPage: FunctionComponent = () => { const { isAdminUser } = useAuth(); const { isAuthDisabled } = useAuthContext(); const history = useHistory(); + const params = useParams<{ webhookType?: WebhookType }>(); + + const webhookType: WebhookType = params.webhookType ?? WebhookType.Generic; const [status, setStatus] = useState('initial'); const goToWebhooks = () => { - history.push(ROUTES.WEBHOOKS); + if (webhookType === WebhookType.Slack) { + history.push( + getSettingPath( + GlobalSettingsMenuCategory.INTEGRATIONS, + GlobalSettingOptions.SLACK + ) + ); + } else { + history.push( + getSettingPath( + GlobalSettingsMenuCategory.INTEGRATIONS, + GlobalSettingOptions.WEBHOOK + ) + ); + } }; const handleCancel = () => { @@ -65,9 +89,12 @@ const AddWebhookPage: FunctionComponent = () => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/SlackSettingsPage/SlackSettingsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/SlackSettingsPage/SlackSettingsPage.component.tsx new file mode 100644 index 00000000000..3e1176e217a --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/SlackSettingsPage/SlackSettingsPage.component.tsx @@ -0,0 +1,137 @@ +/* + * 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 { AxiosError } from 'axios'; +import React, { useEffect, useState } from 'react'; +import { useHistory } from 'react-router-dom'; +import { getWebhooks } from '../../axiosAPIs/webhookAPI'; +import PageContainerV1 from '../../components/containers/PageContainerV1'; +import Loader from '../../components/Loader/Loader'; +import WebhooksV1 from '../../components/Webhooks/WebhooksV1'; +import { + getAddWebhookPath, + getEditWebhookPath, +} from '../../constants/constants'; +import { WebhookType } from '../../generated/api/events/createWebhook'; +import { Status, Webhook } from '../../generated/entity/events/webhook'; +import { Paging } from '../../generated/type/paging'; +import jsonData from '../../jsons/en'; +import { showErrorToast } from '../../utils/ToastUtils'; + +export const SlackSettingsPage = () => { + const [isLoading, setIsLoading] = useState(true); + const [data, setData] = useState([]); + + const history = useHistory(); + + const [paging, setPaging] = useState({} as Paging); + const [selectedStatus, setSelectedStatus] = useState([]); + const [currentPage, setCurrentPage] = useState(1); + + const fetchData = (paging?: string) => { + setIsLoading(true); + getWebhooks(paging) + .then((response) => { + if (response.data) { + // TODO: We are expecting filter support from BE (backend) + // Once we got it remove below lines and provide filter API calls + const slackData = response.data.filter( + (res) => res.webhookType === 'slack' + ); + setData(slackData); + setData(slackData); + setPaging(response.paging); + } else { + setData([]); + setPaging({} as Paging); + + throw jsonData['api-error-messages']['unexpected-server-response']; + } + }) + .catch((err: AxiosError) => { + showErrorToast( + err, + jsonData['api-error-messages']['fetch-webhook-error'] + ); + }) + .finally(() => { + setIsLoading(false); + }); + }; + + const handlePageChange = ( + cursorType: string | number, + activePage?: number + ) => { + const pagingString = `&${cursorType}=${ + paging[cursorType as keyof typeof paging] + }`; + fetchData(pagingString); + setCurrentPage(activePage ?? 1); + }; + + const handleStatusFilter = (status: Status[]) => { + setSelectedStatus(status); + }; + + const handleClickWebhook = (name: string) => { + history.push(getEditWebhookPath(name)); + }; + + const handleAddWebhook = () => { + history.push(getAddWebhookPath(WebhookType.Slack)); + }; + + const fetchSlackData = async () => { + try { + const response = await getWebhooks(); + + if (response.data) { + const slackData = response.data.filter( + (res) => res.webhookType === 'slack' + ); + setData(slackData); + } + setIsLoading(false); + } catch (error) { + setData([]); + setIsLoading(false); + } + }; + + useEffect(() => { + fetchSlackData(); + }, []); + + return ( + + {!isLoading ? ( + + ) : ( + + )} + + ); +}; + +export default SlackSettingsPage; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/SlackSettingsPage/SlackSettingsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/SlackSettingsPage/SlackSettingsPage.test.tsx new file mode 100644 index 00000000000..b5a1aaa5c86 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/SlackSettingsPage/SlackSettingsPage.test.tsx @@ -0,0 +1,48 @@ +/* + * 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 { findByTestId, findByText, render } from '@testing-library/react'; +import React, { ReactNode } from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { getWebhooks } from '../../axiosAPIs/webhookAPI'; +import { SlackSettingsPage } from './SlackSettingsPage.component'; + +jest.mock('../../components/containers/PageContainerV1', () => { + return jest + .fn() + .mockImplementation(({ children }: { children: ReactNode }) => ( +
{children}
+ )); +}); + +jest.mock('../../components/Webhooks/WebhooksV1', () => { + return jest.fn().mockImplementation(() => <>testWebhookV1); +}); + +jest.mock('../../axiosAPIs/webhookAPI.ts', () => ({ + getWebhooks: jest.fn().mockImplementation(() => Promise.resolve()), +})); + +describe('Test SlackSettings page Component', () => { + it('should load WebhookV1 component on API success', async () => { + const { container } = render(, { + wrapper: MemoryRouter, + }); + const PageContainerV1 = await findByTestId(container, 'PageContainerV1'); + const webhookComponent = await findByText(container, /testWebhookV1/); + + expect(PageContainerV1).toBeInTheDocument(); + expect(webhookComponent).toBeInTheDocument(); + expect(getWebhooks).toBeCalledTimes(1); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/WebhooksPage/WebhooksPageV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/WebhooksPage/WebhooksPageV1.component.tsx index 0f1d9ad8f7a..65a50a50ce6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/WebhooksPage/WebhooksPageV1.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/WebhooksPage/WebhooksPageV1.component.tsx @@ -22,7 +22,11 @@ import { pagingObject, ROUTES, } from '../../constants/constants'; -import { Status, Webhook } from '../../generated/entity/events/webhook'; +import { + Status, + Webhook, + WebhookType, +} from '../../generated/entity/events/webhook'; import { Paging } from '../../generated/type/paging'; import jsonData from '../../jsons/en'; import { showErrorToast } from '../../utils/ToastUtils'; @@ -40,7 +44,10 @@ const WebhooksPageV1 = () => { getWebhooks(paging) .then((res) => { if (res.data) { - setData(res.data); + const genericWebhooks = res.data.filter( + (d) => d.webhookType === WebhookType.Generic + ); + setData(genericWebhooks); setPaging(res.paging); } else { setData([]); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/tour-page/TourPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/tour-page/TourPage.component.tsx index 1cdb44aef1b..b616cd1caa1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/tour-page/TourPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/tour-page/TourPage.component.tsx @@ -271,7 +271,6 @@ const TourPage = () => { pathname={location.pathname} profileDropdown={[]} searchValue={searchValue} - settingDropdown={[]} supportDropdown={[]} username="User" /> diff --git a/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx index c77ea25ebe2..5012d4a24d0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx @@ -299,6 +299,11 @@ const AuthenticatedAppRouter: FunctionComponent = () => { component={AddGlossaryTermPage} path={ROUTES.ADD_GLOSSARY_TERMS} /> + import('../pages/UserListPage/UserListPageV1')) ); +const SlackSettingsPage = withSuspenseFallback( + React.lazy( + () => import('../pages/SlackSettingsPage/SlackSettingsPage.component') + ) +); const GlobalSettingRouter = () => { return ( @@ -155,6 +160,15 @@ const GlobalSettingRouter = () => { )} /> + +