[UnifiedFieldList] Remove redundant server routes. Create new example plugin for unified field list components and migrate tests. (#158377)

- Closes https://github.com/elastic/kibana/issues/147885
- Closes https://github.com/elastic/kibana/issues/157109

## Summary

**Before:**

Unified Field List plugin has internal routes (wrappers for client code)
which exist only to run api functional tests against them:
 - `/api/unified_field_list/existing_fields/{dataViewId}`
 - `/api/unified_field_list/field_stats`

Client code does not call these routes directly. So there is no reason
in keeping and versioning them.

**After:**
- Internal routes are removed 
- A new "Unified Field List Examples" page was created
http://localhost:5601/app/unifiedFieldListExamples
- API functional tests (which used the routes) were converted to
functional tests against this new example page
- Created a new `unifiedFieldList` page object which is used now in
functional tests (methods are extracted from existing `discover` page
object).

**For testing:**

Steps:
1. Run Kibana with examples: `yarn start --run-examples` 
2. Install sample data
3. And navigate to Developer Examples > Unified Field List Examples
page.

![May-26-2023
13-24-03](5a2149f7-beb8-40a5-b7d5-9eeaabfd42ca)


### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Julia Rechkunova 2023-05-31 13:25:47 +02:00 committed by GitHub
parent f43096a78f
commit 8d399fe3aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
90 changed files with 2104 additions and 1846 deletions

1
.github/CODEOWNERS vendored
View file

@ -698,6 +698,7 @@ test/plugin_functional/plugins/ui_settings_plugin @elastic/kibana-core
packages/kbn-ui-shared-deps-npm @elastic/kibana-operations
packages/kbn-ui-shared-deps-src @elastic/kibana-operations
packages/kbn-ui-theme @elastic/kibana-operations
examples/unified_field_list_examples @elastic/kibana-data-discovery
src/plugins/unified_field_list @elastic/kibana-data-discovery
src/plugins/unified_histogram @elastic/kibana-data-discovery
src/plugins/unified_search @elastic/kibana-visualizations

View file

@ -0,0 +1,9 @@
# unified_field_list_examples
Examples of unified field list components.
To run this example, ensure you have data to search against (for example, the sample datasets) and start kibana with the `--run-examples` flag.
```bash
yarn start --run-examples
```

View file

@ -6,6 +6,5 @@
* Side Public License, v 1.
*/
export const BASE_API_PATH = '/internal/unified_field_list';
export const FIELD_STATS_API_PATH = `${BASE_API_PATH}/field_stats`;
export const FIELD_EXISTING_API_PATH = `${BASE_API_PATH}/existing_fields/{dataViewId}`;
export const PLUGIN_ID = 'unifiedFieldListExamples';
export const PLUGIN_NAME = 'Unified Field List Examples';

View file

@ -0,0 +1,24 @@
{
"type": "plugin",
"id": "@kbn/unified-field-list-examples-plugin",
"owner": "@elastic/kibana-data-discovery",
"description": "Examples for using unified field list.",
"plugin": {
"id": "unifiedFieldListExamples",
"server": false,
"browser": true,
"requiredPlugins": [
"navigation",
"developerExamples",
"inspector",
"kibanaUtils",
"unifiedSearch",
"unifiedFieldList",
"data",
"dataViews",
"dataViewFieldEditor",
"charts",
"fieldFormats"
]
}
}

View file

@ -0,0 +1,37 @@
/*
* 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 { I18nProvider } from '@kbn/i18n-react';
import type { AppMountParameters, CoreStart } from '@kbn/core/public';
import { AppPluginStartDependencies } from './types';
import { UnifiedFieldListExampleApp } from './example_app';
export const renderApp = (
core: CoreStart,
deps: AppPluginStartDependencies,
{ element }: AppMountParameters
) => {
ReactDOM.render(
<I18nProvider>
<UnifiedFieldListExampleApp
services={{
core,
uiSettings: core.uiSettings,
...deps,
}}
/>
</I18nProvider>,
element
);
return () => {
ReactDOM.unmountComponentAtNode(element);
};
};

View file

@ -0,0 +1,161 @@
/*
* 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, { useCallback, useEffect, useState } from 'react';
import { css } from '@emotion/react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiPage,
EuiPageBody,
EuiPageSidebar,
EuiTitle,
EuiEmptyPrompt,
EuiLoadingLogo,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import type { DataView } from '@kbn/data-views-plugin/public';
import { RootDragDropProvider } from '@kbn/dom-drag-drop';
import type { DataViewField } from '@kbn/data-views-plugin/public';
import { PLUGIN_ID, PLUGIN_NAME } from '../common';
import { FieldListSidebar, FieldListSidebarProps } from './field_list_sidebar';
import { ExampleDropZone } from './example_drop_zone';
interface UnifiedFieldListExampleAppProps {
services: FieldListSidebarProps['services'];
}
export const UnifiedFieldListExampleApp: React.FC<UnifiedFieldListExampleAppProps> = ({
services,
}) => {
const { navigation, data, unifiedSearch } = services;
const { IndexPatternSelect } = unifiedSearch.ui;
const [dataView, setDataView] = useState<DataView | null>();
const [selectedFieldNames, setSelectedFieldNames] = useState<string[]>([]);
const onAddFieldToWorkplace = useCallback(
(field: DataViewField) => {
setSelectedFieldNames((names) => [...names, field.name]);
},
[setSelectedFieldNames]
);
const onRemoveFieldFromWorkspace = useCallback(
(field: DataViewField) => {
setSelectedFieldNames((names) => names.filter((name) => name !== field.name));
},
[setSelectedFieldNames]
);
const onDropFieldToWorkplace = useCallback(
(fieldName: string) => {
setSelectedFieldNames((names) => [...names.filter((name) => name !== fieldName), fieldName]);
},
[setSelectedFieldNames]
);
// Fetch the default data view using the `data.dataViews` service, as the component is mounted.
useEffect(() => {
const setDefaultDataView = async () => {
try {
const defaultDataView = await data.dataViews.getDefault();
setDataView(defaultDataView);
} catch (e) {
setDataView(null);
}
};
setDefaultDataView();
}, [data]);
if (typeof dataView === 'undefined') {
return (
<EuiEmptyPrompt
icon={<EuiLoadingLogo logo="logoKibana" size="xl" />}
title={<h2>{PLUGIN_NAME}</h2>}
body={<p>Loading...</p>}
/>
);
}
return (
<EuiPage grow={true}>
<EuiPageBody paddingSize="s">
<EuiFlexGroup direction="column" gutterSize="xs">
<EuiFlexItem grow={false}>
<EuiTitle>
<h1>{PLUGIN_NAME}</h1>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<IndexPatternSelect
placeholder={i18n.translate('searchSessionExample.selectDataViewPlaceholder', {
defaultMessage: 'Select data view',
})}
indexPatternId={dataView?.id || ''}
onChange={async (dataViewId?: string) => {
if (dataViewId) {
const newDataView = await data.dataViews.get(dataViewId);
setDataView(newDataView);
} else {
setDataView(undefined);
}
}}
isClearable={false}
data-test-subj="dataViewSelector"
/>
</EuiFlexItem>
{dataView ? (
<>
<EuiFlexItem grow={false}>
<navigation.ui.TopNavMenu
appName={PLUGIN_ID}
showSearchBar={true}
useDefaultBehaviors={true}
indexPatterns={dataView ? [dataView] : undefined}
/>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<RootDragDropProvider>
<EuiFlexGroup direction="row" alignItems="stretch">
<EuiFlexItem grow={false}>
<EuiPageSidebar
css={css`
flex: 1;
width: 320px;
`}
>
<FieldListSidebar
services={services}
dataView={dataView}
selectedFieldNames={selectedFieldNames}
onAddFieldToWorkplace={onAddFieldToWorkplace}
onRemoveFieldFromWorkspace={onRemoveFieldFromWorkspace}
/>
</EuiPageSidebar>
</EuiFlexItem>
<EuiFlexItem>
<ExampleDropZone onDropField={onDropFieldToWorkplace} />
</EuiFlexItem>
</EuiFlexGroup>
</RootDragDropProvider>
</EuiFlexItem>
</>
) : (
<EuiEmptyPrompt
iconType="warning"
color="warning"
title={<h2>Select a data view</h2>}
body={<p>Make sure to have at least one data view or install sample data.</p>}
/>
)}
</EuiFlexGroup>
</EuiPageBody>
</EuiPage>
);
};

View file

@ -0,0 +1,73 @@
/*
* 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.
*/
/*
* 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, useMemo } from 'react';
import { DragContext, DragDrop, DropOverlayWrapper, DropType } from '@kbn/dom-drag-drop';
import { EuiEmptyPrompt, EuiPanel } from '@elastic/eui';
const DROP_PROPS = {
value: {
id: 'exampleDropZone',
humanData: {
label: 'Drop zone for selecting fields',
},
},
order: [1, 0, 0, 0],
types: ['field_add'] as DropType[],
};
export interface ExampleDropZoneProps {
onDropField: (fieldName: string) => void;
}
export const ExampleDropZone: React.FC<ExampleDropZoneProps> = ({ onDropField }) => {
const dragDropContext = useContext(DragContext);
const draggingFieldName = dragDropContext.dragging?.id;
const onDroppingField = useMemo(() => {
if (!draggingFieldName) {
return undefined;
}
return () => onDropField(draggingFieldName);
}, [onDropField, draggingFieldName]);
const isDropAllowed = Boolean(onDroppingField);
return (
<DragDrop
draggable={false}
dropTypes={isDropAllowed ? DROP_PROPS.types : undefined}
value={DROP_PROPS.value}
order={DROP_PROPS.order}
onDrop={onDroppingField}
>
<DropOverlayWrapper isVisible={isDropAllowed}>
<EuiPanel hasShadow={false} paddingSize="l" className="eui-fullHeight">
<EuiEmptyPrompt
iconType="beaker"
title={<h3>Example drop zone</h3>}
body={
<p>
Drop <strong>{draggingFieldName || 'a field'}</strong> here to select it
</p>
}
/>
</EuiPanel>
</DropOverlayWrapper>
</DragDrop>
);
};

View file

@ -0,0 +1,225 @@
/*
* 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.
*/
/*
* 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, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type { DataViewField, DataView } from '@kbn/data-views-plugin/common';
import { DragDrop } from '@kbn/dom-drag-drop';
import {
AddFieldFilterHandler,
FieldItemButton,
FieldItemButtonProps,
FieldPopover,
FieldPopoverHeader,
FieldsGroupNames,
FieldStats,
hasQuerySubscriberData,
RenderFieldItemParams,
useQuerySubscriber,
} from '@kbn/unified-field-list-plugin/public';
import { generateFilters } from '@kbn/data-plugin/public';
import type { CoreStart } from '@kbn/core-lifecycle-browser';
import type { AppPluginStartDependencies } from './types';
export interface FieldListItemProps extends RenderFieldItemParams<DataViewField> {
dataView: DataView;
services: AppPluginStartDependencies & {
core: CoreStart;
uiSettings: CoreStart['uiSettings'];
};
isSelected: boolean;
onAddFieldToWorkspace: FieldItemButtonProps<DataViewField>['onAddFieldToWorkspace'];
onRemoveFieldFromWorkspace: FieldItemButtonProps<DataViewField>['onRemoveFieldFromWorkspace'];
onRefreshFields: () => void;
}
export function FieldListItem({
dataView,
services,
isSelected,
field,
fieldSearchHighlight,
groupIndex,
groupName,
itemIndex,
hideDetails,
onRefreshFields,
onAddFieldToWorkspace,
onRemoveFieldFromWorkspace,
}: FieldListItemProps) {
const { dataViewFieldEditor, data } = services;
const querySubscriberResult = useQuerySubscriber({ data, listenToSearchSessionUpdates: false }); // this example app does not use search sessions
const filterManager = data?.query?.filterManager;
const [infoIsOpen, setOpen] = useState(false);
const togglePopover = useCallback(() => {
setOpen((value) => !value);
}, [setOpen]);
const closePopover = useCallback(() => {
setOpen(false);
}, [setOpen]);
const closeFieldEditor = useRef<() => void | undefined>();
const setFieldEditorRef = useCallback((ref: () => void | undefined) => {
closeFieldEditor.current = ref;
}, []);
const value = useMemo(
() => ({
id: field.name,
humanData: { label: field.displayName || field.name },
}),
[field]
);
const order = useMemo(() => [0, groupIndex, itemIndex], [groupIndex, itemIndex]);
const addFilterAndClose: AddFieldFilterHandler | undefined = useMemo(
() =>
filterManager && dataView
? (clickedField, values, operation) => {
closePopover();
const newFilters = generateFilters(
filterManager,
clickedField,
values,
operation,
dataView
);
filterManager.addFilters(newFilters);
}
: undefined,
[dataView, filterManager, closePopover]
);
const editFieldAndClose = useMemo(
() =>
dataView
? (fieldName?: string) => {
const ref = dataViewFieldEditor.openEditor({
ctx: {
dataView,
},
fieldName,
onSave: async () => {
onRefreshFields();
},
});
if (setFieldEditorRef) {
setFieldEditorRef(ref);
}
closePopover();
}
: undefined,
[dataViewFieldEditor, dataView, setFieldEditorRef, closePopover, onRefreshFields]
);
const removeFieldAndClose = useMemo(
() =>
dataView
? async (fieldName: string) => {
const ref = dataViewFieldEditor.openDeleteModal({
ctx: {
dataView,
},
fieldName,
onDelete: async () => {
onRefreshFields();
},
});
if (setFieldEditorRef) {
setFieldEditorRef(ref);
}
closePopover();
}
: undefined,
[dataView, setFieldEditorRef, closePopover, dataViewFieldEditor, onRefreshFields]
);
useEffect(() => {
const cleanup = () => {
if (closeFieldEditor?.current) {
closeFieldEditor?.current();
}
};
return () => {
// Make sure to close the editor when unmounting
cleanup();
};
}, []);
return (
<li>
<FieldPopover
isOpen={infoIsOpen}
closePopover={closePopover}
button={
<DragDrop
draggable
order={order}
value={value}
dataTestSubj={`fieldListPanelField-${field.name}`}
onDragStart={closePopover}
>
<FieldItemButton
field={field}
fieldSearchHighlight={fieldSearchHighlight}
size="xs"
isActive={infoIsOpen}
isEmpty={groupName === FieldsGroupNames.EmptyFields}
isSelected={isSelected}
onClick={togglePopover}
onAddFieldToWorkspace={onAddFieldToWorkspace}
onRemoveFieldFromWorkspace={onRemoveFieldFromWorkspace}
/>
</DragDrop>
}
renderHeader={() => {
return (
<FieldPopoverHeader
field={field}
closePopover={closePopover}
onAddFieldToWorkspace={!isSelected ? onAddFieldToWorkspace : undefined}
onAddFilter={addFilterAndClose}
onEditField={editFieldAndClose}
onDeleteField={removeFieldAndClose}
/>
);
}}
renderContent={
!hideDetails
? () => (
<>
{hasQuerySubscriberData(querySubscriberResult) && (
<FieldStats
services={services}
query={querySubscriberResult.query}
filters={querySubscriberResult.filters}
fromDate={querySubscriberResult.fromDate}
toDate={querySubscriberResult.toDate}
dataViewOrDataViewId={dataView}
onAddFilter={addFilterAndClose}
field={field}
/>
)}
</>
)
: undefined
}
/>
</li>
);
}

View file

@ -0,0 +1,127 @@
/*
* 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.
*/
/*
* 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, { useCallback, useContext, useMemo } from 'react';
import type { DataView, DataViewField } from '@kbn/data-views-plugin/public';
import { ChildDragDropProvider, DragContext } from '@kbn/dom-drag-drop';
import {
FieldList,
FieldListFilters,
FieldListGrouped,
FieldListGroupedProps,
FieldsGroupNames,
useExistingFieldsFetcher,
useGroupedFields,
useQuerySubscriber,
} from '@kbn/unified-field-list-plugin/public';
import { FieldListItem, FieldListItemProps } from './field_list_item';
export interface FieldListSidebarProps {
dataView: DataView;
selectedFieldNames: string[];
services: FieldListItemProps['services'];
onAddFieldToWorkplace: FieldListItemProps['onAddFieldToWorkspace'];
onRemoveFieldFromWorkspace: FieldListItemProps['onRemoveFieldFromWorkspace'];
}
export const FieldListSidebar: React.FC<FieldListSidebarProps> = ({
dataView,
selectedFieldNames,
services,
onAddFieldToWorkplace,
onRemoveFieldFromWorkspace,
}) => {
const dragDropContext = useContext(DragContext);
const allFields = dataView.fields;
const activeDataViews = useMemo(() => [dataView], [dataView]);
const querySubscriberResult = useQuerySubscriber({
data: services.data,
listenToSearchSessionUpdates: false, // this example app does not use search sessions
});
const onSelectedFieldFilter = useCallback(
(field: DataViewField) => {
return selectedFieldNames.includes(field.name);
},
[selectedFieldNames]
);
const { refetchFieldsExistenceInfo, isProcessing } = useExistingFieldsFetcher({
dataViews: activeDataViews, // if you need field existence info for more than one data view, you can specify it here
query: querySubscriberResult.query,
filters: querySubscriberResult.filters,
fromDate: querySubscriberResult.fromDate,
toDate: querySubscriberResult.toDate,
services,
});
const { fieldListFiltersProps, fieldListGroupedProps } = useGroupedFields({
dataViewId: dataView.id ?? null,
allFields,
services,
isAffectedByGlobalFilter: Boolean(querySubscriberResult.filters?.length),
onSupportedFieldFilter,
onSelectedFieldFilter,
});
const onRefreshFields = useCallback(() => {
refetchFieldsExistenceInfo();
}, [refetchFieldsExistenceInfo]);
const renderFieldItem: FieldListGroupedProps<DataViewField>['renderFieldItem'] = useCallback(
(params) => (
<FieldListItem
dataView={dataView}
services={services}
isSelected={
params.groupName === FieldsGroupNames.SelectedFields ||
selectedFieldNames.includes(params.field.name)
}
onRefreshFields={onRefreshFields}
onAddFieldToWorkspace={onAddFieldToWorkplace}
onRemoveFieldFromWorkspace={onRemoveFieldFromWorkspace}
{...params}
/>
),
[
dataView,
services,
onRefreshFields,
selectedFieldNames,
onAddFieldToWorkplace,
onRemoveFieldFromWorkspace,
]
);
return (
<ChildDragDropProvider {...dragDropContext}>
<FieldList
isProcessing={isProcessing}
prepend={<FieldListFilters {...fieldListFiltersProps} />}
>
<FieldListGrouped
{...fieldListGroupedProps}
renderFieldItem={renderFieldItem}
localStorageKeyPrefix="examples"
/>
</FieldList>
</ChildDragDropProvider>
);
};
function onSupportedFieldFilter(field: DataViewField): boolean {
return field.name !== '_source';
}

View file

@ -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.
*/
import { UnifiedFieldListExamplesPlugin } from './plugin';
// This exports static code and TypeScript types,
// as well as, Kibana Platform `plugin()` initializer.
export function plugin() {
return new UnifiedFieldListExamplesPlugin();
}
export type {
UnifiedFieldListExamplesPluginSetup,
UnifiedFieldListExamplesPluginStart,
} from './types';

View file

@ -0,0 +1,77 @@
/*
* 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 {
AppMountParameters,
AppNavLinkStatus,
CoreSetup,
CoreStart,
Plugin,
} from '@kbn/core/public';
import {
AppPluginSetupDependencies,
AppPluginStartDependencies,
UnifiedFieldListExamplesPluginSetup,
UnifiedFieldListExamplesPluginStart,
} from './types';
import { PLUGIN_ID, PLUGIN_NAME } from '../common';
import image from './unified_field_list.png';
export class UnifiedFieldListExamplesPlugin
implements
Plugin<
UnifiedFieldListExamplesPluginSetup,
UnifiedFieldListExamplesPluginStart,
AppPluginSetupDependencies,
AppPluginStartDependencies
>
{
public setup(
core: CoreSetup<AppPluginStartDependencies>,
{ developerExamples }: AppPluginSetupDependencies
): UnifiedFieldListExamplesPluginSetup {
// Register an application into the side navigation menu
core.application.register({
id: PLUGIN_ID,
title: PLUGIN_NAME,
navLinkStatus: AppNavLinkStatus.hidden,
mount: async (params: AppMountParameters) => {
// Load application bundle
const { renderApp } = await import('./application');
// Get start services as specified in kibana.json
const [coreStart, depsStart] = await core.getStartServices();
// Render the application
return renderApp(coreStart, depsStart, params);
},
});
developerExamples.register({
appId: PLUGIN_ID,
title: PLUGIN_NAME,
description: `Examples of unified field list functionality.`,
image,
links: [
{
label: 'README',
href: 'https://github.com/elastic/kibana/tree/main/src/plugins/unified_field_list/README.md',
iconType: 'logoGithub',
target: '_blank',
size: 's',
},
],
});
return {};
}
public start(core: CoreStart): UnifiedFieldListExamplesPluginStart {
return {};
}
public stop() {}
}

View file

@ -0,0 +1,35 @@
/*
* 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 { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
import type { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';
import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { DataViewFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface UnifiedFieldListExamplesPluginSetup {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface UnifiedFieldListExamplesPluginStart {}
export interface AppPluginSetupDependencies {
developerExamples: DeveloperExamplesSetup;
}
export interface AppPluginStartDependencies {
navigation: NavigationPublicPluginStart;
data: DataPublicPluginStart;
dataViews: DataViewsPublicPluginStart;
dataViewFieldEditor: DataViewFieldEditorStart;
unifiedSearch: UnifiedSearchPublicPluginStart;
charts: ChartsPluginStart;
fieldFormats: FieldFormatsStart;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

View file

@ -0,0 +1,32 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types"
},
"include": [
"index.ts",
"common/**/*.ts",
"public/**/*.ts",
"public/**/*.tsx",
"../../typings/**/*",
],
"exclude": [
"target/**/*",
],
"kbn_references": [
"@kbn/core",
"@kbn/data-plugin",
"@kbn/data-views-plugin",
"@kbn/navigation-plugin",
"@kbn/developer-examples-plugin",
"@kbn/unified-search-plugin",
"@kbn/i18n-react",
"@kbn/i18n",
"@kbn/dom-drag-drop",
"@kbn/unified-field-list-plugin",
"@kbn/core-lifecycle-browser",
"@kbn/charts-plugin",
"@kbn/field-formats-plugin",
"@kbn/data-view-field-editor-plugin",
]
}

