test(e2e): organise ingestion tests (#14649)

* test(e2e): organise ingestion tests

* fix ingestion tests

* push all the service ingestions

* fix name regexp issue

* fix database spec

* aut fixes

* fix follow entity tests

* fix follow test case

* fix soft delete tests

* fix tests

* fix dashboard

* fix left side bar issue
This commit is contained in:
Chirag Madlani 2024-01-16 16:38:40 +05:30 committed by GitHub
parent 65af782433
commit ae884d54de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 1295 additions and 1109 deletions

View File

@ -29,9 +29,6 @@ export default defineConfig({
setupNodeEvents(on, config) {
return plugins(on, config);
},
specPattern: [
'cypress/new-tests/**/*.spec.{js,jsx,ts,tsx}',
'cypress/e2e/**/*.{js,jsx,ts,tsx}',
],
specPattern: ['cypress/e2e/**/*.{js,jsx,ts,tsx}'],
},
});

View File

@ -35,6 +35,10 @@ class ContainerClass extends EntityClass {
});
}
followUnfollowEntity() {
// Skiping this since not working from backend
}
// Creation
createEntity() {

View File

@ -50,6 +50,10 @@ class DatabaseClass extends EntityClass {
verifyResponseStatusCode('@fetchDatabase', 200);
}
followUnfollowEntity() {
// Skiping this since not supported for database
}
// Creation
createEntity() {

View File

@ -62,6 +62,10 @@ class DatabaseSchemaClass extends EntityClass {
verifyResponseStatusCode('@fetchDatabaseSchema', 200);
}
followUnfollowEntity() {
// Skiping this since not supported for database
}
// Creation
createEntity() {

View File

@ -32,10 +32,13 @@ import {
createEntityViaREST,
deleteEntity,
deleteEntityViaREST,
followEntity,
hardDeleteEntity as hardDeleteEntityUtil,
restoreEntity as restoreEntityUtil,
unfollowEntity,
updateDescriptioForEntity,
updateDisplayNameForEntity,
validateFollowedEntityToWidget,
} from '../Utils/Entity';
import {
assignGlossaryTerm,
@ -468,6 +471,14 @@ class EntityClass {
deleteAnnoucement();
}
followUnfollowEntity() {
followEntity(this.endPoint);
validateFollowedEntityToWidget(this.entityName, true);
this.visitEntity();
unfollowEntity(this.endPoint);
validateFollowedEntityToWidget(this.entityName, false);
}
// Custom property
setCustomProperty(propertydetails: CustomProperty, value: string) {

View File

@ -35,6 +35,10 @@ class SearchIndexClass extends EntityClass {
});
}
followUnfollowEntity() {
// Skiping this since not working from backend
}
// Creation
createEntity() {

View File

@ -0,0 +1,413 @@
/*
* 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 {
INVALID_NAMES,
NAME_VALIDATION_ERROR,
} from '../../constants/constants';
import {
interceptURL,
replaceAllSpacialCharWith_,
toastNotification,
verifyResponseStatusCode,
} from '../common';
import { visitEntityDetailsPage } from '../Utils/Entity';
import {
deleteService,
getEntityTypeFromService,
retryIngestionRun,
Services,
testConnection,
} from '../Utils/Services';
const RETRIES_COUNT = 4;
const RETRY_TIMES = 4;
const BASE_WAIT_TIME = 20000;
export const descriptionBox =
'.toastui-editor-md-container > .toastui-editor > .ProseMirror';
class ServiceBaseClass {
public category: Services;
protected serviceName: string;
public serviceType: string;
protected entityName: string;
protected shouldTestConnection: boolean;
protected shouldAddIngestion: boolean;
constructor(
category: Services,
name: string,
serviceType: string,
entity: string,
shouldTestConnection = true,
shouldAddIngestion = true
) {
this.category = category;
this.serviceName = name;
this.serviceType = serviceType;
this.entityName = entity;
this.shouldTestConnection = shouldTestConnection;
this.shouldAddIngestion = shouldAddIngestion;
}
visitService() {
// Handle visit service here
}
createService() {
// Handle create service here
// intercept the service requirement md file fetch request
interceptURL(
'GET',
`en-US/*/${this.serviceType}.md`,
'getServiceRequirements'
);
cy.get('[data-testid="add-service-button"]').click();
// Select Service in step 1
this.serviceSetp1(this.serviceType);
// Enter service name in step 2
this.serviceSetp2(this.serviceName);
// Connection Details in step 3
cy.get('[data-testid="add-new-service-container"]')
.parent()
.parent()
.scrollTo('top', {
ensureScrollable: false,
});
cy.contains('Connection Details').scrollIntoView().should('be.visible');
// Requirement panel should be visible and fetch the requirements md file
cy.get('[data-testid="service-requirements"]').should('be.visible');
verifyResponseStatusCode('@getServiceRequirements', [200, 304], {}, true);
this.fillConnectionDetails();
this.shouldTestConnection && testConnection();
this.submitService(this.serviceName);
this.shouldAddIngestion && this.addIngestionPipeline(this.serviceName);
}
serviceSetp1(serviceType: string) {
// Storing the created service name and the type of service
// Select Service in step 1
cy.get(`[data-testid="${serviceType}"]`).click();
cy.get('[data-testid="next-button"]').click();
}
serviceSetp2(serviceName: string) {
// validation should work
cy.get('[data-testid="next-button"]').click();
cy.get('#name_help').should('contain', 'Name is required');
// invalid name validation should work
cy.get('[data-testid="service-name"]').type(
INVALID_NAMES.WITH_SPECIAL_CHARS
);
cy.get('#name_help').should('contain', NAME_VALIDATION_ERROR);
cy.get('[data-testid="service-name"]').clear().type(serviceName);
interceptURL('GET', '/api/v1/services/ingestionPipelines/ip', 'ipApi');
interceptURL(
'GET',
'api/v1/services/ingestionPipelines/*',
'ingestionPipelineStatus'
);
cy.get('[data-testid="next-button"]').click();
}
fillConnectionDetails() {
// Handle fill connection details in respective service here
}
fillIngestionDetails() {
// Handle fill ingestion details in respective service here
}
validateIngestionDetails() {
// Handle validate ingestion details in respective service here
}
addIngestionPipeline(serviceName: string) {
cy.get('[data-testid="add-ingestion-button"]').click();
// Add ingestion page
cy.get('[data-testid="add-ingestion-container"]').should('be.visible');
this.fillIngestionDetails();
cy.get('[data-testid="submit-btn"]').scrollIntoView().click();
// Go back and data should persist
cy.get('[data-testid="back-button"]').scrollIntoView().click();
this.validateIngestionDetails();
// Go Next
cy.get('[data-testid="submit-btn"]').scrollIntoView().click();
this.scheduleIngestion();
cy.contains(`${replaceAllSpacialCharWith_(serviceName)}_metadata`).should(
'be.visible'
);
interceptURL(
'GET',
'/api/v1/services/ingestionPipelines?*',
'ingestionPipelines'
);
interceptURL('GET', '/api/v1/services/*/name/*', 'serviceDetails');
cy.get('[data-testid="view-service-button"]').click();
verifyResponseStatusCode('@serviceDetails', 200);
verifyResponseStatusCode('@ingestionPipelines', 200);
this.handleIngestionRetry();
}
submitService(serviceName: string) {
interceptURL(
'GET',
'/api/v1/services/ingestionPipelines/status',
'getIngestionPipelineStatus'
);
cy.get('[data-testid="submit-btn"]').should('exist').click();
verifyResponseStatusCode('@getIngestionPipelineStatus', 200);
// check success
cy.get('[data-testid="success-line"]').should('be.visible');
cy.contains(`"${serviceName}"`).should('be.visible');
cy.contains('has been created successfully').should('be.visible');
}
scheduleIngestion(hasRetryCount = true) {
interceptURL(
'POST',
'/api/v1/services/ingestionPipelines',
'createIngestionPipelines'
);
interceptURL(
'POST',
'/api/v1/services/ingestionPipelines/deploy/*',
'deployPipeline'
);
interceptURL(
'GET',
'/api/v1/services/ingestionPipelines/status',
'getIngestionPipelineStatus'
);
// Schedule & Deploy
cy.get('[data-testid="cron-type"]').should('be.visible').click();
cy.get('.ant-select-item-option-content').contains('Hour').click();
if (hasRetryCount) {
cy.get('#retries')
.scrollIntoView()
.clear()
.type(RETRIES_COUNT + '');
}
cy.get('[data-testid="deploy-button"]').click();
verifyResponseStatusCode('@createIngestionPipelines', 201);
verifyResponseStatusCode('@deployPipeline', 200, {
responseTimeout: 50000,
});
verifyResponseStatusCode('@getIngestionPipelineStatus', 200);
// check success
cy.get('[data-testid="success-line"]', { timeout: 15000 }).should(
'be.visible'
);
cy.contains('has been created and deployed successfully').should(
'be.visible'
);
}
handleIngestionRetry = (ingestionType = 'metadata') => {
let timer = BASE_WAIT_TIME;
const rowIndex = ingestionType === 'metadata' ? 1 : 2;
interceptURL(
'GET',
'/api/v1/services/ingestionPipelines?*',
'ingestionPipelines'
);
interceptURL(
'GET',
'/api/v1/services/ingestionPipelines/*/pipelineStatus?startTs=*&endTs=*',
'pipelineStatuses'
);
interceptURL('GET', '/api/v1/services/*/name/*', 'serviceDetails');
interceptURL('GET', '/api/v1/permissions?limit=100', 'allPermissions');
// ingestions page
let retryCount = 0;
const testIngestionsTab = () => {
// click on the tab only for the first time
if (retryCount === 0) {
cy.get('[data-testid="ingestions"]').should('exist').and('be.visible');
cy.get('[data-testid="ingestions"] >> [data-testid="count"]').should(
'have.text',
rowIndex
);
cy.get('[data-testid="ingestions"]').click();
if (ingestionType === 'metadata') {
verifyResponseStatusCode('@pipelineStatuses', 200, {
responseTimeout: 50000,
});
}
}
};
const checkSuccessState = () => {
testIngestionsTab();
if (retryCount !== 0) {
cy.wait('@allPermissions').then(() => {
cy.wait('@serviceDetails').then(() => {
verifyResponseStatusCode('@ingestionPipelines', 200);
verifyResponseStatusCode('@pipelineStatuses', 200, {
responseTimeout: 50000,
});
});
});
}
retryCount++;
cy.get(`[data-row-key*="${ingestionType}"]`)
.find('[data-testid="pipeline-status"]')
.as('checkRun');
// the latest run should be success
cy.get('@checkRun').then(($ingestionStatus) => {
const text = $ingestionStatus.text();
if (
text !== 'Success' &&
text !== 'Failed' &&
retryCount <= RETRY_TIMES
) {
// retry after waiting with log1 method [20s,40s,80s,160s,320s]
cy.wait(timer);
timer *= 2;
cy.reload();
checkSuccessState();
} else {
cy.get('@checkRun').should('contain', 'Success');
}
});
};
checkSuccessState();
};
updateService() {
this.updateDescriptionForIngestedTables();
}
updateDescriptionForIngestedTables() {
const description = `${this.entityName} description`;
interceptURL(
'GET',
`/api/v1/services/ingestionPipelines?fields=*&service=*`,
'ingestionPipelines'
);
interceptURL('GET', `/api/v1/*?service=*&fields=*`, 'serviceDetails');
interceptURL(
'GET',
`/api/v1/system/config/pipeline-service-client`,
'pipelineServiceClient'
);
interceptURL(
'GET',
`/api/v1/services/ingestionPipelines/*/pipelineStatus?*`,
'pipelineStatus'
);
// Navigate to ingested table
visitEntityDetailsPage({
term: this.entityName,
serviceName: this.serviceName,
entity: getEntityTypeFromService(this.category),
});
// update description
cy.get('[data-testid="edit-description"]').click();
cy.get(descriptionBox).click().clear().type(description);
interceptURL('PATCH', '/api/v1/*/*', 'updateEntity');
cy.get('[data-testid="save"]').click();
verifyResponseStatusCode('@updateEntity', 200);
// re-run ingestion flow
cy.sidebarClick('app-bar-item-settings');
// Services page
cy.get('.ant-menu-title-content').contains(this.category).click();
interceptURL(
'GET',
'api/v1/search/query?q=*&from=0&size=15&index=*',
'searchService'
);
cy.get('[data-testid="searchbar"]').type(this.serviceName);
verifyResponseStatusCode('@searchService', 200);
// click on created service
cy.get(`[data-testid="service-name-${this.serviceName}"]`).click();
verifyResponseStatusCode('@serviceDetails', 200);
verifyResponseStatusCode('@ingestionPipelines', 200);
verifyResponseStatusCode('@pipelineServiceClient', 200);
cy.get('[data-testid="ingestions"]').click();
verifyResponseStatusCode('@pipelineStatus', 200);
interceptURL(
'POST',
'/api/v1/services/ingestionPipelines/trigger/*',
'checkRun'
);
cy.get(
`[data-row-key*="${replaceAllSpacialCharWith_(
this.serviceName
)}_metadata"] [data-testid="run"]`
).click();
verifyResponseStatusCode('@checkRun', 200);
toastNotification(`Pipeline triggered successfully!`);
// Wait for success
retryIngestionRun();
// Navigate to table name
visitEntityDetailsPage({
term: this.entityName,
serviceName: this.serviceName,
entity: getEntityTypeFromService(this.category),
});
cy.get('[data-testid="markdown-parser"]')
.first()
.invoke('text')
.should('contain', description);
}
deleteService() {
deleteService(this.category, this.serviceName);
}
}
export default ServiceBaseClass;

