From ea4ff59448b0b5cc53697792b00c853d8a5026ad Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 23 Nov 2020 14:02:45 +0100 Subject: [PATCH] [UX]Page load chart breakdown tooltip formatting (#83627) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../PageLoadDistribution/BreakdownSeries.tsx | 2 + .../rum_client/get_page_load_distribution.ts | 19 +- .../lib/rum_client/get_pl_dist_breakdown.ts | 16 +- .../csm/__snapshots__/page_load_dist.snap | 824 ++++++++++++++++++ .../trial/tests/csm/page_load_dist.ts | 64 ++ .../apm_api_integration/trial/tests/index.ts | 1 + 6 files changed, 916 insertions(+), 10 deletions(-) create mode 100644 x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap create mode 100644 x-pack/test/apm_api_integration/trial/tests/csm/page_load_dist.ts diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/BreakdownSeries.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/BreakdownSeries.tsx index f348aca495c71..147f2e495066a 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/BreakdownSeries.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/BreakdownSeries.tsx @@ -6,6 +6,7 @@ import { CurveType, Fit, LineSeries, ScaleType } from '@elastic/charts'; import React, { useEffect } from 'react'; +import numeral from '@elastic/numeral'; import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT, @@ -63,6 +64,7 @@ export function BreakdownSeries({ sortIndex === 0 ? 0 : sortIndex + 1 ] } + tickFormat={(d) => numeral(d).format('0.0') + ' %'} /> ))} diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts b/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts index 225afff2818ab..781209d10034d 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts @@ -15,6 +15,17 @@ export function microToSec(val: number) { return Math.round((val / MICRO_TO_SEC + Number.EPSILON) * 100) / 100; } +export function removeZeroesFromTail( + distData: Array<{ x: number; y: number }> +) { + if (distData.length > 0) { + while (distData[distData.length - 1].y === 0) { + distData.pop(); + } + } + return distData; +} + export const getPLDChartSteps = ({ maxDuration, minDuration, @@ -132,18 +143,14 @@ export async function getPageLoadDistribution({ } // calculate the diff to get actual page load on specific duration value - const pageDist = pageDistVals.map(({ key, value }, index: number, arr) => { + let pageDist = pageDistVals.map(({ key, value }, index: number, arr) => { return { x: microToSec(key), y: index === 0 ? value : value - arr[index - 1].value, }; }); - if (pageDist.length > 0) { - while (pageDist[pageDist.length - 1].y === 0) { - pageDist.pop(); - } - } + pageDist = removeZeroesFromTail(pageDist); Object.entries(durPercentiles?.values ?? {}).forEach(([key, val]) => { if (durPercentiles?.values?.[key]) { diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts b/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts index e2ec59d232b21..f77165b6e76fd 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts @@ -19,6 +19,7 @@ import { getPLDChartSteps, MICRO_TO_SEC, microToSec, + removeZeroesFromTail, } from './get_page_load_distribution'; export const getBreakdownField = (breakdown: string) => { @@ -95,14 +96,21 @@ export const getPageLoadDistBreakdown = async ({ const pageDistBreakdowns = aggregations?.breakdowns.buckets; return pageDistBreakdowns?.map(({ key, page_dist: pageDist }) => { - return { - name: String(key), - data: pageDist.values?.map(({ key: pKey, value }, index: number, arr) => { + let seriesData = pageDist.values?.map( + ({ key: pKey, value }, index: number, arr) => { return { x: microToSec(pKey), y: index === 0 ? value : value - arr[index - 1].value, }; - }), + } + ); + + // remove 0 values from tail + seriesData = removeZeroesFromTail(seriesData); + + return { + name: String(key), + data: seriesData, }; }); }; diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap b/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap new file mode 100644 index 0000000000000..4bf242d8f9b6d --- /dev/null +++ b/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap @@ -0,0 +1,824 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UX page load dist when there is data returns page load distribution 1`] = ` +Object { + "maxDuration": 54.46, + "minDuration": 0, + "pageLoadDistribution": Array [ + Object { + "x": 0, + "y": 0, + }, + Object { + "x": 0.5, + "y": 0, + }, + Object { + "x": 1, + "y": 0, + }, + Object { + "x": 1.5, + "y": 0, + }, + Object { + "x": 2, + "y": 0, + }, + Object { + "x": 2.5, + "y": 0, + }, + Object { + "x": 3, + "y": 16.6666666666667, + }, + Object { + "x": 3.5, + "y": 0, + }, + Object { + "x": 4, + "y": 0, + }, + Object { + "x": 4.5, + "y": 0, + }, + Object { + "x": 5, + "y": 50, + }, + Object { + "x": 5.5, + "y": 0, + }, + Object { + "x": 6, + "y": 0, + }, + Object { + "x": 6.5, + "y": 0, + }, + Object { + "x": 7, + "y": 0, + }, + Object { + "x": 7.5, + "y": 0, + }, + Object { + "x": 8, + "y": 0, + }, + Object { + "x": 8.5, + "y": 0, + }, + Object { + "x": 9, + "y": 0, + }, + Object { + "x": 9.5, + "y": 0, + }, + Object { + "x": 10, + "y": 0, + }, + Object { + "x": 10.5, + "y": 0, + }, + Object { + "x": 11, + "y": 0, + }, + Object { + "x": 11.5, + "y": 0, + }, + Object { + "x": 12, + "y": 0, + }, + Object { + "x": 12.5, + "y": 0, + }, + Object { + "x": 13, + "y": 0, + }, + Object { + "x": 13.5, + "y": 0, + }, + Object { + "x": 14, + "y": 0, + }, + Object { + "x": 14.5, + "y": 0, + }, + Object { + "x": 15, + "y": 0, + }, + Object { + "x": 15.5, + "y": 0, + }, + Object { + "x": 16, + "y": 0, + }, + Object { + "x": 16.5, + "y": 0, + }, + Object { + "x": 17, + "y": 0, + }, + Object { + "x": 17.5, + "y": 0, + }, + Object { + "x": 18, + "y": 0, + }, + Object { + "x": 18.5, + "y": 0, + }, + Object { + "x": 19, + "y": 0, + }, + Object { + "x": 19.5, + "y": 0, + }, + Object { + "x": 20, + "y": 0, + }, + Object { + "x": 20.5, + "y": 0, + }, + Object { + "x": 21, + "y": 0, + }, + Object { + "x": 21.5, + "y": 0, + }, + Object { + "x": 22, + "y": 0, + }, + Object { + "x": 22.5, + "y": 0, + }, + Object { + "x": 23, + "y": 0, + }, + Object { + "x": 23.5, + "y": 0, + }, + Object { + "x": 24, + "y": 0, + }, + Object { + "x": 24.5, + "y": 0, + }, + Object { + "x": 25, + "y": 0, + }, + Object { + "x": 25.5, + "y": 0, + }, + Object { + "x": 26, + "y": 0, + }, + Object { + "x": 26.5, + "y": 0, + }, + Object { + "x": 27, + "y": 0, + }, + Object { + "x": 27.5, + "y": 0, + }, + Object { + "x": 28, + "y": 0, + }, + Object { + "x": 28.5, + "y": 0, + }, + Object { + "x": 29, + "y": 0, + }, + Object { + "x": 29.5, + "y": 0, + }, + Object { + "x": 30, + "y": 0, + }, + Object { + "x": 30.5, + "y": 0, + }, + Object { + "x": 31, + "y": 0, + }, + Object { + "x": 31.5, + "y": 0, + }, + Object { + "x": 32, + "y": 0, + }, + Object { + "x": 32.5, + "y": 0, + }, + Object { + "x": 33, + "y": 0, + }, + Object { + "x": 33.5, + "y": 0, + }, + Object { + "x": 34, + "y": 0, + }, + Object { + "x": 34.5, + "y": 0, + }, + Object { + "x": 35, + "y": 0, + }, + Object { + "x": 35.5, + "y": 0, + }, + Object { + "x": 36, + "y": 0, + }, + Object { + "x": 36.5, + "y": 0, + }, + Object { + "x": 37, + "y": 0, + }, + Object { + "x": 37.5, + "y": 16.6666666666667, + }, + Object { + "x": 38, + "y": 0, + }, + Object { + "x": 38.5, + "y": 0, + }, + Object { + "x": 39, + "y": 0, + }, + Object { + "x": 39.5, + "y": 0, + }, + Object { + "x": 40, + "y": 0, + }, + Object { + "x": 40.5, + "y": 0, + }, + Object { + "x": 41, + "y": 0, + }, + Object { + "x": 41.5, + "y": 0, + }, + Object { + "x": 42, + "y": 0, + }, + Object { + "x": 42.5, + "y": 0, + }, + Object { + "x": 43, + "y": 0, + }, + Object { + "x": 43.5, + "y": 0, + }, + Object { + "x": 44, + "y": 0, + }, + Object { + "x": 44.5, + "y": 0, + }, + Object { + "x": 45, + "y": 0, + }, + Object { + "x": 45.5, + "y": 0, + }, + Object { + "x": 46, + "y": 0, + }, + Object { + "x": 46.5, + "y": 0, + }, + Object { + "x": 47, + "y": 0, + }, + Object { + "x": 47.5, + "y": 0, + }, + Object { + "x": 48, + "y": 0, + }, + Object { + "x": 48.5, + "y": 0, + }, + Object { + "x": 49, + "y": 0, + }, + Object { + "x": 49.5, + "y": 0, + }, + Object { + "x": 50, + "y": 0, + }, + Object { + "x": 50.5, + "y": 0, + }, + Object { + "x": 51, + "y": 0, + }, + Object { + "x": 51.5, + "y": 0, + }, + Object { + "x": 52, + "y": 0, + }, + Object { + "x": 52.5, + "y": 0, + }, + Object { + "x": 53, + "y": 0, + }, + Object { + "x": 53.5, + "y": 0, + }, + Object { + "x": 54, + "y": 0, + }, + Object { + "x": 54.5, + "y": 16.6666666666667, + }, + ], + "percentiles": Object { + "50.0": 4.88, + "75.0": 37.09, + "90.0": 37.09, + "95.0": 54.46, + "99.0": 54.46, + }, +} +`; + +exports[`UX page load dist when there is data returns page load distribution with breakdown 1`] = ` +Array [ + Object { + "data": Array [ + Object { + "x": 0, + "y": 0, + }, + Object { + "x": 0.5, + "y": 0, + }, + Object { + "x": 1, + "y": 0, + }, + Object { + "x": 1.5, + "y": 0, + }, + Object { + "x": 2, + "y": 0, + }, + Object { + "x": 2.5, + "y": 0, + }, + Object { + "x": 3, + "y": 25, + }, + Object { + "x": 3.5, + "y": 0, + }, + Object { + "x": 4, + "y": 0, + }, + Object { + "x": 4.5, + "y": 0, + }, + Object { + "x": 5, + "y": 25, + }, + Object { + "x": 5.5, + "y": 0, + }, + Object { + "x": 6, + "y": 0, + }, + Object { + "x": 6.5, + "y": 0, + }, + Object { + "x": 7, + "y": 0, + }, + Object { + "x": 7.5, + "y": 0, + }, + Object { + "x": 8, + "y": 0, + }, + Object { + "x": 8.5, + "y": 0, + }, + Object { + "x": 9, + "y": 0, + }, + Object { + "x": 9.5, + "y": 0, + }, + Object { + "x": 10, + "y": 0, + }, + Object { + "x": 10.5, + "y": 0, + }, + Object { + "x": 11, + "y": 0, + }, + Object { + "x": 11.5, + "y": 0, + }, + Object { + "x": 12, + "y": 0, + }, + Object { + "x": 12.5, + "y": 0, + }, + Object { + "x": 13, + "y": 0, + }, + Object { + "x": 13.5, + "y": 0, + }, + Object { + "x": 14, + "y": 0, + }, + Object { + "x": 14.5, + "y": 0, + }, + Object { + "x": 15, + "y": 0, + }, + Object { + "x": 15.5, + "y": 0, + }, + Object { + "x": 16, + "y": 0, + }, + Object { + "x": 16.5, + "y": 0, + }, + Object { + "x": 17, + "y": 0, + }, + Object { + "x": 17.5, + "y": 0, + }, + Object { + "x": 18, + "y": 0, + }, + Object { + "x": 18.5, + "y": 0, + }, + Object { + "x": 19, + "y": 0, + }, + Object { + "x": 19.5, + "y": 0, + }, + Object { + "x": 20, + "y": 0, + }, + Object { + "x": 20.5, + "y": 0, + }, + Object { + "x": 21, + "y": 0, + }, + Object { + "x": 21.5, + "y": 0, + }, + Object { + "x": 22, + "y": 0, + }, + Object { + "x": 22.5, + "y": 0, + }, + Object { + "x": 23, + "y": 0, + }, + Object { + "x": 23.5, + "y": 0, + }, + Object { + "x": 24, + "y": 0, + }, + Object { + "x": 24.5, + "y": 0, + }, + Object { + "x": 25, + "y": 0, + }, + Object { + "x": 25.5, + "y": 0, + }, + Object { + "x": 26, + "y": 0, + }, + Object { + "x": 26.5, + "y": 0, + }, + Object { + "x": 27, + "y": 0, + }, + Object { + "x": 27.5, + "y": 0, + }, + Object { + "x": 28, + "y": 0, + }, + Object { + "x": 28.5, + "y": 0, + }, + Object { + "x": 29, + "y": 0, + }, + Object { + "x": 29.5, + "y": 0, + }, + Object { + "x": 30, + "y": 0, + }, + Object { + "x": 30.5, + "y": 0, + }, + Object { + "x": 31, + "y": 0, + }, + Object { + "x": 31.5, + "y": 0, + }, + Object { + "x": 32, + "y": 0, + }, + Object { + "x": 32.5, + "y": 0, + }, + Object { + "x": 33, + "y": 0, + }, + Object { + "x": 33.5, + "y": 0, + }, + Object { + "x": 34, + "y": 0, + }, + Object { + "x": 34.5, + "y": 0, + }, + Object { + "x": 35, + "y": 0, + }, + Object { + "x": 35.5, + "y": 0, + }, + Object { + "x": 36, + "y": 0, + }, + Object { + "x": 36.5, + "y": 0, + }, + Object { + "x": 37, + "y": 0, + }, + Object { + "x": 37.5, + "y": 25, + }, + ], + "name": "Chrome", + }, + Object { + "data": Array [ + Object { + "x": 0, + "y": 0, + }, + Object { + "x": 0.5, + "y": 0, + }, + Object { + "x": 1, + "y": 0, + }, + Object { + "x": 1.5, + "y": 0, + }, + Object { + "x": 2, + "y": 0, + }, + Object { + "x": 2.5, + "y": 0, + }, + Object { + "x": 3, + "y": 0, + }, + Object { + "x": 3.5, + "y": 0, + }, + Object { + "x": 4, + "y": 0, + }, + Object { + "x": 4.5, + "y": 0, + }, + Object { + "x": 5, + "y": 100, + }, + ], + "name": "Chrome Mobile", + }, +] +`; + +exports[`UX page load dist when there is no data returns empty list 1`] = `Object {}`; + +exports[`UX page load dist when there is no data returns empty list with breakdowns 1`] = `Object {}`; diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/page_load_dist.ts b/x-pack/test/apm_api_integration/trial/tests/csm/page_load_dist.ts new file mode 100644 index 0000000000000..fa5fcbcb6a7c3 --- /dev/null +++ b/x-pack/test/apm_api_integration/trial/tests/csm/page_load_dist.ts @@ -0,0 +1,64 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + describe('UX page load dist', () => { + describe('when there is no data', () => { + it('returns empty list', async () => { + const response = await supertest.get( + '/api/apm/rum-client/page-load-distribution?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D' + ); + + expect(response.status).to.be(200); + expectSnapshot(response.body).toMatch(); + }); + it('returns empty list with breakdowns', async () => { + const response = await supertest.get( + '/api/apm/rum-client/page-load-distribution/breakdown?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D&breakdown=Browser' + ); + + expect(response.status).to.be(200); + expectSnapshot(response.body).toMatch(); + }); + }); + + describe('when there is data', () => { + before(async () => { + await esArchiver.load('8.0.0'); + await esArchiver.load('rum_8.0.0'); + }); + after(async () => { + await esArchiver.unload('8.0.0'); + await esArchiver.unload('rum_8.0.0'); + }); + + it('returns page load distribution', async () => { + const response = await supertest.get( + '/api/apm/rum-client/page-load-distribution?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D' + ); + + expect(response.status).to.be(200); + + expectSnapshot(response.body).toMatch(); + }); + it('returns page load distribution with breakdown', async () => { + const response = await supertest.get( + '/api/apm/rum-client/page-load-distribution/breakdown?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D&breakdown=Browser' + ); + + expect(response.status).to.be(200); + + expectSnapshot(response.body).toMatch(); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/trial/tests/index.ts b/x-pack/test/apm_api_integration/trial/tests/index.ts index 97ab662313c7c..ca7f6627842db 100644 --- a/x-pack/test/apm_api_integration/trial/tests/index.ts +++ b/x-pack/test/apm_api_integration/trial/tests/index.ts @@ -37,6 +37,7 @@ export default function observabilityApiIntegrationTests({ loadTestFile }: FtrPr loadTestFile(require.resolve('./csm/page_views.ts')); loadTestFile(require.resolve('./csm/js_errors.ts')); loadTestFile(require.resolve('./csm/has_rum_data.ts')); + loadTestFile(require.resolve('./csm/page_load_dist.ts')); }); }); }