View file

@ -687,6 +687,7 @@
"@kbn/ui-shared-deps-npm": "link:packages/kbn-ui-shared-deps-npm",
"@kbn/ui-shared-deps-src": "link:packages/kbn-ui-shared-deps-src",
"@kbn/ui-theme": "link:packages/kbn-ui-theme",
"@kbn/unified-field-list-examples-plugin": "link:examples/unified_field_list_examples",
"@kbn/unified-field-list-plugin": "link:src/plugins/unified_field_list",
"@kbn/unified-histogram-plugin": "link:src/plugins/unified_histogram",
"@kbn/unified-search-plugin": "link:src/plugins/unified_search",

View file

@ -39,30 +39,25 @@ function getCommonFieldItemButtonProps({
}: GetCommonFieldItemButtonPropsParams): {
field: FieldItemButtonProps<DataViewField>['field'];
isSelected: FieldItemButtonProps<DataViewField>['isSelected'];
dataTestSubj: FieldItemButtonProps<DataViewField>['dataTestSubj'];
buttonAddFieldToWorkspaceProps: FieldItemButtonProps<DataViewField>['buttonAddFieldToWorkspaceProps'];
buttonRemoveFieldFromWorkspaceProps: FieldItemButtonProps<DataViewField>['buttonRemoveFieldFromWorkspaceProps'];
onAddFieldToWorkspace: FieldItemButtonProps<DataViewField>['onAddFieldToWorkspace'];
onRemoveFieldFromWorkspace: FieldItemButtonProps<DataViewField>['onRemoveFieldFromWorkspace'];
} {
const dataTestSubj = `fieldToggle-${field.name}`;
const handler =
field.name === '_source' ? undefined : (f: DataViewField) => toggleDisplay(f, isSelected);
return {
field,
isSelected,
dataTestSubj: `field-${field.name}-showDetails`,
buttonAddFieldToWorkspaceProps: {
'aria-label': i18n.translate('discover.fieldChooser.discoverField.addFieldTooltip', {
defaultMessage: 'Add field as column',
}),
'data-test-subj': dataTestSubj,
},
buttonRemoveFieldFromWorkspaceProps: {
'aria-label': i18n.translate('discover.fieldChooser.discoverField.removeFieldTooltip', {
defaultMessage: 'Remove field from table',
}),
'data-test-subj': dataTestSubj,
},
onAddFieldToWorkspace: handler,
onRemoveFieldFromWorkspace: handler,

View file

@ -5,7 +5,7 @@
"description": "Contains functionality for the field list which can be integrated into apps",
"plugin": {
"id": "unifiedFieldList",
"server": true,
"server": false,
"browser": true,
"requiredPlugins": [
"dataViews",

View file

@ -8,6 +8,7 @@ exports[`UnifiedFieldList <FieldItemButton /> renders properly 1`] = `
}
}
className="unifiedFieldListItemButton unifiedFieldListItemButton--number unifiedFieldListItemButton--exists"
dataTestSubj="field-bytes-showDetails"
fieldIcon={
<WrappedFieldIcon
scripted={false}
@ -38,6 +39,7 @@ exports[`UnifiedFieldList <FieldItemButton /> renders properly for Records (Lens
}
}
className="unifiedFieldListItemButton unifiedFieldListItemButton--document unifiedFieldListItemButton--exists"
dataTestSubj="field-___records___-showDetails"
fieldIcon={
<WrappedFieldIcon
scripted={false}
@ -68,6 +70,7 @@ exports[`UnifiedFieldList <FieldItemButton /> renders properly for text-based co
}
}
className="unifiedFieldListItemButton unifiedFieldListItemButton--string unifiedFieldListItemButton--exists"
dataTestSubj="field-agent-showDetails"
fieldIcon={
<WrappedFieldIcon
type="string"
@ -96,6 +99,7 @@ exports[`UnifiedFieldList <FieldItemButton /> renders properly for wildcard sear
}
}
className="unifiedFieldListItemButton unifiedFieldListItemButton--date unifiedFieldListItemButton--exists"
dataTestSubj="field-script date-showDetails"
fieldIcon={
<WrappedFieldIcon
scripted={true}
@ -125,6 +129,7 @@ exports[`UnifiedFieldList <FieldItemButton /> renders properly when a conflict f
}
}
className="unifiedFieldListItemButton unifiedFieldListItemButton--conflict unifiedFieldListItemButton--exists"
dataTestSubj="field-custom_user_field-showDetails"
fieldIcon={
<WrappedFieldIcon
scripted={false}
@ -156,6 +161,7 @@ exports[`UnifiedFieldList <FieldItemButton /> renders properly when empty 1`] =
}
}
className="unifiedFieldListItemButton unifiedFieldListItemButton--date unifiedFieldListItemButton--missing"
dataTestSubj="field-script date-showDetails"
fieldIcon={
<WrappedFieldIcon
scripted={true}
@ -221,6 +227,7 @@ exports[`UnifiedFieldList <FieldItemButton /> renders properly with an action wh
}
}
className="unifiedFieldListItemButton unifiedFieldListItemButton--number unifiedFieldListItemButton--exists"
dataTestSubj="field-bytes-showDetails"
fieldAction={
<EuiToolTip
content="Add \\"bytes\\" field"
@ -232,7 +239,7 @@ exports[`UnifiedFieldList <FieldItemButton /> renders properly with an action wh
aria-label="Add \\"bytes\\" field"
className="unifiedFieldListItemButton__action unifiedFieldListItemButton__action--always"
color="text"
data-test-subj="unifiedFieldListItem_addField-bytes"
data-test-subj="fieldToggle-bytes"
iconType="plusInCircle"
onClick={[Function]}
/>
@ -267,6 +274,7 @@ exports[`UnifiedFieldList <FieldItemButton /> renders properly with an action wh
}
}
className="unifiedFieldListItemButton unifiedFieldListItemButton--number unifiedFieldListItemButton--exists"
dataTestSubj="field-bytes-showDetails"
fieldAction={
<EuiToolTip
content="Remove \\"bytes\\" field"
@ -278,7 +286,7 @@ exports[`UnifiedFieldList <FieldItemButton /> renders properly with an action wh
aria-label="Remove \\"bytes\\" field"
className="unifiedFieldListItemButton__action"
color="danger"
data-test-subj="unifiedFieldListItem_removeField-bytes"
data-test-subj="fieldToggle-bytes"
iconType="cross"
onClick={[Function]}
/>

View file

@ -136,7 +136,7 @@ export function FieldItemButton<T extends FieldListItem = DataViewField>({
content={removeFieldFromWorkspaceTooltip}
>
<EuiButtonIcon
data-test-subj={`unifiedFieldListItem_removeField-${field.name}`}
data-test-subj={`fieldToggle-${field.name}`}
aria-label={removeFieldFromWorkspaceTooltip}
{...(buttonRemoveFieldFromWorkspaceProps || {})}
className={classnames(
@ -159,7 +159,7 @@ export function FieldItemButton<T extends FieldListItem = DataViewField>({
content={addFieldToWorkspaceTooltip}
>
<EuiButtonIcon
data-test-subj={`unifiedFieldListItem_addField-${field.name}`}
data-test-subj={`fieldToggle-${field.name}`}
aria-label={addFieldToWorkspaceTooltip}
{...(buttonAddFieldToWorkspaceProps || {})}
className={classnames(fieldActionClassName, buttonAddFieldToWorkspaceProps?.className)}
@ -179,7 +179,7 @@ export function FieldItemButton<T extends FieldListItem = DataViewField>({
return (
<FieldButton
key={`field-item-button-${field.name}`}
dataTestSubj={dataTestSubj}
dataTestSubj={dataTestSubj || `field-${field.name}-showDetails`}
size={size || 's'}
className={classes}
isActive={isActive}

View file

@ -18,7 +18,7 @@ import {
} from '@elastic/eui';
import classNames from 'classnames';
import { type DataViewField } from '@kbn/data-views-plugin/common';
import { type FieldListItem, FieldsGroupNames } from '../../types';
import { type FieldListItem, FieldsGroupNames, type RenderFieldItemParams } from '../../types';
import './fields_accordion.scss';
export interface FieldsAccordionProps<T extends FieldListItem> {
@ -35,14 +35,7 @@ export interface FieldsAccordionProps<T extends FieldListItem> {
groupName: FieldsGroupNames;
fieldSearchHighlight?: string;
paginatedFields: T[];
renderFieldItem: (params: {
field: T;
hideDetails?: boolean;
itemIndex: number;
groupIndex: number;
groupName: FieldsGroupNames;
fieldSearchHighlight?: string;
}) => JSX.Element;
renderFieldItem: (params: RenderFieldItemParams<T>) => JSX.Element;
renderCallout: () => JSX.Element;
showExistenceFetchError?: boolean;
showExistenceFetchTimeout?: boolean;

View file

@ -1,4 +1,7 @@
/* 1 - `important` is to ensure that even if styles ordering is different (like for some reason on Developer Examples page),
this one will be used instead of eui defaults */
.unifiedFieldList__fieldPopover__fieldPopoverPanel {
min-width: $euiSizeXXL * 6.5;
max-width: $euiSizeXXL * 7.5;
min-width: $euiSizeXXL * 6.5 !important; /* 1 */
max-width: $euiSizeXXL * 7.5 !important;
}

View file

@ -34,13 +34,13 @@ import {
} from '@elastic/charts';
import { i18n } from '@kbn/i18n';
import { buildEsQuery, Query, Filter, AggregateQuery } from '@kbn/es-query';
import { showExamplesForField } from '../../../common/utils/field_examples_calculator';
import { showExamplesForField } from '../../services/field_stats/field_examples_calculator';
import { OverrideFieldTopValueBarCallback } from './field_top_values_bucket';
import type { BucketedAggregation, NumberSummary } from '../../../common/types';
import {
canProvideStatsForField,
canProvideNumberSummaryForField,
} from '../../../common/utils/field_stats_utils';
} from '../../services/field_stats/field_stats_utils';
import { loadFieldStats } from '../../services/field_stats';
import type { AddFieldFilterHandler } from '../../types';
import {
@ -284,6 +284,7 @@ const FieldStatsComponent: React.FC<FieldStatsProps> = ({
let title = <></>;
function combineWithTitleAndFooter(el: React.ReactElement) {
const dataTestSubjDocsCount = 'unifiedFieldStats-statsFooter-docsCount';
const countsElement = totalDocuments ? (
<EuiText color="subdued" size="xs" data-test-subj={`${dataTestSubject}-statsFooter`}>
{sampledDocuments && sampledDocuments < totalDocuments ? (
@ -293,7 +294,7 @@ const FieldStatsComponent: React.FC<FieldStatsProps> = ({
values={{
sampledDocuments,
sampledDocumentsFormatted: (
<strong>
<strong data-test-subj={dataTestSubjDocsCount}>
{fieldFormats
.getDefaultInstance(KBN_FIELD_TYPES.NUMBER, [ES_FIELD_TYPES.INTEGER])
.convert(sampledDocuments)}
@ -308,7 +309,7 @@ const FieldStatsComponent: React.FC<FieldStatsProps> = ({
values={{
totalDocuments,
totalDocumentsFormatted: (
<strong>
<strong data-test-subj={dataTestSubjDocsCount}>
{fieldFormats
.getDefaultInstance(KBN_FIELD_TYPES.NUMBER, [ES_FIELD_TYPES.INTEGER])
.convert(totalDocuments)}
@ -418,6 +419,7 @@ const FieldStatsComponent: React.FC<FieldStatsProps> = ({
title = (
<>
<EuiButtonGroup
data-test-subj="unifiedFieldStats-buttonGroup"
buttonSize="compressed"
isFullWidth
legend={i18n.translate('unifiedFieldList.fieldStats.displayToggleLegend', {
@ -480,34 +482,71 @@ const FieldStatsComponent: React.FC<FieldStatsProps> = ({
if (field.type === 'date') {
return combineWithTitleAndFooter(
<div data-test-subj={`${dataTestSubject}-histogram`}>
<Chart size={{ height: 200, width: 300 - 32 }}>
<div data-test-subj="unifiedFieldStats-timeDistribution">
<div data-test-subj={`${dataTestSubject}-histogram`}>
<Chart size={{ height: 200, width: 300 - 32 }}>
<Settings
tooltip={{ type: TooltipType.None }}
theme={customChartTheme}
baseTheme={chartBaseTheme}
xDomain={
fromDateParsed && toDateParsed
? {
min: fromDateParsed.valueOf(),
max: toDateParsed.valueOf(),
minInterval: Math.round(
(toDateParsed.valueOf() - fromDateParsed.valueOf()) / 10
),
}
: undefined
}
/>
<Axis
id="key"
position={Position.Bottom}
tickFormat={
fromDateParsed && toDateParsed
? niceTimeFormatter([fromDateParsed.valueOf(), toDateParsed.valueOf()])
: undefined
}
showOverlappingTicks={true}
/>
<HistogramBarSeries
data={histogram.buckets}
id={specId}
xAccessor={'key'}
yAccessors={['count']}
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
timeZone="local"
/>
</Chart>
</div>
</div>
);
}
if (showingHistogram || !topValues || !topValues.buckets.length) {
return combineWithTitleAndFooter(
<div data-test-subj="unifiedFieldStats-histogram">
<Chart
data-test-subj={`${dataTestSubject}-histogram`}
size={{ height: 200, width: '100%' }}
>
<Settings
rotation={90}
tooltip={{ type: TooltipType.None }}
theme={customChartTheme}
baseTheme={chartBaseTheme}
xDomain={
fromDateParsed && toDateParsed
? {
min: fromDateParsed.valueOf(),
max: toDateParsed.valueOf(),
minInterval: Math.round(
(toDateParsed.valueOf() - fromDateParsed.valueOf()) / 10
),
}
: undefined
}
/>
<Axis
id="key"
position={Position.Bottom}
tickFormat={
fromDateParsed && toDateParsed
? niceTimeFormatter([fromDateParsed.valueOf(), toDateParsed.valueOf()])
: undefined
}
position={Position.Left}
showOverlappingTicks={true}
tickFormat={(d) => formatter.convert(d)}
/>
<HistogramBarSeries
@ -515,51 +554,19 @@ const FieldStatsComponent: React.FC<FieldStatsProps> = ({
id={specId}
xAccessor={'key'}
yAccessors={['count']}
xScaleType={ScaleType.Time}
xScaleType={ScaleType.Linear}
yScaleType={ScaleType.Linear}
timeZone="local"
/>
</Chart>
</div>
);
}
if (showingHistogram || !topValues || !topValues.buckets.length) {
return combineWithTitleAndFooter(
<Chart
data-test-subj={`${dataTestSubject}-histogram`}
size={{ height: 200, width: '100%' }}
>
<Settings
rotation={90}
tooltip={{ type: TooltipType.None }}
theme={customChartTheme}
baseTheme={chartBaseTheme}
/>
<Axis
id="key"
position={Position.Left}
showOverlappingTicks={true}
tickFormat={(d) => formatter.convert(d)}
/>
<HistogramBarSeries
data={histogram.buckets}
id={specId}
xAccessor={'key'}
yAccessors={['count']}
xScaleType={ScaleType.Linear}
yScaleType={ScaleType.Linear}
/>
</Chart>
);
}
}
if (topValues && topValues.buckets.length) {
return combineWithTitleAndFooter(
<FieldTopValues
areExamples={showExamplesForField(field)}
buckets={topValues.buckets}
dataView={dataView}
field={field}

View file

@ -44,6 +44,7 @@ describe('UnifiedFieldList <FieldTopValues />', () => {
} as unknown as DataView;
defaultProps = {
areExamples: false,
dataView,
field: dataView.fields.find((f) => f.name === 'source')!,
sampledValuesCount: 5000,

View file

@ -15,6 +15,7 @@ import FieldTopValuesBucket from './field_top_values_bucket';
import type { OverrideFieldTopValueBarCallback } from './field_top_values_bucket';
export interface FieldTopValuesProps {
areExamples: boolean; // real top values or only examples
buckets: BucketedAggregation<number | string>['buckets'];
dataView: DataView;
field: DataViewField;
@ -26,6 +27,7 @@ export interface FieldTopValuesProps {
}
export const FieldTopValues: React.FC<FieldTopValuesProps> = ({
areExamples,
buckets,
dataView,
field,
@ -46,54 +48,60 @@ export const FieldTopValues: React.FC<FieldTopValuesProps> = ({
);
return (
<div data-test-subj={`${dataTestSubject}-topValues`}>
{buckets.map((bucket, index) => {
const fieldValue = bucket.key;
const formatted = formatter.convert(fieldValue);
<div
data-test-subj={
areExamples ? 'unifiedFieldStats-exampleValueBuckets' : 'unifiedFieldStats-topValueBuckets'
}
>
<div data-test-subj={`${dataTestSubject}-topValues`}>
{buckets.map((bucket, index) => {
const fieldValue = bucket.key;
const formatted = formatter.convert(fieldValue);
return (
<Fragment key={fieldValue}>
{index > 0 && <EuiSpacer size="s" />}
return (
<Fragment key={fieldValue}>
{index > 0 && <EuiSpacer size="s" />}
<FieldTopValuesBucket
field={field}
fieldValue={fieldValue}
formattedFieldValue={formatted}
formattedPercentage={getFormattedPercentageValue(
bucket.count,
sampledValuesCount,
digitsRequired
)}
progressValue={getProgressValue(bucket.count, sampledValuesCount)}
count={bucket.count}
color={color}
data-test-subj={dataTestSubject}
onAddFilter={onAddFilter}
overrideFieldTopValueBar={overrideFieldTopValueBar}
/>
</Fragment>
);
})}
{otherCount > 0 && (
<>
<EuiSpacer size="s" />
<FieldTopValuesBucket
type="other"
field={field}
fieldValue={fieldValue}
formattedFieldValue={formatted}
fieldValue={undefined}
formattedPercentage={getFormattedPercentageValue(
bucket.count,
otherCount,
sampledValuesCount,
digitsRequired
)}
progressValue={getProgressValue(bucket.count, sampledValuesCount)}
count={bucket.count}
progressValue={getProgressValue(otherCount, sampledValuesCount)}
count={otherCount}
color={color}
data-test-subj={dataTestSubject}
onAddFilter={onAddFilter}
overrideFieldTopValueBar={overrideFieldTopValueBar}
/>
</Fragment>
);
})}
{otherCount > 0 && (
<>
<EuiSpacer size="s" />
<FieldTopValuesBucket
type="other"
field={field}
fieldValue={undefined}
formattedPercentage={getFormattedPercentageValue(
otherCount,
sampledValuesCount,
digitsRequired
)}
progressValue={getProgressValue(otherCount, sampledValuesCount)}
count={otherCount}
color={color}
data-test-subj={dataTestSubject}
onAddFilter={onAddFilter}
overrideFieldTopValueBar={overrideFieldTopValueBar}
/>
</>
)}
</>
)}
</div>
</div>
);
};

View file

@ -16,6 +16,7 @@ import { getResolvedDateRange } from '../utils/get_resolved_date_range';
*/
export interface QuerySubscriberParams {
data: DataPublicPluginStart;
listenToSearchSessionUpdates?: boolean;
}
/**
@ -31,9 +32,13 @@ export interface QuerySubscriberResult {
/**
* Memorizes current query, filters and absolute date range
* @param data
* @param listenToSearchSessionUpdates
* @public
*/
export const useQuerySubscriber = ({ data }: QuerySubscriberParams) => {
export const useQuerySubscriber = ({
data,
listenToSearchSessionUpdates = true,
}: QuerySubscriberParams) => {
const timefilter = data.query.timefilter.timefilter;
const [result, setResult] = useState<QuerySubscriberResult>(() => {
const state = data.query.getState();
@ -47,6 +52,10 @@ export const useQuerySubscriber = ({ data }: QuerySubscriberParams) => {
});
useEffect(() => {
if (!listenToSearchSessionUpdates) {
return;
}
const subscription = data.search.session.state$.subscribe((sessionState) => {
const dateRange = getResolvedDateRange(timefilter);
setResult((prevState) => ({
@ -57,7 +66,24 @@ export const useQuerySubscriber = ({ data }: QuerySubscriberParams) => {
});
return () => subscription.unsubscribe();
}, [setResult, timefilter, data.search.session.state$]);
}, [setResult, timefilter, data.search.session.state$, listenToSearchSessionUpdates]);
useEffect(() => {
if (listenToSearchSessionUpdates) {
return;
}
const subscription = timefilter.getTimeUpdate$().subscribe(() => {
const dateRange = getResolvedDateRange(timefilter);
setResult((prevState) => ({
...prevState,
fromDate: dateRange.fromDate,
toDate: dateRange.toDate,
}));
});
return () => subscription.unsubscribe();
}, [setResult, timefilter, listenToSearchSessionUpdates]);
useEffect(() => {
const subscription = data.query.state$.subscribe(({ state, changes }) => {

View file

@ -57,6 +57,7 @@ export type {
FieldTypeKnown,
FieldListItem,
GetCustomFieldType,
RenderFieldItemParams,
} from './types';
export { ExistenceFetchStatus, FieldsGroupNames } from './types';

View file

@ -7,12 +7,7 @@
*/
import { DataView } from '@kbn/data-views-plugin/common';
import {
legacyExistingFields,
existingFields,
Field,
buildFieldList,
} from './field_existing_utils';
import { existingFields, buildFieldList } from './field_existing_utils';
describe('existingFields', () => {
it('should remove missing fields by matching names', () => {
@ -46,89 +41,6 @@ describe('existingFields', () => {
});
});
describe('legacyExistingFields', () => {
function field(opts: string | Partial<Field>): Field {
const obj = typeof opts === 'object' ? opts : {};
const name = (typeof opts === 'string' ? opts : opts.name) || 'test';
return {
name,
isScript: false,
isMeta: false,
...obj,
};
}
function searchResults(fields: Record<string, unknown[]> = {}) {
return { fields, _index: '_index', _id: '_id' };
}
it('should handle root level fields', () => {
const result = legacyExistingFields(
[searchResults({ foo: ['bar'] }), searchResults({ baz: [0] })],
[field('foo'), field('bar'), field('baz')]
);
expect(result).toEqual(['foo', 'baz']);
});
it('should handle basic arrays, ignoring empty ones', () => {
const result = legacyExistingFields(
[searchResults({ stuff: ['heyo', 'there'], empty: [] })],
[field('stuff'), field('empty')]
);
expect(result).toEqual(['stuff']);
});
it('should handle objects with dotted fields', () => {
const result = legacyExistingFields(
[searchResults({ 'geo.country_name': ['US'] })],
[field('geo.country_name')]
);
expect(result).toEqual(['geo.country_name']);
});
it('supports scripted fields', () => {
const result = legacyExistingFields(
[searchResults({ bar: ['scriptvalue'] })],
[field({ name: 'bar', isScript: true })]
);
expect(result).toEqual(['bar']);
});
it('supports runtime fields', () => {
const result = legacyExistingFields(
[searchResults({ runtime_foo: ['scriptvalue'] })],
[
field({
name: 'runtime_foo',
runtimeField: { type: 'long', script: { source: '2+2' } },
}),
]
);
expect(result).toEqual(['runtime_foo']);
});
it('supports meta fields', () => {
const result = legacyExistingFields(
[
{
// @ts-expect-error _mymeta is not defined on estypes.SearchHit
_mymeta: 'abc',
...searchResults({ bar: ['scriptvalue'] }),
},
],
[field({ name: '_mymeta', isMeta: true })]
);
expect(result).toEqual(['_mymeta']);
});
});
describe('buildFieldList', () => {
const indexPattern = {
title: 'testpattern',

View file

@ -6,7 +6,6 @@
* Side Public License, v 1.
*/
import Boom from '@hapi/boom';
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { RuntimeField } from '@kbn/data-views-plugin/common';
import type { DataViewsContract, DataView, FieldSpec } from '@kbn/data-views-plugin/common';
@ -120,35 +119,3 @@ export function existingFields(filteredFields: FieldSpec[], allFields: Field[]):
.filter((field) => field.isScript || field.runtimeField || filteredFieldsSet.has(field.name))
.map((f) => f.name);
}
/**
* Exported only for unit tests.
*/
export function legacyExistingFields(docs: estypes.SearchHit[], fields: Field[]): string[] {
const missingFields = new Set(fields);
for (const doc of docs) {
if (missingFields.size === 0) {
break;
}
missingFields.forEach((field) => {
let fieldStore = doc.fields!;
if (field.isMeta) {
fieldStore = doc;
}
const value = fieldStore[field.name];
if (Array.isArray(value) && value.length) {
missingFields.delete(field);
} else if (!Array.isArray(value) && value) {
missingFields.delete(field);
}
});
}
return fields.filter((field) => !missingFields.has(field)).map((f) => f.name);
}
export function isBoomError(error: { isBoom?: boolean }): error is Boom.Boom {
return error.isBoom === true;
}

View file

@ -10,7 +10,7 @@ import { IUiSettingsClient } from '@kbn/core/public';
import { DataPublicPluginStart, UI_SETTINGS } from '@kbn/data-plugin/public';
import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/common';
import { lastValueFrom } from 'rxjs';
import { fetchFieldExistence } from '../../../common/utils/field_existing_utils';
import { fetchFieldExistence } from './field_existing_utils';
interface FetchFieldExistenceParams {
data: DataPublicPluginStart;

View file

@ -11,7 +11,7 @@ import DateMath from '@kbn/datemath';
import type { DataView, DataViewField } from '@kbn/data-views-plugin/common';
import type { ESSearchResponse } from '@kbn/es-types';
import { FieldFormat } from '@kbn/field-formats-plugin/common';
import type { FieldStatsResponse } from '../types';
import type { FieldStatsResponse } from '../../../common/types';
import {
getFieldExampleBuckets,
canProvideExamplesForField,

View file

@ -10,11 +10,7 @@ import { lastValueFrom } from 'rxjs';
import type { DataView, DataViewField } from '@kbn/data-views-plugin/common';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { FieldStatsResponse } from '../../../common/types';
import {
fetchAndCalculateFieldStats,
SearchHandler,
buildSearchParams,
} from '../../../common/utils/field_stats_utils';
import { fetchAndCalculateFieldStats, SearchHandler, buildSearchParams } from './field_stats_utils';
interface FetchFieldStatsParams {
services: {

View file

@ -73,3 +73,12 @@ export type FieldTypeKnown = Exclude<
>;
export type GetCustomFieldType<T extends FieldListItem> = (field: T) => FieldTypeKnown;
export interface RenderFieldItemParams<T extends FieldListItem> {
field: T;
hideDetails?: boolean;
itemIndex: number;
groupIndex: number;
groupName: FieldsGroupNames;
fieldSearchHighlight?: string;
}

View file

@ -1,24 +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 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 { PluginInitializerContext } from '@kbn/core/server';
import { UnifiedFieldListPlugin } from './plugin';
// This exports static code and TypeScript types,
// as well as, Kibana Platform `plugin()` initializer.
export function plugin(initializerContext: PluginInitializerContext) {
return new UnifiedFieldListPlugin(initializerContext);
}
export type {
UnifiedFieldListServerPluginSetup,
UnifiedFieldListServerPluginStart,
PluginSetup,
PluginStart,
} from './types';

View file

@ -1,41 +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 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 { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from '@kbn/core/server';
import {
UnifiedFieldListServerPluginSetup,
UnifiedFieldListServerPluginStart,
PluginStart,
PluginSetup,
} from './types';
import { defineRoutes } from './routes';
export class UnifiedFieldListPlugin
implements Plugin<UnifiedFieldListServerPluginSetup, UnifiedFieldListServerPluginStart>
{
private readonly logger: Logger;
constructor(initializerContext: PluginInitializerContext) {
this.logger = initializerContext.logger.get();
}
public setup(core: CoreSetup<PluginStart>, plugins: PluginSetup) {
this.logger.debug('unifiedFieldList: Setup');
defineRoutes(core, this.logger);
return {};
}
public start(core: CoreStart, plugins: PluginStart) {
this.logger.debug('unifiedFieldList: Started');
return {};
}
public stop() {}
}

View file

@ -1,100 +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 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 { errors } from '@elastic/elasticsearch';
import { schema } from '@kbn/config-schema';
import { CoreSetup, Logger } from '@kbn/core/server';
import { UI_SETTINGS } from '@kbn/data-plugin/server';
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { fetchFieldExistence, isBoomError } from '../../common/utils/field_existing_utils';
import { FIELD_EXISTING_API_PATH } from '../../common/constants';
import { PluginStart } from '../types';
export async function existingFieldsRoute(setup: CoreSetup<PluginStart>, logger: Logger) {
const router = setup.http.createRouter();
router.post(
{
path: FIELD_EXISTING_API_PATH,
validate: {
params: schema.object({
dataViewId: schema.string(),
}),
body: schema.object({
dslQuery: schema.object({}, { unknowns: 'allow' }),
fromDate: schema.maybe(schema.string()),
toDate: schema.maybe(schema.string()),
timeFieldName: schema.maybe(schema.string()),
}),
},
},
async (context, req, res) => {
const [{ savedObjects, elasticsearch, uiSettings }, { dataViews }] =
await setup.getStartServices();
const savedObjectsClient = savedObjects.getScopedClient(req);
const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient);
const [includeFrozen, metaFields] = await Promise.all([
uiSettingsClient.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN),
uiSettingsClient.get(UI_SETTINGS.META_FIELDS),
]);
const esClient = elasticsearch.client.asScoped(req).asCurrentUser;
try {
const dataViewsService = await dataViews.dataViewsServiceFactory(
savedObjectsClient,
esClient
);
return res.ok({
body: await fetchFieldExistence({
...req.body,
dataViewsService,
includeFrozen,
metaFields,
dataView: await dataViewsService.get(req.params.dataViewId),
search: async (params) => {
const contextCore = await context.core;
return await contextCore.elasticsearch.client.asCurrentUser.search<
estypes.SearchHit[]
>(
{ ...params },
{
// Global request timeout. Will cancel the request if exceeded. Overrides the elasticsearch.requestTimeout
requestTimeout: '5000ms',
// Fails fast instead of retrying- default is to retry
maxRetries: 0,
}
);
},
}),
});
} catch (e) {
if (e instanceof errors.TimeoutError) {
logger.info(`Field existence check timed out on ${req.params.dataViewId}`);
// 408 is Request Timeout
return res.customError({ statusCode: 408, body: e.message });
}
logger.info(
`Field existence check failed on ${req.params.dataViewId}: ${
isBoomError(e) ? e.output.payload.message : e.message
}`
);
if (e instanceof errors.ResponseError && e.statusCode === 404) {
return res.notFound({ body: e.message });
}
if (isBoomError(e)) {
if (e.output.statusCode === 404) {
return res.notFound({ body: e.output.payload.message });
}
throw new Error(e.output.payload.message);
} else {
throw e;
}
}
}
);
}

View file

@ -1,105 +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 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 { errors } from '@elastic/elasticsearch';
import { schema } from '@kbn/config-schema';
import { CoreSetup } from '@kbn/core/server';
import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/common';
import { FIELD_STATS_API_PATH } from '../../common/constants';
import type { PluginStart } from '../types';
import {
fetchAndCalculateFieldStats,
SearchHandler,
buildSearchParams,
} from '../../common/utils/field_stats_utils';
export async function initFieldStatsRoute(setup: CoreSetup<PluginStart>) {
const router = setup.http.createRouter();
router.post(
{
path: FIELD_STATS_API_PATH,
validate: {
body: schema.object(
{
dslQuery: schema.object({}, { unknowns: 'allow' }),
fromDate: schema.string(),
toDate: schema.string(),
dataViewId: schema.string(),
fieldName: schema.string(),
size: schema.maybe(schema.number()),
},
{ unknowns: 'allow' }
),
},
},
async (context, req, res) => {
const requestClient = (await context.core).elasticsearch.client.asCurrentUser;
const { fromDate, toDate, fieldName, dslQuery, size, dataViewId } = req.body;
const [{ savedObjects, elasticsearch }, { dataViews }] = await setup.getStartServices();
const savedObjectsClient = savedObjects.getScopedClient(req);
const esClient = elasticsearch.client.asScoped(req).asCurrentUser;
const indexPatternsService = await dataViews.dataViewsServiceFactory(
savedObjectsClient,
esClient
);
try {
const dataView = await indexPatternsService.get(dataViewId);
const field = dataView.fields.find((f) => f.name === fieldName);
if (!field) {
throw new Error(`Field {fieldName} not found in data view ${dataView.title}`);
}
const searchHandler: SearchHandler = async (body) => {
const result = await requestClient.search(
buildSearchParams({
dataViewPattern: dataView.title,
timeFieldName: dataView.timeFieldName,
fromDate,
toDate,
dslQuery,
runtimeMappings: dataView.getRuntimeMappings(),
...body,
})
);
return result;
};
const stats = await fetchAndCalculateFieldStats({
searchHandler,
dataView,
field,
fromDate,
toDate,
size,
});
return res.ok({
body: stats,
});
} catch (e) {
if (e instanceof SavedObjectNotFound) {
return res.notFound();
}
if (e instanceof errors.ResponseError && e.statusCode === 404) {
return res.notFound();
}
if (e.isBoom) {
if (e.output.statusCode === 404) {
return res.notFound();
}
throw new Error(e.output.message);
} else {
throw e;
}
}
}
);
}

View file

@ -1,20 +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 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 { CoreSetup, Logger } from '@kbn/core/server';
import { PluginStart } from '../types';
import { existingFieldsRoute } from './existing_fields';
import { initFieldStatsRoute } from './field_stats';
// These routes are defined only for running integration tests under
// test/api_integration/apis/unified_field_list
// UnifiedFieldList does not call these API - it uses only the client code.
export function defineRoutes(setup: CoreSetup<PluginStart>, logger: Logger) {
initFieldStatsRoute(setup);
existingFieldsRoute(setup, logger);
}

View file

@ -1,22 +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 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 { PluginStart as DataViewsServerPluginStart } from '@kbn/data-views-plugin/server';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface UnifiedFieldListServerPluginSetup {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface UnifiedFieldListServerPluginStart {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface PluginSetup {}
export interface PluginStart {
dataViews: DataViewsServerPluginStart;
}

View file

@ -7,11 +7,9 @@
"../../typings/**/*",
"common/**/*",
"public/**/*",
"server/**/*",
],
"kbn_references": [
"@kbn/core",
"@kbn/kibana-utils-plugin",
"@kbn/data-views-plugin",
"@kbn/data-plugin",
"@kbn/charts-plugin",
@ -24,7 +22,6 @@
"@kbn/field-formats-plugin",
"@kbn/i18n-react",
"@kbn/analytics",
"@kbn/config-schema",
"@kbn/core-lifecycle-browser",
"@kbn/react-field",
"@kbn/field-types",

View file

@ -9,7 +9,14 @@
import { FtrProviderContext } from '../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'discover', 'header', 'share', 'timePicker']);
const PageObjects = getPageObjects([
'common',
'discover',
'header',
'share',
'timePicker',
'unifiedFieldList',
]);
const dataGrid = getService('dataGrid');
const a11y = getService('a11y');
const savedQueryManagementComponent = getService('savedQueryManagementComponent');
@ -68,14 +75,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('a11y test on open sidenav filter', async () => {
await PageObjects.discover.openSidebarFieldFilter();
await PageObjects.unifiedFieldList.openSidebarFieldFilter();
await a11y.testAppSnapshot();
await PageObjects.discover.closeSidebarFieldFilter();
await PageObjects.unifiedFieldList.closeSidebarFieldFilter();
});
it('a11y test on tables with columns view', async () => {
for (const columnName of TEST_COLUMN_NAMES) {
await PageObjects.discover.clickFieldListItemToggle(columnName);
await PageObjects.unifiedFieldList.clickFieldListItemToggle(columnName);
}
await a11y.testAppSnapshot();
});

View file

@ -28,7 +28,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./stats'));
loadTestFile(require.resolve('./ui_metric'));
loadTestFile(require.resolve('./ui_counters'));
loadTestFile(require.resolve('./unified_field_list'));
loadTestFile(require.resolve('./telemetry'));
loadTestFile(require.resolve('./guided_onboarding'));
});

View file

@ -1,157 +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 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 '../../ftr_provider_context';
const TEST_START_TIME = '2010-09-19T06:31:44.000';
const TEST_END_TIME = '2023-09-23T18:31:44.000';
const COMMON_HEADERS = {
'kbn-xsrf': 'some-xsrf-token',
};
const metaFields = ['_id', '_index', '_score', '_source'];
const fieldsWithData = [
'ts',
'filter_field',
'textfield1',
'textfield2',
'mapping_runtime_field',
'data_view_runtime_field',
];
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
const supertest = getService('supertest');
const kibanaServer = getService('kibanaServer');
const dataViewId = 'existence_index';
const API_PATH = `/internal/unified_field_list/existing_fields/${dataViewId}`;
describe('existing_fields apis', () => {
before(async () => {
await esArchiver.load(
'test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword'
);
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/index_patterns/constant_keyword.json'
);
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword'
);
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/index_patterns/constant_keyword.json'
);
});
describe('existence', () => {
it('should find which fields exist in the sample documents', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dslQuery: {
bool: {
filter: [{ match_all: {} }],
},
},
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
})
.expect(200);
expect(body.indexPatternTitle).to.eql('existence_index_*');
expect(body.existingFieldNames.sort()).to.eql([...metaFields, ...fieldsWithData].sort());
});
it('should return fields filtered by term query', async () => {
const expectedFieldNames = [
'ts',
'filter_field',
'textfield1',
// textfield2 and mapping_runtime_field are defined on the other index
'data_view_runtime_field',
];
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dslQuery: {
bool: {
filter: [{ term: { filter_field: 'a' } }],
},
},
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
})
.expect(200);
expect(body.existingFieldNames.sort()).to.eql(
[...metaFields, ...expectedFieldNames].sort()
);
});
it('should return fields filtered by match_phrase query', async () => {
const expectedFieldNames = [
'ts',
'filter_field',
'textfield1',
// textfield2 and mapping_runtime_field are defined on the other index
'data_view_runtime_field',
];
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dslQuery: {
bool: {
filter: [{ match_phrase: { filter_field: 'a' } }],
},
},
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
})
.expect(200);
expect(body.existingFieldNames.sort()).to.eql(
[...metaFields, ...expectedFieldNames].sort()
);
});
it('should return fields filtered by time range', async () => {
const expectedFieldNames = [
'ts',
'filter_field',
'textfield1',
// textfield2 and mapping_runtime_field are defined on the other index
'data_view_runtime_field',
];
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dslQuery: {
bool: {
filter: [{ term: { filter_field: 'a' } }],
},
},
fromDate: TEST_START_TIME,
toDate: '2021-12-12',
})
.expect(200);
expect(body.existingFieldNames.sort()).to.eql(
[...metaFields, ...expectedFieldNames].sort()
);
});
});
});
};

View file

@ -1,601 +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 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 '../../ftr_provider_context';
const TEST_START_TIME = '2015-09-19T06:31:44.000';
const TEST_END_TIME = '2015-09-23T18:31:44.000';
const COMMON_HEADERS = {
'kbn-xsrf': 'some-xsrf-token',
};
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const supertest = getService('supertest');
const API_PATH = '/internal/unified_field_list/field_stats';
describe('field stats apis', () => {
before(async () => {
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional');
});
after(async () => {
await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional');
});
describe('field distribution', () => {
before(async () => {
await kibanaServer.savedObjects.cleanStandardList();
await kibanaServer.importExport.load(
'x-pack/test/functional/fixtures/kbn_archiver/visualize/default'
);
});
after(async () => {
await kibanaServer.savedObjects.cleanStandardList();
});
it('should return a 404 for missing index patterns', async () => {
await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: '123',
dslQuery: { match_all: {} },
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'bytes',
})
.expect(404);
});
it('should also work without specifying a time field', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'logstash-2015.09.22',
dslQuery: { match_all: {} },
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'bytes',
})
.expect(200);
expect(body).to.have.property('totalDocuments', 4634);
});
it('should return an auto histogram for numbers and top values', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'logstash-2015.09.22',
dslQuery: { match_all: {} },
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'bytes',
})
.expect(200);
expect(body).to.eql({
totalDocuments: 4634,
sampledDocuments: 4634,
sampledValues: 4634,
histogram: {
buckets: [
{
count: 705,
key: 0,
},
{
count: 898,
key: 1999,
},
{
count: 886,
key: 3998,
},
{
count: 970,
key: 5997,
},
{
count: 939,
key: 7996,
},
{
count: 44,
key: 9995,
},
{
count: 43,
key: 11994,
},
{
count: 43,
key: 13993,
},
{
count: 57,
key: 15992,
},
{
count: 49,
key: 17991,
},
],
},
topValues: {
buckets: [
{
count: 147,
key: 0,
},
{
count: 5,
key: 3954,
},
{
count: 5,
key: 5846,
},
{
count: 5,
key: 6497,
},
{
count: 4,
key: 1840,
},
{
count: 4,
key: 4206,
},
{
count: 4,
key: 4328,
},
{
count: 4,
key: 4669,
},
{
count: 4,
key: 5863,
},
{
count: 4,
key: 6631,
},
],
},
});
});
it('should return an auto histogram for dates', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'logstash-2015.09.22',
dslQuery: { match_all: {} },
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: '@timestamp',
})
.expect(200);
expect(body).to.eql({
totalDocuments: 4634,
histogram: {
buckets: [
{
count: 1162,
key: 1442875680000,
},
{
count: 3420,
key: 1442914560000,
},
{
count: 52,
key: 1442953440000,
},
],
},
});
});
it('should return top values for strings', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'logstash-2015.09.22',
dslQuery: { match_all: {} },
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'geo.src',
})
.expect(200);
expect(body).to.eql({
totalDocuments: 4634,
sampledDocuments: 4634,
sampledValues: 4633,
topValues: {
buckets: [
{
count: 832,
key: 'CN',
},
{
count: 804,
key: 'IN',
},
{
count: 425,
key: 'US',
},
{
count: 158,
key: 'ID',
},
{
count: 143,
key: 'BR',
},
{
count: 116,
key: 'PK',
},
{
count: 106,
key: 'BD',
},
{
count: 94,
key: 'NG',
},
{
count: 84,
key: 'RU',
},
{
count: 73,
key: 'JP',
},
],
},
});
});
it('should return top values for ip fields', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'logstash-2015.09.22',
dslQuery: { match_all: {} },
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'ip',
})
.expect(200);
expect(body).to.eql({
totalDocuments: 4634,
sampledDocuments: 4634,
sampledValues: 4633,
topValues: {
buckets: [
{
count: 13,
key: '177.194.175.66',
},
{
count: 12,
key: '18.55.141.62',
},
{
count: 12,
key: '53.55.251.105',
},
{
count: 11,
key: '21.111.249.239',
},
{
count: 11,
key: '97.63.84.25',
},
{
count: 11,
key: '100.99.207.174',
},
{
count: 11,
key: '112.34.138.226',
},
{
count: 11,
key: '194.68.89.92',
},
{
count: 11,
key: '235.186.79.201',
},
{
count: 10,
key: '57.79.108.136',
},
],
},
});
});
it('should return histograms for scripted date fields', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'logstash-2015.09.22',
dslQuery: { match_all: {} },
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'scripted_date',
})
.expect(200);
expect(body).to.eql({
histogram: {
buckets: [
{
count: 4634,
key: 0,
},
],
},
totalDocuments: 4634,
});
});
it('should return top values for scripted string fields', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'logstash-2015.09.22',
dslQuery: { match_all: {} },
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'scripted_string',
})
.expect(200);
expect(body).to.eql({
totalDocuments: 4634,
sampledDocuments: 4634,
sampledValues: 4634,
topValues: {
buckets: [
{
count: 4634,
key: 'hello',
},
],
},
});
});
it('should return examples for non-aggregatable fields', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'logstash-2015.09.22',
dslQuery: { match_all: {} },
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'extension', // `extension.keyword` is an aggregatable field but `extension` is not
})
.expect(200);
expect(body.totalDocuments).to.eql(4634);
expect(body.sampledDocuments).to.eql(100);
expect(body.sampledValues).to.eql(100);
expect(body.topValues.buckets.length).to.be.greaterThan(0);
});
it('should return top values for index pattern runtime string fields', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'logstash-2015.09.22',
dslQuery: { match_all: {} },
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'runtime_string_field',
})
.expect(200);
expect(body).to.eql({
totalDocuments: 4634,
sampledDocuments: 4634,
sampledValues: 4634,
topValues: {
buckets: [
{
count: 4634,
key: 'hello world!',
},
],
},
});
});
it('should apply filters and queries', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'logstash-2015.09.22',
dslQuery: {
bool: {
filter: [{ match: { 'geo.src': 'US' } }],
},
},
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'bytes',
})
.expect(200);
expect(body.totalDocuments).to.eql(425);
});
it('should allow filtering on a runtime field other than the field in use', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'logstash-2015.09.22',
dslQuery: {
bool: {
filter: [{ exists: { field: 'runtime_string_field' } }],
},
},
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'runtime_number_field',
})
.expect(200);
expect(body).to.eql({
totalDocuments: 4634,
sampledDocuments: 4634,
sampledValues: 4634,
topValues: {
buckets: [
{
count: 4634,
key: 5,
},
],
},
histogram: { buckets: [] },
});
});
});
describe('histogram', () => {
before(async () => {
await esArchiver.loadIfNeeded(
'x-pack/test/functional/es_archives/pre_calculated_histogram'
);
});
after(async () => {
await esArchiver.unload('x-pack/test/functional/es_archives/pre_calculated_histogram');
});
it('should return an auto histogram for precalculated histograms', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'histogram-test',
dslQuery: { match_all: {} },
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'histogram-content',
})
.expect(200);
expect(body).to.eql({
histogram: {
buckets: [
{
count: 237,
key: 0,
},
{
count: 323,
key: 0.47000000000000003,
},
{
count: 454,
key: 0.9400000000000001,
},
{
count: 166,
key: 1.4100000000000001,
},
{
count: 168,
key: 1.8800000000000001,
},
{
count: 425,
key: 2.35,
},
{
count: 311,
key: 2.8200000000000003,
},
{
count: 391,
key: 3.29,
},
{
count: 406,
key: 3.7600000000000002,
},
{
count: 324,
key: 4.23,
},
{
count: 628,
key: 4.7,
},
],
},
sampledDocuments: 7,
sampledValues: 3833,
totalDocuments: 7,
topValues: { buckets: [] },
});
});
it('should return a single-value histogram when filtering a precalculated histogram', async () => {
const { body } = await supertest
.post(API_PATH)
.set(COMMON_HEADERS)
.send({
dataViewId: 'histogram-test',
dslQuery: { match: { 'histogram-title': 'single value' } },
fromDate: TEST_START_TIME,
toDate: TEST_END_TIME,
fieldName: 'histogram-content',
})
.expect(200);
expect(body).to.eql({
histogram: { buckets: [{ count: 1, key: 1 }] },
sampledDocuments: 1,
sampledValues: 1,
totalDocuments: 1,
topValues: { buckets: [] },
});
});
});
});
};

View file

@ -29,6 +29,7 @@ export default async function ({ readConfigFile }) {
require.resolve('./partial_results'),
require.resolve('./search'),
require.resolve('./content_management'),
require.resolve('./unified_field_list_examples'),
],
services: {
...functionalConfig.get('services'),

View file

@ -0,0 +1,167 @@
/*
* 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 type { FtrProviderContext } from '../../functional/ftr_provider_context';
const TEST_START_TIME = 'Jan 2, 2021 @ 00:00:00.000';
const TEST_END_TIME = 'Jan 2, 2022 @ 00:00:00.000';
const metaFields = ['_id', '_index', '_score'];
const fieldsWithData = [
'ts',
'filter_field',
'textfield1',
'textfield2',
'mapping_runtime_field',
'data_view_runtime_field',
];
// eslint-disable-next-line import/no-default-export
export default ({ getService, getPageObjects }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const comboBox = getService('comboBox');
const retry = getService('retry');
const testSubjects = getService('testSubjects');
const monacoEditor = getService('monacoEditor');
const PageObjects = getPageObjects(['common', 'timePicker', 'header', 'unifiedFieldList']);
const dataViewTitle = 'existence_index_*';
async function addDSLFilter(value: string) {
await testSubjects.click('addFilter');
await testSubjects.click('editQueryDSL');
await monacoEditor.waitCodeEditorReady('addFilterPopover');
await monacoEditor.setCodeEditorValue(value);
await testSubjects.scrollIntoView('saveFilter');
await testSubjects.clickWhenNotDisabled('saveFilter');
await retry.try(async () => {
await testSubjects.waitForDeleted('saveFilter');
});
await PageObjects.header.waitUntilLoadingHasFinished();
}
async function removeAllDSLFilters() {
await testSubjects.click('showQueryBarMenu');
await testSubjects.click('filter-sets-removeAllFilters');
await PageObjects.header.waitUntilLoadingHasFinished();
}
describe('Fields existence info', () => {
before(async () => {
await esArchiver.load(
'test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword'
);
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/index_patterns/constant_keyword.json'
);
await PageObjects.common.navigateToApp('unifiedFieldListExamples');
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.waitFor('combobox is ready', async () => {
return await testSubjects.exists('dataViewSelector');
});
await comboBox.setCustom('dataViewSelector', dataViewTitle);
await retry.waitFor('page is ready', async () => {
return await testSubjects.exists('globalQueryBar');
});
await PageObjects.timePicker.setAbsoluteRange(TEST_START_TIME, TEST_END_TIME);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.toggleSidebarSection('meta');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword'
);
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/index_patterns/constant_keyword.json'
);
await PageObjects.unifiedFieldList.cleanSidebarLocalStorage();
await kibanaServer.savedObjects.cleanStandardList();
});
describe('existence', () => {
it('should find which fields exist in the sample documents', async () => {
const sidebarFields = await PageObjects.unifiedFieldList.getAllFieldNames();
expect(sidebarFields.sort()).to.eql([...metaFields, ...fieldsWithData].sort());
});
it('should return fields filtered by term query', async () => {
const expectedFieldNames = [
'ts',
'filter_field',
'textfield1',
// textfield2 and mapping_runtime_field are defined on the other index
'data_view_runtime_field',
];
await addDSLFilter(`{
"bool": {
"filter": [{ "term": { "filter_field": "a" } }]
}
}`);
const sidebarFields = await PageObjects.unifiedFieldList.getAllFieldNames();
expect(sidebarFields.sort()).to.eql([...metaFields, ...expectedFieldNames].sort());
await removeAllDSLFilters();
});
it('should return fields filtered by match_phrase query', async () => {
const expectedFieldNames = [
'ts',
'filter_field',
'textfield1',
// textfield2 and mapping_runtime_field are defined on the other index
'data_view_runtime_field',
];
await addDSLFilter(`{
"bool": {
"filter": [{ "match_phrase": { "filter_field": "a" } }]
}
}`);
const sidebarFields = await PageObjects.unifiedFieldList.getAllFieldNames();
expect(sidebarFields.sort()).to.eql([...metaFields, ...expectedFieldNames].sort());
await removeAllDSLFilters();
});
it('should return fields filtered by time range', async () => {
const expectedFieldNames = [
'ts',
'filter_field',
'textfield1',
// textfield2 and mapping_runtime_field are defined on the other index
'data_view_runtime_field',
];
await addDSLFilter(`{
"bool": {
"filter": [{ "term": { "filter_field": "a" } }]
}
}`);
await PageObjects.timePicker.setAbsoluteRange(
TEST_START_TIME,
'Dec 12, 2021 @ 00:00:00.000'
);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
const sidebarFields = await PageObjects.unifiedFieldList.getAllFieldNames();
expect(sidebarFields.sort()).to.eql([...metaFields, ...expectedFieldNames].sort());
await removeAllDSLFilters();
});
});
});
};

View file

@ -0,0 +1,191 @@
/*
* 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 type { FtrProviderContext } from '../../functional/ftr_provider_context';
const TEST_START_TIME = 'Sep 19, 2015 @ 06:31:44.000';
const TEST_END_TIME = 'Sep 23, 2015 @ 18:31:44.000';
// eslint-disable-next-line import/no-default-export
export default ({ getService, getPageObjects }: FtrProviderContext) => {
const PageObjects = getPageObjects(['common', 'timePicker', 'header', 'unifiedFieldList']);
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const comboBox = getService('comboBox');
const retry = getService('retry');
const testSubjects = getService('testSubjects');
const filterBar = getService('filterBar');
const dataViewTitle = 'logstash-2015.09.22';
describe('Field stats', () => {
before(async () => {
await kibanaServer.savedObjects.cleanStandardList();
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional');
await kibanaServer.importExport.load(
'x-pack/test/functional/fixtures/kbn_archiver/visualize/default'
);
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/pre_calculated_histogram');
await PageObjects.common.navigateToApp('unifiedFieldListExamples');
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.waitFor('combobox is ready', async () => {
return await testSubjects.exists('dataViewSelector');
});
await comboBox.setCustom('dataViewSelector', dataViewTitle);
await retry.waitFor('page is ready', async () => {
return await testSubjects.exists('globalQueryBar');
});
await PageObjects.timePicker.setAbsoluteRange(TEST_START_TIME, TEST_END_TIME);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
});
after(async () => {
await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional');
await esArchiver.unload('x-pack/test/functional/es_archives/pre_calculated_histogram');
await kibanaServer.savedObjects.cleanStandardList();
await PageObjects.unifiedFieldList.cleanSidebarLocalStorage();
});
describe('field distribution', () => {
before(async () => {
await PageObjects.unifiedFieldList.toggleSidebarSection('empty'); // it will allow to render more fields in Available fields section
});
it('should return an auto histogram for numbers and top values', async () => {
await PageObjects.unifiedFieldList.clickFieldListItem('bytes');
expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be(
'topValuesAndDistribution'
);
expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634);
expect(await PageObjects.unifiedFieldList.getFieldStatsTopValueBucketsVisibleText()).to.be(
'0\n3.2%\n3,954\n0.1%\n5,846\n0.1%\n6,497\n0.1%\n1,840\n0.1%\n4,206\n0.1%\n4,328\n0.1%\n4,669\n0.1%\n5,863\n0.1%\n6,631\n0.1%\nOther\n96.0%'
);
});
it('should return an auto histogram for dates', async () => {
await PageObjects.unifiedFieldList.clickFieldListItem('@timestamp');
expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be(
'timeDistribution'
);
expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634);
});
it('should return top values for strings', async () => {
await PageObjects.unifiedFieldList.clickFieldListItem('geo.src');
expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('topValues');
expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634);
expect(await PageObjects.unifiedFieldList.getFieldStatsTopValueBucketsVisibleText()).to.be(
'CN\n18.0%\nIN\n17.4%\nUS\n9.2%\nID\n3.4%\nBR\n3.1%\nPK\n2.5%\nBD\n2.3%\nNG\n2.0%\nRU\n1.8%\nJP\n1.6%\nOther\n38.8%'
);
});
it('should return top values for ip fields', async () => {
await PageObjects.unifiedFieldList.clickFieldListItem('ip');
expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('topValues');
expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634);
expect(await PageObjects.unifiedFieldList.getFieldStatsTopValueBucketsVisibleText()).to.be(
'177.194.175.66\n0.3%\n18.55.141.62\n0.3%\n53.55.251.105\n0.3%\n21.111.249.239\n0.2%\n97.63.84.25\n0.2%\n100.99.207.174\n0.2%\n112.34.138.226\n0.2%\n194.68.89.92\n0.2%\n235.186.79.201\n0.2%\n57.79.108.136\n0.2%\nOther\n97.6%'
);
});
it('should return histograms for scripted date fields', async () => {
await PageObjects.unifiedFieldList.clickFieldListItem('scripted_date');
expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be(
'timeDistribution'
);
expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634);
});
it('should return top values for scripted string fields', async () => {
await PageObjects.unifiedFieldList.clickFieldListItem('scripted_string');
expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('topValues');
expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634);
expect(await PageObjects.unifiedFieldList.getFieldStatsTopValueBucketsVisibleText()).to.be(
'hello\n100%'
);
});
it('should return examples for non-aggregatable fields', async () => {
await PageObjects.unifiedFieldList.clickFieldListItem('extension');
expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('exampleValues');
expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(100);
// actual hits might vary
expect(
(await PageObjects.unifiedFieldList.getFieldStatsExampleBucketsVisibleText()).length
).to.above(0);
});
it('should return top values for index pattern runtime string fields', async () => {
await PageObjects.unifiedFieldList.clickFieldListItem('runtime_string_field');
expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('topValues');
expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634);
expect(await PageObjects.unifiedFieldList.getFieldStatsTopValueBucketsVisibleText()).to.be(
'hello world!\n100%'
);
});
it('should apply filters and queries', async () => {
await filterBar.addFilter({ field: 'geo.src', operation: 'is', value: 'US' });
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItem('bytes');
expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be(
'topValuesAndDistribution'
);
expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(425);
await filterBar.removeFilter('geo.src');
await PageObjects.header.waitUntilLoadingHasFinished();
});
it('should allow filtering on a runtime field other than the field in use', async () => {
await filterBar.addFilter({ field: 'runtime_string_field', operation: 'exists' });
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItem('runtime_number_field');
expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('topValues');
expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634);
expect(await PageObjects.unifiedFieldList.getFieldStatsTopValueBucketsVisibleText()).to.be(
'5\n100%'
);
await filterBar.removeFilter('runtime_string_field');
await PageObjects.header.waitUntilLoadingHasFinished();
});
});
describe('histogram', () => {
before(async () => {
await comboBox.setCustom('dataViewSelector', 'histogram-test');
await retry.waitFor('page is ready', async () => {
return await testSubjects.exists('globalQueryBar');
});
await PageObjects.timePicker.setAbsoluteRange(TEST_START_TIME, TEST_END_TIME);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
});
it('should return an auto histogram for precalculated histograms', async () => {
await PageObjects.unifiedFieldList.clickFieldListItem('histogram-content');
expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('histogram');
expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(7);
});
it('should return a single-value histogram when filtering a precalculated histogram', async () => {
await filterBar.addFilter({
field: 'histogram-title',
operation: 'is',
value: 'single value',
});
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItem('histogram-content');
expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('histogram');
expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(1);
await filterBar.removeFilter('histogram-title');
await PageObjects.header.waitUntilLoadingHasFinished();
});
});
});
};

View file

@ -6,11 +6,12 @@
* Side Public License, v 1.
*/
import { FtrProviderContext } from '../../ftr_provider_context';
import { FtrProviderContext } from '../../functional/ftr_provider_context';
export default function lensApiIntegrationTests({ loadTestFile }: FtrProviderContext) {
describe('UnifiedFieldList', () => {
loadTestFile(require.resolve('./existing_fields'));
// eslint-disable-next-line import/no-default-export
export default function ({ loadTestFile }: FtrProviderContext) {
describe('Unified Field List Examples', () => {
loadTestFile(require.resolve('./field_stats'));
loadTestFile(require.resolve('./existing_fields'));
});
}

View file

@ -27,6 +27,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'dashboard',
'context',
'header',
'unifiedFieldList',
]);
const testSubjects = getService('testSubjects');
const dashboardAddPanel = getService('dashboardAddPanel');
@ -43,7 +44,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.header.waitUntilLoadingHasFinished();
for (const columnName of TEST_COLUMN_NAMES) {
await PageObjects.discover.clickFieldListItemAdd(columnName);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(columnName);
}
for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) {

View file

@ -27,6 +27,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'dashboard',
'context',
'header',
'unifiedFieldList',
]);
const testSubjects = getService('testSubjects');
const dashboardAddPanel = getService('dashboardAddPanel');
@ -43,12 +44,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.common.navigateToApp('discover');
await PageObjects.header.waitUntilLoadingHasFinished();
for (const columnName of TEST_COLUMN_NAMES) {
await PageObjects.discover.clickFieldListItemAdd(columnName);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(columnName);
}
for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) {
await PageObjects.discover.clickFieldListItem(columnName);
await PageObjects.discover.clickFieldListPlusFilter(columnName, value);
await PageObjects.unifiedFieldList.clickFieldListItem(columnName);
await PageObjects.unifiedFieldList.clickFieldListPlusFilter(columnName, value);
}
});
after(async () => {

View file

@ -24,6 +24,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'visChart',
'share',
'timePicker',
'unifiedFieldList',
]);
const testSubjects = getService('testSubjects');
const browser = getService('browser');
@ -121,7 +122,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.header.clickDiscover();
await PageObjects.timePicker.setHistoricalDataRange();
await PageObjects.discover.clickFieldListItemAdd('bytes');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('bytes');
await PageObjects.discover.saveSearch('my search');
await PageObjects.header.waitUntilLoadingHasFinished();
@ -135,7 +136,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(inViewMode).to.be(true);
await PageObjects.header.clickDiscover();
await PageObjects.discover.clickFieldListItemAdd('agent');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('agent');
await PageObjects.discover.saveSearch('my search');
await PageObjects.header.waitUntilLoadingHasFinished();
@ -154,7 +155,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.dashboard.saveDashboard('Has local edits');
await PageObjects.header.clickDiscover();
await PageObjects.discover.clickFieldListItemAdd('clientip');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('clientip');
await PageObjects.discover.saveSearch('my search');
await PageObjects.header.waitUntilLoadingHasFinished();

View file

@ -14,7 +14,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const retry = getService('retry');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker', 'settings']);
const PageObjects = getPageObjects([
'common',
'discover',
'header',
'timePicker',
'settings',
'unifiedFieldList',
]);
const defaultSettings = {
defaultIndex: 'logstash-*',
'discover:searchFieldsFromSource': false,
@ -48,13 +55,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('adding a column removes a default column', async function () {
await PageObjects.discover.clickFieldListItemAdd('_score');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('_score');
expect(await PageObjects.discover.getDocHeader()).to.have.string('_score');
expect(await PageObjects.discover.getDocHeader()).not.to.have.string('Document');
});
it('removing a column adds a default column', async function () {
await PageObjects.discover.clickFieldListItemRemove('_score');
await PageObjects.unifiedFieldList.clickFieldListItemRemove('_score');
expect(await PageObjects.discover.getDocHeader()).not.to.have.string('_score');
expect(await PageObjects.discover.getDocHeader()).to.have.string('Document');
});

View file

@ -18,7 +18,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const docTable = getService('docTable');
const queryBar = getService('queryBar');
const find = getService('find');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']);
const PageObjects = getPageObjects([
'common',
'discover',
'header',
'timePicker',
'unifiedFieldList',
]);
const defaultSettings = {
defaultIndex: 'logstash-*',
hideAnnouncements: true,
@ -229,19 +235,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
afterEach(async function () {
for (const column of extraColumns) {
await PageObjects.discover.clickFieldListItemRemove(column);
await PageObjects.unifiedFieldList.clickFieldListItemRemove(column);
await PageObjects.header.waitUntilLoadingHasFinished();
}
});
it('should add more columns to the table', async function () {
for (const column of extraColumns) {
await PageObjects.discover.clearFieldSearchInput();
await PageObjects.discover.findFieldByName(column);
await PageObjects.unifiedFieldList.clearFieldSearchInput();
await PageObjects.unifiedFieldList.findFieldByName(column);
await retry.waitFor('field to appear', async function () {
return await testSubjects.exists(`field-${column}`);
});
await PageObjects.discover.clickFieldListItemAdd(column);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(column);
await PageObjects.header.waitUntilLoadingHasFinished();
// test the header now
const docHeader = await find.byCssSelector('thead > tr:nth-child(1)');
@ -252,16 +258,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should remove columns from the table', async function () {
for (const column of extraColumns) {
await PageObjects.discover.clearFieldSearchInput();
await PageObjects.discover.findFieldByName(column);
await PageObjects.unifiedFieldList.clearFieldSearchInput();
await PageObjects.unifiedFieldList.findFieldByName(column);
await retry.waitFor('field to appear', async function () {
return await testSubjects.exists(`field-${column}`);
});
await PageObjects.discover.clickFieldListItemAdd(column);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(column);
await PageObjects.header.waitUntilLoadingHasFinished();
}
// remove the second column
await PageObjects.discover.clickFieldListItemRemove(extraColumns[1]);
await PageObjects.unifiedFieldList.clickFieldListItemRemove(extraColumns[1]);
await PageObjects.header.waitUntilLoadingHasFinished();
// test that the second column is no longer there
const docHeader = await find.byCssSelector('thead > tr:nth-child(1)');
@ -270,9 +276,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should make the document table scrollable', async function () {
await PageObjects.discover.clearFieldSearchInput();
await PageObjects.unifiedFieldList.clearFieldSearchInput();
const dscTableWrapper = await find.byCssSelector('.kbnDocTableWrapper');
const fieldNames = await PageObjects.discover.getAllFieldNames();
const fieldNames = await PageObjects.unifiedFieldList.getAllFieldNames();
const clientHeight = await dscTableWrapper.getAttribute('clientHeight');
let fieldCounter = 0;
const checkScrollable = async () => {
@ -282,7 +288,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
return Number(scrollWidth) > Number(clientWidth);
};
const addColumn = async () => {
await PageObjects.discover.clickFieldListItemAdd(fieldNames[fieldCounter++]);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(fieldNames[fieldCounter++]);
};
await addColumn();

View file

@ -11,7 +11,7 @@ import { FtrProviderContext } from '../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const PageObjects = getPageObjects(['common', 'discover']);
const PageObjects = getPageObjects(['common', 'discover', 'unifiedFieldList']);
const find = getService('find');
const log = getService('log');
const retry = getService('retry');
@ -39,7 +39,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should break text on newlines', async function () {
await PageObjects.discover.clickFieldListItemToggle('message');
await PageObjects.unifiedFieldList.clickFieldListItemToggle('message');
const dscTableRows = await find.allByCssSelector('.kbnDocTable__row');
await retry.waitFor('height of multi-line content > single-line content', async () => {

View file

@ -19,7 +19,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const queryBar = getService('queryBar');
const inspector = getService('inspector');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']);
const PageObjects = getPageObjects([
'common',
'discover',
'header',
'timePicker',
'unifiedFieldList',
]);
const defaultSettings = {
defaultIndex: 'logstash-*',
@ -235,21 +241,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should add a field, sort by it, remove it and also sorting by it', async function () {
await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings();
await PageObjects.common.navigateToApp('discover');
await PageObjects.discover.clickFieldListItemAdd('_score');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('_score');
await PageObjects.discover.clickFieldSort('_score', 'Sort Low-High');
const currentUrlWithScore = await browser.getCurrentUrl();
expect(currentUrlWithScore).to.contain('_score');
await PageObjects.discover.clickFieldListItemRemove('_score');
await PageObjects.unifiedFieldList.clickFieldListItemRemove('_score');
const currentUrlWithoutScore = await browser.getCurrentUrl();
expect(currentUrlWithoutScore).not.to.contain('_score');
});
it('should add a field with customLabel, sort by it, display it correctly', async function () {
await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings();
await PageObjects.common.navigateToApp('discover');
await PageObjects.discover.clickFieldListItemAdd('referer');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('referer');
await PageObjects.discover.clickFieldSort('referer', 'Sort A-Z');
expect(await PageObjects.discover.getDocHeader()).to.have.string('Referer custom');
expect(await PageObjects.discover.getAllFieldNames()).to.contain('Referer custom');
expect(await PageObjects.unifiedFieldList.getAllFieldNames()).to.contain('Referer custom');
const url = await browser.getCurrentUrl();
expect(url).to.contain('referer');
});

View file

@ -16,7 +16,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const queryBar = getService('queryBar');
const browser = getService('browser');
const PageObjects = getPageObjects(['common', 'header', 'discover', 'visualize', 'timePicker']);
const PageObjects = getPageObjects([
'common',
'header',
'discover',
'visualize',
'timePicker',
'unifiedFieldList',
]);
describe('discover tab', function describeIndexTests() {
this.tags('includeFirefox');
@ -46,11 +53,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// marks is the style that highlights the text in yellow
await queryBar.setQuery('php');
await queryBar.submitQuery();
await PageObjects.discover.clickFieldListItemAdd('extension');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('extension');
const marks = await PageObjects.discover.getMarks();
expect(marks.length).to.be.greaterThan(0);
expect(marks.indexOf('php')).to.be(0);
await PageObjects.discover.clickFieldListItemRemove('extension');
await PageObjects.unifiedFieldList.clickFieldListItemRemove('extension');
});
it('search type:apache should show the correct hit count', async function () {

View file

@ -16,7 +16,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const queryBar = getService('queryBar');
const browser = getService('browser');
const PageObjects = getPageObjects(['common', 'header', 'discover', 'visualize', 'timePicker']);
const PageObjects = getPageObjects([
'common',
'header',
'discover',
'visualize',
'timePicker',
'unifiedFieldList',
]);
describe('discover tab with new fields API', function describeIndexTests() {
this.tags('includeFirefox');
@ -44,11 +51,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('the search term should be highlighted in the field data', async function () {
// marks is the style that highlights the text in yellow
await PageObjects.discover.clickFieldListItemAdd('extension');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('extension');
const marks = await PageObjects.discover.getMarks();
expect(marks.length).to.be.greaterThan(0);
expect(marks.indexOf('php')).to.be(0);
await PageObjects.discover.clickFieldListItemRemove('extension');
await PageObjects.unifiedFieldList.clickFieldListItemRemove('extension');
});
it('search type:apache should show the correct hit count', async function () {

View file

@ -15,7 +15,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const retry = getService('retry');
const PageObjects = getPageObjects(['common', 'timePicker', 'discover', 'header', 'settings']);
const PageObjects = getPageObjects([
'common',
'timePicker',
'discover',
'header',
'settings',
'unifiedFieldList',
]);
describe('source filters', function () {
before(async function () {
@ -51,7 +58,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should not get the field referer', async function () {
const fieldNames = await PageObjects.discover.getAllFieldNames();
const fieldNames = await PageObjects.unifiedFieldList.getAllFieldNames();
expect(fieldNames).to.not.contain('referer');
const relatedContentFields = fieldNames.filter(
(fieldName) => fieldName.indexOf('relatedContent') === 0

View file

@ -30,6 +30,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'header',
'context',
'dashboard',
'unifiedFieldList',
]);
const find = getService('find');
const security = getService('security');
@ -64,7 +65,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'_bytes-runtimefield',
`emit(doc["bytes"].value.toString())`
);
await PageObjects.discover.clickFieldListItemToggle('_bytes-runtimefield');
await PageObjects.unifiedFieldList.clickFieldListItemToggle('_bytes-runtimefield');
const second = await PageObjects.discover.getCurrentDataViewId();
expect(first).not.to.equal(second);
@ -144,7 +145,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'_bytes-runtimefield',
`emit(doc["bytes"].value.toString())`
);
await PageObjects.discover.clickFieldListItemToggle('_bytes-runtimefield');
await PageObjects.unifiedFieldList.clickFieldListItemToggle('_bytes-runtimefield');
const newDataViewId = await PageObjects.discover.getCurrentDataViewId();
expect(newDataViewId).not.to.equal(prevDataViewId);
@ -153,7 +154,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.header.waitUntilLoadingHasFinished();
// remove field and create with the same name, but different value
await PageObjects.discover.clickFieldListItemRemove('_bytes-runtimefield');
await PageObjects.unifiedFieldList.clickFieldListItemRemove('_bytes-runtimefield');
await PageObjects.discover.removeField('_bytes-runtimefield');
await PageObjects.header.waitUntilLoadingHasFinished();
@ -162,7 +163,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'_bytes-runtimefield',
`emit((doc["bytes"].value * 2).toString())`
);
await PageObjects.discover.clickFieldListItemToggle('_bytes-runtimefield');
await PageObjects.unifiedFieldList.clickFieldListItemToggle('_bytes-runtimefield');
// save second search
await PageObjects.discover.saveSearch('logst*-ss-_bytes-runtimefield-updated', true);

View file

@ -12,7 +12,7 @@ import { FtrProviderContext } from '../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
describe('discover data grid tests', function describeDiscoverDataGrid() {
const esArchiver = getService('esArchiver');
const PageObjects = getPageObjects(['common', 'discover', 'timePicker']);
const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'unifiedFieldList']);
const kibanaServer = getService('kibanaServer');
const defaultSettings = { defaultIndex: 'logstash-*' };
const testSubjects = getService('testSubjects');
@ -34,16 +34,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(await getTitles()).to.be('@timestamp Document');
await PageObjects.discover.clickFieldListItemAdd('bytes');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('bytes');
expect(await getTitles()).to.be('@timestamp bytes');
await PageObjects.discover.clickFieldListItemAdd('agent');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('agent');
expect(await getTitles()).to.be('@timestamp bytes agent');
await PageObjects.discover.clickFieldListItemRemove('bytes');
await PageObjects.unifiedFieldList.clickFieldListItemRemove('bytes');
expect(await getTitles()).to.be('@timestamp agent');
await PageObjects.discover.clickFieldListItemRemove('agent');
await PageObjects.unifiedFieldList.clickFieldListItemRemove('agent');
expect(await getTitles()).to.be('@timestamp Document');
});
});

View file

@ -28,6 +28,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'settings',
'dashboard',
'header',
'unifiedFieldList',
]);
const defaultSettings = { defaultIndex: 'logstash-*' };
const kibanaServer = getService('kibanaServer');
@ -47,12 +48,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.common.navigateToApp('discover');
for (const columnName of TEST_COLUMN_NAMES) {
await PageObjects.discover.clickFieldListItemAdd(columnName);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(columnName);
}
for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) {
await PageObjects.discover.clickFieldListItem(columnName);
await PageObjects.discover.clickFieldListPlusFilter(columnName, value);
await PageObjects.unifiedFieldList.clickFieldListItem(columnName);
await PageObjects.unifiedFieldList.clickFieldListPlusFilter(columnName, value);
}
});
after(async () => {

View file

@ -18,7 +18,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const monacoEditor = getService('monacoEditor');
const dashboardAddPanel = getService('dashboardAddPanel');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker', 'dashboard']);
const PageObjects = getPageObjects([
'common',
'discover',
'header',
'timePicker',
'dashboard',
'unifiedFieldList',
]);
const defaultSettings = {
defaultIndex: 'logstash-*',
};
@ -221,16 +228,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
afterEach(async function () {
for (const column of extraColumns) {
await PageObjects.discover.clickFieldListItemRemove(column);
await PageObjects.unifiedFieldList.clickFieldListItemRemove(column);
await PageObjects.header.waitUntilLoadingHasFinished();
}
});
it('should add more columns to the table', async function () {
for (const column of extraColumns) {
await PageObjects.discover.clearFieldSearchInput();
await PageObjects.discover.findFieldByName(column);
await PageObjects.discover.clickFieldListItemAdd(column);
await PageObjects.unifiedFieldList.clearFieldSearchInput();
await PageObjects.unifiedFieldList.findFieldByName(column);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(column);
await PageObjects.header.waitUntilLoadingHasFinished();
// test the header now
const header = await dataGrid.getHeaderFields();
@ -240,13 +247,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should remove columns from the table', async function () {
for (const column of extraColumns) {
await PageObjects.discover.clearFieldSearchInput();
await PageObjects.discover.findFieldByName(column);
await PageObjects.discover.clickFieldListItemAdd(column);
await PageObjects.unifiedFieldList.clearFieldSearchInput();
await PageObjects.unifiedFieldList.findFieldByName(column);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(column);
await PageObjects.header.waitUntilLoadingHasFinished();
}
// remove the second column
await PageObjects.discover.clickFieldListItemRemove(extraColumns[1]);
await PageObjects.unifiedFieldList.clickFieldListItemRemove(extraColumns[1]);
await PageObjects.header.waitUntilLoadingHasFinished();
// test that the second column is no longer there
const header = await dataGrid.getHeaderFields();

View file

@ -14,7 +14,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const queryBar = getService('queryBar');
const PageObjects = getPageObjects(['common', 'header', 'discover', 'visualize', 'timePicker']);
const PageObjects = getPageObjects([
'common',
'header',
'discover',
'visualize',
'timePicker',
'unifiedFieldList',
]);
const defaultSettings = { defaultIndex: 'logstash-*' };
const dataGrid = getService('dataGrid');
const security = getService('security');
@ -43,11 +50,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('the search term should be highlighted in the field data', async function () {
// marks is the style that highlights the text in yellow
await PageObjects.discover.clickFieldListItemAdd('extension');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('extension');
const marks = await PageObjects.discover.getMarks();
expect(marks.length).to.be.greaterThan(0);
expect(marks.indexOf('php')).to.be(0);
await PageObjects.discover.clickFieldListItemRemove('extension');
await PageObjects.unifiedFieldList.clickFieldListItemRemove('extension');
});
it('search type:apache should show the correct hit count', async function () {

View file

@ -21,6 +21,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'discover',
'timePicker',
'dashboard',
'unifiedFieldList',
]);
describe('data view flyout', function () {
@ -76,10 +77,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await retry.waitFor('current data view to get updated', async () => {
return (await PageObjects.discover.getCurrentlySelectedDataView()) === `${initialPattern}*`;
});
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getHitCountInt()).to.be(2);
expect((await PageObjects.discover.getAllFieldNames()).length).to.be(3);
expect((await PageObjects.unifiedFieldList.getAllFieldNames()).length).to.be(3);
});
it('create saved data view', async function () {
@ -91,8 +92,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(await PageObjects.discover.getHitCountInt()).to.be(1);
});
await PageObjects.discover.waitUntilSidebarHasLoaded();
expect((await PageObjects.discover.getAllFieldNames()).length).to.be(2);
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect((await PageObjects.unifiedFieldList.getAllFieldNames()).length).to.be(2);
});
it('update data view with a different time field', async function () {
@ -122,8 +123,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await retry.try(async () => {
expect(await PageObjects.discover.getHitCountInt()).to.be(3);
});
await PageObjects.discover.waitUntilSidebarHasLoaded();
expect((await PageObjects.discover.getAllFieldNames()).length).to.be(3);
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect((await PageObjects.unifiedFieldList.getAllFieldNames()).length).to.be(3);
expect(await PageObjects.discover.isChartVisible()).to.be(true);
expect(await PageObjects.timePicker.timePickerExists()).to.be(true);
});
@ -137,8 +138,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await retry.try(async () => {
expect(await PageObjects.discover.getHitCountInt()).to.be(4);
});
await PageObjects.discover.waitUntilSidebarHasLoaded();
expect((await PageObjects.discover.getAllFieldNames()).length).to.be(3);
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect((await PageObjects.unifiedFieldList.getAllFieldNames()).length).to.be(3);
expect(await PageObjects.discover.isChartVisible()).to.be(false);
expect(await PageObjects.timePicker.timePickerExists()).to.be(false);
});

View file

@ -15,7 +15,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const dataGrid = getService('dataGrid');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker', 'settings']);
const PageObjects = getPageObjects([
'common',
'discover',
'header',
'timePicker',
'settings',
'unifiedFieldList',
]);
const security = getService('security');
const defaultSettings = {
defaultIndex: 'logstash-*',
@ -50,13 +57,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('adding a column removes a default column', async function () {
await PageObjects.discover.clickFieldListItemAdd('_score');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('_score');
expect(await PageObjects.discover.getDocHeader()).to.have.string('_score');
expect(await PageObjects.discover.getDocHeader()).not.to.have.string('Document');
});
it('removing a column adds a default column', async function () {
await PageObjects.discover.clickFieldListItemRemove('_score');
await PageObjects.unifiedFieldList.clickFieldListItemRemove('_score');
expect(await PageObjects.discover.getDocHeader()).not.to.have.string('_score');
expect(await PageObjects.discover.getDocHeader()).to.have.string('Document');
});

View file

@ -14,7 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const security = getService('security');
const retry = getService('retry');
const PageObjects = getPageObjects(['common', 'timePicker', 'discover']);
const PageObjects = getPageObjects(['common', 'timePicker', 'discover', 'unifiedFieldList']);
describe('index pattern with unmapped fields', () => {
before(async () => {
@ -48,18 +48,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await retry.try(async function () {
expect(await PageObjects.discover.getHitCount()).to.be(expectedHitCount);
});
let allFields = await PageObjects.discover.getAllFieldNames();
let allFields = await PageObjects.unifiedFieldList.getAllFieldNames();
// message is a mapped field
expect(allFields.includes('message')).to.be(true);
// sender is not a mapped field
expect(allFields.includes('sender')).to.be(false);
await PageObjects.discover.toggleSidebarSection('unmapped');
await PageObjects.unifiedFieldList.toggleSidebarSection('unmapped');
allFields = await PageObjects.discover.getAllFieldNames();
allFields = await PageObjects.unifiedFieldList.getAllFieldNames();
expect(allFields.includes('sender')).to.be(true); // now visible under Unmapped section
await PageObjects.discover.toggleSidebarSection('unmapped');
await PageObjects.unifiedFieldList.toggleSidebarSection('unmapped');
});
it('unmapped fields exist on an existing saved search', async () => {
@ -68,20 +68,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await retry.try(async function () {
expect(await PageObjects.discover.getHitCount()).to.be(expectedHitCount);
});
let allFields = await PageObjects.discover.getAllFieldNames();
let allFields = await PageObjects.unifiedFieldList.getAllFieldNames();
expect(allFields.includes('message')).to.be(true);
expect(allFields.includes('sender')).to.be(false);
expect(allFields.includes('receiver')).to.be(false);
await PageObjects.discover.toggleSidebarSection('unmapped');
await PageObjects.unifiedFieldList.toggleSidebarSection('unmapped');
allFields = await PageObjects.discover.getAllFieldNames();
allFields = await PageObjects.unifiedFieldList.getAllFieldNames();
// now visible under Unmapped section
expect(allFields.includes('sender')).to.be(true);
expect(allFields.includes('receiver')).to.be(true);
await PageObjects.discover.toggleSidebarSection('unmapped');
await PageObjects.unifiedFieldList.toggleSidebarSection('unmapped');
});
});
}

View file

@ -16,7 +16,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const fieldEditor = getService('fieldEditor');
const security = getService('security');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']);
const PageObjects = getPageObjects([
'common',
'discover',
'header',
'timePicker',
'unifiedFieldList',
]);
const defaultSettings = {
defaultIndex: 'logstash-*',
'discover:searchFieldsFromSource': false,
@ -55,8 +61,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await fieldEditor.setCustomLabel(customLabel);
await fieldEditor.save();
await PageObjects.header.waitUntilLoadingHasFinished();
expect((await PageObjects.discover.getAllFieldNames()).includes(customLabel)).to.be(true);
await PageObjects.discover.clickFieldListItemAdd('bytes');
expect((await PageObjects.unifiedFieldList.getAllFieldNames()).includes(customLabel)).to.be(
true
);
await PageObjects.unifiedFieldList.clickFieldListItemAdd('bytes');
expect(await PageObjects.discover.getDocHeader()).to.have.string(customLabel);
});
@ -64,7 +72,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const field = '_runtimefield';
await createRuntimeField(field);
await retry.waitForWithTimeout('fieldNames to include runtimefield', 5000, async () => {
const fieldNames = await PageObjects.discover.getAllFieldNames();
const fieldNames = await PageObjects.unifiedFieldList.getAllFieldNames();
return fieldNames.includes(field);
});
});
@ -81,7 +89,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.discover.waitForDocTableLoadingComplete();
await retry.waitForWithTimeout('fieldNames to include edits', 5000, async () => {
const fieldNames = await PageObjects.discover.getAllFieldNames();
const fieldNames = await PageObjects.unifiedFieldList.getAllFieldNames();
return fieldNames.includes(newFieldName);
});
});
@ -89,7 +97,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('allows creation of a new field and use it in a saved search', async function () {
const fieldName = '_runtimefield-saved-search';
await createRuntimeField(fieldName);
await PageObjects.discover.clickFieldListItemAdd(fieldName);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(fieldName);
expect(await PageObjects.discover.getDocHeader()).to.have.string(fieldName);
expect(await PageObjects.discover.saveSearch('Saved Search with runtimefield'));
await PageObjects.header.waitUntilLoadingHasFinished();
@ -108,7 +116,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.discover.removeField(fieldName);
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.waitForWithTimeout('fieldNames to include edits', 5000, async () => {
const fieldNames = await PageObjects.discover.getAllFieldNames();
const fieldNames = await PageObjects.unifiedFieldList.getAllFieldNames();
return !fieldNames.includes(fieldName);
});
});

View file

@ -16,7 +16,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const queryBar = getService('queryBar');
const kibanaServer = getService('kibanaServer');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']);
const PageObjects = getPageObjects([
'common',
'discover',
'header',
'timePicker',
'unifiedFieldList',
]);
const testSubjects = getService('testSubjects');
const security = getService('security');
const refreshButtonSelector = 'refreshDataButton';
@ -72,7 +78,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should not fetch data from ES initially', async function () {
expect(await testSubjects.exists(refreshButtonSelector)).to.be(true);
await retry.waitFor('number of fetches to be 0', waitForFetches(0));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(false);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(false);
});
it('should not fetch on indexPattern change', async function () {
@ -83,56 +89,56 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(await testSubjects.exists(refreshButtonSelector)).to.be(true);
await retry.waitFor('number of fetches to be 0', waitForFetches(0));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(false);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(false);
});
it('should fetch data from ES after refreshDataButton click', async function () {
expect(await testSubjects.exists(refreshButtonSelector)).to.be(true);
await retry.waitFor('number of fetches to be 0', waitForFetches(0));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(false);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(false);
await testSubjects.click(refreshButtonSelector);
await testSubjects.missingOrFail(refreshButtonSelector);
await retry.waitFor('number of fetches to be 1', waitForFetches(1));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(true);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(true);
});
it('should fetch data from ES after submit query', async function () {
expect(await testSubjects.exists(refreshButtonSelector)).to.be(true);
await retry.waitFor('number of fetches to be 0', waitForFetches(0));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(false);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(false);
await queryBar.submitQuery();
await testSubjects.missingOrFail(refreshButtonSelector);
await retry.waitFor('number of fetches to be 1', waitForFetches(1));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(true);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(true);
});
it('should fetch data from ES after choosing commonly used time range', async function () {
await PageObjects.discover.selectIndexPattern('logstash-*');
expect(await testSubjects.exists(refreshButtonSelector)).to.be(true);
await retry.waitFor('number of fetches to be 0', waitForFetches(0));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(false);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(false);
await PageObjects.timePicker.setCommonlyUsedTime('This_week');
await testSubjects.missingOrFail(refreshButtonSelector);
await retry.waitFor('number of fetches to be 1', waitForFetches(1));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(true);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(true);
});
it('should fetch data when a search is saved', async function () {
await PageObjects.discover.selectIndexPattern('logstash-*');
await retry.waitFor('number of fetches to be 0', waitForFetches(0));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(false);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(false);
await PageObjects.discover.saveSearch(savedSearchName);
await retry.waitFor('number of fetches to be 1', waitForFetches(1));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(true);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(true);
});
it('should reset state after opening a saved search and pressing New', async function () {
@ -140,20 +146,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.waitFor('number of fetches to be 1', waitForFetches(1));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(true);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(true);
await testSubjects.click('discoverNewButton');
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.waitFor('number of fetches to be 0', waitForFetches(0));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(false);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(false);
});
});
it(`when it's true should fetch data from ES initially`, async function () {
await initSearchOnPageLoad(true);
await retry.waitFor('number of fetches to be 1', waitForFetches(1));
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(true);
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(true);
});
});
}

