Skip to content

Commit

Permalink
[8.2] [Security Solution] Rule Preview Table Follow-up (#128981) (#12…
Browse files Browse the repository at this point in the history
…9941)

* [Security Solution] Rule Preview Table Follow-up (#128981)

(cherry picked from commit 268470a)

* fixes test breaking

Co-authored-by: Davis Plumlee <56367316+dplumlee@users.noreply.github.com>
Co-authored-by: Davis Plumlee <davis.plumlee@elastic.co>
  • Loading branch information
3 people authored Apr 12, 2022
1 parent 177c7cd commit 64b05e6
Show file tree
Hide file tree
Showing 26 changed files with 433 additions and 195 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

export enum RULE_PREVIEW_INVOCATION_COUNT {
HOUR = 20,
HOUR = 12,
DAY = 24,
WEEK = 168,
MONTH = 30,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ export enum TimelineId {
casePage = 'timeline-case',
test = 'test', // Reserved for testing purposes
alternateTest = 'alternateTest',
rulePreview = 'rule-preview',
}

export const TimelineIdLiteralRt = runtimeTypes.union([
Expand All @@ -339,6 +340,7 @@ export const TimelineIdLiteralRt = runtimeTypes.union([
runtimeTypes.literal(TimelineId.networkPageExternalAlerts),
runtimeTypes.literal(TimelineId.active),
runtimeTypes.literal(TimelineId.test),
runtimeTypes.literal(TimelineId.rulePreview),
]);

export type TimelineIdLiteral = runtimeTypes.TypeOf<typeof TimelineIdLiteralRt>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,9 +327,9 @@ export const fillDefineEqlRuleAndContinue = (rule: CustomRule) => {
cy.get(PREVIEW_HISTOGRAM)
.invoke('text')
.then((text) => {
if (text !== 'Hits') {
if (text !== 'Rule Preview') {
cy.get(RULES_CREATION_PREVIEW).find(QUERY_PREVIEW_BUTTON).click({ force: true });
cy.get(PREVIEW_HISTOGRAM).should('contain.text', 'Hits');
cy.get(PREVIEW_HISTOGRAM).should('contain.text', 'Rule Preview');
}
});
cy.get(TOAST_ERROR).should('not.exist');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ describe('AlertSummaryView', () => {
expect(queryAllByTestId('hover-actions-filter-for').length).toEqual(0);
});

test('it does NOT render the action cell when readOnly is passed', () => {
const { queryAllByTestId } = render(
<TestProviders>
<AlertSummaryView {...{ ...props, isReadOnly: true }} />
</TestProviders>
);
expect(queryAllByTestId('hover-actions-filter-for').length).toEqual(0);
});

test("render no investigation guide if it doesn't exist", async () => {
(useRuleWithFallback as jest.Mock).mockReturnValue({
rule: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,18 @@ describe('getColumns', () => {
).toEqual('hover-actions-copy-button');
});
});

describe('does not render hover actions when readOnly prop is passed', () => {
test('it renders a filter for (+) button', () => {
actionsColumn = getColumns({ ...defaultProps, isReadOnly: true })[0] as Column;
const wrapper = mount(
<TestProviders>{actionsColumn.render(testValue, testData)}</TestProviders>
) as ReactWrapper;

expect(wrapper.find('[data-test-subj="hover-actions-filter-for"]').exists()).toBeFalsy();
expect(wrapper.find('[data-test-subj="hover-actions-filter-out"]').exists()).toBeFalsy();
expect(wrapper.find('[data-test-subj="more-actions-agent.id"]').exists()).toBeFalsy();
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,20 @@ describe('EventDetails', () => {
alertsWrapper.find('[data-test-subj="threatIntelTab"]').first().simulate('click');
expect(alertsWrapper.find('[data-test-subj="no-enrichments-found"]').exists()).toEqual(true);
});
it('does not render if readOnly prop is passed', async () => {
const newProps = { ...defaultProps, isReadOnly: true };
wrapper = mount(
<TestProviders>
<EventDetails {...newProps} />
</TestProviders>
) as ReactWrapper;
alertsWrapper = mount(
<TestProviders>
<EventDetails {...{ ...alertsProps, ...newProps }} />
</TestProviders>
) as ReactWrapper;
await waitFor(() => wrapper.update());
expect(alertsWrapper.find('[data-test-subj="threatIntelTab"]').exists()).toBeFalsy();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,25 @@ describe('EventFieldsBrowser', () => {
expect(wrapper.find('[data-test-subj="more-actions-@timestamp"]').exists()).toBeTruthy();
});

test('it does not render hover actions when readOnly prop is passed', () => {
const wrapper = mount(
<TestProviders>
<EventFieldsBrowser
browserFields={mockBrowserFields}
data={mockDetailItemData}
eventId={eventId}
timelineId="test"
timelineTabType={TimelineTabs.query}
isReadOnly
/>
</TestProviders>
);

expect(wrapper.find('[data-test-subj="hover-actions-filter-for"]').exists()).toBeFalsy();
expect(wrapper.find('[data-test-subj="hover-actions-filter-out"]').exists()).toBeFalsy();
expect(wrapper.find('[data-test-subj="more-actions-@timestamp"]').exists()).toBeFalsy();
});

test('it renders a column toggle button', () => {
const wrapper = mount(
<TestProviders>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ describe('Event Details Overview Cards', () => {
getByText('Rule');
});

it('renders only readOnly cards', () => {
const { getByText, queryByText } = render(
<TestProviders>
<Overview {...propsWithReadOnly} />
</TestProviders>
);

getByText('Severity');
getByText('Risk Score');

expect(queryByText('Status')).not.toBeInTheDocument();
expect(queryByText('Rule')).not.toBeInTheDocument();
});

it('renders all cards it has data for', () => {
const { getByText, queryByText } = render(
<TestProviders>
Expand Down Expand Up @@ -194,3 +208,8 @@ const propsWithoutSeverity = {
browserFields: { kibana: { fields: fieldsWithoutSeverity } },
data: dataWithoutSeverity,
};

const propsWithReadOnly = {
...props,
isReadOnly: true,
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { useState, useEffect } from 'react';
import { useRouteSpy } from '../route/use_route_spy';

const hideTimelineForRoutes = [`/cases/configure`, '/administration'];
const hideTimelineForRoutes = [`/cases/configure`, '/administration', 'rules/create'];

export const useShowTimeline = () => {
const [{ pageName, pathName }] = useRouteSpy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import { RowRendererId } from '../../../../common/types/timeline';
import { Status } from '../../../../common/detection_engine/schemas/common/schemas';
import { SubsetTimelineModel } from '../../../timelines/store/timeline/model';
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
import { columns } from '../../configurations/security_solution_detections/columns';
import {
columns,
rulePreviewColumns,
} from '../../configurations/security_solution_detections/columns';

export const buildAlertStatusFilter = (status: Status): Filter[] => {
const combinedQuery =
Expand Down Expand Up @@ -156,6 +159,19 @@ export const alertsDefaultModel: SubsetTimelineModel = {
excludedRowRendererIds: Object.values(RowRendererId),
};

export const alertsPreviewDefaultModel: SubsetTimelineModel = {
...alertsDefaultModel,
columns: rulePreviewColumns,
defaultColumns: rulePreviewColumns,
sort: [
{
columnId: 'kibana.alert.original_time',
columnType: 'number',
sortDirection: 'desc',
},
],
};

export const requiredFieldsForActions = [
'@timestamp',
'kibana.alert.workflow_status',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export const getIsRulePreviewDisabled = ({
if (ruleType === 'machine_learning') {
return machineLearningJobId.length === 0;
}
if (ruleType === 'eql') {
if (ruleType === 'eql' || ruleType === 'query' || ruleType === 'threshold') {
return queryBar.query.query.length === 0;
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ import { LoadingHistogram } from './loading_histogram';
import { FieldValueThreshold } from '../threshold_input';
import { isJobStarted } from '../../../../../common/machine_learning/helpers';

const HelpTextComponent = (
<EuiFlexGroup direction="column" gutterSize="none">
<EuiFlexItem>{i18n.QUERY_PREVIEW_HELP_TEXT}</EuiFlexItem>
<EuiFlexItem>{i18n.QUERY_PREVIEW_DISCLAIMER}</EuiFlexItem>
</EuiFlexGroup>
);

export interface RulePreviewProps {
index: string[];
isDisabled: boolean;
Expand Down Expand Up @@ -116,7 +123,7 @@ const RulePreviewComponent: React.FC<RulePreviewProps> = ({
<>
<EuiFormRow
label={i18n.QUERY_PREVIEW_LABEL}
helpText={i18n.QUERY_PREVIEW_HELP_TEXT}
helpText={HelpTextComponent}
error={undefined}
isInvalid={false}
data-test-subj="rule-preview"
Expand Down Expand Up @@ -156,7 +163,6 @@ const RulePreviewComponent: React.FC<RulePreviewProps> = ({
previewId={previewId}
addNoiseWarning={addNoiseWarning}
spaceId={spaceId}
threshold={threshold}
index={index}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@
*/

import React from 'react';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';

import * as i18n from '../rule_preview/translations';
import { useGlobalTime } from '../../../../common/containers/use_global_time';
import { TestProviders } from '../../../../common/mock';
import { usePreviewHistogram } from './use_preview_histogram';

import { PreviewHistogram } from './preview_histogram';
import { ALL_VALUES_ZEROS_TITLE } from '../../../../common/components/charts/translation';

jest.mock('../../../../common/lib/kibana');
jest.mock('../../../../common/containers/use_global_time');
jest.mock('./use_preview_histogram');
jest.mock('../../../../common/lib/kibana');
jest.mock('../../../../common/components/url_state/normalize_time_range.ts');

describe('PreviewHistogram', () => {
const mockSetQuery = jest.fn();
Expand All @@ -35,9 +36,9 @@ describe('PreviewHistogram', () => {
jest.clearAllMocks();
});

test('it renders loader when isLoading is true', () => {
describe('when there is no data', () => {
(usePreviewHistogram as jest.Mock).mockReturnValue([
true,
false,
{
inspect: { dsl: [], response: [] },
totalCount: 1,
Expand All @@ -47,85 +48,52 @@ describe('PreviewHistogram', () => {
},
]);

const wrapper = mount(
<TestProviders>
<PreviewHistogram
addNoiseWarning={jest.fn()}
timeFrame="M"
previewId={'test-preview-id'}
spaceId={'default'}
ruleType={'query'}
index={['']}
/>
</TestProviders>
);
test('it renders an empty histogram and table', async () => {
const wrapper = render(
<TestProviders>
<PreviewHistogram
addNoiseWarning={jest.fn()}
timeFrame="M"
previewId={'test-preview-id'}
spaceId={'default'}
ruleType={'query'}
index={['']}
/>
</TestProviders>
);

expect(wrapper.find('[data-test-subj="preview-histogram-loading"]').exists()).toBeTruthy();
expect(wrapper.find('[data-test-subj="header-section-subtitle"]').text()).toEqual(
i18n.QUERY_PREVIEW_SUBTITLE_LOADING
);
expect(await wrapper.findByText('hello grid')).toBeTruthy();
expect(await wrapper.findByText(ALL_VALUES_ZEROS_TITLE)).toBeTruthy();
});
});

test('it configures data and subtitle', () => {
(usePreviewHistogram as jest.Mock).mockReturnValue([
false,
{
inspect: { dsl: [], response: [] },
totalCount: 9154,
refetch: jest.fn(),
data: [
{ x: 1602247050000, y: 2314, g: 'All others' },
{ x: 1602247162500, y: 3471, g: 'All others' },
{ x: 1602247275000, y: 3369, g: 'All others' },
],
buckets: [],
},
]);
describe('when there is data', () => {
test('it renders loader when isLoading is true', async () => {
(usePreviewHistogram as jest.Mock).mockReturnValue([
true,
{
inspect: { dsl: [], response: [] },
totalCount: 1,
refetch: jest.fn(),
data: [],
buckets: [],
},
]);

const wrapper = mount(
<TestProviders>
<PreviewHistogram
addNoiseWarning={jest.fn()}
timeFrame="M"
previewId={'test-preview-id'}
spaceId={'default'}
ruleType={'query'}
index={['']}
/>
</TestProviders>
);
const wrapper = render(
<TestProviders>
<PreviewHistogram
addNoiseWarning={jest.fn()}
timeFrame="M"
previewId={'test-preview-id'}
spaceId={'default'}
ruleType={'query'}
index={['']}
/>
</TestProviders>
);

expect(wrapper.find('[data-test-subj="preview-histogram-loading"]').exists()).toBeFalsy();
expect(wrapper.find('[data-test-subj="header-section-subtitle"]').text()).toEqual(
i18n.QUERY_PREVIEW_TITLE(9154)
);
expect(
(
wrapper.find('[data-test-subj="preview-histogram-bar-chart"]').props() as {
barChart: unknown;
}
).barChart
).toEqual([
{
key: 'hits',
value: [
{
g: 'All others',
x: 1602247050000,
y: 2314,
},
{
g: 'All others',
x: 1602247162500,
y: 3471,
},
{
g: 'All others',
x: 1602247275000,
y: 3369,
},
],
},
]);
expect(await wrapper.findByTestId('preview-histogram-loading')).toBeTruthy();
});
});
});
Loading

0 comments on commit 64b05e6

Please sign in to comment.