[APM] Creating e2e test for Comparison Feature (#98291) (#99066)

* creating e2e test comparison feature

* fixing test

* comparison e2e tests

* addressing comments

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2021-05-03 14:05:29 -04:00 committed by GitHub
parent 8f2cb58b70
commit e28e63cdfa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 270 additions and 35 deletions

View file

@ -362,11 +362,11 @@
"resize-observer-polyfill": "^1.5.0",
"rison-node": "1.0.2",
"rxjs": "^6.5.5",
"safe-squel": "^5.12.5",
"seedrandom": "^3.0.5",
"semver": "^7.3.2",
"set-value": "^3.0.2",
"source-map-support": "^0.5.19",
"safe-squel": "^5.12.5",
"stats-lite": "^2.2.0",
"strip-ansi": "^6.0.0",
"style-it": "^2.1.3",
@ -668,6 +668,7 @@
"cypress-multi-reporters": "^1.4.0",
"cypress-pipe": "^2.0.0",
"cypress-promise": "^1.1.0",
"cypress-real-events": "^1.4.0",
"debug": "^2.6.9",
"del-cli": "^3.0.1",
"delete-empty": "^2.0.0",

View file

@ -1,21 +0,0 @@
/*
* 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.
*/
describe('Home page', () => {
before(() => {
cy.loginAsSuperUser();
});
it('Redirects to service page with rangeFrom and rangeTo added to the URL', () => {
cy.visit('/app/apm');
cy.url().should(
'include',
'app/apm/services?rangeFrom=now-15m&rangeTo=now'
);
cy.get('.euiTabs .euiTab-isSelected').contains('Services');
});
});

View file

@ -0,0 +1,29 @@
/*
* 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'];
describe('Home page', () => {
before(() => {
esArchiverLoad('apm_8.0.0');
cy.loginAsReadOnlyUser();
});
after(() => {
esArchiverUnload('apm_8.0.0');
});
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(baseUrl);
cy.get('.euiTabs .euiTab-isSelected').contains('Services');
});
});

View file

@ -0,0 +1,203 @@
/*
* 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 moment from 'moment';
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/transactions/charts/latency',
as: 'latencyChartRequest',
},
{
endpoint: '/api/apm/services/opbeans-java/throughput',
as: 'throughputChartRequest',
},
{
endpoint: '/api/apm/services/opbeans-java/transactions/charts/error_rate',
as: 'errorRateChartRequest',
},
{
endpoint:
'/api/apm/services/opbeans-java/transactions/groups/detailed_statistics',
as: 'transactionGroupsDetailedRequest',
},
{
endpoint: '/api/apm/services/opbeans-java/error_groups/detailed_statistics',
as: 'errorGroupsDetailedRequest',
},
{
endpoint:
'/api/apm/services/opbeans-java/service_overview_instances/detailed_statistics',
as: 'instancesDetailedRequest',
},
];
describe('Service overview: Time Comparison', () => {
before(() => {
esArchiverLoad('apm_8.0.0');
});
after(() => {
esArchiverUnload('apm_8.0.0');
});
beforeEach(() => {
cy.loginAsReadOnlyUser();
});
it('enables by default the time comparison feature with Last 24 hours selected', () => {
cy.visit(serviceOverviewPath);
cy.url().should('include', 'comparisonEnabled=true&comparisonType=day');
});
describe('when comparison is toggled off', () => {
it('disables select box', () => {
cy.visit(baseUrl);
cy.contains('opbeans-java');
// Comparison is enabled by default
cy.get('[data-test-subj="comparisonSelect"]').should('be.enabled');
// toggles off comparison
cy.contains('Comparison').click();
cy.get('[data-test-subj="comparisonSelect"]').should('be.disabled');
});
it('calls APIs without comparison time range', () => {
apisToIntercept.map(({ endpoint, as }) => {
cy.intercept('GET', endpoint).as(as);
});
cy.visit(baseUrl);
cy.contains('opbeans-java');
cy.get('[data-test-subj="comparisonSelect"]').should('be.enabled');
const comparisonStartEnd =
'comparisonStart=2020-12-08T13%3A26%3A03.865Z&comparisonEnd=2020-12-08T13%3A57%3A00.000Z';
// When the page loads it fetches all APIs with comparison time range
cy.wait(apisToIntercept.map(({ as }) => `@${as}`)).then(
(interceptions) => {
interceptions.map((interception) => {
expect(interception.request.url).include(comparisonStartEnd);
});
}
);
// toggles off comparison
cy.contains('Comparison').click();
cy.get('[data-test-subj="comparisonSelect"]').should('be.disabled');
// When comparison is disabled APIs are called withou comparison time range
cy.wait(apisToIntercept.map(({ as }) => `@${as}`)).then(
(interceptions) => {
interceptions.map((interception) => {
expect(interception.request.url).not.include(comparisonStartEnd);
});
}
);
});
});
it('changes comparison type', () => {
apisToIntercept.map(({ endpoint, as }) => {
cy.intercept('GET', endpoint).as(as);
});
cy.visit(serviceOverviewPath);
cy.contains('opbeans-java');
// opens the page with "Day before" selected
cy.get('[data-test-subj="comparisonSelect"]').should('have.value', 'day');
// selects another comparison type
cy.get('[data-test-subj="comparisonSelect"]').select('week');
cy.get('[data-test-subj="comparisonSelect"]').should('have.value', 'week');
});
it('changes comparison type when a new time range is selected', () => {
cy.visit(serviceOverviewPath);
cy.contains('opbeans-java');
// Time comparison default value
cy.get('[data-test-subj="comparisonSelect"]').should('have.value', 'day');
cy.contains('Day before');
cy.contains('Week before');
cy.changeTimeRange('Today');
cy.get('[data-test-subj="comparisonSelect"]').should(
'have.value',
'period'
);
cy.get('[data-test-subj="comparisonSelect"]').should(
'not.contain.text',
'Day before'
);
cy.get('[data-test-subj="comparisonSelect"]').should(
'not.contain.text',
'Week before'
);
cy.changeTimeRange('Last 24 hours');
cy.get('[data-test-subj="comparisonSelect"]').should('have.value', 'day');
cy.contains('Day before');
cy.contains('Week before');
cy.changeTimeRange('Last 7 days');
cy.get('[data-test-subj="comparisonSelect"]').should('have.value', 'week');
cy.get('[data-test-subj="comparisonSelect"]').should(
'contain.text',
'Week before'
);
cy.get('[data-test-subj="comparisonSelect"]').should(
'not.contain.text',
'Day before'
);
cy.contains('Week before');
cy.changeTimeRange('Last 30 days');
cy.get('[data-test-subj="comparisonSelect"]').should(
'have.value',
'period'
);
cy.get('[data-test-subj="comparisonSelect"]').should(
'not.contain.text',
'Day before'
);
cy.get('[data-test-subj="comparisonSelect"]').should(
'not.contain.text',
'Week before'
);
});
it('hovers over throughput chart shows previous and current period', () => {
apisToIntercept.map(({ endpoint, as }) => {
cy.intercept('GET', endpoint).as(as);
});
cy.visit(
url.format({
pathname: serviceOverviewPath,
query: {
rangeFrom: moment(end).subtract(15, 'minutes').toISOString(),
rangeTo: end,
},
})
);
cy.contains('opbeans-java');
cy.wait('@throughputChartRequest');
cy.get('[data-test-subj="throughput"]')
.get('#echHighlighterClipPath__throughput')
.realHover({ position: 'center' });
cy.contains('Previous period');
cy.contains('0 tpm');
cy.contains('Throughput');
cy.contains('0 tpm');
});
});

View file

@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import 'cypress-real-events/support';
Cypress.Commands.add('loginAsReadOnlyUser', () => {
cy.loginAs({ username: 'apm_read_user', password: 'changeme' });
@ -33,3 +34,8 @@ Cypress.Commands.add(
});
}
);
Cypress.Commands.add('changeTimeRange', (value: string) => {
cy.get('[data-test-subj="superDatePickerToggleQuickMenuButton"]').click();
cy.contains(value).click();
});

View file

@ -10,5 +10,6 @@ declare namespace Cypress {
loginAsReadOnlyUser(): void;
loginAsSuperUser(): void;
loginAs(params: { username: string; password: string }): void;
changeTimeRange(value: string): void;
}
}

View file

@ -7,25 +7,19 @@
export const esArchiverLoad = (folder: string) => {
cy.exec(
`node ../../../../scripts/es_archiver load ${folder} --dir ./cypress/fixtures/es_archiver --config ../../../test/functional/config.js --es-url ${Cypress.env(
'ELASTICSEARCH_URL'
)} --kibana-url ${Cypress.config().baseUrl}`
`node ../../../../scripts/es_archiver load ${folder} --dir ./cypress/fixtures/es_archiver --config ../../../test/functional/config.js`
);
};
export const esArchiverUnload = (folder: string) => {
cy.exec(
`node ../../../../scripts/es_archiver unload ${folder} --dir ./cypress/fixtures/es_archiver --config ../../../test/functional/config.js --es-url ${Cypress.env(
'ELASTICSEARCH_URL'
)} --kibana-url ${Cypress.config().baseUrl}`
`node ../../../../scripts/es_archiver unload ${folder} --dir ./cypress/fixtures/es_archiver --config ../../../test/functional/config.js`
);
};
export const esArchiverResetKibana = () => {
cy.exec(
`node ../../../../scripts/es_archiver empty-kibana-index --config ../../../test/functional/config.js --es-url ${Cypress.env(
'ELASTICSEARCH_URL'
)} --kibana-url ${Cypress.config().baseUrl}`,
`node ../../../../scripts/es_archiver empty-kibana-index --config ../../../test/functional/config.js`,
{ failOnNonZeroExit: false }
);
};

View file

@ -9,7 +9,8 @@
"compilerOptions": {
"types": [
"cypress",
"node"
"node",
"cypress-real-events"
]
}
}

View file

@ -15,9 +15,16 @@ interface Props {
status: FETCH_STATUS;
height: number;
children: React.ReactNode;
id?: string;
}
export function ChartContainer({ children, height, status, hasData }: Props) {
export function ChartContainer({
children,
height,
status,
hasData,
id,
}: Props) {
if (!hasData && status === FETCH_STATUS.LOADING) {
return <LoadingChartPlaceholder height={height} />;
}
@ -26,7 +33,11 @@ export function ChartContainer({ children, height, status, hasData }: Props) {
return <FailedChartPlaceholder height={height} />;
}
return <div style={{ height }}>{children}</div>;
return (
<div style={{ height }} data-test-subj={id}>
{children}
</div>
);
}
function LoadingChartPlaceholder({ height }: { height: number }) {

View file

@ -98,7 +98,12 @@ export function TimeseriesChart({
const xDomain = isEmpty ? { min: 0, max: 1 } : { min, max };
return (
<ChartContainer hasData={!isEmpty} height={height} status={fetchStatus}>
<ChartContainer
hasData={!isEmpty}
height={height}
status={fetchStatus}
id={id}
>
<Chart ref={chartRef} id={id}>
<Settings
onBrushEnd={({ x }) => onBrushEnd({ x, history })}

View file

@ -10665,6 +10665,11 @@ cypress-promise@^1.1.0:
resolved "https://registry.yarnpkg.com/cypress-promise/-/cypress-promise-1.1.0.tgz#f2d66965945fe198431aaf692d5157cea9d47b25"
integrity sha512-DhIf5PJ/a0iY+Yii6n7Rbwq+9TJxU4pupXYzf9mZd8nPG0AzQrj9i+pqINv4xbI2EV1p+PKW3maCkR7oPG4GrA==
cypress-real-events@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.4.0.tgz#39575031a4020581e0bbf105d7a306ee57d94f48"
integrity sha512-1s4BQN1D++vFSuaad0qKsWcoApM5tQqPBFyDJEa6JeCZIsAdgMdGLuKi5QNIdl5KTJix0jxglzFJAThyz3borQ==
cypress@^6.8.0:
version "6.8.0"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-6.8.0.tgz#8338f39212a8f71e91ff8c017a1b6e22d823d8c1"