View file

@ -18,7 +18,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const monacoEditor = getService('monacoEditor');
const security = getService('security');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']);
const PageObjects = getPageObjects([
'common',
'discover',
'header',
'timePicker',
'unifiedFieldList',
]);
const defaultSettings = {
defaultIndex: 'logstash-*',
@ -39,7 +45,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
describe('test', () => {
it('should render sql view correctly', async function () {
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await testSubjects.exists('showQueryBarMenu')).to.be(true);
expect(await testSubjects.exists('superDatePickerToggleQuickMenuButton')).to.be(true);
@ -57,7 +63,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(await testSubjects.exists('discoverFieldListPanelEdit-@message')).to.be(true);
await PageObjects.discover.selectTextBaseLang('SQL');
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await testSubjects.exists('fieldListFiltersFieldSearch')).to.be(true);
expect(await testSubjects.exists('TextBasedLangEditor')).to.be(true);

View file

@ -12,7 +12,13 @@ import { FtrProviderContext } from '../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'header']);
const PageObjects = getPageObjects([
'common',
'discover',
'timePicker',
'header',
'unifiedFieldList',
]);
describe('discover drag and drop', function describeIndexTests() {
before(async function () {
@ -33,15 +39,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
await kibanaServer.savedObjects.cleanStandardList();
await kibanaServer.uiSettings.replace({});
await PageObjects.discover.cleanSidebarLocalStorage();
await PageObjects.unifiedFieldList.cleanSidebarLocalStorage();
});
describe('should add fields as columns via drag and drop', function () {
it('should support dragging and dropping a field onto the grid', async function () {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'53 available fields. 0 empty fields. 3 meta fields.'
);
expect((await PageObjects.discover.getColumnHeaders()).join(', ')).to.be(
@ -63,7 +69,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(
(await PageObjects.discover.getSidebarSectionFieldNames('selected')).join(', ')
(await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('selected')).join(', ')
).to.be('extension, @message');
});
});

View file

@ -18,6 +18,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'timePicker',
'header',
'unifiedSearch',
'unifiedFieldList',
]);
const testSubjects = getService('testSubjects');
const find = getService('find');
@ -48,21 +49,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
await kibanaServer.savedObjects.cleanStandardList();
await kibanaServer.uiSettings.replace({});
await PageObjects.discover.cleanSidebarLocalStorage();
await PageObjects.unifiedFieldList.cleanSidebarLocalStorage();
});
describe('field filtering', function () {
it('should reveal and hide the filter form when the toggle is clicked', async function () {
await PageObjects.discover.openSidebarFieldFilter();
await PageObjects.discover.closeSidebarFieldFilter();
await PageObjects.unifiedFieldList.openSidebarFieldFilter();
await PageObjects.unifiedFieldList.closeSidebarFieldFilter();
});
it('should filter by field type', async function () {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.discover.openSidebarFieldFilter();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.openSidebarFieldFilter();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
@ -70,7 +71,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await retry.waitFor('first updates', async () => {
return (
(await PageObjects.discover.getSidebarAriaDescription()) ===
(await PageObjects.unifiedFieldList.getSidebarAriaDescription()) ===
'7 available fields. 0 empty fields. 2 meta fields.'
);
});
@ -79,7 +80,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await retry.waitFor('second updates', async () => {
return (
(await PageObjects.discover.getSidebarAriaDescription()) ===
(await PageObjects.unifiedFieldList.getSidebarAriaDescription()) ===
'13 available fields. 0 empty fields. 3 meta fields.'
);
});
@ -88,7 +89,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await retry.waitFor('reset', async () => {
return (
(await PageObjects.discover.getSidebarAriaDescription()) === INITIAL_FIELD_LIST_SUMMARY
(await PageObjects.unifiedFieldList.getSidebarAriaDescription()) ===
INITIAL_FIELD_LIST_SUMMARY
);
});
});
@ -97,20 +99,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await kibanaServer.uiSettings.update({ 'discover:enableSql': true });
await browser.refresh();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.discover.openSidebarFieldFilter();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.openSidebarFieldFilter();
let options = await find.allByCssSelector('[data-test-subj*="typeFilter"]');
expect(options).to.have.length(6);
await PageObjects.discover.closeSidebarFieldFilter();
await PageObjects.unifiedFieldList.closeSidebarFieldFilter();
await PageObjects.discover.selectTextBaseLang('SQL');
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.discover.openSidebarFieldFilter();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.openSidebarFieldFilter();
options = await find.allByCssSelector('[data-test-subj*="typeFilter"]');
expect(options).to.have.length(3);
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'50 selected fields. 51 available fields.'
);
@ -118,7 +120,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await retry.waitFor('updates', async () => {
return (
(await PageObjects.discover.getSidebarAriaDescription()) ===
(await PageObjects.unifiedFieldList.getSidebarAriaDescription()) ===
'6 selected fields. 6 available fields.'
);
});
@ -126,26 +128,26 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should be able to search by string', async function () {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
await PageObjects.discover.findFieldByName('i');
await PageObjects.unifiedFieldList.findFieldByName('i');
await retry.waitFor('first updates', async () => {
return (
(await PageObjects.discover.getSidebarAriaDescription()) ===
(await PageObjects.unifiedFieldList.getSidebarAriaDescription()) ===
'30 available fields. 0 empty fields. 2 meta fields.'
);
});
await PageObjects.discover.findFieldByName('p');
await PageObjects.unifiedFieldList.findFieldByName('p');
await retry.waitFor('second updates', async () => {
return (
(await PageObjects.discover.getSidebarAriaDescription()) ===
(await PageObjects.unifiedFieldList.getSidebarAriaDescription()) ===
'4 available fields. 0 empty fields. 0 meta fields.'
);
});
@ -155,7 +157,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await retry.waitFor('reset', async () => {
return (
(await PageObjects.discover.getSidebarAriaDescription()) === INITIAL_FIELD_LIST_SUMMARY
(await PageObjects.unifiedFieldList.getSidebarAriaDescription()) ===
INITIAL_FIELD_LIST_SUMMARY
);
});
});
@ -166,21 +169,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.header.waitUntilLoadingHasFinished();
const allTermsResult = 'jpg\n65.0%\ncss\n15.4%\npng\n9.8%\ngif\n6.6%\nphp\n3.2%';
await PageObjects.discover.clickFieldListItem('extension');
await PageObjects.unifiedFieldList.clickFieldListItem('extension');
expect(await testSubjects.getVisibleText('dscFieldStats-topValues')).to.be(allTermsResult);
await filterBar.addFilter({ field: 'extension', operation: 'is', value: 'jpg' });
await PageObjects.header.waitUntilLoadingHasFinished();
const onlyJpgResult = 'jpg\n100%';
await PageObjects.discover.clickFieldListItem('extension');
await PageObjects.unifiedFieldList.clickFieldListItem('extension');
expect(await testSubjects.getVisibleText('dscFieldStats-topValues')).to.be(onlyJpgResult);
await filterBar.toggleFilterNegated('extension');
await PageObjects.header.waitUntilLoadingHasFinished();
const jpgExcludedResult = 'css\n44.1%\npng\n28.0%\ngif\n18.8%\nphp\n9.1%';
await PageObjects.discover.clickFieldListItem('extension');
await PageObjects.unifiedFieldList.clickFieldListItem('extension');
expect(await testSubjects.getVisibleText('dscFieldStats-topValues')).to.be(
jpgExcludedResult
);
@ -188,14 +191,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await filterBar.toggleFilterPinned('extension');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.clickFieldListItem('extension');
await PageObjects.unifiedFieldList.clickFieldListItem('extension');
expect(await testSubjects.getVisibleText('dscFieldStats-topValues')).to.be(
jpgExcludedResult
);
await browser.refresh();
await PageObjects.discover.clickFieldListItem('extension');
await PageObjects.unifiedFieldList.clickFieldListItem('extension');
expect(await testSubjects.getVisibleText('dscFieldStats-topValues')).to.be(
jpgExcludedResult
);
@ -203,7 +206,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await filterBar.toggleFilterEnabled('extension');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.clickFieldListItem('extension');
await PageObjects.unifiedFieldList.clickFieldListItem('extension');
expect(await testSubjects.getVisibleText('dscFieldStats-topValues')).to.be(allTermsResult);
});
});
@ -226,24 +229,28 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
describe('renders field groups', function () {
it('should show field list groups excluding subfields', async function () {
await PageObjects.discover.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(true);
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(true);
// Initial Available fields
const expectedInitialAvailableFields =
'@message, @tags, @timestamp, agent, bytes, clientip, extension, geo.coordinates, geo.dest, geo.src, geo.srcdest, headings, host, id, index, ip, links, machine.os, machine.ram, machine.ram_range, memory, meta.char, meta.related, meta.user.firstname, meta.user.lastname, nestedField.child, phpmemory, referer, relatedContent.article:modified_time, relatedContent.article:published_time, relatedContent.article:section, relatedContent.article:tag, relatedContent.og:description, relatedContent.og:image, relatedContent.og:image:height, relatedContent.og:image:width, relatedContent.og:site_name, relatedContent.og:title, relatedContent.og:type, relatedContent.og:url, relatedContent.twitter:card, relatedContent.twitter:description, relatedContent.twitter:image, relatedContent.twitter:site, relatedContent.twitter:title, relatedContent.url, request, response, spaces, type';
let availableFields = await PageObjects.discover.getSidebarSectionFieldNames('available');
let availableFields = await PageObjects.unifiedFieldList.getSidebarSectionFieldNames(
'available'
);
expect(availableFields.length).to.be(50);
expect(availableFields.join(', ')).to.be(expectedInitialAvailableFields);
// Available fields after scrolling down
const emptySectionButton = await find.byCssSelector(
PageObjects.discover.getSidebarSectionSelector('empty', true)
PageObjects.unifiedFieldList.getSidebarSectionSelector('empty', true)
);
await emptySectionButton.scrollIntoViewIfNecessary();
await retry.waitFor('list to update after scrolling', async () => {
availableFields = await PageObjects.discover.getSidebarSectionFieldNames('available');
availableFields = await PageObjects.unifiedFieldList.getSidebarSectionFieldNames(
'available'
);
return availableFields.length === 53;
});
@ -252,18 +259,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
);
// Expand Empty section
await PageObjects.discover.toggleSidebarSection('empty');
expect((await PageObjects.discover.getSidebarSectionFieldNames('empty')).join(', ')).to.be(
''
);
await PageObjects.unifiedFieldList.toggleSidebarSection('empty');
expect(
(await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('empty')).join(', ')
).to.be('');
// Expand Meta section
await PageObjects.discover.toggleSidebarSection('meta');
expect((await PageObjects.discover.getSidebarSectionFieldNames('meta')).join(', ')).to.be(
'_id, _index, _score'
);
await PageObjects.unifiedFieldList.toggleSidebarSection('meta');
expect(
(await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('meta')).join(', ')
).to.be('_id, _index, _score');
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
});
@ -272,11 +279,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await kibanaServer.uiSettings.update({ 'discover:searchFieldsFromSource': true });
await browser.refresh();
await PageObjects.discover.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.doesSidebarShowFields()).to.be(true);
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.unifiedFieldList.doesSidebarShowFields()).to.be(true);
// Initial Available fields
const availableFields = await PageObjects.discover.getSidebarSectionFieldNames('available');
const availableFields = await PageObjects.unifiedFieldList.getSidebarSectionFieldNames(
'available'
);
expect(availableFields.length).to.be(50);
expect(
availableFields
@ -288,68 +297,70 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// Available fields after scrolling down
const emptySectionButton = await find.byCssSelector(
PageObjects.discover.getSidebarSectionSelector('empty', true)
PageObjects.unifiedFieldList.getSidebarSectionSelector('empty', true)
);
await emptySectionButton.scrollIntoViewIfNecessary();
// Expand Empty section
await PageObjects.discover.toggleSidebarSection('empty');
expect((await PageObjects.discover.getSidebarSectionFieldNames('empty')).join(', ')).to.be(
''
);
await PageObjects.unifiedFieldList.toggleSidebarSection('empty');
expect(
(await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('empty')).join(', ')
).to.be('');
// Expand Meta section
await PageObjects.discover.toggleSidebarSection('meta');
expect((await PageObjects.discover.getSidebarSectionFieldNames('meta')).join(', ')).to.be(
'_id, _index, _score'
);
await PageObjects.unifiedFieldList.toggleSidebarSection('meta');
expect(
(await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('meta')).join(', ')
).to.be('_id, _index, _score');
// Expand Unmapped section
await PageObjects.discover.toggleSidebarSection('unmapped');
await PageObjects.unifiedFieldList.toggleSidebarSection('unmapped');
expect(
(await PageObjects.discover.getSidebarSectionFieldNames('unmapped')).join(', ')
(await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('unmapped')).join(', ')
).to.be('relatedContent');
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'53 available fields. 1 unmapped field. 0 empty fields. 3 meta fields.'
);
});
it('should show selected and popular fields', async function () {
await PageObjects.discover.clickFieldListItemAdd('extension');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('extension');
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.discover.clickFieldListItemAdd('@message');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('@message');
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(
(await PageObjects.discover.getSidebarSectionFieldNames('selected')).join(', ')
(await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('selected')).join(', ')
).to.be('extension, @message');
const availableFields = await PageObjects.discover.getSidebarSectionFieldNames('available');
const availableFields = await PageObjects.unifiedFieldList.getSidebarSectionFieldNames(
'available'
);
expect(availableFields.includes('extension')).to.be(true);
expect(availableFields.includes('@message')).to.be(true);
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'2 selected fields. 2 popular fields. 53 available fields. 0 empty fields. 3 meta fields.'
);
await PageObjects.discover.clickFieldListItemRemove('@message');
await PageObjects.unifiedFieldList.clickFieldListItemRemove('@message');
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.discover.clickFieldListItemAdd('_id');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('_id');
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.discover.clickFieldListItemAdd('@message');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('@message');
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(
(await PageObjects.discover.getSidebarSectionFieldNames('selected')).join(', ')
(await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('selected')).join(', ')
).to.be('extension, _id, @message');
expect(
(await PageObjects.discover.getSidebarSectionFieldNames('popular')).join(', ')
(await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('popular')).join(', ')
).to.be('@message, _id, extension');
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'3 selected fields. 3 popular fields. 53 available fields. 0 empty fields. 3 meta fields.'
);
});
@ -358,22 +369,22 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await kibanaServer.uiSettings.update({ 'discover:enableSql': true });
await browser.refresh();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
await PageObjects.discover.selectTextBaseLang('SQL');
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'50 selected fields. 51 available fields.'
);
await PageObjects.discover.clickFieldListItemRemove('extension');
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.clickFieldListItemRemove('extension');
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'49 selected fields. 51 available fields.'
);
@ -385,13 +396,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await monacoEditor.setCodeEditorValue(testQuery);
await testSubjects.click('querySubmitButton');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'3 selected fields. 3 available fields.'
);
expect(
(await PageObjects.discover.getSidebarSectionFieldNames('selected')).join(', ')
(await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('selected')).join(', ')
).to.be('@tags, geo.dest, occurred');
await PageObjects.unifiedSearch.switchDataView(
@ -401,9 +412,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'53 available fields. 0 empty fields. 3 meta fields.'
);
});
@ -414,25 +425,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'test/functional/fixtures/kbn_archiver/index_pattern_without_timefield'
);
await browser.refresh();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
await PageObjects.discover.selectIndexPattern('with-timefield');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'0 available fields. 0 empty fields. 0 meta fields.'
);
await testSubjects.missingOrFail(
`${PageObjects.discover.getSidebarSectionSelector('available')}-fetchWarning`
`${PageObjects.unifiedFieldList.getSidebarSectionSelector('available')}-fetchWarning`
);
await testSubjects.existOrFail(
`${PageObjects.discover.getSidebarSectionSelector(
`${PageObjects.unifiedFieldList.getSidebarSectionSelector(
'available'
)}NoFieldsCallout-noFieldsExist`
);
@ -440,9 +451,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.discover.selectIndexPattern('logstash-*');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
await kibanaServer.importExport.unload(
@ -459,31 +470,31 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
);
await browser.refresh();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
await PageObjects.discover.selectIndexPattern('without-timefield');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'6 available fields. 0 empty fields. 3 meta fields.'
);
await PageObjects.discover.selectIndexPattern('with-timefield');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'0 available fields. 7 empty fields. 3 meta fields.'
);
await testSubjects.existOrFail(
`${PageObjects.discover.getSidebarSectionSelector(
`${PageObjects.unifiedFieldList.getSidebarSectionSelector(
'available'
)}NoFieldsCallout-noFieldsMatch`
);
@ -491,9 +502,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.discover.selectIndexPattern('logstash-*');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
@ -509,25 +520,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should work when filters change', async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
await PageObjects.discover.clickFieldListItem('extension');
await PageObjects.unifiedFieldList.clickFieldListItem('extension');
expect(await testSubjects.getVisibleText('dscFieldStats-topValues')).to.be(
'jpg\n65.0%\ncss\n15.4%\npng\n9.8%\ngif\n6.6%\nphp\n3.2%'
);
await filterBar.addFilter({ field: 'extension', operation: 'is', value: 'jpg' });
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
// check that the filter was passed down to the sidebar
await PageObjects.discover.clickFieldListItem('extension');
await PageObjects.unifiedFieldList.clickFieldListItem('extension');
expect(await testSubjects.getVisibleText('dscFieldStats-topValues')).to.be('jpg\n100%');
});
@ -538,27 +549,27 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
);
await browser.refresh();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
await PageObjects.discover.selectIndexPattern('indices-stats*');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'6873 available fields. 0 empty fields. 3 meta fields.'
);
await PageObjects.discover.selectIndexPattern('logstash-*');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
@ -572,7 +583,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.discover.createAdHocDataView('logstash', true);
await PageObjects.header.waitUntilLoadingHasFinished();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
@ -586,13 +597,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'54 available fields. 0 empty fields. 3 meta fields.'
);
let allFields = await PageObjects.discover.getAllFieldNames();
let allFields = await PageObjects.unifiedFieldList.getAllFieldNames();
expect(allFields.includes('_bytes-runtimefield')).to.be(true);
await PageObjects.discover.editField('_bytes-runtimefield');
@ -605,25 +616,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'54 available fields. 0 empty fields. 3 meta fields.'
);
allFields = await PageObjects.discover.getAllFieldNames();
allFields = await PageObjects.unifiedFieldList.getAllFieldNames();
expect(allFields.includes('_bytes-runtimefield2')).to.be(true);
expect(allFields.includes('_bytes-runtimefield')).to.be(false);
await PageObjects.discover.removeField('_bytes-runtimefield');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
allFields = await PageObjects.discover.getAllFieldNames();
allFields = await PageObjects.unifiedFieldList.getAllFieldNames();
expect(allFields.includes('_bytes-runtimefield2')).to.be(false);
expect(allFields.includes('_bytes-runtimefield')).to.be(false);
});
@ -633,7 +644,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await testSubjects.missingOrFail('discoverNoResultsError');
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
@ -644,13 +655,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// error in fetching documents because of the invalid runtime field
await testSubjects.existOrFail('discoverNoResultsError');
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
// check that the sidebar is rendered
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'54 available fields. 0 empty fields. 3 meta fields.'
);
let allFields = await PageObjects.discover.getAllFieldNames();
let allFields = await PageObjects.unifiedFieldList.getAllFieldNames();
expect(allFields.includes('_invalid-runtimefield')).to.be(true);
await browser.refresh();
@ -658,13 +669,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await testSubjects.existOrFail('discoverNoResultsError'); // still has error
// check that the sidebar is rendered event after a refresh
await PageObjects.discover.waitUntilSidebarHasLoaded();
allFields = await PageObjects.discover.getAllFieldNames();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
allFields = await PageObjects.unifiedFieldList.getAllFieldNames();
expect(allFields.includes('_invalid-runtimefield')).to.be(true);
await PageObjects.discover.removeField('_invalid-runtimefield');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
await testSubjects.missingOrFail('discoverNoResultsError');
});
@ -678,22 +689,22 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
);
await browser.refresh();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
INITIAL_FIELD_LIST_SUMMARY
);
await PageObjects.discover.selectIndexPattern('with-timefield');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'0 available fields. 7 empty fields. 3 meta fields.'
);
await testSubjects.existOrFail(
`${PageObjects.discover.getSidebarSectionSelector(
`${PageObjects.unifiedFieldList.getSidebarSectionSelector(
'available'
)}NoFieldsCallout-noFieldsMatch`
);
@ -704,9 +715,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
expect(await PageObjects.discover.getSidebarAriaDescription()).to.be(
expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be(
'7 available fields. 0 empty fields. 3 meta fields.'
);
@ -728,26 +739,28 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
let selectedFields = await PageObjects.discover.getSidebarSectionFieldNames('selected');
let selectedFields = await PageObjects.unifiedFieldList.getSidebarSectionFieldNames(
'selected'
);
expect(selectedFields.includes(newField)).to.be(false);
expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Document']);
await PageObjects.discover.clickFieldListItemAdd(newField);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(newField);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
selectedFields = await PageObjects.discover.getSidebarSectionFieldNames('selected');
selectedFields = await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('selected');
expect(selectedFields.includes(newField)).to.be(true);
expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', newField]);
await PageObjects.discover.removeField(newField);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitUntilSidebarHasLoaded();
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
await retry.waitFor('sidebar to update', async () => {
return !(await PageObjects.discover.getAllFieldNames()).includes(newField);
return !(await PageObjects.unifiedFieldList.getAllFieldNames()).includes(newField);
});
expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Document']);

View file

@ -16,7 +16,7 @@
// 1. Create scripted field
// 2. See the expected value of the scripted field in Discover doc view
// 3. Filter in Discover by the scripted field
// 4. Visualize with aggregation on the scripted field by clicking discover.clickFieldListItemVisualize
// 4. Visualize with aggregation on the scripted field by clicking unifiedFieldList.clickFieldListItemVisualize
// NOTE: Scripted field input is managed by Ace editor, which automatically
// appends closing braces, for exmaple, if you type opening square brace [
@ -41,6 +41,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'visualize',
'discover',
'timePicker',
'unifiedFieldList',
]);
describe('scripted fields', function () {
@ -154,9 +155,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should see scripted field value in Discover', async function () {
await PageObjects.common.navigateToApp('discover');
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName);
await retry.try(async function () {
await PageObjects.discover.clickFieldListItemAdd(scriptedPainlessFieldName);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(scriptedPainlessFieldName);
});
await PageObjects.header.waitUntilLoadingHasFinished();
@ -196,9 +197,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should filter by scripted field value in Discover', async function () {
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName);
await log.debug('filter by the first value (14) in the expanded scripted field list');
await PageObjects.discover.clickFieldListPlusFilter(scriptedPainlessFieldName, '14');
await PageObjects.unifiedFieldList.clickFieldListPlusFilter(
scriptedPainlessFieldName,
'14'
);
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.try(async function () {
@ -208,7 +212,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should visualize scripted field in vertical bar chart', async function () {
await filterBar.removeAllFilters();
await PageObjects.discover.clickFieldListItemVisualize(scriptedPainlessFieldName);
await PageObjects.unifiedFieldList.clickFieldListItemVisualize(scriptedPainlessFieldName);
await PageObjects.header.waitUntilLoadingHasFinished();
// verify Lens opens a visualization
await retry.waitFor('lens visualization', async () => {
@ -257,9 +261,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should see scripted field value in Discover', async function () {
await PageObjects.common.navigateToApp('discover');
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName2);
await retry.try(async function () {
await PageObjects.discover.clickFieldListItemAdd(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(scriptedPainlessFieldName2);
});
await PageObjects.header.waitUntilLoadingHasFinished();
@ -299,9 +303,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should filter by scripted field value in Discover', async function () {
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName2);
await log.debug('filter by "bad" in the expanded scripted field list');
await PageObjects.discover.clickFieldListPlusFilter(scriptedPainlessFieldName2, 'bad');
await PageObjects.unifiedFieldList.clickFieldListPlusFilter(
scriptedPainlessFieldName2,
'bad'
);
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.try(async function () {
@ -311,7 +318,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should visualize scripted field in vertical bar chart', async function () {
await PageObjects.discover.clickFieldListItemVisualize(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItemVisualize(scriptedPainlessFieldName2);
await PageObjects.header.waitUntilLoadingHasFinished();
// verify Lens opens a visualization
await retry.waitFor('lens visualization', async () => {
@ -359,9 +366,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should see scripted field value in Discover', async function () {
await PageObjects.common.navigateToApp('discover');
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName2);
await retry.try(async function () {
await PageObjects.discover.clickFieldListItemAdd(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(scriptedPainlessFieldName2);
});
await PageObjects.header.waitUntilLoadingHasFinished();
@ -372,9 +379,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should filter by scripted field value in Discover', async function () {
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName2);
await log.debug('filter by "true" in the expanded scripted field list');
await PageObjects.discover.clickFieldListPlusFilter(scriptedPainlessFieldName2, 'true');
await PageObjects.unifiedFieldList.clickFieldListPlusFilter(
scriptedPainlessFieldName2,
'true'
);
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.try(async function () {
@ -406,7 +416,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should visualize scripted field in vertical bar chart', async function () {
await PageObjects.discover.clickFieldListItemVisualize(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItemVisualize(scriptedPainlessFieldName2);
await PageObjects.header.waitUntilLoadingHasFinished();
// verify Lens opens a visualization
await retry.waitFor('lens visualization', async () => {
@ -454,9 +464,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should see scripted field value in Discover', async function () {
await PageObjects.common.navigateToApp('discover');
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName2);
await retry.try(async function () {
await PageObjects.discover.clickFieldListItemAdd(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(scriptedPainlessFieldName2);
});
await PageObjects.header.waitUntilLoadingHasFinished();
@ -502,7 +512,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should visualize scripted field in vertical bar chart', async function () {
await PageObjects.discover.clickFieldListItemVisualize(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItemVisualize(scriptedPainlessFieldName2);
await PageObjects.header.waitUntilLoadingHasFinished();
// verify Lens opens a visualization
await retry.waitFor('lens visualization', async () => {

View file

@ -16,7 +16,7 @@
// 1. Create scripted field
// 2. See the expected value of the scripted field in Discover doc view
// 3. Filter in Discover by the scripted field
// 4. Visualize with aggregation on the scripted field by clicking discover.clickFieldListItemVisualize
// 4. Visualize with aggregation on the scripted field by clicking unifiedFieldList.clickFieldListItemVisualize
// NOTE: Scripted field input is managed by Ace editor, which automatically
// appends closing braces, for exmaple, if you type opening square brace [
@ -40,6 +40,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'visualize',
'discover',
'timePicker',
'unifiedFieldList',
]);
describe('scripted fields', function () {
@ -142,9 +143,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.common.navigateToApp('discover');
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName);
await retry.try(async function () {
await PageObjects.discover.clickFieldListItemAdd(scriptedPainlessFieldName);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(scriptedPainlessFieldName);
});
await PageObjects.header.waitUntilLoadingHasFinished();
@ -176,9 +177,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should filter by scripted field value in Discover', async function () {
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName);
await log.debug('filter by the first value (14) in the expanded scripted field list');
await PageObjects.discover.clickFieldListPlusFilter(scriptedPainlessFieldName, '14');
await PageObjects.unifiedFieldList.clickFieldListPlusFilter(
scriptedPainlessFieldName,
'14'
);
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.try(async function () {
@ -188,7 +192,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should visualize scripted field in vertical bar chart', async function () {
await filterBar.removeAllFilters();
await PageObjects.discover.clickFieldListItemVisualize(scriptedPainlessFieldName);
await PageObjects.unifiedFieldList.clickFieldListItemVisualize(scriptedPainlessFieldName);
await PageObjects.header.waitUntilLoadingHasFinished();
// verify Lens opens a visualization
expect(await testSubjects.getVisibleTextAll('lns-dimensionTrigger')).to.contain(
@ -229,9 +233,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.common.navigateToApp('discover');
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName2);
await retry.try(async function () {
await PageObjects.discover.clickFieldListItemAdd(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(scriptedPainlessFieldName2);
});
await PageObjects.header.waitUntilLoadingHasFinished();
@ -263,9 +267,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should filter by scripted field value in Discover', async function () {
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName2);
await log.debug('filter by "bad" in the expanded scripted field list');
await PageObjects.discover.clickFieldListPlusFilter(scriptedPainlessFieldName2, 'bad');
await PageObjects.unifiedFieldList.clickFieldListPlusFilter(
scriptedPainlessFieldName2,
'bad'
);
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.try(async function () {
@ -275,7 +282,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should visualize scripted field in vertical bar chart', async function () {
await PageObjects.discover.clickFieldListItemVisualize(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItemVisualize(scriptedPainlessFieldName2);
await PageObjects.header.waitUntilLoadingHasFinished();
// verify Lens opens a visualization
expect(await testSubjects.getVisibleTextAll('lns-dimensionTrigger')).to.contain(
@ -315,9 +322,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.common.navigateToApp('discover');
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName2);
await retry.try(async function () {
await PageObjects.discover.clickFieldListItemAdd(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(scriptedPainlessFieldName2);
});
await PageObjects.header.waitUntilLoadingHasFinished();
@ -328,9 +335,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should filter by scripted field value in Discover', async function () {
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName2);
await log.debug('filter by "true" in the expanded scripted field list');
await PageObjects.discover.clickFieldListPlusFilter(scriptedPainlessFieldName2, 'true');
await PageObjects.unifiedFieldList.clickFieldListPlusFilter(
scriptedPainlessFieldName2,
'true'
);
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.try(async function () {
@ -362,7 +372,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should visualize scripted field in vertical bar chart', async function () {
await PageObjects.discover.clickFieldListItemVisualize(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItemVisualize(scriptedPainlessFieldName2);
await PageObjects.header.waitUntilLoadingHasFinished();
// verify Lens opens a visualization
expect(await testSubjects.getVisibleTextAll('lns-dimensionTrigger')).to.contain(
@ -402,9 +412,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.common.navigateToApp('discover');
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName2);
await retry.try(async function () {
await PageObjects.discover.clickFieldListItemAdd(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItemAdd(scriptedPainlessFieldName2);
});
await PageObjects.header.waitUntilLoadingHasFinished();
@ -450,7 +460,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should visualize scripted field in vertical bar chart', async function () {
await PageObjects.discover.clickFieldListItemVisualize(scriptedPainlessFieldName2);
await PageObjects.unifiedFieldList.clickFieldListItemVisualize(scriptedPainlessFieldName2);
await PageObjects.header.waitUntilLoadingHasFinished();
// verify Lens opens a visualization
expect(await testSubjects.getVisibleTextAll('lns-dimensionTrigger')).to.contain('painDate');

View file

@ -10,8 +10,6 @@ import expect from '@kbn/expect';
import { FtrService } from '../ftr_provider_context';
import { WebElementWrapper } from '../services/lib/web_element_wrapper';
type SidebarSectionName = 'meta' | 'empty' | 'available' | 'unmapped' | 'popular' | 'selected';
export class DiscoverPageObject extends FtrService {
private readonly retry = this.ctx.getService('retry');
private readonly testSubjects = this.ctx.getService('testSubjects');
@ -19,6 +17,7 @@ export class DiscoverPageObject extends FtrService {
private readonly flyout = this.ctx.getService('flyout');
private readonly header = this.ctx.getPageObject('header');
private readonly unifiedSearch = this.ctx.getPageObject('unifiedSearch');
private readonly unifiedFieldList = this.ctx.getPageObject('unifiedFieldList');
private readonly browser = this.ctx.getService('browser');
private readonly globalNav = this.ctx.getService('globalNav');
private readonly elasticChart = this.ctx.getService('elasticChart');
@ -45,16 +44,6 @@ export class DiscoverPageObject extends FtrService {
}
}
public async findFieldByName(name: string) {
const fieldSearch = await this.testSubjects.find('fieldListFiltersFieldSearch');
await fieldSearch.type(name);
}
public async clearFieldSearchInput() {
const fieldSearch = await this.testSubjects.find('fieldListFiltersFieldSearch');
await fieldSearch.clearValue();
}
public async saveSearch(
searchName: string,
saveAsNew?: boolean,
@ -391,24 +380,16 @@ export class DiscoverPageObject extends FtrService {
});
}
public async getAllFieldNames() {
const sidebar = await this.testSubjects.find('discover-sidebar');
const $ = await sidebar.parseDomContent();
return $('.kbnFieldButton__name')
.toArray()
.map((field) => $(field).text());
}
public async editField(field: string) {
await this.retry.try(async () => {
await this.clickFieldListItem(field);
await this.unifiedFieldList.clickFieldListItem(field);
await this.testSubjects.click(`discoverFieldListPanelEdit-${field}`);
await this.find.byClassName('indexPatternFieldEditor__form');
});
}
public async removeField(field: string) {
await this.clickFieldListItem(field);
await this.unifiedFieldList.clickFieldListItem(field);
await this.testSubjects.click(`discoverFieldListPanelDelete-${field}`);
await this.retry.waitFor('modal to open', async () => {
return await this.testSubjects.exists('runtimeFieldDeleteConfirmModal');
@ -485,71 +466,6 @@ export class DiscoverPageObject extends FtrService {
return await this.testSubjects.click('discoverNoResultsViewAllMatches');
}
public async getSidebarAriaDescription(): Promise<string> {
return await (
await this.testSubjects.find('fieldListGrouped__ariaDescription')
).getAttribute('innerText');
}
public async cleanSidebarLocalStorage(): Promise<void> {
await this.browser.setLocalStorageItem('discover.unifiedFieldList.initiallyOpenSections', '{}');
}
public async waitUntilSidebarHasLoaded() {
await this.retry.waitFor('sidebar is loaded', async () => {
return (await this.getSidebarAriaDescription()).length > 0;
});
}
public async doesSidebarShowFields() {
return await this.testSubjects.exists('fieldListGroupedFieldGroups');
}
public getSidebarSectionSelector(
sectionName: SidebarSectionName,
asCSSSelector: boolean = false
) {
const testSubj = `fieldListGrouped${sectionName[0].toUpperCase()}${sectionName.substring(
1
)}Fields`;
if (!asCSSSelector) {
return testSubj;
}
return `[data-test-subj="${testSubj}"]`;
}
public async getSidebarSectionFieldNames(sectionName: SidebarSectionName): Promise<string[]> {
const elements = await this.find.allByCssSelector(
`${this.getSidebarSectionSelector(sectionName, true)} li`
);
if (!elements?.length) {
return [];
}
return Promise.all(
elements.map(async (element) => await element.getAttribute('data-attr-field'))
);
}
public async toggleSidebarSection(sectionName: SidebarSectionName) {
return await this.find.clickByCssSelector(
`${this.getSidebarSectionSelector(sectionName, true)} .euiAccordion__iconButton`
);
}
public async waitUntilFieldPopoverIsOpen() {
await this.retry.waitFor('popover is open', async () => {
return Boolean(await this.find.byCssSelector('[data-popover-open="true"]'));
});
}
public async clickFieldListItem(field: string) {
await this.testSubjects.click(`field-${field}`);
await this.waitUntilFieldPopoverIsOpen();
}
public async clickFieldSort(field: string, text = 'Sort New-Old') {
const isLegacyDefault = await this.useLegacyTable();
if (isLegacyDefault) {
@ -558,110 +474,12 @@ export class DiscoverPageObject extends FtrService {
return await this.dataGrid.clickDocSortAsc(field, text);
}
public async clickFieldListItemToggle(field: string) {
await this.testSubjects.moveMouseTo(`field-${field}`);
await this.testSubjects.click(`fieldToggle-${field}`);
}
public async clickFieldListItemAdd(field: string) {
await this.waitUntilSidebarHasLoaded();
// a filter check may make sense here, but it should be properly handled to make
// it work with the _score and _source fields as well
if (await this.isFieldSelected(field)) {
return;
}
if (['_score', '_id', '_index'].includes(field)) {
await this.toggleSidebarSection('meta'); // expand Meta section
}
await this.clickFieldListItemToggle(field);
const isLegacyDefault = await this.useLegacyTable();
if (isLegacyDefault) {
await this.retry.waitFor(`field ${field} to be added to classic table`, async () => {
return await this.testSubjects.exists(`docTableHeader-${field}`);
});
} else {
await this.retry.waitFor(`field ${field} to be added to new table`, async () => {
return await this.testSubjects.exists(`dataGridHeaderCell-${field}`);
});
}
}
public async isAdHocDataViewSelected() {
const dataView = await this.getCurrentlySelectedDataView();
await this.testSubjects.click('discover-dataView-switch-link');
return this.testSubjects.exists(`dataViewItemTempBadge-${dataView}`);
}
public async isFieldSelected(field: string) {
if (!(await this.testSubjects.exists('fieldListGroupedSelectedFields'))) {
return false;
}
const selectedList = await this.testSubjects.find('fieldListGroupedSelectedFields');
return await this.testSubjects.descendantExists(`field-${field}`, selectedList);
}
public async clickFieldListItemRemove(field: string) {
await this.waitUntilSidebarHasLoaded();
if (
!(await this.testSubjects.exists('fieldListGroupedSelectedFields')) ||
!(await this.isFieldSelected(field))
) {
return;
}
await this.clickFieldListItemToggle(field);
}
public async clickFieldListItemVisualize(fieldName: string) {
await this.waitUntilSidebarHasLoaded();
const field = await this.testSubjects.find(`field-${fieldName}-showDetails`);
const isActive = await field.elementHasClass('kbnFieldButton-isActive');
if (!isActive) {
// expand the field to show the "Visualize" button
await field.click();
}
await this.waitUntilFieldPopoverIsOpen();
const visualizeButtonTestSubject = `fieldVisualize-${fieldName}`;
// wrap visualize button click in retry to ensure button is clicked and retry if button click is not registered
await this.retry.try(async () => {
await this.testSubjects.click(visualizeButtonTestSubject);
await this.testSubjects.waitForDeleted(visualizeButtonTestSubject);
await this.testSubjects.missingOrFail(visualizeButtonTestSubject);
});
await this.header.waitUntilLoadingHasFinished();
}
public async expectFieldListItemVisualize(field: string) {
await this.testSubjects.existOrFail(`fieldVisualize-${field}`);
}
public async expectMissingFieldListItemVisualize(field: string) {
await this.testSubjects.missingOrFail(`fieldVisualize-${field}`);
}
public async clickFieldListPlusFilter(field: string, value: string) {
const plusFilterTestSubj = `plus-${field}-${value}`;
if (!(await this.testSubjects.exists(plusFilterTestSubj))) {
// field has to be open
await this.clickFieldListItem(field);
}
// this.testSubjects.find doesn't handle spaces in the data-test-subj value
await this.testSubjects.click(plusFilterTestSubj);
await this.header.waitUntilLoadingHasFinished();
}
public async clickFieldListMinusFilter(field: string, value: string) {
// this method requires the field details to be open from clickFieldListItem()
// this.testSubjects.find doesn't handle spaces in the data-test-subj value
await this.testSubjects.click(`minus-${field}-${value}`);
await this.header.waitUntilLoadingHasFinished();
}
public async selectIndexPattern(indexPattern: string) {
await this.testSubjects.click('discover-dataView-switch-link');
await this.find.setValue('[data-test-subj="indexPattern-switcher"] input', indexPattern);
@ -698,19 +516,6 @@ export class DiscoverPageObject extends FtrService {
}
}
public async openSidebarFieldFilter() {
await this.testSubjects.click('fieldListFiltersFieldTypeFilterToggle');
await this.testSubjects.existOrFail('fieldListFiltersFieldTypeFilterOptions');
}
public async closeSidebarFieldFilter() {
await this.testSubjects.click('fieldListFiltersFieldTypeFilterToggle');
await this.retry.waitFor('sidebar filter closed', async () => {
return !(await this.testSubjects.exists('fieldListFiltersFieldTypeFilterOptions'));
});
}
public async waitForChartLoadingComplete(renderCount: number) {
await this.elasticChart.waitForRenderingCount(renderCount, 'unifiedHistogramChart');
}
@ -886,7 +691,7 @@ export class DiscoverPageObject extends FtrService {
* @param fieldName
* */
public async dragFieldToTable(fieldName: string) {
await this.waitUntilSidebarHasLoaded();
await this.unifiedFieldList.waitUntilSidebarHasLoaded();
const from = `dscFieldListPanelField-${fieldName}`;
await this.find.existsByCssSelector(from);

View file

@ -32,6 +32,7 @@ import { LegacyDataTableVisPageObject } from './legacy/data_table_vis';
import { IndexPatternFieldEditorPageObject } from './management/indexpattern_field_editor_page';
import { DashboardPageControls } from './dashboard_page_controls';
import { UnifiedSearchPageObject } from './unified_search_page';
import { UnifiedFieldListPageObject } from './unified_field_list';
import { FilesManagementPageObject } from './files_management';
export const pageObjects = {
@ -61,5 +62,6 @@ export const pageObjects = {
savedObjects: SavedObjectsPageObject,
indexPatternFieldEditorObjects: IndexPatternFieldEditorPageObject,
unifiedSearch: UnifiedSearchPageObject,
unifiedFieldList: UnifiedFieldListPageObject,
filesManagement: FilesManagementPageObject,
};

View file

@ -0,0 +1,254 @@
/*
* 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 { FtrService } from '../ftr_provider_context';
type SidebarSectionName = 'meta' | 'empty' | 'available' | 'unmapped' | 'popular' | 'selected';
export class UnifiedFieldListPageObject extends FtrService {
private readonly retry = this.ctx.getService('retry');
private readonly testSubjects = this.ctx.getService('testSubjects');
private readonly find = this.ctx.getService('find');
private readonly header = this.ctx.getPageObject('header');
private readonly browser = this.ctx.getService('browser');
public async findFieldByName(name: string) {
const fieldSearch = await this.testSubjects.find('fieldListFiltersFieldSearch');
await fieldSearch.type(name);
}
public async clearFieldSearchInput() {
const fieldSearch = await this.testSubjects.find('fieldListFiltersFieldSearch');
await fieldSearch.clearValue();
}
public async getAllFieldNames() {
const sidebar = await this.testSubjects.find('fieldListGroupedFieldGroups');
const $ = await sidebar.parseDomContent();
return $('.kbnFieldButton__name')
.toArray()
.map((field) => $(field).text());
}
public async getSidebarAriaDescription(): Promise<string> {
return await (
await this.testSubjects.find('fieldListGrouped__ariaDescription')
).getAttribute('innerText');
}
public async cleanSidebarLocalStorage(): Promise<void> {
await this.browser.setLocalStorageItem('discover.unifiedFieldList.initiallyOpenSections', '{}');
}
public async waitUntilSidebarHasLoaded() {
await this.retry.waitFor('sidebar is loaded', async () => {
return (await this.getSidebarAriaDescription()).length > 0;
});
}
public async doesSidebarShowFields() {
return await this.testSubjects.exists('fieldListGroupedFieldGroups');
}
public getSidebarSectionSelector(
sectionName: SidebarSectionName,
asCSSSelector: boolean = false
) {
const testSubj = `fieldListGrouped${sectionName[0].toUpperCase()}${sectionName.substring(
1
)}Fields`;
if (!asCSSSelector) {
return testSubj;
}
return `[data-test-subj="${testSubj}"]`;
}
public async getSidebarSectionFieldNames(sectionName: SidebarSectionName): Promise<string[]> {
const elements = await this.find.allByCssSelector(
`${this.getSidebarSectionSelector(sectionName, true)} li`
);
if (!elements?.length) {
return [];
}
return Promise.all(
elements.map(async (element) => await element.getAttribute('data-attr-field'))
);
}
public async toggleSidebarSection(sectionName: SidebarSectionName) {
return await this.find.clickByCssSelector(
`${this.getSidebarSectionSelector(sectionName, true)} .euiAccordion__iconButton`
);
}
public async waitUntilFieldPopoverIsOpen() {
await this.retry.waitFor('popover is open', async () => {
return Boolean(await this.find.byCssSelector('[data-popover-open="true"]'));
});
}
public async clickFieldListItem(field: string) {
await this.testSubjects.click(`field-${field}`);
await this.waitUntilFieldPopoverIsOpen();
}
public async clickFieldListItemToggle(field: string) {
await this.testSubjects.moveMouseTo(`field-${field}`);
await this.testSubjects.click(`fieldToggle-${field}`);
}
public async clickFieldListItemAdd(field: string) {
await this.waitUntilSidebarHasLoaded();
// a filter check may make sense here, but it should be properly handled to make
// it work with the _score and _source fields as well
if (await this.isFieldSelected(field)) {
return;
}
if (['_score', '_id', '_index'].includes(field)) {
await this.toggleSidebarSection('meta'); // expand Meta section
}
await this.clickFieldListItemToggle(field);
await this.retry.waitFor('field is selected', async () => {
return await this.isFieldSelected(field);
});
}
public async isFieldSelected(field: string) {
if (!(await this.testSubjects.exists('fieldListGroupedSelectedFields'))) {
return false;
}
const selectedList = await this.testSubjects.find('fieldListGroupedSelectedFields');
return await this.testSubjects.descendantExists(`field-${field}`, selectedList);
}
public async clickFieldListItemRemove(field: string) {
await this.waitUntilSidebarHasLoaded();
if (
!(await this.testSubjects.exists('fieldListGroupedSelectedFields')) ||
!(await this.isFieldSelected(field))
) {
return;
}
await this.clickFieldListItemToggle(field);
}
public async clickFieldListItemVisualize(fieldName: string) {
await this.waitUntilSidebarHasLoaded();
const field = await this.testSubjects.find(`field-${fieldName}-showDetails`);
const isActive = await field.elementHasClass('kbnFieldButton-isActive');
if (!isActive) {
// expand the field to show the "Visualize" button
await field.click();
}
await this.waitUntilFieldPopoverIsOpen();
const visualizeButtonTestSubject = `fieldVisualize-${fieldName}`;
// wrap visualize button click in retry to ensure button is clicked and retry if button click is not registered
await this.retry.try(async () => {
await this.testSubjects.click(visualizeButtonTestSubject);
await this.testSubjects.waitForDeleted(visualizeButtonTestSubject);
await this.testSubjects.missingOrFail(visualizeButtonTestSubject);
});
await this.header.waitUntilLoadingHasFinished();
}
public async expectFieldListItemVisualize(field: string) {
await this.testSubjects.existOrFail(`fieldVisualize-${field}`);
}
public async expectMissingFieldListItemVisualize(field: string) {
await this.testSubjects.missingOrFail(`fieldVisualize-${field}`);
}
public async clickFieldListPlusFilter(field: string, value: string) {
const plusFilterTestSubj = `plus-${field}-${value}`;
if (!(await this.testSubjects.exists(plusFilterTestSubj))) {
// field has to be open
await this.clickFieldListItem(field);
}
// this.testSubjects.find doesn't handle spaces in the data-test-subj value
await this.testSubjects.click(plusFilterTestSubj);
await this.header.waitUntilLoadingHasFinished();
}
public async clickFieldListMinusFilter(field: string, value: string) {
// this method requires the field details to be open from clickFieldListItem()
// this.testSubjects.find doesn't handle spaces in the data-test-subj value
await this.testSubjects.click(`minus-${field}-${value}`);
await this.header.waitUntilLoadingHasFinished();
}
public async openSidebarFieldFilter() {
await this.testSubjects.click('fieldListFiltersFieldTypeFilterToggle');
await this.testSubjects.existOrFail('fieldListFiltersFieldTypeFilterOptions');
}
public async closeSidebarFieldFilter() {
await this.testSubjects.click('fieldListFiltersFieldTypeFilterToggle');
await this.retry.waitFor('sidebar filter closed', async () => {
return !(await this.testSubjects.exists('fieldListFiltersFieldTypeFilterOptions'));
});
}
public async getFieldStatsViewType(): Promise<
| 'topValuesAndDistribution'
| 'histogram'
| 'topValues'
| 'timeDistribution'
| 'exampleValues'
| 'unknown'
> {
if (await this.testSubjects.exists('unifiedFieldStats-buttonGroup')) {
return 'topValuesAndDistribution';
}
if (await this.testSubjects.exists('unifiedFieldStats-timeDistribution')) {
return 'timeDistribution';
}
if (await this.testSubjects.exists('unifiedFieldStats-histogram')) {
return 'histogram';
}
if (await this.testSubjects.exists('unifiedFieldStats-topValueBuckets')) {
return 'topValues';
}
if (await this.testSubjects.exists('unifiedFieldStats-exampleValueBuckets')) {
return 'exampleValues';
}
return 'unknown';
}
public async getFieldStatsDocsCount() {
return parseInt(
(
await this.testSubjects.getVisibleText('unifiedFieldStats-statsFooter-docsCount')
).replaceAll(',', ''),
10
);
}
public async getFieldStatsTopValueBucketsVisibleText() {
return await this.testSubjects.getVisibleText('unifiedFieldStats-topValueBuckets');
}
public async getFieldStatsExampleBucketsVisibleText() {
return await this.testSubjects.getVisibleText('unifiedFieldStats-exampleValueBuckets');
}
}

View file

@ -19,6 +19,7 @@ export class DashboardVisualizationsService extends FtrService {
private readonly header = this.ctx.getPageObject('header');
private readonly discover = this.ctx.getPageObject('discover');
private readonly timePicker = this.ctx.getPageObject('timePicker');
private readonly unifiedFieldList = this.ctx.getPageObject('unifiedFieldList');
async createAndAddTSVBVisualization(name: string) {
this.log.debug(`createAndAddTSVBVisualization(${name})`);
@ -54,7 +55,7 @@ export class DashboardVisualizationsService extends FtrService {
if (fields) {
for (let i = 0; i < fields.length; i++) {
await this.discover.clickFieldListItemAdd(fields[i]);
await this.unifiedFieldList.clickFieldListItemAdd(fields[i]);
}
}

View file

@ -10,7 +10,14 @@ import expect from '@kbn/expect';
import { PluginFunctionalProviderContext } from '../../services';
export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) {
const PageObjects = getPageObjects(['common', 'header', 'dashboard', 'discover', 'timePicker']);
const PageObjects = getPageObjects([
'common',
'header',
'dashboard',
'discover',
'timePicker',
'unifiedFieldList',
]);
const filterBar = getService('filterBar');
const testSubjects = getService('testSubjects');
const toasts = getService('toasts');
@ -54,7 +61,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
});
it('Starts a new session on sort', async () => {
await PageObjects.discover.clickFieldListItemAdd('speaker');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('speaker');
await PageObjects.discover.clickFieldSort('speaker', 'Sort A-Z');
await PageObjects.header.waitUntilLoadingHasFinished();
const sessionIds = await getSessionIds();

View file

@ -1390,6 +1390,8 @@
"@kbn/ui-shared-deps-src/*": ["packages/kbn-ui-shared-deps-src/*"],
"@kbn/ui-theme": ["packages/kbn-ui-theme"],
"@kbn/ui-theme/*": ["packages/kbn-ui-theme/*"],
"@kbn/unified-field-list-examples-plugin": ["examples/unified_field_list_examples"],
"@kbn/unified-field-list-examples-plugin/*": ["examples/unified_field_list_examples/*"],
"@kbn/unified-field-list-plugin": ["src/plugins/unified_field_list"],
"@kbn/unified-field-list-plugin/*": ["src/plugins/unified_field_list/*"],
"@kbn/unified-histogram-plugin": ["src/plugins/unified_histogram"],

View file

@ -27,6 +27,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
'share',
'spaceSelector',
'header',
'unifiedFieldList',
]);
const testSubjects = getService('testSubjects');
const appsMenu = getService('appsMenu');
@ -234,8 +235,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await PageObjects.common.waitForTopNavToBeVisible();
await PageObjects.discover.selectIndexPattern('logstash-*');
await setDiscoverTimeRange();
await PageObjects.discover.clickFieldListItem('bytes');
await PageObjects.discover.expectMissingFieldListItemVisualize('bytes');
await PageObjects.unifiedFieldList.clickFieldListItem('bytes');
await PageObjects.unifiedFieldList.expectMissingFieldListItemVisualize('bytes');
});
it(`Permalinks doesn't show create short-url button`, async () => {
@ -326,8 +327,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await PageObjects.common.navigateToApp('discover');
await PageObjects.common.waitForTopNavToBeVisible();
await setDiscoverTimeRange();
await PageObjects.discover.clickFieldListItem('bytes');
await PageObjects.discover.expectMissingFieldListItemVisualize('bytes');
await PageObjects.unifiedFieldList.clickFieldListItem('bytes');
await PageObjects.unifiedFieldList.expectMissingFieldListItemVisualize('bytes');
});
it('Permalinks shows create short-url button', async () => {
@ -404,8 +405,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await PageObjects.common.navigateToApp('discover');
await PageObjects.common.waitForTopNavToBeVisible();
await setDiscoverTimeRange();
await PageObjects.discover.clickFieldListItem('bytes');
await PageObjects.discover.expectFieldListItemVisualize('bytes');
await PageObjects.unifiedFieldList.clickFieldListItem('bytes');
await PageObjects.unifiedFieldList.expectFieldListItemVisualize('bytes');
});
});