View File

@ -65,6 +65,10 @@ class StoreProcedureClass extends EntityClass {
});
}
followUnfollowEntity() {
// Skiping this since not supported for store procedure
}
// Cleanup
override cleanup() {
super.cleanup();

View File

@ -0,0 +1,45 @@
/*
* 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 ServiceBaseClass from '../Entities/ServiceBaseClass';
import { Services } from '../Utils/Services';
class MetabaseIngestionClass extends ServiceBaseClass {
name: string;
serviceName: string;
serviceType: string;
constructor() {
super(Services.Pipeline, 'cypress-Airflow', 'Airflow', 'index_metadata');
}
createService() {
super.createService();
}
updateService() {
// Backend issue for udpating displayName
}
fillConnectionDetails() {
cy.get('#root\\/hostPort').type(Cypress.env('airflowHostPort'));
cy.get('#root\\/connection__oneof_select')
.scrollIntoView()
.select('BackendConnection');
}
deleteService() {
super.deleteService();
}
}
export default MetabaseIngestionClass;

View File

@ -0,0 +1,91 @@
/*
* 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 { checkServiceFieldSectionHighlighting } from '../common';
import ServiceBaseClass from '../Entities/ServiceBaseClass';
import { Services } from '../Utils/Services';
class MetabaseIngestionClass extends ServiceBaseClass {
name: string;
filterPattern;
constructor() {
super(Services.Database, 'cypress-BigQuery', 'BigQuery', 'testtable');
this.filterPattern = 'testschema';
}
createService() {
super.createService();
}
updateService() {
// super.updateService();
// Issue with searching ingested data
}
fillConnectionDetails() {
const clientEmail = Cypress.env('bigqueryClientEmail');
cy.get('.form-group > #root\\/credentials\\/gcpConfig\\/type')
.scrollIntoView()
.type('service_account');
checkServiceFieldSectionHighlighting('type');
cy.get('#root\\/credentials\\/gcpConfig\\/projectId')
.scrollIntoView()
.type(Cypress.env('bigqueryProjectId'));
checkServiceFieldSectionHighlighting('projectId');
cy.get('#root\\/credentials\\/gcpConfig\\/privateKeyId')
.scrollIntoView()
.type(Cypress.env('bigqueryPrivateKeyId'));
checkServiceFieldSectionHighlighting('privateKeyId');
cy.get('#root\\/credentials\\/gcpConfig\\/privateKey')
.scrollIntoView()
.type(Cypress.env('bigqueryPrivateKey'));
checkServiceFieldSectionHighlighting('privateKey');
cy.get('#root\\/credentials\\/gcpConfig\\/clientEmail')
.scrollIntoView()
.type(clientEmail);
checkServiceFieldSectionHighlighting('clientEmail');
cy.get('#root\\/credentials\\/gcpConfig\\/clientId')
.scrollIntoView()
.type(Cypress.env('bigqueryClientId'));
checkServiceFieldSectionHighlighting('clientId');
cy.get('#root\\/credentials\\/gcpConfig\\/clientX509CertUrl')
.scrollIntoView()
.type(
`https://www.googleapis.com/robot/v1/metadata/x509/${encodeURIComponent(
clientEmail
)}`
);
checkServiceFieldSectionHighlighting('clientX509CertUrl');
cy.get('[data-testid="add-item-Taxonomy Project IDs"]')
.scrollIntoView()
.click();
checkServiceFieldSectionHighlighting('taxonomyProjectID');
cy.get('#root\\/taxonomyProjectID\\/0')
.scrollIntoView()
.type(Cypress.env('bigqueryProjectIdTaxonomy'));
checkServiceFieldSectionHighlighting('taxonomyProjectID');
}
fillIngestionDetails() {
cy.get('#root\\/schemaFilterPattern\\/includes')
.scrollIntoView()
.type(`${this.filterPattern}{enter}`);
}
deleteService() {
super.deleteService();
}
}
export default MetabaseIngestionClass;

View File

@ -0,0 +1,52 @@
/*
* 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 { checkServiceFieldSectionHighlighting } from '../common';
import ServiceBaseClass from '../Entities/ServiceBaseClass';
import { Services } from '../Utils/Services';
class KafkaIngestionClass extends ServiceBaseClass {
constructor() {
super(Services.Messaging, 'cypress-Kafka', 'Kafka', '__transaction_state');
}
createService() {
super.createService();
}
updateService() {
super.updateService();
}
fillConnectionDetails() {
cy.get('#root\\/bootstrapServers').type(
Cypress.env('kafkaBootstrapServers')
);
checkServiceFieldSectionHighlighting('bootstrapServers');
cy.get('#root\\/schemaRegistryURL').type(
Cypress.env('kafkaSchemaRegistryUrl')
);
checkServiceFieldSectionHighlighting('schemaRegistryURL');
}
fillIngestionDetails() {
cy.get('#root\\/topicFilterPattern\\/includes')
.scrollIntoView()
.type(`${this.entityName}{enter}`);
}
deleteService() {
super.deleteService();
}
}
export default KafkaIngestionClass;

View File

@ -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 { checkServiceFieldSectionHighlighting } from '../common';
import ServiceBaseClass from '../Entities/ServiceBaseClass';
import { Services } from '../Utils/Services';
class MetabaseIngestionClass extends ServiceBaseClass {
name: string;
tableName = 'jaffle_shop dashboard';
constructor() {
super(
Services.Dashboard,
'cypress-Metabase',
'Metabase',
'jaffle_shop dashboard'
);
this.tableName = 'jaffle_shop dashboard';
}
createService() {
super.createService();
}
updateService() {
// Backend issue for searching with displayName
// super.updateService();
}
fillConnectionDetails() {
cy.get('#root\\/username')
.scrollIntoView()
.type(Cypress.env('metabaseUsername'));
checkServiceFieldSectionHighlighting('username');
cy.get('#root\\/password')
.scrollIntoView()
.type(Cypress.env('metabasePassword'));
checkServiceFieldSectionHighlighting('password');
cy.get('#root\\/hostPort')
.scrollIntoView()
.type(Cypress.env('metabaseHostPort'));
checkServiceFieldSectionHighlighting('hostPort');
}
fillIngestionDetails() {
cy.get('#root\\/dashboardFilterPattern\\/includes')
.scrollIntoView()
.type(`${this.tableName}{enter}`);
}
deleteService() {
super.deleteService();
}
}
export default MetabaseIngestionClass;

View File

@ -0,0 +1,55 @@
/*
* 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 { checkServiceFieldSectionHighlighting } from '../common';
import ServiceBaseClass from '../Entities/ServiceBaseClass';
import { Services } from '../Utils/Services';
class MlFlowIngestionClass extends ServiceBaseClass {
constructor() {
super(
Services.MLModels,
'cypress-Ml-Model',
'Mlflow',
'ElasticnetWineModel',
false,
false
);
}
createService() {
super.createService();
}
updateService() {
// Do nothing here
}
fillConnectionDetails() {
cy.get('#root\\/trackingUri').type('mlModelTrackingUri');
checkServiceFieldSectionHighlighting('trackingUri');
cy.get('#root\\/registryUri').type('mlModelRegistryUri');
checkServiceFieldSectionHighlighting('registryUri');
}
fillIngestionDetails() {
cy.get('#root\\/mlModelFilterPattern\\/includes')
.scrollIntoView()
.type(`${this.entityName}{enter}`);
}
deleteService() {
super.deleteService();
}
}
export default MlFlowIngestionClass;

View File

@ -0,0 +1,52 @@
/*
* 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 { checkServiceFieldSectionHighlighting } from '../common';
import ServiceBaseClass from '../Entities/ServiceBaseClass';
import { Services } from '../Utils/Services';
class MysqlIngestionClass extends ServiceBaseClass {
name: string;
tableFilter: string;
constructor() {
super(Services.Database, 'cypress-mysql', 'Mysql', 'bot_entity');
this.tableFilter = 'bot_entity{enter} alert_entity{enter} chart_entity';
}
createService() {
super.createService();
}
fillConnectionDetails() {
cy.get('#root\\/username').type(Cypress.env('mysqlUsername'));
checkServiceFieldSectionHighlighting('username');
cy.get('#root\\/authType\\/password').type(Cypress.env('mysqlPassword'));
checkServiceFieldSectionHighlighting('password');
cy.get('#root\\/hostPort').type(Cypress.env('mysqlHostPort'));
checkServiceFieldSectionHighlighting('hostPort');
}
fillIngestionDetails() {
cy.get('#root\\/tableFilterPattern\\/includes')
.scrollIntoView()
.type(this.tableFilter);
}
validateIngestionDetails() {
const tables = this.tableFilter.replace(/{enter} /g, '');
cy.get('.ant-select-selection-item-content')
.then((content) => content.text())
.should('deep.eq', tables);
}
}
export default MysqlIngestionClass;

View File

@ -0,0 +1,49 @@
/*
* 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 { checkServiceFieldSectionHighlighting } from '../common';
import ServiceBaseClass from '../Entities/ServiceBaseClass';
import { Services } from '../Utils/Services';
class S3IngestionClass extends ServiceBaseClass {
name: string;
constructor() {
super(Services.Storage, 'cypress-s3-storage', 'S3', 'cypress-bucket');
}
createService() {
super.createService();
}
updateService() {
super.updateService();
}
fillConnectionDetails() {
cy.get('#root\\/awsConfig\\/awsAccessKeyId').type(
Cypress.env('s3StorageAccessKeyId')
);
checkServiceFieldSectionHighlighting('awsAccessKeyId');
cy.get('#root\\/awsConfig\\/awsSecretAccessKey').type(
Cypress.env('s3StorageSecretAccessKey')
);
checkServiceFieldSectionHighlighting('awsSecretAccessKey');
cy.get('#root\\/awsConfig\\/awsRegion').type('us');
checkServiceFieldSectionHighlighting('awsRegion');
cy.get('#root\\/awsConfig\\/endPointURL').type(
Cypress.env('s3StorageEndPointUrl')
);
checkServiceFieldSectionHighlighting('endPointURL');
}
}
export default S3IngestionClass;

View File

@ -0,0 +1,52 @@
/*
* 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 { checkServiceFieldSectionHighlighting } from '../common';
import ServiceBaseClass from '../Entities/ServiceBaseClass';
import { Services } from '../Utils/Services';
class SnowflakeIngestionClass extends ServiceBaseClass {
schema: string;
constructor() {
super(Services.Database, 'cypress-Snowflake', 'Snowflake', 'CUSTOMER');
this.schema = 'TPCH_SF1000';
}
createService() {
super.createService();
}
fillConnectionDetails() {
cy.get('#root\\/username').type(Cypress.env('snowflakeUsername'));
checkServiceFieldSectionHighlighting('username');
cy.get('#root\\/password').type(Cypress.env('snowflakePassword'));
checkServiceFieldSectionHighlighting('password');
cy.get('#root\\/account').type(Cypress.env('snowflakeAccount'));
checkServiceFieldSectionHighlighting('account');
cy.get('#root\\/database').type(Cypress.env('snowflakeDatabase'));
checkServiceFieldSectionHighlighting('database');
cy.get('#root\\/warehouse').type(Cypress.env('snowflakeWarehouse'));
checkServiceFieldSectionHighlighting('warehouse');
}
fillIngestionDetails() {
cy.get('#root\\/schemaFilterPattern\\/includes')
.scrollIntoView()
.type(`${this.schema}{enter}`);
}
deleteService() {
super.deleteService();
}
}
export default SnowflakeIngestionClass;

View File

@ -0,0 +1,59 @@
/*
* 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 ServiceBaseClass from '../Entities/ServiceBaseClass';
import { Services } from '../Utils/Services';
class SupersetIngestionClass extends ServiceBaseClass {
constructor() {
super(
Services.Dashboard,
'cypress-Superset',
'Superset',
"World Bank's Data"
);
}
createService() {
super.createService();
}
updateService() {
// Issue with searching ingested data
}
fillConnectionDetails() {
cy.get('#root\\/connection\\/username')
.scrollIntoView()
.type(Cypress.env('supersetUsername'));
cy.get('#root\\/connection\\/password')
.scrollIntoView()
.type(Cypress.env('supersetPassword'));
cy.get('#root\\/hostPort')
.scrollIntoView()
.focus()
.clear()
.type(Cypress.env('supersetHostPort'));
}
fillIngestionDetails() {
cy.get('#root\\/dashboardFilterPattern\\/includes')
.scrollIntoView()
.type(`${this.entityName}{enter}`);
}
deleteService() {
super.deleteService();
}
}
export default SupersetIngestionClass;

View File

@ -14,7 +14,6 @@ import { DELETE_TERM } from '../../constants/constants';
import {
EntityType,
EXPLORE_PAGE_TABS,
SEARCH_INDEX,
} from '../../constants/Entity.interface';
import {
ENTITIES_WITHOUT_FOLLOWING_BUTTON,
@ -177,11 +176,6 @@ export const visitEntityDetailsPage = ({
interceptURL('GET', '/api/v1/*/name/*', 'getEntityDetails');
}
interceptURL(
'GET',
`/api/v1/search/query?q=**&index=${SEARCH_INDEX[entity]}&from=*&size=**`,
'explorePageTabSearch'
);
interceptURL(
'GET',
`/api/v1/search/query?q=**&from=*&size=*&index=all`,
@ -221,9 +215,9 @@ export const visitEntityDetailsPage = ({
cy.get(`[data-testid="${tabName}-tab"]`).click();
verifyResponseStatusCode('@explorePageTabSearch', 200);
cy.get(`[data-testid="${id}"] [data-testid="entity-link"]`)
cy.get(`[data-testid="${id}"] [data-testid="entity-link"]`, {
timeout: 10000,
})
.scrollIntoView()
.click();
}
@ -441,7 +435,11 @@ export const deleteEntity = (entityName: string, endPoint: EntityType) => {
toastNotification('deleted successfully!');
cy.reload();
cy.get('[data-testid="deleted-badge"]').should('have.text', 'Deleted');
cy.get('[data-testid="deleted-badge"]', { timeout: 10000 }).should(
'have.text',
'Deleted'
);
deletedEntityCommonChecks({ entityType: endPoint, deleted: true });
@ -471,8 +469,7 @@ export const deleteEntity = (entityName: string, endPoint: EntityType) => {
'1'
);
cy.get('.ant-table-row > :nth-child(1)').should('contain', entityName);
cy.get('.ant-table-row > :nth-child(1)').contains(entityName).click();
cy.get(`[data-testid=${entityName}]`).click();
}
restoreEntity();
@ -555,3 +552,36 @@ export const updateDescriptioForEntity = (
'[data-testid="asset-description-container"] [data-testid="viewer-container"]'
).should('contain', description);
};
export const followEntity = (entityType: EntityType) => {
interceptURL('PUT', `/api/v1/${entityType}/*/followers`, 'waitAfterFollow');
cy.get('[data-testid="entity-follow-button"]').scrollIntoView().click();
verifyResponseStatusCode('@waitAfterFollow', 200);
};
export const validateFollowedEntityToWidget = (
entityName: string,
isFollowed = true
) => {
interceptURL('GET', '/api/v1/users/*?fields=follows*', 'getFollowedEntities');
cy.goToHomePage();
verifyResponseStatusCode('@getFollowedEntities', 200, { timeout: 10000 });
cy.get(`[data-testid="following-${entityName}"]`).should(
isFollowed ? 'be.visible' : 'not.exist'
);
};
export const unfollowEntity = (entityType: EntityType) => {
interceptURL(
'DELETE',
`/api/v1/${entityType}/*/followers/*`,
'waitAfterUnFollow'
);
cy.get('[data-testid="entity-follow-button"]').scrollIntoView().click();
verifyResponseStatusCode('@waitAfterUnFollow', 200);
};

View File

@ -10,7 +10,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EntityType } from '../../new-tests/base/EntityClass';
import { EntityType } from '../../constants/Entity.interface';
import { interceptURL, verifyResponseStatusCode } from '../common';
export const assignGlossaryTerm = (

View File

@ -0,0 +1,206 @@
/*
* 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 { DELETE_TERM } from '../../constants/constants';
import { EntityType } from '../../constants/Entity.interface';
import {
interceptURL,
toastNotification,
verifyResponseStatusCode,
} from '../common';
export enum Services {
Database = 'Databases',
Messaging = 'Messaging',
Dashboard = 'Dashboards',
Pipeline = 'Pipelines',
MLModels = 'ML Models',
Storage = 'Storages',
Search = 'Search',
}
export const RETRY_TIMES = 4;
export const BASE_WAIT_TIME = 20000;
export const goToServiceListingPage = (services: Services) => {
interceptURL(
'GET',
'api/v1/teams/name/Organization?fields=*',
'getSettingsPage'
);
// Click on settings page
cy.sidebarClick('app-bar-item-settings');
verifyResponseStatusCode('@getSettingsPage', 200);
// Services page
interceptURL('GET', '/api/v1/services/*', 'getServiceList');
cy.get(`[data-testid="global-setting-left-panel"]`)
.contains(services)
.click();
verifyResponseStatusCode('@getServiceList', 200);
};
export const getEntityTypeFromService = (service: Services) => {
switch (service) {
case Services.Dashboard:
return EntityType.DashboardService;
case Services.Database:
return EntityType.DatabaseService;
case Services.Storage:
return EntityType.StorageService;
case Services.Messaging:
return EntityType.MessagingService;
case Services.Search:
return EntityType.SearchService;
case Services.MLModels:
return EntityType.MlModelService;
case Services.Pipeline:
return EntityType.PipelineService;
default:
return EntityType.DatabaseService;
}
};
export const retryIngestionRun = () => {
interceptURL('GET', '/api/v1/services/*/name/*', 'serviceDetails');
interceptURL(
'GET',
'/api/v1/services/ingestionPipelines/*/pipelineStatus/*',
'pipelineStatus'
);
let timer = BASE_WAIT_TIME;
let retryCount = 0;
const testIngestionsTab = () => {
cy.get('[data-testid="ingestions"]').scrollIntoView().should('be.visible');
cy.get('[data-testid="ingestions"] >> [data-testid="count"]').should(
'have.text',
'1'
);
if (retryCount === 0) {
cy.wait(1000);
cy.get('[data-testid="ingestions"]').should('be.visible');
}
};
const checkSuccessState = () => {
testIngestionsTab();
retryCount++;
// the latest run should be success
cy.get('[data-testid="pipeline-status"]').then(($ingestionStatus) => {
if ($ingestionStatus.text() !== 'Success' && retryCount <= RETRY_TIMES) {
// retry after waiting with log1 method [20s,40s,80s,160s,320s]
cy.wait(timer);
timer *= 2;
cy.reload();
verifyResponseStatusCode('@serviceDetails', 200);
verifyResponseStatusCode('@pipelineStatus', 200);
checkSuccessState();
} else {
cy.get('[data-testid="pipeline-status"]').should('contain', 'Success');
}
});
};
checkSuccessState();
};
export const deleteService = (typeOfService: Services, serviceName: string) => {
interceptURL(
'GET',
'api/v1/search/query?q=*&from=0&size=15&index=*',
'searchService'
);
cy.get('[data-testid="searchbar"]').type(serviceName);
verifyResponseStatusCode('@searchService', 200);
// click on created service
cy.get(`[data-testid="service-name-${serviceName}"]`).click();
cy.get(`[data-testid="entity-header-display-name"]`)
.invoke('text')
.then((text) => {
expect(text).to.equal(serviceName);
});
// Clicking on permanent delete radio button and checking the service name
cy.get('[data-testid="manage-button"]').click();
cy.get('[data-menu-id*="delete-button"]').should('be.visible');
cy.get('[data-testid="delete-button-title"]').click();
// Clicking on permanent delete radio button and checking the service name
cy.get('[data-testid="hard-delete-option"]').contains(serviceName).click();
cy.get('[data-testid="confirmation-text-input"]').type(DELETE_TERM);
interceptURL(
'DELETE',
`/api/v1/${getEntityTypeFromService(typeOfService)}/*`,
'deleteService'
);
interceptURL(
'GET',
'/api/v1/services/*/name/*?fields=owner',
'serviceDetails'
);
cy.get('[data-testid="confirm-button"]').click();
verifyResponseStatusCode('@deleteService', 200);
// Closing the toast notification
toastNotification(`Service deleted successfully!`);
cy.get(`[data-testid="service-name-${serviceName}"]`).should('not.exist');
};
export const testConnection = () => {
// Test the connection
interceptURL(
'GET',
'/api/v1/services/testConnectionDefinitions/name/*',
'testConnectionStepDefinition'
);
interceptURL('POST', '/api/v1/automations/workflows', 'createWorkflow');
interceptURL(
'POST',
'/api/v1/automations/workflows/trigger/*',
'triggerWorkflow'
);
interceptURL('GET', '/api/v1/automations/workflows/*', 'getWorkflow');
cy.get('[data-testid="test-connection-btn"]').should('exist').click();
verifyResponseStatusCode('@testConnectionStepDefinition', 200);
verifyResponseStatusCode('@createWorkflow', 201);
// added extra buffer time as triggerWorkflow API can take up to 2minute to provide result
verifyResponseStatusCode('@triggerWorkflow', 200, {
responseTimeout: 120000,
});
cy.get('[data-testid="test-connection-modal"]').should('exist');
cy.get('.ant-modal-footer > .ant-btn-primary')
.should('exist')
.contains('OK')
.click();
verifyResponseStatusCode('@getWorkflow', 200);
cy.get('[data-testid="messag-text"]').then(($message) => {
if ($message.text().includes('partially successful')) {
cy.contains('Test connection partially successful').should('exist');
} else {
cy.contains('Connection test was successful').should('exist');
}
});
};

View File

@ -24,16 +24,9 @@ export const searchServiceFromSettingPage = (service) => {
};
export const visitServiceDetailsPage = (service, verifyHeader = true) => {
// Click on settings page
interceptURL(
'GET',
'api/v1/teams/name/Organization?fields=*',
'getSettingsPage'
);
cy.sidebarClick('app-bar-item-settings');
verifyResponseStatusCode('@getSettingsPage', 200);
// Services page
interceptURL('GET', '/api/v1/services/*', 'getServices');

View File

@ -29,6 +29,7 @@ export enum EntityType {
PipelineService = 'services/pipelineServices',
MessagingService = 'services/messagingServices',
SearchService = 'services/searchServices',
MetadataService = 'services/metadataServices',
Database = 'databases',
DatabaseSchema = 'databaseSchemas',
DataModel = 'dashboard/datamodels',
@ -48,6 +49,7 @@ export const EXPLORE_PAGE_TABS: Record<
| EntityType.DatabaseSchema
| EntityType.GlossaryTerm
| EntityType.Domain
| EntityType.MetadataService
>,
string
> = {
@ -76,6 +78,7 @@ export const SEARCH_INDEX: Record<
| EntityType.Database
| EntityType.DatabaseSchema
| EntityType.GlossaryTerm
| EntityType.MetadataService
>,
string
> = {

View File

@ -77,7 +77,6 @@ describe('Activity feed', () => {
beforeEach(() => {
cy.login();
cy.get("[data-testid='welcome-screen-close-btn']").click();
});
it('Create feed', () => {

View File

@ -1,208 +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,
verifyResponseStatusCode,
visitEntityDetailsPage,
} from '../../common/common';
import {
createEntityTable,
createSingleLevelEntity,
hardDeleteService,
} from '../../common/EntityUtils';
import {
DASHBOARD_DATA_MODEL_DETAILS,
DATABASE_SERVICE,
SINGLE_LEVEL_SERVICE,
STORED_PROCEDURE_DETAILS,
VISIT_ENTITIES_DATA,
} from '../../constants/EntityConstant';
import { SERVICE_CATEGORIES } from '../../constants/service.constants';
// eslint-disable-next-line spaced-comment
/// <reference types="cypress" />
// Update list if we support this for other entities too
const FOLLOWING_ENTITIES = [
VISIT_ENTITIES_DATA.table,
VISIT_ENTITIES_DATA.dashboard,
VISIT_ENTITIES_DATA.topic,
VISIT_ENTITIES_DATA.pipeline,
VISIT_ENTITIES_DATA.mlmodel,
VISIT_ENTITIES_DATA.storedProcedure,
VISIT_ENTITIES_DATA.dataModel,
];
const followEntity = ({ term, serviceName, entity }, isUnfollow) => {
visitEntityDetailsPage({ term, serviceName, entity });
if (entity === 'dashboardDataModel') {
interceptURL(
isUnfollow ? 'DELETE' : 'PUT',
isUnfollow
? '/api/v1/dashboard/datamodels/*/followers/*'
: '/api/v1/dashboard/datamodels/*/followers',
'waitAfterFollow'
);
} else {
interceptURL(
isUnfollow ? 'DELETE' : 'PUT',
isUnfollow ? '/api/v1/*/*/followers/*' : '/api/v1/*/*/followers',
'waitAfterFollow'
);
}
interceptURL(
isUnfollow ? 'DELETE' : 'PUT',
isUnfollow ? '/api/v1/*/*/followers/*' : '/api/v1/*/*/followers',
'waitAfterFollow'
);
cy.get('[data-testid="entity-follow-button"]')
.scrollIntoView()
.should('be.visible')
.click();
verifyResponseStatusCode('@waitAfterFollow', 200);
};
describe('Following data assets', () => {
before(() => {
cy.login();
cy.getAllLocalStorage().then((data) => {
const token = Object.values(data)[0].oidcIdToken;
createEntityTable({
token,
...DATABASE_SERVICE,
tables: [DATABASE_SERVICE.entity],
});
SINGLE_LEVEL_SERVICE.forEach((data) => {
createSingleLevelEntity({
token,
...data,
entity: [data.entity],
});
});
// creating data model
cy.request({
method: 'POST',
url: `/api/v1/dashboard/datamodels`,
headers: { Authorization: `Bearer ${token}` },
body: DASHBOARD_DATA_MODEL_DETAILS,
});
// creating stored procedure
cy.request({
method: 'POST',
url: `/api/v1/storedProcedures`,
headers: { Authorization: `Bearer ${token}` },
body: STORED_PROCEDURE_DETAILS,
});
});
});
after(() => {
cy.login();
cy.getAllLocalStorage().then((data) => {
const token = Object.values(data)[0].oidcIdToken;
hardDeleteService({
token,
serviceFqn: DATABASE_SERVICE.service.name,
serviceType: SERVICE_CATEGORIES.DATABASE_SERVICES,
});
SINGLE_LEVEL_SERVICE.forEach((data) => {
hardDeleteService({
token,
serviceFqn: data.service.name,
serviceType: data.serviceType,
});
});
});
});
beforeEach(() => {
cy.login();
cy.get("[data-testid='welcome-screen-close-btn']").click();
});
it('following section should be present', () => {
cy.get('[data-testid="following-widget"]')
.scrollIntoView()
.should('be.visible');
cy.get('[data-testid="following-widget"]').contains(
'You have not followed anything yet.'
);
cy.get(`[data-testid="following-widget"] .right-panel-list-item`).should(
'have.length',
0
);
});
// Follow entity
FOLLOWING_ENTITIES.map((entity, index) => {
it(`following section should have ${entity.term} followed`, () => {
followEntity(entity);
interceptURL(
'GET',
'/api/v1/feed?type=Announcement&activeAnnouncement=true',
'getAnnoucemenets'
);
cy.clickOnLogo();
verifyResponseStatusCode('@getAnnoucemenets', 200);
if (index < 5) {
cy.get(`[data-testid="following-${entity.displayName}"]`).should(
'be.visible'
);
}
// Checking count of following
cy.get(`[data-testid="following-data"]`).should('contain', index + 1);
});
});
// UnFollow entity
FOLLOWING_ENTITIES.map((entity, index) => {
it(`unfollowing entity ${entity.term} should removed from following section`, () => {
followEntity(entity, true);
interceptURL(
'GET',
'/api/v1/feed?type=Announcement&activeAnnouncement=true',
'getAnnoucemenets'
);
cy.clickOnLogo();
verifyResponseStatusCode('@getAnnoucemenets', 200);
cy.get(`[data-testid="following-${entity.displayName}"]`).should(
'not.exist'
);
if (index === FOLLOWING_ENTITIES.length - 1) {
// Checking count of following
cy.get(`[data-testid="following-data"]`).should('not.exist');
} else {
// Checking count of following
cy.get(`[data-testid="following-data"]`).should(
'contain',
FOLLOWING_ENTITIES.length - (index + 1)
);
}
});
});
});

View File

@ -82,7 +82,6 @@ describe('Query Entity', () => {
beforeEach(() => {
cy.login();
cy.get("[data-testid='welcome-screen-close-btn']").click();
});
it('Create query', () => {

View File

@ -92,7 +92,6 @@ describe('Recently viwed data assets', () => {
beforeEach(() => {
cy.login();
cy.get("[data-testid='welcome-screen-close-btn']").click();
});
it('recently view section should be present', () => {

View File

@ -102,6 +102,10 @@ entities.forEach((entity) => {
entity.downVote();
});
it(`follow unfollow entity`, () => {
entity.followUnfollowEntity();
});
Object.values(CustomPropertyType).forEach((type) => {
it(`Set ${type} Custom Property `, () => {
entity.setCustomProperty(

View File

@ -130,7 +130,11 @@ entities.forEach((entity) => {
});
}
it(`Update displayName`, () => {
it(`follow unfollow entity`, () => {
entity.followUnfollowEntity();
});
it.skip(`Update displayName`, () => {
entity.renameEntity();
});

View File

@ -1,75 +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.
*/
// eslint-disable-next-line spaced-comment
/// <reference types="cypress" />
import {
deleteCreatedService,
goToAddNewServicePage,
testServiceCreationAndIngestion,
updateDescriptionForIngestedTables,
uuid,
} from '../../common/common';
import {
API_SERVICE,
DATA_ASSETS,
SERVICE_TYPE,
} from '../../constants/constants';
const serviceType = 'Airflow';
const serviceName = `${serviceType}-ct-test-${uuid()}`;
const tableName = 'index_metadata';
const description = `This is ${tableName} description`;
const connectionInput = () => {
cy.get('#root\\/hostPort').type(Cypress.env('airflowHostPort'));
cy.get('#root\\/connection__oneof_select')
.scrollIntoView()
.select('BackendConnection');
};
describe('Airflow Ingestion', () => {
beforeEach(() => {
cy.login();
});
it('add and ingest data', () => {
goToAddNewServicePage(SERVICE_TYPE.Pipeline);
testServiceCreationAndIngestion({
serviceType,
connectionInput,
serviceName,
type: SERVICE_TYPE.Pipeline,
serviceCategory: SERVICE_TYPE.Pipeline,
});
});
it.skip('Update pipeline description and verify description after re-run', () => {
updateDescriptionForIngestedTables(
serviceName,
tableName,
description,
SERVICE_TYPE.Pipeline,
DATA_ASSETS.pipelines
);
});
it('delete created service', () => {
deleteCreatedService(
SERVICE_TYPE.Pipeline,
serviceName,
API_SERVICE.pipelineServices
);
});
});

View File

@ -1,67 +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 {
checkServiceFieldSectionHighlighting,
deleteCreatedService,
goToAddNewServicePage,
testServiceCreationAndIngestion,
uuid,
} from '../../common/common';
import { API_SERVICE, SERVICE_TYPE } from '../../constants/constants';
const serviceType = 'Mlflow';
const serviceName = `${serviceType}-ct-test-${uuid()}`;
const modelName = 'ElasticnetWineModel';
const connectionInput = () => {
cy.get('#root\\/trackingUri').type('mlModelTrackingUri');
checkServiceFieldSectionHighlighting('trackingUri');
cy.get('#root\\/registryUri').type('mlModelRegistryUri');
checkServiceFieldSectionHighlighting('registryUri');
};
const addIngestionInput = () => {
cy.get('#root\\/mlModelFilterPattern\\/includes')
.scrollIntoView()
.type(`${modelName}{enter}`);
};
describe('ML Flow Ingestion', () => {
beforeEach(() => {
cy.login();
});
it('add and ingest data', () => {
goToAddNewServicePage(SERVICE_TYPE.MLModels);
testServiceCreationAndIngestion({
serviceType,
connectionInput,
addIngestionInput,
serviceName,
type: SERVICE_TYPE.MLModels,
serviceCategory: 'MlModel',
shouldAddIngestion: false,
allowTestConnection: false,
});
});
it('delete created service', () => {
deleteCreatedService(
SERVICE_TYPE.MLModels,
serviceName,
API_SERVICE.mlmodelServices,
'Mlmodel'
);
});
});

View File

@ -1,84 +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 {
checkServiceFieldSectionHighlighting,
deleteCreatedService,
goToAddNewServicePage,
testServiceCreationAndIngestion,
updateDescriptionForIngestedTables,
uuid,
} from '../../common/common';
import {
API_SERVICE,
DATA_ASSETS,
SERVICE_TYPE,
} from '../../constants/constants';
const serviceType = 'S3';
const serviceName = `${serviceType}-ct-test-${uuid()}`;
const tableName = 'cypress-bucket';
const description = `This is ${tableName} description`;
const connectionInput = () => {
cy.get('#root\\/awsConfig\\/awsAccessKeyId').type(
Cypress.env('s3StorageAccessKeyId')
);
checkServiceFieldSectionHighlighting('awsAccessKeyId');
cy.get('#root\\/awsConfig\\/awsSecretAccessKey').type(
Cypress.env('s3StorageSecretAccessKey')
);
checkServiceFieldSectionHighlighting('awsSecretAccessKey');
cy.get('#root\\/awsConfig\\/awsRegion').type('us');
checkServiceFieldSectionHighlighting('awsRegion');
cy.get('#root\\/awsConfig\\/endPointURL').type(
Cypress.env('s3StorageEndPointUrl')
);
checkServiceFieldSectionHighlighting('endPointURL');
};
describe('S3Storage Ingestion', () => {
beforeEach(() => {
cy.login();
});
it('add and ingest data', () => {
goToAddNewServicePage(SERVICE_TYPE.Storage);
testServiceCreationAndIngestion({
serviceType,
connectionInput,
serviceName,
type: SERVICE_TYPE.Storage,
serviceCategory: SERVICE_TYPE.Storage,
});
});
it('Update container description and verify description after re-run', () => {
updateDescriptionForIngestedTables(
serviceName,
tableName,
description,
SERVICE_TYPE.Storage,
DATA_ASSETS.containers
);
});
// Todo: unskip below test once issue is fixed https://github.com/open-metadata/OpenMetadata/issues/11700
it.skip('delete created service', () => {
deleteCreatedService(
SERVICE_TYPE.Storage,
serviceName,
API_SERVICE.storageServices
);
});
});

View File

@ -0,0 +1,55 @@
/*
* 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 AirflowIngestionClass from '../../common/Services/AirflowIngestionClass';
import BigQueryIngestionClass from '../../common/Services/BigQueryIngestionClass';
import KafkaIngestionClass from '../../common/Services/KafkaIngestionClass';
import MetabaseIngestionClass from '../../common/Services/MetabaseIngestionClass';
import MlFlowIngestionClass from '../../common/Services/MlFlowIngestionClass';
import MysqlIngestionClass from '../../common/Services/MysqlIngestionClass';
import S3IngestionClass from '../../common/Services/S3IngestionClass';
import SnowflakeIngestionClass from '../../common/Services/SnowflakeIngestionClass';
import SupersetIngestionClass from '../../common/Services/SupersetIngestionClass';
import { goToServiceListingPage } from '../../common/Utils/Services';
const services = [
new S3IngestionClass(),
new MetabaseIngestionClass(),
new MysqlIngestionClass(),
new AirflowIngestionClass(),
new BigQueryIngestionClass(),
new KafkaIngestionClass(),
new MlFlowIngestionClass(),
new SnowflakeIngestionClass(),
new SupersetIngestionClass(),
];
services.forEach((service) => {
describe(`${service.serviceType} Ingestion`, () => {
beforeEach(() => {
cy.login();
goToServiceListingPage(service.category);
});
it(`Create & Ingest ${service.serviceType} service`, () => {
service.createService();
});
it(`Update description and verify description after re-run`, () => {
service.updateService();
});
it(`Delete ${service.serviceType} service`, () => {
service.deleteService();
});
});
});

View File

@ -1,113 +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 {
checkServiceFieldSectionHighlighting,
deleteCreatedService,
goToAddNewServicePage,
testServiceCreationAndIngestion,
updateDescriptionForIngestedTables,
uuid,
} from '../../common/common';
import { API_SERVICE, SERVICE_TYPE } from '../../constants/constants';
const serviceType = 'BigQuery';
const serviceName = `${serviceType}-ct-test-${uuid()}`;
const tableName = 'testtable';
const description = `This is ${serviceName} description`;
const filterPattern = 'testschema';
describe('BigQuery Ingestion', () => {
beforeEach(() => {
cy.login();
});
it('add and ingest data', () => {
goToAddNewServicePage(SERVICE_TYPE.Database);
const connectionInput = () => {
const clientEmail = Cypress.env('bigqueryClientEmail');
cy.get('.form-group > #root\\/credentials\\/gcpConfig\\/type')
.scrollIntoView()
.type('service_account');
checkServiceFieldSectionHighlighting('type');
cy.get('#root\\/credentials\\/gcpConfig\\/projectId')
.scrollIntoView()
.type(Cypress.env('bigqueryProjectId'));
checkServiceFieldSectionHighlighting('projectId');
cy.get('#root\\/credentials\\/gcpConfig\\/privateKeyId')
.scrollIntoView()
.type(Cypress.env('bigqueryPrivateKeyId'));
checkServiceFieldSectionHighlighting('privateKeyId');
cy.get('#root\\/credentials\\/gcpConfig\\/privateKey')
.scrollIntoView()
.type(Cypress.env('bigqueryPrivateKey'));
checkServiceFieldSectionHighlighting('privateKey');
cy.get('#root\\/credentials\\/gcpConfig\\/clientEmail')
.scrollIntoView()
.type(clientEmail);
checkServiceFieldSectionHighlighting('clientEmail');
cy.get('#root\\/credentials\\/gcpConfig\\/clientId')
.scrollIntoView()
.type(Cypress.env('bigqueryClientId'));
checkServiceFieldSectionHighlighting('clientId');
cy.get('#root\\/credentials\\/gcpConfig\\/clientX509CertUrl')
.scrollIntoView()
.type(
`https://www.googleapis.com/robot/v1/metadata/x509/${encodeURIComponent(
clientEmail
)}`
);
checkServiceFieldSectionHighlighting('clientX509CertUrl');
cy.get('[data-testid="add-item-Taxonomy Project IDs"]')
.scrollIntoView()
.click();
checkServiceFieldSectionHighlighting('taxonomyProjectID');
cy.get('#root\\/taxonomyProjectID\\/0')
.scrollIntoView()
.type(Cypress.env('bigqueryProjectIdTaxonomy'));
checkServiceFieldSectionHighlighting('taxonomyProjectID');
};
const addIngestionInput = () => {
cy.get('#root\\/schemaFilterPattern\\/includes')
.scrollIntoView()
.type(`${filterPattern}{enter}`);
};
testServiceCreationAndIngestion({
serviceType,
connectionInput,
addIngestionInput,
serviceName,
serviceCategory: SERVICE_TYPE.Database,
});
});
it('Update table description and verify description after re-run', () => {
updateDescriptionForIngestedTables(
serviceName,
tableName,
description,
SERVICE_TYPE.Database,
'tables'
);
});
it('delete created service', () => {
deleteCreatedService(
SERVICE_TYPE.Database,
serviceName,
API_SERVICE.databaseServices
);
});
});

View File

@ -1,90 +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 {
checkServiceFieldSectionHighlighting,
deleteCreatedService,
goToAddNewServicePage,
testServiceCreationAndIngestion,
updateDescriptionForIngestedTables,
uuid,
} from '../../common/common';
import { API_SERVICE, SERVICE_TYPE } from '../../constants/constants';
const serviceType = 'Glue';
const serviceName = `${serviceType}-ct-test-${uuid()}`;
const tableName = 'cloudfront_logs2';
const description = `This is ${serviceName} description`;
const filterPattern = 'default';
// We do not have creds for glue to validate
describe.skip('Glue Ingestion', () => {
beforeEach(() => {
cy.login();
});
it('add and ingest data', () => {
goToAddNewServicePage(SERVICE_TYPE.Database);
const connectionInput = () => {
cy.get('#root\\/awsConfig\\/awsAccessKeyId')
.scrollIntoView()
.type(Cypress.env('glueAwsAccessKeyId'));
checkServiceFieldSectionHighlighting('awsAccessKeyId');
cy.get('#root\\/awsConfig\\/awsSecretAccessKey')
.scrollIntoView()
.type(Cypress.env('glueAwsSecretAccessKey'));
checkServiceFieldSectionHighlighting('awsSecretAccessKey');
cy.get('#root\\/awsConfig\\/awsRegion')
.scrollIntoView()
.type(Cypress.env('glueAwsRegion'));
checkServiceFieldSectionHighlighting('awsRegion');
cy.get('#root\\/awsConfig\\/endPointURL')
.scrollIntoView()
.type(Cypress.env('glueEndPointURL'));
checkServiceFieldSectionHighlighting('endPointURL');
};
const addIngestionInput = () => {
cy.get('#root\\/schemaFilterPattern\\/includes')
.scrollIntoView()
.type(`${filterPattern}{enter}`);
};
testServiceCreationAndIngestion({
serviceType,
connectionInput,
addIngestionInput,
serviceName,
testIngestionButton: false,
serviceCategory: SERVICE_TYPE.Database,
});
});
it('Update table description and verify description after re-run', () => {
updateDescriptionForIngestedTables(
serviceName,
tableName,
description,
SERVICE_TYPE.Database,
'tables'
);
});
it('delete created service', () => {
deleteCreatedService(
SERVICE_TYPE.Database,
serviceName,
API_SERVICE.databaseServices
);
});
});

View File

@ -1,88 +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 {
checkServiceFieldSectionHighlighting,
deleteCreatedService,
goToAddNewServicePage,
testServiceCreationAndIngestion,
updateDescriptionForIngestedTables,
uuid,
} from '../../common/common';
import { API_SERVICE, SERVICE_TYPE } from '../../constants/constants';
const serviceType = 'Kafka';
const serviceName = `${serviceType}-ct-test-${uuid()}`;
const topicName = '__transaction_state';
const description = `This is ${serviceName} description`;
describe('Kafka Ingestion', () => {
beforeEach(() => {
cy.login();
});
it('add and ingest data', () => {
goToAddNewServicePage(SERVICE_TYPE.Messaging);
// Select Dashboard services
cy.get('[data-testid="service-category"]').should('be.visible').click();
cy.get('.ant-select-item-option-content')
.contains('Messaging Services')
.click();
const connectionInput = () => {
cy.get('#root\\/bootstrapServers').type(
Cypress.env('kafkaBootstrapServers')
);
checkServiceFieldSectionHighlighting('bootstrapServers');
cy.get('#root\\/schemaRegistryURL').type(
Cypress.env('kafkaSchemaRegistryUrl')
);
checkServiceFieldSectionHighlighting('schemaRegistryURL');
};
const addIngestionInput = () => {
cy.get('#root\\/topicFilterPattern\\/includes')
.scrollIntoView()
.type(`${topicName}{enter}`);
};
testServiceCreationAndIngestion({
serviceType: 'Kafka',
connectionInput,
addIngestionInput,
serviceName,
type: 'messaging',
serviceCategory: SERVICE_TYPE.Messaging,
});
});
it('Update table description and verify description after re-run', () => {
updateDescriptionForIngestedTables(
serviceName,
topicName,
description,
SERVICE_TYPE.Messaging,
'topics'
);
});
it('delete created service', () => {
deleteCreatedService(
SERVICE_TYPE.Messaging,
serviceName,
API_SERVICE.messagingServices
);
});
});

View File

@ -1,92 +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 {
checkServiceFieldSectionHighlighting,
deleteCreatedService,
goToAddNewServicePage,
testServiceCreationAndIngestion,
updateDescriptionForIngestedTables,
uuid,
} from '../../common/common';
import { API_SERVICE, SERVICE_TYPE } from '../../constants/constants';
const serviceType = 'Metabase';
const serviceName = `${serviceType}-ct-test-${uuid()}`;
const tableName = 'jaffle_shop dashboard';
const description = `This is ${serviceName} description`;
describe('Metabase Ingestion', () => {
beforeEach(() => {
cy.login();
});
it('add and ingest data', () => {
goToAddNewServicePage(SERVICE_TYPE.Dashboard);
// Select Dashboard services
cy.get('[data-testid="service-category"]').should('be.visible').click();
cy.get('.ant-select-item-option-content')
.contains('Dashboard Services')
.click();
const connectionInput = () => {
cy.get('#root\\/username')
.scrollIntoView()
.type(Cypress.env('metabaseUsername'));
checkServiceFieldSectionHighlighting('username');
cy.get('#root\\/password')
.scrollIntoView()
.type(Cypress.env('metabasePassword'));
checkServiceFieldSectionHighlighting('password');
cy.get('#root\\/hostPort')
.scrollIntoView()
.type(Cypress.env('metabaseHostPort'));
checkServiceFieldSectionHighlighting('hostPort');
};
const addIngestionInput = () => {
cy.get('#root\\/dashboardFilterPattern\\/includes')
.scrollIntoView()
.type(`${tableName}{enter}`);
};
testServiceCreationAndIngestion({
serviceType,
connectionInput,
addIngestionInput,
serviceName,
type: 'dashboard',
serviceCategory: SERVICE_TYPE.Dashboard,
});
});
it.skip('Update table description and verify description after re-run', () => {
updateDescriptionForIngestedTables(
serviceName,
tableName,
description,
SERVICE_TYPE.Dashboard,
'dashboards'
);
});
it('delete created service', () => {
deleteCreatedService(
SERVICE_TYPE.Dashboard,
serviceName,
API_SERVICE.dashboardServices
);
});
});

View File

@ -1,80 +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 {
deleteCreatedService,
goToAddNewServicePage,
mySqlConnectionInput,
testServiceCreationAndIngestion,
updateDescriptionForIngestedTables,
uuid,
} from '../../common/common';
import {
API_SERVICE,
SERVICE_TYPE,
TEAM_ENTITY,
} from '../../constants/constants';
const serviceType = 'Mysql';
const serviceName = `${serviceType}.ct%test-${uuid()}`;
const tableName = TEAM_ENTITY;
const description = `This is ${tableName} description`;
describe('MySQL Ingestion', () => {
beforeEach(() => {
cy.login();
});
it('add and ingest data', () => {
goToAddNewServicePage(SERVICE_TYPE.Database);
const addIngestionInput = () => {
cy.get('#root\\/schemaFilterPattern\\/includes')
.scrollIntoView()
.type(`${Cypress.env('mysqlDatabaseSchema')}{enter}`);
};
const viewIngestionInput = () => {
cy.get('.ant-select-selection-item-content')
.scrollIntoView()
.contains(`${Cypress.env('mysqlDatabaseSchema')}`);
};
testServiceCreationAndIngestion({
serviceType,
connectionInput: mySqlConnectionInput,
addIngestionInput,
serviceName,
serviceCategory: SERVICE_TYPE.Database,
viewIngestionInput,
});
});
it('Update table description and verify description after re-run', () => {
updateDescriptionForIngestedTables(
serviceName,
tableName,
description,
SERVICE_TYPE.Database,
'tables'
);
});
it('delete created service', () => {
deleteCreatedService(
SERVICE_TYPE.Database,
serviceName,
API_SERVICE.databaseServices
);
});
});

View File

@ -1,87 +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 {
checkServiceFieldSectionHighlighting,
deleteCreatedService,
goToAddNewServicePage,
testServiceCreationAndIngestion,
updateDescriptionForIngestedTables,
uuid,
} from '../../common/common';
import { API_SERVICE, SERVICE_TYPE } from '../../constants/constants';
const serviceType = 'Snowflake';
const serviceName = `${serviceType}-ct-test-${uuid()}`;
const tableName = 'CUSTOMER';
const schema = 'TPCH_SF1000';
const description = `This is ${serviceName} description`;
const entityFqn = `${serviceName}.${Cypress.env(
'snowflakeDatabase'
)}.${schema}.${tableName}`;
describe('Snowflake Ingestion', () => {
beforeEach(() => {
cy.login();
});
it('add and ingest data', { defaultCommandTimeout: 8000 }, () => {
goToAddNewServicePage(SERVICE_TYPE.Database);
const connectionInput = () => {
cy.get('#root\\/username').type(Cypress.env('snowflakeUsername'));
checkServiceFieldSectionHighlighting('username');
cy.get('#root\\/password').type(Cypress.env('snowflakePassword'));
checkServiceFieldSectionHighlighting('password');
cy.get('#root\\/account').type(Cypress.env('snowflakeAccount'));
checkServiceFieldSectionHighlighting('account');
cy.get('#root\\/database').type(Cypress.env('snowflakeDatabase'));
checkServiceFieldSectionHighlighting('database');
cy.get('#root\\/warehouse').type(Cypress.env('snowflakeWarehouse'));
checkServiceFieldSectionHighlighting('warehouse');
};
const addIngestionInput = () => {
cy.get('#root\\/schemaFilterPattern\\/includes')
.scrollIntoView()
.type(`${schema}{enter}`);
};
testServiceCreationAndIngestion({
serviceType,
connectionInput,
addIngestionInput,
serviceName,
serviceCategory: SERVICE_TYPE.Database,
});
});
it('Update table description and verify description after re-run', () => {
updateDescriptionForIngestedTables(
serviceName,
tableName,
description,
SERVICE_TYPE.Database,
'tables',
entityFqn
);
});
it('delete created service', () => {
deleteCreatedService(
SERVICE_TYPE.Database,
serviceName,
API_SERVICE.databaseServices
);
});
});

View File

@ -1,90 +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 {
deleteCreatedService,
goToAddNewServicePage,
testServiceCreationAndIngestion,
updateDescriptionForIngestedTables,
uuid,
} from '../../common/common';
import { API_SERVICE, SERVICE_TYPE } from '../../constants/constants';
const serviceType = 'Superset';
const serviceName = `${serviceType}-ct-test-${uuid()}`;
const tableName = "World Bank's Data";
const description = `This is ${serviceName} description`;
describe('Superset Ingestion', () => {
beforeEach(() => {
cy.login();
});
it('add and ingest data', () => {
goToAddNewServicePage(SERVICE_TYPE.Dashboard);
// Select Dashboard services
cy.get('[data-testid="service-category"]').should('be.visible').click();
cy.get('.ant-select-item-option-content')
.contains('Dashboard Services')
.click();
const connectionInput = () => {
cy.get('#root\\/connection\\/username')
.scrollIntoView()
.type(Cypress.env('supersetUsername'));
cy.get('#root\\/connection\\/password')
.scrollIntoView()
.type(Cypress.env('supersetPassword'));
cy.get('#root\\/hostPort')
.scrollIntoView()
.focus()
.clear()
.type(Cypress.env('supersetHostPort'));
};
const addIngestionInput = () => {
cy.get('#root\\/dashboardFilterPattern\\/includes')
.scrollIntoView()
.type(`${tableName}{enter}`);
};
testServiceCreationAndIngestion({
serviceType,
connectionInput,
addIngestionInput,
serviceName,
type: 'dashboard',
serviceCategory: SERVICE_TYPE.Dashboard,
});
});
it('Update table description and verify description after re-run', () => {
updateDescriptionForIngestedTables(
serviceName,
tableName,
description,
SERVICE_TYPE.Dashboard,
'dashboards'
);
});
it('delete created service', () => {
deleteCreatedService(
SERVICE_TYPE.Dashboard,
serviceName,
API_SERVICE.dashboardServices
);
});
});

View File

@ -126,6 +126,7 @@ Cypress.Commands.add('storeSession', (username, password) => {
.replaceAll('.', '_')}`;
cy.setCookie(versionCookie, 'true');
window.localStorage.setItem('loggedInUsers', 'admin');
});
});
});

View File

@ -62,9 +62,7 @@ export const EntityListWithV1: FunctionComponent<AntdEntityListProp> = ({
return (
<div
className="right-panel-list-item flex items-center justify-between"
data-testid={`${testIDText}-${getEntityName(
item as unknown as EntityReference
)}`}
data-testid={`${testIDText}-${item.name}`}
key={item.id}>
<div className="flex items-center">
<Link

View File

@ -237,7 +237,7 @@ const ExploreV1: React.FC<ExploreProps> = ({
<div className="w-full h-full">
{tabItems.length > 0 && (
<Layout hasSider className="bg-white">
<Sider className="bg-white border-right" width={250}>
<Sider className="bg-white border-right" width={270}>
<Typography.Paragraph className="explore-data-header">
{t('label.data-asset-plural')}
</Typography.Paragraph>

View File

@ -197,6 +197,7 @@ const DatabaseDetails: FunctionComponent = () => {
setIsDatabaseDetailsLoading(true);
getDatabaseDetailsByFQN(databaseFQN, {
fields: 'owner,tags,domain,votes',
include: Include.All,
})
.then((res) => {
if (res) {

View File

@ -80,7 +80,7 @@ function SchemaTablesTab({
<div className="d-inline-flex w-max-90">
<Link
className="break-word"
data-testid="table-name-link"
data-testid={record.name}
to={entityUtilClassBase.getEntityLink(
EntityType.TABLE,
record.fullyQualifiedName as string

View File

@ -117,7 +117,7 @@ const MyDataPage = () => {
setIsLoadingOwnedData(true);
try {
const userData = await getUserById(currentUser?.id, {
fields: 'follows, owns',
fields: 'follows,owns',
});
if (userData) {

View File

@ -160,6 +160,7 @@ describe('StoredProcedure component', () => {
expect(getStoredProceduresByFqn).toHaveBeenCalledWith('fqn', {
fields: STORED_PROCEDURE_DEFAULT_FIELDS,
include: 'all',
});
});
@ -178,6 +179,7 @@ describe('StoredProcedure component', () => {
expect(getStoredProceduresByFqn).toHaveBeenCalledWith('fqn', {
fields: STORED_PROCEDURE_DEFAULT_FIELDS,
include: 'all',
});
});
@ -208,6 +210,7 @@ describe('StoredProcedure component', () => {
expect(getStoredProceduresByFqn).toHaveBeenCalledWith('fqn', {
fields: STORED_PROCEDURE_DEFAULT_FIELDS,
include: 'all',
});
expect(await screen.findByText('testDataAssetsHeader')).toBeInTheDocument();
@ -234,6 +237,7 @@ describe('StoredProcedure component', () => {
expect(getStoredProceduresByFqn).toHaveBeenCalledWith('fqn', {
fields: STORED_PROCEDURE_DEFAULT_FIELDS,
include: 'all',
});
expect(await screen.findByText('testSchemaEditor')).toBeInTheDocument();

View File

@ -57,6 +57,7 @@ import {
StoredProcedure,
StoredProcedureCodeObject,
} from '../../generated/entity/data/storedProcedure';
import { Include } from '../../generated/type/include';
import { TagLabel } from '../../generated/type/tagLabel';
import { postThread } from '../../rest/feedsAPI';
import {
@ -172,6 +173,7 @@ const StoredProcedurePage = () => {
try {
const response = await getStoredProceduresByFqn(storedProcedureFQN, {
fields: STORED_PROCEDURE_DEFAULT_FIELDS,
include: Include.All,
});
setStoredProcedure(response);

View File

@ -80,7 +80,7 @@ class SearchClassBase {
path: 'dashboards',
},
[SearchIndex.DASHBOARD_DATA_MODEL]: {
label: i18n.t('label.data-model-plural'),
label: i18n.t('label.dashboard-data-model-plural'),
sortingFields: entitySortingFields,
sortField: INITIAL_SORT_FIELD,
path: 'dashboardDataModel',