mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[UX Dashboard] Move core web vitals query from APM routes to UX plugin (#133974)
* Migrate service list query out of APM. * Rename non-snakecase files. * Migrate service list query out of APM. * Rename non-snakecase files. * Move core web vitals query out of APM routes. * Delete obsolete snapshot. * Refactor o11y overview registration to not rely on deleted APM route. * Fix some types. * Delete obsolete API test. * Delete obsolete APM API tests. * Delete CWV test from APM API suite. * Add test journey for core web vitals. Co-authored-by: Shahzad <shahzad.muhammad@elastic.co>
This commit is contained in:
parent
027dad3d3c
commit
34fa750da5
17 changed files with 406 additions and 1265 deletions
|
@ -465,121 +465,3 @@ Object {
|
|||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`rum client dashboard queries fetches rum core vitals 1`] = `
|
||||
Object {
|
||||
"apm": Object {
|
||||
"events": Array [
|
||||
"transaction",
|
||||
],
|
||||
},
|
||||
"body": Object {
|
||||
"aggs": Object {
|
||||
"cls": Object {
|
||||
"percentiles": Object {
|
||||
"field": "transaction.experience.cls",
|
||||
"percents": Array [
|
||||
50,
|
||||
],
|
||||
},
|
||||
},
|
||||
"clsRanks": Object {
|
||||
"percentile_ranks": Object {
|
||||
"field": "transaction.experience.cls",
|
||||
"keyed": false,
|
||||
"values": Array [
|
||||
0.1,
|
||||
0.25,
|
||||
],
|
||||
},
|
||||
},
|
||||
"coreVitalPages": Object {
|
||||
"filter": Object {
|
||||
"exists": Object {
|
||||
"field": "transaction.experience",
|
||||
},
|
||||
},
|
||||
},
|
||||
"fcp": Object {
|
||||
"percentiles": Object {
|
||||
"field": "transaction.marks.agent.firstContentfulPaint",
|
||||
"percents": Array [
|
||||
50,
|
||||
],
|
||||
},
|
||||
},
|
||||
"fid": Object {
|
||||
"percentiles": Object {
|
||||
"field": "transaction.experience.fid",
|
||||
"percents": Array [
|
||||
50,
|
||||
],
|
||||
},
|
||||
},
|
||||
"fidRanks": Object {
|
||||
"percentile_ranks": Object {
|
||||
"field": "transaction.experience.fid",
|
||||
"keyed": false,
|
||||
"values": Array [
|
||||
100,
|
||||
300,
|
||||
],
|
||||
},
|
||||
},
|
||||
"lcp": Object {
|
||||
"percentiles": Object {
|
||||
"field": "transaction.marks.agent.largestContentfulPaint",
|
||||
"percents": Array [
|
||||
50,
|
||||
],
|
||||
},
|
||||
},
|
||||
"lcpRanks": Object {
|
||||
"percentile_ranks": Object {
|
||||
"field": "transaction.marks.agent.largestContentfulPaint",
|
||||
"keyed": false,
|
||||
"values": Array [
|
||||
2500,
|
||||
4000,
|
||||
],
|
||||
},
|
||||
},
|
||||
"tbt": Object {
|
||||
"percentiles": Object {
|
||||
"field": "transaction.experience.tbt",
|
||||
"percents": Array [
|
||||
50,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"@timestamp": Object {
|
||||
"format": "epoch_millis",
|
||||
"gte": 0,
|
||||
"lte": 50000,
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"term": Object {
|
||||
"transaction.type": "page-load",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "transaction.marks.navigationTiming.fetchStart",
|
||||
},
|
||||
},
|
||||
],
|
||||
"must_not": Array [],
|
||||
},
|
||||
},
|
||||
"size": 0,
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -13,8 +13,6 @@ import { getClientMetrics } from './get_client_metrics';
|
|||
import { getPageViewTrends } from './get_page_view_trends';
|
||||
import { getPageLoadDistribution } from './get_page_load_distribution';
|
||||
import { getLongTaskMetrics } from './get_long_task_metrics';
|
||||
import { getWebCoreVitals } from './get_web_core_vitals';
|
||||
import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values';
|
||||
|
||||
describe('rum client dashboard queries', () => {
|
||||
let mock: SearchParamsMock;
|
||||
|
@ -66,19 +64,6 @@ describe('rum client dashboard queries', () => {
|
|||
expect(mock.params).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('fetches rum core vitals', async () => {
|
||||
mock = await inspectSearchParams(
|
||||
(setup) =>
|
||||
getWebCoreVitals({
|
||||
setup,
|
||||
start: 0,
|
||||
end: 50000,
|
||||
}),
|
||||
{ uiFilters: { environment: ENVIRONMENT_ALL.value } }
|
||||
);
|
||||
expect(mock.params).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('fetches long task metrics', async () => {
|
||||
mock = await inspectSearchParams((setup) =>
|
||||
getLongTaskMetrics({
|
||||
|
|
|
@ -14,7 +14,6 @@ import { getPageLoadDistribution } from './get_page_load_distribution';
|
|||
import { getPageViewTrends } from './get_page_view_trends';
|
||||
import { getPageLoadDistBreakdown } from './get_pl_dist_breakdown';
|
||||
import { getVisitorBreakdown } from './get_visitor_breakdown';
|
||||
import { getWebCoreVitals } from './get_web_core_vitals';
|
||||
import { hasRumData } from './has_rum_data';
|
||||
import { createApmServerRoute } from '../apm_routes/create_apm_server_route';
|
||||
import { rangeRt } from '../default_api_types';
|
||||
|
@ -214,41 +213,6 @@ const rumVisitorsBreakdownRoute = createApmServerRoute({
|
|||
},
|
||||
});
|
||||
|
||||
const rumWebCoreVitals = createApmServerRoute({
|
||||
endpoint: 'GET /internal/apm/ux/web-core-vitals',
|
||||
params: t.type({
|
||||
query: uxQueryRt,
|
||||
}),
|
||||
options: { tags: ['access:apm'] },
|
||||
handler: async (
|
||||
resources
|
||||
): Promise<{
|
||||
coreVitalPages: number;
|
||||
cls: number | null;
|
||||
fid: number | null | undefined;
|
||||
lcp: number | null | undefined;
|
||||
tbt: number;
|
||||
fcp: number | null | undefined;
|
||||
lcpRanks: number[];
|
||||
fidRanks: number[];
|
||||
clsRanks: number[];
|
||||
}> => {
|
||||
const setup = await setupUXRequest(resources);
|
||||
|
||||
const {
|
||||
query: { urlQuery, percentile, start, end },
|
||||
} = resources.params;
|
||||
|
||||
return getWebCoreVitals({
|
||||
setup,
|
||||
urlQuery,
|
||||
percentile: percentile ? Number(percentile) : undefined,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const rumLongTaskMetrics = createApmServerRoute({
|
||||
endpoint: 'GET /internal/apm/ux/long-task-metrics',
|
||||
params: t.type({
|
||||
|
@ -338,7 +302,6 @@ export const rumRouteRepository = {
|
|||
...rumPageLoadDistBreakdownRoute,
|
||||
...rumPageViewsTrendRoute,
|
||||
...rumVisitorsBreakdownRoute,
|
||||
...rumWebCoreVitals,
|
||||
...rumLongTaskMetrics,
|
||||
...rumHasDataRoute,
|
||||
};
|
||||
|
|
56
x-pack/plugins/ux/e2e/journeys/core_web_vitals.ts
Normal file
56
x-pack/plugins/ux/e2e/journeys/core_web_vitals.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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 { journey, step, expect, before } from '@elastic/synthetics';
|
||||
import { UXDashboardDatePicker } from '../page_objects/date_picker';
|
||||
import { loginToKibana, waitForLoadingToFinish } from './utils';
|
||||
|
||||
journey('Core Web Vitals', async ({ page, params }) => {
|
||||
before(async () => {
|
||||
await waitForLoadingToFinish({ page });
|
||||
});
|
||||
|
||||
const queryParams = {
|
||||
percentile: '50',
|
||||
rangeFrom: '2020-05-18T11:51:00.000Z',
|
||||
rangeTo: '2021-10-30T06:37:15.536Z',
|
||||
};
|
||||
const queryString = new URLSearchParams(queryParams).toString();
|
||||
|
||||
const baseUrl = `${params.kibanaUrl}/app/ux`;
|
||||
|
||||
step('Go to UX Dashboard', async () => {
|
||||
await page.goto(`${baseUrl}?${queryString}`, {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
await loginToKibana({
|
||||
page,
|
||||
user: { username: 'viewer_user', password: 'changeme' },
|
||||
});
|
||||
});
|
||||
|
||||
step('Set date range', async () => {
|
||||
const datePickerPage = new UXDashboardDatePicker(page);
|
||||
await datePickerPage.setDefaultE2eRange();
|
||||
});
|
||||
|
||||
step('Check Core Web Vitals', async () => {
|
||||
expect(await page.$('text=Largest contentful paint'));
|
||||
expect(await page.$('text=First input delay'));
|
||||
expect(await page.$('text=Cumulative layout shift'));
|
||||
expect(
|
||||
await page.innerText('text=1.93 s', {
|
||||
strict: true,
|
||||
timeout: 29000,
|
||||
})
|
||||
).toEqual('1.93 s');
|
||||
expect(await page.innerText('text=5 ms', { strict: true })).toEqual('5 ms');
|
||||
expect(await page.innerText('text=0.003', { strict: true })).toEqual(
|
||||
'0.003'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -5,5 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './core_web_vitals';
|
||||
export * from './url_ux_query.journey';
|
||||
export * from './ux_js_errors.journey';
|
||||
|
|
|
@ -17,11 +17,11 @@ import {
|
|||
import { getCoreVitalsComponent } from '@kbn/observability-plugin/public';
|
||||
import { I18LABELS } from '../translations';
|
||||
import { KeyUXMetrics } from './key_ux_metrics';
|
||||
import { useFetcher } from '../../../../hooks/use_fetcher';
|
||||
import { useUxQuery } from '../hooks/use_ux_query';
|
||||
import { CsmSharedContext } from '../csm_shared_context';
|
||||
import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params';
|
||||
import { getPercentileLabel } from './translations';
|
||||
import { useCoreWebVitalsQuery } from '../../../../hooks/use_core_web_vitals_query';
|
||||
|
||||
export function UXMetrics() {
|
||||
const {
|
||||
|
@ -30,19 +30,9 @@ export function UXMetrics() {
|
|||
|
||||
const uxQuery = useUxQuery();
|
||||
|
||||
const { data, status } = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (uxQuery) {
|
||||
return callApmApi('GET /internal/apm/ux/web-core-vitals', {
|
||||
params: {
|
||||
query: uxQuery,
|
||||
},
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
},
|
||||
[uxQuery]
|
||||
);
|
||||
const { data, loading: loadingResponse } = useCoreWebVitalsQuery(uxQuery);
|
||||
|
||||
const loading = loadingResponse ?? true;
|
||||
|
||||
const {
|
||||
sharedData: { totalPageViews },
|
||||
|
@ -53,11 +43,11 @@ export function UXMetrics() {
|
|||
getCoreVitalsComponent({
|
||||
data,
|
||||
totalPageViews,
|
||||
loading: status !== 'success',
|
||||
loading,
|
||||
displayTrafficMetric: true,
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[status]
|
||||
[loading]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -70,7 +60,7 @@ export function UXMetrics() {
|
|||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<KeyUXMetrics data={data} loading={status !== 'success'} />
|
||||
<KeyUXMetrics data={data} loading={loading} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="xs" />
|
||||
|
|
|
@ -5,35 +5,91 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ESSearchResponse } from '@kbn/core/types/elasticsearch';
|
||||
import {
|
||||
DataPublicPluginStart,
|
||||
isCompleteResponse,
|
||||
} from '@kbn/data-plugin/public';
|
||||
import {
|
||||
FetchDataParams,
|
||||
HasDataParams,
|
||||
UxFetchDataResponse,
|
||||
UXHasDataResponse,
|
||||
UXMetrics,
|
||||
} from '@kbn/observability-plugin/public';
|
||||
import {
|
||||
coreWebVitalsQuery,
|
||||
transformCoreWebVitalsResponse,
|
||||
DEFAULT_RANKS,
|
||||
} from '../../../services/data/core_web_vitals_query';
|
||||
import { callApmApi } from '../../../services/rest/create_call_apm_api';
|
||||
|
||||
export { createCallApmApi } from '../../../services/rest/create_call_apm_api';
|
||||
|
||||
export const fetchUxOverviewDate = async ({
|
||||
absoluteTime,
|
||||
relativeTime,
|
||||
serviceName,
|
||||
}: FetchDataParams): Promise<UxFetchDataResponse> => {
|
||||
const data = await callApmApi('GET /internal/apm/ux/web-core-vitals', {
|
||||
signal: null,
|
||||
params: {
|
||||
query: {
|
||||
start: new Date(absoluteTime.start).toISOString(),
|
||||
end: new Date(absoluteTime.end).toISOString(),
|
||||
uiFilters: `{"serviceName":["${serviceName}"]}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
type FetchUxOverviewDateParams = FetchDataParams & {
|
||||
dataStartPlugin: DataPublicPluginStart;
|
||||
};
|
||||
|
||||
async function getCoreWebVitalsResponse({
|
||||
absoluteTime,
|
||||
serviceName,
|
||||
dataStartPlugin,
|
||||
}: FetchUxOverviewDateParams) {
|
||||
const dataView = await callApmApi('GET /internal/apm/data_view/dynamic', {
|
||||
signal: null,
|
||||
});
|
||||
return new Promise<
|
||||
ESSearchResponse<{}, ReturnType<typeof coreWebVitalsQuery>>
|
||||
>((resolve) => {
|
||||
const search$ = dataStartPlugin.search
|
||||
.search(
|
||||
{
|
||||
params: {
|
||||
index: dataView.dynamicDataView?.title,
|
||||
...coreWebVitalsQuery(
|
||||
absoluteTime.start,
|
||||
absoluteTime.end,
|
||||
undefined,
|
||||
{
|
||||
serviceName: serviceName ? [serviceName] : undefined,
|
||||
}
|
||||
),
|
||||
},
|
||||
},
|
||||
{}
|
||||
)
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
if (isCompleteResponse(result)) {
|
||||
resolve(result.rawResponse as any);
|
||||
search$.unsubscribe();
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const CORE_WEB_VITALS_DEFAULTS: UXMetrics = {
|
||||
coreVitalPages: 0,
|
||||
cls: 0,
|
||||
fid: 0,
|
||||
lcp: 0,
|
||||
tbt: 0,
|
||||
fcp: 0,
|
||||
lcpRanks: DEFAULT_RANKS,
|
||||
fidRanks: DEFAULT_RANKS,
|
||||
clsRanks: DEFAULT_RANKS,
|
||||
};
|
||||
|
||||
export const fetchUxOverviewDate = async (
|
||||
params: FetchUxOverviewDateParams
|
||||
): Promise<UxFetchDataResponse> => {
|
||||
const coreWebVitalsResponse = await getCoreWebVitalsResponse(params);
|
||||
return {
|
||||
coreWebVitals: data,
|
||||
appLink: `/app/ux?rangeFrom=${relativeTime.start}&rangeTo=${relativeTime.end}`,
|
||||
coreWebVitals:
|
||||
transformCoreWebVitalsResponse(coreWebVitalsResponse) ??
|
||||
CORE_WEB_VITALS_DEFAULTS,
|
||||
appLink: `/app/ux?rangeFrom=${params.relativeTime.start}&rangeTo=${params.relativeTime.end}`,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
44
x-pack/plugins/ux/public/hooks/use_core_web_vitals_query.ts
Normal file
44
x-pack/plugins/ux/public/hooks/use_core_web_vitals_query.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 { useEsSearch } from '@kbn/observability-plugin/public';
|
||||
import { useMemo } from 'react';
|
||||
import { useDataView } from '../components/app/rum_dashboard/local_uifilters/use_data_view';
|
||||
import { callDateMath } from '../services/data/call_date_math';
|
||||
import {
|
||||
coreWebVitalsQuery,
|
||||
transformCoreWebVitalsResponse,
|
||||
PERCENTILE_DEFAULT,
|
||||
} from '../services/data/core_web_vitals_query';
|
||||
import { useUxQuery } from '../components/app/rum_dashboard/hooks/use_ux_query';
|
||||
|
||||
export function useCoreWebVitalsQuery(uxQuery: ReturnType<typeof useUxQuery>) {
|
||||
const { dataViewTitle } = useDataView();
|
||||
const { data: esQueryResponse, loading } = useEsSearch(
|
||||
{
|
||||
index: uxQuery ? dataViewTitle : undefined,
|
||||
...coreWebVitalsQuery(
|
||||
callDateMath(uxQuery?.start),
|
||||
callDateMath(uxQuery?.end),
|
||||
uxQuery?.urlQuery,
|
||||
uxQuery?.uiFilters ? JSON.parse(uxQuery.uiFilters) : {},
|
||||
uxQuery?.percentile ? Number(uxQuery.percentile) : undefined
|
||||
),
|
||||
},
|
||||
[uxQuery, dataViewTitle],
|
||||
{ name: 'UxCoreWebVitals' }
|
||||
);
|
||||
const data = useMemo(
|
||||
() =>
|
||||
transformCoreWebVitalsResponse(
|
||||
esQueryResponse,
|
||||
uxQuery?.percentile ? Number(uxQuery?.percentile) : PERCENTILE_DEFAULT
|
||||
),
|
||||
[esQueryResponse, uxQuery?.percentile]
|
||||
);
|
||||
return { data, loading };
|
||||
}
|
|
@ -79,8 +79,14 @@ export class UxPlugin implements Plugin<UxPluginSetup, UxPluginStart> {
|
|||
return await dataHelper.hasRumData(params!);
|
||||
},
|
||||
fetchData: async (params: FetchDataParams) => {
|
||||
const [_, startPlugins] = await core.getStartServices();
|
||||
|
||||
const { data: dataStartPlugin } = startPlugins as ApmPluginStartDeps;
|
||||
const dataHelper = await getUxDataHelper();
|
||||
return await dataHelper.fetchUxOverviewDate(params);
|
||||
return await dataHelper.fetchUxOverviewDate({
|
||||
...params,
|
||||
dataStartPlugin,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
114
x-pack/plugins/ux/public/services/data/__snapshots__/core_web_vitals_query.test.ts.snap
generated
Normal file
114
x-pack/plugins/ux/public/services/data/__snapshots__/core_web_vitals_query.test.ts.snap
generated
Normal file
|
@ -0,0 +1,114 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`core web vitals query fetches rum core vitals 1`] = `
|
||||
Object {
|
||||
"body": Object {
|
||||
"aggs": Object {
|
||||
"cls": Object {
|
||||
"percentiles": Object {
|
||||
"field": "transaction.experience.cls",
|
||||
"percents": Array [
|
||||
50,
|
||||
],
|
||||
},
|
||||
},
|
||||
"clsRanks": Object {
|
||||
"percentile_ranks": Object {
|
||||
"field": "transaction.experience.cls",
|
||||
"keyed": false,
|
||||
"values": Array [
|
||||
0.1,
|
||||
0.25,
|
||||
],
|
||||
},
|
||||
},
|
||||
"coreVitalPages": Object {
|
||||
"filter": Object {
|
||||
"exists": Object {
|
||||
"field": "transaction.experience",
|
||||
},
|
||||
},
|
||||
},
|
||||
"fcp": Object {
|
||||
"percentiles": Object {
|
||||
"field": "transaction.marks.agent.firstContentfulPaint",
|
||||
"percents": Array [
|
||||
50,
|
||||
],
|
||||
},
|
||||
},
|
||||
"fid": Object {
|
||||
"percentiles": Object {
|
||||
"field": "transaction.experience.fid",
|
||||
"percents": Array [
|
||||
50,
|
||||
],
|
||||
},
|
||||
},
|
||||
"fidRanks": Object {
|
||||
"percentile_ranks": Object {
|
||||
"field": "transaction.experience.fid",
|
||||
"keyed": false,
|
||||
"values": Array [
|
||||
100,
|
||||
300,
|
||||
],
|
||||
},
|
||||
},
|
||||
"lcp": Object {
|
||||
"percentiles": Object {
|
||||
"field": "transaction.marks.agent.largestContentfulPaint",
|
||||
"percents": Array [
|
||||
50,
|
||||
],
|
||||
},
|
||||
},
|
||||
"lcpRanks": Object {
|
||||
"percentile_ranks": Object {
|
||||
"field": "transaction.marks.agent.largestContentfulPaint",
|
||||
"keyed": false,
|
||||
"values": Array [
|
||||
2500,
|
||||
4000,
|
||||
],
|
||||
},
|
||||
},
|
||||
"tbt": Object {
|
||||
"percentiles": Object {
|
||||
"field": "transaction.experience.tbt",
|
||||
"percents": Array [
|
||||
50,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"@timestamp": Object {
|
||||
"format": "epoch_millis",
|
||||
"gte": 0,
|
||||
"lte": 5000,
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"term": Object {
|
||||
"transaction.type": "page-load",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "transaction.marks.navigationTiming.fetchStart",
|
||||
},
|
||||
},
|
||||
],
|
||||
"must_not": Array [],
|
||||
},
|
||||
},
|
||||
"size": 0,
|
||||
},
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 { coreWebVitalsQuery } from './core_web_vitals_query';
|
||||
|
||||
describe('core web vitals query', () => {
|
||||
it('fetches rum core vitals', async () => {
|
||||
expect(
|
||||
coreWebVitalsQuery(
|
||||
0,
|
||||
5000,
|
||||
'',
|
||||
{
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
},
|
||||
50
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -5,37 +5,89 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
|
||||
import { mergeProjection } from '../../projections/util/merge_projection';
|
||||
import { SetupUX } from './route';
|
||||
import { ESSearchResponse } from '@kbn/core/types/elasticsearch';
|
||||
import { UXMetrics } from '@kbn/observability-plugin/public';
|
||||
import {
|
||||
CLS_FIELD,
|
||||
TBT_FIELD,
|
||||
FCP_FIELD,
|
||||
CLS_FIELD,
|
||||
FID_FIELD,
|
||||
LCP_FIELD,
|
||||
TBT_FIELD,
|
||||
} from '../../../common/elasticsearch_fieldnames';
|
||||
import { SetupUX, UxUIFilters } from '../../../typings/ui_filters';
|
||||
import { mergeProjection } from '../../../common/utils/merge_projection';
|
||||
import { getRumPageLoadTransactionsProjection } from './projections';
|
||||
|
||||
export const DEFAULT_RANKS = [100, 0, 0];
|
||||
|
||||
const getRanksPercentages = (ranks?: Record<string, number | null>) => {
|
||||
if (!Array.isArray(ranks)) return null;
|
||||
const ranksVal = ranks?.map(({ value }) => value?.toFixed(0) ?? 0) ?? [];
|
||||
return [
|
||||
Number(ranksVal?.[0]),
|
||||
Number(ranksVal?.[1]) - Number(ranksVal?.[0]),
|
||||
100 - Number(ranksVal?.[1]),
|
||||
];
|
||||
};
|
||||
|
||||
export function transformCoreWebVitalsResponse<T>(
|
||||
response?: ESSearchResponse<T, ReturnType<typeof coreWebVitalsQuery>>,
|
||||
percentile = PERCENTILE_DEFAULT
|
||||
): UXMetrics | undefined {
|
||||
if (!response) return response;
|
||||
const {
|
||||
lcp,
|
||||
cls,
|
||||
fid,
|
||||
tbt,
|
||||
fcp,
|
||||
lcpRanks,
|
||||
fidRanks,
|
||||
clsRanks,
|
||||
coreVitalPages,
|
||||
} = response.aggregations ?? {};
|
||||
|
||||
const pkey = percentile.toFixed(1);
|
||||
|
||||
return {
|
||||
coreVitalPages: coreVitalPages?.doc_count ?? 0,
|
||||
/* Because cls is required in the type UXMetrics, and defined as number | null,
|
||||
* we need to default to null in the case where cls is undefined in order to satisfy the UXMetrics type */
|
||||
cls: cls?.values[pkey] ?? null,
|
||||
fid: fid?.values[pkey],
|
||||
lcp: lcp?.values[pkey],
|
||||
tbt: tbt?.values[pkey] ?? 0,
|
||||
fcp: fcp?.values[pkey],
|
||||
|
||||
lcpRanks: lcp?.values[pkey]
|
||||
? getRanksPercentages(lcpRanks?.values) ?? DEFAULT_RANKS
|
||||
: DEFAULT_RANKS,
|
||||
fidRanks: fid?.values[pkey]
|
||||
? getRanksPercentages(fidRanks?.values) ?? DEFAULT_RANKS
|
||||
: DEFAULT_RANKS,
|
||||
clsRanks: cls?.values[pkey]
|
||||
? getRanksPercentages(clsRanks?.values) ?? DEFAULT_RANKS
|
||||
: DEFAULT_RANKS,
|
||||
};
|
||||
}
|
||||
|
||||
export const PERCENTILE_DEFAULT = 50;
|
||||
|
||||
export function coreWebVitalsQuery(
|
||||
start: number,
|
||||
end: number,
|
||||
urlQuery?: string,
|
||||
uiFilters?: UxUIFilters,
|
||||
percentile = PERCENTILE_DEFAULT
|
||||
) {
|
||||
const setup: SetupUX = { uiFilters: uiFilters ? uiFilters : {} };
|
||||
|
||||
export async function getWebCoreVitals({
|
||||
setup,
|
||||
urlQuery,
|
||||
percentile = 50,
|
||||
start,
|
||||
end,
|
||||
}: {
|
||||
setup: SetupUX;
|
||||
urlQuery?: string;
|
||||
percentile?: number;
|
||||
start: number;
|
||||
end: number;
|
||||
}) {
|
||||
const projection = getRumPageLoadTransactionsProjection({
|
||||
setup,
|
||||
urlQuery,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
|
||||
const params = mergeProjection(projection, {
|
||||
body: {
|
||||
size: 0,
|
||||
|
@ -106,55 +158,6 @@ export async function getWebCoreVitals({
|
|||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { apmEventClient } = setup;
|
||||
|
||||
const response = await apmEventClient.search('get_web_core_vitals', params);
|
||||
const {
|
||||
lcp,
|
||||
cls,
|
||||
fid,
|
||||
tbt,
|
||||
fcp,
|
||||
lcpRanks,
|
||||
fidRanks,
|
||||
clsRanks,
|
||||
coreVitalPages,
|
||||
} = response.aggregations ?? {};
|
||||
|
||||
const getRanksPercentages = (
|
||||
ranks?: Array<{ key: number; value: number | null }>
|
||||
) => {
|
||||
const ranksVal = ranks?.map(({ value }) => value?.toFixed(0) ?? 0) ?? [];
|
||||
return [
|
||||
Number(ranksVal?.[0]),
|
||||
Number(ranksVal?.[1]) - Number(ranksVal?.[0]),
|
||||
100 - Number(ranksVal?.[1]),
|
||||
];
|
||||
};
|
||||
|
||||
const defaultRanks = [100, 0, 0];
|
||||
|
||||
const pkey = percentile.toFixed(1);
|
||||
|
||||
return {
|
||||
coreVitalPages: coreVitalPages?.doc_count ?? 0,
|
||||
/* Because cls is required in the type UXMetrics, and defined as number | null,
|
||||
* we need to default to null in the case where cls is undefined in order to satisfy the UXMetrics type */
|
||||
cls: cls?.values[pkey] ?? null,
|
||||
fid: fid?.values[pkey],
|
||||
lcp: lcp?.values[pkey],
|
||||
tbt: tbt?.values[pkey] ?? 0,
|
||||
fcp: fcp?.values[pkey],
|
||||
|
||||
lcpRanks: lcp?.values[pkey]
|
||||
? getRanksPercentages(lcpRanks?.values)
|
||||
: defaultRanks,
|
||||
fidRanks: fid?.values[pkey]
|
||||
? getRanksPercentages(fidRanks?.values)
|
||||
: defaultRanks,
|
||||
clsRanks: cls?.values[pkey]
|
||||
? getRanksPercentages(clsRanks?.values)
|
||||
: defaultRanks,
|
||||
};
|
||||
const { apm, ...rest } = params;
|
||||
return rest;
|
||||
}
|
|
@ -5,17 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { TRANSACTION_TYPE } from '../../../common/elasticsearch_fieldnames';
|
||||
import {
|
||||
AGENT_NAME,
|
||||
PROCESSOR_EVENT,
|
||||
SERVICE_LANGUAGE_NAME,
|
||||
TRANSACTION_TYPE,
|
||||
} from '../../../common/elasticsearch_fieldnames';
|
||||
import { ProcessorEvent } from '../../../common/processor_event';
|
||||
import { TRANSACTION_PAGE_LOAD } from '../../../common/transaction_types';
|
||||
import { SetupUX } from '../../../typings/ui_filters';
|
||||
import { getEsFilter } from './get_es_filter';
|
||||
import { rangeQuery } from './range_query';
|
||||
import {
|
||||
AGENT_NAME,
|
||||
SERVICE_LANGUAGE_NAME,
|
||||
PROCESSOR_EVENT,
|
||||
} from '../../../common/elasticsearch_fieldnames';
|
||||
|
||||
export function getRumPageLoadTransactionsProjection({
|
||||
setup,
|
||||
|
|
|
@ -37,6 +37,6 @@ export function serviceNameQuery(
|
|||
},
|
||||
},
|
||||
});
|
||||
const { apm: _apm, ...rest } = params;
|
||||
const { apm, ...rest } = params;
|
||||
return rest;
|
||||
}
|
||||
|
|
|
@ -1,832 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`APM API tests trial 8.0.0,rum_8.0.0 UX page load dist with data returns page load distribution 1`] = `
|
||||
Object {
|
||||
"pageLoadDistribution": 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[`APM API tests trial 8.0.0,rum_8.0.0 UX page load dist with data returns page load distribution with breakdown 1`] = `
|
||||
Object {
|
||||
"pageLoadDistBreakdown": 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[`APM API tests trial no data UX page load dist without data returns empty list 1`] = `
|
||||
Object {
|
||||
"pageLoadDistribution": null,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`APM API tests trial no data UX page load dist without data returns empty list with breakdowns 1`] = `Object {}`;
|
|
@ -1,73 +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.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
|
||||
export default function rumServicesApiTests({ getService }: FtrProviderContext) {
|
||||
const registry = getService('registry');
|
||||
const supertest = getService('legacySupertestAsApmReadUser');
|
||||
|
||||
registry.when('UX page load dist without data', { config: 'trial', archives: [] }, () => {
|
||||
it('returns empty list', async () => {
|
||||
const response = await supertest.get('/internal/apm/ux/page-load-distribution').query({
|
||||
start: '2020-09-07T20:35:54.654Z',
|
||||
end: '2020-09-14T20:35:54.654Z',
|
||||
uiFilters: '{"serviceName":["elastic-co-rum-test"]}',
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
expectSnapshot(response.body).toMatch();
|
||||
});
|
||||
|
||||
it('returns empty list with breakdowns', async () => {
|
||||
const response = await supertest
|
||||
.get('/internal/apm/ux/page-load-distribution/breakdown')
|
||||
.query({
|
||||
start: '2020-09-07T20:35:54.654Z',
|
||||
end: '2020-09-14T20:35:54.654Z',
|
||||
uiFilters: '{"serviceName":["elastic-co-rum-test"]}',
|
||||
breakdown: 'Browser',
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
expectSnapshot(response.body).toMatch();
|
||||
});
|
||||
});
|
||||
|
||||
registry.when(
|
||||
'UX page load dist with data',
|
||||
{ config: 'trial', archives: ['8.0.0', 'rum_8.0.0'] },
|
||||
() => {
|
||||
it('returns page load distribution', async () => {
|
||||
const response = await supertest.get('/internal/apm/ux/page-load-distribution').query({
|
||||
start: '2020-09-07T20:35:54.654Z',
|
||||
end: '2020-09-16T20:35:54.654Z',
|
||||
uiFilters: '{"serviceName":["kibana-frontend-8_0_0"]}',
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
expectSnapshot(response.body).toMatch();
|
||||
});
|
||||
it('returns page load distribution with breakdown', async () => {
|
||||
const response = await supertest
|
||||
.get('/internal/apm/ux/page-load-distribution/breakdown')
|
||||
.query({
|
||||
start: '2020-09-07T20:35:54.654Z',
|
||||
end: '2020-09-16T20:35:54.654Z',
|
||||
uiFilters: '{"serviceName":["kibana-frontend-8_0_0"]}',
|
||||
breakdown: 'Browser',
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
expectSnapshot(response.body).toMatch();
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
|
@ -1,78 +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.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
|
||||
export default function rumServicesApiTests({ getService }: FtrProviderContext) {
|
||||
const registry = getService('registry');
|
||||
const supertest = getService('legacySupertestAsApmReadUser');
|
||||
|
||||
registry.when('CSM web core vitals without data', { config: 'trial', archives: [] }, () => {
|
||||
it('returns empty list', async () => {
|
||||
const response = await supertest.get('/internal/apm/ux/web-core-vitals').query({
|
||||
start: '2020-09-07T20:35:54.654Z',
|
||||
end: '2020-09-14T20:35:54.654Z',
|
||||
uiFilters: '{"serviceName":["elastic-co-rum-test"]}',
|
||||
percentile: 50,
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body).to.eql({
|
||||
coreVitalPages: 0,
|
||||
cls: null,
|
||||
tbt: 0,
|
||||
lcpRanks: [100, 0, 0],
|
||||
fidRanks: [100, 0, 0],
|
||||
clsRanks: [100, 0, 0],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
registry.when(
|
||||
'CSM web core vitals with data',
|
||||
{ config: 'trial', archives: ['8.0.0', 'rum_8.0.0'] },
|
||||
() => {
|
||||
it('returns web core vitals values', async () => {
|
||||
const response = await supertest.get('/internal/apm/ux/web-core-vitals').query({
|
||||
start: '2020-09-07T20:35:54.654Z',
|
||||
end: '2020-09-16T20:35:54.654Z',
|
||||
uiFilters: '{"serviceName":["kibana-frontend-8_0_0"]}',
|
||||
percentile: 50,
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
expectSnapshot(response.body).toMatchInline(`
|
||||
Object {
|
||||
"cls": 0,
|
||||
"clsRanks": Array [
|
||||
100,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"coreVitalPages": 6,
|
||||
"fcp": 817.5,
|
||||
"fid": 1352.13,
|
||||
"fidRanks": Array [
|
||||
0,
|
||||
0,
|
||||
100,
|
||||
],
|
||||
"lcp": 1019,
|
||||
"lcpRanks": Array [
|
||||
100,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"tbt": 0,
|
||||
}
|
||||
`);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue