[APM] e2e tests (#99098)

* adding e2e tests

* adding e2e tests

* adding e2e tests

* fixing ci

* fixing ci

* fixing e2e and jest
This commit is contained in:
Cauê Marcondes 2021-05-04 13:05:52 -04:00 committed by GitHub
parent a17d26132a
commit b8ba6eaa81
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 322 additions and 8 deletions

View file

@ -4,26 +4,45 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import url from 'url';
import archives_metadata from '../../fixtures/es_archiver/archives_metadata';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
const { start, end } = archives_metadata['apm_8.0.0'];
const servicesPath = '/app/apm/services';
const baseUrl = url.format({
pathname: servicesPath,
query: { rangeFrom: start, rangeTo: end },
});
describe('Home page', () => {
before(() => {
esArchiverLoad('apm_8.0.0');
cy.loginAsReadOnlyUser();
});
after(() => {
esArchiverUnload('apm_8.0.0');
});
beforeEach(() => {
cy.loginAsReadOnlyUser();
});
it('Redirects to service page with rangeFrom and rangeTo added to the URL', () => {
const baseUrl = url.format({
pathname: '/app/apm',
query: { rangeFrom: start, rangeTo: end },
});
cy.visit('/app/apm');
cy.visit(baseUrl);
cy.url().should(
'include',
'app/apm/services?rangeFrom=now-15m&rangeTo=now'
);
cy.get('.euiTabs .euiTab-isSelected').contains('Services');
});
it('includes services with only metric documents', () => {
cy.visit(
`${baseUrl}&kuery=not%2520(processor.event%2520%253A%2522transaction%2522%2520)`
);
cy.contains('opbeans-python');
cy.contains('opbeans-java');
cy.contains('opbeans-node');
});
});

View file

@ -0,0 +1,145 @@
/*
* 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 url from 'url';
import archives_metadata from '../../../fixtures/es_archiver/archives_metadata';
import { esArchiverLoad, esArchiverUnload } from '../../../tasks/es_archiver';
const { start, end } = archives_metadata['apm_8.0.0'];
const serviceOverviewPath = '/app/apm/services/kibana/overview';
const baseUrl = url.format({
pathname: serviceOverviewPath,
query: { rangeFrom: start, rangeTo: end },
});
const apisToIntercept = [
{
endpoint: '/api/apm/services/kibana/transactions/charts/latency',
as: 'latencyChartRequest',
},
{
endpoint: '/api/apm/services/kibana/throughput',
as: 'throughputChartRequest',
},
{
endpoint: '/api/apm/services/kibana/transactions/charts/error_rate',
as: 'errorRateChartRequest',
},
{
endpoint:
'/api/apm/services/kibana/transactions/groups/detailed_statistics',
as: 'transactionGroupsDetailedRequest',
},
{
endpoint:
'/api/apm/services/kibana/service_overview_instances/detailed_statistics',
as: 'instancesDetailedRequest',
},
{
endpoint:
'/api/apm/services/kibana/service_overview_instances/main_statistics',
as: 'instancesMainStatisticsRequest',
},
{
endpoint: '/api/apm/services/kibana/error_groups/main_statistics',
as: 'errorGroupsMainStatisticsRequest',
},
{
endpoint: '/api/apm/services/kibana/transaction/charts/breakdown',
as: 'transactonBreakdownRequest',
},
{
endpoint: '/api/apm/services/kibana/transactions/groups/main_statistics',
as: 'transactionsGroupsMainStatisticsRequest',
},
];
describe('Service overview - header filters', () => {
before(() => {
esArchiverLoad('apm_8.0.0');
});
after(() => {
esArchiverUnload('apm_8.0.0');
});
beforeEach(() => {
cy.loginAsReadOnlyUser();
});
describe('Filtering by transaction type', () => {
it('changes url when selecting different value', () => {
cy.visit(baseUrl);
cy.contains('Kibana');
cy.url().should('not.include', 'transactionType');
cy.get('[data-test-subj="headerFilterTransactionType"]').should(
'have.value',
'request'
);
cy.get('[data-test-subj="headerFilterTransactionType"]').select(
'taskManager'
);
cy.url().should('include', 'transactionType=taskManager');
cy.get('[data-test-subj="headerFilterTransactionType"]').should(
'have.value',
'taskManager'
);
});
it('calls APIs with correct transaction type', () => {
apisToIntercept.map(({ endpoint, as }) => {
cy.intercept('GET', endpoint).as(as);
});
cy.visit(baseUrl);
cy.contains('Kibana');
cy.get('[data-test-subj="headerFilterTransactionType"]').should(
'have.value',
'request'
);
cy.expectAPIsToHaveBeenCalledWith({
apisIntercepted: apisToIntercept.map(({ as }) => `@${as}`),
value: 'transactionType=request',
});
cy.get('[data-test-subj="headerFilterTransactionType"]').select(
'taskManager'
);
cy.url().should('include', 'transactionType=taskManager');
cy.get('[data-test-subj="headerFilterTransactionType"]').should(
'have.value',
'taskManager'
);
cy.expectAPIsToHaveBeenCalledWith({
apisIntercepted: apisToIntercept.map(({ as }) => `@${as}`),
value: 'transactionType=taskManager',
});
});
});
describe('Filtering by kuerybar', () => {
it('filters by transaction.name', () => {
cy.visit(
url.format({
pathname: '/app/apm/services/opbeans-java/overview',
query: { rangeFrom: start, rangeTo: end },
})
);
cy.contains('opbeans-java');
cy.get('[data-test-subj="headerFilterKuerybar"]').type('transaction.n');
cy.contains('transaction.name');
cy.get('[data-test-subj="suggestionContainer"]')
.find('li')
.first()
.click();
cy.get('[data-test-subj="headerFilterKuerybar"]').type(':');
cy.get('[data-test-subj="suggestionContainer"]')
.find('li')
.first()
.click();
cy.get('[data-test-subj="suggestionContainer"]').realPress('{enter}');
cy.url().should('include', '&kuery=transaction.name');
});
});
});

View file

@ -0,0 +1,114 @@
/*
* 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 url from 'url';
import archives_metadata from '../../../fixtures/es_archiver/archives_metadata';
import { esArchiverLoad, esArchiverUnload } from '../../../tasks/es_archiver';
const { start, end } = archives_metadata['apm_8.0.0'];
const serviceOverviewPath = '/app/apm/services/opbeans-java/overview';
const baseUrl = url.format({
pathname: serviceOverviewPath,
query: { rangeFrom: start, rangeTo: end },
});
const apisToIntercept = [
{
endpoint:
'/api/apm/services/opbeans-java/service_overview_instances/main_statistics',
as: 'instancesMainRequest',
},
{
endpoint:
'/api/apm/services/opbeans-java/service_overview_instances/detailed_statistics',
as: 'instancesDetailsRequest',
},
{
endpoint:
'/api/apm/services/opbeans-java/service_overview_instances/details/02950c4c5fbb0fda1cc98c47bf4024b473a8a17629db6530d95dcee68bd54c6c',
as: 'instanceDetailsRequest',
},
{
endpoint:
'/api/apm/services/opbeans-java/service_overview_instances/details/02950c4c5fbb0fda1cc98c47bf4024b473a8a17629db6530d95dcee68bd54c6c',
as: 'instanceDetailsRequest',
},
];
describe('Instances table', () => {
beforeEach(() => {
cy.loginAsReadOnlyUser();
});
describe('when data is not loaded', () => {
it('shows empty message', () => {
cy.visit(baseUrl);
cy.contains('opbeans-java');
cy.get('[data-test-subj="serviceInstancesTableContainer"]').contains(
'No items found'
);
});
});
describe('when data is loaded', () => {
before(() => {
esArchiverLoad('apm_8.0.0');
});
after(() => {
esArchiverUnload('apm_8.0.0');
});
const serviceNodeName =
'02950c4c5fbb0fda1cc98c47bf4024b473a8a17629db6530d95dcee68bd54c6c';
it('has data in the table', () => {
cy.visit(baseUrl);
cy.contains('opbeans-java');
cy.contains(serviceNodeName);
});
it('shows instance details', () => {
apisToIntercept.map(({ endpoint, as }) => {
cy.intercept('GET', endpoint).as(as);
});
cy.visit(baseUrl);
cy.contains('opbeans-java');
cy.wait('@instancesMainRequest');
cy.contains(serviceNodeName);
cy.wait('@instancesDetailsRequest');
cy.get(
`[data-test-subj="instanceDetailsButton_${serviceNodeName}"]`
).realClick();
cy.get('[data-test-subj="loadingSpinner"]').should('be.visible');
cy.wait('@instanceDetailsRequest').then(() => {
cy.contains('Service');
});
});
it('shows actions available', () => {
apisToIntercept.map(({ endpoint, as }) => {
cy.intercept('GET', endpoint).as(as);
});
cy.visit(baseUrl);
cy.contains('opbeans-java');
cy.wait('@instancesMainRequest');
cy.contains(serviceNodeName);
cy.wait('@instancesDetailsRequest');
cy.get(
`[data-test-subj="instanceActionsButton_${serviceNodeName}"]`
).realClick();
cy.contains('Pod logs');
cy.contains('Pod metrics');
cy.contains('Container logs');
cy.contains('Container metrics');
cy.contains('Filter overview by instance');
cy.contains('Metrics');
});
});
});

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import 'cypress-real-events/support';
import { Interception } from 'cypress/types/net-stubbing';
Cypress.Commands.add('loginAsReadOnlyUser', () => {
cy.loginAs({ username: 'apm_read_user', password: 'changeme' });
@ -39,3 +40,24 @@ Cypress.Commands.add('changeTimeRange', (value: string) => {
cy.get('[data-test-subj="superDatePickerToggleQuickMenuButton"]').click();
cy.contains(value).click();
});
Cypress.Commands.add(
'expectAPIsToHaveBeenCalledWith',
({
apisIntercepted,
value,
}: {
apisIntercepted: string[];
value: string;
}) => {
cy.wait(apisIntercepted).then((interceptions) => {
if (Array.isArray(interceptions)) {
interceptions.map((interception) => {
expect(interception.request.url).include(value);
});
} else {
expect((interceptions as Interception).request.url).include(value);
}
});
}
);

View file

@ -11,5 +11,9 @@ declare namespace Cypress {
loginAsSuperUser(): void;
loginAs(params: { username: string; password: string }): void;
changeTimeRange(value: string): void;
expectAPIsToHaveBeenCalledWith(params: {
apisIntercepted: string[];
value: string;
}): void;
}
}

View file

@ -234,6 +234,7 @@ export function getColumns({
anchorPosition="leftCenter"
button={
<EuiButtonIcon
data-test-subj={`instanceActionsButton_${instanceItem.serviceNodeName}`}
iconType="boxesHorizontal"
onClick={() =>
toggleRowActionMenu(instanceItem.serviceNodeName)
@ -257,6 +258,7 @@ export function getColumns({
render: (instanceItem: MainStatsServiceInstanceItem) => {
return (
<EuiButtonIcon
data-test-subj={`instanceDetailsButton_${instanceItem.serviceNodeName}`}
onClick={() => toggleRowDetails(instanceItem.serviceNodeName)}
aria-label={
itemIdToExpandedRowMap[instanceItem.serviceNodeName]

View file

@ -138,12 +138,13 @@ export function ServiceOverviewInstancesTable({
</h2>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexItem data-test-subj="serviceInstancesTableContainer">
<TableFetchWrapper status={status}>
<ServiceOverviewTableContainer
isEmptyAndLoading={mainStatsItemCount === 0 && isLoading}
>
<EuiBasicTable
data-test-subj="instancesTable"
loading={isLoading}
items={mainStatsItems}
columns={columns}

View file

@ -72,7 +72,12 @@ class Suggestions extends Component {
});
return (
<List innerRef={(node) => (this.parentNode = node)}>{suggestions}</List>
<List
data-test-subj="suggestionContainer"
innerRef={(node) => (this.parentNode = node)}
>
{suggestions}
</List>
);
}
}

View file

@ -172,6 +172,7 @@ export class Typeahead extends Component {
>
<div style={{ position: 'relative' }}>
<EuiFieldSearch
data-test-subj="headerFilterKuerybar"
fullWidth
style={{
backgroundImage: 'none',

View file

@ -38,6 +38,7 @@ export function TransactionTypeSelect() {
return (
<>
<EuiSelectWithWidth
data-test-subj="headerFilterTransactionType"
onChange={handleChange}
options={options}
value={transactionType}