Skip to content

Commit

Permalink
[Security Solution] add threat intelligence overview to expandable fl…
Browse files Browse the repository at this point in the history
…yout (#155328)
  • Loading branch information
PhilippeOberti authored Apr 21, 2023
1 parent 8a3f5eb commit 4eeec18
Show file tree
Hide file tree
Showing 15 changed files with 1,250 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ import {
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_VIEW_ALL_ENTITIES_BUTTON,
DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ANALYZER_TREE,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON,
} from '../../../screens/document_expandable_flyout';
import {
expandFirstAlertExpandableFlyout,
Expand Down Expand Up @@ -180,6 +184,38 @@ describe.skip(
.click();
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible');
});

// TODO work on getting proper IoC data to make the threat intelligence section work here
it.skip('should display threat intelligence section', () => {
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER)
.scrollIntoView()
.should('be.visible')
.and('have.text', 'Threat Intelligence');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT)
.should('be.visible')
.within(() => {
// threat match detected
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES)
.eq(0)
.should('be.visible')
.and('have.text', '1 threat match detected'); // TODO

// field with threat enrichement
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES)
.eq(1)
.should('be.visible')
.and('have.text', '1 field enriched with threat intelligence'); // TODO
});
});

// TODO work on getting proper IoC data to make the threat intelligence section work here
// and improve when we can navigate Threat Intelligence to sub tab directly
it.skip('should navigate to left panel, entities tab when view all fields of threat intelligence is clicked', () => {
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON)
.should('be.visible')
.click();
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible');
});
});

describe('visualizations section', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ import {
ENTITIES_VIEW_ALL_BUTTON_TEST_ID,
VISUALIZATIONS_SECTION_HEADER_TEST_ID,
ANALYZER_TREE_TEST_ID,
INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID,
INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID,
INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID,
INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID,
} from '../../public/flyout/right/components/test_ids';
import {
getClassSelector,
Expand Down Expand Up @@ -303,6 +307,14 @@ export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_HEADER =
getDataTestSubjectSelector(ENTITY_PANEL_HEADER_TEST_ID);
export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_CONTENT =
getDataTestSubjectSelector(ENTITY_PANEL_CONTENT_TEST_ID);
export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER =
getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID);
export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT =
getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID);
export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES =
getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID);
export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON =
getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID);

export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_VISUALIZATIONS_SECTION_HEADER =
getDataTestSubjectSelector(VISUALIZATIONS_SECTION_HEADER_TEST_ID);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import React from 'react';
import { ThreatIntelligenceOverview } from './threat_intelligence_overview';
import { INSIGHTS_TEST_ID } from './test_ids';
import { INSIGHTS_TITLE } from './translations';
import { EntitiesOverview } from './entities_overview';
Expand All @@ -25,6 +26,7 @@ export const InsightsSection: React.FC<InsightsSectionProps> = ({ expanded = fal
return (
<ExpandableSection title={INSIGHTS_TITLE} expanded={expanded} data-test-subj={INSIGHTS_TEST_ID}>
<EntitiesOverview />
<ThreatIntelligenceOverview />
</ExpandableSection>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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 type { Story } from '@storybook/react';
import { InsightsSubSection } from './insights_subsection';

export default {
component: InsightsSubSection,
title: 'Flyout/InsightsSubSection',
};

const title = 'Title';
const children = <div>{'hello'}</div>;

export const Basic: Story<void> = () => {
return <InsightsSubSection title={title}>{children}</InsightsSubSection>;
};

export const Loading: Story<void> = () => {
return (
<InsightsSubSection loading={true} title={title}>
{null}
</InsightsSubSection>
);
};

export const NoTitle: Story<void> = () => {
return <InsightsSubSection title={''}>{children}</InsightsSubSection>;
};

export const NoChildren: Story<void> = () => {
return <InsightsSubSection title={title}>{null}</InsightsSubSection>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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 { render } from '@testing-library/react';
import { InsightsSubSection } from './insights_subsection';

const title = 'Title';
const dataTestSubj = 'test';
const children = <div>{'hello'}</div>;

describe('<InsightsSubSection />', () => {
it('should render children component', () => {
const { getByTestId } = render(
<InsightsSubSection title={title} data-test-subj={dataTestSubj}>
{children}
</InsightsSubSection>
);

const titleDataTestSubj = `${dataTestSubj}Title`;
const contentDataTestSubj = `${dataTestSubj}Content`;

expect(getByTestId(titleDataTestSubj)).toHaveTextContent(title);
expect(getByTestId(contentDataTestSubj)).toBeInTheDocument();
});

it('should render loading component', () => {
const { getByTestId } = render(
<InsightsSubSection loading={true} title={title} data-test-subj={dataTestSubj}>
{children}
</InsightsSubSection>
);

const loadingDataTestSubj = `${dataTestSubj}Loading`;
expect(getByTestId(loadingDataTestSubj)).toBeInTheDocument();
});

it('should render null if error', () => {
const { container } = render(
<InsightsSubSection error={true} title={title}>
{children}
</InsightsSubSection>
);

expect(container).toBeEmptyDOMElement();
});

it('should render null if no title', () => {
const { container } = render(<InsightsSubSection title={''}>{children}</InsightsSubSection>);

expect(container).toBeEmptyDOMElement();
});

it('should render null if no children', () => {
const { container } = render(
<InsightsSubSection error={true} title={title}>
{null}
</InsightsSubSection>
);

expect(container).toBeEmptyDOMElement();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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 { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSpacer, EuiTitle } from '@elastic/eui';

export interface InsightsSectionProps {
/**
* Renders a loading spinner if true
*/
loading?: boolean;
/**
* Returns a null component if true
*/
error?: boolean;
/**
* Title at the top of the component
*/
title: string;
/**
* Content of the component
*/
children: React.ReactNode;
/**
* Prefix data-test-subj to use for the elements
*/
['data-test-subj']?: string;
}

/**
* Presentational component to handle loading and error in the subsections of the Insights section.
* Should be used for Entities, Threat Intelligence, Prevalence, Correlations and Results
*/
export const InsightsSubSection: React.FC<InsightsSectionProps> = ({
loading = false,
error = false,
title,
'data-test-subj': dataTestSubj,
children,
}) => {
const loadingDataTestSubj = `${dataTestSubj}Loading`;
// showing the loading in this component instead of SummaryPanel because we're hiding the entire section if no data

if (loading) {
return (
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner data-test-subj={loadingDataTestSubj} />
</EuiFlexItem>
</EuiFlexGroup>
);
}

// hide everything
if (error || !title || !children) {
return null;
}

const titleDataTestSubj = `${dataTestSubj}Title`;
const contentDataTestSubj = `${dataTestSubj}Content`;

return (
<>
<EuiTitle size="xxs" data-test-subj={titleDataTestSubj}>
<h5>{title}</h5>
</EuiTitle>
<EuiSpacer size="s" />
<EuiFlexGroup data-test-subj={contentDataTestSubj} direction="column" gutterSize="s">
{children}
</EuiFlexGroup>
</>
);
};

InsightsSubSection.displayName = 'InsightsSubSection';
Loading

0 comments on commit 4eeec18

Please sign in to comment.