Skip to content

Commit

Permalink
[Enterprise Search] MongoDB Connector cypress test. (elastic#155886)
Browse files Browse the repository at this point in the history
## Summary

Add automated tests via Cypress for MongoDB and Web Crawler happy paths.

Tests are quite basic and covers only adding simple one time sync paths.
Fill in the `cypress.env.json` with the credentials before running.
Mongo specs will run only against a local environment (via `./cypress.sh
dev`) as connector has to be running locally as well.


### Checklist

Delete any items that are not applicable to this PR.

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
efegurkan and kibanamachine authored Apr 28, 2023
1 parent 8c87a3f commit a4953ed
Show file tree
Hide file tree
Showing 22 changed files with 299 additions and 12 deletions.
12 changes: 12 additions & 0 deletions x-pack/plugins/enterprise_search/cypress.env.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"mongo_test": {
"host": "",
"username": "",
"password": "",
"database": "",
"collection": ""
},
"crawler_test": {
"domain": ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,55 @@

import { login } from '../../tasks/login';

import { NEW_INDEX_CARD, CRAWLER_INDEX, INDEX_OVERVIEW, ROUTES, getIndexRoute } from './selectors';

describe('Enterprise Search Crawler', () => {
it('test', () => {
const envConfig = Cypress.env('crawler_test');
const indexName = 'cypress-crawler-' + Math.random();

login();
cy.visit('/app/enterprise_search/content/search_indices/new_index');
cy.visit(ROUTES.NEW_INDEX);

// Crawler selected by default
cy.getBySel(NEW_INDEX_CARD.SELECT_CRAWLER).click();

// we are in correct route
cy.url().should('contain', ROUTES.NEW_INDEX);

cy.getBySel(CRAWLER_INDEX.CREATE_BUTTON).should('be.disabled');

// type new name
cy.getBySel(CRAWLER_INDEX.INDEX_NAME_INPUT).type(indexName);

// create index
cy.getBySel(CRAWLER_INDEX.CREATE_BUTTON).click();

// make sure we are in new index
cy.url().should('contain', getIndexRoute(indexName) + 'domain_management');

cy.getBySel(CRAWLER_INDEX.DOMAIN_MANAGEMENT.DOMAIN_INPUT).type(envConfig.domain);
cy.getBySel(CRAWLER_INDEX.DOMAIN_MANAGEMENT.DOMAIN_BUTTON).click();

cy.getBySel(CRAWLER_INDEX.DOMAIN_MANAGEMENT.SUBMIT_BUTTON).should('be.enabled');
cy.getBySel(CRAWLER_INDEX.DOMAIN_MANAGEMENT.SUBMIT_BUTTON).click();

cy.getBySel(CRAWLER_INDEX.CRAWL_DROPDOWN).should('be.enabled');
cy.getBySel(CRAWLER_INDEX.CRAWL_DROPDOWN).click();
cy.getBySel(CRAWLER_INDEX.CRAWL_ALL_DOMAINS).click();

// go to overview tab
cy.getBySel(INDEX_OVERVIEW.TABS.OVERVIEW).click();

// Page header has index name
cy.get('main header h1').should('contain.text', indexName);
// check Ingestion Type stat is Crawler
cy.getBySel(INDEX_OVERVIEW.STATS.INGESTION_TYPE).should('contain.text', 'Crawler');

cy.getBySel(INDEX_OVERVIEW.STATS.DOCUMENT_COUNT).should((el) => {
const text = el.text();
const count = parseInt(text.match(/[0-9]+/g), 10);
expect(count).to.gt(0);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { login } from '../../../tasks/login';
import {
CONNECTOR_INDEX,
getIndexRoute,
INDEX_OVERVIEW,
NEW_INDEX_CARD,
NEW_CONNECTOR_PAGE,
ROUTES,
SELECT_CONNECTOR,
SEARCH_INDICES,
} from '../selectors';

describe('Enterprise Search MongoDB connector', () => {
it('succesfully syncs documents with single sync', () => {
// Get configuration information from cypress.env.json
const mongoConfig = Cypress.env('mongo_test');
const indexName = 'cypress-mongodb-' + Math.random();
const baseUrl = Cypress.config().baseUrl;
login();

cy.visit(ROUTES.SEARCH_INDICES_OVERVIEW);
cy.getBySel(SEARCH_INDICES.CREATE_INDEX_BUTTON).click();

cy.url().should('eq', baseUrl + ROUTES.NEW_INDEX);

// select connector
cy.getBySel(NEW_INDEX_CARD.SELECT_CONNECTOR).click();

// we are in correct route
cy.url().should('contain', ROUTES.SELECT_CONNECTOR);

// Select MongoDB from the list
cy.get('#checkableCard-mongodb').should('not.be.selected');
cy.get('#checkableCard-mongodb-details')
.find('a')
.invoke('attr', 'href')
.should('include', 'connectors-mongodb.html');

cy.get('#checkableCard-mongodb').click();

cy.getBySel(SELECT_CONNECTOR.SELECT_AND_CONFIGURE_BUTTON).click();

// Connector URL, mongo selected
cy.url().should('contain', 'service_type=mongodb');

cy.getBySel(NEW_CONNECTOR_PAGE.INDEX_NAME_INPUT).type(indexName);

// create index
cy.getBySel(NEW_CONNECTOR_PAGE.CREATE_BUTTON).click();

// make sure we are in new index route
cy.url().should('contain', getIndexRoute(indexName) + 'configuration');

// Fill in connector configuration
cy.getBySel(CONNECTOR_INDEX.getConfigurationRow('host')).type(mongoConfig.host);
cy.getBySel(CONNECTOR_INDEX.getConfigurationRow('user')).type(mongoConfig.username);
cy.getBySel(CONNECTOR_INDEX.getConfigurationRow('password')).type(mongoConfig.password);
cy.getBySel(CONNECTOR_INDEX.getConfigurationRow('database')).type(mongoConfig.database);
cy.getBySel(CONNECTOR_INDEX.getConfigurationRow('collection')).type(mongoConfig.collection);
cy.getBySel(CONNECTOR_INDEX.SAVE_CONFIG).click();

// Wait until configuration is saved
cy.getBySel(CONNECTOR_INDEX.EDIT_CONFIG);
cy.getBySel(CONNECTOR_INDEX.SET_SCHEDULE_BUTTON).click();

// Scheduling Tab opened
cy.url().should('contain', getIndexRoute(indexName) + 'scheduling');

// Start one time sync
cy.getBySel(CONNECTOR_INDEX.HEADER_SYNC_MENU).click();
cy.getBySel(CONNECTOR_INDEX.HEADER_SYNC_MENU_START).click();

// go to overview tab
cy.getBySel(INDEX_OVERVIEW.TABS.OVERVIEW).click();

cy.getBySel(INDEX_OVERVIEW.STATS.INGESTION_TYPE).should('contain.text', 'Connector');
cy.getBySel(INDEX_OVERVIEW.STATS.CONNECTOR_TYPE).should('contain.text', 'MongoDB');
cy.getBySel(INDEX_OVERVIEW.STATS.INGESTION_STATUS).should('contain.text', 'Configured');
cy.getBySel(INDEX_OVERVIEW.STATS.INGESTION_STATUS).should('contain.text', 'Connected');

// Wait until document count > 0
cy.getBySel(INDEX_OVERVIEW.STATS.DOCUMENT_COUNT).should((el) => {
const text = el.text();
const count = parseInt(text.match(/[0-9]+/g), 10);
expect(count).to.gt(0);
});
});
});
70 changes: 70 additions & 0 deletions x-pack/plugins/enterprise_search/cypress/e2e/content/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export const ROUTES = {
CRAWLER_INDEX: '/app/enterprise_search/content/search_indices/new_index/crawler',
NEW_INDEX: '/app/enterprise_search/content/search_indices/new_index',
SEARCH_INDICES_OVERVIEW: '/app/enterprise_search/content/search_indices/',
SELECT_CONNECTOR: '/app/enterprise_search/content/search_indices/new_index/select_connector',
};

export const SEARCH_INDICES = {
CREATE_INDEX_BUTTON: 'entSearchContent-searchIndices-createButton',
};

export const SELECT_CONNECTOR = {
SELECT_AND_CONFIGURE_BUTTON: 'entSearchContent-connector-selectConnector-selectAndConfigure',
};

export const NEW_CONNECTOR_PAGE = {
CREATE_BUTTON: 'entSearchContent-connector-newIndex-createIndex',
INDEX_NAME_INPUT: 'entSearchContent-connector-newIndex-editName',
};

export const CONNECTOR_INDEX = {
EDIT_CONFIG: 'entSearchContent-connector-configuration-editConfiguration',
HEADER_SYNC_MENU: 'entSearchContent-connector-header-sync-menu',
HEADER_SYNC_MENU_START: 'entSearchContent-connector-header-sync-startSync',
SAVE_CONFIG: 'entSearchContent-connector-configuration-saveConfiguration',
SET_SCHEDULE_BUTTON: 'entSearchContent-connector-configuration-setScheduleAndSync',
getConfigurationRow: (rowkey: string) =>
`entSearchContent-connector-configuration-formrow-${rowkey}`,
};

export const NEW_INDEX_CARD = {
SELECT_CONNECTOR: 'entSearchContent-newIndexCard-button-connector',
SELECT_CRAWLER: 'entSearchContent-newIndexCard-button-crawler',
};

export const CRAWLER_INDEX = {
CRAWL_ALL_DOMAINS: 'entSearchContent-crawler-startCrawlMenu-crawlAllDomains',
CRAWL_DROPDOWN: 'entSearchContent-crawler-startCrawlMenu-menuButton',
CREATE_BUTTON: 'entSearchContent-crawler-newIndex-createIndex',
DOMAIN_MANAGEMENT: {
DOMAIN_BUTTON: 'entSearchContent-crawler-addDomainForm-validate-button',
DOMAIN_INPUT: 'entSearchContent-crawler-addDomainForm-validate-input',
SUBMIT_BUTTON: 'entSearchContent-crawler-addDomain-submitButton',
},
INDEX_NAME_INPUT: 'entSearchContent-crawler-newIndex-editName',
};

export const INDEX_OVERVIEW = {
STATS: {
CONNECTOR_TYPE: 'entSearchContent-indexOverview-totalStats-connectorType',
DOCUMENT_COUNT: 'entSearchContent-indexOverview-totalStats-documentCount',
INGESTION_STATUS: 'entSearchContent-indexOverview-connectorStats-ingestionStatus',
INGESTION_TYPE: 'entSearchContent-indexOverview-totalStats-ingestionType',
},
TABS: {
CRAWLER_SCHEDULER: 'entSearchContent-index-crawler-scheduler-tab',
OVERVIEW: 'entSearchContent-index-overview-tab',
},
};

export const getIndexRoute = (indexName: string) => {
return `/app/enterprise_search/content/search_indices/search-${indexName}/`;
};
9 changes: 8 additions & 1 deletion x-pack/plugins/enterprise_search/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
interface Chainable {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
findBySel(value: string, ...args: any[]): Chainable<any>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getBySel(value: string, ...args: any[]): Chainable<any>;
getKibanaVersion(): Chainable<string>;
}
}
}
Expand All @@ -41,7 +42,13 @@ function getBySel(selector: string, ...args: any[]) {
return cy.get(`[data-test-subj="${selector}"]`, ...args);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function findBySel(selector: string, ...args: any[]) {
return cy.find(`[data-test-subj="${selector}"]`, ...args);
}

Cypress.Commands.add('getBySel', getBySel);
Cypress.Commands.add('findBySel', findBySel);

Cypress.on('uncaught:exception', () => {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export const NewIndexCard: React.FC<NewIndexCardProps> = ({ onSelect, isSelected

return (
<EuiCard
data-test-subj="entSearch-content-newIndexCard-cardBody"
hasBorder
icon={<EuiIcon type={icon} size="xxl" />}
title={title}
Expand All @@ -110,6 +111,7 @@ export const NewIndexCard: React.FC<NewIndexCardProps> = ({ onSelect, isSelected
</>
)}
<EuiButton
data-test-subj={`entSearchContent-newIndexCard-button-${type}`}
fullWidth
onClick={onSelect}
color={isSelected ? 'success' : 'primary'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export const NewSearchIndexTemplate: React.FC<Props> = ({
fullWidth
>
<EuiFieldText
data-test-subj={`entSearchContent-${type}-newIndex-editName`}
data-telemetry-id={`entSearchContent-${type}-newIndex-editName`}
placeholder={i18n.translate(
'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputPlaceholder',
Expand Down Expand Up @@ -253,6 +254,7 @@ export const NewSearchIndexTemplate: React.FC<Props> = ({
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj={`entSearchContent-${type}-newIndex-createIndex`}
data-telemetry-id={`entSearchContent-${type}-newIndex-createIndex`}
fill
isDisabled={!rawName || buttonLoading || formInvalid || disabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export const SelectConnector: React.FC = () => {
<EuiFlexItem grow={false}>
<span>
<EuiButton
data-test-subj="entSearchContent-connector-selectConnector-selectAndConfigure"
data-telemetry-id="entSearchContent-connector-selectConnector-selectAndConfigure"
disabled={!selectedConnector}
fill
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ export const SyncsContextMenu: React.FC = () => {
<EuiLoadingSpinner size="m" />
</EuiFlexItem>
)}
<EuiFlexItem>{getSyncButtonText()}</EuiFlexItem>
<EuiFlexItem data-test-subj={`entSearchContent-${ingestionMethod}-header-sync-menu`}>
{getSyncButtonText()}
</EuiFlexItem>
</EuiFlexGroup>
</EuiButton>
}
Expand All @@ -90,6 +92,7 @@ export const SyncsContextMenu: React.FC = () => {
? []
: [
<EuiContextMenuItem
data-test-subj={`entSearchContent-${ingestionMethod}-header-sync-startSync`}
data-telemetry-id={`entSearchContent-${ingestionMethod}-header-sync-startSync`}
disabled={ingestionStatus === IngestionStatus.INCOMPLETE}
key="Sync"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ export const ConnectorConfiguration: React.FC = () => {
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButtonTo
data-test-subj="entSearchContent-connector-configuration-setScheduleAndSync"
data-telemetry-id="entSearchContent-connector-configuration-setScheduleAndSync"
to={`${generateEncodedPath(SEARCH_INDEX_TAB_PATH, {
indexName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const ConnectorConfigurationConfig: React.FC = ({ children }) => {
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="entSearchContent-connector-configuration-editConfiguration"
data-telemetry-id="entSearchContent-connector-overview-configuration-editConfiguration"
onClick={() => setIsEditing(!isEditing)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,12 @@ export const ConnectorConfigurationForm = () => {
<>
{topSpacing}
<EuiPanel color="subdued" borderRadius="none">
<EuiFormRow label={rowLabel} key={key} helpText={helpText}>
<EuiFormRow
label={rowLabel}
key={key}
helpText={helpText}
data-test-subj={`entSearchContent-connector-configuration-formrow-${key}`}
>
<ConnectorConfigurationField configEntry={configEntry} />
</EuiFormRow>
</EuiPanel>
Expand All @@ -99,7 +104,12 @@ export const ConnectorConfigurationForm = () => {
}

return (
<EuiFormRow label={rowLabel} key={key} helpText={helpText}>
<EuiFormRow
label={rowLabel}
key={key}
helpText={helpText}
data-test-subj={`entSearchContent-connector-configuration-formrow-${key}`}
>
<ConnectorConfigurationField configEntry={configEntry} />
</EuiFormRow>
);
Expand All @@ -108,6 +118,7 @@ export const ConnectorConfigurationForm = () => {
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="entSearchContent-connector-configuration-saveConfiguration"
data-telemetry-id="entSearchContent-connector-configuration-saveConfiguration"
type="submit"
isLoading={status === Status.LOADING}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const ConnectorOverviewPanels: React.FC = () => {
<EuiFlexItem grow={1}>
<EuiPanel color="primary" hasShadow={false} paddingSize="l">
<EuiStat
data-test-subj="entSearchContent-indexOverview-totalStats-documentCount"
titleSize="m"
description={i18n.translate(
'xpack.enterpriseSearch.content.searchIndex.totalStats.documentCountCardLabel',
Expand All @@ -59,7 +60,10 @@ export const ConnectorOverviewPanels: React.FC = () => {
/>
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem grow={1}>
<EuiFlexItem
grow={1}
data-test-subj="entSearchContent-indexOverview-connectorStats-ingestionStatus"
>
{ingestionStatus === IngestionStatus.INCOMPLETE ? (
<EuiLinkTo
to={generateEncodedPath(SEARCH_INDEX_TAB_PATH, {
Expand Down
Loading

0 comments on commit a4953ed

Please sign in to comment.