mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Expressions] Partial results example plugin (#113001)
* Update mapColumn expression function implementation to support partial results * Add partial results example plugin
This commit is contained in:
parent
9e95786b42
commit
16aa9bf85f
17 changed files with 485 additions and 56 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -56,6 +56,7 @@
|
|||
/examples/url_generators_examples/ @elastic/kibana-app-services
|
||||
/examples/url_generators_explorer/ @elastic/kibana-app-services
|
||||
/examples/field_formats_example/ @elastic/kibana-app-services
|
||||
/examples/partial_results_example/ @elastic/kibana-app-services
|
||||
/packages/elastic-datemath/ @elastic/kibana-app-services
|
||||
/packages/kbn-interpreter/ @elastic/kibana-app-services
|
||||
/src/plugins/bfetch/ @elastic/kibana-app-services
|
||||
|
|
9
examples/partial_results_example/README.md
Executable file
9
examples/partial_results_example/README.md
Executable file
|
@ -0,0 +1,9 @@
|
|||
## Partial Results Example
|
||||
|
||||
The partial results is a feature of the expressions plugin allowing to emit intermediate execution results over time.
|
||||
|
||||
This example plugin demonstrates:
|
||||
|
||||
1. An expression function emitting a datatable with intermediate results (`getEvents`).
|
||||
2. An expression function emitting an infinite number of results (`countEvent`).
|
||||
3. A combination of those two functions using the `mapColumn` function that continuously updates the resulting table.
|
12
examples/partial_results_example/kibana.json
Normal file
12
examples/partial_results_example/kibana.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"id": "paertialResultsExample",
|
||||
"version": "0.1.0",
|
||||
"kibanaVersion": "kibana",
|
||||
"ui": true,
|
||||
"owner": {
|
||||
"name": "App Services",
|
||||
"githubTeam": "kibana-app-services"
|
||||
},
|
||||
"description": "A plugin demonstrating partial results in the expressions plugin",
|
||||
"requiredPlugins": ["developerExamples", "expressions"]
|
||||
}
|
90
examples/partial_results_example/public/app/app.tsx
Normal file
90
examples/partial_results_example/public/app/app.tsx
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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 React, { useContext, useEffect, useState } from 'react';
|
||||
import { pluck } from 'rxjs/operators';
|
||||
import {
|
||||
EuiBasicTable,
|
||||
EuiCallOut,
|
||||
EuiCodeBlock,
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiPageContentBody,
|
||||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import type { Datatable } from 'src/plugins/expressions';
|
||||
import { ExpressionsContext } from './expressions_context';
|
||||
|
||||
const expression = `getEvents
|
||||
| mapColumn name="Count" expression={
|
||||
countEvent {pluck "event"}
|
||||
}
|
||||
`;
|
||||
|
||||
export function App() {
|
||||
const expressions = useContext(ExpressionsContext);
|
||||
const [datatable, setDatatable] = useState<Datatable>();
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = expressions
|
||||
?.execute<null, Datatable>(expression, null)
|
||||
.getData()
|
||||
.pipe(pluck('result'))
|
||||
.subscribe((value) => setDatatable(value as Datatable));
|
||||
|
||||
return () => subscription?.unsubscribe();
|
||||
}, [expressions]);
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody style={{ maxWidth: 1200, margin: '0 auto' }}>
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiTitle size="l">
|
||||
<h1>Partial Results Demo</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentBody style={{ maxWidth: 800, margin: '0 auto' }}>
|
||||
<EuiText data-test-subj="example-help">
|
||||
<p>
|
||||
This example listens for the window events and adds them to the table along with a
|
||||
trigger counter.
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size={'m'} />
|
||||
<EuiCodeBlock>{expression}</EuiCodeBlock>
|
||||
<EuiSpacer size={'m'} />
|
||||
{datatable ? (
|
||||
<EuiBasicTable
|
||||
textOnly={true}
|
||||
data-test-subj={'example-table'}
|
||||
columns={datatable.columns?.map(({ id: field, name }) => ({
|
||||
field,
|
||||
name,
|
||||
'data-test-subj': `example-column-${field.toLowerCase()}`,
|
||||
}))}
|
||||
items={datatable.rows ?? []}
|
||||
/>
|
||||
) : (
|
||||
<EuiCallOut color="success">
|
||||
<p>Click or press any key.</p>
|
||||
</EuiCallOut>
|
||||
)}
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 { createContext } from 'react';
|
||||
import type { ExpressionsServiceStart } from 'src/plugins/expressions';
|
||||
|
||||
export const ExpressionsContext = createContext<ExpressionsServiceStart | undefined>(undefined);
|
10
examples/partial_results_example/public/app/index.ts
Normal file
10
examples/partial_results_example/public/app/index.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './app';
|
||||
export * from './expressions_context';
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 { Observable, fromEvent } from 'rxjs';
|
||||
import { scan, startWith } from 'rxjs/operators';
|
||||
import type { ExpressionFunctionDefinition } from 'src/plugins/expressions';
|
||||
|
||||
export interface CountEventArguments {
|
||||
event: string;
|
||||
}
|
||||
|
||||
export const countEvent: ExpressionFunctionDefinition<
|
||||
'countEvent',
|
||||
null,
|
||||
CountEventArguments,
|
||||
Observable<number>
|
||||
> = {
|
||||
name: 'countEvent',
|
||||
type: 'number',
|
||||
help: 'Subscribes for an event and counts a number of triggers.',
|
||||
args: {
|
||||
event: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: 'The event name.',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
fn(input, { event }) {
|
||||
return fromEvent(window, event).pipe(
|
||||
scan((count) => count + 1, 1),
|
||||
startWith(1)
|
||||
);
|
||||
},
|
||||
};
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { Observable, fromEvent, merge } from 'rxjs';
|
||||
import { distinct, map, pluck, scan, take } from 'rxjs/operators';
|
||||
import type { Datatable, ExpressionFunctionDefinition } from 'src/plugins/expressions';
|
||||
|
||||
const EVENTS: Array<keyof WindowEventMap> = [
|
||||
'mousedown',
|
||||
'mouseup',
|
||||
'click',
|
||||
'keydown',
|
||||
'keyup',
|
||||
'keypress',
|
||||
];
|
||||
|
||||
export const getEvents: ExpressionFunctionDefinition<
|
||||
'getEvents',
|
||||
null,
|
||||
{},
|
||||
Observable<Datatable>
|
||||
> = {
|
||||
name: 'getEvents',
|
||||
type: 'datatable',
|
||||
help: 'Listens for the window events and returns a table with the triggered ones.',
|
||||
args: {},
|
||||
fn() {
|
||||
return merge(...EVENTS.map((event) => fromEvent(window, event))).pipe(
|
||||
pluck('type'),
|
||||
distinct(),
|
||||
take(EVENTS.length),
|
||||
scan((events, event) => [...events, event], [] as string[]),
|
||||
map((events) => ({
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{
|
||||
id: 'event',
|
||||
meta: { type: 'string' },
|
||||
name: 'Event',
|
||||
},
|
||||
],
|
||||
rows: Array.from(events).map((event) => ({ event })),
|
||||
}))
|
||||
);
|
||||
},
|
||||
};
|
11
examples/partial_results_example/public/functions/index.ts
Normal file
11
examples/partial_results_example/public/functions/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './count_event';
|
||||
export * from './get_events';
|
||||
export * from './pluck';
|
32
examples/partial_results_example/public/functions/pluck.ts
Normal file
32
examples/partial_results_example/public/functions/pluck.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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 { Datatable, ExpressionFunctionDefinition } from 'src/plugins/expressions';
|
||||
|
||||
export interface PluckArguments {
|
||||
key: string;
|
||||
}
|
||||
|
||||
export const pluck: ExpressionFunctionDefinition<'pluck', Datatable, PluckArguments, unknown> = {
|
||||
name: 'pluck',
|
||||
inputTypes: ['datatable'],
|
||||
help: 'Takes a cell from the first table row.',
|
||||
args: {
|
||||
key: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: 'The column id.',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
fn({ rows }, { key }) {
|
||||
const [{ [key]: value }] = rows;
|
||||
|
||||
return value;
|
||||
},
|
||||
};
|
12
examples/partial_results_example/public/index.ts
Executable file
12
examples/partial_results_example/public/index.ts
Executable file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 { PartialResultsExamplePlugin } from './plugin';
|
||||
|
||||
export function plugin() {
|
||||
return new PartialResultsExamplePlugin();
|
||||
}
|
57
examples/partial_results_example/public/plugin.tsx
Executable file
57
examples/partial_results_example/public/plugin.tsx
Executable file
|
@ -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 React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import type { ExpressionsService, ExpressionsServiceSetup } from 'src/plugins/expressions';
|
||||
import { AppMountParameters, AppNavLinkStatus, CoreSetup, Plugin } from '../../../src/core/public';
|
||||
import type { DeveloperExamplesSetup } from '../../developer_examples/public';
|
||||
import { App, ExpressionsContext } from './app';
|
||||
import { countEvent, getEvents, pluck } from './functions';
|
||||
|
||||
interface SetupDeps {
|
||||
developerExamples: DeveloperExamplesSetup;
|
||||
expressions: ExpressionsServiceSetup;
|
||||
}
|
||||
|
||||
export class PartialResultsExamplePlugin implements Plugin<void, void, SetupDeps> {
|
||||
private expressions?: ExpressionsService;
|
||||
|
||||
setup({ application }: CoreSetup, { expressions, developerExamples }: SetupDeps) {
|
||||
this.expressions = expressions.fork();
|
||||
this.expressions.registerFunction(countEvent);
|
||||
this.expressions.registerFunction(getEvents);
|
||||
this.expressions.registerFunction(pluck);
|
||||
|
||||
application.register({
|
||||
id: 'partialResultsExample',
|
||||
title: 'Partial Results Example',
|
||||
navLinkStatus: AppNavLinkStatus.hidden,
|
||||
mount: async ({ element }: AppMountParameters) => {
|
||||
ReactDOM.render(
|
||||
<ExpressionsContext.Provider value={this.expressions}>
|
||||
<App />
|
||||
</ExpressionsContext.Provider>,
|
||||
element
|
||||
);
|
||||
return () => ReactDOM.unmountComponentAtNode(element);
|
||||
},
|
||||
});
|
||||
|
||||
developerExamples.register({
|
||||
appId: 'partialResultsExample',
|
||||
title: 'Partial Results Example',
|
||||
description: 'Learn how to use partial results in the expressions plugin.',
|
||||
});
|
||||
}
|
||||
|
||||
start() {
|
||||
return {};
|
||||
}
|
||||
|
||||
stop() {}
|
||||
}
|
19
examples/partial_results_example/tsconfig.json
Normal file
19
examples/partial_results_example/tsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./target",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"index.ts",
|
||||
"public/**/*.ts",
|
||||
"public/**/*.tsx",
|
||||
"../../typings/**/*"
|
||||
],
|
||||
"exclude": [],
|
||||
"references": [
|
||||
{ "path": "../../src/core/tsconfig.json" },
|
||||
{ "path": "../developer_examples/tsconfig.json" },
|
||||
{ "path": "../../src/plugins/expressions/tsconfig.json" },
|
||||
]
|
||||
}
|
|
@ -6,11 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Observable, defer, of, zip } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Observable, combineLatest, defer } from 'rxjs';
|
||||
import { defaultIfEmpty, map } from 'rxjs/operators';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExpressionFunctionDefinition } from '../types';
|
||||
import { Datatable, DatatableColumn, DatatableColumnType, getType } from '../../expression_types';
|
||||
import { Datatable, DatatableColumnType, getType } from '../../expression_types';
|
||||
|
||||
export interface MapColumnArguments {
|
||||
id?: string | null;
|
||||
|
@ -81,64 +81,59 @@ export const mapColumn: ExpressionFunctionDefinition<
|
|||
},
|
||||
},
|
||||
fn(input, args) {
|
||||
const metaColumn = args.copyMetaFrom
|
||||
? input.columns.find(({ id }) => id === args.copyMetaFrom)
|
||||
: undefined;
|
||||
const existingColumnIndex = input.columns.findIndex(({ id, name }) =>
|
||||
args.id ? id === args.id : name === args.name
|
||||
);
|
||||
const id = input.columns[existingColumnIndex]?.id ?? args.id ?? args.name;
|
||||
const columnIndex = existingColumnIndex === -1 ? input.columns.length : existingColumnIndex;
|
||||
const id = input.columns[columnIndex]?.id ?? args.id ?? args.name;
|
||||
|
||||
return defer(() => {
|
||||
const rows$ = input.rows.length
|
||||
? zip(
|
||||
...input.rows.map((row) =>
|
||||
args
|
||||
.expression({
|
||||
type: 'datatable',
|
||||
columns: [...input.columns],
|
||||
rows: [row],
|
||||
})
|
||||
.pipe(map((value) => ({ ...row, [id]: value })))
|
||||
return defer(() =>
|
||||
combineLatest(
|
||||
input.rows.map((row) =>
|
||||
args
|
||||
.expression({
|
||||
type: 'datatable',
|
||||
columns: [...input.columns],
|
||||
rows: [row],
|
||||
})
|
||||
.pipe(
|
||||
map((value) => ({ ...row, [id]: value })),
|
||||
defaultIfEmpty(row)
|
||||
)
|
||||
)
|
||||
: of([]);
|
||||
|
||||
return rows$.pipe<Datatable>(
|
||||
map((rows) => {
|
||||
let type: DatatableColumnType = 'null';
|
||||
if (rows.length) {
|
||||
for (const row of rows) {
|
||||
const rowType = getType(row[id]);
|
||||
if (rowType !== 'null') {
|
||||
type = rowType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const newColumn: DatatableColumn = {
|
||||
id,
|
||||
name: args.name,
|
||||
meta: { type, params: { id: type } },
|
||||
};
|
||||
if (args.copyMetaFrom) {
|
||||
const metaSourceFrom = input.columns.find(
|
||||
({ id: columnId }) => columnId === args.copyMetaFrom
|
||||
);
|
||||
newColumn.meta = { ...newColumn.meta, ...(metaSourceFrom?.meta ?? {}) };
|
||||
)
|
||||
)
|
||||
).pipe(
|
||||
defaultIfEmpty([] as Datatable['rows']),
|
||||
map((rows) => {
|
||||
let type: DatatableColumnType = 'null';
|
||||
for (const row of rows) {
|
||||
const rowType = getType(row[id]);
|
||||
if (rowType !== 'null') {
|
||||
type = rowType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const columns = [...input.columns];
|
||||
if (existingColumnIndex === -1) {
|
||||
columns.push(newColumn);
|
||||
} else {
|
||||
columns[existingColumnIndex] = newColumn;
|
||||
}
|
||||
const columns = [...input.columns];
|
||||
columns[columnIndex] = {
|
||||
id,
|
||||
name: args.name,
|
||||
meta: {
|
||||
type,
|
||||
params: { id: type },
|
||||
...(metaColumn?.meta ?? {}),
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
columns,
|
||||
rows,
|
||||
type: 'datatable',
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
return {
|
||||
columns,
|
||||
rows,
|
||||
type: 'datatable',
|
||||
};
|
||||
})
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -12,8 +12,9 @@ import { Datatable } from '../../../expression_types';
|
|||
import { mapColumn, MapColumnArguments } from '../map_column';
|
||||
import { emptyTable, functionWrapper, testTable, tableWithNulls } from './utils';
|
||||
|
||||
const pricePlusTwo = (datatable: Datatable) =>
|
||||
of(typeof datatable.rows[0].price === 'number' ? datatable.rows[0].price + 2 : null);
|
||||
const pricePlusTwo = jest.fn((datatable: Datatable) =>
|
||||
of(typeof datatable.rows[0].price === 'number' ? datatable.rows[0].price + 2 : null)
|
||||
);
|
||||
|
||||
describe('mapColumn', () => {
|
||||
const fn = functionWrapper(mapColumn);
|
||||
|
@ -266,4 +267,33 @@ describe('mapColumn', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('supports partial results', () => {
|
||||
testScheduler.run(({ expectObservable, cold }) => {
|
||||
pricePlusTwo.mockReturnValueOnce(cold('ab|', { a: 1000, b: 2000 }));
|
||||
|
||||
expectObservable(
|
||||
runFn(testTable, {
|
||||
id: 'pricePlusTwo',
|
||||
name: 'pricePlusTwo',
|
||||
expression: pricePlusTwo,
|
||||
})
|
||||
).toBe('01|', [
|
||||
expect.objectContaining({
|
||||
rows: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
pricePlusTwo: 1000,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
rows: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
pricePlusTwo: 2000,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -32,6 +32,7 @@ export default async function ({ readConfigFile }) {
|
|||
require.resolve('./expressions_explorer'),
|
||||
require.resolve('./index_pattern_field_editor_example'),
|
||||
require.resolve('./field_formats'),
|
||||
require.resolve('./partial_results'),
|
||||
],
|
||||
services: {
|
||||
...functionalConfig.get('services'),
|
||||
|
|
47
test/examples/partial_results/index.ts
Normal file
47
test/examples/partial_results/index.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from 'test/functional/ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const PageObjects = getPageObjects(['common']);
|
||||
|
||||
describe('Partial Results Example', function () {
|
||||
before(async () => {
|
||||
this.tags('ciGroup2');
|
||||
await PageObjects.common.navigateToApp('partialResultsExample');
|
||||
|
||||
const element = await testSubjects.find('example-help');
|
||||
|
||||
await element.click();
|
||||
await element.click();
|
||||
await element.click();
|
||||
});
|
||||
|
||||
it('should trace mouse events', async () => {
|
||||
const events = await Promise.all(
|
||||
(
|
||||
await testSubjects.findAll('example-column-event')
|
||||
).map((wrapper) => wrapper.getVisibleText())
|
||||
);
|
||||
expect(events).to.eql(['mousedown', 'mouseup', 'click']);
|
||||
});
|
||||
|
||||
it('should keep track of the events number', async () => {
|
||||
const counters = await Promise.all(
|
||||
(
|
||||
await testSubjects.findAll('example-column-count')
|
||||
).map((wrapper) => wrapper.getVisibleText())
|
||||
);
|
||||
expect(counters).to.eql(['3', '3', '3']);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue