mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[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:
parent
a17d26132a
commit
b8ba6eaa81
10 changed files with 322 additions and 8 deletions
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,6 +172,7 @@ export class Typeahead extends Component {
|
|||
>
|
||||
<div style={{ position: 'relative' }}>
|
||||
<EuiFieldSearch
|
||||
data-test-subj="headerFilterKuerybar"
|
||||
fullWidth
|
||||
style={{
|
||||
backgroundImage: 'none',
|
||||
|
|
|
@ -38,6 +38,7 @@ export function TransactionTypeSelect() {
|
|||
return (
|
||||
<>
|
||||
<EuiSelectWithWidth
|
||||
data-test-subj="headerFilterTransactionType"
|
||||
onChange={handleChange}
|
||||
options={options}
|
||||
value={transactionType}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue