mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[EBT] Better FTR helper APIs (#135298)
This commit is contained in:
parent
a6f17d5dd2
commit
a2b0927e65
41 changed files with 461 additions and 118 deletions
|
@ -25,7 +25,7 @@ export function getPluginSearchPaths({ rootDir, oss, examples, testPlugins }: Se
|
|||
resolve(rootDir, '..', 'kibana-extra'),
|
||||
...(testPlugins
|
||||
? [
|
||||
resolve(rootDir, 'test/analytics/__fixtures__/plugins'),
|
||||
resolve(rootDir, 'test/analytics/fixtures/plugins'),
|
||||
resolve(rootDir, 'test/plugin_functional/plugins'),
|
||||
resolve(rootDir, 'test/interpreter_functional/plugins'),
|
||||
resolve(rootDir, 'test/common/fixtures/plugins'),
|
||||
|
|
2
packages/kbn-pm/dist/index.js
vendored
2
packages/kbn-pm/dist/index.js
vendored
|
@ -2168,7 +2168,7 @@ function getPluginSearchPaths({
|
|||
examples,
|
||||
testPlugins
|
||||
}) {
|
||||
return [(0, _path.resolve)(rootDir, 'src', 'plugins'), ...(oss ? [] : [(0, _path.resolve)(rootDir, 'x-pack', 'plugins')]), (0, _path.resolve)(rootDir, 'plugins'), ...(examples ? [(0, _path.resolve)(rootDir, 'examples')] : []), ...(examples && !oss ? [(0, _path.resolve)(rootDir, 'x-pack', 'examples')] : []), (0, _path.resolve)(rootDir, '..', 'kibana-extra'), ...(testPlugins ? [(0, _path.resolve)(rootDir, 'test/analytics/__fixtures__/plugins'), (0, _path.resolve)(rootDir, 'test/plugin_functional/plugins'), (0, _path.resolve)(rootDir, 'test/interpreter_functional/plugins'), (0, _path.resolve)(rootDir, 'test/common/fixtures/plugins')] : []), ...(testPlugins && !oss ? [(0, _path.resolve)(rootDir, 'x-pack/test/plugin_functional/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/functional_with_es_ssl/fixtures/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/alerting_api_integration/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/plugin_api_integration/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/plugin_api_perf/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/licensing_plugin/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/usage_collection/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/security_functional/fixtures/common')] : [])];
|
||||
return [(0, _path.resolve)(rootDir, 'src', 'plugins'), ...(oss ? [] : [(0, _path.resolve)(rootDir, 'x-pack', 'plugins')]), (0, _path.resolve)(rootDir, 'plugins'), ...(examples ? [(0, _path.resolve)(rootDir, 'examples')] : []), ...(examples && !oss ? [(0, _path.resolve)(rootDir, 'x-pack', 'examples')] : []), (0, _path.resolve)(rootDir, '..', 'kibana-extra'), ...(testPlugins ? [(0, _path.resolve)(rootDir, 'test/analytics/fixtures/plugins'), (0, _path.resolve)(rootDir, 'test/plugin_functional/plugins'), (0, _path.resolve)(rootDir, 'test/interpreter_functional/plugins'), (0, _path.resolve)(rootDir, 'test/common/fixtures/plugins')] : []), ...(testPlugins && !oss ? [(0, _path.resolve)(rootDir, 'x-pack/test/plugin_functional/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/functional_with_es_ssl/fixtures/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/alerting_api_integration/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/plugin_api_integration/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/plugin_api_perf/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/licensing_plugin/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/usage_collection/plugins'), (0, _path.resolve)(rootDir, 'x-pack/test/security_functional/fixtures/common')] : [])];
|
||||
}
|
||||
|
||||
/***/ }),
|
||||
|
|
|
@ -89,6 +89,7 @@ export const PROJECTS = [
|
|||
'x-pack/plugins/*/tsconfig.json',
|
||||
'examples/*/tsconfig.json',
|
||||
'x-pack/examples/*/tsconfig.json',
|
||||
'test/analytics/fixtures/plugins/*/tsconfig.json',
|
||||
'test/plugin_functional/plugins/*/tsconfig.json',
|
||||
'test/interpreter_functional/plugins/*/tsconfig.json',
|
||||
'test/server_integration/__fixtures__/plugins/*/tsconfig.json',
|
||||
|
|
|
@ -14,8 +14,10 @@ There are 2 FTR helpers to allow you to retrieve the generated events:
|
|||
The API is the same for both of them:
|
||||
```typescript
|
||||
// To retrieve 2 events of type "my-event-type"
|
||||
const events = await getService('kibana_ebt_ui').getEvents(2, ['my-event-type']);
|
||||
const events = await getService('kibana_ebt_ui').getEvents(2, { eventTypes: ['my-event-type'] });
|
||||
expect(events).to...
|
||||
```
|
||||
|
||||
If you are reusing these helpers in another suite, please remember to make sure to optIn via `await getService('kibana_ebt_ui').setOptIn(true);`
|
||||
If you are reusing these helpers in another suite, please remember to make sure to optIn via `await getService('kibana_ebt_ui').setOptIn(true);`
|
||||
|
||||
Refer to [`EBTHelpersContract`](./fixtures/plugins/analytics_ftr_helpers/common/types.ts#:~:text=EBTHelpersContract) for more details about the existing APIs and all the options they accept.
|
|
@ -35,8 +35,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
serverArgs: [
|
||||
...functionalConfig.get('kbnTestServer.serverArgs'),
|
||||
'--telemetry.optIn=true',
|
||||
`--plugin-path=${path.resolve(__dirname, './__fixtures__/plugins/analytics_plugin_a')}`,
|
||||
`--plugin-path=${path.resolve(__dirname, './__fixtures__/plugins/analytics_ftr_helpers')}`,
|
||||
`--plugin-path=${path.resolve(__dirname, './fixtures/plugins/analytics_plugin_a')}`,
|
||||
`--plugin-path=${path.resolve(__dirname, './fixtures/plugins/analytics_ftr_helpers')}`,
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 {
|
||||
filter,
|
||||
firstValueFrom,
|
||||
map,
|
||||
NEVER,
|
||||
type Observable,
|
||||
take,
|
||||
takeUntil,
|
||||
timer,
|
||||
toArray,
|
||||
} from 'rxjs';
|
||||
import type { Event } from '@kbn/analytics-client';
|
||||
import type { GetEventsOptions } from './types';
|
||||
|
||||
export async function fetchEvents(
|
||||
events$: Observable<Event>,
|
||||
takeNumberOfEvents: number,
|
||||
options: GetEventsOptions = {}
|
||||
): Promise<Event[]> {
|
||||
const { eventTypes = [], withTimeoutMs, fromTimestamp } = options;
|
||||
|
||||
const filteredEvents$ = events$.pipe(
|
||||
filter((event) => {
|
||||
if (eventTypes.length > 0) {
|
||||
return eventTypes.includes(event.event_type);
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
filter((event) => {
|
||||
if (fromTimestamp) {
|
||||
return new Date(event.timestamp).getTime() - new Date(fromTimestamp).getTime() >= 0;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
);
|
||||
|
||||
return firstValueFrom(
|
||||
filteredEvents$.pipe(
|
||||
take(takeNumberOfEvents),
|
||||
// If timeout is specified, close the subscriber when the timeout occurs
|
||||
takeUntil(withTimeoutMs ? timer(withTimeoutMs) : NEVER),
|
||||
toArray(),
|
||||
// Sorting the events by timestamp... on CI it's happening an event may occur while the client is still forwarding the early ones...
|
||||
map((_events) =>
|
||||
_events.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime())
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 { Event, EventType } from '@kbn/analytics-client';
|
||||
|
||||
export interface GetEventsOptions {
|
||||
/**
|
||||
* eventTypes (optional) array of event types to return
|
||||
*/
|
||||
eventTypes?: EventType[];
|
||||
/**
|
||||
* withTimeoutMs (optional) if specified, the method returns all the events received when the first occurs:
|
||||
* - The number of received events match `takeNumberOfEvents`.
|
||||
* - The number of ms in `withTimeoutMs` has elapsed.
|
||||
*/
|
||||
withTimeoutMs?: number;
|
||||
/**
|
||||
* fromTimestamp (optional) if specified, only the events that are greater or equal to the provided timestamp will be returned.
|
||||
* @remarks Useful when we need to retrieve the events after a specific action, and we don't care about anything prior to that.
|
||||
*/
|
||||
fromTimestamp?: string;
|
||||
}
|
||||
|
||||
export interface EBTHelpersContract {
|
||||
/**
|
||||
* Change the opt-in state of the Kibana EBT client.
|
||||
* @param optIn `true` to opt-in, `false` to opt-out.
|
||||
*/
|
||||
setOptIn: (optIn: boolean) => void;
|
||||
/**
|
||||
* Returns the first N number of events of the specified types.
|
||||
* @param takeNumberOfEvents - number of events to return
|
||||
* @param options (optional) list of options to filter events or for advanced usage of the API {@link GetEventsOptions}.
|
||||
*/
|
||||
getEvents: (takeNumberOfEvents: number, options?: GetEventsOptions) => Promise<Event[]>;
|
||||
/**
|
||||
* Count the number of events that match the filters.
|
||||
* @param options list of options to filter the events {@link GetEventsOptions}. `withTimeoutMs` is required in this API.
|
||||
*/
|
||||
getEventCount: (
|
||||
options: Required<Pick<GetEventsOptions, 'withTimeoutMs'>> &
|
||||
Omit<GetEventsOptions, 'withTimeoutMs'>
|
||||
) => Promise<number>;
|
||||
}
|
|
@ -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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../../../..',
|
||||
roots: ['<rootDir>/test/analytics/fixtures/plugins/analytics_ftr_helpers'],
|
||||
coverageDirectory:
|
||||
'<rootDir>/target/kibana-coverage/jest/test/analytics/fixtures/plugins/analytics_ftr_helpers',
|
||||
coverageReporters: ['text', 'html'],
|
||||
collectCoverageFrom: [
|
||||
'<rootDir>/test/analytics/fixtures/plugins/analytics_ftr_helpers/{common,public,server}/**/*.{ts,tsx}',
|
||||
],
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "analytics_ftr_helpers",
|
||||
"version": "1.0.0",
|
||||
"main": "target/test/analytics/__fixtures__/plugins/analytics_ftr_helpers",
|
||||
"main": "target/test/analytics/fixtures/plugins/analytics_ftr_helpers",
|
||||
"kibana": {
|
||||
"version": "kibana",
|
||||
"templateVersion": "1.0.0"
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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 { ReplaySubject } from 'rxjs';
|
||||
import type { AnalyticsServiceSetup, Event } from '@kbn/core/public';
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
|
||||
// Importing types to have the window properties defined
|
||||
import './types';
|
||||
import { AnalyticsFTRHelpers } from './plugin';
|
||||
import { withTimeout } from '@kbn/std';
|
||||
|
||||
describe('AnalyticsFTRHelpers', () => {
|
||||
let plugin: AnalyticsFTRHelpers;
|
||||
let events$: ReplaySubject<Event>;
|
||||
let analyticsMock: jest.Mocked<AnalyticsServiceSetup>;
|
||||
|
||||
beforeEach(() => {
|
||||
plugin = new AnalyticsFTRHelpers();
|
||||
// eslint-disable-next-line dot-notation
|
||||
events$ = plugin['events$'];
|
||||
const coreSetup = coreMock.createSetup();
|
||||
analyticsMock = coreSetup.analytics;
|
||||
plugin.setup(coreSetup);
|
||||
});
|
||||
|
||||
describe('setOptIn', () => {
|
||||
test.each([true, false])('sets optIn value to %p', (optInValue) => {
|
||||
window.__analytics_ftr_helpers__.setOptIn(optInValue);
|
||||
expect(analyticsMock.optIn).toHaveBeenCalledWith({ global: { enabled: optInValue } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getEvents', () => {
|
||||
const event: Event = {
|
||||
timestamp: new Date().toISOString(),
|
||||
event_type: 'test-event',
|
||||
context: {},
|
||||
properties: {},
|
||||
};
|
||||
|
||||
test('should return any previously enqueued events as long as they match the required number of events', async () => {
|
||||
// 3 enqueued events
|
||||
const events = new Array(3).fill(event);
|
||||
events.forEach((ev) => events$.next(ev));
|
||||
|
||||
await expect(window.__analytics_ftr_helpers__.getEvents(3)).resolves.toEqual(events);
|
||||
});
|
||||
|
||||
test('should await until it matches the required number of events', async () => {
|
||||
// 3 enqueued events
|
||||
const events = new Array(3).fill(event);
|
||||
events.forEach((ev) => events$.next(ev));
|
||||
|
||||
const getEventsPromise = window.__analytics_ftr_helpers__.getEvents(4);
|
||||
|
||||
await expect(withTimeout({ promise: getEventsPromise, timeoutMs: 1000 })).resolves.toEqual({
|
||||
timedout: true,
|
||||
});
|
||||
|
||||
// we are not filtering in the call by event type, so it should resolve as well
|
||||
const anotherEvent = { ...event, event_type: 'another-test-event' };
|
||||
events$.next(anotherEvent);
|
||||
|
||||
await expect(getEventsPromise).resolves.toEqual([...events, anotherEvent]);
|
||||
});
|
||||
|
||||
test('should await until it times out', async () => {
|
||||
// 3 enqueued events
|
||||
const events = new Array(3).fill(event);
|
||||
events.forEach((ev) => events$.next(ev));
|
||||
|
||||
// expecting 4 with timeout of 1.5s
|
||||
const getEventsPromise = window.__analytics_ftr_helpers__.getEvents(4, {
|
||||
withTimeoutMs: 1500,
|
||||
});
|
||||
|
||||
// after 1 second it still doesn't emit
|
||||
await expect(withTimeout({ promise: getEventsPromise, timeoutMs: 1000 })).resolves.toEqual({
|
||||
timedout: true,
|
||||
});
|
||||
|
||||
// it emits 3 events at some point
|
||||
await expect(getEventsPromise).resolves.toEqual(events);
|
||||
});
|
||||
|
||||
test('should filter by event-types when provided', async () => {
|
||||
// 3 enqueued events
|
||||
const events = [
|
||||
{ ...event, event_type: 'one-test-event' },
|
||||
{ ...event, event_type: 'another-test-event' },
|
||||
{ ...event, event_type: 'skipped-test-event' },
|
||||
{ ...event, event_type: 'another-test-event' },
|
||||
];
|
||||
events.forEach((ev) => events$.next(ev));
|
||||
|
||||
await expect(
|
||||
window.__analytics_ftr_helpers__.getEvents(3, {
|
||||
eventTypes: ['one-test-event', 'another-test-event'],
|
||||
})
|
||||
).resolves.toEqual([
|
||||
{ ...event, event_type: 'one-test-event' },
|
||||
{ ...event, event_type: 'another-test-event' },
|
||||
{ ...event, event_type: 'another-test-event' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('should filter by timestamp when provided', async () => {
|
||||
// 3 enqueued events
|
||||
const events = [
|
||||
{ ...event, timestamp: '2022-01-10T00:00:00.000Z' },
|
||||
{ ...event, timestamp: '2022-03-10T00:00:00.000Z' },
|
||||
{ ...event, timestamp: '2022-06-10T00:00:00.000Z' },
|
||||
];
|
||||
events.forEach((ev) => events$.next(ev));
|
||||
|
||||
await expect(
|
||||
window.__analytics_ftr_helpers__.getEvents(2, {
|
||||
eventTypes: [event.event_type],
|
||||
fromTimestamp: '2022-03-10T00:00:00.000Z',
|
||||
})
|
||||
).resolves.toEqual([
|
||||
{ ...event, timestamp: '2022-03-10T00:00:00.000Z' },
|
||||
{ ...event, timestamp: '2022-06-10T00:00:00.000Z' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -6,10 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ReplaySubject, firstValueFrom, filter, take, toArray, map } from 'rxjs';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
import type { Plugin, CoreSetup, Event } from '@kbn/core/public';
|
||||
import { CustomShipper } from './custom_shipper';
|
||||
import './types';
|
||||
import { fetchEvents } from '../common/fetch_events';
|
||||
|
||||
export class AnalyticsFTRHelpers implements Plugin {
|
||||
private readonly events$ = new ReplaySubject<Event>();
|
||||
|
@ -21,25 +22,10 @@ export class AnalyticsFTRHelpers implements Plugin {
|
|||
setOptIn(optIn: boolean) {
|
||||
analytics.optIn({ global: { enabled: optIn } });
|
||||
},
|
||||
getEvents: async (takeNumberOfEvents, eventTypes = []) =>
|
||||
firstValueFrom(
|
||||
this.events$.pipe(
|
||||
filter((event) => {
|
||||
if (eventTypes.length > 0) {
|
||||
return eventTypes.includes(event.event_type);
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
take(takeNumberOfEvents),
|
||||
toArray(),
|
||||
// Sorting the events by timestamp... on CI it's happening an event may occur while the client is still forwarding the early ones...
|
||||
map((_events) =>
|
||||
_events.sort(
|
||||
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
getEvents: async (takeNumberOfEvents, options) =>
|
||||
fetchEvents(this.events$, takeNumberOfEvents, options),
|
||||
getEventCount: async (options) =>
|
||||
(await fetchEvents(this.events$, Number.MAX_SAFE_INTEGER, options)).length,
|
||||
};
|
||||
}
|
||||
public start() {}
|
|
@ -6,13 +6,10 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { Event, EventType } from '@kbn/core/public';
|
||||
import { EBTHelpersContract } from '../common/types';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__analytics_ftr_helpers__: {
|
||||
setOptIn: (optIn: boolean) => void;
|
||||
getEvents: (takeNumberOfEvents: number, eventTypes?: EventType[]) => Promise<Event[]>;
|
||||
};
|
||||
__analytics_ftr_helpers__: EBTHelpersContract;
|
||||
}
|
||||
}
|
|
@ -6,9 +6,10 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { firstValueFrom, ReplaySubject, filter, take, toArray, map } from 'rxjs';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import type { Plugin, CoreSetup, Event } from '@kbn/core/server';
|
||||
import { fetchEvents } from '../common/fetch_events';
|
||||
import { CustomShipper } from './custom_shipper';
|
||||
|
||||
export class AnalyticsFTRHelpers implements Plugin {
|
||||
|
@ -45,34 +46,34 @@ export class AnalyticsFTRHelpers implements Plugin {
|
|||
query: schema.object({
|
||||
takeNumberOfEvents: schema.number({ min: 1 }),
|
||||
eventTypes: schema.arrayOf(schema.string()),
|
||||
withTimeoutMs: schema.maybe(schema.number()),
|
||||
fromTimestamp: schema.maybe(schema.string()),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (context, req, res) => {
|
||||
const { takeNumberOfEvents, eventTypes } = req.query;
|
||||
|
||||
const events = await firstValueFrom(
|
||||
events$.pipe(
|
||||
filter((event) => {
|
||||
if (eventTypes.length > 0) {
|
||||
return eventTypes.includes(event.event_type);
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
take(takeNumberOfEvents),
|
||||
toArray(),
|
||||
// Sorting the events by timestamp... on CI it's happening an event may occur while the client is still forwarding the early ones...
|
||||
map((_events) =>
|
||||
_events.sort(
|
||||
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const { takeNumberOfEvents, ...options } = req.query;
|
||||
const events = await fetchEvents(events$, takeNumberOfEvents, options);
|
||||
return res.ok({ body: events });
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
{
|
||||
path: '/internal/analytics_ftr_helpers/count_events',
|
||||
validate: {
|
||||
query: schema.object({
|
||||
eventTypes: schema.arrayOf(schema.string()),
|
||||
withTimeoutMs: schema.number(),
|
||||
fromTimestamp: schema.maybe(schema.string()),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (context, req, res) => {
|
||||
const events = await fetchEvents(events$, Number.MAX_SAFE_INTEGER, req.query);
|
||||
return res.ok({ body: { count: events.length } });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public start() {}
|
|
@ -5,6 +5,7 @@
|
|||
},
|
||||
"include": [
|
||||
"index.ts",
|
||||
"common/**/*.ts",
|
||||
"public/**/*.ts",
|
||||
"server/**/*.ts",
|
||||
"../../../../../typings/**/*",
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "analytics_plugin_a",
|
||||
"version": "1.0.0",
|
||||
"main": "target/test/analytics/__fixtures__/plugins/analytics_plugin_a",
|
||||
"main": "target/test/analytics/fixtures/plugins/analytics_plugin_a",
|
||||
"kibana": {
|
||||
"version": "kibana",
|
||||
"templateVersion": "1.0.0"
|
13
test/analytics/jest.config.js
Normal file
13
test/analytics/jest.config.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../..',
|
||||
roots: ['<rootDir>/test/analytics'],
|
||||
};
|
|
@ -6,10 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../functional/ftr_provider_context';
|
||||
import '@kbn/analytics-ftr-helpers-plugin/public/types';
|
||||
import type { EBTHelpersContract } from '@kbn/analytics-ftr-helpers-plugin/common/types';
|
||||
import type { FtrProviderContext } from '../../functional/ftr_provider_context';
|
||||
|
||||
export function KibanaEBTServerProvider({ getService }: FtrProviderContext) {
|
||||
export function KibanaEBTServerProvider({ getService }: FtrProviderContext): EBTHelpersContract {
|
||||
const supertest = getService('supertest');
|
||||
|
||||
const setOptIn = async (optIn: boolean) => {
|
||||
|
@ -21,30 +22,42 @@ export function KibanaEBTServerProvider({ getService }: FtrProviderContext) {
|
|||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Change the opt-in state of the Kibana EBT client.
|
||||
* @param optIn `true` to opt-in, `false` to opt-out.
|
||||
*/
|
||||
setOptIn,
|
||||
/**
|
||||
* Returns the last events of the specified types.
|
||||
* @param takeNumberOfEvents - number of events to return
|
||||
* @param eventTypes (Optional) array of event types to return
|
||||
*/
|
||||
getEvents: async (takeNumberOfEvents: number, eventTypes: string[] = []) => {
|
||||
getEvents: async (
|
||||
takeNumberOfEvents,
|
||||
{ eventTypes = [], withTimeoutMs, fromTimestamp } = {}
|
||||
) => {
|
||||
await setOptIn(true);
|
||||
const resp = await supertest
|
||||
.get(`/internal/analytics_ftr_helpers/events`)
|
||||
.query({ takeNumberOfEvents, eventTypes: JSON.stringify(eventTypes) })
|
||||
.query({
|
||||
takeNumberOfEvents,
|
||||
eventTypes: JSON.stringify(eventTypes),
|
||||
withTimeoutMs,
|
||||
fromTimestamp,
|
||||
})
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200);
|
||||
|
||||
return resp.body;
|
||||
},
|
||||
getEventCount: async ({ eventTypes = [], withTimeoutMs, fromTimestamp }) => {
|
||||
await setOptIn(true);
|
||||
const resp = await supertest
|
||||
.get(`/internal/analytics_ftr_helpers/count_events`)
|
||||
.query({ eventTypes: JSON.stringify(eventTypes), withTimeoutMs, fromTimestamp })
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200);
|
||||
|
||||
return resp.body.count;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function KibanaEBTUIProvider({ getService, getPageObjects }: FtrProviderContext) {
|
||||
export function KibanaEBTUIProvider({
|
||||
getService,
|
||||
getPageObjects,
|
||||
}: FtrProviderContext): EBTHelpersContract {
|
||||
const { common } = getPageObjects(['common']);
|
||||
const browser = getService('browser');
|
||||
|
||||
|
@ -53,30 +66,24 @@ export function KibanaEBTUIProvider({ getService, getPageObjects }: FtrProviderC
|
|||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Change the opt-in state of the Kibana EBT client.
|
||||
* @param optIn `true` to opt-in, `false` to opt-out.
|
||||
*/
|
||||
setOptIn: async (optIn: boolean) => {
|
||||
setOptIn: async (optIn) => {
|
||||
await common.navigateToApp('home');
|
||||
await setOptIn(optIn);
|
||||
},
|
||||
/**
|
||||
* Returns the last events of the specified types.
|
||||
* @param numberOfEvents - number of events to return
|
||||
* @param eventTypes (Optional) array of event types to return
|
||||
*/
|
||||
getEvents: async (numberOfEvents: number, eventTypes: string[] = []) => {
|
||||
getEvents: async (numberOfEvents, options) => {
|
||||
await setOptIn(true);
|
||||
const events = await browser.execute(
|
||||
({ eventTypes: _eventTypes, numberOfEvents: _numberOfEvents }) =>
|
||||
window.__analytics_ftr_helpers__.getEvents(_numberOfEvents, _eventTypes),
|
||||
{
|
||||
eventTypes,
|
||||
numberOfEvents,
|
||||
}
|
||||
return await browser.execute(
|
||||
({ numberOfEvents: _numberOfEvents, options: _options }) =>
|
||||
window.__analytics_ftr_helpers__.getEvents(_numberOfEvents, _options),
|
||||
{ numberOfEvents, options }
|
||||
);
|
||||
},
|
||||
getEventCount: async (options) => {
|
||||
await setOptIn(true);
|
||||
return await browser.execute(
|
||||
({ options: _options }) => window.__analytics_ftr_helpers__.getEventCount(_options),
|
||||
{ options }
|
||||
);
|
||||
return events;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import type { Event, TelemetryCounter } from '@kbn/core/server';
|
||||
import type { Action } from '@kbn/analytics-plugin-a-plugin/server/custom_shipper';
|
||||
import type { Action } from '@kbn/analytics-plugin-a-plugin/public/custom_shipper';
|
||||
import type { FtrProviderContext } from '../services';
|
||||
import '@kbn/analytics-plugin-a-plugin/public/types';
|
||||
|
||||
|
@ -146,7 +146,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
expect(startEvent).to.be.greaterThan(setupEvent);
|
||||
|
||||
// This helps us to also test the helpers
|
||||
const events = await ebtUIHelper.getEvents(2, ['test-plugin-lifecycle']);
|
||||
const events = await ebtUIHelper.getEvents(2, { eventTypes: ['test-plugin-lifecycle'] });
|
||||
expect(events).to.eql([
|
||||
{
|
||||
timestamp: reportTestPluginLifecycleEventsAction!.meta[setupEvent].timestamp,
|
||||
|
@ -164,11 +164,45 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('it should extend the contexts with pid injected by "analytics_plugin_a"', async () => {
|
||||
const [event] = await ebtUIHelper.getEvents(1, ['test-plugin-lifecycle']);
|
||||
const [event] = await ebtUIHelper.getEvents(1, { eventTypes: ['test-plugin-lifecycle'] });
|
||||
// Validating the remote user_agent because that's the only field that it's added by the FTR plugin.
|
||||
expect(event.context).to.have.property('user_agent');
|
||||
expect(event.context.user_agent).to.be.a('string');
|
||||
});
|
||||
|
||||
describe('Test helpers capabilities', () => {
|
||||
it('should return the count of the events', async () => {
|
||||
const eventCount = await ebtUIHelper.getEventCount({
|
||||
withTimeoutMs: 500,
|
||||
eventTypes: ['test-plugin-lifecycle'],
|
||||
});
|
||||
expect(eventCount).to.be(2);
|
||||
});
|
||||
|
||||
it('should return 0 events when filtering by timestamp', async () => {
|
||||
const eventCount = await ebtUIHelper.getEventCount({
|
||||
withTimeoutMs: 500,
|
||||
eventTypes: ['test-plugin-lifecycle'],
|
||||
fromTimestamp: new Date().toISOString(),
|
||||
});
|
||||
expect(eventCount).to.be(0);
|
||||
});
|
||||
|
||||
it('should return 1 event when filtering by the latest timestamp', async () => {
|
||||
const events = await ebtUIHelper.getEvents(Number.MAX_SAFE_INTEGER, {
|
||||
eventTypes: ['test-plugin-lifecycle'],
|
||||
withTimeoutMs: 500,
|
||||
});
|
||||
expect(events.length).to.be.greaterThan(0);
|
||||
const lastEvent = events[events.length - 1];
|
||||
const eventCount = await ebtUIHelper.getEventCount({
|
||||
withTimeoutMs: 500,
|
||||
eventTypes: ['test-plugin-lifecycle'],
|
||||
fromTimestamp: lastEvent.timestamp,
|
||||
});
|
||||
expect(eventCount).to.be(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -139,7 +139,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(startEvent).to.be.greaterThan(setupEvent);
|
||||
|
||||
// This helps us to also test the helpers
|
||||
const events = await ebtServerHelper.getEvents(2, ['test-plugin-lifecycle']);
|
||||
const events = await ebtServerHelper.getEvents(2, {
|
||||
eventTypes: ['test-plugin-lifecycle'],
|
||||
});
|
||||
expect(events).to.eql([
|
||||
{
|
||||
timestamp: reportTestPluginLifecycleEventsAction!.meta[setupEvent].timestamp,
|
||||
|
@ -157,11 +159,46 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('it should extend the contexts with pid injected by "analytics_plugin_a"', async () => {
|
||||
const [event] = await ebtServerHelper.getEvents(1, ['test-plugin-lifecycle']);
|
||||
const [event] = await ebtServerHelper.getEvents(1, {
|
||||
eventTypes: ['test-plugin-lifecycle'],
|
||||
});
|
||||
// Validating the remote PID because that's the only field that it's added by the FTR plugin.
|
||||
expect(event.context).to.have.property('pid');
|
||||
expect(event.context.pid).to.be.a('number');
|
||||
});
|
||||
|
||||
describe('Test helpers capabilities', () => {
|
||||
it('should return the count of the events', async () => {
|
||||
const eventCount = await ebtServerHelper.getEventCount({
|
||||
withTimeoutMs: 500,
|
||||
eventTypes: ['test-plugin-lifecycle'],
|
||||
});
|
||||
expect(eventCount).to.be(2);
|
||||
});
|
||||
|
||||
it('should return 0 events when filtering by timestamp', async () => {
|
||||
const eventCount = await ebtServerHelper.getEventCount({
|
||||
withTimeoutMs: 500,
|
||||
eventTypes: ['test-plugin-lifecycle'],
|
||||
fromTimestamp: new Date().toISOString(),
|
||||
});
|
||||
expect(eventCount).to.be(0);
|
||||
});
|
||||
it('should return 1 event when filtering by the latest timestamp', async () => {
|
||||
const events = await ebtServerHelper.getEvents(Number.MAX_SAFE_INTEGER, {
|
||||
withTimeoutMs: 500,
|
||||
eventTypes: ['test-plugin-lifecycle'],
|
||||
});
|
||||
expect(events.length).to.be.greaterThan(0);
|
||||
const lastEvent = events[events.length - 1];
|
||||
const eventCount = await ebtServerHelper.getEventCount({
|
||||
withTimeoutMs: 500,
|
||||
eventTypes: ['test-plugin-lifecycle'],
|
||||
fromTimestamp: lastEvent.timestamp,
|
||||
});
|
||||
expect(eventCount).to.be(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('should emit a "click" event', async () => {
|
||||
const [event] = await ebtUIHelper.getEvents(1, ['click']);
|
||||
const [event] = await ebtUIHelper.getEvents(1, { eventTypes: ['click'] });
|
||||
expect(event.event_type).to.eql('click');
|
||||
expect(event.properties.target).to.be.an('array');
|
||||
const targets = event.properties.target as string[];
|
||||
|
|
|
@ -19,7 +19,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
let event: Event;
|
||||
before(async () => {
|
||||
await common.navigateToApp('home');
|
||||
[event] = await ebtUIHelper.getEvents(1, ['Loaded Kibana']); // Get the loaded Kibana event
|
||||
[event] = await ebtUIHelper.getEvents(1, { eventTypes: ['Loaded Kibana'] }); // Get the loaded Kibana event
|
||||
});
|
||||
|
||||
it('should have the properties provided by the "cluster info" context provider', () => {
|
||||
|
@ -74,7 +74,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
it('should have the properties provided by the "license info" context provider', async () => {
|
||||
await common.clickAndValidate('kibanaChrome', 'kibanaChrome');
|
||||
[event] = await ebtUIHelper.getEvents(1, ['click']); // Get a later event to ensure license has been obtained already.
|
||||
[event] = await ebtUIHelper.getEvents(1, { eventTypes: ['click'] }); // Get a later event to ensure license has been obtained already.
|
||||
expect(event.context).to.have.property('license_id');
|
||||
expect(event.context.license_id).to.be.a('string');
|
||||
expect(event.context).to.have.property('license_status');
|
||||
|
|
|
@ -20,7 +20,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('should emit the "Loaded Kibana" event', async () => {
|
||||
const [event] = await ebtUIHelper.getEvents(1, ['Loaded Kibana']);
|
||||
const [event] = await ebtUIHelper.getEvents(1, { eventTypes: ['Loaded Kibana'] });
|
||||
expect(event.event_type).to.eql('Loaded Kibana');
|
||||
expect(event.properties).to.have.property('kibana_version');
|
||||
expect(event.properties.kibana_version).to.be.a('string');
|
||||
|
|
|
@ -20,7 +20,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
let i = 2;
|
||||
do {
|
||||
// Wait until we get a GREEN "status_changed" event. At that point all the context providers should be set up.
|
||||
const events = await ebtServerHelper.getEvents(i, ['core-overall_status_changed']);
|
||||
const events = await ebtServerHelper.getEvents(i, {
|
||||
eventTypes: ['core-overall_status_changed'],
|
||||
});
|
||||
event = events[i - 1];
|
||||
i++;
|
||||
} while (event.properties.overall_status_level !== 'available');
|
||||
|
|
|
@ -18,9 +18,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
let secondEvent: Event;
|
||||
|
||||
before(async () => {
|
||||
[initialEvent, secondEvent] = await ebtServerHelper.getEvents(2, [
|
||||
'core-overall_status_changed',
|
||||
]);
|
||||
[initialEvent, secondEvent] = await ebtServerHelper.getEvents(2, {
|
||||
eventTypes: ['core-overall_status_changed'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit the initial "degraded" event with the context set to `initializing`', () => {
|
||||
|
|
|
@ -14,16 +14,20 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
describe('kibana_started', () => {
|
||||
it('should emit the "kibana_started" event', async () => {
|
||||
const [event] = await ebtServerHelper.getEvents(1, ['kibana_started']);
|
||||
const [event] = await ebtServerHelper.getEvents(1, { eventTypes: ['kibana_started'] });
|
||||
expect(event.event_type).to.eql('kibana_started');
|
||||
expect(event.properties.uptime_per_step.constructor.start).to.be.a('number');
|
||||
expect(event.properties.uptime_per_step.constructor.end).to.be.a('number');
|
||||
expect(event.properties.uptime_per_step.preboot.start).to.be.a('number');
|
||||
expect(event.properties.uptime_per_step.preboot.end).to.be.a('number');
|
||||
expect(event.properties.uptime_per_step.setup.start).to.be.a('number');
|
||||
expect(event.properties.uptime_per_step.setup.end).to.be.a('number');
|
||||
expect(event.properties.uptime_per_step.start.start).to.be.a('number');
|
||||
expect(event.properties.uptime_per_step.start.end).to.be.a('number');
|
||||
const uptimePerStep = event.properties.uptime_per_step as Record<
|
||||
'constructor' | 'preboot' | 'setup' | 'start',
|
||||
Record<'start' | 'end', number>
|
||||
>;
|
||||
expect(uptimePerStep.constructor.start).to.be.a('number');
|
||||
expect(uptimePerStep.constructor.end).to.be.a('number');
|
||||
expect(uptimePerStep.preboot.start).to.be.a('number');
|
||||
expect(uptimePerStep.preboot.end).to.be.a('number');
|
||||
expect(uptimePerStep.setup.start).to.be.a('number');
|
||||
expect(uptimePerStep.setup.end).to.be.a('number');
|
||||
expect(uptimePerStep.start.start).to.be.a('number');
|
||||
expect(uptimePerStep.start.end).to.be.a('number');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"types": [
|
||||
"node",
|
||||
"node",
|
||||
"@emotion/react/types/css-prop",
|
||||
"@kbn/ambient-ui-types",
|
||||
]
|
||||
|
@ -22,7 +22,7 @@
|
|||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
"analytics/__fixtures__/plugins/**/*",
|
||||
"analytics/fixtures/plugins/**/*",
|
||||
"interpreter_functional/plugins/**/*",
|
||||
"plugin_functional/plugins/**/*",
|
||||
"server_integration/__fixtures__/plugins/**/*",
|
||||
|
@ -59,8 +59,8 @@
|
|||
{ "path": "../src/plugins/usage_collection/tsconfig.json" },
|
||||
{ "path": "../src/plugins/data_view_management/tsconfig.json" },
|
||||
{ "path": "../src/plugins/visualizations/tsconfig.json" },
|
||||
{ "path": "analytics/__fixtures__/plugins/analytics_ftr_helpers/tsconfig.json"},
|
||||
{ "path": "analytics/__fixtures__/plugins/analytics_plugin_a/tsconfig.json"},
|
||||
{ "path": "analytics/fixtures/plugins/analytics_ftr_helpers/tsconfig.json"},
|
||||
{ "path": "analytics/fixtures/plugins/analytics_plugin_a/tsconfig.json"},
|
||||
{ "path": "interactive_setup_api_integration/fixtures/test_endpoints/tsconfig.json" },
|
||||
{ "path": "plugin_functional/plugins/core_app_status/tsconfig.json" },
|
||||
{ "path": "plugin_functional/plugins/core_provider_plugin/tsconfig.json" },
|
||||
|
|
|
@ -191,10 +191,10 @@
|
|||
"@kbn/vis-type-xy-plugin/*": ["src/plugins/vis_types/xy/*"],
|
||||
"@kbn/visualizations-plugin": ["src/plugins/visualizations"],
|
||||
"@kbn/visualizations-plugin/*": ["src/plugins/visualizations/*"],
|
||||
"@kbn/analytics-ftr-helpers-plugin": ["test/analytics/__fixtures__/plugins/analytics_ftr_helpers"],
|
||||
"@kbn/analytics-ftr-helpers-plugin/*": ["test/analytics/__fixtures__/plugins/analytics_ftr_helpers/*"],
|
||||
"@kbn/analytics-plugin-a-plugin": ["test/analytics/__fixtures__/plugins/analytics_plugin_a"],
|
||||
"@kbn/analytics-plugin-a-plugin/*": ["test/analytics/__fixtures__/plugins/analytics_plugin_a/*"],
|
||||
"@kbn/analytics-ftr-helpers-plugin": ["test/analytics/fixtures/plugins/analytics_ftr_helpers"],
|
||||
"@kbn/analytics-ftr-helpers-plugin/*": ["test/analytics/fixtures/plugins/analytics_ftr_helpers/*"],
|
||||
"@kbn/analytics-plugin-a-plugin": ["test/analytics/fixtures/plugins/analytics_plugin_a"],
|
||||
"@kbn/analytics-plugin-a-plugin/*": ["test/analytics/fixtures/plugins/analytics_plugin_a/*"],
|
||||
"@kbn/coverage-fixtures-plugin": ["test/common/fixtures/plugins/coverage"],
|
||||
"@kbn/coverage-fixtures-plugin/*": ["test/common/fixtures/plugins/coverage/*"],
|
||||
"@kbn/newsfeed-fixtures-plugin": ["test/common/fixtures/plugins/newsfeed"],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue