mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security Solution][Timeline] - ESQL in timeline (#166764)
## Summary
This PR leverages the work done
[here](https://github.com/elastic/kibana/pull/165596) to introduce ES|QL
into timeline. The goal of this PR is to provide security users easy
access to ESQL from within the security solution. It will be released in
`technical preview` for the 8.11 release.
<img width="1725" alt="image"
src="0e275cf7
-bbce-476b-b1dc-8936427ad14f">
### Checklist
Delete any items that are not applicable to this PR.
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
---------
Co-authored-by: Jatin Kathuria <jatin.kathuria@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Jatin Kathuria <jtn.kathuria@gmail.com>
This commit is contained in:
parent
471ae6a858
commit
96a1ef4581
24 changed files with 248 additions and 246 deletions
|
@ -35,7 +35,6 @@ xpack.fleet.internal.registry.spec.max: '3.0'
|
|||
|
||||
# Serverless security specific options
|
||||
xpack.securitySolution.enableExperimental:
|
||||
- discoverInTimeline
|
||||
- esqlRulesDisabled
|
||||
|
||||
xpack.ml.ad.enabled: true
|
||||
|
|
|
@ -46,6 +46,11 @@ const mockSearchBarCustomizationWithCustomSearchBar: SearchBarCustomization = {
|
|||
CustomSearchBar: MockCustomSearchBar,
|
||||
};
|
||||
|
||||
const mockSearchBarCustomizationWithHiddenDataViewPicker: SearchBarCustomization = {
|
||||
id: 'search_bar',
|
||||
hideDataViewPicker: true,
|
||||
};
|
||||
|
||||
let mockUseCustomizations = false;
|
||||
|
||||
jest.mock('../../../../customizations', () => ({
|
||||
|
@ -256,5 +261,23 @@ describe('Discover topnav component', () => {
|
|||
).find(mockSearchBarCustomization.CustomDataViewPicker!);
|
||||
expect(dataViewPickerOverride.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should not render the dataView picker when hideDataViewPicker is true', () => {
|
||||
(useDiscoverCustomization as jest.Mock).mockImplementation((id: DiscoverCustomizationId) => {
|
||||
if (id === 'search_bar') {
|
||||
return mockSearchBarCustomizationWithHiddenDataViewPicker;
|
||||
}
|
||||
});
|
||||
|
||||
const props = getProps();
|
||||
const component = mountWithIntl(
|
||||
<DiscoverMainProvider value={props.stateContainer}>
|
||||
<DiscoverTopNav {...props} />
|
||||
</DiscoverMainProvider>
|
||||
);
|
||||
|
||||
const topNav = component.find(mockDiscoverService.navigation.ui.AggregateQueryTopNavMenu);
|
||||
expect(topNav.prop('dataViewPickerComponentProps')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -114,6 +114,7 @@ export const DiscoverTopNav = ({
|
|||
}, [dataViewEditor, stateContainer]);
|
||||
|
||||
const topNavCustomization = useDiscoverCustomization('top_nav');
|
||||
|
||||
const topNavMenu = useMemo(
|
||||
() =>
|
||||
getTopNavLinks({
|
||||
|
@ -171,6 +172,17 @@ export const DiscoverTopNav = ({
|
|||
if (isESQLModeEnabled) {
|
||||
supportedTextBasedLanguages.push('ESQL');
|
||||
}
|
||||
|
||||
const searchBarCustomization = useDiscoverCustomization('search_bar');
|
||||
|
||||
const SearchBar = useMemo(
|
||||
() => searchBarCustomization?.CustomSearchBar ?? AggregateQueryTopNavMenu,
|
||||
[searchBarCustomization?.CustomSearchBar, AggregateQueryTopNavMenu]
|
||||
);
|
||||
|
||||
const shouldHideDefaultDataviewPicker =
|
||||
!!searchBarCustomization?.CustomDataViewPicker || !!searchBarCustomization?.hideDataViewPicker;
|
||||
|
||||
const dataViewPickerProps: DataViewPickerProps = {
|
||||
trigger: {
|
||||
label: dataView?.getName() || '',
|
||||
|
@ -201,13 +213,6 @@ export const DiscoverTopNav = ({
|
|||
[services, stateContainer]
|
||||
);
|
||||
|
||||
const searchBarCustomization = useDiscoverCustomization('search_bar');
|
||||
|
||||
const SearchBar = useMemo(
|
||||
() => searchBarCustomization?.CustomSearchBar ?? AggregateQueryTopNavMenu,
|
||||
[searchBarCustomization?.CustomSearchBar, AggregateQueryTopNavMenu]
|
||||
);
|
||||
|
||||
return (
|
||||
<SearchBar
|
||||
appName="discover"
|
||||
|
@ -231,7 +236,7 @@ export const DiscoverTopNav = ({
|
|||
) : undefined
|
||||
}
|
||||
dataViewPickerComponentProps={
|
||||
searchBarCustomization?.CustomDataViewPicker ? undefined : dataViewPickerProps
|
||||
shouldHideDefaultDataviewPicker ? undefined : dataViewPickerProps
|
||||
}
|
||||
displayStyle="detached"
|
||||
textBasedLanguageModeErrors={
|
||||
|
|
|
@ -15,4 +15,5 @@ export interface SearchBarCustomization {
|
|||
CustomDataViewPicker?: ComponentType;
|
||||
PrependFilterBar?: ComponentType;
|
||||
CustomSearchBar?: ComponentType<TopNavMenuProps<AggregateQuery>>;
|
||||
hideDataViewPicker?: boolean;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { LIVE_QUERY_EDITOR } from '../../screens/live_query';
|
||||
import { OSQUERY_FLYOUT_BODY_EDITOR } from '../../screens/live_query';
|
||||
import {
|
||||
cleanupCase,
|
||||
cleanupPack,
|
||||
|
@ -79,7 +79,7 @@ describe(
|
|||
cy.getBySel('osquery-action-item').click();
|
||||
cy.contains(/^\d+ agen(t|ts) selected/);
|
||||
cy.contains('Run a set of queries in a pack').click();
|
||||
cy.get(LIVE_QUERY_EDITOR).should('not.exist');
|
||||
cy.get(OSQUERY_FLYOUT_BODY_EDITOR).should('not.exist');
|
||||
cy.getBySel('select-live-pack').click().type(`${packName}{downArrow}{enter}`);
|
||||
submitQuery();
|
||||
cy.get('[aria-label="Add to Case"]').first().click();
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
takeOsqueryActionWithParams,
|
||||
} from '../../tasks/live_query';
|
||||
import { ServerlessRoleName } from '../../support/roles';
|
||||
import { OSQUERY_FLYOUT_BODY_EDITOR } from '../../screens/live_query';
|
||||
|
||||
describe(
|
||||
'Alert Event Details - dynamic params',
|
||||
|
@ -43,12 +44,14 @@ describe(
|
|||
it('should substitute parameters in investigation guide', () => {
|
||||
cy.getBySel('expand-event').first().click();
|
||||
cy.getBySel('securitySolutionFlyoutInvestigationGuideButton').click();
|
||||
cy.contains('Get processes').click();
|
||||
cy.getBySel('flyout-body-osquery').within(() => {
|
||||
cy.contains("SELECT * FROM os_version where name='Ubuntu';");
|
||||
cy.contains('host.os.platform');
|
||||
cy.contains('platform');
|
||||
});
|
||||
// Flakes at times if the button is only clicked once
|
||||
cy.contains('Get processes').should('be.visible').dblclick({ force: true });
|
||||
// Cypress can properly reads the fields when the codeEditor is interacted with first
|
||||
// This is probably due to the tokenization of the fields when it's inactive
|
||||
cy.get(OSQUERY_FLYOUT_BODY_EDITOR).click();
|
||||
cy.getBySel('flyout-body-osquery').contains("SELECT * FROM os_version where name='Ubuntu';");
|
||||
cy.getBySel('flyout-body-osquery').contains('host.os.platform');
|
||||
cy.getBySel('flyout-body-osquery').contains('platform');
|
||||
});
|
||||
|
||||
// response-actions-notification doesn't exist in expandable flyout
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
export const AGENT_FIELD = '[data-test-subj="comboBoxInput"]';
|
||||
export const ALL_AGENTS_OPTION = '[title="All agents"]';
|
||||
export const LIVE_QUERY_EDITOR = '.kibanaCodeEditor';
|
||||
export const OSQUERY_FLYOUT_BODY_EDITOR =
|
||||
'[data-test-subj="flyout-body-osquery"] .kibanaCodeEditor';
|
||||
export const SUBMIT_BUTTON = '#submit-button';
|
||||
|
||||
export const RESULTS_TABLE = 'osqueryResultsTable';
|
||||
|
|
|
@ -108,7 +108,7 @@ export const allowedExperimentalValues = Object.freeze({
|
|||
* Enables Discover embedded within timeline
|
||||
*
|
||||
* */
|
||||
discoverInTimeline: false,
|
||||
discoverInTimeline: true,
|
||||
|
||||
/**
|
||||
* disables ES|QL rules
|
||||
|
|
|
@ -7,22 +7,22 @@
|
|||
|
||||
export const useDiscoverInTimelineActions = () => {
|
||||
return {
|
||||
resetDiscoverAppState: jest.fn(),
|
||||
resetDiscoverAppState: jest.fn().mockResolvedValue(true),
|
||||
restoreDiscoverAppStateFromSavedSearch: jest.fn(),
|
||||
updateSavedSearch: jest.fn(),
|
||||
getAppStateFromSavedSearch: jest.fn(),
|
||||
defaultDiscoverAppState: {
|
||||
getDefaultDiscoverAppState: () => ({
|
||||
query: {
|
||||
query: '',
|
||||
language: 'kuery',
|
||||
language: 'esql',
|
||||
},
|
||||
sort: [['@timestamp', 'desc']],
|
||||
columns: [],
|
||||
index: 'security-solution-default',
|
||||
interval: 'auto',
|
||||
filters: [],
|
||||
hideChart: true,
|
||||
hideChart: false,
|
||||
grid: {},
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -25,8 +25,12 @@ import { createStore } from '../../store';
|
|||
import { TimelineId } from '../../../../common/types';
|
||||
import type { ComponentType, FC, PropsWithChildren } from 'react';
|
||||
import React from 'react';
|
||||
import type { DataView } from '@kbn/data-views-plugin/common';
|
||||
import TestRenderer from 'react-test-renderer';
|
||||
|
||||
const mockDiscoverStateContainerRef = {
|
||||
const { act } = TestRenderer;
|
||||
|
||||
let mockDiscoverStateContainerRef = {
|
||||
current: discoverPluginMock.getDiscoverStateMock({}),
|
||||
};
|
||||
|
||||
|
@ -64,6 +68,9 @@ const getTestProviderWithCustomState = (state: State = mockState) => {
|
|||
};
|
||||
|
||||
const renderTestHook = (customWrapper: ComponentType = getTestProviderWithCustomState()) => {
|
||||
mockDiscoverStateContainerRef = {
|
||||
current: discoverPluginMock.getDiscoverStateMock({}),
|
||||
};
|
||||
return renderHook(() => useDiscoverInTimelineActions(mockDiscoverStateContainerRef), {
|
||||
wrapper: customWrapper,
|
||||
});
|
||||
|
@ -120,6 +127,13 @@ export const savedSearchMock = {
|
|||
|
||||
const startServicesMock = createStartServicesMock();
|
||||
|
||||
startServicesMock.dataViews.get = jest.fn(
|
||||
async () =>
|
||||
({
|
||||
getIndexPattern: jest.fn(),
|
||||
} as unknown as DataView)
|
||||
);
|
||||
|
||||
describe('useDiscoverInTimelineActions', () => {
|
||||
beforeEach(() => {
|
||||
(useKibana as jest.Mock).mockImplementation(() => ({
|
||||
|
@ -188,15 +202,15 @@ describe('useDiscoverInTimelineActions', () => {
|
|||
describe('resetDiscoverAppState', () => {
|
||||
it('should reset Discover AppState to a default state', async () => {
|
||||
const { result, waitFor } = renderTestHook();
|
||||
result.current.resetDiscoverAppState();
|
||||
await result.current.resetDiscoverAppState();
|
||||
await waitFor(() => {
|
||||
const appState = mockDiscoverStateContainerRef.current.appState.getState();
|
||||
expect(appState).toMatchObject(result.current.defaultDiscoverAppState);
|
||||
expect(appState).toMatchObject(result.current.getDefaultDiscoverAppState());
|
||||
});
|
||||
});
|
||||
it('should reset Discover time to a default state', async () => {
|
||||
const { result, waitFor } = renderTestHook();
|
||||
result.current.resetDiscoverAppState();
|
||||
await result.current.resetDiscoverAppState();
|
||||
await waitFor(() => {
|
||||
const globalState = mockDiscoverStateContainerRef.current.globalState.get();
|
||||
expect(globalState).toMatchObject({ time: { from: 'now-15m', to: 'now' } });
|
||||
|
@ -206,7 +220,9 @@ describe('useDiscoverInTimelineActions', () => {
|
|||
describe('updateSavedSearch', () => {
|
||||
it('should add defaults to the savedSearch before updating saved search', async () => {
|
||||
const { result } = renderTestHook();
|
||||
await result.current.updateSavedSearch(savedSearchMock, TimelineId.active);
|
||||
await act(async () => {
|
||||
await result.current.updateSavedSearch(savedSearchMock, TimelineId.active);
|
||||
});
|
||||
|
||||
expect(startServicesMock.savedSearch.save).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
|
@ -242,7 +258,9 @@ describe('useDiscoverInTimelineActions', () => {
|
|||
|
||||
const LocalTestProvider = getTestProviderWithCustomState(localMockState);
|
||||
const { result } = renderTestHook(LocalTestProvider);
|
||||
await result.current.updateSavedSearch(savedSearchMock, TimelineId.active);
|
||||
await act(async () => {
|
||||
await result.current.updateSavedSearch(savedSearchMock, TimelineId.active);
|
||||
});
|
||||
|
||||
expect(startServicesMock.savedSearch.save).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
|
|
|
@ -40,7 +40,11 @@ export const useDiscoverInTimelineActions = (
|
|||
const { addError } = useAppToasts();
|
||||
|
||||
const {
|
||||
services: { customDataService: discoverDataService, savedSearch: savedSearchService },
|
||||
services: {
|
||||
customDataService: discoverDataService,
|
||||
savedSearch: savedSearchService,
|
||||
dataViews: dataViewService,
|
||||
},
|
||||
} = useKibana();
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
@ -69,18 +73,23 @@ export const useDiscoverInTimelineActions = (
|
|||
},
|
||||
});
|
||||
|
||||
const defaultDiscoverAppState: DiscoverAppState = useMemo(() => {
|
||||
const getDefaultDiscoverAppState: () => Promise<DiscoverAppState> = useCallback(async () => {
|
||||
const localDataViewId = dataViewId ?? 'security-solution-default';
|
||||
|
||||
const dataView = await dataViewService.get(localDataViewId);
|
||||
|
||||
return {
|
||||
query: discoverDataService.query.queryString.getDefaultQuery(),
|
||||
query: {
|
||||
esql: dataView ? `from ${dataView.getIndexPattern()} | limit 10` : '',
|
||||
},
|
||||
sort: [['@timestamp', 'desc']],
|
||||
columns: [],
|
||||
index: dataViewId ?? 'security-solution-default',
|
||||
interval: 'auto',
|
||||
filters: [],
|
||||
hideChart: true,
|
||||
grid: {},
|
||||
};
|
||||
}, [discoverDataService, dataViewId]);
|
||||
}, [dataViewService, dataViewId]);
|
||||
|
||||
/*
|
||||
* generates Appstate from a given saved Search object
|
||||
|
@ -123,13 +132,14 @@ export const useDiscoverInTimelineActions = (
|
|||
* resets discover state to a default value
|
||||
*
|
||||
* */
|
||||
const resetDiscoverAppState = useCallback(() => {
|
||||
const resetDiscoverAppState = useCallback(async () => {
|
||||
const defaultDiscoverAppState = await getDefaultDiscoverAppState();
|
||||
discoverStateContainer.current?.appState.set(defaultDiscoverAppState);
|
||||
discoverStateContainer.current?.globalState.set({
|
||||
...discoverStateContainer.current?.globalState.get(),
|
||||
time: defaultDiscoverTimeRange,
|
||||
});
|
||||
}, [defaultDiscoverAppState, discoverStateContainer]);
|
||||
}, [getDefaultDiscoverAppState, discoverStateContainer]);
|
||||
|
||||
const persistSavedSearch = useCallback(
|
||||
async (savedSearch: SavedSearch, savedSearchOption: SaveSavedSearchOptions) => {
|
||||
|
@ -220,14 +230,14 @@ export const useDiscoverInTimelineActions = (
|
|||
restoreDiscoverAppStateFromSavedSearch,
|
||||
updateSavedSearch,
|
||||
getAppStateFromSavedSearch,
|
||||
defaultDiscoverAppState,
|
||||
getDefaultDiscoverAppState,
|
||||
}),
|
||||
[
|
||||
resetDiscoverAppState,
|
||||
restoreDiscoverAppStateFromSavedSearch,
|
||||
updateSavedSearch,
|
||||
getAppStateFromSavedSearch,
|
||||
defaultDiscoverAppState,
|
||||
getDefaultDiscoverAppState,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -6,15 +6,18 @@
|
|||
*/
|
||||
|
||||
import type { CustomizationCallback } from '@kbn/discover-plugin/public';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
import { useGetStatefulQueryBar } from '../use_get_stateful_query_bar';
|
||||
|
||||
export const useSearchBarCustomizations = () => {
|
||||
const { CustomStatefulTopNavKqlQueryBar } = useGetStatefulQueryBar();
|
||||
const isDiscoverInTimelineEnabled = useIsExperimentalFeatureEnabled('discoverInTimeline');
|
||||
|
||||
const setSearchBarCustomizations: CustomizationCallback = ({ customizations }) => {
|
||||
customizations.set({
|
||||
id: 'search_bar',
|
||||
CustomSearchBar: CustomStatefulTopNavKqlQueryBar,
|
||||
hideDataViewPicker: isDiscoverInTimelineEnabled,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import { useSearchBarCustomizations } from './use_search_bar_customizations';
|
|||
|
||||
export const useSetDiscoverCustomizationCallbacks = (): CustomizationCallback[] => {
|
||||
const searchBarCustomizationCallback = useSearchBarCustomizations();
|
||||
|
||||
const histogramCustomizationCallback = useHistogramCustomization();
|
||||
|
||||
return [searchBarCustomizationCallback, histogramCustomizationCallback];
|
||||
|
|
|
@ -72,6 +72,7 @@ export const DiscoverTabContent: FC<DiscoverTabContentProps> = ({ timelineId })
|
|||
updateSavedSearch,
|
||||
restoreDiscoverAppStateFromSavedSearch,
|
||||
resetDiscoverAppState,
|
||||
getDefaultDiscoverAppState,
|
||||
} = useDiscoverInTimelineContext();
|
||||
|
||||
const {
|
||||
|
@ -126,9 +127,10 @@ export const DiscoverTabContent: FC<DiscoverTabContentProps> = ({ timelineId })
|
|||
if (!savedSearchById) {
|
||||
// nothing to restore if savedSearchById is null
|
||||
if (status === 'draft') {
|
||||
resetDiscoverAppState();
|
||||
resetDiscoverAppState().then(() => {
|
||||
setSavedSearchLoaded(true);
|
||||
});
|
||||
}
|
||||
setSavedSearchLoaded(true);
|
||||
return;
|
||||
}
|
||||
restoreDiscoverAppStateFromSavedSearch(savedSearchById);
|
||||
|
@ -227,15 +229,13 @@ export const DiscoverTabContent: FC<DiscoverTabContentProps> = ({ timelineId })
|
|||
savedSearchAppState = getAppStateFromSavedSearch(localSavedSearch);
|
||||
}
|
||||
|
||||
const finalAppState = savedSearchAppState?.appState ?? discoverAppState;
|
||||
const defaultDiscoverAppState = await getDefaultDiscoverAppState();
|
||||
|
||||
if (finalAppState) {
|
||||
stateContainer.appState.set(finalAppState);
|
||||
await stateContainer.appState.replaceUrlState(finalAppState);
|
||||
} else {
|
||||
// set initial dataView Id
|
||||
if (dataView) stateContainer.actions.setDataView(dataView);
|
||||
}
|
||||
const finalAppState =
|
||||
savedSearchAppState?.appState ?? discoverAppState ?? defaultDiscoverAppState;
|
||||
|
||||
stateContainer.appState.set(finalAppState);
|
||||
await stateContainer.appState.replaceUrlState(finalAppState);
|
||||
|
||||
const unsubscribeState = stateContainer.appState.state$.subscribe({
|
||||
next: setDiscoverAppState,
|
||||
|
@ -272,12 +272,12 @@ export const DiscoverTabContent: FC<DiscoverTabContentProps> = ({ timelineId })
|
|||
setDiscoverSavedSearchState,
|
||||
setDiscoverInternalState,
|
||||
setDiscoverAppState,
|
||||
dataView,
|
||||
setDiscoverStateContainer,
|
||||
getAppStateFromSavedSearch,
|
||||
discoverDataService.query.timefilter.timefilter,
|
||||
savedSearchId,
|
||||
savedSearchService,
|
||||
getDefaultDiscoverAppState,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiBadge, EuiSkeletonText, EuiTabs, EuiTab } from '@elastic/eui';
|
||||
import { EuiBadge, EuiSkeletonText, EuiTabs, EuiTab, EuiBetaBadge } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { Assistant } from '@kbn/elastic-assistant';
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
|
@ -14,6 +14,7 @@ import React, { lazy, memo, Suspense, useCallback, useEffect, useMemo, useState
|
|||
import { useDispatch } from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useAssistantTelemetry } from '../../../../assistant/use_assistant_telemetry';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
|
||||
import { useConversationStore } from '../../../../assistant/use_conversation_store';
|
||||
|
@ -44,6 +45,7 @@ import * as i18n from './translations';
|
|||
import { useLicense } from '../../../../common/hooks/use_license';
|
||||
import { TIMELINE_CONVERSATION_TITLE } from '../../../../assistant/content/conversations/translations';
|
||||
import { initializeTimelineSettings } from '../../../store/timeline/actions';
|
||||
import { DISCOVER_ESQL_IN_TIMELINE_TECHNICAL_PREVIEW } from './translations';
|
||||
|
||||
const HideShowContainer = styled.div.attrs<{ $isVisible: boolean; isOverflowYScroll: boolean }>(
|
||||
({ $isVisible = false, isOverflowYScroll = false }) => ({
|
||||
|
@ -248,8 +250,18 @@ const CountBadge = styled(EuiBadge)`
|
|||
margin-left: ${({ theme }) => theme.eui.euiSizeS};
|
||||
`;
|
||||
|
||||
const StyledEuiBetaBadge = styled(EuiBetaBadge)`
|
||||
vertical-align: middle;
|
||||
margin-left: ${({ theme }) => theme.eui.euiSizeS};
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledEuiTab = styled(EuiTab)`
|
||||
.euiTab__content {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
white-space: pre;
|
||||
|
@ -388,6 +400,28 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({
|
|||
<span>{i18n.QUERY_TAB}</span>
|
||||
{showTimeline && <TimelineEventsCountBadge />}
|
||||
</StyledEuiTab>
|
||||
{isDiscoverInTimelineEnabled && (
|
||||
<StyledEuiTab
|
||||
data-test-subj={`timelineTabs-${TimelineTabs.discover}`}
|
||||
onClick={setDiscoverAsActiveTab}
|
||||
isSelected={activeTab === TimelineTabs.discover}
|
||||
disabled={false}
|
||||
key={TimelineTabs.discover}
|
||||
>
|
||||
<span>{i18n.DISCOVER_ESQL_IN_TIMELINE_TAB}</span>
|
||||
<StyledEuiBetaBadge
|
||||
label={DISCOVER_ESQL_IN_TIMELINE_TECHNICAL_PREVIEW}
|
||||
size="s"
|
||||
iconType="beaker"
|
||||
tooltipContent={
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.timeline.tabs.discoverEsqlInTimeline.technicalPreviewTooltip"
|
||||
defaultMessage="This functionality is in technical preview and may be changed or removed completely in a future release. Elastic will take a best effort approach to fix any issues, but features in technical preview are not subject to the support SLA of official GA features."
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</StyledEuiTab>
|
||||
)}
|
||||
{timelineType === TimelineType.default && (
|
||||
<StyledEuiTab
|
||||
data-test-subj={`timelineTabs-${TimelineTabs.eql}`}
|
||||
|
@ -459,17 +493,6 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({
|
|||
<span>{i18n.SECURITY_ASSISTANT}</span>
|
||||
</StyledEuiTab>
|
||||
)}
|
||||
{isDiscoverInTimelineEnabled && (
|
||||
<StyledEuiTab
|
||||
data-test-subj={`timelineTabs-${TimelineTabs.discover}`}
|
||||
onClick={setDiscoverAsActiveTab}
|
||||
isSelected={activeTab === TimelineTabs.discover}
|
||||
disabled={false}
|
||||
key={TimelineTabs.discover}
|
||||
>
|
||||
<span>{i18n.DISCOVER_IN_TIMELINE_TAB}</span>
|
||||
</StyledEuiTab>
|
||||
)}
|
||||
</EuiTabs>
|
||||
)}
|
||||
|
||||
|
|
|
@ -46,10 +46,17 @@ export const SECURITY_ASSISTANT = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const DISCOVER_IN_TIMELINE_TAB = i18n.translate(
|
||||
'xpack.securitySolution.timeline.tabs.discoverInTimeline',
|
||||
export const DISCOVER_ESQL_IN_TIMELINE_TAB = i18n.translate(
|
||||
'xpack.securitySolution.timeline.tabs.discoverEsqlInTimeline',
|
||||
{
|
||||
defaultMessage: 'Discover',
|
||||
defaultMessage: 'ES|QL',
|
||||
}
|
||||
);
|
||||
|
||||
export const DISCOVER_ESQL_IN_TIMELINE_TECHNICAL_PREVIEW = i18n.translate(
|
||||
'xpack.securitySolution.timeline.tabs.discoverEsqlInTimeline.technicalPreviewLabel',
|
||||
{
|
||||
defaultMessage: 'Technical Preview',
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import { grantClipboardReadPerm } from '../../../../tasks/common/clipboard';
|
|||
import {
|
||||
DISCOVER_CELL_ACTIONS,
|
||||
DISCOVER_CONTAINER,
|
||||
DISCOVER_FILTER_BADGES,
|
||||
GET_DISCOVER_DATA_GRID_CELL,
|
||||
} from '../../../../screens/discover';
|
||||
import { waitForDiscoverGridToLoad } from '../../../../tasks/discover';
|
||||
|
@ -28,7 +27,7 @@ describe.skip(
|
|||
`Discover Datagrid Cell Actions`,
|
||||
{
|
||||
env: { ftrConfig: { enableExperimental: ['discoverInTimeline'] } },
|
||||
tags: ['@serverless', '@brokenInServerless'],
|
||||
tags: ['@ess'],
|
||||
},
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
|
@ -39,35 +38,6 @@ describe.skip(
|
|||
updateDateRangeInLocalDatePickers(DISCOVER_CONTAINER, INITIAL_START_DATE, INITIAL_END_DATE);
|
||||
waitForDiscoverGridToLoad();
|
||||
});
|
||||
it('Filter for', () => {
|
||||
cy.get(GET_DISCOVER_DATA_GRID_CELL(TIMESTAMP_COLUMN_NAME, 0)).then((sub) => {
|
||||
const selectedTimestamp = sub.text();
|
||||
cy.get(GET_DISCOVER_DATA_GRID_CELL(TIMESTAMP_COLUMN_NAME, 0)).realHover();
|
||||
cy.get(DISCOVER_CELL_ACTIONS.FILTER_FOR).should('be.visible').trigger('click');
|
||||
|
||||
cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1);
|
||||
cy.get(DISCOVER_FILTER_BADGES)
|
||||
.first()
|
||||
.should(
|
||||
'have.text',
|
||||
`${TIMESTAMP_COLUMN_NAME}: ${selectedTimestamp} to ${selectedTimestamp}`
|
||||
);
|
||||
});
|
||||
});
|
||||
it('Filter out', { tags: ['@brokenInServerless'] }, () => {
|
||||
cy.get(GET_DISCOVER_DATA_GRID_CELL(TIMESTAMP_COLUMN_NAME, 0)).then((sub) => {
|
||||
const selectedTimestamp = sub.text();
|
||||
cy.get(GET_DISCOVER_DATA_GRID_CELL(TIMESTAMP_COLUMN_NAME, 0)).realHover();
|
||||
cy.get(DISCOVER_CELL_ACTIONS.FILTER_OUT).should('be.visible').trigger('click');
|
||||
cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1);
|
||||
cy.get(DISCOVER_FILTER_BADGES)
|
||||
.first()
|
||||
.should(
|
||||
'have.text',
|
||||
`NOT ${TIMESTAMP_COLUMN_NAME}: ${selectedTimestamp} to ${selectedTimestamp}`
|
||||
);
|
||||
});
|
||||
});
|
||||
// @TODO: copy is incredibly flaky although it is written same strategy as above tests
|
||||
// Need to see what is the reaosn for that. Trusting that above tests prove that `Copy`
|
||||
// will also work correctly.
|
||||
|
|
|
@ -5,19 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { fillAddFilterForm } from '../../../../tasks/search_bar';
|
||||
import {
|
||||
addDiscoverKqlQuery,
|
||||
addDiscoverEsqlQuery,
|
||||
addFieldToTable,
|
||||
openAddDiscoverFilterPopover,
|
||||
submitDiscoverSearchBar,
|
||||
switchDataViewTo,
|
||||
verifyDiscoverEsqlQuery,
|
||||
} from '../../../../tasks/discover';
|
||||
import { navigateFromHeaderTo } from '../../../../tasks/security_header';
|
||||
import {
|
||||
DISCOVER_CONTAINER,
|
||||
DISCOVER_QUERY_INPUT,
|
||||
DISCOVER_FILTER_BADGES,
|
||||
DISCOVER_DATA_VIEW_SWITCHER,
|
||||
GET_DISCOVER_DATA_GRID_CELL_HEADER,
|
||||
} from '../../../../screens/discover';
|
||||
|
@ -34,14 +30,14 @@ import { ALERTS, CSP_FINDINGS } from '../../../../screens/security_header';
|
|||
|
||||
const INITIAL_START_DATE = 'Jan 18, 2021 @ 20:33:29.186';
|
||||
const INITIAL_END_DATE = 'Jan 19, 2024 @ 20:33:29.186';
|
||||
const DEFAULT_ESQL_QUERY =
|
||||
'from .alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-* | limit 10';
|
||||
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/165663
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/165747
|
||||
describe(
|
||||
'Discover State',
|
||||
{
|
||||
env: { ftrConfig: { enableExperimental: ['discoverInTimeline'] } },
|
||||
tags: ['@ess', '@serverless', '@brokenInServerless'],
|
||||
tags: ['@ess'],
|
||||
},
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
|
@ -51,36 +47,22 @@ describe(
|
|||
gotToDiscoverTab();
|
||||
updateDateRangeInLocalDatePickers(DISCOVER_CONTAINER, INITIAL_START_DATE, INITIAL_END_DATE);
|
||||
});
|
||||
it('should remember kql query when navigating away and back to discover ', () => {
|
||||
const kqlQuery = '_id:*';
|
||||
addDiscoverKqlQuery(kqlQuery);
|
||||
it('should not allow the dataview to be changed', () => {
|
||||
cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).should('not.exist');
|
||||
});
|
||||
it('should have the default esql query on load', () => {
|
||||
verifyDiscoverEsqlQuery(DEFAULT_ESQL_QUERY);
|
||||
});
|
||||
it('should remember esql query when navigating away and back to discover ', () => {
|
||||
const esqlQuery = 'from auditbeat-* | limit 5';
|
||||
addDiscoverEsqlQuery(esqlQuery);
|
||||
submitDiscoverSearchBar();
|
||||
navigateFromHeaderTo(CSP_FINDINGS);
|
||||
navigateFromHeaderTo(ALERTS);
|
||||
openActiveTimeline();
|
||||
gotToDiscoverTab();
|
||||
cy.get(DISCOVER_QUERY_INPUT).should('have.text', kqlQuery);
|
||||
});
|
||||
it('should remember filters when navigating away and back to discover ', () => {
|
||||
openAddDiscoverFilterPopover();
|
||||
fillAddFilterForm({
|
||||
key: 'agent.type',
|
||||
value: 'winlogbeat',
|
||||
});
|
||||
navigateFromHeaderTo(CSP_FINDINGS);
|
||||
navigateFromHeaderTo(ALERTS);
|
||||
openActiveTimeline();
|
||||
gotToDiscoverTab();
|
||||
cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1);
|
||||
});
|
||||
it.skip('should remember dataView when navigating away and back to discover ', () => {
|
||||
const dataviewName = '.kibana-event-log';
|
||||
switchDataViewTo(dataviewName);
|
||||
navigateFromHeaderTo(CSP_FINDINGS);
|
||||
navigateFromHeaderTo(ALERTS);
|
||||
openActiveTimeline();
|
||||
gotToDiscoverTab();
|
||||
cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).should('contain.text', dataviewName);
|
||||
|
||||
verifyDiscoverEsqlQuery(esqlQuery);
|
||||
});
|
||||
it('should remember columns when navigating away and back to discover ', () => {
|
||||
addFieldToTable('host.name');
|
||||
|
|
|
@ -13,13 +13,10 @@ import {
|
|||
navigateFromKibanaCollapsibleTo,
|
||||
openKibanaNavigation,
|
||||
} from '../../../../tasks/kibana_navigation';
|
||||
import { fillAddFilterForm } from '../../../../tasks/search_bar';
|
||||
import {
|
||||
addDiscoverKqlQuery,
|
||||
addDiscoverEsqlQuery,
|
||||
addFieldToTable,
|
||||
openAddDiscoverFilterPopover,
|
||||
switchDataViewTo,
|
||||
switchDataViewToESQL,
|
||||
verifyDiscoverEsqlQuery,
|
||||
} from '../../../../tasks/discover';
|
||||
import {
|
||||
GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON,
|
||||
|
@ -29,8 +26,6 @@ import { ALERTS_URL } from '../../../../urls/navigation';
|
|||
import {
|
||||
DISCOVER_CONTAINER,
|
||||
DISCOVER_DATA_VIEW_SWITCHER,
|
||||
DISCOVER_FILTER_BADGES,
|
||||
DISCOVER_QUERY_INPUT,
|
||||
GET_DISCOVER_DATA_GRID_CELL_HEADER,
|
||||
} from '../../../../screens/discover';
|
||||
import { updateDateRangeInLocalDatePickers } from '../../../../tasks/date_picker';
|
||||
|
@ -63,6 +58,7 @@ const TIMELINE_PATCH_REQ = 'TIMELINE_PATCH_REQ';
|
|||
|
||||
const TIMELINE_RESPONSE_SAVED_OBJECT_ID_PATH =
|
||||
'response.body.data.persistTimeline.timeline.savedObjectId';
|
||||
const esqlQuery = 'from auditbeat-* | where ecs.version == "8.0.0"';
|
||||
|
||||
describe(
|
||||
'Discover Timeline State Integration',
|
||||
|
@ -126,19 +122,11 @@ describe(
|
|||
);
|
||||
});
|
||||
it('should save/restore discover dataview/timerange/filter/query/columns when saving/resoring timeline', () => {
|
||||
const dataviewName = '.kibana-event-log';
|
||||
const timelineSuffix = Date.now();
|
||||
const timelineName = `DataView timeline-${timelineSuffix}`;
|
||||
const kqlQuery = '_id:*';
|
||||
const column1 = 'event.category';
|
||||
const column2 = 'ecs.version';
|
||||
switchDataViewTo(dataviewName);
|
||||
addDiscoverKqlQuery(kqlQuery);
|
||||
openAddDiscoverFilterPopover();
|
||||
fillAddFilterForm({
|
||||
key: 'ecs.version',
|
||||
value: '1.8.0',
|
||||
});
|
||||
addDiscoverEsqlQuery(esqlQuery);
|
||||
addFieldToTable(column1);
|
||||
addFieldToTable(column2);
|
||||
|
||||
|
@ -155,10 +143,7 @@ describe(
|
|||
openTimelineById(timelineId);
|
||||
cy.get(LOADING_INDICATOR).should('not.exist');
|
||||
gotToDiscoverTab();
|
||||
cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).should('contain.text', dataviewName);
|
||||
cy.get(DISCOVER_QUERY_INPUT).should('have.text', kqlQuery);
|
||||
cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1);
|
||||
cy.get(DISCOVER_FILTER_BADGES).should('contain.text', 'ecs.version: 1.8.0');
|
||||
verifyDiscoverEsqlQuery(esqlQuery);
|
||||
cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER(column1)).should('exist');
|
||||
cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER(column2)).should('exist');
|
||||
cy.get(GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON(DISCOVER_CONTAINER)).should(
|
||||
|
@ -168,19 +153,11 @@ describe(
|
|||
});
|
||||
});
|
||||
it('should save/restore discover dataview/timerange/filter/query/columns when timeline is opened via url', () => {
|
||||
const dataviewName = '.kibana-event-log';
|
||||
const timelineSuffix = Date.now();
|
||||
const timelineName = `DataView timeline-${timelineSuffix}`;
|
||||
const kqlQuery = '_id:*';
|
||||
const column1 = 'event.category';
|
||||
const column2 = 'ecs.version';
|
||||
switchDataViewTo(dataviewName);
|
||||
addDiscoverKqlQuery(kqlQuery);
|
||||
openAddDiscoverFilterPopover();
|
||||
fillAddFilterForm({
|
||||
key: 'ecs.version',
|
||||
value: '1.8.0',
|
||||
});
|
||||
addDiscoverEsqlQuery(esqlQuery);
|
||||
addFieldToTable(column1);
|
||||
addFieldToTable(column2);
|
||||
|
||||
|
@ -192,10 +169,7 @@ describe(
|
|||
cy.wait(`@${TIMELINE_REQ_WITH_SAVED_SEARCH}`);
|
||||
// reload the page with the exact url
|
||||
cy.reload();
|
||||
cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).should('contain.text', dataviewName);
|
||||
cy.get(DISCOVER_QUERY_INPUT).should('have.text', kqlQuery);
|
||||
cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1);
|
||||
cy.get(DISCOVER_FILTER_BADGES).should('contain.text', 'ecs.version: 1.8.0');
|
||||
verifyDiscoverEsqlQuery(esqlQuery);
|
||||
cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER(column1)).should('exist');
|
||||
cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER(column2)).should('exist');
|
||||
cy.get(GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON(DISCOVER_CONTAINER)).should(
|
||||
|
@ -207,7 +181,6 @@ describe(
|
|||
it('should save/restore discover ES|QL when saving timeline', () => {
|
||||
const timelineSuffix = Date.now();
|
||||
const timelineName = `ES|QL timeline-${timelineSuffix}`;
|
||||
switchDataViewToESQL();
|
||||
addNameToTimeline(timelineName);
|
||||
cy.wait(`@${TIMELINE_PATCH_REQ}`)
|
||||
.its(TIMELINE_RESPONSE_SAVED_OBJECT_ID_PATH)
|
||||
|
@ -220,7 +193,7 @@ describe(
|
|||
openTimelineById(timelineId);
|
||||
cy.get(LOADING_INDICATOR).should('not.exist');
|
||||
gotToDiscoverTab();
|
||||
cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).should('contain.text', 'ES|QL');
|
||||
cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -235,8 +208,7 @@ describe(
|
|||
it('should save discover saved search with `Security Solution` tag', () => {
|
||||
const timelineSuffix = Date.now();
|
||||
const timelineName = `SavedObject timeline-${timelineSuffix}`;
|
||||
const kqlQuery = '_id: *';
|
||||
addDiscoverKqlQuery(kqlQuery);
|
||||
addDiscoverEsqlQuery(esqlQuery);
|
||||
addNameToTimeline(timelineName);
|
||||
cy.wait(`@${TIMELINE_REQ_WITH_SAVED_SEARCH}`);
|
||||
openKibanaNavigation();
|
||||
|
@ -257,8 +229,7 @@ describe(
|
|||
it('should rename the saved search on timeline rename', () => {
|
||||
const timelineSuffix = Date.now();
|
||||
const timelineName = `Rename timeline-${timelineSuffix}`;
|
||||
const kqlQuery = '_id: *';
|
||||
addDiscoverKqlQuery(kqlQuery);
|
||||
addDiscoverEsqlQuery(esqlQuery);
|
||||
|
||||
addNameToTimeline(timelineName);
|
||||
cy.wait(`@${TIMELINE_PATCH_REQ}`)
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON } from '../../../../screens/date_picker';
|
||||
import { fillAddFilterForm, fillAddFilterFormAsQueryDSL } from '../../../../tasks/search_bar';
|
||||
import {
|
||||
setStartDate,
|
||||
updateDateRangeInLocalDatePickers,
|
||||
|
@ -14,20 +13,15 @@ import {
|
|||
} from '../../../../tasks/date_picker';
|
||||
import {
|
||||
DISCOVER_CONTAINER,
|
||||
DISCOVER_NO_RESULTS,
|
||||
DISCOVER_RESULT_HITS,
|
||||
DISCOVER_FILTER_BADGES,
|
||||
DISCOVER_QUERY_INPUT,
|
||||
GET_DISCOVER_DATA_GRID_CELL_HEADER,
|
||||
DISCOVER_DATA_VIEW_SWITCHER,
|
||||
DISCOVER_ESQL_INPUT_TEXT_CONTAINER,
|
||||
} from '../../../../screens/discover';
|
||||
import {
|
||||
addDiscoverKqlQuery,
|
||||
switchDataViewTo,
|
||||
addDiscoverEsqlQuery,
|
||||
submitDiscoverSearchBar,
|
||||
openAddDiscoverFilterPopover,
|
||||
addFieldToTable,
|
||||
createAdHocDataView,
|
||||
convertNBSPToSP,
|
||||
} from '../../../../tasks/discover';
|
||||
import { createNewTimeline, gotToDiscoverTab } from '../../../../tasks/timeline';
|
||||
import { login } from '../../../../tasks/login';
|
||||
|
@ -37,13 +31,14 @@ import { ALERTS_URL } from '../../../../urls/navigation';
|
|||
const INITIAL_START_DATE = 'Jan 18, 2021 @ 20:33:29.186';
|
||||
const INITIAL_END_DATE = 'Jan 19, 2024 @ 20:33:29.186';
|
||||
const NEW_START_DATE = 'Jan 18, 2023 @ 20:33:29.186';
|
||||
const esqlQuery = 'from auditbeat-* | where ecs.version == "8.0.0"';
|
||||
|
||||
// Failing: See https://github.com/elastic/kibana/issues/167186
|
||||
describe.skip(
|
||||
describe(
|
||||
'Basic discover search and filter operations',
|
||||
{
|
||||
env: { ftrConfig: { enableExperimental: ['discoverInTimeline'] } },
|
||||
tags: ['@ess', '@serverless'],
|
||||
tags: ['@ess'],
|
||||
},
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
|
@ -53,76 +48,34 @@ describe.skip(
|
|||
gotToDiscoverTab();
|
||||
updateDateRangeInLocalDatePickers(DISCOVER_CONTAINER, INITIAL_START_DATE, INITIAL_END_DATE);
|
||||
});
|
||||
it('should change data when dataView is changed', () => {
|
||||
switchDataViewTo('.kibana-event-log');
|
||||
cy.get(DISCOVER_RESULT_HITS).should('have.text', '1');
|
||||
});
|
||||
|
||||
it('should show data according to kql query', () => {
|
||||
const kqlQuery = '_id:"invalid"';
|
||||
addDiscoverKqlQuery(kqlQuery);
|
||||
it('should show data according to esql query', () => {
|
||||
addDiscoverEsqlQuery(`${esqlQuery} | limit 1`);
|
||||
submitDiscoverSearchBar();
|
||||
cy.get(DISCOVER_NO_RESULTS).should('be.visible');
|
||||
cy.get(DISCOVER_RESULT_HITS).should('have.text', 1);
|
||||
});
|
||||
it('should show correct data according to filter applied', () => {
|
||||
openAddDiscoverFilterPopover();
|
||||
fillAddFilterForm({
|
||||
key: 'agent.type',
|
||||
value: 'winlogbeat',
|
||||
});
|
||||
cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1);
|
||||
cy.get(DISCOVER_RESULT_HITS).should('have.text', '1');
|
||||
});
|
||||
it('should show correct data according to query DSL', () => {
|
||||
const query = {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
term: {
|
||||
'agent.type': 'winlogbeat',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
openAddDiscoverFilterPopover();
|
||||
fillAddFilterFormAsQueryDSL(JSON.stringify(query));
|
||||
cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1);
|
||||
cy.get(DISCOVER_RESULT_HITS).should('have.text', '1');
|
||||
});
|
||||
|
||||
it('should be able to create ad-hoc dataview without saving', () => {
|
||||
const adHocDVName = 'adHocDataView';
|
||||
const indexPattern = 'audit';
|
||||
createAdHocDataView(adHocDVName, indexPattern);
|
||||
cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).should('contain.text', adHocDVName);
|
||||
});
|
||||
|
||||
it('should be able to add fields to the table', () => {
|
||||
addFieldToTable('host.name');
|
||||
cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER('host.name')).should('be.visible');
|
||||
addFieldToTable('user.name');
|
||||
cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER('host.name')).should('be.visible');
|
||||
cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER('user.name')).should('be.visible');
|
||||
});
|
||||
|
||||
context('navigation', () => {
|
||||
it('should remove the filter when back is pressed after adding a filter', () => {
|
||||
openAddDiscoverFilterPopover();
|
||||
fillAddFilterForm({
|
||||
key: 'agent.type',
|
||||
value: 'winlogbeat',
|
||||
});
|
||||
cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1);
|
||||
cy.go('back');
|
||||
cy.get(DISCOVER_FILTER_BADGES).should('not.exist');
|
||||
});
|
||||
it('should removed the query when back is pressed after adding a query', () => {
|
||||
const kqlQuery = '_id:"invalid"';
|
||||
addDiscoverKqlQuery(kqlQuery);
|
||||
addDiscoverEsqlQuery(esqlQuery);
|
||||
submitDiscoverSearchBar();
|
||||
cy.get(DISCOVER_QUERY_INPUT).should('have.text', kqlQuery);
|
||||
cy.get(DISCOVER_ESQL_INPUT_TEXT_CONTAINER).then((subj) => {
|
||||
const currentQuery = subj.text();
|
||||
const sanitizedQuery = convertNBSPToSP(currentQuery);
|
||||
expect(sanitizedQuery).to.eq(esqlQuery);
|
||||
});
|
||||
cy.go('back');
|
||||
cy.get(DISCOVER_QUERY_INPUT).should('not.have.text', kqlQuery);
|
||||
cy.get(DISCOVER_ESQL_INPUT_TEXT_CONTAINER).then((subj) => {
|
||||
const currentQuery = subj.text();
|
||||
const sanitizedQuery = convertNBSPToSP(currentQuery);
|
||||
expect(sanitizedQuery).to.not.eq(esqlQuery);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should changed the timerange to ${INITIAL_START_DATE} when back is pressed after modifying timerange from ${INITIAL_START_DATE} to ${NEW_START_DATE} `, () => {
|
||||
|
|
|
@ -26,10 +26,14 @@ export const DISCOVER_DATA_VIEW_EDITOR_FLYOUT = {
|
|||
SAVE_DATA_VIEW_BTN: getDataTestSubjectSelector('saveIndexPatternButton'),
|
||||
};
|
||||
|
||||
export const DISCOVER_QUERY_INPUT = `${DISCOVER_CONTAINER} ${getDataTestSubjectSelector(
|
||||
'unifiedQueryInput'
|
||||
export const DISCOVER_ESQL_INPUT = `${DISCOVER_CONTAINER} ${getDataTestSubjectSelector(
|
||||
'kibanaCodeEditor'
|
||||
)}`;
|
||||
|
||||
export const DISCOVER_ESQL_INPUT_TEXT_CONTAINER = `${DISCOVER_ESQL_INPUT} .view-lines`;
|
||||
|
||||
export const DISCOVER_ESQL_EDITABLE_INPUT = `${DISCOVER_ESQL_INPUT} textarea:first`;
|
||||
|
||||
export const DISCOVER_ADD_FILTER = `${DISCOVER_CONTAINER} ${getDataTestSubjectSelector(
|
||||
'addFilter'
|
||||
)}`;
|
||||
|
|
|
@ -162,7 +162,7 @@ export const setEnrichmentDates = (from?: string, to?: string) => {
|
|||
cy.get(ENRICHMENT_QUERY_END_INPUT).type(`{selectall}${to}{enter}`);
|
||||
}
|
||||
});
|
||||
cy.get(UPDATE_ENRICHMENT_RANGE_BUTTON).click();
|
||||
cy.get(UPDATE_ENRICHMENT_RANGE_BUTTON).click({ force: true });
|
||||
};
|
||||
|
||||
export const refreshAlertPageFilter = () => {
|
||||
|
|
|
@ -10,11 +10,12 @@ import {
|
|||
DISCOVER_CONTAINER,
|
||||
DISCOVER_DATA_GRID_UPDATING,
|
||||
DISCOVER_DATA_VIEW_SWITCHER,
|
||||
DISCOVER_QUERY_INPUT,
|
||||
DISCOVER_ESQL_INPUT,
|
||||
GET_DISCOVER_COLUMN_TOGGLE_BTN,
|
||||
DISCOVER_FIELD_SEARCH,
|
||||
DISCOVER_DATA_VIEW_EDITOR_FLYOUT,
|
||||
DISCOVER_FIELD_LIST_LOADING,
|
||||
DISCOVER_ESQL_EDITABLE_INPUT,
|
||||
} from '../screens/discover';
|
||||
import { GET_LOCAL_SEARCH_BAR_SUBMIT_BUTTON } from '../screens/search_bar';
|
||||
|
||||
|
@ -43,8 +44,35 @@ export const waitForDiscoverGridToLoad = () => {
|
|||
cy.get(DISCOVER_FIELD_LIST_LOADING).should('not.exist');
|
||||
};
|
||||
|
||||
export const addDiscoverKqlQuery = (kqlQuery: string) => {
|
||||
cy.get(DISCOVER_QUERY_INPUT).type(`${kqlQuery}{enter}`);
|
||||
export const selectCurrentDiscoverEsqlQuery = (discoverEsqlInput = DISCOVER_ESQL_INPUT) => {
|
||||
cy.get(discoverEsqlInput).click();
|
||||
cy.get(discoverEsqlInput).focused();
|
||||
cy.get(discoverEsqlInput).type(Cypress.platform === 'darwin' ? '{cmd+a}' : '{ctrl+a}');
|
||||
};
|
||||
|
||||
export const addDiscoverEsqlQuery = (esqlQuery: string) => {
|
||||
// ESQL input uses the monaco editor which doesn't allow for traditional input updates
|
||||
selectCurrentDiscoverEsqlQuery(DISCOVER_ESQL_EDITABLE_INPUT);
|
||||
cy.get(DISCOVER_ESQL_EDITABLE_INPUT).clear();
|
||||
cy.get(DISCOVER_ESQL_EDITABLE_INPUT).type(`${esqlQuery}`);
|
||||
cy.get(DISCOVER_ESQL_EDITABLE_INPUT).blur();
|
||||
cy.get(GET_LOCAL_SEARCH_BAR_SUBMIT_BUTTON(DISCOVER_CONTAINER)).realClick();
|
||||
};
|
||||
|
||||
export const convertNBSPToSP = (str: string) => {
|
||||
return str.replaceAll(String.fromCharCode(160), ' ');
|
||||
};
|
||||
|
||||
export const verifyDiscoverEsqlQuery = (esqlQueryToVerify: string) => {
|
||||
// We select the query first as multi-line queries do not render fully unless all the text is selected
|
||||
selectCurrentDiscoverEsqlQuery();
|
||||
/**
|
||||
* When selected all visual spaces actually render the middot character, so we replace the spaces with the middot
|
||||
* If testing without selecting first you can replace with a Non-breaking space character
|
||||
* https://github.com/cypress-io/cypress/issues/15863#issuecomment-816746693
|
||||
*/
|
||||
const unicodeReplacedQuery = esqlQueryToVerify.replaceAll(' ', '\u00b7');
|
||||
cy.get(DISCOVER_ESQL_INPUT).should('include.text', unicodeReplacedQuery);
|
||||
};
|
||||
|
||||
export const submitDiscoverSearchBar = () => {
|
||||
|
@ -69,6 +97,7 @@ export const clearFieldSearch = () => {
|
|||
|
||||
export const addFieldToTable = (fieldId: string) => {
|
||||
searchForField(fieldId);
|
||||
cy.get(GET_DISCOVER_COLUMN_TOGGLE_BTN(fieldId)).first().should('exist');
|
||||
cy.get(GET_DISCOVER_COLUMN_TOGGLE_BTN(fieldId)).first().trigger('click');
|
||||
clearFieldSearch();
|
||||
};
|
||||
|
|
|
@ -102,7 +102,7 @@ export const addDescriptionToTimeline = (
|
|||
if (!modalAlreadyOpen) {
|
||||
cy.get(TIMELINE_EDIT_MODAL_OPEN_BUTTON).first().click();
|
||||
}
|
||||
cy.get(TIMELINE_DESCRIPTION_INPUT).type(description);
|
||||
cy.get(TIMELINE_DESCRIPTION_INPUT).should('not.be.disabled').type(description);
|
||||
cy.get(TIMELINE_DESCRIPTION_INPUT).invoke('val').should('equal', description);
|
||||
cy.get(TIMELINE_EDIT_MODAL_SAVE_BUTTON).click();
|
||||
cy.get(TIMELINE_TITLE_INPUT).should('not.exist');
|
||||
|
@ -112,7 +112,7 @@ export const addNameToTimeline = (name: string, modalAlreadyOpen: boolean = fals
|
|||
if (!modalAlreadyOpen) {
|
||||
cy.get(TIMELINE_EDIT_MODAL_OPEN_BUTTON).first().click();
|
||||
}
|
||||
cy.get(TIMELINE_TITLE_INPUT).type(`${name}{enter}`);
|
||||
cy.get(TIMELINE_TITLE_INPUT).should('not.be.disabled').type(`${name}{enter}`);
|
||||
cy.get(TIMELINE_TITLE_INPUT).should('have.attr', 'value', name);
|
||||
cy.get(TIMELINE_EDIT_MODAL_SAVE_BUTTON).click();
|
||||
cy.get(TIMELINE_TITLE_INPUT).should('not.exist');
|
||||
|
@ -315,7 +315,7 @@ export const createNewTimeline = () => {
|
|||
cy.get(TIMELINE_SETTINGS_ICON).should('be.visible');
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(300);
|
||||
cy.get(CREATE_NEW_TIMELINE).eq(0).click();
|
||||
cy.get(CREATE_NEW_TIMELINE).eq(0).should('be.visible').click();
|
||||
};
|
||||
|
||||
export const openCreateTimelineOptionsPopover = () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue