Skip to content

Commit

Permalink
Merge branch 'main' into ml-154294-overview-page
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Jun 20, 2023
2 parents ab49032 + 97dc2ec commit 6486ff9
Show file tree
Hide file tree
Showing 112 changed files with 3,544 additions and 544 deletions.
1 change: 1 addition & 0 deletions .buildkite/ftr_configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ enabled:
- x-pack/test/api_integration/apis/security/config.ts
- x-pack/test/api_integration/apis/security_solution/config.ts
- x-pack/test/api_integration/apis/spaces/config.ts
- x-pack/test/api_integration/apis/status/config.ts
- x-pack/test/api_integration/apis/synthetics/config.ts
- x-pack/test/api_integration/apis/telemetry/config.ts
- x-pack/test/api_integration/apis/transform/config.ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { act } from 'react-dom/test-utils';
import { createMemoryHistory, MemoryHistory } from 'history';

import { httpServiceMock } from '@kbn/core-http-browser-mocks';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import type { AppMountParameters, AppUpdater } from '@kbn/core-application-browser';
import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks';
Expand All @@ -38,11 +39,13 @@ describe('ApplicationService', () => {
beforeEach(() => {
history = createMemoryHistory();
const http = httpServiceMock.createSetupContract({ basePath: '/test' });
const analytics = analyticsServiceMock.createAnalyticsServiceSetup();

http.post.mockResolvedValue({ navLinks: {} });

setupDeps = {
http,
analytics,
history: history as any,
};
startDeps = {
Expand Down Expand Up @@ -87,6 +90,45 @@ describe('ApplicationService', () => {

expect(await currentAppId$.pipe(take(1)).toPromise()).toEqual('app1');
});

it('updates the page_url analytics context', async () => {
const { register } = service.setup(setupDeps);

const context$ = setupDeps.analytics.registerContextProvider.mock.calls[0][0]
.context$ as Observable<{
page_url: string;
}>;
const locations: string[] = [];
context$.subscribe((context) => locations.push(context.page_url));

register(Symbol(), {
id: 'app1',
title: 'App1',
mount: async () => () => undefined,
});
register(Symbol(), {
id: 'app2',
title: 'App2',
mount: async () => () => undefined,
});

const { getComponent } = await service.start(startDeps);
update = createRenderer(getComponent());

await navigate('/app/app1/bar?hello=dolly');
await flushPromises();
await navigate('/app/app2#/foo');
await flushPromises();
await navigate('/app/app2#/another-path');
await flushPromises();

expect(locations).toEqual([
'/',
'/app/app1/bar',
'/app/app2#/foo',
'/app/app2#/another-path',
]);
});
});

describe('using navigateToApp', () => {
Expand Down Expand Up @@ -127,6 +169,46 @@ describe('ApplicationService', () => {
expect(currentAppIds).toEqual(['app1']);
});

it('updates the page_url analytics context', async () => {
const { register } = service.setup(setupDeps);

const context$ = setupDeps.analytics.registerContextProvider.mock.calls[0][0]
.context$ as Observable<{
page_url: string;
}>;
const locations: string[] = [];
context$.subscribe((context) => locations.push(context.page_url));

register(Symbol(), {
id: 'app1',
title: 'App1',
mount: async () => () => undefined,
});
register(Symbol(), {
id: 'app2',
title: 'App2',
mount: async () => () => undefined,
});

const { navigateToApp, getComponent } = await service.start(startDeps);
update = createRenderer(getComponent());

await act(async () => {
await navigateToApp('app1');
update();
});
await act(async () => {
await navigateToApp('app2', { path: '/nested' });
update();
});
await act(async () => {
await navigateToApp('app2', { path: '/another-path' });
update();
});

expect(locations).toEqual(['/', '/app/app1', '/app/app2/nested', '/app/app2/another-path']);
});

it('replaces the current history entry when the `replace` option is true', async () => {
const { register } = service.setup(setupDeps);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,23 @@ jest.doMock('history', () => ({
}));

export const parseAppUrlMock = jest.fn();
export const getLocationObservableMock = jest.fn();
jest.doMock('./utils', () => {
const original = jest.requireActual('./utils');

return {
...original,
parseAppUrl: parseAppUrlMock,
getLocationObservable: getLocationObservableMock,
};
});

export const registerAnalyticsContextProviderMock = jest.fn();
jest.doMock('./register_analytics_context_provider', () => {
const original = jest.requireActual('./register_analytics_context_provider');

return {
...original,
registerAnalyticsContextProvider: registerAnalyticsContextProviderMock,
};
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@ import {
MockCapabilitiesService,
MockHistory,
parseAppUrlMock,
getLocationObservableMock,
registerAnalyticsContextProviderMock,
} from './application_service.test.mocks';

import { createElement } from 'react';
import { BehaviorSubject, firstValueFrom, Subject } from 'rxjs';
import { bufferCount, takeUntil } from 'rxjs/operators';
import { mount, shallow } from 'enzyme';
import { createBrowserHistory } from 'history';

import { httpServiceMock } from '@kbn/core-http-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks';
import { customBrandingServiceMock } from '@kbn/core-custom-branding-browser-mocks';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { MockLifecycle } from './test_helpers/test_types';
import { ApplicationService } from './application_service';
import {
Expand Down Expand Up @@ -48,9 +52,12 @@ let service: ApplicationService;

describe('#setup()', () => {
beforeEach(() => {
jest.clearAllMocks();
const http = httpServiceMock.createSetupContract({ basePath: '/base-path' });
const analytics = analyticsServiceMock.createAnalyticsServiceSetup();
setupDeps = {
http,
analytics,
redirectTo: jest.fn(),
};
startDeps = {
Expand Down Expand Up @@ -469,13 +476,38 @@ describe('#setup()', () => {
]);
});
});

describe('analytics context provider', () => {
it('calls getLocationObservable with the correct parameters', () => {
const history = createBrowserHistory();
service.setup({ ...setupDeps, history });

expect(getLocationObservableMock).toHaveBeenCalledTimes(1);
expect(getLocationObservableMock).toHaveBeenCalledWith(window.location, history);
});

it('calls registerAnalyticsContextProvider with the correct parameters', () => {
const location$ = new Subject<string>();
getLocationObservableMock.mockReturnValue(location$);

service.setup(setupDeps);

expect(registerAnalyticsContextProviderMock).toHaveBeenCalledTimes(1);
expect(registerAnalyticsContextProviderMock).toHaveBeenCalledWith({
analytics: setupDeps.analytics,
location$,
});
});
});
});

describe('#start()', () => {
beforeEach(() => {
const http = httpServiceMock.createSetupContract({ basePath: '/base-path' });
const analytics = analyticsServiceMock.createAnalyticsServiceSetup();
setupDeps = {
http,
analytics,
redirectTo: jest.fn(),
};
startDeps = {
Expand Down Expand Up @@ -1185,8 +1217,10 @@ describe('#stop()', () => {

MockHistory.push.mockReset();
const http = httpServiceMock.createSetupContract({ basePath: '/test' });
const analytics = analyticsServiceMock.createAnalyticsServiceSetup();
setupDeps = {
http,
analytics,
};
startDeps = {
http,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type { HttpSetup, HttpStart } from '@kbn/core-http-browser';
import type { Capabilities } from '@kbn/core-capabilities-common';
import type { MountPoint } from '@kbn/core-mount-utils-browser';
import type { OverlayStart } from '@kbn/core-overlays-browser';
import type { AnalyticsServiceSetup } from '@kbn/core-analytics-browser';
import type {
App,
AppDeepLink,
Expand All @@ -35,10 +36,18 @@ import type { InternalApplicationSetup, InternalApplicationStart, Mounter } from

import { getLeaveAction, isConfirmAction } from './application_leave';
import { getUserConfirmationHandler } from './navigation_confirm';
import { appendAppPath, parseAppUrl, relativeToAbsolute, getAppInfo } from './utils';
import {
appendAppPath,
parseAppUrl,
relativeToAbsolute,
getAppInfo,
getLocationObservable,
} from './utils';
import { registerAnalyticsContextProvider } from './register_analytics_context_provider';

export interface SetupDeps {
http: HttpSetup;
analytics: AnalyticsServiceSetup;
history?: History<any>;
/** Used to redirect to external urls */
redirectTo?: (path: string) => void;
Expand Down Expand Up @@ -111,6 +120,7 @@ export class ApplicationService {

public setup({
http: { basePath },
analytics,
redirectTo = (path: string) => {
window.location.assign(path);
},
Expand All @@ -126,6 +136,12 @@ export class ApplicationService {
}),
});

const location$ = getLocationObservable(window.location, this.history);
registerAnalyticsContextProvider({
analytics,
location$,
});

this.navigate = (url, state, replace) => {
// basePath not needed here because `history` is configured with basename
return replace ? this.history!.replace(url, state) : this.history!.push(url, state);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { firstValueFrom, ReplaySubject, Subject } from 'rxjs';
import { registerAnalyticsContextProvider } from './register_analytics_context_provider';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';

describe('registerAnalyticsContextProvider', () => {
let analytics: ReturnType<typeof analyticsServiceMock.createAnalyticsServiceSetup>;
let location$: Subject<string>;

beforeEach(() => {
analytics = analyticsServiceMock.createAnalyticsServiceSetup();
location$ = new ReplaySubject<string>(1);
registerAnalyticsContextProvider({ analytics, location$ });
});

test('should register the analytics context provider', () => {
expect(analytics.registerContextProvider).toHaveBeenCalledTimes(1);
expect(analytics.registerContextProvider).toHaveBeenCalledWith(
expect.objectContaining({
name: 'page url',
})
);
});

test('emits a context value when location$ emits', async () => {
location$.next('/some_url');
await expect(
firstValueFrom(analytics.registerContextProvider.mock.calls[0][0].context$)
).resolves.toEqual({ page_url: '/some_url' });
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { AnalyticsServiceSetup } from '@kbn/core-analytics-browser';
import { type Observable, map } from 'rxjs';

export function registerAnalyticsContextProvider({
analytics,
location$,
}: {
analytics: AnalyticsServiceSetup;
location$: Observable<string>;
}) {
analytics.registerContextProvider({
name: 'page url',
context$: location$.pipe(map((location) => ({ page_url: location }))),
schema: {
page_url: { type: 'text', _meta: { description: 'The page url' } },
},
});
}
Loading

0 comments on commit 6486ff9

Please sign in to comment.