Skip to content

Commit

Permalink
[Security Solution] Rule Preview Table Follow-up (#128981)
Browse files Browse the repository at this point in the history
  • Loading branch information
dplumlee authored Apr 11, 2022
1 parent 4ff0a6e commit 268470a
Show file tree
Hide file tree
Showing 26 changed files with 418 additions and 170 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,20 @@
*/

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 +37,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,42 +49,38 @@ 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', () => {
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(wrapper.findByText('hello grid')).toBeTruthy();
expect(wrapper.findByText(ALL_VALUES_ZEROS_TITLE)).toBeTruthy();
});
});

test('it configures data and subtitle', () => {
test('it renders loader when isLoading is true', () => {
(usePreviewHistogram as jest.Mock).mockReturnValue([
false,
true,
{
inspect: { dsl: [], response: [] },
totalCount: 9154,
totalCount: 1,
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' },
],
data: [],
buckets: [],
},
]);

const wrapper = mount(
const wrapper = render(
<TestProviders>
<PreviewHistogram
addNoiseWarning={jest.fn()}
Expand All @@ -95,37 +93,7 @@ describe('PreviewHistogram', () => {
</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(wrapper.findByTestId('preview-histogram-loading')).toBeTruthy();
expect(wrapper.findByText(i18n.QUERY_PREVIEW_SUBTITLE_LOADING)).toBeTruthy();
});
});
Loading

0 comments on commit 268470a

Please sign in to comment.