[Log Explorer] Refactor into new design (#165877)

## 📓 Summary

**Main task**
Closes #165309 

**Skipped tests fixes**
Closes #165839 
Closes #165520 
Closes #166494 

This work refactors the UX for browsing integrations and uncategorized
datasets.


fadbf952-5a03-4747-b68a-29ed7e864120

## 💡 Review hints

- To reflect the UI changes, the Dataset selector state machine has been
updated as described by the chart attached.
The main difference consists of new events to navigate between tabs and
a nested history state, to keep both track of the last opened tab and,
for the integration tab, of the latest panel.

<img width="1531" alt="Screenshot 2023-09-07 at 14 26 21"
src="12da24e8-8300-4788-8382-71a4cf9748c8">



## 🧪 Tests suite

With the test updates to match the new UI, the selector tests suite now
is:
```
↳ Observability Log Explorer
   ↳ Dataset Selector
     ↳ as consistent behavior
       ↳ should always display the Integrations and Uncategorized top level tabs
       ↳ should always display the "Show all logs" action
       ↳ when open on the integrations tab
         ↳ should display an error prompt if could not retrieve the integrations
         ↳ should display an empty prompt for no integrations
       ↳ when open on the uncategorized tab
         ↳ should display a loading skeleton while loading uncategorized datasets
         ↳ should display an error prompt if could not retrieve the datasets
         ↳ should display an empty prompt for no uncategorized data streams
     ↳ with installed integrations and uncategorized data streams
       ↳ when open on the integrations tab
         ↳ should display a list of installed integrations
         ↳ should sort the integrations list by the clicked sorting option
         ↳ should filter the integrations list by the typed integration name
         ↳ should display an empty prompt when the search does not match any result
         ↳ should load more integrations by scrolling to the end of the list
         ↳ clicking on integration and moving into the second navigation level
           ↳ should display a list of available datasets
           ↳ should sort the datasets list by the clicked sorting option
           ↳ should filter the datasets list by the typed dataset name
           ↳ should update the current selection with the clicked dataset
       ↳ when open on the uncategorized tab
         ↳ should display a list of available datasets
         ↳ should sort the datasets list by the clicked sorting option
         ↳ should filter the datasets list by the typed dataset name
         ↳ should update the current selection with the clicked dataset
       ↳ when open/close the selector
         ↳ should restore the latest navigation panel
         ↳ should restore the latest search results
       ↳ when switching between tabs or integration panels
         ↳ should remember the latest search and restore its results
```

---------

Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co>
This commit is contained in:
Marco Antonio Ghiani 2023-09-21 11:26:19 +02:00 committed by GitHub
parent 07b31f1d49
commit 3709e772b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 985 additions and 824 deletions

View file

@ -75,8 +75,8 @@ export class Dataset {
public static createAllLogsDataset() {
return new Dataset({
name: 'logs-*-*' as IndexPattern,
title: 'All log datasets',
iconType: 'editorChecklist',
title: 'All logs',
iconType: 'pagesSelect',
});
}

View file

@ -8,17 +8,16 @@
import { i18n } from '@kbn/i18n';
export const POPOVER_ID = 'dataset-selector-popover';
export const INTEGRATION_PANEL_ID = 'integrations_panel';
export const UNMANAGED_STREAMS_PANEL_ID = 'unmanaged_streams_panel';
export const INTEGRATIONS_PANEL_ID = 'dataset-selector-integrations-panel';
export const INTEGRATIONS_TAB_ID = 'dataset-selector-integrations-tab';
export const UNCATEGORIZED_PANEL_ID = 'dataset-selector-uncategorized-panel';
export const UNCATEGORIZED_TAB_ID = 'dataset-selector-uncategorized-tab';
export const DATA_VIEW_POPOVER_CONTENT_WIDTH = 300;
export const contextMenuStyles = { maxHeight: 440 };
export const selectDatasetLabel = i18n.translate(
'xpack.logExplorer.datasetSelector.selectDataset',
{ defaultMessage: 'Select dataset' }
);
export const showAllLogsLabel = i18n.translate('xpack.logExplorer.datasetSelector.showAllLogs', {
defaultMessage: 'Show all logs',
});
export const integrationsLabel = i18n.translate('xpack.logExplorer.datasetSelector.integrations', {
defaultMessage: 'Integrations',

View file

@ -5,37 +5,30 @@
* 2.0.
*/
import { EuiContextMenu, EuiHorizontalRule } from '@elastic/eui';
import React, { useMemo } from 'react';
import styled from '@emotion/styled';
import { EuiContextMenu, EuiHorizontalRule, EuiTab, EuiTabs } from '@elastic/eui';
import { useIntersectionRef } from '../../hooks/use_intersection_ref';
import { dynamic } from '../../utils/dynamic';
import {
contextMenuStyles,
DATA_VIEW_POPOVER_CONTENT_WIDTH,
integrationsLabel,
INTEGRATION_PANEL_ID,
INTEGRATIONS_PANEL_ID,
INTEGRATIONS_TAB_ID,
uncategorizedLabel,
UNMANAGED_STREAMS_PANEL_ID,
UNCATEGORIZED_PANEL_ID,
UNCATEGORIZED_TAB_ID,
} from './constants';
import { useDatasetSelector } from './state_machine/use_dataset_selector';
import { DatasetsPopover } from './sub_components/datasets_popover';
import { DatasetSkeleton } from './sub_components/datasets_skeleton';
import { SearchControls } from './sub_components/search_controls';
import { SelectorActions } from './sub_components/selector_actions';
import { DatasetSelectorProps } from './types';
import {
buildIntegrationsTree,
createAllLogDatasetsItem,
createUnmanagedDatasetsItem,
createIntegrationStatusItem,
createUncategorizedStatusItem,
} from './utils';
/**
* Lazy load hidden components
*/
const DatasetsList = dynamic(() => import('./sub_components/datasets_list'), {
fallback: <DatasetSkeleton />,
});
export function DatasetSelector({
datasets,
datasetsError,
@ -44,6 +37,7 @@ export function DatasetSelector({
integrationsError,
isLoadingIntegrations,
isLoadingStreams,
isSearchingIntegrations,
onIntegrationsLoadMore,
onIntegrationsReload,
onIntegrationsSearch,
@ -57,16 +51,20 @@ export function DatasetSelector({
onUnmanagedStreamsSort,
}: DatasetSelectorProps) {
const {
isOpen,
panelId,
search,
closePopover,
tabId,
isOpen,
isAllMode,
changePanel,
closePopover,
scrollToIntegrationsBottom,
searchByName,
selectAllLogDataset,
selectDataset,
sortByOrder,
switchToIntegrationsTab,
switchToUncategorizedTab,
togglePopover,
} = useDatasetSelector({
initialContext: { selection: datasetSelection },
@ -85,17 +83,13 @@ export function DatasetSelector({
const [setSpyRef] = useIntersectionRef({ onIntersecting: scrollToIntegrationsBottom });
const { items: integrationItems, panels: integrationPanels } = useMemo(() => {
const allLogDatasetsItem = createAllLogDatasetsItem({ onClick: selectAllLogDataset });
const unmanagedDatasetsItem = createUnmanagedDatasetsItem({ onClick: onStreamsEntryClick });
if (!integrations || integrations.length === 0) {
return {
items: [
allLogDatasetsItem,
unmanagedDatasetsItem,
createIntegrationStatusItem({
data: integrations,
error: integrationsError,
integrations,
isLoading: isLoadingIntegrations,
onRetry: onIntegrationsReload,
}),
],
@ -103,50 +97,67 @@ export function DatasetSelector({
};
}
const { items, panels } = buildIntegrationsTree({
return buildIntegrationsTree({
integrations,
onDatasetSelected: selectDataset,
spyRef: setSpyRef,
});
return {
items: [allLogDatasetsItem, unmanagedDatasetsItem, ...items],
panels,
};
}, [
integrations,
integrationsError,
selectAllLogDataset,
isLoadingIntegrations,
selectDataset,
onIntegrationsReload,
onStreamsEntryClick,
setSpyRef,
]);
const panels = [
const uncategorizedItems = useMemo(() => {
if (!datasets || datasets.length === 0) {
return [
createUncategorizedStatusItem({
data: datasets,
error: datasetsError,
isLoading: isLoadingStreams,
onRetry: onUnmanagedStreamsReload,
}),
];
}
return datasets.map((dataset) => ({
name: dataset.title,
onClick: () => selectDataset(dataset),
}));
}, [datasets, datasetsError, isLoadingStreams, selectDataset, onUnmanagedStreamsReload]);
const tabs = [
{
id: INTEGRATION_PANEL_ID,
title: integrationsLabel,
width: DATA_VIEW_POPOVER_CONTENT_WIDTH,
items: integrationItems,
id: INTEGRATIONS_TAB_ID,
name: integrationsLabel,
onClick: switchToIntegrationsTab,
'data-test-subj': 'datasetSelectorIntegrationsTab',
},
{
id: UNMANAGED_STREAMS_PANEL_ID,
title: uncategorizedLabel,
width: DATA_VIEW_POPOVER_CONTENT_WIDTH,
content: (
<DatasetsList
datasets={datasets}
error={datasetsError}
isLoading={isLoadingStreams}
onRetry={onUnmanagedStreamsReload}
onDatasetClick={selectDataset}
/>
),
id: UNCATEGORIZED_TAB_ID,
name: uncategorizedLabel,
onClick: () => {
onStreamsEntryClick(); // Lazy-load uncategorized datasets only when accessing the Uncategorized tab
switchToUncategorizedTab();
},
'data-test-subj': 'datasetSelectorUncategorizedTab',
},
...integrationPanels,
];
const tabEntries = tabs.map((tab) => (
<EuiTab
key={tab.id}
onClick={tab.onClick}
isSelected={tab.id === tabId}
data-test-subj={tab['data-test-subj']}
>
{tab.name}
</EuiTab>
));
return (
<DatasetsPopover
selection={datasetSelection.selection}
@ -154,23 +165,65 @@ export function DatasetSelector({
closePopover={closePopover}
onClick={togglePopover}
>
<Tabs>{tabEntries}</Tabs>
<SelectorActions>
<SelectorActions.ShowAllLogs isSelected={isAllMode} onClick={selectAllLogDataset} />
</SelectorActions>
<EuiHorizontalRule margin="none" />
<SearchControls
key={panelId}
search={search}
onSearch={searchByName}
onSort={sortByOrder}
isLoading={isLoadingIntegrations || isLoadingStreams}
isLoading={isSearchingIntegrations || isLoadingStreams}
/>
<EuiHorizontalRule margin="none" />
<EuiContextMenu
{/* For a smoother user experience, we keep each tab content mount and we only show the select one
"hiding" all the others. Unmounting mounting each tab content on change makes it feel glitchy,
while the tradeoff of keeping the contents in memory provide a better UX. */}
{/* Integrations tab content */}
<ContextMenu
hidden={tabId !== INTEGRATIONS_TAB_ID}
initialPanelId={panelId}
panels={panels}
panels={[
{
id: INTEGRATIONS_PANEL_ID,
title: integrationsLabel,
width: DATA_VIEW_POPOVER_CONTENT_WIDTH,
items: integrationItems,
},
...integrationPanels,
]}
onPanelChange={changePanel}
className="eui-yScroll"
css={contextMenuStyles}
data-test-subj="datasetSelectorContextMenu"
data-test-subj="integrationsContextMenu"
size="s"
/>
{/* Uncategorized tab content */}
<ContextMenu
hidden={tabId !== UNCATEGORIZED_TAB_ID}
initialPanelId={UNCATEGORIZED_PANEL_ID}
panels={[
{
id: UNCATEGORIZED_PANEL_ID,
title: uncategorizedLabel,
width: DATA_VIEW_POPOVER_CONTENT_WIDTH,
items: uncategorizedItems,
},
]}
className="eui-yScroll"
data-test-subj="uncategorizedContextMenu"
size="s"
/>
</DatasetsPopover>
);
}
const Tabs = styled(EuiTabs)`
padding: 0 8px;
`;
const ContextMenu = styled(EuiContextMenu)`
max-height: 440px;
transition: none !important;
`;

View file

@ -7,7 +7,7 @@
import { AllDatasetSelection } from '../../../../common/dataset_selection';
import { HashedCache } from '../../../../common/hashed_cache';
import { INTEGRATION_PANEL_ID } from '../constants';
import { INTEGRATIONS_PANEL_ID, INTEGRATIONS_TAB_ID } from '../constants';
import { DatasetsSelectorSearchParams } from '../types';
import { DefaultDatasetsSelectorContext } from './types';
@ -19,6 +19,7 @@ export const defaultSearch: DatasetsSelectorSearchParams = {
export const DEFAULT_CONTEXT: DefaultDatasetsSelectorContext = {
selection: AllDatasetSelection.create(),
searchCache: new HashedCache(),
panelId: INTEGRATION_PANEL_ID,
panelId: INTEGRATIONS_PANEL_ID,
tabId: INTEGRATIONS_TAB_ID,
search: defaultSearch,
};

View file

@ -7,7 +7,7 @@
import { actions, assign, createMachine, raise } from 'xstate';
import { AllDatasetSelection, SingleDatasetSelection } from '../../../../common/dataset_selection';
import { UNMANAGED_STREAMS_PANEL_ID } from '../constants';
import { INTEGRATIONS_TAB_ID, UNCATEGORIZED_TAB_ID } from '../constants';
import { defaultSearch, DEFAULT_CONTEXT } from './defaults';
import {
DatasetsSelectorContext,
@ -20,7 +20,7 @@ import {
export const createPureDatasetsSelectorStateMachine = (
initialContext: Partial<DefaultDatasetsSelectorContext> = DEFAULT_CONTEXT
) =>
/** @xstate-layout N4IgpgJg5mDOIC5QBECGAXVsztgZTABswBjdAewCcA6AB3PoDcwaTDzsIBiAFQHkA4gIAyAUQDaABgC6iUPVgBLdIvIA7OSAAeiALQBmAKzUALAEZDAdgBslk4ckAmEwA5HhkwBoQAT0T6LakdHFzMLMLN9dzsAXxjvNExsXAJiMio6BnJmGgYwNS4AYWE+PAkZTQVlVQ0kbUQzSQBOY0szd31zE0lrJutrbz8EVxdTM0solya3YP64hIwsHHwiUgoaeiYWajyC-iExKVk6qpV1TR0ERr7qa0cwx0lJE0tLF0l9QcQTbtveyXa1gB+j6LnmIESSxSq3SGyyOR2tHy1EIilgKjUUAAkmp0GAoJQMDVYEUABIAQQAcgJRAB9AAKVNEwiOlQ41XOdUu+iit0MhjMtgsnQFjgGvkQTQBQV6YXMhncj0c4MhyRWaXWmS2uSRahRaIx2Nx+MJZzUJMKFOpdMZlOZ4jMx3k7LNF0QhiBpn6Hh+7kMTU+EoQ70s1EkHmBjiM+ksTWV8QhizVqTWGU22W2u316MUmJxeIJRPUJLwhQASnxhMJafxaVjKTxRAIy+SeFi+JS8LSAEJ8Hj8ACyrJOLpqboQujMJn01Bad2e7m6Fi8QcsYuoCpMTXGTl9thVSeWKdhWozOuRqJzeeNhbNJdE5LLlp7AE1aZTyQPyk6QKcx1y9DMJomlnBxHDjacWhcCZLC+YN3GoSwekkN4106awpgPJIjxhTV0wRLNL0NfMTSLc0uDwPgyx4V9aSo5BRDLYdnSUV0AInMJjHuCYxSaJDgiQww4MaZ5Z3MZxXHcFwFUsLCoXVVM4W1RELwNXMjQLU1iQo5lREKGjySrWkSgELtkFbckyh4Zjf1HTlQEuRobDDGwjG3cwzCmcUhhcEEgj6R4XGsF5DDk5NcLTeFM11bNiJvLT1DwdBKDAVAAFsLStGkGSZFkKhHVj-wc-wFWoRoRRE-QAUMFw4N6UNrE8qwWgmHkarCnCNUi5TCLU69NLIpKUvS+9H2fbs3w-L8bL-ez6gQBwTFMKMozXUIPSqoSgy3JbBOA-R-iicZQoTVVOsU08CJioj1JI28aiG1KMooqiaImuiywYpj8pYjlamKhBYyW7pmmeMIWhMZw4J2xDkMkKYtzCeGOuhLqlLPFS9Ru-rSLNR6Rp0sR9NpcyeEs0RrJ+2zCrmxz4eMH4mheYCXmeWqgwwyRqCCtdGtsD0ejBU7D1Ri78Oi1SrygABVNQ0tQNRUBgCB8eey0qWy217Sp2b-vmzpHGoEE7n5qMQ1goNGnMI2mYO6YpJklGFJPcXzyxvqZblhWlcgVXRqfUlaKm782RpvXLhko2nPpxwkMFQMhk44wHHCaN-T4kwnePPCord2L1Nl+XFeVv2Xuo2j6MYma7PDxA3FGPjt1trcmYmYSLGT6rIkMNyM6ziL0auyXDUL72S+Sp77yJmjSfJymf118cnOsMNIZMDC1wcBUtsT54QKlF4tysRH1-7tHqGwDUagv9TiEJvSDKMkyzIsqzq7D8cMKCbpIOcRqgpQnBAUG43BAm6Hbe4LghYLGwqLE8l81jXyUJiO+ZRp4k1fhTd+f1xy6D-rDDwrVRS2G8noA6MpBRAjXNuAB8YYHyWzhkBBZBr6oEIIQe+xNZ5vx1jXcc25uYBACL5VwUppgJz0K8bm1gapRgBFOeucQExqHIBAOAmgzpwPWKHHB7EDDTlAk4CCIIap2Dgng0qQVZEfGnAEV40DEywOdjnbUOi2IAzwS0Qx4FnAmOgiuIYAlEIhGXLxHunQz5i1ztQNgHBIBuKKvNSIRhubTFavYSIHw4IxlDN0f+bwarhkaNYSJLtom7ASbTPQzhQwtCMb4qCMFoY9GkeBKc0xXABBKcLJxjDB4Sz1AACwNJU2uE4lpPHlL5HofRmqOGhkzI2dxtwHQcHYbcpSXEY16lLO6CVzSjKXlzIw9gpRVTuCCdmPlgE2G3FOCwionCbO6ts66Hs9mDQniNQ57F-SG3kSEIK3EXD2DqlORCnk2gRBsSEZ5-S87Y09kXH2KsvkZR+QDGqS0oFM39HYFOUxhJPC5j-dZx93LdPoeFc+zD3HU10R48Cu1wwvDaoCGwATAIyKWbIwUrwQawp6QwgeF9cJINvmADF81dC2EQrGEFTgeRFLaHBN4phtz+k2h6DCvk4WiqvuoagbDCBSu5NymMwV3BOCsDGTlwYubAQsAGcM2qoH6CUTEIAA */
/** @xstate-layout N4IgpgJg5mDOIC5QBECGAXVsztgZTABswBjdAewCcA6AB3PoDcwaTDzsIBiAFQHkA4gIAyAUQDaABgC6iUPVgBLdIvIA7OSAAeiAJwBmXdUkAWSZIAcAJgBs+gIy6A7PpsAaEAE9E+q-eoW+k5OVuY24ZJONgC+0R5omNi4BMRkVHQM5Mw0DGBqXADCwnx4EjKaCsqqGkjaen7UAKySjVYONoEmFk66Ht4IjU4m1B36Y4G6k7omVrHxGFg4+ESkFDT0TCzUufn8QmJSsrWVKuqaOgi69o3UTpZWznaSuoONfYitN5K29k6tNj1nvY5iAEotkis0utMtltrQ8lxSmICjwAPoAQWEwlRxQEeFRyHRPHRpR4hwqHCqZ1qFws3Wo+kkNkaFnsJhs90a+neCBs7OoVgeJl0NnsdmuJhBYKSy1Sawymxy8LU1EUanQYCglAw1VgPFQACNEQB1ACSPAKAAlUfxUQBVAByBSJogEfAASqaAFqiZA29EAIXJx0ppxqoAu9meDMawqszUsjRFvS8iHsUf8kRMUVCjXsrn0jSlCxlKVW6Q2WS2O1V6s12rDesN1EIilgKjUUFNda1OvUsEKlvRDoEolRAAVh6JhMH5KHquc09HbPGuq4bLoLEyLDy-v4XoY41FvvYLMXEksy1CFVWlXlaxre439QaW22O12ew3dYiCu6+FiNp8KipoOjwrrukSpp8A6+IBnwPD8AAsrOIAnAuNKIAAtIKNyDPoJgOOyhanim-SDFYIxXAY+F2H4MRxKCJaXpC8qVrCNZqo+379i+b7tmqn7cX2agDqU6LulaqIBgAmqiDrokhZRHHOShhouCBYYRXw2H4FgmAmQQGSYPJ8kY8YdFyTgWACTjAox0osXKFYwtWyoPvWIlNq+rYCZ23bCY2iIemismoh6yCiO6qHodSEbYQR+hNFY7IhD0fgSjyFiNHh3yRCKorOEE57grK5bQoqcL3lxnnPs2vkfgFtXVHg6CUGAqAALYDlaw6jhOU4zuUIZqRh8UIFG5m6bGgThJuTJvKmCB3Jmop-FZoRBAx8wXhCzkVbeVUqjVT66nxDWCU1p3qK17VdWJogSVJYUKUpMXznFdQDKeIzNPo+khLZrQ8jl5nXARG5BNMhglaWrEuZVnFfl553vpdyNhrdHXdcF7qhXJEVRe9o2fRcbROMYFiTA8jgivpVgg601DTOmfhtHSfKzA5zF7eVN4ce5J08aJqN+UJzU3W12MPciaKEsSpLE1S4ZfUm-ispMfJjJIjLTLu-3UIRALfM06a6dtTG7WV17sW594AK5qCQGCalQigAF6QC+JrmlJtqgeBAiQTw0Gwf6QbDapysaVpki3ARYRRi4KV8u4S1RgZxhCs03QuACsNOXztt3iqjvO4+buexA3viZJ1ovYpykUiTKsXDhQSG40zIhOTOV0Ty9hWNZ1AZ78035nmBe8zbrkl9QZcu1Alde4auP4+F7qRdFkdoR9rfYYKRjMqegLZmKXI7unzw3FYVMvDZOX5n8U-W2xs9HfPTuL8v1er0iogogJESEkogyQ71ivvBAVM46xlGBMW+LQyJphSkYYIhFBQpUFAZF+V55TYDlNUagShOzEERNOABaJMTYlxPieWICwEqV3i3GOjgkqrkwWMOwhE6QDxcMMdkJgJ6xlsOEHB8MaD4NWIQ4hUBSH-0AXQxW4C94xy5HHUwkQOh0QeGnfoYp+TZWzMbVcLIzzcytrg9IkiyCENQIQQgZDZZAIVqApW6lMKaXPgEfSSYuh+GcLGAeHJhgckmCYcJrgZimFiIxNQ5AIBwE0I5aeaxm7Rw8VhXSFMmR6QMuYIyi1+guGZmMKyx5HC-H0GI-a-MWBpPceNLCgwjA5NPHknWQxClplFIbUp-0hiMhSuyapRd35sA4JAepY0vqDyjDGeM0NdA00mDyXw6sgghDCBEKIIyZ6I2VFM0m2EuSrVyYZTpPIUoWBKYWQwIQWSDF+Lst++z7wAAt3yHMgQCa5m54y-EqSlEIqzXC9MLGYO4Nk77PIRodJGgUzqGi+THNcxhdJtPOcZHkgjrnhPTKeawe4+QwoOgLaqGNEWvg+e2ZFHibJotvnYXS24dYMyWkspKXRB4GXFIRHWJLalzyFijeqaN-IUv7LSxpoQbDGCjFTQRllsrxlMkMEeeTrKGE3GfAVxcP7Crqj5MV4trpqCxvdKVX0HiUR1kmEIXRbUslMnSEpAJu4cj+roXV78awLwrpQD2K8DSWouIIgUkgxQihcCyc2TgB6hDjkPQi-1mh2QMlU8xpVLESNYtMiBqjh4aKiDZXwOiB55gppNaG6aWTBAFdYsMRDBLEBDQlYUAoNxUyiDlQI3J05RCSmMQwZsDCuFsPW3N6hqB2MIK2ia1wR6xh6FGHt4MTL9ruMzX48YHhEucDE6IQA */
createMachine<DatasetsSelectorContext, DatasetsSelectorEvent, DatasetsSelectorTypestate>(
{
context: { ...DEFAULT_CONTEXT, ...initialContext },
@ -39,56 +39,73 @@ export const createPureDatasetsSelectorStateMachine = (
},
},
open: {
initial: 'listingIntegrations',
initial: 'integrationsTab',
on: {
CLOSE: 'closed',
TOGGLE: 'closed',
SELECT_ALL_LOGS_DATASET: 'closed',
},
states: {
hist: {
type: 'history',
history: 'deep',
},
listingIntegrations: {
entry: ['storePanelId', 'retrieveSearchFromCache', 'maybeRestoreSearchResult'],
integrationsTab: {
initial: 'listingIntegrations',
entry: ['storeIntegrationsTabId'],
on: {
CHANGE_PANEL: [
{
cond: 'isUnmanagedStreamsId',
target: 'listingUnmanagedStreams',
SWITCH_TO_UNCATEGORIZED_TAB: 'uncategorizedTab',
},
states: {
hist: {
type: 'history',
},
listingIntegrations: {
entry: [
'storePanelId',
'retrieveSearchFromCache',
'maybeRestoreSearchResult',
],
on: {
CHANGE_PANEL: 'listingIntegrationStreams',
SCROLL_TO_INTEGRATIONS_BOTTOM: {
actions: 'loadMoreIntegrations',
},
SEARCH_BY_NAME: {
actions: ['storeSearch', 'searchIntegrations'],
},
SORT_BY_ORDER: {
actions: ['storeSearch', 'sortIntegrations'],
},
},
{
target: 'listingIntegrationStreams',
},
listingIntegrationStreams: {
entry: [
'storePanelId',
'retrieveSearchFromCache',
'maybeRestoreSearchResult',
],
on: {
CHANGE_PANEL: 'listingIntegrations',
SEARCH_BY_NAME: {
actions: ['storeSearch', 'searchIntegrationsStreams'],
},
SORT_BY_ORDER: {
actions: ['storeSearch', 'sortIntegrationsStreams'],
},
SELECT_DATASET: '#closed',
},
],
SCROLL_TO_INTEGRATIONS_BOTTOM: {
actions: 'loadMoreIntegrations',
},
SEARCH_BY_NAME: {
actions: ['storeSearch', 'searchIntegrations'],
},
SORT_BY_ORDER: {
actions: ['storeSearch', 'sortIntegrations'],
},
SELECT_ALL_LOGS_DATASET: '#closed',
},
},
listingIntegrationStreams: {
entry: ['storePanelId', 'retrieveSearchFromCache', 'maybeRestoreSearchResult'],
uncategorizedTab: {
entry: [
'storeUncategorizedTabId',
'retrieveSearchFromCache',
'maybeRestoreSearchResult',
],
on: {
CHANGE_PANEL: 'listingIntegrations',
SEARCH_BY_NAME: {
actions: ['storeSearch', 'searchIntegrationsStreams'],
},
SORT_BY_ORDER: {
actions: ['storeSearch', 'sortIntegrationsStreams'],
},
SELECT_DATASET: '#closed',
},
},
listingUnmanagedStreams: {
entry: ['storePanelId', 'retrieveSearchFromCache', 'maybeRestoreSearchResult'],
on: {
CHANGE_PANEL: 'listingIntegrations',
SWITCH_TO_INTEGRATIONS_TAB: 'integrationsTab.hist',
SEARCH_BY_NAME: {
actions: ['storeSearch', 'searchUnmanagedStreams'],
},
@ -130,12 +147,15 @@ export const createPureDatasetsSelectorStateMachine = (
},
{
actions: {
storeIntegrationsTabId: assign((_context) => ({ tabId: INTEGRATIONS_TAB_ID })),
storeUncategorizedTabId: assign((_context) => ({ tabId: UNCATEGORIZED_TAB_ID })),
storePanelId: assign((_context, event) =>
'panelId' in event ? { panelId: event.panelId } : {}
),
storeSearch: assign((context, event) => {
if ('search' in event) {
context.searchCache.set(context.panelId, event.search);
const id = context.tabId === UNCATEGORIZED_TAB_ID ? context.tabId : context.panelId;
context.searchCache.set(id, event.search);
return {
search: event.search,
@ -149,22 +169,31 @@ export const createPureDatasetsSelectorStateMachine = (
storeSingleSelection: assign((_context, event) =>
'dataset' in event ? { selection: SingleDatasetSelection.create(event.dataset) } : {}
),
retrieveSearchFromCache: assign((context, event) =>
'panelId' in event
? { search: context.searchCache.get(event.panelId) ?? defaultSearch }
: {}
),
retrieveSearchFromCache: assign((context, event) => {
if (event.type === 'CHANGE_PANEL' && 'panelId' in event) {
return { search: context.searchCache.get(event.panelId) ?? defaultSearch };
}
if (event.type === 'SWITCH_TO_INTEGRATIONS_TAB' && 'panelId' in context) {
return { search: context.searchCache.get(context.panelId) ?? defaultSearch };
}
if (event.type === 'SWITCH_TO_UNCATEGORIZED_TAB' && 'tabId' in context) {
return { search: context.searchCache.get(context.tabId) ?? defaultSearch };
}
return {};
}),
maybeRestoreSearchResult: actions.pure((context, event) => {
if (event.type === 'CHANGE_PANEL' && context.searchCache.has(event.panelId)) {
const hasSearchOnChangePanel =
event.type === 'CHANGE_PANEL' && context.searchCache.has(event.panelId);
const hasSearchOnIntegrationsTab =
event.type === 'SWITCH_TO_INTEGRATIONS_TAB' && context.searchCache.has(context.panelId);
const hasSearchOnUncategorizedTab =
event.type === 'SWITCH_TO_UNCATEGORIZED_TAB' && context.searchCache.has(context.tabId);
if (hasSearchOnChangePanel || hasSearchOnIntegrationsTab || hasSearchOnUncategorizedTab) {
return raise({ type: 'SORT_BY_ORDER', search: context.search });
}
}),
},
guards: {
isUnmanagedStreamsId: (_context, event) => {
return 'panelId' in event && event.panelId === UNMANAGED_STREAMS_PANEL_ID;
},
},
}
);

View file

@ -13,12 +13,13 @@ import {
SearchIntegrations,
} from '../../../hooks/use_integrations';
import type { IHashedCache } from '../../../../common/hashed_cache';
import { DatasetsSelectorSearchParams, PanelId } from '../types';
import { DatasetsSelectorSearchParams, PanelId, TabId } from '../types';
export interface DefaultDatasetsSelectorContext {
selection: DatasetSelection;
tabId: TabId;
panelId: PanelId;
searchCache: IHashedCache<PanelId, DatasetsSelectorSearchParams>;
searchCache: IHashedCache<PanelId | TabId, DatasetsSelectorSearchParams>;
search: DatasetsSelectorSearchParams;
}
@ -40,15 +41,19 @@ export type DatasetsSelectorTypestate =
context: DefaultDatasetsSelectorContext;
}
| {
value: 'popover.open.listingIntegrations';
value: 'popover.open.integrationsTab';
context: DefaultDatasetsSelectorContext;
}
| {
value: 'popover.open.listingIntegrationStreams';
value: 'popover.open.integrationsTab.listingIntegrations';
context: DefaultDatasetsSelectorContext;
}
| {
value: 'popover.open.listingUnmanagedStreams';
value: 'popover.open.integrationsTab.listingIntegrationStreams';
context: DefaultDatasetsSelectorContext;
}
| {
value: 'popover.open.uncategorizedTab';
context: DefaultDatasetsSelectorContext;
}
| {
@ -73,6 +78,12 @@ export type DatasetsSelectorEvent =
| {
type: 'TOGGLE';
}
| {
type: 'SWITCH_TO_INTEGRATIONS_TAB';
}
| {
type: 'SWITCH_TO_UNCATEGORIZED_TAB';
}
| {
type: 'CHANGE_PANEL';
panelId: PanelId;

View file

@ -51,6 +51,18 @@ export const useDatasetSelector = ({
const panelId = useSelector(datasetsSelectorStateService, (state) => state.context.panelId);
const search = useSelector(datasetsSelectorStateService, (state) => state.context.search);
const selection = useSelector(datasetsSelectorStateService, (state) => state.context.selection);
const tabId = useSelector(datasetsSelectorStateService, (state) => state.context.tabId);
const switchToIntegrationsTab = useCallback(
() => datasetsSelectorStateService.send({ type: 'SWITCH_TO_INTEGRATIONS_TAB' }),
[datasetsSelectorStateService]
);
const switchToUncategorizedTab = useCallback(
() => datasetsSelectorStateService.send({ type: 'SWITCH_TO_UNCATEGORIZED_TAB' }),
[datasetsSelectorStateService]
);
const changePanel = useCallback<ChangePanelHandler>(
(panelDetails) =>
@ -98,17 +110,23 @@ export const useDatasetSelector = ({
return {
// Data
isOpen,
panelId,
search,
selection,
tabId,
// Flags
isOpen,
isAllMode: selection.selectionType === 'all',
// Actions
closePopover,
changePanel,
closePopover,
scrollToIntegrationsBottom,
searchByName,
selectAllLogDataset,
selectDataset,
sortByOrder,
switchToIntegrationsTab,
switchToUncategorizedTab,
togglePopover,
};
};

View file

@ -8,18 +8,15 @@
import React from 'react';
import {
EuiButton,
EuiHorizontalRule,
EuiIcon,
EuiPanel,
EuiPopover,
EuiPopoverProps,
EuiTitle,
useIsWithinBreakpoints,
} from '@elastic/eui';
import styled from '@emotion/styled';
import { PackageIcon } from '@kbn/fleet-plugin/public';
import { DatasetSelection } from '../../../../common/dataset_selection';
import { DATA_VIEW_POPOVER_CONTENT_WIDTH, POPOVER_ID, selectDatasetLabel } from '../constants';
import { DATA_VIEW_POPOVER_CONTENT_WIDTH, POPOVER_ID } from '../constants';
import { getPopoverButtonStyles } from '../utils';
const panelStyle = { width: DATA_VIEW_POPOVER_CONTENT_WIDTH };
@ -81,17 +78,8 @@ export const DatasetsPopover = ({
css={panelStyle}
data-test-subj="datasetSelectorContent"
>
<Title size="xxs">
<span>{selectDatasetLabel}</span>
</Title>
<EuiHorizontalRule margin="none" />
{children}
</EuiPanel>
</EuiPopover>
);
};
const Title = styled(EuiTitle)`
padding: 12px;
display: block;
`;

View file

@ -1,77 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { EuiButton, EuiEmptyPrompt, EuiText, EuiToolTip } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { Integration } from '../../../../common/datasets';
import { ReloadIntegrations } from '../../../hooks/use_integrations';
import {
errorLabel,
noDataRetryLabel,
noIntegrationsDescriptionLabel,
noIntegrationsLabel,
} from '../constants';
export interface IntegrationsListStatusProps {
integrations: Integration[] | null;
error: Error | null;
onRetry: ReloadIntegrations;
}
export const IntegrationsListStatus = ({
integrations,
error,
onRetry,
}: IntegrationsListStatusProps) => {
const isEmpty = integrations == null || integrations.length <= 0;
const hasError = error !== null;
if (hasError) {
return (
<EuiEmptyPrompt
data-test-subj="integrationsErrorPrompt"
iconType="warning"
iconColor="danger"
paddingSize="m"
title={<h2>{noIntegrationsLabel}</h2>}
titleSize="s"
body={
<FormattedMessage
id="xpack.logExplorer.datasetSelector.noIntegrationsError"
defaultMessage="An {error} occurred while getting your integrations. Please retry."
values={{
error: (
<EuiToolTip content={error.message}>
<EuiText color="danger">{errorLabel}</EuiText>
</EuiToolTip>
),
}}
/>
}
actions={[<EuiButton onClick={onRetry}>{noDataRetryLabel}</EuiButton>]}
/>
);
}
if (isEmpty) {
return (
<EuiEmptyPrompt
iconType="search"
paddingSize="s"
title={<h2>{noIntegrationsLabel}</h2>}
titleSize="s"
body={<p>{noIntegrationsDescriptionLabel}</p>}
/>
);
}
return null;
};
// eslint-disable-next-line import/no-default-export
export default IntegrationsListStatus;

View file

@ -6,35 +6,31 @@
*/
import React from 'react';
import { EuiButton, EuiContextMenuItem, EuiEmptyPrompt, EuiText, EuiToolTip } from '@elastic/eui';
import { EuiButton, EuiEmptyPrompt, EuiText, EuiToolTip } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { ReloadDatasets } from '../../../hooks/use_datasets';
import {
errorLabel,
noDatasetsDescriptionLabel,
noDatasetsLabel,
noDataRetryLabel,
} from '../constants';
import { Dataset } from '../../../../common/datasets';
import { errorLabel, noDataRetryLabel } from '../constants';
import type { Dataset, Integration } from '../../../../common/datasets';
import { DatasetSkeleton } from './datasets_skeleton';
import { DatasetSelectionHandler } from '../types';
interface DatasetListProps {
datasets: Dataset[] | null;
export interface ListStatusProps {
data: Dataset[] | Integration[] | null;
description: string;
error: Error | null;
isLoading: boolean;
onRetry: ReloadDatasets;
onDatasetClick: DatasetSelectionHandler;
title: string;
}
export const DatasetsList = ({
datasets,
export const ListStatus = ({
data,
description,
error,
isLoading,
onRetry,
onDatasetClick,
}: DatasetListProps) => {
const isEmpty = datasets == null || datasets.length <= 0;
title,
}: ListStatusProps) => {
const isEmpty = data == null || data.length <= 0;
const hasError = error !== null;
if (isLoading) {
@ -44,16 +40,16 @@ export const DatasetsList = ({
if (hasError) {
return (
<EuiEmptyPrompt
data-test-subj="datasetErrorPrompt"
data-test-subj="datasetSelectorListStatusErrorPrompt"
iconType="warning"
iconColor="danger"
paddingSize="m"
title={<h2>{noDatasetsLabel}</h2>}
title={<h2>{title}</h2>}
titleSize="s"
body={
<FormattedMessage
id="xpack.logExplorer.datasetSelector.noDatasetsError"
defaultMessage="An {error} occurred while getting your data streams. Please retry."
id="xpack.logExplorer.datasetSelector.noDataError"
defaultMessage="An {error} occurred while getting your data. Please retry."
values={{
error: (
<EuiToolTip content={error.message}>
@ -71,22 +67,18 @@ export const DatasetsList = ({
if (isEmpty) {
return (
<EuiEmptyPrompt
data-test-subj="emptyDatasetPrompt"
data-test-subj="datasetSelectorListStatusEmptyPrompt"
iconType="search"
paddingSize="m"
title={<h2>{noDatasetsLabel}</h2>}
title={<h2>{title}</h2>}
titleSize="s"
body={<p>{noDatasetsDescriptionLabel}</p>}
body={<p>{description}</p>}
/>
);
}
return datasets.map((dataset) => (
<EuiContextMenuItem key={dataset.id} onClick={() => onDatasetClick(dataset)}>
{dataset.title}
</EuiContextMenuItem>
));
return null;
};
// eslint-disable-next-line import/no-default-export
export default DatasetsList;
export default ListStatus;

View file

@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import {
EuiListGroup,
EuiListGroupItem,
EuiListGroupItemProps,
EuiListGroupProps,
} from '@elastic/eui';
import { createAllLogDatasetsItem } from '../utils';
import { showAllLogsLabel } from '../constants';
type DatasetsAllActionProps = EuiListGroupProps;
interface ShowAllLogsProps extends Omit<EuiListGroupItemProps, 'label'> {
isSelected: boolean;
onClick(): void;
}
export const SelectorActions = (props: DatasetsAllActionProps) => {
return <EuiListGroup flush size="s" {...props} />;
};
const ShowAllLogs = ({ isSelected, onClick, ...props }: ShowAllLogsProps) => {
const allLogs = createAllLogDatasetsItem();
return (
<EuiListGroupItem
data-test-subj={allLogs['data-test-subj']}
onClick={onClick}
iconType={isSelected ? 'check' : allLogs.iconType}
label={showAllLogsLabel}
{...props}
/>
);
};
SelectorActions.Action = EuiListGroupItem;
SelectorActions.ShowAllLogs = ShowAllLogs;

View file

@ -15,13 +15,13 @@ import {
ReloadIntegrations,
SearchIntegrations,
} from '../../hooks/use_integrations';
import { INTEGRATION_PANEL_ID, UNMANAGED_STREAMS_PANEL_ID } from './constants';
import { INTEGRATIONS_PANEL_ID, INTEGRATIONS_TAB_ID, UNCATEGORIZED_TAB_ID } from './constants';
export interface DatasetSelectorProps {
/* The generic data stream list */
datasets: Dataset[] | null;
/* Any error occurred to show when the user preview the generic data streams */
datasetsError?: Error | null;
datasetsError: Error | null;
/* The current selection instance */
datasetSelection: DatasetSelection;
/* The integrations list, each integration includes its data streams */
@ -31,6 +31,7 @@ export interface DatasetSelectorProps {
/* Flags for loading/searching integrations or data streams*/
isLoadingIntegrations: boolean;
isLoadingStreams: boolean;
isSearchingIntegrations: boolean;
/* Triggered when we reach the bottom of the integration list and want to load more */
onIntegrationsLoadMore: LoadMoreIntegrations;
/* Triggered when the user reload the list after an error */
@ -50,10 +51,9 @@ export interface DatasetSelectorProps {
onSelectionChange: DatasetSelectionChange;
}
export type PanelId =
| typeof INTEGRATION_PANEL_ID
| typeof UNMANAGED_STREAMS_PANEL_ID
| IntegrationId;
export type PanelId = typeof INTEGRATIONS_PANEL_ID | IntegrationId;
export type TabId = typeof INTEGRATIONS_TAB_ID | typeof UNCATEGORIZED_TAB_ID;
export interface SearchParams {
integrationId?: PanelId;

View file

@ -6,24 +6,18 @@
*/
import React, { RefCallback } from 'react';
import {
EuiContextMenuPanelDescriptor,
EuiContextMenuPanelItemDescriptor,
EuiIcon,
} from '@elastic/eui';
import { EuiContextMenuPanelDescriptor, EuiContextMenuPanelItemDescriptor } from '@elastic/eui';
import { PackageIcon } from '@kbn/fleet-plugin/public';
import { Dataset, Integration } from '../../../common/datasets';
import {
DATA_VIEW_POPOVER_CONTENT_WIDTH,
uncategorizedLabel,
UNMANAGED_STREAMS_PANEL_ID,
noDatasetsDescriptionLabel,
noDatasetsLabel,
noIntegrationsDescriptionLabel,
noIntegrationsLabel,
} from './constants';
import { DatasetSelectionHandler } from './types';
import { LoadDatasets } from '../../hooks/use_datasets';
import { dynamic } from '../../utils/dynamic';
import type { IntegrationsListStatusProps } from './sub_components/integrations_list_status';
const IntegrationsListStatus = dynamic(() => import('./sub_components/integrations_list_status'));
import ListStatus, { ListStatusProps } from './sub_components/list_status';
export const getPopoverButtonStyles = ({ fullWidth }: { fullWidth?: boolean }) => ({
maxWidth: fullWidth ? undefined : DATA_VIEW_POPOVER_CONTENT_WIDTH,
@ -82,30 +76,43 @@ export const buildIntegrationsTree = ({
);
};
export const createAllLogDatasetsItem = ({ onClick }: { onClick(): void }) => {
export const createAllLogDatasetsItem = () => {
const allLogDataset = Dataset.createAllLogsDataset();
return {
'data-test-subj': 'datasetSelectorshowAllLogs',
iconType: allLogDataset.iconType,
name: allLogDataset.title,
'data-test-subj': 'allLogDatasets',
icon: allLogDataset.iconType && <EuiIcon type={allLogDataset.iconType} />,
onClick,
};
};
export const createUnmanagedDatasetsItem = ({ onClick }: { onClick: LoadDatasets }) => {
return {
name: uncategorizedLabel,
'data-test-subj': 'unmanagedDatasets',
icon: <EuiIcon type="documents" />,
onClick,
panel: UNMANAGED_STREAMS_PANEL_ID,
};
};
export const createIntegrationStatusItem = (props: IntegrationsListStatusProps) => {
export const createIntegrationStatusItem = (
props: Omit<ListStatusProps, 'description' | 'title'>
) => {
return {
disabled: true,
name: <IntegrationsListStatus {...props} />,
'data-test-subj': 'integrationStatusItem',
name: (
<ListStatus
key="integrationStatusItem"
description={noIntegrationsDescriptionLabel}
title={noIntegrationsLabel}
{...props}
/>
),
};
};
export const createUncategorizedStatusItem = (
props: Omit<ListStatusProps, 'description' | 'title'>
) => {
return {
disabled: true,
name: (
<ListStatus
key="uncategorizedStatusItem"
description={noDatasetsDescriptionLabel}
title={noDatasetsLabel}
{...props}
/>
),
};
};

View file

@ -26,6 +26,7 @@ export const CustomDatasetSelector = withProviders(({ logExplorerProfileStateSer
error: integrationsError,
integrations,
isLoading: isLoadingIntegrations,
isSearching: isSearchingIntegrations,
loadMore,
reloadIntegrations,
searchIntegrations,
@ -53,6 +54,7 @@ export const CustomDatasetSelector = withProviders(({ logExplorerProfileStateSer
integrationsError={integrationsError}
isLoadingIntegrations={isLoadingIntegrations}
isLoadingStreams={isLoadingStreams}
isSearchingIntegrations={isSearchingIntegrations}
onIntegrationsLoadMore={loadMore}
onIntegrationsReload={reloadIntegrations}
onIntegrationsSearch={searchIntegrations}

View file

@ -37,10 +37,11 @@ const useIntegrations = ({ datasetsClient }: IntegrationsContextDeps) => {
const error = useSelector(integrationsStateService, (state) => state.context.error);
const isLoading = useSelector(
const isLoading = useSelector(integrationsStateService, (state) => state.matches('loading'));
const isSearching = useSelector(
integrationsStateService,
(state) =>
state.matches('loading') ||
state.matches({ loaded: 'loadingMore' }) ||
state.matches({ loaded: 'debounceSearchingIntegrations' }) ||
state.matches({ loaded: 'debounceSearchingIntegrationsStreams' })
@ -95,16 +96,13 @@ const useIntegrations = ({ datasetsClient }: IntegrationsContextDeps) => {
return {
// Underlying state machine
integrationsStateService,
// Failure states
error,
// Loading states
isLoading,
isSearching,
// Data
integrations,
// Actions
loadMore,
reloadIntegrations,

View file

@ -16,12 +16,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
describe('DatasetSelection initialization and update', () => {
describe('when the "index" query param does not exist', () => {
it('should initialize the "All log datasets" selection', async () => {
it('should initialize the "All logs" selection', async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
const datasetSelectionTitle =
await PageObjects.observabilityLogExplorer.getDatasetSelectorButtonText();
expect(datasetSelectionTitle).to.be('All log datasets');
expect(datasetSelectionTitle).to.be('All logs');
});
});
@ -41,7 +41,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(datasetSelectionTitle).to.be('[Azure Logs] activitylogs');
});
it('should fallback to the "All log datasets" selection and notify the user of an invalid encoded index', async () => {
it('should fallback to the "All logs" selection and notify the user of an invalid encoded index', async () => {
const invalidEncodedIndex = 'invalid-encoded-index';
await PageObjects.observabilityLogExplorer.navigateTo({
search: querystring.stringify({
@ -53,7 +53,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.getDatasetSelectorButtonText();
await PageObjects.observabilityLogExplorer.assertRestoreFailureToastExist();
expect(datasetSelectionTitle).to.be('All log datasets');
expect(datasetSelectionTitle).to.be('All logs');
});
});
@ -62,7 +62,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.navigateTo();
const allDatasetSelectionTitle =
await PageObjects.observabilityLogExplorer.getDatasetSelectorButtonText();
expect(allDatasetSelectionTitle).to.be('All log datasets');
expect(allDatasetSelectionTitle).to.be('All logs');
const azureActivitylogsIndex =
'BQZwpgNmDGAuCWB7AdgLmAEwIay+W6yWAtmKgOQSIDmIAtFgF4CuATmAHRZzwBu8sAJ5VadAFTkANAlhRU3BPyEiQASklFS8lu2kC55AII6wAAgAyNEFN5hWIJGnIBGDgFYOAJgDM5deCgeFAAVQQAHMgdkaihVIA===';
@ -81,7 +81,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await browser.goBack();
const backNavigationDatasetSelectionTitle =
await PageObjects.observabilityLogExplorer.getDatasetSelectorButtonText();
expect(backNavigationDatasetSelectionTitle).to.be('All log datasets');
expect(backNavigationDatasetSelectionTitle).to.be('All logs');
});
// Go forward to previous page selection

View file

@ -23,12 +23,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const retry = getService('retry');
const PageObjects = getPageObjects(['common', 'observabilityLogExplorer']);
const noIntegrationsTitle = 'No integrations found';
const noUncategorizedTitle = 'No data streams found';
describe('Dataset Selector', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.removeInstalledPackages();
});
describe('without installed integrations or uncategorized data streams', () => {
describe('as consistent behavior', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
});
@ -38,36 +41,33 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.openDatasetSelector();
});
describe('when open on the first navigation level', () => {
it('should always display the "All log datasets" entry as the first item', async () => {
const allLogDatasetButton =
await PageObjects.observabilityLogExplorer.getAllLogDatasetsButton();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
it('should always display the Integrations and Uncategorized top level tabs', async () => {
const integrationsTab = await PageObjects.observabilityLogExplorer.getIntegrationsTab();
const uncategorizedTab = await PageObjects.observabilityLogExplorer.getUncategorizedTab();
const allLogDatasetTitle = await allLogDatasetButton.getVisibleText();
const firstEntryTitle = await menuEntries[0].getVisibleText();
expect(await integrationsTab.isDisplayed()).to.be(true);
expect(await integrationsTab.getVisibleText()).to.be('Integrations');
expect(await uncategorizedTab.isDisplayed()).to.be(true);
expect(await uncategorizedTab.getVisibleText()).to.be('Uncategorized');
});
expect(allLogDatasetTitle).to.be('All log datasets');
expect(allLogDatasetTitle).to.be(firstEntryTitle);
});
it('should always display the "Show all logs" action', async () => {
const allLogDatasetButton =
await PageObjects.observabilityLogExplorer.getAllLogDatasetsButton();
it('should always display the unmanaged datasets entry as the second item', async () => {
const unamanagedDatasetButton =
await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const allLogDatasetTitle = await allLogDatasetButton.getVisibleText();
const unmanagedDatasetTitle = await unamanagedDatasetButton.getVisibleText();
const secondEntryTitle = await menuEntries[1].getVisibleText();
expect(unmanagedDatasetTitle).to.be('Uncategorized');
expect(unmanagedDatasetTitle).to.be(secondEntryTitle);
});
expect(allLogDatasetTitle).to.be('Show all logs');
});
describe('when open on the integrations tab', () => {
it('should display an error prompt if could not retrieve the integrations', async function () {
// Skip the test in case network condition utils are not available
try {
await retry.try(async () => {
await PageObjects.observabilityLogExplorer.assertNoIntegrationsPromptExists();
await PageObjects.observabilityLogExplorer.assertListStatusEmptyPromptExistsWithTitle(
noIntegrationsTitle
);
});
await PageObjects.common.sleep(5000);
@ -75,7 +75,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('a');
await retry.try(async () => {
await PageObjects.observabilityLogExplorer.assertNoIntegrationsErrorExists();
await PageObjects.observabilityLogExplorer.assertListStatusErrorPromptExistsWithTitle(
noIntegrationsTitle
);
});
await browser.restoreNetworkConditions();
@ -85,21 +87,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should display an empty prompt for no integrations', async () => {
const { integrations } = await PageObjects.observabilityLogExplorer.getIntegrations();
expect(integrations.length).to.be(0);
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
await PageObjects.observabilityLogExplorer.assertNoIntegrationsPromptExists();
expect(menuEntries.length).to.be(0);
await PageObjects.observabilityLogExplorer.assertListStatusEmptyPromptExistsWithTitle(
noIntegrationsTitle
);
});
});
describe('when navigating into Uncategorized data streams', () => {
it('should display a loading skeleton while loading', async function () {
describe('when open on the uncategorized tab', () => {
it('should display a loading skeleton while loading uncategorized datasets', async function () {
// Skip the test in case network condition utils are not available
try {
await browser.setNetworkConditions('SLOW_3G'); // Almost stuck network conditions
const unamanagedDatasetButton =
await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await unamanagedDatasetButton.click();
const uncategorizedTab =
await PageObjects.observabilityLogExplorer.getUncategorizedTab();
await uncategorizedTab.click();
await PageObjects.observabilityLogExplorer.assertLoadingSkeletonExists();
@ -109,22 +115,26 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
}
});
it('should display an error prompt if could not retrieve the data streams', async function () {
it('should display an error prompt if could not retrieve the datasets', async function () {
const uncategorizedTab = await PageObjects.observabilityLogExplorer.getUncategorizedTab();
await uncategorizedTab.click();
// Skip the test in case network condition utils are not available
try {
const unamanagedDatasetButton =
await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await unamanagedDatasetButton.click();
await retry.try(async () => {
await PageObjects.observabilityLogExplorer.assertNoDataStreamsPromptExists();
await PageObjects.observabilityLogExplorer.assertListStatusEmptyPromptExistsWithTitle(
noUncategorizedTitle
);
});
await PageObjects.common.sleep(5000);
await browser.setNetworkConditions('OFFLINE');
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('a');
await retry.try(async () => {
await PageObjects.observabilityLogExplorer.assertNoDataStreamsErrorExists();
await PageObjects.observabilityLogExplorer.assertListStatusErrorPromptExistsWithTitle(
noUncategorizedTitle
);
});
await browser.restoreNetworkConditions();
@ -133,17 +143,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
}
});
it('should display an empty prompt for no data streams', async () => {
const unamanagedDatasetButton =
await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await unamanagedDatasetButton.click();
it('should display an empty prompt for no uncategorized data streams', async () => {
const uncategorizedTab = await PageObjects.observabilityLogExplorer.getUncategorizedTab();
await uncategorizedTab.click();
const unamanagedDatasetEntries =
await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const uncategorizedEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(unamanagedDatasetEntries.length).to.be(0);
expect(uncategorizedEntries.length).to.be(0);
await PageObjects.observabilityLogExplorer.assertNoDataStreamsPromptExists();
await PageObjects.observabilityLogExplorer.assertListStatusEmptyPromptExistsWithTitle(
noUncategorizedTitle
);
});
});
});
@ -166,7 +178,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await cleanupIntegrationsSetup();
});
describe('when open on the first navigation level', () => {
describe('when open on the integrations tab', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
});
@ -176,30 +188,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.openDatasetSelector();
});
it('should always display the "All log datasets" entry as the first item', async () => {
const allLogDatasetButton =
await PageObjects.observabilityLogExplorer.getAllLogDatasetsButton();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const allLogDatasetTitle = await allLogDatasetButton.getVisibleText();
const firstEntryTitle = await menuEntries[0].getVisibleText();
expect(allLogDatasetTitle).to.be('All log datasets');
expect(allLogDatasetTitle).to.be(firstEntryTitle);
});
it('should always display the unmanaged datasets entry as the second item', async () => {
const unamanagedDatasetButton =
await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const unmanagedDatasetTitle = await unamanagedDatasetButton.getVisibleText();
const secondEntryTitle = await menuEntries[1].getVisibleText();
expect(unmanagedDatasetTitle).to.be('Uncategorized');
expect(unmanagedDatasetTitle).to.be(secondEntryTitle);
});
it('should display a list of installed integrations', async () => {
const { integrations } = await PageObjects.observabilityLogExplorer.getIntegrations();
@ -257,7 +245,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(integrations.length).to.be(0);
});
await PageObjects.observabilityLogExplorer.assertNoIntegrationsPromptExists();
await PageObjects.observabilityLogExplorer.assertListStatusEmptyPromptExistsWithTitle(
noIntegrationsTitle
);
});
it('should load more integrations by scrolling to the end of the list', async () => {
@ -290,9 +280,157 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
cleanupAdditionalSetup();
});
describe('clicking on integration and moving into the second navigation level', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
});
beforeEach(async () => {
await browser.refresh();
await PageObjects.observabilityLogExplorer.openDatasetSelector();
});
it('should display a list of available datasets', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const [panelTitleNode, integrationDatasetEntries] =
await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) =>
Promise.all([
PageObjects.observabilityLogExplorer.getPanelTitle(menu),
PageObjects.observabilityLogExplorer.getPanelEntries(menu),
])
);
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
expect(await integrationDatasetEntries[0].getVisibleText()).to.be('access');
expect(await integrationDatasetEntries[1].getVisibleText()).to.be('error');
});
});
it('should sort the datasets list by the clicked sorting option', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
});
// Test ascending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
// Test descending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('desc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be('error');
expect(await menuEntries[1].getVisibleText()).to.be('access');
});
// Test back ascending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
});
it('should filter the datasets list by the typed dataset name', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('err');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(menuEntries.length).to.be(1);
expect(await menuEntries[0].getVisibleText()).to.be('error');
});
});
it('should update the current selection with the clicked dataset', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be('access');
menuEntries[0].click();
});
await retry.try(async () => {
const selectorButton =
await PageObjects.observabilityLogExplorer.getDatasetSelectorButton();
expect(await selectorButton.getVisibleText()).to.be('[Apache HTTP Server] access');
});
});
});
});
describe('when clicking on integration and moving into the second navigation level', () => {
describe('when open on the uncategorized tab', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
});
@ -300,143 +438,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
beforeEach(async () => {
await browser.refresh();
await PageObjects.observabilityLogExplorer.openDatasetSelector();
await PageObjects.observabilityLogExplorer
.getUncategorizedTab()
.then((tab) => tab.click());
});
it('should display a list of available datasets', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
});
it('should sort the datasets list by the clicked sorting option', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
});
// Test ascending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
// Test descending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('desc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(await menuEntries[0].getVisibleText()).to.be('error');
expect(await menuEntries[1].getVisibleText()).to.be('access');
});
// Test back ascending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
});
it('should filter the datasets list by the typed dataset name', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('err');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(menuEntries.length).to.be(1);
expect(await menuEntries[0].getVisibleText()).to.be('error');
});
});
it('should update the current selection with the clicked dataset', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(await menuEntries[0].getVisibleText()).to.be('access');
menuEntries[0].click();
});
await retry.try(async () => {
const selectorButton =
await PageObjects.observabilityLogExplorer.getDatasetSelectorButton();
expect(await selectorButton.getVisibleText()).to.be('[Apache HTTP Server] access');
});
});
});
describe('when navigating into Uncategorized data streams', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
});
beforeEach(async () => {
await browser.refresh();
await PageObjects.observabilityLogExplorer.openDatasetSelector();
});
it('should display a list of available datasets', async () => {
const button = await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await button.click();
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const [panelTitleNode, menuEntries] = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) =>
Promise.all([
PageObjects.observabilityLogExplorer.getPanelTitle(menu),
PageObjects.observabilityLogExplorer.getPanelEntries(menu),
])
);
expect(await panelTitleNode.getVisibleText()).to.be('Uncategorized');
expect(await menuEntries[0].getVisibleText()).to.be(expectedUncategorized[0]);
@ -446,12 +462,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should sort the datasets list by the clicked sorting option', async () => {
const button = await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await button.click();
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
expect(await panelTitleNode.getVisibleText()).to.be('Uncategorized');
});
@ -459,7 +473,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// Test ascending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be(expectedUncategorized[0]);
expect(await menuEntries[1].getVisibleText()).to.be(expectedUncategorized[1]);
@ -469,7 +485,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// Test descending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('desc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be(expectedUncategorized[2]);
expect(await menuEntries[1].getVisibleText()).to.be(expectedUncategorized[1]);
@ -479,7 +497,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// Test back ascending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be(expectedUncategorized[0]);
expect(await menuEntries[1].getVisibleText()).to.be(expectedUncategorized[1]);
@ -488,18 +508,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should filter the datasets list by the typed dataset name', async () => {
const button = await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await button.click();
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
expect(await panelTitleNode.getVisibleText()).to.be('Uncategorized');
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be(expectedUncategorized[0]);
expect(await menuEntries[1].getVisibleText()).to.be(expectedUncategorized[1]);
@ -509,7 +529,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('retail');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(menuEntries.length).to.be(1);
expect(await menuEntries[0].getVisibleText()).to.be('retail');
@ -517,18 +539,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should update the current selection with the clicked dataset', async () => {
const button = await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await button.click();
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
expect(await panelTitleNode.getVisibleText()).to.be('Uncategorized');
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be(expectedUncategorized[0]);
menuEntries[0].click();
@ -560,9 +582,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const [panelTitleNode, menuEntries] = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) =>
Promise.all([
PageObjects.observabilityLogExplorer.getPanelTitle(menu),
PageObjects.observabilityLogExplorer.getPanelEntries(menu),
])
);
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
expect(await menuEntries[0].getVisibleText()).to.be('access');
@ -573,9 +600,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.openDatasetSelector();
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const [panelTitleNode, menuEntries] = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) =>
Promise.all([
PageObjects.observabilityLogExplorer.getPanelTitle(menu),
PageObjects.observabilityLogExplorer.getPanelEntries(menu),
])
);
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
expect(await menuEntries[0].getVisibleText()).to.be('access');
@ -601,12 +633,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
});
describe('when switching between integration panels', () => {
describe('when switching between tabs or integration panels', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
});
it('should remember the latest search and restore its results for each integration', async () => {
it('should remember the latest search and restore its results', async () => {
await PageObjects.observabilityLogExplorer.openDatasetSelector();
await PageObjects.observabilityLogExplorer.clearSearchField();
@ -620,9 +652,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const [panelTitleNode, menuEntries] = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) =>
Promise.all([
PageObjects.observabilityLogExplorer.getPanelTitle(menu),
PageObjects.observabilityLogExplorer.getPanelEntries(menu),
])
);
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
expect(await menuEntries[0].getVisibleText()).to.be('access');
@ -632,15 +669,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('err');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(menuEntries.length).to.be(1);
expect(await menuEntries[0].getVisibleText()).to.be('error');
});
// Navigate back to integrations
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
panelTitleNode.click();
await retry.try(async () => {
@ -655,7 +695,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
const searchValue = await PageObjects.observabilityLogExplorer.getSearchFieldValue();
expect(searchValue).to.eql('err');

View file

@ -12,6 +12,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const retry = getService('retry');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['discover', 'observabilityLogExplorer', 'timePicker']);
describe('Header menu', () => {
@ -48,6 +49,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should navigate to discover keeping the current columns/filters/query/time/data view', async () => {
// Set timerange to specific values to match data and retrieve config
await PageObjects.discover.expandTimeRangeAsSuggestedInNoResultsMessage();
await retry.try(async () => {
await testSubjects.existOrFail('superDatePickerstartDatePopoverButton');
await testSubjects.existOrFail('superDatePickerendDatePopoverButton');
});
const timeConfig = await PageObjects.timePicker.getTimeConfig();
// Set query bar value
@ -59,9 +66,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.discover.waitForDocTableLoadingComplete();
await retry.try(async () => {
expect(await PageObjects.discover.getCurrentlySelectedDataView()).to.eql(
'All log datasets'
);
expect(await PageObjects.discover.getCurrentlySelectedDataView()).to.eql('All logs');
});
await retry.try(async () => {

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import expect from '@kbn/expect';
import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper';
import { FtrProviderContext } from '../ftr_provider_context';
export interface IntegrationPackage {
@ -180,7 +181,7 @@ export function ObservabilityLogExplorerPageObject({
},
getDatasetSelectorButton() {
return testSubjects.find('datasetSelectorPopoverButton');
return testSubjects.find('datasetSelectorPopoverButton', 30000); // Increase timeout if refresh takes longer before opening the selector
},
getDatasetSelectorContent() {
@ -191,12 +192,24 @@ export function ObservabilityLogExplorerPageObject({
return testSubjects.find('datasetSelectorSearchControls');
},
getDatasetSelectorContextMenu() {
return testSubjects.find('datasetSelectorContextMenu');
getIntegrationsContextMenu() {
return testSubjects.find('integrationsContextMenu');
},
getDatasetSelectorContextMenuPanelTitle() {
return testSubjects.find('contextMenuPanelTitleButton');
getIntegrationsTab() {
return testSubjects.find('datasetSelectorIntegrationsTab');
},
getUncategorizedContextMenu() {
return testSubjects.find('uncategorizedContextMenu');
},
getUncategorizedTab() {
return testSubjects.find('datasetSelectorUncategorizedTab');
},
getPanelTitle(contextMenu: WebElementWrapper) {
return contextMenu.findByClassName('euiContextMenuPanelTitle');
},
async getDatasetSelectorButtonText() {
@ -204,13 +217,12 @@ export function ObservabilityLogExplorerPageObject({
return button.getVisibleText();
},
async getCurrentPanelEntries() {
const contextMenu = await this.getDatasetSelectorContextMenu();
return contextMenu.findAllByClassName('euiContextMenuItem', 2000);
getPanelEntries(contextMenu: WebElementWrapper) {
return contextMenu.findAllByCssSelector('.euiContextMenuItem:not([disabled])', 2000);
},
getAllLogDatasetsButton() {
return testSubjects.find('allLogDatasets');
return testSubjects.find('datasetSelectorshowAllLogs');
},
getUnmanagedDatasetsButton() {
@ -218,9 +230,9 @@ export function ObservabilityLogExplorerPageObject({
},
async getIntegrations() {
const content = await this.getDatasetSelectorContent();
const menu = await this.getIntegrationsContextMenu();
const nodes = await content.findAllByCssSelector('[data-test-subj*="integration-"]', 2000);
const nodes = await menu.findAllByCssSelector('[data-test-subj*="integration-"]', 2000);
const integrations = await Promise.all(nodes.map((node) => node.getVisibleText()));
return {
@ -287,32 +299,18 @@ export function ObservabilityLogExplorerPageObject({
return testSubjects.existOrFail('datasetSelectorSkeleton');
},
async assertNoIntegrationsPromptExists() {
const integrationStatus = await testSubjects.find('integrationStatusItem');
const promptTitle = await integrationStatus.findByTagName('h2');
async assertListStatusEmptyPromptExistsWithTitle(title: string) {
const [listStatus] = await testSubjects.findAll('datasetSelectorListStatusEmptyPrompt');
const promptTitle = await listStatus.findByTagName('h2');
expect(await promptTitle.getVisibleText()).to.be('No integrations found');
expect(await promptTitle.getVisibleText()).to.be(title);
},
async assertNoIntegrationsErrorExists() {
const integrationStatus = await testSubjects.find('integrationsErrorPrompt');
const promptTitle = await integrationStatus.findByTagName('h2');
async assertListStatusErrorPromptExistsWithTitle(title: string) {
const listStatus = await testSubjects.find('datasetSelectorListStatusErrorPrompt');
const promptTitle = await listStatus.findByTagName('h2');
expect(await promptTitle.getVisibleText()).to.be('No integrations found');
},
async assertNoDataStreamsPromptExists() {
const integrationStatus = await testSubjects.find('emptyDatasetPrompt');
const promptTitle = await integrationStatus.findByTagName('h2');
expect(await promptTitle.getVisibleText()).to.be('No data streams found');
},
async assertNoDataStreamsErrorExists() {
const integrationStatus = await testSubjects.find('datasetErrorPrompt');
const promptTitle = await integrationStatus.findByTagName('h2');
expect(await promptTitle.getVisibleText()).to.be('No data streams found');
expect(await promptTitle.getVisibleText()).to.be(title);
},
getHeaderMenu() {

View file

@ -17,12 +17,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// FLAKY: https://github.com/elastic/kibana/issues/166016
describe.skip('DatasetSelection initialization and update', () => {
describe('when the "index" query param does not exist', () => {
it('should initialize the "All log datasets" selection', async () => {
it('should initialize the "All logs" selection', async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
const datasetSelectionTitle =
await PageObjects.observabilityLogExplorer.getDatasetSelectorButtonText();
expect(datasetSelectionTitle).to.be('All log datasets');
expect(datasetSelectionTitle).to.be('All logs');
});
});
@ -42,7 +42,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(datasetSelectionTitle).to.be('[Azure Logs] activitylogs');
});
it('should fallback to the "All log datasets" selection and notify the user of an invalid encoded index', async () => {
it('should fallback to the "All logs" selection and notify the user of an invalid encoded index', async () => {
const invalidEncodedIndex = 'invalid-encoded-index';
await PageObjects.observabilityLogExplorer.navigateTo({
search: querystring.stringify({
@ -54,7 +54,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.getDatasetSelectorButtonText();
await PageObjects.observabilityLogExplorer.assertRestoreFailureToastExist();
expect(datasetSelectionTitle).to.be('All log datasets');
expect(datasetSelectionTitle).to.be('All logs');
});
});
@ -63,7 +63,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.navigateTo();
const allDatasetSelectionTitle =
await PageObjects.observabilityLogExplorer.getDatasetSelectorButtonText();
expect(allDatasetSelectionTitle).to.be('All log datasets');
expect(allDatasetSelectionTitle).to.be('All logs');
const azureActivitylogsIndex =
'BQZwpgNmDGAuCWB7AdgLmAEwIay+W6yWAtmKgOQSIDmIAtFgF4CuATmAHRZzwBu8sAJ5VadAFTkANAlhRU3BPyEiQASklFS8lu2kC55AII6wAAgAyNEFN5hWIJGnIBGDgFYOAJgDM5deCgeFAAVQQAHMgdkaihVIA===';
@ -82,7 +82,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await browser.goBack();
const backNavigationDatasetSelectionTitle =
await PageObjects.observabilityLogExplorer.getDatasetSelectorButtonText();
expect(backNavigationDatasetSelectionTitle).to.be('All log datasets');
expect(backNavigationDatasetSelectionTitle).to.be('All logs');
});
// Go forward to previous page selection

View file

@ -23,6 +23,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const retry = getService('retry');
const PageObjects = getPageObjects(['common', 'observabilityLogExplorer', 'svlCommonPage']);
const noIntegrationsTitle = 'No integrations found';
const noUncategorizedTitle = 'No data streams found';
describe('Dataset Selector', () => {
before(async () => {
await PageObjects.svlCommonPage.login();
@ -33,7 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.svlCommonPage.forceLogout();
});
describe('without installed integrations or uncategorized data streams', () => {
describe('as consistent behavior', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
});
@ -43,36 +46,33 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.openDatasetSelector();
});
describe('when open on the first navigation level', () => {
it('should always display the "All log datasets" entry as the first item', async () => {
const allLogDatasetButton =
await PageObjects.observabilityLogExplorer.getAllLogDatasetsButton();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
it('should always display the Integrations and Uncategorized top level tabs', async () => {
const integrationsTab = await PageObjects.observabilityLogExplorer.getIntegrationsTab();
const uncategorizedTab = await PageObjects.observabilityLogExplorer.getUncategorizedTab();
const allLogDatasetTitle = await allLogDatasetButton.getVisibleText();
const firstEntryTitle = await menuEntries[0].getVisibleText();
expect(await integrationsTab.isDisplayed()).to.be(true);
expect(await integrationsTab.getVisibleText()).to.be('Integrations');
expect(await uncategorizedTab.isDisplayed()).to.be(true);
expect(await uncategorizedTab.getVisibleText()).to.be('Uncategorized');
});
expect(allLogDatasetTitle).to.be('All log datasets');
expect(allLogDatasetTitle).to.be(firstEntryTitle);
});
it('should always display the "Show all logs" action', async () => {
const allLogDatasetButton =
await PageObjects.observabilityLogExplorer.getAllLogDatasetsButton();
it('should always display the unmanaged datasets entry as the second item', async () => {
const unamanagedDatasetButton =
await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const allLogDatasetTitle = await allLogDatasetButton.getVisibleText();
const unmanagedDatasetTitle = await unamanagedDatasetButton.getVisibleText();
const secondEntryTitle = await menuEntries[1].getVisibleText();
expect(unmanagedDatasetTitle).to.be('Uncategorized');
expect(unmanagedDatasetTitle).to.be(secondEntryTitle);
});
expect(allLogDatasetTitle).to.be('Show all logs');
});
describe('when open on the integrations tab', () => {
it('should display an error prompt if could not retrieve the integrations', async function () {
// Skip the test in case network condition utils are not available
try {
await retry.try(async () => {
await PageObjects.observabilityLogExplorer.assertNoIntegrationsPromptExists();
await PageObjects.observabilityLogExplorer.assertListStatusEmptyPromptExistsWithTitle(
noIntegrationsTitle
);
});
await PageObjects.common.sleep(5000);
@ -80,7 +80,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('a');
await retry.try(async () => {
await PageObjects.observabilityLogExplorer.assertNoIntegrationsErrorExists();
await PageObjects.observabilityLogExplorer.assertListStatusErrorPromptExistsWithTitle(
noIntegrationsTitle
);
});
await browser.restoreNetworkConditions();
@ -89,24 +91,26 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
}
});
// Skip: failing assertion
// Issue: https://github.com/elastic/kibana/issues/165138
it.skip('should display an empty prompt for no integrations', async () => {
const { integrations } = await PageObjects.observabilityLogExplorer.getIntegrations();
expect(integrations.length).to.be(0);
it('should display an empty prompt for no integrations', async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
await PageObjects.observabilityLogExplorer.assertNoIntegrationsPromptExists();
expect(menuEntries.length).to.be(0);
await PageObjects.observabilityLogExplorer.assertListStatusEmptyPromptExistsWithTitle(
noIntegrationsTitle
);
});
});
describe('when navigating into Uncategorized data streams', () => {
it('should display a loading skeleton while loading', async function () {
describe('when open on the uncategorized tab', () => {
it('should display a loading skeleton while loading uncategorized datasets', async function () {
// Skip the test in case network condition utils are not available
try {
await browser.setNetworkConditions('SLOW_3G'); // Almost stuck network conditions
const unamanagedDatasetButton =
await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await unamanagedDatasetButton.click();
const uncategorizedTab =
await PageObjects.observabilityLogExplorer.getUncategorizedTab();
await uncategorizedTab.click();
await PageObjects.observabilityLogExplorer.assertLoadingSkeletonExists();
@ -116,22 +120,26 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
}
});
it('should display an error prompt if could not retrieve the data streams', async function () {
it('should display an error prompt if could not retrieve the datasets', async function () {
const uncategorizedTab = await PageObjects.observabilityLogExplorer.getUncategorizedTab();
await uncategorizedTab.click();
// Skip the test in case network condition utils are not available
try {
const unamanagedDatasetButton =
await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await unamanagedDatasetButton.click();
await retry.try(async () => {
await PageObjects.observabilityLogExplorer.assertNoDataStreamsPromptExists();
await PageObjects.observabilityLogExplorer.assertListStatusEmptyPromptExistsWithTitle(
noUncategorizedTitle
);
});
await PageObjects.common.sleep(5000);
await browser.setNetworkConditions('OFFLINE');
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('a');
await retry.try(async () => {
await PageObjects.observabilityLogExplorer.assertNoDataStreamsErrorExists();
await PageObjects.observabilityLogExplorer.assertListStatusErrorPromptExistsWithTitle(
noUncategorizedTitle
);
});
await browser.restoreNetworkConditions();
@ -140,17 +148,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
}
});
it('should display an empty prompt for no data streams', async () => {
const unamanagedDatasetButton =
await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await unamanagedDatasetButton.click();
it('should display an empty prompt for no uncategorized data streams', async () => {
const uncategorizedTab = await PageObjects.observabilityLogExplorer.getUncategorizedTab();
await uncategorizedTab.click();
const unamanagedDatasetEntries =
await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const uncategorizedEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(unamanagedDatasetEntries.length).to.be(0);
expect(uncategorizedEntries.length).to.be(0);
await PageObjects.observabilityLogExplorer.assertNoDataStreamsPromptExists();
await PageObjects.observabilityLogExplorer.assertListStatusEmptyPromptExistsWithTitle(
noUncategorizedTitle
);
});
});
});
@ -173,7 +183,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await cleanupIntegrationsSetup();
});
describe('when open on the first navigation level', () => {
describe('when open on the integrations tab', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
});
@ -183,30 +193,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.openDatasetSelector();
});
it('should always display the "All log datasets" entry as the first item', async () => {
const allLogDatasetButton =
await PageObjects.observabilityLogExplorer.getAllLogDatasetsButton();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const allLogDatasetTitle = await allLogDatasetButton.getVisibleText();
const firstEntryTitle = await menuEntries[0].getVisibleText();
expect(allLogDatasetTitle).to.be('All log datasets');
expect(allLogDatasetTitle).to.be(firstEntryTitle);
});
it('should always display the unmanaged datasets entry as the second item', async () => {
const unamanagedDatasetButton =
await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const unmanagedDatasetTitle = await unamanagedDatasetButton.getVisibleText();
const secondEntryTitle = await menuEntries[1].getVisibleText();
expect(unmanagedDatasetTitle).to.be('Uncategorized');
expect(unmanagedDatasetTitle).to.be(secondEntryTitle);
});
it('should display a list of installed integrations', async () => {
const { integrations } = await PageObjects.observabilityLogExplorer.getIntegrations();
@ -264,7 +250,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(integrations.length).to.be(0);
});
await PageObjects.observabilityLogExplorer.assertNoIntegrationsPromptExists();
await PageObjects.observabilityLogExplorer.assertListStatusEmptyPromptExistsWithTitle(
noIntegrationsTitle
);
});
it('should load more integrations by scrolling to the end of the list', async () => {
@ -297,9 +285,157 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
cleanupAdditionalSetup();
});
describe('clicking on integration and moving into the second navigation level', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
});
beforeEach(async () => {
await browser.refresh();
await PageObjects.observabilityLogExplorer.openDatasetSelector();
});
it('should display a list of available datasets', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const [panelTitleNode, integrationDatasetEntries] =
await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) =>
Promise.all([
PageObjects.observabilityLogExplorer.getPanelTitle(menu),
PageObjects.observabilityLogExplorer.getPanelEntries(menu),
])
);
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
expect(await integrationDatasetEntries[0].getVisibleText()).to.be('access');
expect(await integrationDatasetEntries[1].getVisibleText()).to.be('error');
});
});
it('should sort the datasets list by the clicked sorting option', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
});
// Test ascending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
// Test descending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('desc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be('error');
expect(await menuEntries[1].getVisibleText()).to.be('access');
});
// Test back ascending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
});
it('should filter the datasets list by the typed dataset name', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('err');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(menuEntries.length).to.be(1);
expect(await menuEntries[0].getVisibleText()).to.be('error');
});
});
it('should update the current selection with the clicked dataset', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be('access');
menuEntries[0].click();
});
await retry.try(async () => {
const selectorButton =
await PageObjects.observabilityLogExplorer.getDatasetSelectorButton();
expect(await selectorButton.getVisibleText()).to.be('[Apache HTTP Server] access');
});
});
});
});
describe('when clicking on integration and moving into the second navigation level', () => {
describe('when open on the uncategorized tab', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
});
@ -307,143 +443,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
beforeEach(async () => {
await browser.refresh();
await PageObjects.observabilityLogExplorer.openDatasetSelector();
await PageObjects.observabilityLogExplorer
.getUncategorizedTab()
.then((tab) => tab.click());
});
it('should display a list of available datasets', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
});
it('should sort the datasets list by the clicked sorting option', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
});
// Test ascending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
// Test descending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('desc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(await menuEntries[0].getVisibleText()).to.be('error');
expect(await menuEntries[1].getVisibleText()).to.be('access');
});
// Test back ascending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
});
it('should filter the datasets list by the typed dataset name', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(await menuEntries[0].getVisibleText()).to.be('access');
expect(await menuEntries[1].getVisibleText()).to.be('error');
});
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('err');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(menuEntries.length).to.be(1);
expect(await menuEntries[0].getVisibleText()).to.be('error');
});
});
it('should update the current selection with the clicked dataset', async () => {
await retry.try(async () => {
const { nodes } = await PageObjects.observabilityLogExplorer.getIntegrations();
await nodes[0].click();
});
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
expect(await menuEntries[0].getVisibleText()).to.be('access');
menuEntries[0].click();
});
await retry.try(async () => {
const selectorButton =
await PageObjects.observabilityLogExplorer.getDatasetSelectorButton();
expect(await selectorButton.getVisibleText()).to.be('[Apache HTTP Server] access');
});
});
});
describe('when navigating into Uncategorized data streams', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
});
beforeEach(async () => {
await browser.refresh();
await PageObjects.observabilityLogExplorer.openDatasetSelector();
});
it('should display a list of available datasets', async () => {
const button = await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await button.click();
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const [panelTitleNode, menuEntries] = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) =>
Promise.all([
PageObjects.observabilityLogExplorer.getPanelTitle(menu),
PageObjects.observabilityLogExplorer.getPanelEntries(menu),
])
);
expect(await panelTitleNode.getVisibleText()).to.be('Uncategorized');
expect(await menuEntries[0].getVisibleText()).to.be(expectedUncategorized[0]);
@ -453,12 +467,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should sort the datasets list by the clicked sorting option', async () => {
const button = await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await button.click();
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
expect(await panelTitleNode.getVisibleText()).to.be('Uncategorized');
});
@ -466,7 +478,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// Test ascending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be(expectedUncategorized[0]);
expect(await menuEntries[1].getVisibleText()).to.be(expectedUncategorized[1]);
@ -476,7 +490,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// Test descending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('desc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be(expectedUncategorized[2]);
expect(await menuEntries[1].getVisibleText()).to.be(expectedUncategorized[1]);
@ -486,7 +502,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// Test back ascending order
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be(expectedUncategorized[0]);
expect(await menuEntries[1].getVisibleText()).to.be(expectedUncategorized[1]);
@ -495,18 +513,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should filter the datasets list by the typed dataset name', async () => {
const button = await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await button.click();
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
expect(await panelTitleNode.getVisibleText()).to.be('Uncategorized');
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be(expectedUncategorized[0]);
expect(await menuEntries[1].getVisibleText()).to.be(expectedUncategorized[1]);
@ -516,7 +534,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('retail');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(menuEntries.length).to.be(1);
expect(await menuEntries[0].getVisibleText()).to.be('retail');
@ -524,18 +544,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should update the current selection with the clicked dataset', async () => {
const button = await PageObjects.observabilityLogExplorer.getUnmanagedDatasetsButton();
await button.click();
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
expect(await panelTitleNode.getVisibleText()).to.be('Uncategorized');
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getUncategorizedContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(await menuEntries[0].getVisibleText()).to.be(expectedUncategorized[0]);
menuEntries[0].click();
@ -567,9 +587,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const [panelTitleNode, menuEntries] = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) =>
Promise.all([
PageObjects.observabilityLogExplorer.getPanelTitle(menu),
PageObjects.observabilityLogExplorer.getPanelEntries(menu),
])
);
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
expect(await menuEntries[0].getVisibleText()).to.be('access');
@ -580,9 +605,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.openDatasetSelector();
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const [panelTitleNode, menuEntries] = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) =>
Promise.all([
PageObjects.observabilityLogExplorer.getPanelTitle(menu),
PageObjects.observabilityLogExplorer.getPanelEntries(menu),
])
);
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
expect(await menuEntries[0].getVisibleText()).to.be('access');
@ -608,12 +638,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
});
describe('when switching between integration panels', () => {
describe('when switching between tabs or integration panels', () => {
before(async () => {
await PageObjects.observabilityLogExplorer.navigateTo();
});
it('should remember the latest search and restore its results for each integration', async () => {
it('should remember the latest search and restore its results', async () => {
await PageObjects.observabilityLogExplorer.openDatasetSelector();
await PageObjects.observabilityLogExplorer.clearSearchField();
@ -627,9 +657,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
await retry.try(async () => {
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const [panelTitleNode, menuEntries] = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) =>
Promise.all([
PageObjects.observabilityLogExplorer.getPanelTitle(menu),
PageObjects.observabilityLogExplorer.getPanelEntries(menu),
])
);
expect(await panelTitleNode.getVisibleText()).to.be('Apache HTTP Server');
expect(await menuEntries[0].getVisibleText()).to.be('access');
@ -639,15 +674,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('err');
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
expect(menuEntries.length).to.be(1);
expect(await menuEntries[0].getVisibleText()).to.be('error');
});
// Navigate back to integrations
const panelTitleNode =
await PageObjects.observabilityLogExplorer.getDatasetSelectorContextMenuPanelTitle();
const panelTitleNode = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
panelTitleNode.click();
await retry.try(async () => {
@ -662,7 +700,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogExplorer.getCurrentPanelEntries();
const menuEntries = await PageObjects.observabilityLogExplorer
.getIntegrationsContextMenu()
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
const searchValue = await PageObjects.observabilityLogExplorer.getSearchFieldValue();
expect(searchValue).to.eql('err');

View file

@ -12,19 +12,26 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const retry = getService('retry');
const PageObjects = getPageObjects(['discover', 'observabilityLogExplorer', 'timePicker']);
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects([
'discover',
'observabilityLogExplorer',
'svlCommonPage',
'timePicker',
]);
// Failing: See https://github.com/elastic/kibana/issues/165839
describe.skip('Header menu', () => {
describe('Header menu', () => {
before(async () => {
await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover');
await esArchiver.load(
'x-pack/test/functional/es_archives/observability_log_explorer/data_streams'
);
await PageObjects.svlCommonPage.login();
await PageObjects.observabilityLogExplorer.navigateTo();
});
after(async () => {
await PageObjects.svlCommonPage.forceLogout();
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
await esArchiver.unload(
'x-pack/test/functional/es_archives/observability_log_explorer/data_streams'
@ -49,6 +56,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should navigate to discover keeping the current columns/filters/query/time/data view', async () => {
// Set timerange to specific values to match data and retrieve config
await PageObjects.discover.expandTimeRangeAsSuggestedInNoResultsMessage();
await retry.try(async () => {
await testSubjects.existOrFail('superDatePickerstartDatePopoverButton');
await testSubjects.existOrFail('superDatePickerendDatePopoverButton');
});
const timeConfig = await PageObjects.timePicker.getTimeConfig();
// Set query bar value
@ -60,9 +73,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.discover.waitForDocTableLoadingComplete();
await retry.try(async () => {
expect(await PageObjects.discover.getCurrentlySelectedDataView()).to.eql(
'All log datasets'
);
expect(await PageObjects.discover.getCurrentlySelectedDataView()).to.eql('All logs');
});
await retry.try(async () => {