View file

@ -19,6 +19,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
'timePicker',
'security',
'spaceSelector',
'unifiedFieldList',
]);
const testSubjects = getService('testSubjects');
const appsMenu = getService('appsMenu');
@ -84,8 +85,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
basePath: '/s/custom_space',
});
await setDiscoverTimeRange();
await PageObjects.discover.clickFieldListItem('bytes');
await PageObjects.discover.expectFieldListItemVisualize('bytes');
await PageObjects.unifiedFieldList.clickFieldListItem('bytes');
await PageObjects.unifiedFieldList.expectFieldListItemVisualize('bytes');
});
});
@ -154,8 +155,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
basePath: '/s/custom_space',
});
await setDiscoverTimeRange();
await PageObjects.discover.clickFieldListItem('bytes');
await PageObjects.discover.expectMissingFieldListItemVisualize('bytes');
await PageObjects.unifiedFieldList.clickFieldListItem('bytes');
await PageObjects.unifiedFieldList.expectMissingFieldListItemVisualize('bytes');
});
});

View file

@ -26,6 +26,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
'security',
'spaceSelector',
'header',
'unifiedFieldList',
]);
const monacoEditor = getService('monacoEditor');
@ -58,13 +59,13 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
it('shows "visualize" field button', async () => {
await PageObjects.discover.clickFieldListItem('bytes');
await PageObjects.discover.expectFieldListItemVisualize('bytes');
await PageObjects.unifiedFieldList.clickFieldListItem('bytes');
await PageObjects.unifiedFieldList.expectFieldListItemVisualize('bytes');
});
it('visualizes field to Lens and loads fields to the dimesion editor', async () => {
await PageObjects.discover.findFieldByName('bytes');
await PageObjects.discover.clickFieldListItemVisualize('bytes');
await PageObjects.unifiedFieldList.findFieldByName('bytes');
await PageObjects.unifiedFieldList.clickFieldListItemVisualize('bytes');
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.try(async () => {
const dimensions = await testSubjects.findAll('lns-dimensionTrigger');
@ -79,8 +80,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
operation: 'is between',
value: { from: '3500', to: '4000' },
});
await PageObjects.discover.findFieldByName('geo.src');
await PageObjects.discover.clickFieldListItemVisualize('geo.src');
await PageObjects.unifiedFieldList.findFieldByName('geo.src');
await PageObjects.unifiedFieldList.clickFieldListItemVisualize('geo.src');
await PageObjects.header.waitUntilLoadingHasFinished();
expect(await filterBar.hasFilter('bytes', '3,500 to 4,000')).to.be(true);
@ -89,8 +90,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
it('should preserve query in lens', async () => {
await queryBar.setQuery('machine.os : ios');
await queryBar.submitQuery();
await PageObjects.discover.findFieldByName('geo.dest');
await PageObjects.discover.clickFieldListItemVisualize('geo.dest');
await PageObjects.unifiedFieldList.findFieldByName('geo.dest');
await PageObjects.unifiedFieldList.clickFieldListItemVisualize('geo.dest');
await PageObjects.header.waitUntilLoadingHasFinished();
expect(await queryBar.getQueryString()).to.equal('machine.os : ios');

View file

@ -19,6 +19,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'timeToVisualize',
'common',
'discover',
'unifiedFieldList',
]);
const elasticChart = getService('elasticChart');
const fieldEditor = getService('fieldEditor');
@ -218,7 +219,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'_bytes-runtimefield',
`emit(doc["bytes"].value.toString())`
);
await PageObjects.discover.clickFieldListItemToggle('_bytes-runtimefield');
await PageObjects.unifiedFieldList.clickFieldListItemToggle('_bytes-runtimefield');
const newDataViewId = await PageObjects.discover.getCurrentDataViewId();
expect(newDataViewId).not.to.equal(prevDataViewId);
expect(await PageObjects.unifiedSearch.isAdHocDataView()).to.be(true);

View file

@ -9,7 +9,14 @@ import expect from '@kbn/expect';
export default function ({ getService, getPageObjects }) {
const queryBar = getService('queryBar');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'maps', 'timePicker']);
const PageObjects = getPageObjects([
'common',
'discover',
'header',
'maps',
'timePicker',
'unifiedFieldList',
]);
const security = getService('security');
const from = 'Sep 22, 2015 @ 00:00:00.000';
const to = 'Sep 22, 2015 @ 04:00:00.000';
@ -34,7 +41,7 @@ export default function ({ getService, getPageObjects }) {
it('should link geo_shape fields to Maps application', async () => {
await PageObjects.discover.selectIndexPattern('geo_shapes*');
await PageObjects.discover.clickFieldListItemVisualize('geometry');
await PageObjects.unifiedFieldList.clickFieldListItemVisualize('geometry');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.maps.waitForLayersToLoad();
const doesLayerExist = await PageObjects.maps.doesLayerExist('geo_shapes*');
@ -51,7 +58,7 @@ export default function ({ getService, getPageObjects }) {
await queryBar.submitQuery();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.clickFieldListItemVisualize('geo.coordinates');
await PageObjects.unifiedFieldList.clickFieldListItemVisualize('geo.coordinates');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.maps.waitForLayersToLoad();
const doesLayerExist = await PageObjects.maps.doesLayerExist('logstash-*');

View file

@ -10,7 +10,14 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const PageObjects = getPageObjects(['common', 'visualize', 'discover', 'visChart', 'visEditor']);
const PageObjects = getPageObjects([
'common',
'visualize',
'discover',
'visChart',
'visEditor',
'unifiedFieldList',
]);
const kibanaServer = getService('kibanaServer');
const log = getService('log');
@ -28,7 +35,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('appears correctly in discover', async function () {
await PageObjects.common.navigateToApp('discover');
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.discover.clickFieldListItemAdd('histogram-content');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('histogram-content');
const rowData = await PageObjects.discover.getDocTableIndex(1);
expect(rowData).to.contain('"values":[0.3,1,3,4.2,4.8]');
});

View file

@ -29,7 +29,7 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi
const ml = getService('ml');
const toasts = getService('toasts');
const pageObjects = getPageObjects(['discover', 'timePicker']);
const pageObjects = getPageObjects(['discover', 'timePicker', 'unifiedFieldList']);
return {
async clickNextButton() {
@ -1083,7 +1083,7 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi
async assertDiscoverContainField(field: string) {
await pageObjects.discover.isDiscoverAppOnScreen();
await retry.tryForTime(60 * 1000, async () => {
const allFields = await pageObjects.discover.getAllFieldNames();
const allFields = await pageObjects.unifiedFieldList.getAllFieldNames();
if (Array.isArray(allFields)) {
expect(allFields).to.contain(
field,

View file

@ -5527,6 +5527,10 @@
version "0.0.0"
uid ""
"@kbn/unified-field-list-examples-plugin@link:examples/unified_field_list_examples":
version "0.0.0"
uid ""
"@kbn/unified-field-list-plugin@link:src/plugins/unified_field_list":
version "0.0.0"
uid ""