Skip to content

Commit

Permalink
[RUM Dashboard] Chart breakdowns (elastic#69420)
Browse files Browse the repository at this point in the history
Co-authored-by: Casper Hübertz <[email protected]>
Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
3 people authored and Bamieh committed Jul 1, 2020
1 parent b4d0967 commit ee529c0
Show file tree
Hide file tree
Showing 32 changed files with 1,136 additions and 324 deletions.
32 changes: 32 additions & 0 deletions x-pack/plugins/apm/e2e/cypress/integration/rum_dashboard.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,35 @@ Feature: RUM Dashboard
When the user inspects the real user monitoring tab
Then should redirect to rum dashboard
And should have correct client metrics

Scenario Outline: Rum page filters
When the user filters by "<filterName>"
Then it filters the client metrics
Examples:
| filterName |
| os |
| location |

Scenario: Page load distribution percentiles
Given a user browses the APM UI application for RUM Data
When the user inspects the real user monitoring tab
Then should redirect to rum dashboard
And should display percentile for page load chart

Scenario: Page load distribution chart tooltip
Given a user browses the APM UI application for RUM Data
When the user inspects the real user monitoring tab
Then should redirect to rum dashboard
And should display tooltip on hover

Scenario: Page load distribution chart legends
Given a user browses the APM UI application for RUM Data
When the user inspects the real user monitoring tab
Then should redirect to rum dashboard
And should display chart legend

Scenario: Breakdown filter
Given a user click page load breakdown filter
When the user selected the breakdown
Then breakdown series should appear in chart

21 changes: 20 additions & 1 deletion x-pack/plugins/apm/e2e/cypress/integration/snapshots.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,28 @@ module.exports = {
},
"RUM Dashboard": {
"Client metrics": {
"1": "62",
"1": "62 ",
"2": "0.07 sec",
"3": "0.01 sec"
},
"Rum page filters (example #1)": {
"1": "15 ",
"2": "0.07 sec",
"3": "0.01 sec"
},
"Rum page filters (example #2)": {
"1": "35 ",
"2": "0.07 sec",
"3": "0.01 sec"
},
"Page load distribution percentiles": {
"1": "50th",
"2": "75th",
"3": "90th",
"4": "95th"
},
"Page load distribution chart legends": {
"1": "Overall"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { Given, When, Then } from 'cypress-cucumber-preprocessor/steps';

/** The default time in ms to wait for a Cypress command to complete */
export const DEFAULT_TIMEOUT = 60 * 1000;

Given(`a user click page load breakdown filter`, () => {
// wait for all loading to finish
cy.get('kbnLoadingIndicator').should('not.be.visible');
cy.get('.euiStat__title-isLoading').should('not.be.visible');
const breakDownBtn = cy.get('[data-cy=breakdown-popover_pageLoad]');
breakDownBtn.click();
});

When(`the user selected the breakdown`, () => {
cy.get('[data-cy=filter-breakdown-item_Browser]').click();
// click outside popover to close it
cy.get('[data-cy=pageLoadDist]').click();
});

Then(`breakdown series should appear in chart`, () => {
cy.get('.euiLoadingChart').should('not.be.visible');
cy.get('div.echLegendItem__label[title=Chrome] ')
.invoke('text')
.should('eq', 'Chrome');
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { Given, When, Then } from 'cypress-cucumber-preprocessor/steps';
import { loginAndWaitForPage } from '../../integration/helpers';
import { loginAndWaitForPage } from '../../../integration/helpers';

/** The default time in ms to wait for a Cypress command to complete */
export const DEFAULT_TIMEOUT = 60 * 1000;
Expand Down Expand Up @@ -41,3 +41,46 @@ Then(`should have correct client metrics`, () => {

cy.get(clientMetrics).eq(0).invoke('text').snapshot();
});

Then(`should display percentile for page load chart`, () => {
const pMarkers = '[data-cy=percentile-markers] span';

cy.get('.euiLoadingChart').should('be.visible');

// wait for all loading to finish
cy.get('kbnLoadingIndicator').should('not.be.visible');
cy.get('.euiStat__title-isLoading').should('not.be.visible');

cy.get(pMarkers).eq(0).invoke('text').snapshot();

cy.get(pMarkers).eq(1).invoke('text').snapshot();

cy.get(pMarkers).eq(2).invoke('text').snapshot();

cy.get(pMarkers).eq(3).invoke('text').snapshot();
});

Then(`should display chart legend`, () => {
const chartLegend = 'div.echLegendItem__label';

// wait for all loading to finish
cy.get('kbnLoadingIndicator').should('not.be.visible');
cy.get('.euiLoadingChart').should('not.be.visible');

cy.get(chartLegend).eq(0).invoke('text').snapshot();
});

Then(`should display tooltip on hover`, () => {
cy.get('.euiLoadingChart').should('not.be.visible');

const pMarkers = '[data-cy=percentile-markers] span.euiToolTipAnchor';

// wait for all loading to finish
cy.get('kbnLoadingIndicator').should('not.be.visible');
cy.get('.euiLoadingChart').should('not.be.visible');

const marker = cy.get(pMarkers).eq(0);
marker.invoke('show');
marker.trigger('mouseover', { force: true });
cy.get('span[data-cy=percentileTooltipTitle]').should('be.visible');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { When, Then } from 'cypress-cucumber-preprocessor/steps';

When(/^the user filters by "([^"]*)"$/, (filterName) => {
// wait for all loading to finish
cy.get('kbnLoadingIndicator').should('not.be.visible');
cy.get('.euiStat__title-isLoading').should('not.be.visible');
cy.get(`#local-filter-${filterName}`).click();

if (filterName === 'os') {
cy.get('button.euiSelectableListItem[title="Mac OS X"]').click();
} else {
cy.get('button.euiSelectableListItem[title="DE"]').click();
}
cy.get('[data-cy=applyFilter]').click();
});

Then(`it filters the client metrics`, () => {
const clientMetrics = '[data-cy=client-metrics] .euiStat__title';

// wait for all loading to finish
cy.get('kbnLoadingIndicator').should('not.be.visible');
cy.get('.euiStat__title-isLoading').should('not.be.visible');

cy.get(clientMetrics).eq(2).invoke('text').snapshot();

cy.get(clientMetrics).eq(1).invoke('text').snapshot();

cy.get(clientMetrics).eq(0).invoke('text').snapshot();

cy.get('[data-cy=clearFilters]').click();
});
17 changes: 15 additions & 2 deletions x-pack/plugins/apm/e2e/ingest-data/replay.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const pLimit = require('p-limit');
const pRetry = require('p-retry');
const { argv } = require('yargs');
const ora = require('ora');
const userAgents = require('./user_agents');
const userIps = require('./rum_ips');

const APM_SERVER_URL = argv.serverUrl;
const SECRET_TOKEN = argv.secretToken;
Expand Down Expand Up @@ -66,14 +68,23 @@ function incrementSpinnerCount({ success }) {

spinner.text = `Remaining: ${remaining}. Succeeded: ${requestProgress.succeeded}. Failed: ${requestProgress.failed}.`;
}

let iterIndex = 0;
async function insertItem(item) {
try {
const url = `${APM_SERVER_URL}${item.url}`;
const headers = {
'content-type': 'application/x-ndjson',
};

if (item.url === '/intake/v2/rum/events') {
if (iterIndex === userAgents.length) {
iterIndex = 0;
}
headers['User-Agent'] = userAgents[iterIndex];
headers['X-Forwarded-For'] = userIps[iterIndex];
iterIndex++;
}

if (SECRET_TOKEN) {
headers.Authorization = `Bearer ${SECRET_TOKEN}`;
}
Expand Down Expand Up @@ -113,7 +124,9 @@ async function init() {
items.map(async (item) => {
try {
// retry 5 times with exponential backoff
await pRetry(() => limit(() => insertItem(item)), { retries: 5 });
await pRetry(() => limit(() => insertItem(item)), {
retries: 5,
});
incrementSpinnerCount({ success: true });
} catch (e) {
incrementSpinnerCount({ success: false });
Expand Down
19 changes: 19 additions & 0 deletions x-pack/plugins/apm/e2e/ingest-data/rum_ips.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

const IPS = [
'89.191.86.214', // check24.de
'167.40.79.24', // canada.ca
'151.101.130.217', // elastic.co
'185.143.68.17',
'151.101.130.217',
'185.143.68.17',
'185.143.68.17',
'151.101.130.217',
'185.143.68.17',
];

module.exports = IPS;
23 changes: 23 additions & 0 deletions x-pack/plugins/apm/e2e/ingest-data/user_agents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

/* eslint-disable no-console */

/* eslint-disable import/no-extraneous-dependencies */

const UserAgents = [
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36',
'Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36',
'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/69.0.3497.105 Mobile/15E148 Safari/605.1',
'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1',
'Mozilla/5.0 (Linux; Android 7.0; Pixel C Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36',
'Mozilla/5.0 (X11; CrOS x86_64 8172.45.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.64 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1',
'Mozilla/5.0 (CrKey armv7l 1.5.16041) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.0 Safari/537.36',
];

module.exports = UserAgents;
4 changes: 2 additions & 2 deletions x-pack/plugins/apm/public/components/app/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { ServiceOverview } from '../ServiceOverview';
import { TraceOverview } from '../TraceOverview';
import { RumOverview } from '../RumDashboard';
import { RumOverviewLink } from '../../shared/Links/apm/RumOverviewLink';
import { EndUserExperienceLabel } from '../RumDashboard/translations';
import { I18LABELS } from '../RumDashboard/translations';

function getHomeTabs({
serviceMapEnabled = true,
Expand Down Expand Up @@ -111,7 +111,7 @@ export function Home({ tab }: Props) {
<EuiTitle size="l">
<h1>
{selectedTab.name === 'rum-overview'
? EndUserExperienceLabel
? I18LABELS.endUserExperience
: 'APM'}
</h1>
</EuiTitle>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';
import { BreakdownGroup } from './BreakdownGroup';
import { BreakdownItem } from '../../../../../typings/ui_filters';
import {
CLIENT_GEO_COUNTRY_ISO_CODE,
USER_AGENT_DEVICE,
USER_AGENT_NAME,
USER_AGENT_OS,
} from '../../../../../common/elasticsearch_fieldnames';

interface Props {
id: string;
selectedBreakdowns: BreakdownItem[];
onBreakdownChange: (values: BreakdownItem[]) => void;
}

export const BreakdownFilter = ({
id,
selectedBreakdowns,
onBreakdownChange,
}: Props) => {
const categories: BreakdownItem[] = [
{
name: 'Browser',
type: 'category',
count: 0,
selected: selectedBreakdowns.some(({ name }) => name === 'Browser'),
fieldName: USER_AGENT_NAME,
},
{
name: 'OS',
type: 'category',
count: 0,
selected: selectedBreakdowns.some(({ name }) => name === 'OS'),
fieldName: USER_AGENT_OS,
},
{
name: 'Device',
type: 'category',
count: 0,
selected: selectedBreakdowns.some(({ name }) => name === 'Device'),
fieldName: USER_AGENT_DEVICE,
},
{
name: 'Location',
type: 'category',
count: 0,
selected: selectedBreakdowns.some(({ name }) => name === 'Location'),
fieldName: CLIENT_GEO_COUNTRY_ISO_CODE,
},
];

return (
<BreakdownGroup
id={id}
items={categories}
onChange={(selValues: BreakdownItem[]) => {
onBreakdownChange(selValues);
}}
/>
);
};
Loading

0 comments on commit ee529c0

Please sign in to comment.