mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution] Expandable flyout - clean up loading states and no data messages (#166939)
## Summary This PR cleaned up the loading and empty state of most components in the new expandable alerts flyout: 1) Refactor `FlyoutLoading` component to be used across components 2) Updated unit tests to explicitly check no data messages 3) Changed loading spinner in smaller components to be skeleton text 4) Moved error/no data message to table 5) Added loading and/or error messages for: 1) Right panel -> about -> Rule description, note that rule preview is disabled if no rule is found  2) Right panel -> about -> Alert reason, note that the alert reason preview is disabled if no reason available  3) Right panel -> Investigation -> Highlighted fields  4) Right panel -> Insights -> host and user preview  5) Right panel -> Visualization -> analyzer preview (to match error in analyzer graph)  6) Right panel -> About -> show rule summary & show alert reason when there is an error - Very uncommon because the button should be disabled if data is not available, adding as fall back  ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [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
This commit is contained in:
parent
bb5439f4e1
commit
7b4e6a6775
51 changed files with 700 additions and 621 deletions
|
@ -20,7 +20,6 @@ import {
|
|||
CORRELATIONS_DETAILS_BY_SESSION_SECTION_TABLE_TEST_ID,
|
||||
CORRELATIONS_DETAILS_BY_SOURCE_SECTION_TABLE_TEST_ID,
|
||||
CORRELATIONS_DETAILS_CASES_SECTION_TABLE_TEST_ID,
|
||||
CORRELATIONS_DETAILS_NO_DATA_TEST_ID,
|
||||
CORRELATIONS_DETAILS_SUPPRESSED_ALERTS_SECTION_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { useFetchRelatedAlertsBySession } from '../../shared/hooks/use_fetch_related_alerts_by_session';
|
||||
|
@ -53,12 +52,13 @@ const renderCorrelationDetails = () => {
|
|||
</TestProviders>
|
||||
);
|
||||
};
|
||||
|
||||
const CORRELATIONS_DETAILS_SUPPRESSED_ALERTS_TITLE_TEST_ID =
|
||||
EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(
|
||||
CORRELATIONS_DETAILS_SUPPRESSED_ALERTS_SECTION_TEST_ID
|
||||
);
|
||||
|
||||
const NO_DATA_MESSAGE = 'No correlations data available.';
|
||||
|
||||
describe('CorrelationsDetails', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
@ -102,14 +102,14 @@ describe('CorrelationsDetails', () => {
|
|||
dataCount: 1,
|
||||
});
|
||||
|
||||
const { getByTestId, queryByTestId } = renderCorrelationDetails();
|
||||
const { getByTestId, queryByText } = renderCorrelationDetails();
|
||||
|
||||
expect(getByTestId(CORRELATIONS_DETAILS_BY_ANCESTRY_SECTION_TABLE_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(CORRELATIONS_DETAILS_BY_SOURCE_SECTION_TABLE_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(CORRELATIONS_DETAILS_BY_SESSION_SECTION_TABLE_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(CORRELATIONS_DETAILS_CASES_SECTION_TABLE_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(CORRELATIONS_DETAILS_SUPPRESSED_ALERTS_TITLE_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(CORRELATIONS_DETAILS_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render no section and show error message if show values are false', () => {
|
||||
|
@ -125,7 +125,7 @@ describe('CorrelationsDetails', () => {
|
|||
jest.mocked(useShowRelatedCases).mockReturnValue(false);
|
||||
jest.mocked(useShowSuppressedAlerts).mockReturnValue({ show: false, alertSuppressionCount: 0 });
|
||||
|
||||
const { getByTestId, queryByTestId } = renderCorrelationDetails();
|
||||
const { getByText, queryByTestId } = renderCorrelationDetails();
|
||||
|
||||
expect(
|
||||
queryByTestId(CORRELATIONS_DETAILS_BY_ANCESTRY_SECTION_TABLE_TEST_ID)
|
||||
|
@ -140,10 +140,7 @@ describe('CorrelationsDetails', () => {
|
|||
expect(
|
||||
queryByTestId(CORRELATIONS_DETAILS_SUPPRESSED_ALERTS_TITLE_TEST_ID)
|
||||
).not.toBeInTheDocument();
|
||||
expect(getByTestId(CORRELATIONS_DETAILS_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(CORRELATIONS_DETAILS_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'No correlations data available.'
|
||||
);
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render no section if values are null', () => {
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiPanel, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { CORRELATIONS_DETAILS_NO_DATA_TEST_ID } from './test_ids';
|
||||
import { CORRELATIONS_DETAILS_TEST_ID } from './test_ids';
|
||||
import { RelatedAlertsBySession } from './related_alerts_by_session';
|
||||
import { RelatedAlertsBySameSourceEvent } from './related_alerts_by_same_source_event';
|
||||
import { RelatedCases } from './related_cases';
|
||||
|
@ -56,7 +56,7 @@ export const CorrelationsDetails: React.FC = () => {
|
|||
showSuppressedAlerts;
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiPanel paddingSize="none" data-test-subj={CORRELATIONS_DETAILS_TEST_ID} color="transparent">
|
||||
{canShowAtLeastOneInsight ? (
|
||||
<EuiFlexGroup gutterSize="l" direction="column">
|
||||
{showSuppressedAlerts && (
|
||||
|
@ -98,14 +98,12 @@ export const CorrelationsDetails: React.FC = () => {
|
|||
)}
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<p data-test-subj={CORRELATIONS_DETAILS_NO_DATA_TEST_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.left.insights.correlations.noDataDescription"
|
||||
defaultMessage="No correlations data available."
|
||||
/>
|
||||
</p>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.left.insights.correlations.noDataDescription"
|
||||
defaultMessage="No correlations data available."
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</EuiPanel>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -11,12 +11,7 @@ import '@testing-library/jest-dom';
|
|||
import { LeftPanelContext } from '../context';
|
||||
import { TestProviders } from '../../../common/mock';
|
||||
import { EntitiesDetails } from './entities_details';
|
||||
import {
|
||||
ENTITIES_DETAILS_NO_DATA_TEST_ID,
|
||||
ENTITIES_DETAILS_TEST_ID,
|
||||
HOST_DETAILS_TEST_ID,
|
||||
USER_DETAILS_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { ENTITIES_DETAILS_TEST_ID, HOST_DETAILS_TEST_ID, USER_DETAILS_TEST_ID } from './test_ids';
|
||||
import { mockContextValue } from '../mocks/mock_context';
|
||||
import { EXPANDABLE_PANEL_CONTENT_TEST_ID } from '../../shared/components/test_ids';
|
||||
|
||||
|
@ -40,6 +35,8 @@ jest.mock('react-redux', () => {
|
|||
const USER_TEST_ID = EXPANDABLE_PANEL_CONTENT_TEST_ID(USER_DETAILS_TEST_ID);
|
||||
const HOST_TEST_ID = EXPANDABLE_PANEL_CONTENT_TEST_ID(HOST_DETAILS_TEST_ID);
|
||||
|
||||
const NO_DATA_MESSAGE = 'Host and user information are unavailable for this alert.';
|
||||
|
||||
const renderEntitiesDetails = (contextValue: LeftPanelContext) =>
|
||||
render(
|
||||
<TestProviders>
|
||||
|
@ -51,11 +48,11 @@ const renderEntitiesDetails = (contextValue: LeftPanelContext) =>
|
|||
|
||||
describe('<EntitiesDetails />', () => {
|
||||
it('renders entities details correctly', () => {
|
||||
const { getByTestId, queryByTestId } = renderEntitiesDetails(mockContextValue);
|
||||
const { getByTestId, queryByText } = renderEntitiesDetails(mockContextValue);
|
||||
expect(getByTestId(ENTITIES_DETAILS_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(USER_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(HOST_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(ENTITIES_DETAILS_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render no data message if user name and host name are not available', () => {
|
||||
|
@ -64,11 +61,8 @@ describe('<EntitiesDetails />', () => {
|
|||
getFieldsData: (fieldName: string) =>
|
||||
fieldName === '@timestamp' ? ['2022-07-25T08:20:18.966Z'] : [],
|
||||
};
|
||||
const { getByTestId, queryByTestId } = renderEntitiesDetails(contextValue);
|
||||
expect(getByTestId(ENTITIES_DETAILS_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(ENTITIES_DETAILS_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'Host and user information are unavailable for this alert.'
|
||||
);
|
||||
const { getByText, queryByTestId } = renderEntitiesDetails(contextValue);
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
expect(queryByTestId(USER_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(HOST_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
|
@ -87,11 +81,8 @@ describe('<EntitiesDetails />', () => {
|
|||
}
|
||||
},
|
||||
};
|
||||
const { getByTestId, queryByTestId } = renderEntitiesDetails(contextValue);
|
||||
expect(getByTestId(ENTITIES_DETAILS_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(ENTITIES_DETAILS_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'Host and user information are unavailable for this alert.'
|
||||
);
|
||||
const { getByText, queryByTestId } = renderEntitiesDetails(contextValue);
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
expect(queryByTestId(USER_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(HOST_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@ import { useLeftPanelContext } from '../context';
|
|||
import { getField } from '../../shared/utils';
|
||||
import { UserDetails } from './user_details';
|
||||
import { HostDetails } from './host_details';
|
||||
import { ENTITIES_DETAILS_NO_DATA_TEST_ID, ENTITIES_DETAILS_TEST_ID } from './test_ids';
|
||||
import { ENTITIES_DETAILS_TEST_ID } from './test_ids';
|
||||
|
||||
export const ENTITIES_TAB_ID = 'entities-details';
|
||||
|
||||
|
@ -45,12 +45,10 @@ export const EntitiesDetails: React.FC = () => {
|
|||
)}
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<p data-test-subj={ENTITIES_DETAILS_NO_DATA_TEST_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.left.insights.entities.noDataDescription"
|
||||
defaultMessage="Host and user information are unavailable for this alert."
|
||||
/>
|
||||
</p>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.left.insights.entities.noDataDescription"
|
||||
defaultMessage="Host and user information are unavailable for this alert."
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -10,15 +10,15 @@ import { render } from '@testing-library/react';
|
|||
import { InvestigationGuide } from './investigation_guide';
|
||||
import { LeftPanelContext } from '../context';
|
||||
import { TestProviders } from '../../../common/mock';
|
||||
import {
|
||||
INVESTIGATION_GUIDE_LOADING_TEST_ID,
|
||||
INVESTIGATION_GUIDE_NO_DATA_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { INVESTIGATION_GUIDE_TEST_ID, INVESTIGATION_GUIDE_LOADING_TEST_ID } from './test_ids';
|
||||
import { mockContextValue } from '../mocks/mock_context';
|
||||
import { useInvestigationGuide } from '../../shared/hooks/use_investigation_guide';
|
||||
|
||||
jest.mock('../../shared/hooks/use_investigation_guide');
|
||||
|
||||
const NO_DATA_TEXT =
|
||||
"There's no investigation guide for this rule. Edit the rule's settingsExternal link(opens in a new tab or window) to add one.";
|
||||
|
||||
const renderInvestigationGuide = (context: LeftPanelContext = mockContextValue) => (
|
||||
<TestProviders>
|
||||
<LeftPanelContext.Provider value={context}>
|
||||
|
@ -35,8 +35,10 @@ describe('<InvestigationGuide />', () => {
|
|||
basicAlertData: { ruleId: 'ruleId' },
|
||||
ruleNote: 'test note',
|
||||
});
|
||||
const { queryByTestId } = render(renderInvestigationGuide());
|
||||
expect(queryByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
|
||||
const { queryByTestId, getByText, queryByText } = render(renderInvestigationGuide());
|
||||
expect(getByText('test note')).toBeInTheDocument();
|
||||
expect(queryByText(NO_DATA_TEXT)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(INVESTIGATION_GUIDE_LOADING_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
@ -54,10 +56,7 @@ describe('<InvestigationGuide />', () => {
|
|||
ruleNote: 'test note',
|
||||
});
|
||||
const { getByTestId } = render(renderInvestigationGuide());
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
`There’s no investigation guide for this rule. Edit the rule's settingsExternal link(opens in a new tab or window) to add one.`
|
||||
);
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).toHaveTextContent(NO_DATA_TEXT);
|
||||
});
|
||||
|
||||
it('should render no data message when there is no rule note', () => {
|
||||
|
@ -66,10 +65,7 @@ describe('<InvestigationGuide />', () => {
|
|||
ruleNote: undefined,
|
||||
});
|
||||
const { getByTestId } = render(renderInvestigationGuide());
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
`There’s no investigation guide for this rule. Edit the rule's settingsExternal link(opens in a new tab or window) to add one.`
|
||||
);
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).toHaveTextContent(NO_DATA_TEXT);
|
||||
});
|
||||
|
||||
it('should render no data message when useInvestigationGuide errors out', () => {
|
||||
|
@ -78,6 +74,6 @@ describe('<InvestigationGuide />', () => {
|
|||
error: true,
|
||||
});
|
||||
const { getByTestId } = render(renderInvestigationGuide());
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).toHaveTextContent(NO_DATA_TEXT);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,15 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useInvestigationGuide } from '../../shared/hooks/use_investigation_guide';
|
||||
import { useLeftPanelContext } from '../context';
|
||||
import {
|
||||
INVESTIGATION_GUIDE_LOADING_TEST_ID,
|
||||
INVESTIGATION_GUIDE_NO_DATA_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { INVESTIGATION_GUIDE_TEST_ID, INVESTIGATION_GUIDE_LOADING_TEST_ID } from './test_ids';
|
||||
import { InvestigationGuideView } from '../../../common/components/event_details/investigation_guide_view';
|
||||
import { FlyoutLoading } from '../../shared/components/flyout_loading';
|
||||
|
||||
/**
|
||||
* Investigation guide displayed in the left panel.
|
||||
|
@ -26,22 +24,11 @@ export const InvestigationGuide: React.FC = () => {
|
|||
dataFormattedForFieldBrowser,
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
justifyContent="spaceAround"
|
||||
data-test-subj={INVESTIGATION_GUIDE_LOADING_TEST_ID}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="m" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{!error && basicAlertData.ruleId && ruleNote ? (
|
||||
<div data-test-subj={INVESTIGATION_GUIDE_TEST_ID}>
|
||||
{loading ? (
|
||||
<FlyoutLoading data-test-subj={INVESTIGATION_GUIDE_LOADING_TEST_ID} />
|
||||
) : !error && basicAlertData.ruleId && ruleNote ? (
|
||||
<InvestigationGuideView
|
||||
basicData={basicAlertData}
|
||||
ruleNote={ruleNote}
|
||||
|
@ -49,27 +36,25 @@ export const InvestigationGuide: React.FC = () => {
|
|||
showFullView={true}
|
||||
/>
|
||||
) : (
|
||||
<p data-test-subj={INVESTIGATION_GUIDE_NO_DATA_TEST_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.left.investigation.noDataDescription"
|
||||
defaultMessage="There’s no investigation guide for this rule. {documentation} to add one."
|
||||
values={{
|
||||
documentation: (
|
||||
<EuiLink
|
||||
href="https://www.elastic.co/guide/en/security/current/rules-ui-management.html#edit-rules-settings"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.left.investigation.noDataLinkText"
|
||||
defaultMessage="Edit the rule's settings"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.left.investigation.noDataDescription"
|
||||
defaultMessage="There's no investigation guide for this rule. {documentation} to add one."
|
||||
values={{
|
||||
documentation: (
|
||||
<EuiLink
|
||||
href="https://www.elastic.co/guide/en/security/current/rules-ui-management.html#edit-rules-settings"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.left.investigation.noDataLinkText"
|
||||
defaultMessage="Edit the rule's settings"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -10,12 +10,10 @@ import React from 'react';
|
|||
import { LeftPanelContext } from '../context';
|
||||
import { PrevalenceDetails } from './prevalence_details';
|
||||
import {
|
||||
PREVALENCE_DETAILS_LOADING_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_ALERT_COUNT_CELL_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_DOC_COUNT_CELL_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_FIELD_CELL_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_HOST_PREVALENCE_CELL_TEST_ID,
|
||||
PREVALENCE_DETAILS_NO_DATA_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_TEST_ID,
|
||||
PREVALENCE_DETAILS_UPSELL_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_USER_PREVALENCE_CELL_TEST_ID,
|
||||
|
@ -47,6 +45,8 @@ jest.mock('../../../common/hooks/use_license', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const NO_DATA_MESSAGE = 'No prevalence data available.';
|
||||
|
||||
const panelContextValue = {
|
||||
eventId: 'event id',
|
||||
indexName: 'indexName',
|
||||
|
@ -96,7 +96,7 @@ describe('PrevalenceDetails', () => {
|
|||
],
|
||||
});
|
||||
|
||||
const { getByTestId, getAllByTestId, queryByTestId } = renderPrevalenceDetails();
|
||||
const { getByTestId, getAllByTestId, queryByTestId, queryByText } = renderPrevalenceDetails();
|
||||
|
||||
expect(getByTestId(PREVALENCE_DETAILS_TABLE_TEST_ID)).toBeInTheDocument();
|
||||
expect(getAllByTestId(PREVALENCE_DETAILS_TABLE_FIELD_CELL_TEST_ID).length).toBeGreaterThan(1);
|
||||
|
@ -114,7 +114,7 @@ describe('PrevalenceDetails', () => {
|
|||
getAllByTestId(PREVALENCE_DETAILS_TABLE_USER_PREVALENCE_CELL_TEST_ID).length
|
||||
).toBeGreaterThan(1);
|
||||
expect(queryByTestId(PREVALENCE_DETAILS_UPSELL_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(PREVALENCE_DETAILS_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render formatted numbers for the alert and document count columns', () => {
|
||||
|
@ -201,20 +201,6 @@ describe('PrevalenceDetails', () => {
|
|||
expect(getByTestId(PREVALENCE_DETAILS_UPSELL_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render loading', () => {
|
||||
(usePrevalence as jest.Mock).mockReturnValue({
|
||||
loading: true,
|
||||
error: false,
|
||||
data: [],
|
||||
});
|
||||
|
||||
const { getByTestId, queryByTestId } = renderPrevalenceDetails();
|
||||
|
||||
expect(getByTestId(PREVALENCE_DETAILS_LOADING_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(PREVALENCE_DETAILS_UPSELL_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(PREVALENCE_DETAILS_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render no data message if call errors out', () => {
|
||||
(usePrevalence as jest.Mock).mockReturnValue({
|
||||
loading: false,
|
||||
|
@ -222,13 +208,8 @@ describe('PrevalenceDetails', () => {
|
|||
data: [],
|
||||
});
|
||||
|
||||
const { getByTestId, queryByTestId } = renderPrevalenceDetails();
|
||||
|
||||
expect(getByTestId(PREVALENCE_DETAILS_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(PREVALENCE_DETAILS_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'No prevalence data available.'
|
||||
);
|
||||
expect(queryByTestId(PREVALENCE_DETAILS_LOADING_TEST_ID)).not.toBeInTheDocument();
|
||||
const { getByText } = renderPrevalenceDetails();
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render no data message if no data', () => {
|
||||
|
@ -238,12 +219,7 @@ describe('PrevalenceDetails', () => {
|
|||
data: [],
|
||||
});
|
||||
|
||||
const { getByTestId, queryByTestId } = renderPrevalenceDetails();
|
||||
|
||||
expect(getByTestId(PREVALENCE_DETAILS_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(PREVALENCE_DETAILS_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'No prevalence data available.'
|
||||
);
|
||||
expect(queryByTestId(PREVALENCE_DETAILS_LOADING_TEST_ID)).not.toBeInTheDocument();
|
||||
const { getByText } = renderPrevalenceDetails();
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
EuiFlexItem,
|
||||
EuiInMemoryTable,
|
||||
EuiLink,
|
||||
EuiLoadingSpinner,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiSuperDatePicker,
|
||||
|
@ -28,14 +27,12 @@ import { InvestigateInTimelineButton } from '../../../common/components/event_de
|
|||
import type { PrevalenceData } from '../../shared/hooks/use_prevalence';
|
||||
import { usePrevalence } from '../../shared/hooks/use_prevalence';
|
||||
import {
|
||||
PREVALENCE_DETAILS_LOADING_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_ALERT_COUNT_CELL_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_DOC_COUNT_CELL_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_HOST_PREVALENCE_CELL_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_VALUE_CELL_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_FIELD_CELL_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_USER_PREVALENCE_CELL_TEST_ID,
|
||||
PREVALENCE_DETAILS_NO_DATA_TEST_ID,
|
||||
PREVALENCE_DETAILS_DATE_PICKER_TEST_ID,
|
||||
PREVALENCE_DETAILS_TABLE_TEST_ID,
|
||||
PREVALENCE_DETAILS_UPSELL_TEST_ID,
|
||||
|
@ -319,19 +316,6 @@ export const PrevalenceDetails: React.FC = () => {
|
|||
[data, absoluteStart, absoluteEnd]
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
justifyContent="spaceAround"
|
||||
data-test-subj={PREVALENCE_DETAILS_LOADING_TEST_ID}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="m" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
const upsell = (
|
||||
<>
|
||||
<EuiCallOut data-test-subj={PREVALENCE_DETAILS_UPSELL_TEST_ID}>
|
||||
|
@ -366,20 +350,18 @@ export const PrevalenceDetails: React.FC = () => {
|
|||
width="full"
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
{data.length > 0 ? (
|
||||
<EuiInMemoryTable
|
||||
items={items}
|
||||
columns={columns}
|
||||
data-test-subj={PREVALENCE_DETAILS_TABLE_TEST_ID}
|
||||
/>
|
||||
) : (
|
||||
<p data-test-subj={PREVALENCE_DETAILS_NO_DATA_TEST_ID}>
|
||||
<EuiInMemoryTable
|
||||
items={error ? [] : items}
|
||||
columns={columns}
|
||||
loading={loading}
|
||||
data-test-subj={PREVALENCE_DETAILS_TABLE_TEST_ID}
|
||||
message={
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.left.insights.prevalence.noDataDescription"
|
||||
defaultMessage="No prevalence data available."
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -7,10 +7,9 @@
|
|||
|
||||
import React from 'react';
|
||||
import type { EuiBasicTableColumn } from '@elastic/eui';
|
||||
import { EuiInMemoryTable, EuiSkeletonText } from '@elastic/eui';
|
||||
import { EuiInMemoryTable } from '@elastic/eui';
|
||||
import type { RelatedCase } from '@kbn/cases-plugin/common';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { CellTooltipWrapper } from '../../shared/components/cell_tooltip_wrapper';
|
||||
import { CaseDetailsLink } from '../../../common/components/links';
|
||||
import {
|
||||
|
@ -65,22 +64,6 @@ export interface RelatedCasesProps {
|
|||
export const RelatedCases: React.VFC<RelatedCasesProps> = ({ eventId }) => {
|
||||
const { loading, error, data, dataCount } = useFetchRelatedCases({ eventId });
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<EuiSkeletonText
|
||||
lines={1}
|
||||
size="m"
|
||||
isLoading={loading}
|
||||
contentAriaLabel={i18n.translate(
|
||||
'xpack.securitySolution.flyout.left.insights.correlations.relatedCasesLoadingAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Related cases is loading',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import { render } from '@testing-library/react';
|
|||
import '@testing-library/jest-dom';
|
||||
import { LeftPanelContext } from '../context';
|
||||
import { rawEventData, TestProviders } from '../../../common/mock';
|
||||
import { RESPONSE_DETAILS_TEST_ID, RESPONSE_NO_DATA_TEST_ID } from './test_ids';
|
||||
import { RESPONSE_DETAILS_TEST_ID } from './test_ids';
|
||||
import { ResponseDetails } from './response_details';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
|
||||
|
||||
|
@ -59,6 +59,9 @@ jest.mock('../../../common/lib/kibana', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const NO_DATA_MESSAGE =
|
||||
"There are no response actions defined for this event. To add some, edit the rule's settings and set up response actionsExternal link(opens in a new tab or window).";
|
||||
|
||||
const defaultContextValue = {
|
||||
dataAsNestedObject: {
|
||||
_id: 'test',
|
||||
|
@ -114,7 +117,7 @@ describe('<ResponseDetails />', () => {
|
|||
expect(wrapper.getByTestId('responseActionsViewWrapper')).toBeInTheDocument();
|
||||
expect(wrapper.queryByTestId('osqueryViewWrapper')).not.toBeInTheDocument();
|
||||
|
||||
expect(wrapper.queryByTestId(RESPONSE_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(wrapper.getByTestId(RESPONSE_DETAILS_TEST_ID)).not.toHaveTextContent(NO_DATA_MESSAGE);
|
||||
});
|
||||
|
||||
it('should render the view with osquery only', () => {
|
||||
|
@ -135,9 +138,6 @@ describe('<ResponseDetails />', () => {
|
|||
expect(wrapper.queryByTestId('responseActionsViewWrapper')).not.toBeInTheDocument();
|
||||
expect(wrapper.queryByTestId('osqueryViewWrapper')).not.toBeInTheDocument();
|
||||
|
||||
expect(wrapper.getByTestId(RESPONSE_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(wrapper.getByTestId(RESPONSE_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'There are no response actions defined for this event. To add some, edit the rule’s settings and set up response actionsExternal link(opens in a new tab or window).'
|
||||
);
|
||||
expect(wrapper.getByTestId(RESPONSE_DETAILS_TEST_ID)).toHaveTextContent(NO_DATA_MESSAGE);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -69,7 +69,7 @@ export const ResponseDetails: React.FC = () => {
|
|||
<InlineBlock data-test-subj={RESPONSE_NO_DATA_TEST_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.left.response.noDataDescription"
|
||||
defaultMessage="There are no response actions defined for this event. To add some, edit the rule’s settings and set up {link}."
|
||||
defaultMessage="There are no response actions defined for this event. To add some, edit the rule's settings and set up {link}."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink
|
||||
|
|
|
@ -20,8 +20,6 @@ export const SESSION_VIEW_ERROR_TEST_ID = `${PREFIX}SessionViewError` as const;
|
|||
const PREVALENCE_DETAILS_TEST_ID = `${PREFIX}PrevalenceDetails` as const;
|
||||
export const PREVALENCE_DETAILS_DATE_PICKER_TEST_ID =
|
||||
`${PREVALENCE_DETAILS_TEST_ID}DatePicker` as const;
|
||||
export const PREVALENCE_DETAILS_LOADING_TEST_ID = `${PREVALENCE_DETAILS_TEST_ID}Loading` as const;
|
||||
export const PREVALENCE_DETAILS_NO_DATA_TEST_ID = `${PREVALENCE_DETAILS_TEST_ID}NoData` as const;
|
||||
export const PREVALENCE_DETAILS_UPSELL_TEST_ID = `${PREVALENCE_DETAILS_TEST_ID}Upsell` as const;
|
||||
export const PREVALENCE_DETAILS_TABLE_TEST_ID = `${PREVALENCE_DETAILS_TEST_ID}Table` as const;
|
||||
export const PREVALENCE_DETAILS_TABLE_FIELD_CELL_TEST_ID =
|
||||
|
@ -40,7 +38,6 @@ export const PREVALENCE_DETAILS_TABLE_USER_PREVALENCE_CELL_TEST_ID =
|
|||
/* Entities */
|
||||
|
||||
export const ENTITIES_DETAILS_TEST_ID = `${PREFIX}EntitiesDetails` as const;
|
||||
export const ENTITIES_DETAILS_NO_DATA_TEST_ID = `${ENTITIES_DETAILS_TEST_ID}NoData` as const;
|
||||
export const USER_DETAILS_TEST_ID = `${PREFIX}UsersDetails` as const;
|
||||
export const USER_DETAILS_RELATED_HOSTS_TABLE_TEST_ID =
|
||||
`${USER_DETAILS_TEST_ID}RelatedHostsTable` as const;
|
||||
|
@ -58,9 +55,7 @@ export const THREAT_INTELLIGENCE_DETAILS_LOADING_TEST_ID =
|
|||
|
||||
/* Correlations */
|
||||
|
||||
const CORRELATIONS_DETAILS_TEST_ID = `${PREFIX}CorrelationsDetails` as const;
|
||||
export const CORRELATIONS_DETAILS_NO_DATA_TEST_ID =
|
||||
`${CORRELATIONS_DETAILS_TEST_ID}NoData` as const;
|
||||
export const CORRELATIONS_DETAILS_TEST_ID = `${PREFIX}CorrelationsDetails` as const;
|
||||
|
||||
export const CORRELATIONS_DETAILS_BY_ANCESTRY_SECTION_TEST_ID =
|
||||
`${CORRELATIONS_DETAILS_TEST_ID}AlertsByAncestrySection` as const;
|
||||
|
@ -91,5 +86,5 @@ export const RESPONSE_NO_DATA_TEST_ID = `${RESPONSE_TEST_ID}NoData` as const;
|
|||
|
||||
/* Investigation */
|
||||
|
||||
export const INVESTIGATION_GUIDE_LOADING_TEST_ID = `${PREFIX}InvestigationGuideLoading` as const;
|
||||
export const INVESTIGATION_GUIDE_NO_DATA_TEST_ID = `${PREFIX}NoData` as const;
|
||||
export const INVESTIGATION_GUIDE_TEST_ID = `${PREFIX}InvestigationGuide` as const;
|
||||
export const INVESTIGATION_GUIDE_LOADING_TEST_ID = `${INVESTIGATION_GUIDE_TEST_ID}Loading` as const;
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { EnrichmentRangePicker } from '../../../common/components/event_details/cti_details/enrichment_range_picker';
|
||||
import { ThreatDetailsView } from '../../../common/components/event_details/cti_details/threat_details_view';
|
||||
import { useThreatIntelligenceDetails } from '../hooks/use_threat_intelligence_details';
|
||||
import { THREAT_INTELLIGENCE_DETAILS_LOADING_TEST_ID } from './test_ids';
|
||||
import { FlyoutLoading } from '../../shared/components/flyout_loading';
|
||||
|
||||
export const THREAT_INTELLIGENCE_TAB_ID = 'threat-intelligence-details';
|
||||
|
||||
|
@ -29,33 +30,20 @@ export const ThreatIntelligenceDetails: React.FC = () => {
|
|||
setRange,
|
||||
} = useThreatIntelligenceDetails();
|
||||
|
||||
if (isEventDataLoading) {
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
justifyContent="spaceAround"
|
||||
data-test-subj={THREAT_INTELLIGENCE_DETAILS_LOADING_TEST_ID}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="m" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ThreatDetailsView
|
||||
before={null}
|
||||
loading={isLoading}
|
||||
enrichments={enrichments}
|
||||
showInvestigationTimeEnrichments={!isEmpty(eventFields)}
|
||||
>
|
||||
<>
|
||||
<EnrichmentRangePicker setRange={setRange} loading={isEnrichmentsLoading} range={range} />
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
</ThreatDetailsView>
|
||||
</>
|
||||
return isEventDataLoading ? (
|
||||
<FlyoutLoading data-test-subj={THREAT_INTELLIGENCE_DETAILS_LOADING_TEST_ID} />
|
||||
) : (
|
||||
<ThreatDetailsView
|
||||
before={null}
|
||||
loading={isLoading}
|
||||
enrichments={enrichments}
|
||||
showInvestigationTimeEnrichments={!isEmpty(eventFields)}
|
||||
>
|
||||
<>
|
||||
<EnrichmentRangePicker setRange={setRange} loading={isEnrichmentsLoading} range={range} />
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
</ThreatDetailsView>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ const panelContextValue = {
|
|||
...mockContextValue,
|
||||
};
|
||||
|
||||
const NO_DATA_MESSAGE = 'There was an error displaying data.';
|
||||
|
||||
describe('<AlertReasonPreview />', () => {
|
||||
it('should render alert reason preview', () => {
|
||||
const { getByTestId } = render(
|
||||
|
@ -35,4 +37,17 @@ describe('<AlertReasonPreview />', () => {
|
|||
expect(getByTestId(ALERT_REASON_PREVIEW_BODY_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(ALERT_REASON_PREVIEW_BODY_TEST_ID)).toHaveTextContent('Alert reason');
|
||||
});
|
||||
|
||||
it('should render no data message if alert reason is not available', () => {
|
||||
const { getByText } = render(
|
||||
<IntlProvider locale="en">
|
||||
<PreviewPanelContext.Provider value={{} as unknown as PreviewPanelContext}>
|
||||
<ThemeProvider theme={mockTheme}>
|
||||
<AlertReasonPreview />
|
||||
</ThemeProvider>
|
||||
</PreviewPanelContext.Provider>
|
||||
</IntlProvider>
|
||||
);
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@ import { ALERT_REASON_PREVIEW_BODY_TEST_ID } from './test_ids';
|
|||
import { usePreviewPanelContext } from '../context';
|
||||
import { getRowRenderer } from '../../../timelines/components/timeline/body/renderers/get_row_renderer';
|
||||
import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
|
||||
import { FlyoutError } from '../../shared/components/flyout_error';
|
||||
|
||||
const ReasonPreviewContainerWrapper = styled.div`
|
||||
overflow-x: auto;
|
||||
|
@ -47,7 +48,11 @@ export const AlertReasonPreview: React.FC = () => {
|
|||
);
|
||||
|
||||
if (!renderer) {
|
||||
return null;
|
||||
return (
|
||||
<EuiPanel hasShadow={false} hasBorder={false}>
|
||||
<FlyoutError />
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -67,11 +67,9 @@ const renderRulePreview = () =>
|
|||
</TestProviders>
|
||||
);
|
||||
|
||||
describe('<RulePreview />', () => {
|
||||
beforeEach(() => {
|
||||
// (useAppToasts as jest.Mock).mockReturnValue(useAppToastsValueMock);
|
||||
});
|
||||
const NO_DATA_MESSAGE = 'There was an error displaying data.';
|
||||
|
||||
describe('<RulePreview />', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
@ -134,9 +132,10 @@ describe('<RulePreview />', () => {
|
|||
|
||||
it('should not render rule preview when rule is null', async () => {
|
||||
mockUseRuleWithFallback.mockReturnValue({});
|
||||
const { queryByTestId } = renderRulePreview();
|
||||
const { queryByTestId, getByText } = renderRulePreview();
|
||||
await act(async () => {
|
||||
expect(queryByTestId(RULE_PREVIEW_BODY_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import React, { memo, useState, useEffect } from 'react';
|
||||
import { EuiText, EuiHorizontalRule, EuiSpacer, EuiPanel, EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { EuiText, EuiHorizontalRule, EuiSpacer, EuiPanel } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useKibana } from '../../../common/lib/kibana';
|
||||
import { useGetSavedQuery } from '../../../detections/pages/detection_engine/rules/use_get_saved_query';
|
||||
|
@ -19,6 +19,8 @@ import { StepAboutRuleReadOnly } from '../../../detections/components/rules/step
|
|||
import { StepDefineRuleReadOnly } from '../../../detections/components/rules/step_define_rule';
|
||||
import { StepScheduleRuleReadOnly } from '../../../detections/components/rules/step_schedule_rule';
|
||||
import { StepRuleActionsReadOnly } from '../../../detections/components/rules/step_rule_actions';
|
||||
import { FlyoutLoading } from '../../shared/components/flyout_loading';
|
||||
import { FlyoutError } from '../../shared/components/flyout_error';
|
||||
import {
|
||||
RULE_PREVIEW_BODY_TEST_ID,
|
||||
RULE_PREVIEW_ABOUT_TEST_ID,
|
||||
|
@ -79,7 +81,9 @@ export const RulePreview: React.FC = memo(() => {
|
|||
const hasResponseActions = Boolean(ruleActionsData?.responseActions?.length);
|
||||
const hasActions = ruleActionsData != null && (hasNotificationActions || hasResponseActions);
|
||||
|
||||
return rule ? (
|
||||
return ruleLoading ? (
|
||||
<FlyoutLoading data-test-subj={RULE_PREVIEW_LOADING_TEST_ID} />
|
||||
) : rule ? (
|
||||
<EuiPanel hasShadow={false} data-test-subj={RULE_PREVIEW_BODY_TEST_ID} className="eui-yScroll">
|
||||
<RulePreviewTitle rule={rule} isSuppressed={!isExistingRule} />
|
||||
<EuiHorizontalRule margin="s" />
|
||||
|
@ -169,9 +173,11 @@ export const RulePreview: React.FC = memo(() => {
|
|||
</ExpandableSection>
|
||||
)}
|
||||
</EuiPanel>
|
||||
) : ruleLoading ? (
|
||||
<EuiLoadingSpinner size="l" data-test-subj={RULE_PREVIEW_LOADING_TEST_ID} />
|
||||
) : null;
|
||||
) : (
|
||||
<EuiPanel hasBorder={false} hasShadow={false}>
|
||||
<FlyoutError />
|
||||
</EuiPanel>
|
||||
);
|
||||
});
|
||||
|
||||
RulePreview.displayName = 'RulePreview';
|
||||
|
|
|
@ -30,6 +30,8 @@ const renderAnalyzerPreview = (contextValue: RightPanelContext) =>
|
|||
</TestProviders>
|
||||
);
|
||||
|
||||
const NO_DATA_MESSAGE = 'An error is preventing this alert from being analyzed.';
|
||||
|
||||
describe('<AnalyzerPreview />', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
|
@ -57,7 +59,7 @@ describe('<AnalyzerPreview />', () => {
|
|||
expect(wrapper.getByTestId(ANALYZER_PREVIEW_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show analyzer preview when documentId and index are not present', () => {
|
||||
it('shows error message when documentid and index are not present', () => {
|
||||
mockUseAlertPrevalenceFromProcessTree.mockReturnValue({
|
||||
loading: false,
|
||||
error: false,
|
||||
|
@ -76,13 +78,25 @@ describe('<AnalyzerPreview />', () => {
|
|||
},
|
||||
],
|
||||
};
|
||||
const { queryByTestId } = renderAnalyzerPreview(contextValue);
|
||||
const { getByText } = renderAnalyzerPreview(contextValue);
|
||||
|
||||
expect(mockUseAlertPrevalenceFromProcessTree).toHaveBeenCalledWith({
|
||||
isActiveTimeline: false,
|
||||
documentId: '',
|
||||
indices: [],
|
||||
});
|
||||
expect(queryByTestId(ANALYZER_PREVIEW_TEST_ID)).not.toBeInTheDocument();
|
||||
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows error message when there is an error', () => {
|
||||
mockUseAlertPrevalenceFromProcessTree.mockReturnValue({
|
||||
loading: false,
|
||||
error: true,
|
||||
alertIds: undefined,
|
||||
statsNodes: undefined,
|
||||
});
|
||||
const { getByText } = renderAnalyzerPreview(mockContextValue);
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { find } from 'lodash/fp';
|
||||
import { EuiTreeView } from '@elastic/eui';
|
||||
import { EuiTreeView, EuiSkeletonText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ANALYZER_PREVIEW_TEST_ID } from './test_ids';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { ANALYZER_PREVIEW_TEST_ID, ANALYZER_PREVIEW_LOADING_TEST_ID } from './test_ids';
|
||||
import { getTreeNodes } from '../utils/analyzer_helpers';
|
||||
import { ANCESTOR_ID, RULE_INDICES } from '../../shared/constants/field_names';
|
||||
import { useRightPanelContext } from '../context';
|
||||
|
@ -41,7 +42,7 @@ export const AnalyzerPreview: React.FC = () => {
|
|||
const index = find({ category: 'kibana', field: RULE_INDICES }, data);
|
||||
const indices = index?.values ?? [];
|
||||
|
||||
const { statsNodes } = useAlertPrevalenceFromProcessTree({
|
||||
const { statsNodes, loading, error } = useAlertPrevalenceFromProcessTree({
|
||||
isActiveTimeline: isActiveTimeline(scopeId),
|
||||
documentId: processDocumentId,
|
||||
indices,
|
||||
|
@ -58,24 +59,36 @@ export const AnalyzerPreview: React.FC = () => {
|
|||
[cache.statsNodes]
|
||||
);
|
||||
|
||||
if (!documentId || !index || !items || items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const showAnalyzerTree = documentId && index && items && items.length > 0 && !error;
|
||||
|
||||
return (
|
||||
<div data-test-subj={ANALYZER_PREVIEW_TEST_ID}>
|
||||
<EuiTreeView
|
||||
items={items}
|
||||
display="compressed"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.visualizations.analyzerPreview.treeViewAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Analyzer preview',
|
||||
}
|
||||
)}
|
||||
showExpansionArrows
|
||||
/>
|
||||
</div>
|
||||
return loading ? (
|
||||
<EuiSkeletonText
|
||||
data-test-subj={ANALYZER_PREVIEW_LOADING_TEST_ID}
|
||||
contentAriaLabel={i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.visualizations.analyzerPreview.loadingAriaLabel',
|
||||
{
|
||||
defaultMessage: 'analyzer preview',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
) : showAnalyzerTree ? (
|
||||
<EuiTreeView
|
||||
items={items}
|
||||
display="compressed"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.visualizations.analyzerPreview.treeViewAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Analyzer preview',
|
||||
}
|
||||
)}
|
||||
showExpansionArrows
|
||||
data-test-subj={ANALYZER_PREVIEW_TEST_ID}
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.analyzerPreview.errorDescription"
|
||||
defaultMessage="An error is preventing this alert from being analyzed."
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -9,9 +9,10 @@ import { render, screen } from '@testing-library/react';
|
|||
import { TestProviders } from '../../../common/mock';
|
||||
import React from 'react';
|
||||
import { RightPanelContext } from '../context';
|
||||
import { mockContextValue } from '../mocks/mock_context';
|
||||
import { AnalyzerPreviewContainer } from './analyzer_preview_container';
|
||||
import { isInvestigateInResolverActionEnabled } from '../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver';
|
||||
import { ANALYZER_PREVIEW_NO_DATA_TEST_ID, ANALYZER_PREVIEW_TEST_ID } from './test_ids';
|
||||
import { ANALYZER_PREVIEW_TEST_ID } from './test_ids';
|
||||
import { useAlertPrevalenceFromProcessTree } from '../../../common/containers/alerts/use_alert_prevalence_from_process_tree';
|
||||
import * as mock from '../mocks/mock_analyzer_data';
|
||||
import {
|
||||
|
@ -42,9 +43,13 @@ jest.mock('react-redux', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const NO_ANALYZER_MESSAGE =
|
||||
'You can only visualize events triggered by hosts configured with the Elastic Defend integration or any sysmon data from winlogbeat. Refer to Visual event analyzerExternal link(opens in a new tab or window) for more information.';
|
||||
|
||||
const panelContextValue = {
|
||||
...mockContextValue,
|
||||
dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
|
||||
} as unknown as RightPanelContext;
|
||||
};
|
||||
|
||||
const renderAnalyzerPreview = () =>
|
||||
render(
|
||||
|
@ -72,10 +77,9 @@ describe('AnalyzerPreviewContainer', () => {
|
|||
investigateInTimelineAlertClick: jest.fn(),
|
||||
});
|
||||
|
||||
const { getByTestId, queryByTestId } = renderAnalyzerPreview();
|
||||
const { getByTestId } = renderAnalyzerPreview();
|
||||
|
||||
expect(getByTestId(ANALYZER_PREVIEW_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(ANALYZER_PREVIEW_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(
|
||||
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
|
||||
).toBeInTheDocument();
|
||||
|
@ -91,6 +95,9 @@ describe('AnalyzerPreviewContainer', () => {
|
|||
expect(
|
||||
screen.queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
|
||||
).not.toHaveTextContent(NO_ANALYZER_MESSAGE);
|
||||
});
|
||||
|
||||
it('should render error message and text in header', () => {
|
||||
|
@ -99,16 +106,13 @@ describe('AnalyzerPreviewContainer', () => {
|
|||
investigateInTimelineAlertClick: jest.fn(),
|
||||
});
|
||||
|
||||
const { getByTestId, queryByTestId } = renderAnalyzerPreview();
|
||||
|
||||
expect(queryByTestId(ANALYZER_PREVIEW_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(getByTestId(ANALYZER_PREVIEW_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(ANALYZER_PREVIEW_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'You can only visualize events triggered by hosts configured with the Elastic Defend integration or any sysmon data from winlogbeat. Refer to Visual event analyzerExternal link(opens in a new tab or window) for more information.'
|
||||
);
|
||||
const { getByTestId } = renderAnalyzerPreview();
|
||||
expect(
|
||||
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
|
||||
).toHaveTextContent(NO_ANALYZER_MESSAGE);
|
||||
});
|
||||
|
||||
it('should navigate to left section Visualize tab when clicking on title', () => {
|
||||
|
|
|
@ -18,7 +18,7 @@ import { setActiveTabTimeline } from '../../../timelines/store/timeline/actions'
|
|||
import { useRightPanelContext } from '../context';
|
||||
import { isInvestigateInResolverActionEnabled } from '../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver';
|
||||
import { AnalyzerPreview } from './analyzer_preview';
|
||||
import { ANALYZER_PREVIEW_NO_DATA_TEST_ID, ANALYZER_PREVIEW_TEST_ID } from './test_ids';
|
||||
import { ANALYZER_PREVIEW_TEST_ID } from './test_ids';
|
||||
import { ExpandablePanel } from '../../shared/components/expandable_panel';
|
||||
|
||||
const timelineId = 'timeline-1';
|
||||
|
@ -81,27 +81,25 @@ export const AnalyzerPreviewContainer: React.FC = () => {
|
|||
{isEnabled ? (
|
||||
<AnalyzerPreview />
|
||||
) : (
|
||||
<p data-test-subj={ANALYZER_PREVIEW_NO_DATA_TEST_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.analyzerPreview.noDataDescription"
|
||||
defaultMessage="You can only visualize events triggered by hosts configured with the Elastic Defend integration or any {sysmon} data from {winlogbeat}. Refer to {link} for more information."
|
||||
values={{
|
||||
sysmon: <EuiMark>{'sysmon'}</EuiMark>,
|
||||
winlogbeat: <EuiMark>{'winlogbeat'}</EuiMark>,
|
||||
link: (
|
||||
<EuiLink
|
||||
href="https://www.elastic.co/guide/en/security/current/visual-event-analyzer.html"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.analyzerPreview.noDataLinkText"
|
||||
defaultMessage="Visual event analyzer"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.analyzerPreview.noDataDescription"
|
||||
defaultMessage="You can only visualize events triggered by hosts configured with the Elastic Defend integration or any {sysmon} data from {winlogbeat}. Refer to {link} for more information."
|
||||
values={{
|
||||
sysmon: <EuiMark>{'sysmon'}</EuiMark>,
|
||||
winlogbeat: <EuiMark>{'winlogbeat'}</EuiMark>,
|
||||
link: (
|
||||
<EuiLink
|
||||
href="https://www.elastic.co/guide/en/security/current/visual-event-analyzer.html"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.analyzerPreview.noDataLinkText"
|
||||
defaultMessage="Visual event analyzer"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ExpandablePanel>
|
||||
);
|
||||
|
|
|
@ -14,7 +14,6 @@ import { CorrelationsOverview } from './correlations_overview';
|
|||
import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details';
|
||||
import { LeftPanelInsightsTab, LeftPanelKey } from '../../left';
|
||||
import {
|
||||
CORRELATIONS_NO_DATA_TEST_ID,
|
||||
CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID,
|
||||
CORRELATIONS_RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID,
|
||||
CORRELATIONS_RELATED_ALERTS_BY_SESSION_TEST_ID,
|
||||
|
@ -82,6 +81,8 @@ const renderCorrelationsOverview = (contextValue: RightPanelContext) => (
|
|||
</TestProviders>
|
||||
);
|
||||
|
||||
const NO_DATA_MESSAGE = 'No correlations data available.';
|
||||
|
||||
describe('<CorrelationsOverview />', () => {
|
||||
it('should render wrapper component', () => {
|
||||
jest.mocked(useShowRelatedAlertsByAncestry).mockReturnValue({ show: false });
|
||||
|
@ -131,13 +132,13 @@ describe('<CorrelationsOverview />', () => {
|
|||
dataCount: 1,
|
||||
});
|
||||
|
||||
const { getByTestId, queryByTestId } = render(renderCorrelationsOverview(panelContextValue));
|
||||
const { getByTestId, queryByText } = render(renderCorrelationsOverview(panelContextValue));
|
||||
expect(getByTestId(RELATED_ALERTS_BY_ANCESTRY_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(RELATED_ALERTS_BY_SESSION_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(RELATED_CASES_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(SUPPRESSED_ALERTS_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(CORRELATIONS_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should hide rows and show error message if show values are false', () => {
|
||||
|
@ -153,16 +154,13 @@ describe('<CorrelationsOverview />', () => {
|
|||
jest.mocked(useShowRelatedCases).mockReturnValue(false);
|
||||
jest.mocked(useShowSuppressedAlerts).mockReturnValue({ show: false, alertSuppressionCount: 0 });
|
||||
|
||||
const { getByTestId, queryByTestId } = render(renderCorrelationsOverview(panelContextValue));
|
||||
const { getByText, queryByTestId } = render(renderCorrelationsOverview(panelContextValue));
|
||||
expect(queryByTestId(RELATED_ALERTS_BY_ANCESTRY_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(RELATED_ALERTS_BY_SESSION_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(RELATED_CASES_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(SUPPRESSED_ALERTS_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(getByTestId(CORRELATIONS_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(CORRELATIONS_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'No correlations data available.'
|
||||
);
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should hide rows if values are null', () => {
|
||||
|
@ -172,13 +170,13 @@ describe('<CorrelationsOverview />', () => {
|
|||
jest.mocked(useShowRelatedCases).mockReturnValue(false);
|
||||
jest.mocked(useShowSuppressedAlerts).mockReturnValue({ show: false, alertSuppressionCount: 0 });
|
||||
|
||||
const { queryByTestId } = render(renderCorrelationsOverview(panelContextValue));
|
||||
const { queryByTestId, queryByText } = render(renderCorrelationsOverview(panelContextValue));
|
||||
expect(queryByTestId(RELATED_ALERTS_BY_ANCESTRY_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(RELATED_ALERTS_BY_SAME_SOURCE_EVENT_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(RELATED_ALERTS_BY_SESSION_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(RELATED_CASES_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(SUPPRESSED_ALERTS_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(CORRELATIONS_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should navigate to the left section Insights tab when clicking on button', () => {
|
||||
|
|
|
@ -20,7 +20,7 @@ import { SuppressedAlerts } from './suppressed_alerts';
|
|||
import { useShowSuppressedAlerts } from '../../shared/hooks/use_show_suppressed_alerts';
|
||||
import { RelatedCases } from './related_cases';
|
||||
import { useShowRelatedCases } from '../../shared/hooks/use_show_related_cases';
|
||||
import { CORRELATIONS_NO_DATA_TEST_ID, CORRELATIONS_TEST_ID } from './test_ids';
|
||||
import { CORRELATIONS_TEST_ID } from './test_ids';
|
||||
import { useRightPanelContext } from '../context';
|
||||
import { LeftPanelKey, LeftPanelInsightsTab } from '../../left';
|
||||
import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details';
|
||||
|
@ -120,12 +120,10 @@ export const CorrelationsOverview: React.FC = () => {
|
|||
)}
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<p data-test-subj={CORRELATIONS_NO_DATA_TEST_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.insights.correlations.noDataDescription"
|
||||
defaultMessage="No correlations data available."
|
||||
/>
|
||||
</p>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.insights.correlations.noDataDescription"
|
||||
defaultMessage="No correlations data available."
|
||||
/>
|
||||
)}
|
||||
</ExpandablePanel>
|
||||
);
|
||||
|
|
|
@ -8,7 +8,11 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage, __IntlProvider as IntlProvider } from '@kbn/i18n-react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { DESCRIPTION_TITLE_TEST_ID, RULE_SUMMARY_BUTTON_TEST_ID } from './test_ids';
|
||||
import {
|
||||
DESCRIPTION_TITLE_TEST_ID,
|
||||
RULE_SUMMARY_BUTTON_TEST_ID,
|
||||
DESCRIPTION_DETAILS_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { Description } from './description';
|
||||
import { RightPanelContext } from '../context';
|
||||
import { mockGetFieldsData } from '../../shared/mocks/mock_get_fields_data';
|
||||
|
@ -64,6 +68,8 @@ const renderDescription = (panelContext: RightPanelContext) =>
|
|||
</IntlProvider>
|
||||
);
|
||||
|
||||
const NO_DATA_MESSAGE = "There's no description for this rule.";
|
||||
|
||||
describe('<Description />', () => {
|
||||
it('should render the component', () => {
|
||||
const { getByTestId } = renderDescription(
|
||||
|
@ -72,17 +78,15 @@ describe('<Description />', () => {
|
|||
|
||||
expect(getByTestId(DESCRIPTION_TITLE_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(DESCRIPTION_TITLE_TEST_ID)).toHaveTextContent('Rule description');
|
||||
expect(getByTestId(DESCRIPTION_DETAILS_TEST_ID)).toHaveTextContent(ruleDescription.values[0]);
|
||||
expect(getByTestId(RULE_SUMMARY_BUTTON_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render rule preview button if rule name is not available', () => {
|
||||
const { getByTestId, queryByTestId } = renderDescription(
|
||||
panelContextValue([ruleUuid, ruleDescription])
|
||||
);
|
||||
it('should render no data message if rule description is not available', () => {
|
||||
const { getByTestId, getByText } = renderDescription(panelContextValue([ruleUuid]));
|
||||
|
||||
expect(getByTestId(DESCRIPTION_TITLE_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(DESCRIPTION_TITLE_TEST_ID)).toHaveTextContent('Rule description');
|
||||
expect(queryByTestId(RULE_SUMMARY_BUTTON_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(getByTestId(DESCRIPTION_DETAILS_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render document title if document is not an alert', () => {
|
||||
|
@ -92,32 +96,48 @@ describe('<Description />', () => {
|
|||
expect(getByTestId(DESCRIPTION_TITLE_TEST_ID)).toHaveTextContent('Document description');
|
||||
});
|
||||
|
||||
it('should open preview panel when clicking on button', () => {
|
||||
const panelContext = panelContextValue([ruleUuid, ruleDescription, ruleName]);
|
||||
describe('rule preview', () => {
|
||||
it('should render rule preview button as disabled if rule name is not available', () => {
|
||||
const { getByTestId } = renderDescription(panelContextValue([ruleUuid, ruleDescription]));
|
||||
expect(getByTestId(RULE_SUMMARY_BUTTON_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(RULE_SUMMARY_BUTTON_TEST_ID)).toHaveAttribute('disabled');
|
||||
});
|
||||
|
||||
const { getByTestId } = renderDescription(panelContext);
|
||||
it('should render rule preview button as disabled if rule id is not available', () => {
|
||||
const { getByTestId } = renderDescription(
|
||||
panelContextValue([{ ...ruleUuid, values: [] }, ruleName, ruleDescription])
|
||||
);
|
||||
expect(getByTestId(RULE_SUMMARY_BUTTON_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(RULE_SUMMARY_BUTTON_TEST_ID)).toHaveAttribute('disabled');
|
||||
});
|
||||
|
||||
getByTestId(RULE_SUMMARY_BUTTON_TEST_ID).click();
|
||||
it('should open preview panel when clicking on button', () => {
|
||||
const panelContext = panelContextValue([ruleUuid, ruleDescription, ruleName]);
|
||||
|
||||
expect(flyoutContextValue.openPreviewPanel).toHaveBeenCalledWith({
|
||||
id: PreviewPanelKey,
|
||||
path: { tab: 'rule-preview' },
|
||||
params: {
|
||||
id: panelContext.eventId,
|
||||
indexName: panelContext.indexName,
|
||||
scopeId: panelContext.scopeId,
|
||||
banner: {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.about.description.rulePreviewTitle"
|
||||
defaultMessage="Preview rule details"
|
||||
/>
|
||||
),
|
||||
backgroundColor: 'warning',
|
||||
textColor: 'warning',
|
||||
const { getByTestId } = renderDescription(panelContext);
|
||||
|
||||
getByTestId(RULE_SUMMARY_BUTTON_TEST_ID).click();
|
||||
|
||||
expect(flyoutContextValue.openPreviewPanel).toHaveBeenCalledWith({
|
||||
id: PreviewPanelKey,
|
||||
path: { tab: 'rule-preview' },
|
||||
params: {
|
||||
id: panelContext.eventId,
|
||||
indexName: panelContext.indexName,
|
||||
scopeId: panelContext.scopeId,
|
||||
banner: {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.about.description.rulePreviewTitle"
|
||||
defaultMessage="Preview rule details"
|
||||
/>
|
||||
),
|
||||
backgroundColor: 'warning',
|
||||
textColor: 'warning',
|
||||
},
|
||||
ruleId: ruleUuid.values[0],
|
||||
},
|
||||
ruleId: ruleUuid.values[0],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -56,34 +56,41 @@ export const Description: FC = () => {
|
|||
}, [eventId, openPreviewPanel, indexName, scopeId, ruleId]);
|
||||
|
||||
const viewRule = useMemo(
|
||||
() =>
|
||||
!isEmpty(ruleName) &&
|
||||
!isEmpty(ruleId) && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
size="s"
|
||||
iconType="expand"
|
||||
onClick={openRulePreview}
|
||||
iconSide="right"
|
||||
data-test-subj={RULE_SUMMARY_BUTTON_TEST_ID}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.about.description.ruleSummaryButtonAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Show rule summary',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.about.description.ruleSummaryButtonLabel"
|
||||
defaultMessage="Show rule summary"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
),
|
||||
() => (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
size="s"
|
||||
iconType="expand"
|
||||
onClick={openRulePreview}
|
||||
iconSide="right"
|
||||
data-test-subj={RULE_SUMMARY_BUTTON_TEST_ID}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.about.description.ruleSummaryButtonAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Show rule summary',
|
||||
}
|
||||
)}
|
||||
disabled={isEmpty(ruleName) || isEmpty(ruleId)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.about.description.ruleSummaryButtonLabel"
|
||||
defaultMessage="Show rule summary"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
),
|
||||
[ruleName, openRulePreview, ruleId]
|
||||
);
|
||||
|
||||
const hasRuleDescription = ruleDescription && ruleDescription.length > 0;
|
||||
const alertRuleDescription =
|
||||
ruleDescription?.length > 0 ? (
|
||||
ruleDescription
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.about.description.noRuleDescription"
|
||||
defaultMessage="There's no description for this rule."
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="column" gutterSize="s">
|
||||
|
@ -112,7 +119,7 @@ export const Description: FC = () => {
|
|||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem data-test-subj={DESCRIPTION_DETAILS_TEST_ID}>
|
||||
{hasRuleDescription ? ruleDescription : '-'}
|
||||
{isAlert ? alertRuleDescription : '-'}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -11,7 +11,6 @@ import { RightPanelContext } from '../context';
|
|||
import {
|
||||
ENTITIES_HOST_OVERVIEW_TEST_ID,
|
||||
ENTITIES_USER_OVERVIEW_TEST_ID,
|
||||
INSIGHTS_ENTITIES_NO_DATA_TEST_ID,
|
||||
INSIGHTS_ENTITIES_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { EntitiesOverview } from './entities_overview';
|
||||
|
@ -45,6 +44,8 @@ const renderEntitiesOverview = (contextValue: RightPanelContext) =>
|
|||
</TestProviders>
|
||||
);
|
||||
|
||||
const NO_DATA_MESSAGE = 'Host and user information are unavailable for this alert.';
|
||||
|
||||
describe('<EntitiesOverview />', () => {
|
||||
it('should render wrapper component', () => {
|
||||
const { getByTestId, queryByTestId } = renderEntitiesOverview(mockContextValue);
|
||||
|
@ -57,10 +58,10 @@ describe('<EntitiesOverview />', () => {
|
|||
});
|
||||
|
||||
it('should render user and host', () => {
|
||||
const { getByTestId, queryByTestId } = renderEntitiesOverview(mockContextValue);
|
||||
const { getByTestId, queryByText } = renderEntitiesOverview(mockContextValue);
|
||||
expect(getByTestId(ENTITIES_USER_OVERVIEW_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(ENTITIES_HOST_OVERVIEW_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(INSIGHTS_ENTITIES_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should only render user when host name is null', () => {
|
||||
|
@ -69,11 +70,11 @@ describe('<EntitiesOverview />', () => {
|
|||
getFieldsData: (field: string) => (field === 'user.name' ? 'user1' : null),
|
||||
} as unknown as RightPanelContext;
|
||||
|
||||
const { queryByTestId, getByTestId } = renderEntitiesOverview(contextValue);
|
||||
const { queryByTestId, getByTestId, queryByText } = renderEntitiesOverview(contextValue);
|
||||
|
||||
expect(getByTestId(ENTITIES_USER_OVERVIEW_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(ENTITIES_HOST_OVERVIEW_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(INSIGHTS_ENTITIES_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should only render host when user name is null', () => {
|
||||
|
@ -82,11 +83,11 @@ describe('<EntitiesOverview />', () => {
|
|||
getFieldsData: (field: string) => (field === 'host.name' ? 'host1' : null),
|
||||
} as unknown as RightPanelContext;
|
||||
|
||||
const { queryByTestId, getByTestId } = renderEntitiesOverview(contextValue);
|
||||
const { queryByTestId, getByTestId, queryByText } = renderEntitiesOverview(contextValue);
|
||||
|
||||
expect(getByTestId(ENTITIES_HOST_OVERVIEW_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(ENTITIES_USER_OVERVIEW_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(INSIGHTS_ENTITIES_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render no data message if both host name and user name are null/blank', () => {
|
||||
|
@ -95,11 +96,7 @@ describe('<EntitiesOverview />', () => {
|
|||
getFieldsData: (field: string) => {},
|
||||
} as unknown as RightPanelContext;
|
||||
|
||||
const { queryByTestId } = renderEntitiesOverview(contextValue);
|
||||
|
||||
expect(queryByTestId(INSIGHTS_ENTITIES_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(INSIGHTS_ENTITIES_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'Host and user information are unavailable for this alert.'
|
||||
);
|
||||
const { getByText } = renderEntitiesOverview(contextValue);
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ import React, { useCallback } from 'react';
|
|||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
||||
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { INSIGHTS_ENTITIES_NO_DATA_TEST_ID, INSIGHTS_ENTITIES_TEST_ID } from './test_ids';
|
||||
import { INSIGHTS_ENTITIES_TEST_ID } from './test_ids';
|
||||
import { ExpandablePanel } from '../../shared/components/expandable_panel';
|
||||
import { useRightPanelContext } from '../context';
|
||||
import { getField } from '../../shared/utils';
|
||||
|
@ -80,12 +80,10 @@ export const EntitiesOverview: React.FC = () => {
|
|||
)}
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<p data-test-subj={INSIGHTS_ENTITIES_NO_DATA_TEST_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.insights.entities.noDataDescription"
|
||||
defaultMessage="Host and user information are unavailable for this alert."
|
||||
/>
|
||||
</p>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.insights.entities.noDataDescription"
|
||||
defaultMessage="Host and user information are unavailable for this alert."
|
||||
/>
|
||||
)}
|
||||
</ExpandablePanel>
|
||||
</>
|
||||
|
|
|
@ -27,6 +27,8 @@ const renderHighlightedFields = (contextValue: RightPanelContext) =>
|
|||
</TestProviders>
|
||||
);
|
||||
|
||||
const NO_DATA_MESSAGE = "There's no highlighted fields for this alert.";
|
||||
|
||||
describe('<HighlightedFields />', () => {
|
||||
beforeEach(() => {
|
||||
(useRuleWithFallback as jest.Mock).mockReturnValue({ investigation_fields: undefined });
|
||||
|
@ -49,15 +51,14 @@ describe('<HighlightedFields />', () => {
|
|||
expect(getByTestId(HIGHLIGHTED_FIELDS_DETAILS_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it(`should render empty component if there aren't any highlighted fields`, () => {
|
||||
it(`should render no data message if there aren't any highlighted fields`, () => {
|
||||
const contextValue = {
|
||||
dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
|
||||
scopeId: 'scopeId',
|
||||
} as unknown as RightPanelContext;
|
||||
(useHighlightedFields as jest.Mock).mockReturnValue({});
|
||||
|
||||
const { container } = renderHighlightedFields(contextValue);
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
const { getByText } = renderHighlightedFields(contextValue);
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -95,7 +95,7 @@ const columns: Array<EuiBasicTableColumn<HighlightedFieldsTableRow>> = [
|
|||
export const HighlightedFields: FC = () => {
|
||||
const { dataFormattedForFieldBrowser, scopeId } = useRightPanelContext();
|
||||
const { ruleId } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser);
|
||||
const { rule: maybeRule } = useRuleWithFallback(ruleId);
|
||||
const { loading, error, rule: maybeRule } = useRuleWithFallback(ruleId);
|
||||
|
||||
const highlightedFields = useHighlightedFields({
|
||||
dataFormattedForFieldBrowser,
|
||||
|
@ -106,10 +106,6 @@ export const HighlightedFields: FC = () => {
|
|||
[highlightedFields, scopeId]
|
||||
);
|
||||
|
||||
if (items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="column" gutterSize="s">
|
||||
<EuiFlexItem data-test-subj={HIGHLIGHTED_FIELDS_TITLE_TEST_ID}>
|
||||
|
@ -124,7 +120,18 @@ export const HighlightedFields: FC = () => {
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem data-test-subj={HIGHLIGHTED_FIELDS_DETAILS_TEST_ID}>
|
||||
<EuiPanel hasBorder hasShadow={false}>
|
||||
<EuiInMemoryTable items={items} columns={columns} compressed />
|
||||
<EuiInMemoryTable
|
||||
items={error ? [] : items}
|
||||
columns={columns}
|
||||
compressed
|
||||
loading={loading}
|
||||
message={
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.investigation.highlightedFields.noDataDescription"
|
||||
defaultMessage="There's no highlighted fields for this alert."
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
ENTITIES_HOST_OVERVIEW_LAST_SEEN_TEST_ID,
|
||||
ENTITIES_HOST_OVERVIEW_LINK_TEST_ID,
|
||||
ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID,
|
||||
ENTITIES_HOST_OVERVIEW_LOADING_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { RightPanelContext } from '../context';
|
||||
import { mockContextValue } from '../mocks/mock_context';
|
||||
|
@ -100,6 +101,33 @@ describe('<HostEntityContent />', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should render loading if loading for host details is true', () => {
|
||||
mockUseHostDetails.mockReturnValue([true, { hostDetails: null }]);
|
||||
mockUseRiskScore.mockReturnValue({ data: null, isAuthorized: true });
|
||||
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
<RightPanelContext.Provider value={panelContextValue}>
|
||||
<HostEntityOverview hostName={hostName} />
|
||||
</RightPanelContext.Provider>
|
||||
</TestProviders>
|
||||
);
|
||||
expect(getByTestId(ENTITIES_HOST_OVERVIEW_LOADING_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render loading if loading for risk score is true', () => {
|
||||
mockUseHostDetails.mockReturnValue([false, { hostDetails: null }]);
|
||||
mockUseRiskScore.mockReturnValue({ data: null, isAuthorized: true, loading: true });
|
||||
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
<RightPanelContext.Provider value={panelContextValue}>
|
||||
<HostEntityOverview hostName={hostName} />
|
||||
</RightPanelContext.Provider>
|
||||
</TestProviders>
|
||||
);
|
||||
expect(getByTestId(ENTITIES_HOST_OVERVIEW_LOADING_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
describe('license is not valid', () => {
|
||||
it('should render os family and last seen', () => {
|
||||
mockUseHostDetails.mockReturnValue([false, { hostDetails: hostData }]);
|
||||
|
|
|
@ -14,9 +14,11 @@ import {
|
|||
useEuiTheme,
|
||||
useEuiFontSize,
|
||||
EuiIconTip,
|
||||
EuiSkeletonText,
|
||||
} from '@elastic/eui';
|
||||
import { css } from '@emotion/css';
|
||||
import { getOr } from 'lodash/fp';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useRightPanelContext } from '../context';
|
||||
|
@ -35,7 +37,11 @@ import { useSourcererDataView } from '../../../common/containers/sourcerer';
|
|||
import { useGlobalTime } from '../../../common/containers/use_global_time';
|
||||
import { useRiskScore } from '../../../explore/containers/risk_score';
|
||||
import { useHostDetails } from '../../../explore/hosts/containers/hosts/details';
|
||||
import * as i18n from '../../../overview/components/host_overview/translations';
|
||||
import {
|
||||
FAMILY,
|
||||
LAST_SEEN,
|
||||
HOST_RISK_CLASSIFICATION,
|
||||
} from '../../../overview/components/host_overview/translations';
|
||||
import { ENTITIES_TAB_ID } from '../../left/components/entities_details';
|
||||
import {
|
||||
ENTITIES_HOST_OVERVIEW_TEST_ID,
|
||||
|
@ -43,6 +49,7 @@ import {
|
|||
ENTITIES_HOST_OVERVIEW_LAST_SEEN_TEST_ID,
|
||||
ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID,
|
||||
ENTITIES_HOST_OVERVIEW_LINK_TEST_ID,
|
||||
ENTITIES_HOST_OVERVIEW_LOADING_TEST_ID,
|
||||
TECHNICAL_PREVIEW_ICON_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { LeftPanelInsightsTab, LeftPanelKey } from '../../left';
|
||||
|
@ -91,14 +98,18 @@ export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName
|
|||
[hostName]
|
||||
);
|
||||
|
||||
const { data: hostRisk, isAuthorized } = useRiskScore({
|
||||
const {
|
||||
data: hostRisk,
|
||||
isAuthorized,
|
||||
loading: isRiskScoreLoading,
|
||||
} = useRiskScore({
|
||||
filterQuery,
|
||||
riskEntity: RiskScoreEntity.host,
|
||||
skip: hostName == null,
|
||||
timerange,
|
||||
});
|
||||
|
||||
const [_, { hostDetails }] = useHostDetails({
|
||||
const [isHostDetailsLoading, { hostDetails }] = useHostDetails({
|
||||
hostName,
|
||||
indexNames: selectedPatterns,
|
||||
startDate: from,
|
||||
|
@ -108,7 +119,7 @@ export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName
|
|||
const hostOSFamily: DescriptionList[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: i18n.FAMILY,
|
||||
title: FAMILY,
|
||||
description: (
|
||||
<DefaultFieldRenderer
|
||||
rowItems={getOr([], 'host.os.family', hostDetails)}
|
||||
|
@ -125,7 +136,7 @@ export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName
|
|||
const hostLastSeen: DescriptionList[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: i18n.LAST_SEEN,
|
||||
title: LAST_SEEN,
|
||||
description: (
|
||||
<FirstLastSeen
|
||||
indexPatterns={selectedPatterns}
|
||||
|
@ -148,7 +159,7 @@ export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName
|
|||
{
|
||||
title: (
|
||||
<>
|
||||
{i18n.HOST_RISK_CLASSIFICATION}
|
||||
{HOST_RISK_CLASSIFICATION}
|
||||
<EuiIconTip
|
||||
title={
|
||||
<FormattedMessage
|
||||
|
@ -206,29 +217,39 @@ export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<OverviewDescriptionList
|
||||
dataTestSubj={ENTITIES_HOST_OVERVIEW_OS_FAMILY_TEST_ID}
|
||||
descriptionList={hostOSFamily}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{isAuthorized ? (
|
||||
<DescriptionListStyled
|
||||
data-test-subj={ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID}
|
||||
listItems={[hostRiskLevel]}
|
||||
/>
|
||||
) : (
|
||||
{isRiskScoreLoading || isHostDetailsLoading ? (
|
||||
<EuiSkeletonText
|
||||
data-test-subj={ENTITIES_HOST_OVERVIEW_LOADING_TEST_ID}
|
||||
contentAriaLabel={i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.insights.entities.hostLoadingAriaLabel',
|
||||
{ defaultMessage: 'host overview' }
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<OverviewDescriptionList
|
||||
dataTestSubj={ENTITIES_HOST_OVERVIEW_LAST_SEEN_TEST_ID}
|
||||
descriptionList={hostLastSeen}
|
||||
dataTestSubj={ENTITIES_HOST_OVERVIEW_OS_FAMILY_TEST_ID}
|
||||
descriptionList={hostOSFamily}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{isAuthorized ? (
|
||||
<DescriptionListStyled
|
||||
data-test-subj={ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID}
|
||||
listItems={[hostRiskLevel]}
|
||||
/>
|
||||
) : (
|
||||
<OverviewDescriptionList
|
||||
dataTestSubj={ENTITIES_HOST_OVERVIEW_LAST_SEEN_TEST_ID}
|
||||
descriptionList={hostLastSeen}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -13,7 +13,6 @@ import { RightPanelContext } from '../context';
|
|||
import {
|
||||
INVESTIGATION_GUIDE_BUTTON_TEST_ID,
|
||||
INVESTIGATION_GUIDE_LOADING_TEST_ID,
|
||||
INVESTIGATION_GUIDE_NO_DATA_TEST_ID,
|
||||
INVESTIGATION_GUIDE_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { mockContextValue } from '../mocks/mock_context';
|
||||
|
@ -24,6 +23,8 @@ import { LeftPanelInvestigationTab, LeftPanelKey } from '../../left';
|
|||
|
||||
jest.mock('../../shared/hooks/use_investigation_guide');
|
||||
|
||||
const NO_DATA_MESSAGE = 'Investigation guideThere’s no investigation guide for this rule.';
|
||||
|
||||
const renderInvestigationGuide = () =>
|
||||
render(
|
||||
<IntlProvider locale="en">
|
||||
|
@ -51,7 +52,7 @@ describe('<InvestigationGuide />', () => {
|
|||
'Show investigation guide'
|
||||
);
|
||||
expect(queryByTestId(INVESTIGATION_GUIDE_LOADING_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).not.toHaveTextContent(NO_DATA_MESSAGE);
|
||||
});
|
||||
|
||||
it('should render loading', () => {
|
||||
|
@ -59,10 +60,10 @@ describe('<InvestigationGuide />', () => {
|
|||
loading: true,
|
||||
});
|
||||
const { getByTestId, queryByTestId } = renderInvestigationGuide();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_LOADING_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(INVESTIGATION_GUIDE_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(INVESTIGATION_GUIDE_BUTTON_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).not.toHaveTextContent(NO_DATA_MESSAGE);
|
||||
});
|
||||
|
||||
it('should render no data message when there is no ruleId', () => {
|
||||
|
@ -70,12 +71,9 @@ describe('<InvestigationGuide />', () => {
|
|||
basicAlertData: {},
|
||||
ruleNote: 'test note',
|
||||
});
|
||||
const { getByTestId, queryByTestId } = renderInvestigationGuide();
|
||||
const { queryByTestId, getByTestId } = renderInvestigationGuide();
|
||||
expect(queryByTestId(INVESTIGATION_GUIDE_BUTTON_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'There’s no investigation guide for this rule.'
|
||||
);
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).toHaveTextContent(NO_DATA_MESSAGE);
|
||||
});
|
||||
|
||||
it('should render no data message when there is no rule note', () => {
|
||||
|
@ -85,10 +83,7 @@ describe('<InvestigationGuide />', () => {
|
|||
});
|
||||
const { getByTestId, queryByTestId } = renderInvestigationGuide();
|
||||
expect(queryByTestId(INVESTIGATION_GUIDE_BUTTON_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'There’s no investigation guide for this rule.'
|
||||
);
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).toHaveTextContent(NO_DATA_MESSAGE);
|
||||
});
|
||||
|
||||
it('should render no data message when useInvestigationGuide errors out', () => {
|
||||
|
@ -97,8 +92,9 @@ describe('<InvestigationGuide />', () => {
|
|||
error: true,
|
||||
});
|
||||
|
||||
const { getByTestId } = renderInvestigationGuide();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
const { queryByTestId, getByTestId } = renderInvestigationGuide();
|
||||
expect(queryByTestId(INVESTIGATION_GUIDE_BUTTON_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).toHaveTextContent(NO_DATA_MESSAGE);
|
||||
});
|
||||
|
||||
it('should navigate to investigation guide when clicking on button', () => {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import React, { useCallback } from 'react';
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiTitle } from '@elastic/eui';
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSkeletonText } from '@elastic/eui';
|
||||
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -15,7 +15,6 @@ import { LeftPanelKey, LeftPanelInvestigationTab } from '../../left';
|
|||
import {
|
||||
INVESTIGATION_GUIDE_BUTTON_TEST_ID,
|
||||
INVESTIGATION_GUIDE_LOADING_TEST_ID,
|
||||
INVESTIGATION_GUIDE_NO_DATA_TEST_ID,
|
||||
INVESTIGATION_GUIDE_TEST_ID,
|
||||
} from './test_ids';
|
||||
|
||||
|
@ -45,22 +44,9 @@ export const InvestigationGuide: React.FC = () => {
|
|||
});
|
||||
}, [eventId, indexName, openLeftPanel, scopeId]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
justifyContent="spaceAround"
|
||||
data-test-subj={INVESTIGATION_GUIDE_LOADING_TEST_ID}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="m" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="column" gutterSize="s">
|
||||
<EuiFlexItem data-test-subj={INVESTIGATION_GUIDE_TEST_ID}>
|
||||
<EuiFlexGroup direction="column" gutterSize="s" data-test-subj={INVESTIGATION_GUIDE_TEST_ID}>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="xxs">
|
||||
<h5>
|
||||
<FormattedMessage
|
||||
|
@ -70,8 +56,16 @@ export const InvestigationGuide: React.FC = () => {
|
|||
</h5>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{!error && basicAlertData.ruleId && ruleNote ? (
|
||||
{loading ? (
|
||||
<EuiSkeletonText
|
||||
data-test-subj={INVESTIGATION_GUIDE_LOADING_TEST_ID}
|
||||
contentAriaLabel={i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.investigation.investigationGuide.investigationGuideLoadingAriaLabel',
|
||||
{ defaultMessage: 'investigation guide' }
|
||||
)}
|
||||
/>
|
||||
) : !error && basicAlertData.ruleId && ruleNote ? (
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
onClick={goToInvestigationsTab}
|
||||
iconType="documentation"
|
||||
|
@ -88,15 +82,13 @@ export const InvestigationGuide: React.FC = () => {
|
|||
defaultMessage="Show investigation guide"
|
||||
/>
|
||||
</EuiButton>
|
||||
) : (
|
||||
<p data-test-subj={INVESTIGATION_GUIDE_NO_DATA_TEST_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.investigation.investigationGuide.noDataDescription"
|
||||
defaultMessage="There’s no investigation guide for this rule."
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexItem>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.investigation.investigationGuide.noDataDescription"
|
||||
defaultMessage="There’s no investigation guide for this rule."
|
||||
/>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ export const MitreAttack: FC = () => {
|
|||
const threatDetails = useMemo(() => getMitreComponentParts(searchHit), [searchHit]);
|
||||
|
||||
if (!threatDetails || !threatDetails[0]) {
|
||||
// Do not render empty message on MITRE attack because other frameworks could be used
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context';
|
|||
import { render } from '@testing-library/react';
|
||||
import { TestProviders } from '../../../common/mock';
|
||||
import { RightPanelContext } from '../context';
|
||||
import { PREVALENCE_NO_DATA_TEST_ID, PREVALENCE_TEST_ID } from './test_ids';
|
||||
import { PREVALENCE_TEST_ID } from './test_ids';
|
||||
import { LeftPanelInsightsTab, LeftPanelKey } from '../../left';
|
||||
import React from 'react';
|
||||
import { PrevalenceOverview } from './prevalence_overview';
|
||||
|
@ -31,6 +31,8 @@ const TITLE_LINK_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(PREVALENCE
|
|||
const TITLE_ICON_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(PREVALENCE_TEST_ID);
|
||||
const TITLE_TEXT_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(PREVALENCE_TEST_ID);
|
||||
|
||||
const NO_DATA_MESSAGE = 'No prevalence data available.';
|
||||
|
||||
const flyoutContextValue = {
|
||||
openLeftPanel: jest.fn(),
|
||||
} as unknown as ExpandableFlyoutContext;
|
||||
|
@ -69,10 +71,10 @@ describe('<PrevalenceOverview />', () => {
|
|||
data: [],
|
||||
});
|
||||
|
||||
const { getByTestId, queryByTestId } = renderPrevalenceOverview();
|
||||
const { getByTestId, queryByText } = renderPrevalenceOverview();
|
||||
|
||||
expect(getByTestId(EXPANDABLE_PANEL_LOADING_TEST_ID(PREVALENCE_TEST_ID))).toBeInTheDocument();
|
||||
expect(queryByTestId(PREVALENCE_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render no-data message', () => {
|
||||
|
@ -82,12 +84,8 @@ describe('<PrevalenceOverview />', () => {
|
|||
data: [],
|
||||
});
|
||||
|
||||
const { getByTestId } = renderPrevalenceOverview();
|
||||
|
||||
expect(getByTestId(PREVALENCE_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(PREVALENCE_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'No prevalence data available.'
|
||||
);
|
||||
const { getByText } = renderPrevalenceOverview();
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render only data with prevalence less than 10%', () => {
|
||||
|
@ -116,7 +114,7 @@ describe('<PrevalenceOverview />', () => {
|
|||
],
|
||||
});
|
||||
|
||||
const { queryByTestId, getByTestId } = renderPrevalenceOverview();
|
||||
const { queryByTestId, getByTestId, queryByText } = renderPrevalenceOverview();
|
||||
|
||||
expect(getByTestId(TITLE_LINK_TEST_ID)).toHaveTextContent('Prevalence');
|
||||
|
||||
|
@ -131,7 +129,7 @@ describe('<PrevalenceOverview />', () => {
|
|||
expect(queryByTestId(iconDataTestSubj2)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(valueDataTestSubj2)).not.toBeInTheDocument();
|
||||
|
||||
expect(queryByTestId(PREVALENCE_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should navigate to left section Insights tab when clicking on button', () => {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { ExpandablePanel } from '../../shared/components/expandable_panel';
|
||||
import { usePrevalence } from '../../shared/hooks/use_prevalence';
|
||||
import { PREVALENCE_NO_DATA_TEST_ID, PREVALENCE_TEST_ID } from './test_ids';
|
||||
import { PREVALENCE_TEST_ID } from './test_ids';
|
||||
import { useRightPanelContext } from '../context';
|
||||
import { LeftPanelKey, LeftPanelInsightsTab } from '../../left';
|
||||
import { PREVALENCE_TAB_ID } from '../../left/components/prevalence_details';
|
||||
|
@ -107,12 +107,10 @@ export const PrevalenceOverview: FC = () => {
|
|||
/>
|
||||
))
|
||||
) : (
|
||||
<p data-test-subj={PREVALENCE_NO_DATA_TEST_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.insights.prevalence.noDataDescription"
|
||||
defaultMessage="No prevalence data available."
|
||||
/>
|
||||
</p>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.insights.prevalence.noDataDescription"
|
||||
defaultMessage="No prevalence data available."
|
||||
/>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</ExpandablePanel>
|
||||
|
|
|
@ -8,11 +8,7 @@
|
|||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { FormattedMessage, __IntlProvider as IntlProvider } from '@kbn/i18n-react';
|
||||
import {
|
||||
REASON_DETAILS_PREVIEW_BUTTON_TEST_ID,
|
||||
REASON_DETAILS_TEST_ID,
|
||||
REASON_TITLE_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { REASON_DETAILS_PREVIEW_BUTTON_TEST_ID, REASON_TITLE_TEST_ID } from './test_ids';
|
||||
import { Reason } from './reason';
|
||||
import { RightPanelContext } from '../context';
|
||||
import { mockGetFieldsData } from '../../shared/mocks/mock_get_fields_data';
|
||||
|
@ -43,6 +39,8 @@ const renderReason = (panelContext: RightPanelContext = panelContextValue) =>
|
|||
</IntlProvider>
|
||||
);
|
||||
|
||||
const NO_DATA_MESSAGE = "There's no source event information for this alert.";
|
||||
|
||||
describe('<Reason />', () => {
|
||||
it('should render the component for alert', () => {
|
||||
const { getByTestId } = renderReason();
|
||||
|
@ -73,9 +71,9 @@ describe('<Reason />', () => {
|
|||
getFieldsData: () => {},
|
||||
} as unknown as RightPanelContext;
|
||||
|
||||
const { getByTestId } = renderReason(panelContext);
|
||||
const { getByText } = renderReason(panelContext);
|
||||
|
||||
expect(getByTestId(REASON_DETAILS_TEST_ID)).toBeEmptyDOMElement();
|
||||
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should open preview panel when clicking on button', () => {
|
||||
|
|
|
@ -69,6 +69,7 @@ export const Reason: FC = () => {
|
|||
defaultMessage: 'Show full reason',
|
||||
}
|
||||
)}
|
||||
disabled={!alertReason}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.about.reason.alertReasonButtonLabel"
|
||||
|
@ -77,7 +78,16 @@ export const Reason: FC = () => {
|
|||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
),
|
||||
[openRulePreview]
|
||||
[alertReason, openRulePreview]
|
||||
);
|
||||
|
||||
const alertReasonText = alertReason ? (
|
||||
alertReason
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.about.reason.noReasonDescription"
|
||||
defaultMessage="There's no source event information for this alert."
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -108,7 +118,9 @@ export const Reason: FC = () => {
|
|||
</h5>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem data-test-subj={REASON_DETAILS_TEST_ID}>{alertReason}</EuiFlexItem>
|
||||
<EuiFlexItem data-test-subj={REASON_DETAILS_TEST_ID}>
|
||||
{isAlert ? alertReasonText : '-'}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -12,11 +12,7 @@ import { RightPanelContext } from '../context';
|
|||
import { SessionPreviewContainer } from './session_preview_container';
|
||||
import { useSessionPreview } from '../hooks/use_session_preview';
|
||||
import { useLicense } from '../../../common/hooks/use_license';
|
||||
import {
|
||||
SESSION_PREVIEW_NO_DATA_TEST_ID,
|
||||
SESSION_PREVIEW_TEST_ID,
|
||||
SESSION_PREVIEW_UPSELL_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { SESSION_PREVIEW_TEST_ID } from './test_ids';
|
||||
import {
|
||||
EXPANDABLE_PANEL_CONTENT_TEST_ID,
|
||||
EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID,
|
||||
|
@ -29,6 +25,11 @@ import { mockGetFieldsData } from '../../shared/mocks/mock_get_fields_data';
|
|||
jest.mock('../hooks/use_session_preview');
|
||||
jest.mock('../../../common/hooks/use_license');
|
||||
|
||||
const NO_DATA_MESSAGE =
|
||||
'You can only view Linux session details if you’ve enabled the Include session data setting in your Elastic Defend integration policy. Refer to Enable Session View dataExternal link(opens in a new tab or window) for more information.';
|
||||
|
||||
const UPSELL_TEXT = 'This feature requires an Enterprise subscription';
|
||||
|
||||
const panelContextValue = {
|
||||
getFieldsData: mockGetFieldsData,
|
||||
} as unknown as RightPanelContext;
|
||||
|
@ -57,14 +58,13 @@ describe('SessionPreviewContainer', () => {
|
|||
(useSessionPreview as jest.Mock).mockReturnValue(sessionViewConfig);
|
||||
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
|
||||
|
||||
const { getByTestId, queryByTestId } = renderSessionPreview();
|
||||
const { getByTestId } = renderSessionPreview();
|
||||
|
||||
expect(getByTestId(SESSION_PREVIEW_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(SESSION_PREVIEW_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(SESSION_PREVIEW_UPSELL_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(
|
||||
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
screen.queryByTestId(EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID(SESSION_PREVIEW_TEST_ID))
|
||||
).not.toBeInTheDocument();
|
||||
|
@ -77,6 +77,12 @@ describe('SessionPreviewContainer', () => {
|
|||
expect(
|
||||
screen.queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
|
||||
).not.toHaveTextContent(NO_DATA_MESSAGE);
|
||||
expect(
|
||||
screen.queryByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
|
||||
).not.toHaveTextContent(UPSELL_TEXT);
|
||||
});
|
||||
|
||||
it('should render error message and text in header if no sessionConfig', () => {
|
||||
|
@ -84,32 +90,35 @@ describe('SessionPreviewContainer', () => {
|
|||
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
|
||||
|
||||
const { getByTestId, queryByTestId } = renderSessionPreview();
|
||||
|
||||
expect(queryByTestId(SESSION_PREVIEW_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(getByTestId(SESSION_PREVIEW_NO_DATA_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(SESSION_PREVIEW_NO_DATA_TEST_ID)).toHaveTextContent(
|
||||
'You can only view Linux session details if you’ve enabled the Include session data setting in your Elastic Defend integration policy. Refer to Enable Session View dataExternal link(opens in a new tab or window) for more information.'
|
||||
);
|
||||
expect(queryByTestId(SESSION_PREVIEW_UPSELL_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(
|
||||
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
|
||||
).toHaveTextContent(NO_DATA_MESSAGE);
|
||||
|
||||
expect(
|
||||
screen.queryByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
|
||||
).not.toHaveTextContent(UPSELL_TEXT);
|
||||
expect(queryByTestId(SESSION_PREVIEW_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render error message and text in header if no correct license', () => {
|
||||
it('should render upsell message in header if no correct license', () => {
|
||||
(useSessionPreview as jest.Mock).mockReturnValue(sessionViewConfig);
|
||||
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => false });
|
||||
|
||||
const { getByTestId, queryByTestId } = renderSessionPreview();
|
||||
|
||||
expect(queryByTestId(SESSION_PREVIEW_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(SESSION_PREVIEW_NO_DATA_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(getByTestId(SESSION_PREVIEW_UPSELL_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(SESSION_PREVIEW_UPSELL_TEST_ID)).toHaveTextContent(
|
||||
'This feature requires an Enterprise subscription'
|
||||
);
|
||||
expect(
|
||||
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
|
||||
).toHaveTextContent(UPSELL_TEXT);
|
||||
|
||||
expect(
|
||||
screen.queryByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
|
||||
).not.toHaveTextContent(NO_DATA_MESSAGE);
|
||||
expect(queryByTestId(SESSION_PREVIEW_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,11 +18,7 @@ import { useInvestigateInTimeline } from '../../../detections/components/alerts_
|
|||
import { useRightPanelContext } from '../context';
|
||||
import { ALERTS_ACTIONS } from '../../../common/lib/apm/user_actions';
|
||||
import { ExpandablePanel } from '../../shared/components/expandable_panel';
|
||||
import {
|
||||
SESSION_PREVIEW_NO_DATA_TEST_ID,
|
||||
SESSION_PREVIEW_TEST_ID,
|
||||
SESSION_PREVIEW_UPSELL_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { SESSION_PREVIEW_TEST_ID } from './test_ids';
|
||||
import { useStartTransaction } from '../../../common/lib/apm/use_start_transaction';
|
||||
import { setActiveTabTimeline } from '../../../timelines/store/timeline/actions';
|
||||
import { getScopedActions } from '../../../helpers';
|
||||
|
@ -70,54 +66,50 @@ export const SessionPreviewContainer: FC = () => {
|
|||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
const noSessionMessage = !isEnterprisePlus ? (
|
||||
<div data-test-subj={SESSION_PREVIEW_UPSELL_TEST_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.sessionPreview.upsellDescription"
|
||||
defaultMessage="This feature requires an {subscription}"
|
||||
values={{
|
||||
subscription: (
|
||||
<EuiLink href="https://www.elastic.co/pricing/" target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.sessionPreview.upsellLinkText"
|
||||
defaultMessage="Enterprise subscription"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.sessionPreview.upsellDescription"
|
||||
defaultMessage="This feature requires an {subscription}"
|
||||
values={{
|
||||
subscription: (
|
||||
<EuiLink href="https://www.elastic.co/pricing/" target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.sessionPreview.upsellLinkText"
|
||||
defaultMessage="Enterprise subscription"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
) : !sessionViewConfig ? (
|
||||
<div data-test-subj={SESSION_PREVIEW_NO_DATA_TEST_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.sessionPreview.noDataDescription"
|
||||
defaultMessage="You can only view Linux session details if you’ve enabled the {setting} setting in your Elastic Defend integration policy. Refer to {link} for more information."
|
||||
values={{
|
||||
setting: (
|
||||
<span
|
||||
css={css`
|
||||
font-weight: ${euiTheme.font.weight.bold};
|
||||
`}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.sessionPreview.noDataSettingDescription"
|
||||
defaultMessage="Include session data"
|
||||
/>
|
||||
</span>
|
||||
),
|
||||
link: (
|
||||
<EuiLink
|
||||
href="https://www.elastic.co/guide/en/security/current/session-view.html#enable-session-view"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.sessionPreview.noDataLinkText"
|
||||
defaultMessage="Enable Session View data"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.sessionPreview.noDataDescription"
|
||||
defaultMessage="You can only view Linux session details if you’ve enabled the {setting} setting in your Elastic Defend integration policy. Refer to {link} for more information."
|
||||
values={{
|
||||
setting: (
|
||||
<span
|
||||
css={css`
|
||||
font-weight: ${euiTheme.font.weight.bold};
|
||||
`}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.sessionPreview.noDataSettingDescription"
|
||||
defaultMessage="Include session data"
|
||||
/>
|
||||
</span>
|
||||
),
|
||||
link: (
|
||||
<EuiLink
|
||||
href="https://www.elastic.co/guide/en/security/current/session-view.html#enable-session-view"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.visualizations.sessionPreview.noDataLinkText"
|
||||
defaultMessage="Enable Session View data"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
|
|
|
@ -28,14 +28,17 @@ export const CHAT_BUTTON_TEST_ID = 'newChatById' as const;
|
|||
export const ABOUT_SECTION_TEST_ID = `${PREFIX}AboutSection` as const;
|
||||
export const ABOUT_SECTION_HEADER_TEST_ID = ABOUT_SECTION_TEST_ID + HEADER_TEST_ID;
|
||||
export const ABOUT_SECTION_CONTENT_TEST_ID = ABOUT_SECTION_TEST_ID + CONTENT_TEST_ID;
|
||||
|
||||
export const RULE_SUMMARY_BUTTON_TEST_ID = `${PREFIX}RuleSummaryButton` as const;
|
||||
const DESCRIPTION_TEST_ID = `${PREFIX}Description` as const;
|
||||
export const DESCRIPTION_TITLE_TEST_ID = `${DESCRIPTION_TEST_ID}Title` as const;
|
||||
export const DESCRIPTION_DETAILS_TEST_ID = `${DESCRIPTION_TEST_ID}Details` as const;
|
||||
|
||||
const REASON_TEST_ID = `${PREFIX}Reason` as const;
|
||||
export const REASON_TITLE_TEST_ID = `${REASON_TEST_ID}Title` as const;
|
||||
export const REASON_DETAILS_TEST_ID = `${REASON_TEST_ID}Details` as const;
|
||||
export const REASON_DETAILS_PREVIEW_BUTTON_TEST_ID = `${REASON_TEST_ID}PreviewButton` as const;
|
||||
|
||||
const MITRE_ATTACK_TEST_ID = `${PREFIX}MitreAttack` as const;
|
||||
export const MITRE_ATTACK_TITLE_TEST_ID = `${MITRE_ATTACK_TEST_ID}Title` as const;
|
||||
export const MITRE_ATTACK_DETAILS_TEST_ID = `${MITRE_ATTACK_TEST_ID}Details` as const;
|
||||
|
@ -46,10 +49,11 @@ export const INVESTIGATION_SECTION_TEST_ID = `${PREFIX}InvestigationSection` as
|
|||
export const INVESTIGATION_SECTION_HEADER_TEST_ID = INVESTIGATION_SECTION_TEST_ID + HEADER_TEST_ID;
|
||||
export const INVESTIGATION_SECTION_CONTENT_TEST_ID =
|
||||
INVESTIGATION_SECTION_TEST_ID + CONTENT_TEST_ID;
|
||||
|
||||
export const INVESTIGATION_GUIDE_TEST_ID = `${PREFIX}InvestigationGuide` as const;
|
||||
export const INVESTIGATION_GUIDE_BUTTON_TEST_ID = `${INVESTIGATION_GUIDE_TEST_ID}Button` as const;
|
||||
export const INVESTIGATION_GUIDE_LOADING_TEST_ID = `${INVESTIGATION_GUIDE_TEST_ID}Loading` as const;
|
||||
export const INVESTIGATION_GUIDE_NO_DATA_TEST_ID = `${INVESTIGATION_GUIDE_TEST_ID}NoData` as const;
|
||||
|
||||
const HIGHLIGHTED_FIELDS_TEST_ID = `${PREFIX}HighlightedFields` as const;
|
||||
export const HIGHLIGHTED_FIELDS_TITLE_TEST_ID = `${HIGHLIGHTED_FIELDS_TEST_ID}Title` as const;
|
||||
export const HIGHLIGHTED_FIELDS_DETAILS_TEST_ID = `${HIGHLIGHTED_FIELDS_TEST_ID}Details` as const;
|
||||
|
@ -75,8 +79,10 @@ export const SUMMARY_ROW_VALUE_TEST_ID = (dataTestSubj: string) => `${dataTestSu
|
|||
/* Entities */
|
||||
|
||||
export const INSIGHTS_ENTITIES_TEST_ID = `${PREFIX}InsightsEntities` as const;
|
||||
export const INSIGHTS_ENTITIES_NO_DATA_TEST_ID = `${INSIGHTS_ENTITIES_TEST_ID}NoData` as const;
|
||||
|
||||
export const ENTITIES_USER_OVERVIEW_TEST_ID = `${INSIGHTS_ENTITIES_TEST_ID}UserOverview` as const;
|
||||
export const ENTITIES_USER_OVERVIEW_LOADING_TEST_ID =
|
||||
`${ENTITIES_USER_OVERVIEW_TEST_ID}Loading` as const;
|
||||
export const ENTITIES_USER_OVERVIEW_LINK_TEST_ID = `${ENTITIES_USER_OVERVIEW_TEST_ID}Link` as const;
|
||||
export const ENTITIES_USER_OVERVIEW_DOMAIN_TEST_ID =
|
||||
`${ENTITIES_USER_OVERVIEW_TEST_ID}Domain` as const;
|
||||
|
@ -84,7 +90,10 @@ export const ENTITIES_USER_OVERVIEW_LAST_SEEN_TEST_ID =
|
|||
`${ENTITIES_USER_OVERVIEW_TEST_ID}LastSeen` as const;
|
||||
export const ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID =
|
||||
`${ENTITIES_USER_OVERVIEW_TEST_ID}RiskLevel` as const;
|
||||
|
||||
export const ENTITIES_HOST_OVERVIEW_TEST_ID = `${INSIGHTS_ENTITIES_TEST_ID}HostOverview` as const;
|
||||
export const ENTITIES_HOST_OVERVIEW_LOADING_TEST_ID =
|
||||
`${ENTITIES_HOST_OVERVIEW_TEST_ID}Loading` as const;
|
||||
export const ENTITIES_HOST_OVERVIEW_LINK_TEST_ID = `${ENTITIES_HOST_OVERVIEW_TEST_ID}Link` as const;
|
||||
export const ENTITIES_HOST_OVERVIEW_OS_FAMILY_TEST_ID =
|
||||
`${ENTITIES_HOST_OVERVIEW_TEST_ID}OsFamily` as const;
|
||||
|
@ -102,7 +111,6 @@ export const INSIGHTS_THREAT_INTELLIGENCE_TEST_ID = `${PREFIX}InsightsThreatInte
|
|||
/* Correlations */
|
||||
|
||||
export const CORRELATIONS_TEST_ID = `${PREFIX}Correlations` as const;
|
||||
export const CORRELATIONS_NO_DATA_TEST_ID = `${CORRELATIONS_TEST_ID}NoData` as const;
|
||||
export const CORRELATIONS_SUPPRESSED_ALERTS_TEST_ID =
|
||||
`${CORRELATIONS_TEST_ID}SuppressedAlerts` as const;
|
||||
export const CORRELATIONS_SUPPRESSED_ALERTS_TECHNICAL_PREVIEW_TEST_ID =
|
||||
|
@ -118,7 +126,6 @@ export const CORRELATIONS_RELATED_ALERTS_BY_ANCESTRY_TEST_ID =
|
|||
/* Insights Prevalence */
|
||||
|
||||
export const PREVALENCE_TEST_ID = `${PREFIX}InsightsPrevalence` as const;
|
||||
export const PREVALENCE_NO_DATA_TEST_ID = `${PREVALENCE_TEST_ID}NoData` as const;
|
||||
|
||||
/* Visualizations section */
|
||||
|
||||
|
@ -127,10 +134,10 @@ export const VISUALIZATIONS_SECTION_TEST_ID = `${VISUALIZATIONS_TEST_ID}Title` a
|
|||
export const VISUALIZATIONS_SECTION_HEADER_TEST_ID =
|
||||
`${VISUALIZATIONS_TEST_ID}TitleHeader` as const;
|
||||
export const ANALYZER_PREVIEW_TEST_ID = `${PREFIX}AnalyzerPreview` as const;
|
||||
export const ANALYZER_PREVIEW_NO_DATA_TEST_ID = `${ANALYZER_PREVIEW_TEST_ID}NoData` as const;
|
||||
export const ANALYZER_PREVIEW_LOADING_TEST_ID = `${ANALYZER_PREVIEW_TEST_ID}Loading` as const;
|
||||
|
||||
export const SESSION_PREVIEW_TEST_ID = `${PREFIX}SessionPreview` as const;
|
||||
export const SESSION_PREVIEW_UPSELL_TEST_ID = `${SESSION_PREVIEW_TEST_ID}UpSell` as const;
|
||||
export const SESSION_PREVIEW_NO_DATA_TEST_ID = `${SESSION_PREVIEW_TEST_ID}NoData` as const;
|
||||
|
||||
/* Response section */
|
||||
|
||||
|
|
|
@ -134,9 +134,9 @@ describe('<ThreatIntelligenceOverview />', () => {
|
|||
loading: true,
|
||||
});
|
||||
|
||||
const { getAllByTestId } = render(renderThreatIntelligenceOverview(panelContextValue));
|
||||
const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue));
|
||||
|
||||
expect(getAllByTestId(LOADING_TEST_ID)).toHaveLength(2);
|
||||
expect(getByTestId(LOADING_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should navigate to left section Insights tab when clicking on button', () => {
|
||||
|
|
|
@ -67,6 +67,7 @@ export const ThreatIntelligenceOverview: FC = () => {
|
|||
iconType: 'arrowStart',
|
||||
}}
|
||||
data-test-subj={INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}
|
||||
content={{ loading }}
|
||||
>
|
||||
<EuiFlexGroup
|
||||
direction="column"
|
||||
|
@ -74,7 +75,6 @@ export const ThreatIntelligenceOverview: FC = () => {
|
|||
data-test-subj={`${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Container`}
|
||||
>
|
||||
<InsightsSummaryRow
|
||||
loading={loading}
|
||||
icon={'warning'}
|
||||
value={threatMatchesCount}
|
||||
text={
|
||||
|
@ -87,7 +87,6 @@ export const ThreatIntelligenceOverview: FC = () => {
|
|||
data-test-subj={INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}
|
||||
/>
|
||||
<InsightsSummaryRow
|
||||
loading={loading}
|
||||
icon={'warning'}
|
||||
value={threatEnrichmentsCount}
|
||||
text={
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
ENTITIES_USER_OVERVIEW_LAST_SEEN_TEST_ID,
|
||||
ENTITIES_USER_OVERVIEW_LINK_TEST_ID,
|
||||
ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID,
|
||||
ENTITIES_USER_OVERVIEW_LOADING_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { useObservedUserDetails } from '../../../explore/users/containers/users/observed_details';
|
||||
import { mockContextValue } from '../mocks/mock_context';
|
||||
|
@ -122,6 +123,36 @@ describe('<UserEntityOverview />', () => {
|
|||
expect(getByTestId(ENTITIES_USER_OVERVIEW_LAST_SEEN_TEST_ID)).toHaveTextContent('—');
|
||||
});
|
||||
|
||||
it('should render loading if user details returns loading as true', () => {
|
||||
mockUseUserDetails.mockReturnValue([true, { userDetails: null }]);
|
||||
mockUseRiskScore.mockReturnValue({ data: null, isAuthorized: true });
|
||||
|
||||
const { getByTestId, queryByTestId } = render(
|
||||
<TestProviders>
|
||||
<RightPanelContext.Provider value={panelContextValue}>
|
||||
<UserEntityOverview userName={userName} />
|
||||
</RightPanelContext.Provider>
|
||||
</TestProviders>
|
||||
);
|
||||
expect(getByTestId(ENTITIES_USER_OVERVIEW_LOADING_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(ENTITIES_USER_OVERVIEW_DOMAIN_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render loading if risk score returns loading as true', () => {
|
||||
mockUseUserDetails.mockReturnValue([false, { userDetails: null }]);
|
||||
mockUseRiskScore.mockReturnValue({ data: null, isAuthorized: true, loading: true });
|
||||
|
||||
const { getByTestId, queryByTestId } = render(
|
||||
<TestProviders>
|
||||
<RightPanelContext.Provider value={panelContextValue}>
|
||||
<UserEntityOverview userName={userName} />
|
||||
</RightPanelContext.Provider>
|
||||
</TestProviders>
|
||||
);
|
||||
expect(getByTestId(ENTITIES_USER_OVERVIEW_LOADING_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId(ENTITIES_USER_OVERVIEW_DOMAIN_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should navigate to left panel entities tab when clicking on title', () => {
|
||||
mockUseUserDetails.mockReturnValue([false, { userDetails: userData }]);
|
||||
mockUseRiskScore.mockReturnValue({ data: riskLevel, isAuthorized: true });
|
||||
|
|
|
@ -14,9 +14,11 @@ import {
|
|||
useEuiTheme,
|
||||
useEuiFontSize,
|
||||
EuiIconTip,
|
||||
EuiSkeletonText,
|
||||
} from '@elastic/eui';
|
||||
import { css } from '@emotion/css';
|
||||
import { getOr } from 'lodash/fp';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { LeftPanelInsightsTab, LeftPanelKey } from '../../left';
|
||||
|
@ -36,7 +38,11 @@ import { RiskScore } from '../../../explore/components/risk_score/severity/commo
|
|||
import { useSourcererDataView } from '../../../common/containers/sourcerer';
|
||||
import { useGlobalTime } from '../../../common/containers/use_global_time';
|
||||
import { useRiskScore } from '../../../explore/containers/risk_score';
|
||||
import * as i18n from '../../../overview/components/user_overview/translations';
|
||||
import {
|
||||
USER_DOMAIN,
|
||||
LAST_SEEN,
|
||||
USER_RISK_CLASSIFICATION,
|
||||
} from '../../../overview/components/user_overview/translations';
|
||||
import {
|
||||
ENTITIES_USER_OVERVIEW_TEST_ID,
|
||||
ENTITIES_USER_OVERVIEW_DOMAIN_TEST_ID,
|
||||
|
@ -44,6 +50,7 @@ import {
|
|||
ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID,
|
||||
ENTITIES_USER_OVERVIEW_LINK_TEST_ID,
|
||||
TECHNICAL_PREVIEW_ICON_TEST_ID,
|
||||
ENTITIES_USER_OVERVIEW_LOADING_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { useObservedUserDetails } from '../../../explore/users/containers/users/observed_details';
|
||||
|
||||
|
@ -90,14 +97,18 @@ export const UserEntityOverview: React.FC<UserEntityOverviewProps> = ({ userName
|
|||
() => (userName ? buildUserNamesFilter([userName]) : undefined),
|
||||
[userName]
|
||||
);
|
||||
const [_, { userDetails }] = useObservedUserDetails({
|
||||
const [isUserDetailsLoading, { userDetails }] = useObservedUserDetails({
|
||||
endDate: to,
|
||||
userName,
|
||||
indexNames: selectedPatterns,
|
||||
startDate: from,
|
||||
});
|
||||
|
||||
const { data: userRisk, isAuthorized } = useRiskScore({
|
||||
const {
|
||||
data: userRisk,
|
||||
isAuthorized,
|
||||
loading: isRiskScoreLoading,
|
||||
} = useRiskScore({
|
||||
filterQuery,
|
||||
riskEntity: RiskScoreEntity.user,
|
||||
timerange,
|
||||
|
@ -106,7 +117,7 @@ export const UserEntityOverview: React.FC<UserEntityOverviewProps> = ({ userName
|
|||
const userDomain: DescriptionList[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: i18n.USER_DOMAIN,
|
||||
title: USER_DOMAIN,
|
||||
description: (
|
||||
<DefaultFieldRenderer
|
||||
rowItems={getOr([], 'user.domain', userDetails)}
|
||||
|
@ -123,7 +134,7 @@ export const UserEntityOverview: React.FC<UserEntityOverviewProps> = ({ userName
|
|||
const userLastSeen: DescriptionList[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: i18n.LAST_SEEN,
|
||||
title: LAST_SEEN,
|
||||
description: (
|
||||
<FirstLastSeen
|
||||
indexPatterns={selectedPatterns}
|
||||
|
@ -147,7 +158,7 @@ export const UserEntityOverview: React.FC<UserEntityOverviewProps> = ({ userName
|
|||
{
|
||||
title: (
|
||||
<>
|
||||
{i18n.USER_RISK_CLASSIFICATION}
|
||||
{USER_RISK_CLASSIFICATION}
|
||||
<EuiIconTip
|
||||
title={
|
||||
<FormattedMessage
|
||||
|
@ -206,27 +217,37 @@ export const UserEntityOverview: React.FC<UserEntityOverviewProps> = ({ userName
|
|||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<OverviewDescriptionList
|
||||
dataTestSubj={ENTITIES_USER_OVERVIEW_DOMAIN_TEST_ID}
|
||||
descriptionList={userDomain}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{isAuthorized ? (
|
||||
<DescriptionListStyled
|
||||
data-test-subj={ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID}
|
||||
listItems={[userRiskLevel]}
|
||||
/>
|
||||
) : (
|
||||
<OverviewDescriptionList
|
||||
dataTestSubj={ENTITIES_USER_OVERVIEW_LAST_SEEN_TEST_ID}
|
||||
descriptionList={userLastSeen}
|
||||
/>
|
||||
{isUserDetailsLoading || isRiskScoreLoading ? (
|
||||
<EuiSkeletonText
|
||||
contentAriaLabel={i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.insights.entities.userLoadingAriaLabel',
|
||||
{ defaultMessage: 'user overview' }
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
data-test-subj={ENTITIES_USER_OVERVIEW_LOADING_TEST_ID}
|
||||
/>
|
||||
) : (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<OverviewDescriptionList
|
||||
dataTestSubj={ENTITIES_USER_OVERVIEW_DOMAIN_TEST_ID}
|
||||
descriptionList={userDomain}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{isAuthorized ? (
|
||||
<DescriptionListStyled
|
||||
data-test-subj={ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID}
|
||||
listItems={[userRiskLevel]}
|
||||
/>
|
||||
) : (
|
||||
<OverviewDescriptionList
|
||||
dataTestSubj={ENTITIES_USER_OVERVIEW_LAST_SEEN_TEST_ID}
|
||||
descriptionList={userLastSeen}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -16,13 +16,13 @@ import {
|
|||
EuiLink,
|
||||
EuiTitle,
|
||||
EuiText,
|
||||
EuiLoadingSpinner,
|
||||
useEuiTheme,
|
||||
EuiToolTip,
|
||||
EuiSkeletonText,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { IconType } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export interface ExpandablePanelPanelProps {
|
||||
header: {
|
||||
|
@ -197,11 +197,13 @@ export const ExpandablePanel: React.FC<ExpandablePanelPanelProps> = ({
|
|||
}, [children, expandable, toggleStatus]);
|
||||
|
||||
const content = loading ? (
|
||||
<EuiFlexGroup justifyContent="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner data-test-subj={`${dataTestSubj}Loading`} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSkeletonText
|
||||
data-test-subj={`${dataTestSubj}Loading`}
|
||||
contentAriaLabel={i18n.translate(
|
||||
'xpack.securitySolution.flyout.shared.expandablePanelLoadingAriaLabel',
|
||||
{ defaultMessage: 'expandable panel' }
|
||||
)}
|
||||
/>
|
||||
) : error ? null : (
|
||||
children
|
||||
);
|
||||
|
|
|
@ -15,4 +15,10 @@ describe('<FlyoutLoading />', () => {
|
|||
const { getByTestId } = render(<FlyoutLoading />);
|
||||
expect(getByTestId(FLYOUT_LOADING_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render loading when data test subject is passed', () => {
|
||||
const { getByTestId, queryByTestId } = render(<FlyoutLoading data-test-subj="test-id" />);
|
||||
expect(getByTestId('test-id')).toBeInTheDocument();
|
||||
expect(queryByTestId(FLYOUT_LOADING_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,18 +10,25 @@ import { EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
|
|||
import { css } from '@emotion/react';
|
||||
import { FLYOUT_LOADING_TEST_ID } from '../test_ids';
|
||||
|
||||
interface FlyoutLoadingProps {
|
||||
/**
|
||||
Data test subject string for testing
|
||||
*/
|
||||
['data-test-subj']?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this when you need to show a loading state in the flyout
|
||||
*/
|
||||
export const FlyoutLoading: React.VFC = () => (
|
||||
export const FlyoutLoading: React.FC<FlyoutLoadingProps> = ({
|
||||
'data-test-subj': dataTestSubj = FLYOUT_LOADING_TEST_ID,
|
||||
}) => (
|
||||
<EuiFlexItem
|
||||
css={css`
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`}
|
||||
>
|
||||
<EuiLoadingSpinner size="xxl" data-test-subj={FLYOUT_LOADING_TEST_ID} />
|
||||
<EuiLoadingSpinner size="xxl" data-test-subj={dataTestSubj} />
|
||||
</EuiFlexItem>
|
||||
);
|
||||
|
||||
FlyoutLoading.displayName = 'FlyoutLoading';
|
||||
|
|
|
@ -5,10 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_BUTTON,
|
||||
DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_ERROR,
|
||||
} from '../../../../screens/expandable_flyout/alert_details_left_panel_session_view_tab';
|
||||
import { DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_BUTTON } from '../../../../screens/expandable_flyout/alert_details_left_panel_session_view_tab';
|
||||
import {
|
||||
DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB,
|
||||
DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_BUTTON_GROUP,
|
||||
|
@ -48,12 +45,6 @@ describe.skip(
|
|||
cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_BUTTON)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Session View');
|
||||
|
||||
// TODO ideally we would have a test for the session view component instead
|
||||
cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_ERROR)
|
||||
.should('be.visible')
|
||||
.and('contain.text', 'Unable to display session view')
|
||||
.and('contain.text', 'There was an error displaying session view');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
@ -6,10 +6,15 @@
|
|||
*/
|
||||
|
||||
import { RESPONSE_TAB_TEST_ID } from '@kbn/security-solution-plugin/public/flyout/left/test_ids';
|
||||
import { RESPONSE_NO_DATA_TEST_ID } from '@kbn/security-solution-plugin/public/flyout/left/components/test_ids';
|
||||
import {
|
||||
RESPONSE_DETAILS_TEST_ID,
|
||||
RESPONSE_NO_DATA_TEST_ID,
|
||||
} from '@kbn/security-solution-plugin/public/flyout/left/components/test_ids';
|
||||
import { getDataTestSubjectSelector } from '../../helpers/common';
|
||||
|
||||
export const DOCUMENT_DETAILS_FLYOUT_RESPONSE_TAB =
|
||||
getDataTestSubjectSelector(RESPONSE_TAB_TEST_ID);
|
||||
export const DOCUMENT_DETAILS_FLYOUT_RESPONSE_DETAILS =
|
||||
getDataTestSubjectSelector(RESPONSE_DETAILS_TEST_ID);
|
||||
export const DOCUMENT_DETAILS_FLYOUT_RESPONSE_EMPTY =
|
||||
getDataTestSubjectSelector(RESPONSE_NO_DATA_TEST_ID);
|
||||
|
|
|
@ -6,12 +6,8 @@
|
|||
*/
|
||||
|
||||
import { VISUALIZE_TAB_SESSION_VIEW_BUTTON_TEST_ID } from '@kbn/security-solution-plugin/public/flyout/left/tabs/test_ids';
|
||||
import { SESSION_VIEW_ERROR_TEST_ID } from '@kbn/security-solution-plugin/public/flyout/left/components/test_ids';
|
||||
import { getDataTestSubjectSelector } from '../../helpers/common';
|
||||
|
||||
export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_BUTTON = getDataTestSubjectSelector(
|
||||
VISUALIZE_TAB_SESSION_VIEW_BUTTON_TEST_ID
|
||||
);
|
||||
export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_ERROR = getDataTestSubjectSelector(
|
||||
SESSION_VIEW_ERROR_TEST_ID
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue