[Security Solution] Prebuilt rule installation / upgrade flyout test coverage (#169537)

**Resolves: https://github.com/elastic/kibana/issues/166161**

**Flaky test runner pipelne:
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3868**

## Summary
Adds test coverage based on the test plan:
https://github.com/elastic/kibana/pull/167727/files

<img width="1301" alt="272390474-1aa7aa8b-3746-4767-82ad-f4910034d5eb"
src="4ad6a27a-eeef-467b-bb72-1caf48bfb7e5">

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Nikita Indik 2023-11-05 17:19:15 +01:00 committed by GitHub
parent 5406e2c17b
commit e389991557
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 2156 additions and 268 deletions

View file

@ -167,7 +167,7 @@ There's a legacy prebuilt rules API and a new one. Both should be tested against
#### **Scenario: API can install all prebuilt rules**
**Automation**: 8 integration tests with mock rules: 4 examples below * 2 (we split checking API response and installed rules into two different tests).
**Automation**: 8 integration tests with mock rules: 4 examples below \* 2 (we split checking API response and installed rules into two different tests).
```Gherkin
Given the package <package_type> is installed
@ -436,7 +436,7 @@ And user should see the number of rules available to install (Y)
#### **Scenario: User can install prebuilt rules one by one**
**Automation**: 1 e2e test with mock rules + integration tests with mock rules that would test /status and /installation/* endpoints in integration.
**Automation**: 1 e2e test with mock rules + integration tests with mock rules that would test /status and /installation/\* endpoints in integration.
```Gherkin
Given no prebuilt rules are installed in Kibana
@ -453,7 +453,7 @@ And user should see the number of rules available to install decreased by 1
#### **Scenario: User can install multiple prebuilt rules selected on the page**
**Automation**: 1 e2e test with mock rules + integration tests with mock rules that would test /status and /installation/* endpoints in integration.
**Automation**: 1 e2e test with mock rules + integration tests with mock rules that would test /status and /installation/\* endpoints in integration.
```Gherkin
Given no prebuilt rules are installed in Kibana
@ -477,7 +477,7 @@ Examples:
#### **Scenario: User can install all available prebuilt rules at once**
**Automation**: 1 e2e test with mock rules + integration tests with mock rules that would test /status and /installation/* endpoints in integration.
**Automation**: 1 e2e test with mock rules + integration tests with mock rules that would test /status and /installation/\* endpoints in integration.
```Gherkin
Given no prebuilt rules are installed in Kibana
@ -506,7 +506,7 @@ Then user should see a message indicating that all available rules have been ins
And user should see a CTA that leads to the Rule Management page
```
#### **Scenario: User can preview a rule before installing**
#### **Scenario: User can preview rules available for installation**
**Automation**: 1 e2e test
@ -519,15 +519,26 @@ When user opens the rule preview for the 1st rule
Then the preview should open
When user closes the preview
Then it should disappear
When user opens the rule preview for the 2nd rule
```
#### **Scenario: User can install a rule using the rule preview**
**Automation**: 1 e2e test
```Gherkin
Given no prebuilt rules are installed in Kibana
And there are 2 rules available to install
When user opens the Add Rules page
Then all rules available for installation should be displayed in the table
When user opens the rule preview for the rule
Then the preview should open
When user installs the rule using a CTA in the rule preview
Then the 2nd rule should be installed
Then the rule should be installed
And a success message should be displayed after installation
And the 2nd rule should be removed from the Add Rules table
And the rule should be removed from the Add Rules table
When user navigates back to the Rule Management page
Then user should see a CTA to install prebuilt rules
And user should see the number of rules available to install as 1
And user should see the number of rules available to install as initial number minus 1
```
#### **Scenario: User can see correct rule information in preview before installing**
@ -594,7 +605,7 @@ Then the Rule Management page should be displayed
#### **Scenario: User can upgrade prebuilt rules one by one**
**Automation**: 1 e2e test with mock rules + integration tests with mock rules that would test /status and /upgrade/* endpoints in integration.
**Automation**: 1 e2e test with mock rules + integration tests with mock rules that would test /status and /upgrade/\* endpoints in integration.
```Gherkin
Given X prebuilt rules are installed in Kibana
@ -610,7 +621,7 @@ And user should see the number of rules available to upgrade decreased by 1
#### **Scenario: User can upgrade multiple prebuilt rules selected on the page**
**Automation**: 1 e2e test with mock rules + integration tests with mock rules that would test /status and /upgrade/* endpoints in integration.
**Automation**: 1 e2e test with mock rules + integration tests with mock rules that would test /status and /upgrade/\* endpoints in integration.
```Gherkin
Given X prebuilt rules are installed in Kibana
@ -633,7 +644,7 @@ Examples:
#### **Scenario: User can upgrade all available prebuilt rules at once**
**Automation**: 1 e2e test with mock rules + integration tests with mock rules that would test /status and /upgrade/* endpoints in integration.
**Automation**: 1 e2e test with mock rules + integration tests with mock rules that would test /status and /upgrade/\* endpoints in integration.
```Gherkin
Given X prebuilt rules are installed in Kibana
@ -648,27 +659,37 @@ And user should NOT see a number of rules available to upgrade
And user should NOT see the Rule Updates table
```
#### **Scenario: User can preview a rule before upgrading**
#### **Scenario: User can preview rules available for upgrade**
```Gherkin
Given there is at least one prebuilt rule installed in Kibana
And for this rule there is a new version available
And user is on the Rule Management page
When user opens the Rule Updates table
Then this rule should be displayed in the table
When user opens the rule preview for this rule
Then the preview should open
When user closes the preview
Then it should disappear
```
#### **Scenario: User can upgrade a rule using the rule preview**
**Automation**: 1 e2e test
```Gherkin
Given 2 prebuilt rules are installed in Kibana
And for these 2 installed rules there are new versions available
Given there is at least one prebuilt rule installed in Kibana
And for this rule there is a new version available
And user is on the Rule Management page
When user opens the Rule Updates table
Then all rules available for upgrade should be displayed in the table
When user opens the rule preview for the 1st rule
Then the preview should open
When user closes the preview
Then it should disappear
When user opens the rule preview for the 2nd rule
Then this rule should be displayed in the table
When user opens the rule preview for this rule
Then the preview should open
When user upgrades the rule using a CTA in the rule preview
Then the 2nd rule should be upgraded to the latest version
Then the rule should be upgraded to the latest version
And a success message should be displayed after upgrade
And the 2nd rule should be removed from the Rule Updates table
And user should see the number of rules available to upgrade as 1
And the rule should be removed from the Rule Updates table
And user should see the number of rules available to upgrade as initial number minus 1
```
#### **Scenario: User can see correct rule information in preview before upgrading**

View file

@ -17,12 +17,13 @@ const StyledEuiBadge = styled(EuiBadge)`
interface BadgeListProps {
badges: string[];
'data-test-subj'?: string;
}
export const BadgeList = ({ badges }: BadgeListProps) => (
<EuiFlexGroup responsive={false} gutterSize="xs" wrap>
export const BadgeList = ({ badges, 'data-test-subj': dataTestSubj }: BadgeListProps) => (
<EuiFlexGroup responsive={false} gutterSize="xs" wrap data-test-subj={dataTestSubj}>
{badges.map((badge: string) => (
<EuiFlexItem grow={false} key={`badge-${badge}`}>
<EuiFlexItem grow={false} key={`badge-${badge}`} data-test-subj={`${dataTestSubj}Item`}>
<StyledEuiBadge color="hollow">{badge}</StyledEuiBadge>
</EuiFlexItem>
))}

View file

@ -71,9 +71,15 @@ interface AuthorProps {
author: string[];
}
const Author = ({ author }: AuthorProps) => <BadgeList badges={author} />;
const Author = ({ author }: AuthorProps) => (
<BadgeList badges={author} data-test-subj="authorPropertyValue" />
);
const BuildingBlock = () => <EuiText size="s">{i18n.BUILDING_BLOCK_FIELD_DESCRIPTION}</EuiText>;
const BuildingBlock = () => (
<EuiText size="s" data-test-subj="buildingBlockPropertyValue">
{i18n.BUILDING_BLOCK_FIELD_DESCRIPTION}
</EuiText>
);
interface SeverityMappingItemProps {
severityMappingItem: SeverityMappingItemType;
@ -86,7 +92,7 @@ const SeverityMappingItem = ({ severityMappingItem }: SeverityMappingItemProps)
content={severityMappingItem.field}
data-test-subj={`severityOverrideField-${severityMappingItem.value}`}
>
<>{`${severityMappingItem.field}:`}</>
<span data-test-subj="severityOverrideField">{`${severityMappingItem.field}:`}</span>
</EuiToolTip>
</OverrideColumn>
<OverrideValueColumn>
@ -94,7 +100,9 @@ const SeverityMappingItem = ({ severityMappingItem }: SeverityMappingItemProps)
content={severityMappingItem.value}
data-test-subj={`severityOverrideValue-${severityMappingItem.value}`}
>
{defaultToEmptyTag(severityMappingItem.value)}
<span data-test-subj="severityOverrideValue">
{defaultToEmptyTag(severityMappingItem.value)}
</span>
</EuiToolTip>
</OverrideValueColumn>
<EuiFlexItem grow={false}>
@ -102,7 +110,7 @@ const SeverityMappingItem = ({ severityMappingItem }: SeverityMappingItemProps)
</EuiFlexItem>
<EuiFlexItem>
<SeverityBadge
data-test-subj={`severityOverrideSeverity-${severityMappingItem.value}`}
data-test-subj="severityOverrideSeverity"
value={severityMappingItem.severity}
/>
</EuiFlexItem>
@ -113,7 +121,11 @@ interface RiskScoreProps {
riskScore: number;
}
const RiskScore = ({ riskScore }: RiskScoreProps) => <EuiText size="s">{riskScore}</EuiText>;
const RiskScore = ({ riskScore }: RiskScoreProps) => (
<EuiText size="s" data-test-subj="riskScorePropertyValue">
{riskScore}
</EuiText>
);
interface RiskScoreMappingItemProps {
riskScoreMappingItem: RiskScoreMappingItemType;
@ -126,13 +138,15 @@ const RiskScoreMappingItem = ({ riskScoreMappingItem }: RiskScoreMappingItemProp
content={riskScoreMappingItem.field}
data-test-subj={`riskScoreOverrideField-${riskScoreMappingItem.value}`}
>
<>{riskScoreMappingItem.field}</>
<span data-test-subj="riskScoreOverridePropertyFieldName">
{riskScoreMappingItem.field}
</span>
</EuiToolTip>
</OverrideColumn>
<EuiFlexItem grow={false}>
<EuiIcon type={'sortRight'} />
</EuiFlexItem>
<EuiFlexItem>{ALERT_RISK_SCORE}</EuiFlexItem>
<EuiFlexItem data-test-subj="riskScoreOverridePropertyOverride">{ALERT_RISK_SCORE}</EuiFlexItem>
</EuiFlexGroup>
);
@ -161,7 +175,7 @@ const FalsePositives = ({ falsePositives }: { falsePositives: string[] }) => (
<ul>
{falsePositives.map((falsePositivesItem) => (
<li
data-test-subj="unorderedListArrayDescriptionItem"
data-test-subj="falsePositivesPropertyValueItem"
key={`falsePositives-${falsePositivesItem}`}
>
{falsePositivesItem}
@ -176,21 +190,27 @@ interface InvestigationFieldsProps {
}
const InvestigationFields = ({ investigationFields }: InvestigationFieldsProps) => (
<BadgeList badges={investigationFields} />
<BadgeList badges={investigationFields} data-test-subj="investigationFieldsPropertyValue" />
);
interface LicenseProps {
license: string;
}
const License = ({ license }: LicenseProps) => <EuiText size="s">{license}</EuiText>;
const License = ({ license }: LicenseProps) => (
<EuiText size="s" data-test-subj="licensePropertyValue">
{license}
</EuiText>
);
interface RuleNameOverrideProps {
ruleNameOverride: string;
}
const RuleNameOverride = ({ ruleNameOverride }: RuleNameOverrideProps) => (
<EuiText size="s">{ruleNameOverride}</EuiText>
<EuiText size="s" data-test-subj="ruleNameOverridePropertyValue">
{ruleNameOverride}
</EuiText>
);
interface ThreatProps {
@ -198,7 +218,7 @@ interface ThreatProps {
}
const Threat = ({ threat }: ThreatProps) => (
<ThreatEuiFlexGroup threat={filterEmptyThreats(threat)} label="" />
<ThreatEuiFlexGroup threat={filterEmptyThreats(threat)} data-test-subj="threatPropertyValue" />
);
interface ThreatIndicatorPathProps {
@ -214,14 +234,18 @@ interface TimestampOverrideProps {
}
const TimestampOverride = ({ timestampOverride }: TimestampOverrideProps) => (
<EuiText size="s">{timestampOverride}</EuiText>
<EuiText size="s" data-test-subj="timestampOverridePropertyValue">
{timestampOverride}
</EuiText>
);
interface TagsProps {
tags: string[];
}
const Tags = ({ tags }: TagsProps) => <BadgeList badges={tags} />;
const Tags = ({ tags }: TagsProps) => (
<BadgeList badges={tags} data-test-subj="tagsPropertyValue" />
);
// eslint-disable-next-line complexity
const prepareAboutSectionListItems = (
@ -247,22 +271,24 @@ const prepareAboutSectionListItems = (
if (rule.author && rule.author.length > 0) {
aboutSectionListItems.push({
title: i18n.AUTHOR_FIELD_LABEL,
title: <span data-test-subj="authorPropertyTitle">{i18n.AUTHOR_FIELD_LABEL}</span>,
description: <Author author={rule.author} />,
});
}
if (rule.building_block_type) {
aboutSectionListItems.push({
title: i18n.BUILDING_BLOCK_FIELD_LABEL,
title: (
<span data-test-subj="buildingBlockPropertyTitle">{i18n.BUILDING_BLOCK_FIELD_LABEL}</span>
),
description: <BuildingBlock />,
});
}
if (rule.severity) {
aboutSectionListItems.push({
title: i18n.SEVERITY_FIELD_LABEL,
description: <SeverityBadge value={rule.severity} />,
title: <span data-test-subj="severityPropertyTitle">{i18n.SEVERITY_FIELD_LABEL}</span>,
description: <SeverityBadge value={rule.severity} data-test-subj="severityPropertyValue" />,
});
}
@ -272,7 +298,14 @@ const prepareAboutSectionListItems = (
.filter((severityMappingItem) => severityMappingItem.field !== '')
.map((severityMappingItem, index) => {
return {
title: index === 0 ? i18n.SEVERITY_MAPPING_FIELD_LABEL : '',
title:
index === 0 ? (
<span data-test-subj="severityOverridePropertyTitle">
{i18n.SEVERITY_MAPPING_FIELD_LABEL}
</span>
) : (
''
),
description: <SeverityMappingItem severityMappingItem={severityMappingItem} />,
};
})
@ -281,7 +314,7 @@ const prepareAboutSectionListItems = (
if (rule.risk_score) {
aboutSectionListItems.push({
title: i18n.RISK_SCORE_FIELD_LABEL,
title: <span data-test-subj="riskScorePropertyTitle">{i18n.RISK_SCORE_FIELD_LABEL}</span>,
description: <RiskScore riskScore={rule.risk_score} />,
});
}
@ -292,7 +325,14 @@ const prepareAboutSectionListItems = (
.filter((riskScoreMappingItem) => riskScoreMappingItem.field !== '')
.map((riskScoreMappingItem, index) => {
return {
title: index === 0 ? i18n.RISK_SCORE_MAPPING_FIELD_LABEL : '',
title:
index === 0 ? (
<span data-test-subj="riskScoreOverridePropertyTitle">
{i18n.RISK_SCORE_MAPPING_FIELD_LABEL}
</span>
) : (
''
),
description: <RiskScoreMappingItem riskScoreMappingItem={riskScoreMappingItem} />,
};
})
@ -301,21 +341,27 @@ const prepareAboutSectionListItems = (
if (rule.references && rule.references.length > 0) {
aboutSectionListItems.push({
title: i18n.REFERENCES_FIELD_LABEL,
title: <span data-test-subj="referencesPropertyTitle">{i18n.REFERENCES_FIELD_LABEL}</span>,
description: <References references={rule.references} />,
});
}
if (rule.false_positives && rule.false_positives.length > 0) {
aboutSectionListItems.push({
title: i18n.FALSE_POSITIVES_FIELD_LABEL,
title: (
<span data-test-subj="falsePositivesPropertyTitle">{i18n.FALSE_POSITIVES_FIELD_LABEL}</span>
),
description: <FalsePositives falsePositives={rule.false_positives} />,
});
}
if (rule.investigation_fields && rule.investigation_fields.field_names.length > 0) {
aboutSectionListItems.push({
title: i18n.INVESTIGATION_FIELDS_FIELD_LABEL,
title: (
<span data-test-subj="investigationFieldsPropertyTitle">
{i18n.INVESTIGATION_FIELDS_FIELD_LABEL}
</span>
),
description: (
<InvestigationFields investigationFields={rule.investigation_fields.field_names} />
),
@ -324,21 +370,25 @@ const prepareAboutSectionListItems = (
if (rule.license) {
aboutSectionListItems.push({
title: i18n.LICENSE_FIELD_LABEL,
title: <span data-test-subj="licensePropertyTitle">{i18n.LICENSE_FIELD_LABEL}</span>,
description: <License license={rule.license} />,
});
}
if (rule.rule_name_override) {
aboutSectionListItems.push({
title: i18n.RULE_NAME_OVERRIDE_FIELD_LABEL,
title: (
<span data-test-subj="ruleNameOverridePropertyTitle">
{i18n.RULE_NAME_OVERRIDE_FIELD_LABEL}
</span>
),
description: <RuleNameOverride ruleNameOverride={rule.rule_name_override} />,
});
}
if (rule.threat && rule.threat.length > 0) {
aboutSectionListItems.push({
title: i18n.THREAT_FIELD_LABEL,
title: <span data-test-subj="threatPropertyTitle">{i18n.THREAT_FIELD_LABEL}</span>,
description: <Threat threat={rule.threat} />,
});
}
@ -352,14 +402,18 @@ const prepareAboutSectionListItems = (
if (rule.timestamp_override) {
aboutSectionListItems.push({
title: i18n.TIMESTAMP_OVERRIDE_FIELD_LABEL,
title: (
<span data-test-subj="timestampOverridePropertyTitle">
{i18n.TIMESTAMP_OVERRIDE_FIELD_LABEL}
</span>
),
description: <TimestampOverride timestampOverride={rule.timestamp_override} />,
});
}
if (rule.tags && rule.tags.length > 0) {
aboutSectionListItems.push({
title: i18n.TAGS_FIELD_LABEL,
title: <span data-test-subj="tagsPropertyTitle">{i18n.TAGS_FIELD_LABEL}</span>,
description: <Tags tags={rule.tags} />,
});
}

View file

@ -61,7 +61,9 @@ interface SavedQueryNameProps {
}
const SavedQueryName = ({ savedQueryName }: SavedQueryNameProps) => (
<EuiText size="s">{savedQueryName}</EuiText>
<EuiText size="s" data-test-subj="savedQueryNamePropertyValue">
{savedQueryName}
</EuiText>
);
const EuiBadgeWrap = styled(EuiBadge)`
@ -74,9 +76,10 @@ interface FiltersProps {
filters: Filter[];
dataViewId?: string;
index?: string[];
'data-test-subj'?: string;
}
const Filters = ({ filters, dataViewId, index }: FiltersProps) => {
const Filters = ({ filters, dataViewId, index, 'data-test-subj': dataTestSubj }: FiltersProps) => {
const { indexPattern } = useRuleIndexPattern({
dataSourceType: dataViewId ? DataSourceType.DataView : DataSourceType.IndexPatterns,
index: index ?? [],
@ -86,9 +89,14 @@ const Filters = ({ filters, dataViewId, index }: FiltersProps) => {
const flattenedFilters = mapAndFlattenFilters(filters);
return (
<EuiFlexGroup wrap responsive={false} gutterSize="xs">
<EuiFlexGroup wrap responsive={false} gutterSize="xs" data-test-subj={dataTestSubj}>
{flattenedFilters.map((filter, idx) => (
<EuiFlexItem grow={false} key={`filter-${idx}`} css={{ width: '100%' }}>
<EuiFlexItem
grow={false}
key={`filter-${idx}`}
css={{ width: '100%' }}
data-test-subj={`filterItem-${filter.meta.key}`}
>
<EuiBadgeWrap color="hollow">
{indexPattern != null ? (
<FilterBadgeGroup filters={[filter]} dataViews={[indexPattern]} />
@ -108,21 +116,30 @@ const QueryContent = styled.div`
interface QueryProps {
query: string;
'data-test-subj'?: string;
}
const Query = ({ query }: QueryProps) => <QueryContent>{query}</QueryContent>;
const Query = ({ query, 'data-test-subj': dataTestSubj = 'query' }: QueryProps) => (
<QueryContent data-test-subj={dataTestSubj}>{query}</QueryContent>
);
interface IndexProps {
index: string[];
}
const Index = ({ index }: IndexProps) => <BadgeList badges={index} />;
const Index = ({ index }: IndexProps) => (
<BadgeList badges={index} data-test-subj="indexPropertyValue" />
);
interface DataViewIdProps {
dataViewId: string;
}
const DataViewId = ({ dataViewId }: DataViewIdProps) => <EuiText size="s">{dataViewId}</EuiText>;
const DataViewId = ({ dataViewId }: DataViewIdProps) => (
<EuiText size="s" data-test-subj="dataViewIdPropertyValue">
{dataViewId}
</EuiText>
);
interface DataViewIndexPatternProps {
dataViewId: string;
@ -152,7 +169,11 @@ const DataViewIndexPattern = ({ dataViewId }: DataViewIndexPatternProps) => {
return <EuiLoadingSpinner size="m" />;
}
return <EuiText size="s">{indexPattern}</EuiText>;
return (
<EuiText size="s" data-test-subj="dataViewIndexPatternPropertyValue">
{indexPattern}
</EuiText>
);
};
interface ThresholdProps {
@ -160,13 +181,13 @@ interface ThresholdProps {
}
const Threshold = ({ threshold }: ThresholdProps) => (
<>
<div data-test-subj="thresholdPropertyValue">
{isEmpty(threshold.field[0])
? `${descriptionStepI18n.THRESHOLD_RESULTS_ALL} >= ${threshold.value}`
: `${descriptionStepI18n.THRESHOLD_RESULTS_AGGREGATED_BY} ${
Array.isArray(threshold.field) ? threshold.field.join(',') : threshold.field
} >= ${threshold.value}`}
</>
</div>
);
interface AnomalyThresholdProps {
@ -174,7 +195,9 @@ interface AnomalyThresholdProps {
}
const AnomalyThreshold = ({ anomalyThreshold }: AnomalyThresholdProps) => (
<EuiText size="s">{anomalyThreshold}</EuiText>
<EuiText size="s" data-test-subj="anomalyThresholdPropertyValue">
{anomalyThreshold}
</EuiText>
);
interface MachineLearningJobListProps {
@ -245,7 +268,7 @@ interface RequiredFieldsProps {
}
const RequiredFields = ({ requiredFields }: RequiredFieldsProps) => (
<EuiFlexGrid gutterSize={'s'}>
<EuiFlexGrid gutterSize={'s'} data-test-subj="requiredFieldsPropertyValue">
{requiredFields.map((rF, index) => (
<EuiFlexItem grow={false} key={rF.name}>
<EuiFlexGroup alignItems="center" gutterSize={'xs'}>
@ -257,7 +280,11 @@ const RequiredFields = ({ requiredFields }: RequiredFieldsProps) => (
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<StyledFieldTypeText grow={false} size={'s'}>
<StyledFieldTypeText
grow={false}
size={'s'}
data-test-subj="requiredFieldsPropertyValueItem"
>
{` ${rF.name}${index + 1 !== requiredFields.length ? ', ' : ''}`}
</StyledFieldTypeText>
</EuiFlexItem>
@ -272,14 +299,18 @@ interface TimelineTitleProps {
}
const TimelineTitle = ({ timelineTitle }: TimelineTitleProps) => (
<EuiText size="s">{timelineTitle}</EuiText>
<EuiText size="s" data-test-subj="timelineTemplatePropertyValue">
{timelineTitle}
</EuiText>
);
interface ThreatIndexProps {
threatIndex: string[];
}
const ThreatIndex = ({ threatIndex }: ThreatIndexProps) => <BadgeList badges={threatIndex} />;
const ThreatIndex = ({ threatIndex }: ThreatIndexProps) => (
<BadgeList badges={threatIndex} data-test-subj="threatIndexPropertyValue" />
);
interface ThreatMappingProps {
threatMapping: ThreatMappingType;
@ -312,7 +343,11 @@ const ThreatMapping = ({ threatMapping }: ThreatMappingProps) => {
''
);
return <EuiText size="s">{description}</EuiText>;
return (
<EuiText size="s" data-test-subj="threatMappingPropertyValue">
{description}
</EuiText>
);
};
interface AlertSuppressionTitleProps {
@ -330,7 +365,7 @@ interface SuppressAlertsByFieldProps {
}
const SuppressAlertsByField = ({ fields }: SuppressAlertsByFieldProps) => (
<BadgeList badges={fields} />
<BadgeList badges={fields} data-test-subj="alertSuppressionGroupByPropertyValue" />
);
interface SuppressAlertsDurationProps {
@ -342,7 +377,11 @@ const SuppressAlertsDuration = ({ duration }: SuppressAlertsDurationProps) => {
? `${duration.value}${duration.unit}`
: descriptionStepI18n.ALERT_SUPPRESSION_PER_RULE_EXECUTION;
return <EuiText size="s">{durationDescription}</EuiText>;
return (
<EuiText size="s" data-test-subj="alertSuppressionDurationPropertyValue">
{durationDescription}
</EuiText>
);
};
interface MissingFieldsStrategyProps {
@ -355,7 +394,11 @@ const MissingFieldsStrategy = ({ missingFieldsStrategy }: MissingFieldsStrategyP
? descriptionStepI18n.ALERT_SUPPRESSION_SUPPRESS_ON_MISSING_FIELDS
: descriptionStepI18n.ALERT_SUPPRESSION_DO_NOT_SUPPRESS_ON_MISSING_FIELDS;
return <EuiText size="s">{missingFieldsDescription}</EuiText>;
return (
<EuiText size="s" data-test-subj="alertSuppressionSuppressionFieldPropertyValue">
{missingFieldsDescription}
</EuiText>
);
};
interface NewTermsFieldsProps {
@ -363,7 +406,7 @@ interface NewTermsFieldsProps {
}
const NewTermsFields = ({ newTermsFields }: NewTermsFieldsProps) => (
<BadgeList badges={newTermsFields} />
<BadgeList badges={newTermsFields} data-test-subj="newTermsFieldsPropertyValue" />
);
interface HistoryWindowSizeProps {
@ -373,7 +416,11 @@ interface HistoryWindowSizeProps {
const HistoryWindowSize = ({ historyWindowStart }: HistoryWindowSizeProps) => {
const size = historyWindowStart ? convertHistoryStartToSize(historyWindowStart) : '7d';
return <EuiText size="s">{size}</EuiText>;
return (
<EuiText size="s" data-test-subj={`newTermsWindowSizePropertyValue-${historyWindowStart}`}>
{size}
</EuiText>
);
};
// eslint-disable-next-line complexity
@ -386,7 +433,7 @@ const prepareDefinitionSectionListItems = (
if ('index' in rule && rule.index && rule.index.length > 0) {
definitionSectionListItems.push({
title: i18n.INDEX_FIELD_LABEL,
title: <span data-test-subj="indexPropertyTitle">{i18n.INDEX_FIELD_LABEL}</span>,
description: <Index index={rule.index} />,
});
}
@ -394,11 +441,17 @@ const prepareDefinitionSectionListItems = (
if ('data_view_id' in rule && rule.data_view_id) {
definitionSectionListItems.push(
{
title: i18n.DATA_VIEW_ID_FIELD_LABEL,
title: (
<span data-test-subj="dataViewIdPropertyTitle">{i18n.DATA_VIEW_ID_FIELD_LABEL}</span>
),
description: <DataViewId dataViewId={rule.data_view_id} />,
},
{
title: i18n.DATA_VIEW_INDEX_PATTERN_FIELD_LABEL,
title: (
<span data-test-subj="dataViewIndexPatternPropertyTitle">
{i18n.DATA_VIEW_INDEX_PATTERN_FIELD_LABEL}
</span>
),
description: <DataViewIndexPattern dataViewId={rule.data_view_id} />,
}
);
@ -406,52 +459,86 @@ const prepareDefinitionSectionListItems = (
if (savedQuery) {
definitionSectionListItems.push({
title: descriptionStepI18n.SAVED_QUERY_NAME_LABEL,
title: (
<span data-test-subj="savedQueryNamePropertyTitle">
{descriptionStepI18n.SAVED_QUERY_NAME_LABEL}
</span>
),
description: <SavedQueryName savedQueryName={savedQuery.attributes.title} />,
});
if (savedQuery.attributes.filters) {
definitionSectionListItems.push({
title: descriptionStepI18n.SAVED_QUERY_FILTERS_LABEL,
description: <Filters filters={savedQuery.attributes.filters as Filter[]} />,
title: (
<span data-test-subj="savedQueryFiltersPropertyTitle">
{descriptionStepI18n.SAVED_QUERY_FILTERS_LABEL}
</span>
),
description: (
<Filters
filters={savedQuery.attributes.filters as Filter[]}
data-test-subj="savedQueryFiltersPropertyValue"
/>
),
});
}
if (typeof savedQuery.attributes.query.query === 'string') {
definitionSectionListItems.push({
title: descriptionStepI18n.SAVED_QUERY_LABEL,
description: <Query query={savedQuery.attributes.query.query} />,
title: (
<span data-test-subj="savedQueryContentPropertyTitle">
{descriptionStepI18n.SAVED_QUERY_LABEL}
</span>
),
description: (
<Query
query={savedQuery.attributes.query.query}
data-test-subj="savedQueryContentPropertyValue"
/>
),
});
}
}
if ('filters' in rule && rule.filters?.length) {
definitionSectionListItems.push({
title: descriptionStepI18n.FILTERS_LABEL,
title: <span data-test-subj="filtersPropertyTitle">{descriptionStepI18n.FILTERS_LABEL}</span>,
description: (
<Filters
filters={rule.filters as Filter[]}
dataViewId={rule.data_view_id}
index={rule.index}
data-test-subj="filtersPropertyValue"
/>
),
});
}
if ('query' in rule && rule.query) {
let title = descriptionStepI18n.QUERY_LABEL;
if (rule.type === 'saved_query') {
title = descriptionStepI18n.SAVED_QUERY_LABEL;
} else if (rule.type === 'eql') {
title = descriptionStepI18n.EQL_QUERY_LABEL;
if (rule.type === 'eql') {
definitionSectionListItems.push({
title: (
<span data-test-subj="eqlQueryPropertyTitle">{descriptionStepI18n.EQL_QUERY_LABEL}</span>
),
description: <Query query={rule.query} data-test-subj="eqlQueryPropertyValue" />,
});
} else if (rule.type === 'esql') {
title = descriptionStepI18n.ESQL_QUERY_LABEL;
definitionSectionListItems.push({
title: (
<span data-test-subj="esqlQueryPropertyTitle">
{descriptionStepI18n.ESQL_QUERY_LABEL}
</span>
),
description: <Query query={rule.query} data-test-subj="esqlQueryPropertyValue" />,
});
} else {
definitionSectionListItems.push({
title: (
<span data-test-subj="customQueryPropertyTitle">{descriptionStepI18n.QUERY_LABEL}</span>
),
description: <Query query={rule.query} data-test-subj="customQueryPropertyValue" />,
});
}
definitionSectionListItems.push({
title,
description: <Query query={rule.query} />,
});
}
if (rule.type) {
@ -463,14 +550,20 @@ const prepareDefinitionSectionListItems = (
if ('anomaly_threshold' in rule && rule.anomaly_threshold) {
definitionSectionListItems.push({
title: i18n.ANOMALY_THRESHOLD_FIELD_LABEL,
title: (
<span data-test-subj="anomalyThresholdPropertyTitle">
{i18n.ANOMALY_THRESHOLD_FIELD_LABEL}
</span>
),
description: <AnomalyThreshold anomalyThreshold={rule.anomaly_threshold} />,
});
}
if ('machine_learning_job_id' in rule) {
definitionSectionListItems.push({
title: i18n.MACHINE_LEARNING_JOB_ID_FIELD_LABEL,
title: (
<span data-test-subj="mlJobPropertyTitle">{i18n.MACHINE_LEARNING_JOB_ID_FIELD_LABEL}</span>
),
description: (
<MachineLearningJobList
jobIds={rule.machine_learning_job_id as string[]}
@ -482,22 +575,33 @@ const prepareDefinitionSectionListItems = (
if (rule.related_integrations && rule.related_integrations.length > 0) {
definitionSectionListItems.push({
title: i18n.RELATED_INTEGRATIONS_FIELD_LABEL,
title: (
<span data-test-subj="relatedIntegrationsPropertyTitle">
{i18n.RELATED_INTEGRATIONS_FIELD_LABEL}
</span>
),
description: (
<RelatedIntegrationsDescription relatedIntegrations={rule.related_integrations} />
<RelatedIntegrationsDescription
relatedIntegrations={rule.related_integrations}
dataTestSubj="relatedIntegrationsPropertyValue"
/>
),
});
}
if (rule.required_fields && rule.required_fields.length > 0) {
definitionSectionListItems.push({
title: i18n.REQUIRED_FIELDS_FIELD_LABEL,
title: (
<span data-test-subj="requiredFieldsPropertyTitle">{i18n.REQUIRED_FIELDS_FIELD_LABEL}</span>
),
description: <RequiredFields requiredFields={rule.required_fields} />,
});
}
definitionSectionListItems.push({
title: i18n.TIMELINE_TITLE_FIELD_LABEL,
title: (
<span data-test-subj="timelineTemplatePropertyTitle">{i18n.TIMELINE_TITLE_FIELD_LABEL}</span>
),
description: (
<TimelineTitle timelineTitle={rule.timeline_title || timelinesI18n.DEFAULT_TIMELINE_TITLE} />
),
@ -505,33 +609,38 @@ const prepareDefinitionSectionListItems = (
if ('threshold' in rule && rule.threshold) {
definitionSectionListItems.push({
title: i18n.THRESHOLD_FIELD_LABEL,
title: <span data-test-subj="thresholdPropertyTitle">{i18n.THRESHOLD_FIELD_LABEL}</span>,
description: <Threshold threshold={rule.threshold} />,
});
}
if ('threat_index' in rule && rule.threat_index) {
definitionSectionListItems.push({
title: i18n.THREAT_INDEX_FIELD_LABEL,
title: <span data-test-subj="threatIndexPropertyTitle">{i18n.THREAT_INDEX_FIELD_LABEL}</span>,
description: <ThreatIndex threatIndex={rule.threat_index} />,
});
}
if ('threat_mapping' in rule && rule.threat_mapping) {
definitionSectionListItems.push({
title: i18n.THREAT_MAPPING_FIELD_LABEL,
title: (
<span data-test-subj="threatMappingPropertyTitle">{i18n.THREAT_MAPPING_FIELD_LABEL}</span>
),
description: <ThreatMapping threatMapping={rule.threat_mapping} />,
});
}
if ('threat_filters' in rule && rule.threat_filters && rule.threat_filters.length > 0) {
definitionSectionListItems.push({
title: i18n.THREAT_FILTERS_FIELD_LABEL,
title: (
<span data-test-subj="threatFiltersPropertyTitle">{i18n.THREAT_FILTERS_FIELD_LABEL}</span>
),
description: (
<Filters
filters={rule.threat_filters as Filter[]}
dataViewId={rule.data_view_id}
index={rule.index}
data-test-subj="threatFiltersPropertyValue"
/>
),
});
@ -539,24 +648,40 @@ const prepareDefinitionSectionListItems = (
if ('threat_query' in rule && rule.threat_query) {
definitionSectionListItems.push({
title: descriptionStepI18n.THREAT_QUERY_LABEL,
description: <Query query={rule.threat_query} />,
title: (
<span data-test-subj="threatQueryPropertyTitle">
{descriptionStepI18n.THREAT_QUERY_LABEL}
</span>
),
description: <Query query={rule.threat_query} data-test-subj="threatQueryPropertyValue" />,
});
}
if ('alert_suppression' in rule && rule.alert_suppression) {
definitionSectionListItems.push({
title: <AlertSuppressionTitle title={i18n.SUPPRESS_ALERTS_BY_FIELD_LABEL} />,
title: (
<span data-test-subj="alertSuppressionGroupByPropertyTitle">
<AlertSuppressionTitle title={i18n.SUPPRESS_ALERTS_BY_FIELD_LABEL} />
</span>
),
description: <SuppressAlertsByField fields={rule.alert_suppression.group_by} />,
});
definitionSectionListItems.push({
title: <AlertSuppressionTitle title={i18n.SUPPRESS_ALERTS_DURATION_FIELD_LABEL} />,
title: (
<span data-test-subj="alertSuppressionDurationPropertyTitle">
<AlertSuppressionTitle title={i18n.SUPPRESS_ALERTS_DURATION_FIELD_LABEL} />
</span>
),
description: <SuppressAlertsDuration duration={rule.alert_suppression.duration} />,
});
definitionSectionListItems.push({
title: <AlertSuppressionTitle title={i18n.SUPPRESSION_FIELD_MISSING_FIELD_LABEL} />,
title: (
<span data-test-subj="alertSuppressionSuppressionFieldPropertyTitle">
<AlertSuppressionTitle title={i18n.SUPPRESSION_FIELD_MISSING_FIELD_LABEL} />
</span>
),
description: (
<MissingFieldsStrategy
missingFieldsStrategy={rule.alert_suppression.missing_fields_strategy}
@ -567,14 +692,22 @@ const prepareDefinitionSectionListItems = (
if ('new_terms_fields' in rule && rule.new_terms_fields && rule.new_terms_fields.length > 0) {
definitionSectionListItems.push({
title: i18n.NEW_TERMS_FIELDS_FIELD_LABEL,
title: (
<span data-test-subj="newTermsFieldsPropertyTitle">
{i18n.NEW_TERMS_FIELDS_FIELD_LABEL}
</span>
),
description: <NewTermsFields newTermsFields={rule.new_terms_fields} />,
});
}
if ('history_window_start' in rule) {
definitionSectionListItems.push({
title: i18n.HISTORY_WINDOW_SIZE_FIELD_LABEL,
title: (
<span data-test-subj="newTermsWindowSizePropertyTitle">
{i18n.HISTORY_WINDOW_SIZE_FIELD_LABEL}
</span>
),
description: <HistoryWindowSize historyWindowStart={rule.history_window_start} />,
});
}

View file

@ -10,7 +10,6 @@ import styled from 'styled-components';
import { css } from '@emotion/css';
import { euiThemeVars } from '@kbn/ui-theme';
import {
EuiButton,
EuiButtonEmpty,
EuiTitle,
EuiFlyout,
@ -102,17 +101,15 @@ const TabContentPadding: React.FC = ({ children }) => (
interface RuleDetailsFlyoutProps {
rule: RuleResponse;
actionButtonLabel: string;
isActionButtonDisabled: boolean;
onActionButtonClick: (ruleId: string) => void;
ruleActions?: React.ReactNode;
dataTestSubj?: string;
closeFlyout: () => void;
}
export const RuleDetailsFlyout = ({
rule,
actionButtonLabel,
isActionButtonDisabled,
onActionButtonClick,
ruleActions,
dataTestSubj,
closeFlyout,
}: RuleDetailsFlyoutProps) => {
const { expandedOverviewSections, toggleOverviewSection } = useOverviewTabSections();
@ -176,6 +173,7 @@ export const RuleDetailsFlyout = ({
ownFocus={false}
key="prebuilt-rules-flyout"
paddingSize="l"
data-test-subj={dataTestSubj}
>
<EuiFlyoutHeader>
<EuiTitle size="m">
@ -197,18 +195,7 @@ export const RuleDetailsFlyout = ({
{i18n.DISMISS_BUTTON_LABEL}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
disabled={isActionButtonDisabled}
onClick={() => {
onActionButtonClick(rule.rule_id ?? '');
closeFlyout();
}}
fill
>
{actionButtonLabel}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>{ruleActions}</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutFooter>
</EuiFlyout>

View file

@ -16,7 +16,11 @@ interface IntervalProps {
interval: string;
}
const Interval = ({ interval }: IntervalProps) => <EuiText size="s">{interval}</EuiText>;
const Interval = ({ interval }: IntervalProps) => (
<EuiText size="s" data-test-subj="intervalPropertyValue">
{interval}
</EuiText>
);
interface FromProps {
from: string;
@ -24,7 +28,9 @@ interface FromProps {
}
const From = ({ from, interval }: FromProps) => (
<EuiText size="s">{getHumanizedDuration(from, interval)}</EuiText>
<EuiText size="s" data-test-subj={`fromPropertyValue-${from}`}>
{getHumanizedDuration(from, interval)}
</EuiText>
);
export interface RuleScheduleSectionProps extends React.ComponentProps<typeof EuiDescriptionList> {
@ -43,11 +49,11 @@ export const RuleScheduleSection = ({
ruleSectionListItems.push(
{
title: i18n.INTERVAL_FIELD_LABEL,
title: <span data-test-subj="intervalPropertyTitle">{i18n.INTERVAL_FIELD_LABEL}</span>,
description: <Interval interval={rule.interval} />,
},
{
title: i18n.FROM_FIELD_LABEL,
title: <span data-test-subj="fromPropertyTitle">{i18n.FROM_FIELD_LABEL}</span>,
description: <From from={rule.from} interval={rule.interval} />,
}
);

View file

@ -7,6 +7,7 @@
import type { Dispatch, SetStateAction } from 'react';
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
import { EuiButton } from '@elastic/eui';
import { useFetchPrebuiltRulesStatusQuery } from '../../../../rule_management/api/hooks/prebuilt_rules/use_fetch_prebuilt_rules_status_query';
import { useIsUpgradingSecurityPackages } from '../../../../rule_management/logic/use_upgrade_security_packages';
import type { RuleSignatureId } from '../../../../../../common/api/detection_engine';
@ -232,10 +233,21 @@ export const AddPrebuiltRulesTableContextProvider = ({
{previewedRule && (
<RuleDetailsFlyout
rule={previewedRule}
actionButtonLabel={i18n.INSTALL_BUTTON_LABEL}
isActionButtonDisabled={canPreviewedRuleBeInstalled}
onActionButtonClick={installOneRule}
dataTestSubj="installPrebuiltRulePreview"
closeFlyout={closeRulePreview}
ruleActions={
<EuiButton
disabled={canPreviewedRuleBeInstalled}
onClick={() => {
installOneRule(previewedRule.rule_id ?? '');
closeRulePreview();
}}
fill
data-test-subj="installPrebuiltRuleFromFlyoutButton"
>
{i18n.INSTALL_BUTTON_LABEL}
</EuiButton>
}
/>
)}
</>

View file

@ -7,6 +7,7 @@
import type { Dispatch, SetStateAction } from 'react';
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
import { EuiButton } from '@elastic/eui';
import { useIsUpgradingSecurityPackages } from '../../../../rule_management/logic/use_upgrade_security_packages';
import { useInstalledSecurityJobs } from '../../../../../common/components/ml/hooks/use_installed_security_jobs';
import { useBoolState } from '../../../../../common/hooks/use_bool_state';
@ -270,10 +271,21 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
{previewedRule && (
<RuleDetailsFlyout
rule={previewedRule}
actionButtonLabel={i18n.UPDATE_BUTTON_LABEL}
isActionButtonDisabled={canPreviewedRuleBeUpgraded}
onActionButtonClick={upgradeOneRule}
dataTestSubj="updatePrebuiltRulePreview"
closeFlyout={closeRulePreview}
ruleActions={
<EuiButton
disabled={canPreviewedRuleBeUpgraded}
onClick={() => {
upgradeOneRule(previewedRule.rule_id ?? '');
closeRulePreview();
}}
fill
data-test-subj="updatePrebuiltRuleFromFlyoutButton"
>
{i18n.UPDATE_BUTTON_LABEL}
</EuiButton>
}
/>
)}
</>

View file

@ -25,7 +25,7 @@ import React from 'react';
import styled from 'styled-components';
import { FieldIcon } from '@kbn/react-field';
import type { ThreatMapping, Type } from '@kbn/securitysolution-io-ts-alerting-types';
import type { ThreatMapping, Type, Threats } from '@kbn/securitysolution-io-ts-alerting-types';
import { FilterBadgeGroup } from '@kbn/unified-search-plugin/public';
import type {
RequiredFieldArray,
@ -40,7 +40,7 @@ import * as i18nSeverity from '../severity_mapping/translations';
import * as i18nRiskScore from '../risk_score_mapping/translations';
import * as i18n from './translations';
import type { BuildQueryBarDescription, BuildThreatDescription, ListItems } from './types';
import type { BuildQueryBarDescription, ListItems } from './types';
import { SeverityBadge } from '../severity_badge';
import type {
AboutStepRiskScore,
@ -163,12 +163,20 @@ export const buildEqlOptionsDescription = (eqlOptions: EqlOptionsSelected): List
return items;
};
export const buildThreatDescription = ({ label, threat }: BuildThreatDescription): ListItems[] => {
interface BuildThreatDescriptionProps {
label: string;
threat: Threats;
}
export const buildThreatDescription = ({
threat,
label,
}: BuildThreatDescriptionProps): ListItems[] => {
if (threat.length > 0) {
return [
{
title: label,
description: <ThreatEuiFlexGroup label={label} threat={threat} />,
description: <ThreatEuiFlexGroup threat={threat} />,
},
];
}

View file

@ -40,7 +40,10 @@ const TechniqueLinkItem = styled(EuiButtonEmpty)`
align-self: flex-start;
`;
export const ThreatEuiFlexGroup = ({ label, threat }: BuildThreatDescription) => {
export const ThreatEuiFlexGroup = ({
threat,
'data-test-subj': dataTestSubj = 'threat',
}: BuildThreatDescription) => {
const [techniquesOptions, setTechniquesOptions] = useState<MitreTechnique[]>([]);
const [tacticsOptions, setTacticsOptions] = useState<MitreTactic[]>([]);
const [subtechniquesOptions, setSubtechniquesOptions] = useState<MitreSubTechnique[]>([]);
@ -54,8 +57,9 @@ export const ThreatEuiFlexGroup = ({ label, threat }: BuildThreatDescription) =>
}
getMitre();
}, []);
return (
<ThreatEuiFlexGroupStyles direction="column">
<ThreatEuiFlexGroupStyles direction="column" data-test-subj={dataTestSubj}>
{threat.map((singleThreat, index) => {
const tactic = tacticsOptions.find((t) => t.id === singleThreat.tactic.id);
return (

View file

@ -27,6 +27,6 @@ export interface BuildQueryBarDescription {
}
export interface BuildThreatDescription {
label: string;
threat: Threats;
'data-test-subj'?: string;
}

View file

@ -21,11 +21,12 @@ const Wrapper = styled.div`
overflow: hidden;
`;
export const IntegrationDescriptionComponent: React.FC<{ integration: IntegrationDetails }> = ({
integration,
}) => {
export const IntegrationDescriptionComponent: React.FC<{
integration: IntegrationDetails;
dataTestSubj?: string;
}> = ({ integration, dataTestSubj = 'integrationDescription' }) => {
return (
<Wrapper>
<Wrapper data-test-subj={`${dataTestSubj}-${integration.packageName}`}>
<IntegrationLink integration={integration} />{' '}
<IntegrationStatusBadge integration={integration} />
<IntegrationVersionMismatchIcon integration={integration} />
@ -37,7 +38,8 @@ export const IntegrationDescription = React.memo(IntegrationDescriptionComponent
export const RelatedIntegrationsDescription: React.FC<{
relatedIntegrations: RelatedIntegrationArray;
}> = ({ relatedIntegrations }) => {
dataTestSubj?: string;
}> = ({ relatedIntegrations, dataTestSubj = 'relatedIntegrationsDescription' }) => {
const { integrations } = useRelatedIntegrations(relatedIntegrations);
return (
@ -46,6 +48,7 @@ export const RelatedIntegrationsDescription: React.FC<{
<IntegrationDescription
key={`${integration.packageName}-${index}`}
integration={integration}
dataTestSubj={dataTestSubj}
/>
))}
</>

View file

@ -22,14 +22,22 @@ const severityToColorMap: Record<Severity, string> = {
interface Props {
value: Severity;
'data-test-subj'?: string;
}
const SeverityBadgeComponent: React.FC<Props> = ({ value }) => {
const SeverityBadgeComponent: React.FC<Props> = ({
value,
'data-test-subj': dataTestSubj = 'severity',
}) => {
const displayValue = upperFirst(value);
const color = severityToColorMap[value] ?? 'subdued';
return (
<HealthTruncateText healthColor={color} tooltipContent={displayValue} dataTestSubj="severity">
<HealthTruncateText
healthColor={color}
tooltipContent={displayValue}
dataTestSubj={dataTestSubj}
>
{displayValue}
</HealthTruncateText>
);

View file

@ -205,7 +205,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
);
expect(await rulePageDescription.getVisibleText()).to.be(data[0].rule.rationale);
const severity = await testSubjects.find('severity');
const severity = await testSubjects.find('severityPropertyValue');
expect(await severity.getVisibleText()).to.be('Low');
const referenceUrls = await testSubjects.find('urlsDescriptionReferenceLinkItem');

View file

@ -24,14 +24,16 @@ import {
import { cleanKibana } from '../../../tasks/common';
import { login } from '../../../tasks/login';
import {
addElasticRulesButtonClick,
clickAddElasticRulesButton,
assertInstallationRequestIsComplete,
interceptInstallationRequestToFail,
interceptUpgradeRequestToFail,
assertUpgradeFailure,
ruleUpdatesTabClick,
assertInstallationFailure,
clickRuleUpdatesTab,
assertUpgradeRequestIsComplete,
assertRuleInstallationFailureToastShown,
assertRulesPresentInAddPrebuiltRulesTable,
assertRuleUpgradeFailureToastShown,
assertRulesPresentInRuleUpdatesTable,
} from '../../../tasks/prebuilt_rules';
import { visitRulesManagementTable } from '../../../tasks/rules_management';
@ -64,7 +66,7 @@ describe(
it('installing prebuilt rules one by one', () => {
// Navigate to install Elastic rules page
addElasticRulesButtonClick();
clickAddElasticRulesButton();
// Intercept and force the installation request to fail
interceptInstallationRequestToFail([RULE_1]);
@ -74,34 +76,38 @@ describe(
// Wait for request to complete
assertInstallationRequestIsComplete([RULE_1]);
assertInstallationFailure([RULE_1]);
assertRuleInstallationFailureToastShown([RULE_1]);
assertRulesPresentInAddPrebuiltRulesTable([RULE_1]);
});
it('installing multiple selected prebuilt rules by selecting them individually', () => {
addElasticRulesButtonClick();
clickAddElasticRulesButton();
interceptInstallationRequestToFail([RULE_1, RULE_2]);
selectRulesByName([RULE_1['security-rule'].name, RULE_2['security-rule'].name]);
cy.get(INSTALL_SELECTED_RULES_BUTTON).click();
assertInstallationRequestIsComplete([RULE_1, RULE_2]);
assertInstallationFailure([RULE_1, RULE_2]);
assertRuleInstallationFailureToastShown([RULE_1, RULE_2]);
assertRulesPresentInAddPrebuiltRulesTable([RULE_1, RULE_2]);
});
it('installing multiple selected prebuilt rules by selecting all in page', () => {
addElasticRulesButtonClick();
clickAddElasticRulesButton();
interceptInstallationRequestToFail([RULE_1, RULE_2]);
cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click();
cy.get(INSTALL_SELECTED_RULES_BUTTON).click();
assertInstallationRequestIsComplete([RULE_1, RULE_2]);
assertInstallationFailure([RULE_1, RULE_2]);
assertRuleInstallationFailureToastShown([RULE_1, RULE_2]);
assertRulesPresentInAddPrebuiltRulesTable([RULE_1, RULE_2]);
});
it('installing all available rules at once', () => {
addElasticRulesButtonClick();
clickAddElasticRulesButton();
interceptInstallationRequestToFail([RULE_1, RULE_2]);
cy.get(INSTALL_ALL_RULES_BUTTON).click();
assertInstallationRequestIsComplete([RULE_1, RULE_2]);
assertInstallationFailure([RULE_1, RULE_2]);
assertRuleInstallationFailureToastShown([RULE_1, RULE_2]);
assertRulesPresentInAddPrebuiltRulesTable([RULE_1, RULE_2]);
});
});
@ -141,7 +147,7 @@ describe(
interceptUpgradeRequestToFail([OUTDATED_RULE_1]);
// Navigate to Rule Upgrade table
ruleUpdatesTabClick();
clickRuleUpdatesTab();
// Attempt to upgrade rule
cy.get(
@ -150,14 +156,15 @@ describe(
// Wait for request to complete
assertUpgradeRequestIsComplete([OUTDATED_RULE_1]);
assertUpgradeFailure([OUTDATED_RULE_1]);
assertRuleUpgradeFailureToastShown([OUTDATED_RULE_1]);
assertRulesPresentInRuleUpdatesTable([OUTDATED_RULE_1]);
});
it('upgrading multiple selected prebuilt rules by selecting them individually', () => {
interceptUpgradeRequestToFail([OUTDATED_RULE_1, OUTDATED_RULE_2]);
// Navigate to Rule Upgrade table
ruleUpdatesTabClick();
clickRuleUpdatesTab();
selectRulesByName([
OUTDATED_RULE_1['security-rule'].name,
@ -165,29 +172,32 @@ describe(
]);
cy.get(UPGRADE_SELECTED_RULES_BUTTON).click();
assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertUpgradeFailure([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertRuleUpgradeFailureToastShown([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertRulesPresentInRuleUpdatesTable([OUTDATED_RULE_1, OUTDATED_RULE_2]);
});
it('upgrading multiple selected prebuilt rules by selecting all in page', () => {
interceptUpgradeRequestToFail([OUTDATED_RULE_1, OUTDATED_RULE_2]);
// Navigate to Rule Upgrade table
ruleUpdatesTabClick();
clickRuleUpdatesTab();
cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click();
cy.get(UPGRADE_SELECTED_RULES_BUTTON).click();
assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertUpgradeFailure([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertRuleUpgradeFailureToastShown([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertRulesPresentInRuleUpdatesTable([OUTDATED_RULE_1, OUTDATED_RULE_2]);
});
it('upgrading all rules with available upgrades at once', () => {
interceptUpgradeRequestToFail([OUTDATED_RULE_1, OUTDATED_RULE_2]);
// Navigate to Rule Upgrade table
ruleUpdatesTabClick();
clickRuleUpdatesTab();
cy.get(UPGRADE_ALL_RULES_BUTTON).click();
assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertUpgradeFailure([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertRuleUpgradeFailureToastShown([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertRulesPresentInRuleUpdatesTable([OUTDATED_RULE_1, OUTDATED_RULE_2]);
});
});
}

View file

@ -12,7 +12,7 @@ import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/commo
import { INSTALL_ALL_RULES_BUTTON, TOASTER } from '../../../screens/alerts_detection_rules';
import { getRuleAssets } from '../../../tasks/api_calls/prebuilt_rules';
import { login } from '../../../tasks/login';
import { addElasticRulesButtonClick } from '../../../tasks/prebuilt_rules';
import { clickAddElasticRulesButton } from '../../../tasks/prebuilt_rules';
import { visitRulesManagementTable } from '../../../tasks/rules_management';
describe(
@ -86,7 +86,7 @@ describe(
);
const numberOfRulesToInstall = new Set(ruleIds).size;
addElasticRulesButtonClick();
clickAddElasticRulesButton();
cy.get(INSTALL_ALL_RULES_BUTTON).should('be.enabled').click();
cy.wait('@installPrebuiltRules', {

View file

@ -18,12 +18,14 @@ import {
TOASTER,
} from '../../../screens/alerts_detection_rules';
import { selectRulesByName } from '../../../tasks/alerts_detection_rules';
import { RULE_MANAGEMENT_PAGE_BREADCRUMB } from '../../../screens/breadcrumbs';
import { installPrebuiltRuleAssets } from '../../../tasks/api_calls/prebuilt_rules';
import { login } from '../../../tasks/login';
import {
addElasticRulesButtonClick,
assertInstallationSuccess,
assertInstallationRequestIsComplete,
assertRuleInstallationSuccessToastShown,
assertRulesPresentInInstalledRulesTable,
clickAddElasticRulesButton,
} from '../../../tasks/prebuilt_rules';
import { visitRulesManagementTable } from '../../../tasks/rules_management';
@ -49,7 +51,7 @@ describe(
cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform').as(
'installPrebuiltRules'
);
addElasticRulesButtonClick();
clickAddElasticRulesButton();
});
it('should install prebuilt rules one by one', () => {
@ -58,27 +60,39 @@ describe(
// Wait for request to complete
assertInstallationRequestIsComplete([RULE_1]);
// Assert installation succeeded
assertInstallationSuccess([RULE_1]);
assertRuleInstallationSuccessToastShown([RULE_1]);
// Go back to rules table and assert that the rules are installed
cy.get(RULE_MANAGEMENT_PAGE_BREADCRUMB).click();
assertRulesPresentInInstalledRulesTable([RULE_1]);
});
it('should install multiple selected prebuilt rules by selecting them individually', () => {
selectRulesByName([RULE_1['security-rule'].name, RULE_2['security-rule'].name]);
cy.get(INSTALL_SELECTED_RULES_BUTTON).click();
assertInstallationRequestIsComplete([RULE_1, RULE_2]);
assertInstallationSuccess([RULE_1, RULE_2]);
assertRuleInstallationSuccessToastShown([RULE_1, RULE_2]);
// Go back to rules table and assert that the rules are installed
cy.get(RULE_MANAGEMENT_PAGE_BREADCRUMB).click();
assertRulesPresentInInstalledRulesTable([RULE_1, RULE_2]);
});
it('should install multiple selected prebuilt rules by selecting all in page', () => {
cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click();
cy.get(INSTALL_SELECTED_RULES_BUTTON).click();
assertInstallationRequestIsComplete([RULE_1, RULE_2]);
assertInstallationSuccess([RULE_1, RULE_2]);
assertRuleInstallationSuccessToastShown([RULE_1, RULE_2]);
// Go back to rules table and assert that the rules are installed
cy.get(RULE_MANAGEMENT_PAGE_BREADCRUMB).click();
assertRulesPresentInInstalledRulesTable([RULE_1, RULE_2]);
});
it('should install all available rules at once', () => {
cy.get(INSTALL_ALL_RULES_BUTTON).click();
assertInstallationRequestIsComplete([RULE_1, RULE_2]);
assertInstallationSuccess([RULE_1, RULE_2]);
assertRuleInstallationSuccessToastShown([RULE_1, RULE_2]);
// Go back to rules table and assert that the rules are installed
cy.get(RULE_MANAGEMENT_PAGE_BREADCRUMB).click();
assertRulesPresentInInstalledRulesTable([RULE_1, RULE_2]);
});
it('should display an empty screen when all available prebuilt rules have been installed', () => {

View file

@ -22,9 +22,10 @@ import {
import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common';
import { login } from '../../../tasks/login';
import {
ruleUpdatesTabClick,
assertRulesNotPresentInRuleUpdatesTable,
assertRuleUpgradeSuccessToastShown,
assertUpgradeRequestIsComplete,
assertUpgradeSuccess,
clickRuleUpdatesTab,
} from '../../../tasks/prebuilt_rules';
import { visitRulesManagementTable } from '../../../tasks/rules_management';
@ -68,7 +69,7 @@ describe(
installPrebuiltRuleAssets([UPDATED_RULE_1, UPDATED_RULE_2]);
visitRulesManagementTable();
ruleUpdatesTabClick();
clickRuleUpdatesTab();
});
it('should upgrade prebuilt rules one by one', () => {
@ -79,7 +80,8 @@ describe(
// Wait for request to complete
assertUpgradeRequestIsComplete([OUTDATED_RULE_1]);
assertUpgradeSuccess([OUTDATED_RULE_1]);
assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_1]);
assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_1]);
});
it('should upgrade multiple selected prebuilt rules by selecting them individually', () => {
@ -89,20 +91,23 @@ describe(
]);
cy.get(UPGRADE_SELECTED_RULES_BUTTON).click();
assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertUpgradeSuccess([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_1, OUTDATED_RULE_2]);
});
it('should upgrade multiple selected prebuilt rules by selecting all in page', () => {
cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click();
cy.get(UPGRADE_SELECTED_RULES_BUTTON).click();
assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertUpgradeSuccess([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_1, OUTDATED_RULE_2]);
});
it('should upgrade all rules with available upgrades at once', () => {
cy.get(UPGRADE_ALL_RULES_BUTTON).click();
assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertUpgradeSuccess([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_1, OUTDATED_RULE_2]);
assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_1, OUTDATED_RULE_2]);
});
it('should display an empty screen when all rules with available updates have been upgraded', () => {

View file

@ -191,3 +191,151 @@ export const NO_RULES_AVAILABLE_FOR_INSTALL_MESSAGE =
'[data-test-subj="noPrebuiltRulesAvailableForInstall"]';
export const NO_RULES_AVAILABLE_FOR_UPGRADE_MESSAGE =
'[data-test-subj="noPrebuiltRulesAvailableForUpgrade"]';
export const INSTALL_PREBUILT_RULE_PREVIEW = '[data-test-subj="installPrebuiltRulePreview"]';
export const INSTALL_PREBUILT_RULE_BUTTON =
'[data-test-subj="installPrebuiltRuleFromFlyoutButton"]';
export const UPDATE_PREBUILT_RULE_PREVIEW = '[data-test-subj="updatePrebuiltRulePreview"]';
export const UPDATE_PREBUILT_RULE_BUTTON = '[data-test-subj="updatePrebuiltRuleFromFlyoutButton"]';
export const FLYOUT_CLOSE_BTN = '[data-test-subj="euiFlyoutCloseButton"]';
export const AUTHOR_PROPERTY_TITLE = '[data-test-subj="authorPropertyTitle"]';
export const AUTHOR_PROPERTY_VALUE_ITEM = '[data-test-subj="authorPropertyValueItem"]';
export const BUILDING_BLOCK_TITLE = '[data-test-subj="buildingBlockPropertyTitle"]';
export const BUILDING_BLOCK_VALUE = '[data-test-subj="buildingBlockPropertyValue"]';
export const SEVERITY_TITLE = '[data-test-subj="severityPropertyTitle"]';
export const SEVERITY_VALUE = '[data-test-subj="severityPropertyValue"]';
export const SEVERITY_MAPPING_TITLE = '[data-test-subj="severityOverridePropertyTitle"]';
export const SEVERITY_MAPPING_VALUE_FIELD = '[data-test-subj="severityOverrideField"]';
export const SEVERITY_MAPPING_VALUE_VALUE = '[data-test-subj="severityOverrideValue"]';
export const SEVERITY_MAPPING_VALUE_SEVERITY = '[data-test-subj="severityOverrideSeverity"]';
export const RISK_SCORE_TITLE = '[data-test-subj="riskScorePropertyTitle"]';
export const RISK_SCORE_VALUE = '[data-test-subj="riskScorePropertyValue"]';
export const RISK_SCORE_MAPPING_TITLE = '[data-test-subj="riskScoreOverridePropertyTitle"]';
export const RISK_SCORE_MAPPING_VALUE_FIELD_NAME =
'[data-test-subj="riskScoreOverridePropertyFieldName"]';
export const RISK_SCORE_MAPPING_VALUE_OVERRIDE_NAME =
'[data-test-subj="riskScoreOverridePropertyOverride"]';
export const REFERENCES_TITLE = '[data-test-subj="referencesPropertyTitle"]';
export const REFERENCES_VALUE_ITEM = '[data-test-subj="urlsDescriptionReferenceLinkItem"]';
export const FALSE_POSITIVES_TITLE = '[data-test-subj="falsePositivesPropertyTitle"]';
export const FALSE_POSITIVES_VALUE_ITEM = '[data-test-subj="falsePositivesPropertyValueItem"]';
export const INVESTIGATION_FIELDS_TITLE = '[data-test-subj="investigationFieldsPropertyTitle"]';
export const INVESTIGATION_FIELDS_VALUE_ITEM =
'[data-test-subj="investigationFieldsPropertyValueItem"]';
export const LICENSE_TITLE = '[data-test-subj="licensePropertyTitle"]';
export const LICENSE_VALUE = '[data-test-subj="licensePropertyValue"]';
export const RULE_NAME_OVERRIDE_TITLE = '[data-test-subj="ruleNameOverridePropertyTitle"]';
export const RULE_NAME_OVERRIDE_VALUE = '[data-test-subj="ruleNameOverridePropertyValue"]';
export const THREAT_TITLE = '[data-test-subj="threatPropertyTitle"]';
export const THREAT_TACTIC = '[data-test-subj="threatTacticLink"]';
export const TIMESTAMP_OVERRIDE_TITLE = '[data-test-subj="timestampOverridePropertyTitle"]';
export const TIMESTAMP_OVERRIDE_VALUE = '[data-test-subj="timestampOverridePropertyValue"]';
export const TAGS_PROPERTY_TITLE = '[data-test-subj="tagsPropertyTitle"]';
export const TAGS_PROPERTY_VALUE_ITEM = '[data-test-subj="tagsPropertyValueItem"]';
export const RELATED_INTEGRATIONS_TITLE = '[data-test-subj="relatedIntegrationsPropertyTitle"]';
export const RELATED_INTEGRATIONS_VALUE = '[data-test-subj^="relatedIntegrationsPropertyValue"]';
export const REQUIRED_FIELDS_PROPERTY_TITLE = '[data-test-subj="requiredFieldsPropertyTitle"]';
export const REQUIRED_FIELDS_PROPERTY_VALUE_ITEM =
'[data-test-subj="requiredFieldsPropertyValueItem"]';
export const TIMELINE_TEMPLATE_TITLE = '[data-test-subj="timelineTemplatePropertyTitle"]';
export const TIMELINE_TEMPLATE_VALUE = '[data-test-subj="timelineTemplatePropertyValue"]';
export const INTERVAL_TITLE = '[data-test-subj="intervalPropertyTitle"]';
export const INTERVAL_VALUE = '[data-test-subj="intervalPropertyValue"]';
export const FROM_TITLE = '[data-test-subj="fromPropertyTitle"]';
export const FROM_VALUE = '[data-test-subj^="fromPropertyValue"]';
export const INDEX_TITLE = '[data-test-subj="indexPropertyTitle"]';
export const INDEX_VALUE_ITEM = '[data-test-subj="indexPropertyValueItem"]';
export const CUSTOM_QUERY_TITLE = '[data-test-subj="customQueryPropertyTitle"]';
export const CUSTOM_QUERY_VALUE = '[data-test-subj="customQueryPropertyValue"]';
export const FILTERS_TITLE = '[data-test-subj="filtersPropertyTitle"]';
export const FILTERS_VALUE_ITEM =
'[data-test-subj="filtersPropertyValue"] [data-test-subj^="filterItem-"]';
export const ALERT_SUPPRESSION_GROUP_BY_TITLE =
'[data-test-subj="alertSuppressionGroupByPropertyTitle"]';
export const ALERT_SUPPRESSION_GROUP_BY_VALUE_ITEM =
'[data-test-subj="alertSuppressionGroupByPropertyValueItem"]';
export const ALERT_SUPPRESSION_DURATION_TITLE =
'[data-test-subj="alertSuppressionDurationPropertyTitle"]';
export const ALERT_SUPPRESSION_DURATION_VALUE =
'[data-test-subj="alertSuppressionDurationPropertyValue"]';
export const ALERT_SUPPRESSION_FIELD_TITLE =
'[data-test-subj="alertSuppressionSuppressionFieldPropertyTitle"]';
export const ALERT_SUPPRESSION_FIELD_VALUE =
'[data-test-subj="alertSuppressionSuppressionFieldPropertyValue"]';
export const DATA_VIEW_ID_TITLE = '[data-test-subj="dataViewIdPropertyTitle"]';
export const DATA_VIEW_ID_VALUE = '[data-test-subj="dataViewIdPropertyValue"]';
export const DATA_VIEW_INDEX_PATTERN_TITLE = '[data-test-subj="dataViewIndexPatternPropertyTitle"]';
export const DATA_VIEW_INDEX_PATTERN_VALUE = '[data-test-subj="dataViewIndexPatternPropertyValue"]';
export const SAVED_QUERY_CONTENT_TITLE = '[data-test-subj="savedQueryContentPropertyTitle"]';
export const SAVED_QUERY_CONTENT_VALUE = '[data-test-subj="savedQueryContentPropertyValue"]';
export const SAVED_QUERY_FILTERS_TITLE = '[data-test-subj="savedQueryFiltersPropertyTitle"]';
export const SAVED_QUERY_FILTERS_VALUE =
'[data-test-subj="savedQueryFiltersPropertyValue"] [data-test-subj^="filterItem-"]';
export const SAVED_QUERY_NAME_TITLE = '[data-test-subj="savedQueryNamePropertyTitle"]';
export const SAVED_QUERY_NAME_VALUE = '[data-test-subj="savedQueryNamePropertyValue"]';
export const ANOMALY_THRESHOLD_TITLE = '[data-test-subj="anomalyThresholdPropertyTitle"]';
export const ANOMALY_THRESHOLD_VALUE = '[data-test-subj="anomalyThresholdPropertyValue"]';
export const MACHINE_LEARNING_JOB_TITLE = '[data-test-subj="mlJobPropertyTitle"]';
export const MACHINE_LEARNING_JOB_VALUE = '[data-test-subj="machineLearningJob"]';
export const THRESHOLD_TITLE = '[data-test-subj="thresholdPropertyTitle"]';
export const THRESHOLD_VALUE = '[data-test-subj="thresholdPropertyValue"]';
export const EQL_QUERY_TITLE = '[data-test-subj="eqlQueryPropertyTitle"]';
export const EQL_QUERY_VALUE = '[data-test-subj="eqlQueryPropertyValue"]';
export const THREAT_INDEX_TITLE = '[data-test-subj="threatIndexPropertyTitle"]';
export const THREAT_INDEX_VALUE_ITEM = '[data-test-subj="threatIndexPropertyValueItem"]';
export const THREAT_MAPPING_TITLE = '[data-test-subj="threatMappingPropertyTitle"]';
export const THREAT_MAPPING_VALUE = '[data-test-subj="threatMappingPropertyValue"]';
export const THREAT_FILTERS_TITLE = '[data-test-subj="threatFiltersPropertyTitle"]';
export const THREAT_FILTERS_VALUE_ITEM =
'[data-test-subj="threatFiltersPropertyValue"] [data-test-subj^="filterItem-"]';
export const THREAT_QUERY_TITLE = '[data-test-subj="threatQueryPropertyTitle"]';
export const THREAT_QUERY_VALUE = '[data-test-subj="threatQueryPropertyValue"]';
export const NEW_TERMS_FIELDS_TITLE = '[data-test-subj="newTermsFieldsPropertyTitle"]';
export const NEW_TERMS_FIELDS_VALUE_ITEM = '[data-test-subj="newTermsFieldsPropertyValueItem"]';
export const NEW_TERMS_WINDOW_SIZE_TITLE = '[data-test-subj="newTermsWindowSizePropertyTitle"]';
export const NEW_TERMS_WINDOW_SIZE_VALUE = '[data-test-subj^="newTermsWindowSizePropertyValue"]';
export const ESQL_QUERY_TITLE = '[data-test-subj="esqlQueryPropertyTitle"]';
export const ESQL_QUERY_VALUE = '[data-test-subj="esqlQueryPropertyValue"]';

View file

@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ML_INTERNAL_BASE_PATH } from '@kbn/ml-plugin/common/constants/app';
import type { Module } from '@kbn/ml-plugin/common/types/modules';
import { rootRequest } from '../common';
export const fetchMachineLearningModules = () => {
return rootRequest<Module[]>({
method: 'GET',
url: `${ML_INTERNAL_BASE_PATH}/modules/get_module`,
headers: {
'kbn-xsrf': 'cypress-creds',
'x-elastic-internal-origin': 'security-solution',
'elastic-api-version': 1,
},
failOnStatusCode: false,
});
};

View file

@ -36,7 +36,11 @@ export const createSavedQuery = (
},
],
},
headers: { 'kbn-xsrf': 'cypress-creds', [ELASTIC_HTTP_VERSION_HEADER]: '1' },
headers: {
'kbn-xsrf': 'cypress-creds',
[ELASTIC_HTTP_VERSION_HEADER]: '1',
'x-elastic-internal-origin': 'security-solution',
},
});
export const deleteSavedQueries = () => {

View file

@ -305,16 +305,16 @@ export const deletePrebuiltRulesAssets = () => {
});
};
export const postDataView = (dataSource: string) => {
export const postDataView = (indexPattern: string, name?: string, id?: string) => {
rootRequest({
method: 'POST',
url: DATA_VIEW_PATH,
body: {
data_view: {
id: dataSource,
name: dataSource,
id: id || indexPattern,
name: name || indexPattern,
fieldAttrs: '{}',
title: dataSource,
title: indexPattern,
timeFieldName: '@timestamp',
},
},
@ -327,11 +327,17 @@ export const postDataView = (dataSource: string) => {
});
};
export const deleteDataView = (dataSource: string) => {
export const deleteDataView = (dataViewId: string) => {
rootRequest({
method: 'DELETE',
url: `api/data_views/data_view/${dataSource}`,
method: 'POST',
url: 'api/content_management/rpc/delete',
headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' },
body: {
contentTypeId: 'index-pattern',
id: dataViewId,
options: { force: true },
version: 1,
},
failOnStatusCode: false,
});
};

View file

@ -16,15 +16,14 @@ import {
RULES_UPDATES_TABLE,
TOASTER,
} from '../screens/alerts_detection_rules';
import { RULE_MANAGEMENT_PAGE_BREADCRUMB } from '../screens/breadcrumbs';
import type { SAMPLE_PREBUILT_RULE } from './api_calls/prebuilt_rules';
export const addElasticRulesButtonClick = () => {
export const clickAddElasticRulesButton = () => {
cy.get(ADD_ELASTIC_RULES_BTN).click();
cy.location('pathname').should('include', RULES_ADD_PATH);
};
export const ruleUpdatesTabClick = () => {
export const clickRuleUpdatesTab = () => {
cy.get(RULES_UPDATES_TAB).click();
cy.location('pathname').should('include', RULES_UPDATES);
};
@ -55,39 +54,6 @@ export const assertUpgradeRequestIsComplete = (rules: Array<typeof SAMPLE_PREBUI
}
};
/**
* Assert that when the rule installation succeeds, the toast is shown with the right message
* -confirming the succesful install- and subsequently check that the rules available for installation
* are not present in the Add Elastic Rules table anymore
*/
export const assertInstallationSuccess = (rules: Array<typeof SAMPLE_PREBUILT_RULE>) => {
const rulesString = rules.length > 1 ? 'rules' : 'rule';
const toastMessage = `${rules.length} ${rulesString} installed successfully.`;
cy.get(TOASTER).should('be.visible').should('have.text', toastMessage);
// Go back to rules table and assert that the rules are installed
cy.get(RULE_MANAGEMENT_PAGE_BREADCRUMB).click();
for (const rule of rules) {
cy.get(RULES_MANAGEMENT_TABLE).contains(rule['security-rule'].name);
}
};
/**
* Assert that when the rule installation fails, the toast is shown with the right message
* -notifying that the installation failed- and subsequently check that the rules available for installation
* are still present in the Rule Update table
*/
export const assertInstallationFailure = (rules: Array<typeof SAMPLE_PREBUILT_RULE>) => {
const rulesString = rules.length > 1 ? 'rules' : 'rule';
const toastMessage = `${rules.length} ${rulesString} failed to install.`;
cy.get(TOASTER).should('be.visible').should('have.text', toastMessage);
// Check rules are still available for install
for (const rule of rules) {
cy.get(ADD_ELASTIC_RULES_TABLE).contains(rule['security-rule'].name);
}
};
export const interceptInstallationRequestToFail = (rules: Array<typeof SAMPLE_PREBUILT_RULE>) => {
cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform', {
body: {
@ -101,35 +67,6 @@ export const interceptInstallationRequestToFail = (rules: Array<typeof SAMPLE_PR
}).as('installPrebuiltRules');
};
/**
* Assert that when the rule version upgrade succeeds, the toast is shown with the right message
* -confirming the succesful upgrade- and subsequently check that the rules available for upgrade
* are not present in the Rule Update table anymore
*/
export const assertUpgradeSuccess = (rules: Array<typeof SAMPLE_PREBUILT_RULE>) => {
const rulesString = rules.length > 1 ? 'rules' : 'rule';
const toastMessage = `${rules.length} ${rulesString} updated successfully.`;
cy.get(TOASTER).should('be.visible').should('have.text', toastMessage);
for (const rule of rules) {
cy.get(rule['security-rule'].name).should('not.exist');
}
};
/**
* Assert that when the rule version upgrade fails, the toast is shown with the right message
* -notifying that the upgrade failed- and subsequently check that the rules available for upgrade
* are still present in the Rule Update table
*/
export const assertUpgradeFailure = (rules: Array<typeof SAMPLE_PREBUILT_RULE>) => {
const rulesString = rules.length > 1 ? 'rules' : 'rule';
const toastMessage = `${rules.length} ${rulesString} failed to update.`;
cy.get(TOASTER).should('be.visible').should('have.text', toastMessage);
for (const rule of rules) {
cy.get(RULES_UPDATES_TABLE).contains(rule['security-rule'].name);
}
};
export const interceptUpgradeRequestToFail = (rules: Array<typeof SAMPLE_PREBUILT_RULE>) => {
cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform', {
body: {
@ -142,3 +79,74 @@ export const interceptUpgradeRequestToFail = (rules: Array<typeof SAMPLE_PREBUIL
delay: 500, // Add delay to give Cypress time to find the loading spinner
}).as('updatePrebuiltRules');
};
export const assertRuleInstallationSuccessToastShown = (
rules: Array<typeof SAMPLE_PREBUILT_RULE>
) => {
const rulesString = rules.length > 1 ? 'rules' : 'rule';
cy.get(TOASTER)
.should('be.visible')
.should('have.text', `${rules.length} ${rulesString} installed successfully.`);
};
export const assertRuleInstallationFailureToastShown = (
rules: Array<typeof SAMPLE_PREBUILT_RULE>
) => {
const rulesString = rules.length > 1 ? 'rules' : 'rule';
cy.get(TOASTER)
.should('be.visible')
.should('have.text', `${rules.length} ${rulesString} failed to install.`);
};
export const assertRuleUpgradeSuccessToastShown = (rules: Array<typeof SAMPLE_PREBUILT_RULE>) => {
const rulesString = rules.length > 1 ? 'rules' : 'rule';
cy.get(TOASTER)
.should('be.visible')
.should('have.text', `${rules.length} ${rulesString} updated successfully.`);
};
export const assertRuleUpgradeFailureToastShown = (rules: Array<typeof SAMPLE_PREBUILT_RULE>) => {
const rulesString = rules.length > 1 ? 'rules' : 'rule';
cy.get(TOASTER)
.should('be.visible')
.should('have.text', `${rules.length} ${rulesString} failed to update.`);
};
export const assertRulesPresentInInstalledRulesTable = (
rules: Array<typeof SAMPLE_PREBUILT_RULE>
) => {
for (const rule of rules) {
cy.get(RULES_MANAGEMENT_TABLE).contains(rule['security-rule'].name);
}
};
export const assertRulesPresentInAddPrebuiltRulesTable = (
rules: Array<typeof SAMPLE_PREBUILT_RULE>
) => {
for (const rule of rules) {
cy.get(ADD_ELASTIC_RULES_TABLE).contains(rule['security-rule'].name);
}
};
export const assertRulesNotPresentInAddPrebuiltRulesTable = (
rules: Array<typeof SAMPLE_PREBUILT_RULE>
) => {
for (const rule of rules) {
cy.get(ADD_ELASTIC_RULES_TABLE).contains(rule['security-rule'].name).should('not.exist');
}
};
export const assertRulesPresentInRuleUpdatesTable = (rules: Array<typeof SAMPLE_PREBUILT_RULE>) => {
for (const rule of rules) {
cy.get(RULES_UPDATES_TABLE).contains(rule['security-rule'].name);
}
};
export const assertRulesNotPresentInRuleUpdatesTable = (
rules: Array<typeof SAMPLE_PREBUILT_RULE>
) => {
cy.url().should('include', RULES_UPDATES);
for (const rule of rules) {
cy.get(rule['security-rule'].name).should('not.exist');
}
};

View file

@ -0,0 +1,368 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { capitalize } from 'lodash';
import type { ThreatMapping } from '@kbn/securitysolution-io-ts-alerting-types';
import type { Module } from '@kbn/ml-plugin/common/types/modules';
import { AlertSuppression } from '@kbn/security-solution-plugin/common/api/detection_engine/model/rule_schema';
import type { Filter } from '@kbn/es-query';
import type { PrebuiltRuleAsset } from '@kbn/security-solution-plugin/server/lib/detection_engine/prebuilt_rules';
import {
ALERT_SUPPRESSION_DURATION_TITLE,
ALERT_SUPPRESSION_DURATION_VALUE,
ALERT_SUPPRESSION_FIELD_TITLE,
ALERT_SUPPRESSION_FIELD_VALUE,
ALERT_SUPPRESSION_GROUP_BY_TITLE,
ALERT_SUPPRESSION_GROUP_BY_VALUE_ITEM,
ANOMALY_THRESHOLD_TITLE,
ANOMALY_THRESHOLD_VALUE,
AUTHOR_PROPERTY_TITLE,
AUTHOR_PROPERTY_VALUE_ITEM,
BUILDING_BLOCK_TITLE,
BUILDING_BLOCK_VALUE,
CUSTOM_QUERY_TITLE,
CUSTOM_QUERY_VALUE,
DATA_VIEW_ID_TITLE,
DATA_VIEW_ID_VALUE,
DATA_VIEW_INDEX_PATTERN_TITLE,
DATA_VIEW_INDEX_PATTERN_VALUE,
EQL_QUERY_TITLE,
EQL_QUERY_VALUE,
ESQL_QUERY_TITLE,
ESQL_QUERY_VALUE,
FALSE_POSITIVES_TITLE,
FALSE_POSITIVES_VALUE_ITEM,
FILTERS_TITLE,
FILTERS_VALUE_ITEM,
FLYOUT_CLOSE_BTN,
FROM_TITLE,
FROM_VALUE,
INDEX_TITLE,
INDEX_VALUE_ITEM,
INSTALL_PREBUILT_RULE_PREVIEW,
INTERVAL_TITLE,
INTERVAL_VALUE,
INVESTIGATION_FIELDS_TITLE,
INVESTIGATION_FIELDS_VALUE_ITEM,
LICENSE_TITLE,
LICENSE_VALUE,
MACHINE_LEARNING_JOB_TITLE,
MACHINE_LEARNING_JOB_VALUE,
NEW_TERMS_FIELDS_TITLE,
NEW_TERMS_FIELDS_VALUE_ITEM,
NEW_TERMS_WINDOW_SIZE_TITLE,
NEW_TERMS_WINDOW_SIZE_VALUE,
REFERENCES_TITLE,
REFERENCES_VALUE_ITEM,
RELATED_INTEGRATIONS_TITLE,
RELATED_INTEGRATIONS_VALUE,
REQUIRED_FIELDS_PROPERTY_TITLE,
REQUIRED_FIELDS_PROPERTY_VALUE_ITEM,
RISK_SCORE_MAPPING_TITLE,
RISK_SCORE_MAPPING_VALUE_FIELD_NAME,
RISK_SCORE_MAPPING_VALUE_OVERRIDE_NAME,
RISK_SCORE_TITLE,
RISK_SCORE_VALUE,
RULE_NAME_OVERRIDE_TITLE,
RULE_NAME_OVERRIDE_VALUE,
SAVED_QUERY_CONTENT_TITLE,
SAVED_QUERY_CONTENT_VALUE,
SAVED_QUERY_FILTERS_TITLE,
SAVED_QUERY_FILTERS_VALUE,
SAVED_QUERY_NAME_TITLE,
SAVED_QUERY_NAME_VALUE,
SEVERITY_MAPPING_TITLE,
SEVERITY_MAPPING_VALUE_FIELD,
SEVERITY_MAPPING_VALUE_SEVERITY,
SEVERITY_MAPPING_VALUE_VALUE,
SEVERITY_TITLE,
SEVERITY_VALUE,
TAGS_PROPERTY_TITLE,
TAGS_PROPERTY_VALUE_ITEM,
THREAT_FILTERS_TITLE,
THREAT_FILTERS_VALUE_ITEM,
THREAT_INDEX_TITLE,
THREAT_INDEX_VALUE_ITEM,
THREAT_MAPPING_TITLE,
THREAT_MAPPING_VALUE,
THREAT_QUERY_TITLE,
THREAT_QUERY_VALUE,
THREAT_TACTIC,
THREAT_TITLE,
THRESHOLD_TITLE,
THRESHOLD_VALUE,
TIMELINE_TEMPLATE_TITLE,
TIMELINE_TEMPLATE_VALUE,
TIMESTAMP_OVERRIDE_TITLE,
TIMESTAMP_OVERRIDE_VALUE,
UPDATE_PREBUILT_RULE_PREVIEW,
} from '../screens/alerts_detection_rules';
export const openRuleInstallPreview = (ruleName: string) => {
cy.contains(ruleName).click();
cy.get(INSTALL_PREBUILT_RULE_PREVIEW).should('be.visible');
};
export const openRuleUpdatePreview = (ruleName: string) => {
cy.contains(ruleName).click();
cy.get(UPDATE_PREBUILT_RULE_PREVIEW).should('be.visible');
};
export const closeRulePreview = () => {
cy.get(FLYOUT_CLOSE_BTN).click();
cy.get(INSTALL_PREBUILT_RULE_PREVIEW).should('not.exist');
};
export const assertCommonPropertiesShown = (properties: Partial<PrebuiltRuleAsset>) => {
cy.get(AUTHOR_PROPERTY_TITLE).should('have.text', 'Author');
cy.get(AUTHOR_PROPERTY_VALUE_ITEM).then((items) => {
const authorItems = items.map((index, item) => item.textContent).toArray();
cy.wrap(authorItems).should('deep.equal', properties.author);
});
cy.get(BUILDING_BLOCK_TITLE).should('have.text', 'Building block');
cy.get(BUILDING_BLOCK_VALUE).should(
'have.text',
'All generated alerts will be marked as "building block" alerts'
);
cy.get(SEVERITY_TITLE).should('have.text', 'Severity');
cy.get(SEVERITY_VALUE).should('have.text', capitalize(properties.severity));
cy.get(SEVERITY_MAPPING_TITLE).should('have.text', 'Severity override');
properties.severity_mapping?.forEach((severityMapping, index) => {
cy.get(SEVERITY_MAPPING_VALUE_FIELD).eq(index).should('have.text', `${severityMapping.field}:`);
cy.get(SEVERITY_MAPPING_VALUE_VALUE).eq(index).should('have.text', severityMapping.value);
cy.get(SEVERITY_MAPPING_VALUE_SEVERITY)
.eq(index)
.should('have.text', capitalize(severityMapping.severity));
});
cy.get(RISK_SCORE_TITLE).should('have.text', 'Risk score');
cy.get(RISK_SCORE_VALUE).should('have.text', properties.risk_score);
cy.get(RISK_SCORE_MAPPING_TITLE).should('have.text', 'Risk score override');
properties.risk_score_mapping?.forEach((riskScoreMapping, index) => {
cy.get(RISK_SCORE_MAPPING_VALUE_FIELD_NAME)
.eq(index)
.should('have.text', riskScoreMapping.field);
cy.get(RISK_SCORE_MAPPING_VALUE_OVERRIDE_NAME)
.eq(index)
.should('have.text', 'kibana.alert.risk_score');
});
cy.get(REFERENCES_TITLE).should('have.text', 'Reference URLs');
cy.get(`${REFERENCES_VALUE_ITEM} a`).then((items) => {
const referenceUrls = (items as JQuery<HTMLLinkElement>)
.map((index, item) => item.href)
.toArray();
cy.wrap(referenceUrls).should('deep.equal', properties.references);
});
cy.get(FALSE_POSITIVES_TITLE).should('have.text', 'False positive examples');
cy.get(FALSE_POSITIVES_VALUE_ITEM).then((items) => {
const falsePositives = items.map((index, item) => item.textContent).toArray();
cy.wrap(falsePositives).should('deep.equal', properties.false_positives);
});
cy.get(INVESTIGATION_FIELDS_TITLE).should('have.text', 'Custom highlighted fields');
cy.get(INVESTIGATION_FIELDS_VALUE_ITEM).then((items) => {
const investigationFields = items.map((index, item) => item.textContent).toArray();
cy.wrap(investigationFields).should('deep.equal', properties.investigation_fields?.field_names);
});
cy.get(LICENSE_TITLE).should('have.text', 'License');
cy.get(LICENSE_VALUE).should('have.text', properties.license);
cy.get(RULE_NAME_OVERRIDE_TITLE).should('have.text', 'Rule name override');
cy.get(RULE_NAME_OVERRIDE_VALUE).should('have.text', properties.rule_name_override);
cy.get(THREAT_TITLE).should('have.text', 'MITRE ATT&CK™');
properties.threat?.forEach((threatItem, index) => {
cy.get(THREAT_TACTIC).eq(index).should('contain', threatItem.tactic.id);
});
cy.get(TIMESTAMP_OVERRIDE_TITLE).should('have.text', 'Timestamp override');
cy.get(TIMESTAMP_OVERRIDE_VALUE).should('have.text', properties.timestamp_override);
cy.get(TAGS_PROPERTY_TITLE).should('have.text', 'Tags');
cy.get(TAGS_PROPERTY_VALUE_ITEM).then((items) => {
const tags = items.map((index, item) => item.textContent).toArray();
cy.wrap(tags).should('deep.equal', properties.tags);
});
cy.get(RELATED_INTEGRATIONS_TITLE).should('have.text', 'Related integrations');
properties.related_integrations?.forEach((relatedIntegration, index) => {
cy.get(RELATED_INTEGRATIONS_VALUE)
.eq(index)
.invoke('attr', 'data-test-subj')
.should('contain', relatedIntegration.package);
});
cy.get(REQUIRED_FIELDS_PROPERTY_TITLE).should('have.text', 'Required fields');
properties.required_fields?.forEach((requiredField, index) => {
cy.get(REQUIRED_FIELDS_PROPERTY_VALUE_ITEM).eq(index).should('contain', requiredField.name);
});
cy.get(TIMELINE_TEMPLATE_TITLE).should('have.text', 'Timeline template');
cy.get(TIMELINE_TEMPLATE_VALUE).should('have.text', properties.timeline_title);
cy.get(INTERVAL_TITLE).should('have.text', 'Runs every');
cy.get(INTERVAL_VALUE).should('have.text', properties.interval);
cy.get(FROM_TITLE).should('have.text', 'Additional look-back time');
cy.get(FROM_VALUE).invoke('attr', 'data-test-subj').should('contain', properties.from);
};
export const assertIndexPropertyShown = (index: string[]) => {
cy.get(INDEX_TITLE).should('have.text', 'Index patterns');
cy.get(INDEX_VALUE_ITEM).then((items) => {
const indexPatternItems = items.map((itemIndex, item) => item.textContent).toArray();
cy.wrap(indexPatternItems).should('deep.equal', index);
});
};
export const assertCustomQueryPropertyShown = (query: string) => {
cy.get(CUSTOM_QUERY_TITLE).should('have.text', 'Custom query');
cy.get(CUSTOM_QUERY_VALUE).should('have.text', query);
};
export const assertFiltersPropertyShown = (queryFilters: Filter[]) => {
cy.get(FILTERS_TITLE).should('have.text', 'Filters');
queryFilters.forEach((filter, filterIndex) => {
cy.get(FILTERS_VALUE_ITEM)
.eq(filterIndex)
.invoke('attr', 'data-test-subj')
.should('contain', filter.meta.key);
});
};
export const assertAlertSuppressionPropertiesShown = (alertSuppression: AlertSuppression) => {
cy.get(ALERT_SUPPRESSION_GROUP_BY_TITLE).should('contain', 'Suppress alerts by');
cy.get(ALERT_SUPPRESSION_GROUP_BY_VALUE_ITEM).then((items) => {
const groupByItems = items.map((itemIndex, item) => item.textContent).toArray();
cy.wrap(groupByItems).should('deep.equal', alertSuppression.group_by);
});
const { duration } = alertSuppression as { duration: { value: number; unit: string } };
cy.get(ALERT_SUPPRESSION_DURATION_TITLE).should('contain', 'Suppress alerts for');
cy.get(ALERT_SUPPRESSION_DURATION_VALUE).should('contain', `${duration.value}${duration.unit}`);
cy.get(ALERT_SUPPRESSION_FIELD_TITLE).should('contain', 'If a suppression field is missing');
cy.get(ALERT_SUPPRESSION_FIELD_VALUE).should(
'contain',
'Suppress and group alerts for events with missing fields'
);
};
export const assertDataViewPropertiesShown = (dataViewId: string, dataViewIndexPattern: string) => {
cy.get(DATA_VIEW_ID_TITLE).should('have.text', 'Data view ID');
cy.get(DATA_VIEW_ID_VALUE).should('have.text', dataViewId);
cy.get(DATA_VIEW_INDEX_PATTERN_TITLE).should('have.text', 'Data view index pattern');
cy.get(DATA_VIEW_INDEX_PATTERN_VALUE).should('have.text', dataViewIndexPattern);
};
export const assertSavedQueryPropertiesShown = (query: string, filterKey: string, name: string) => {
cy.get(SAVED_QUERY_CONTENT_TITLE).should('have.text', 'Saved query');
cy.get(SAVED_QUERY_CONTENT_VALUE).should('have.text', query);
cy.get(SAVED_QUERY_FILTERS_TITLE).should('have.text', 'Saved query filters');
cy.get(SAVED_QUERY_FILTERS_VALUE).should('contain', filterKey);
cy.get(SAVED_QUERY_NAME_TITLE).should('have.text', 'Saved query name');
cy.get(SAVED_QUERY_NAME_VALUE).should('have.text', name);
};
export const assertMachineLearningPropertiesShown = (
anomalyThreshold: number,
machineLearningJobIds: string[],
mlModules: Module[]
) => {
const mlJobs = mlModules.map((mlModule: Module) => mlModule.jobs).flat();
const mlJobNameById: Record<string, string> = mlJobs.reduce((nameById, job) => {
return {
...nameById,
[job.id]: job.config?.custom_settings?.security_app_display_name || '',
};
}, {});
cy.get(ANOMALY_THRESHOLD_TITLE).should('have.text', 'Anomaly score threshold');
cy.get(ANOMALY_THRESHOLD_VALUE).should('have.text', anomalyThreshold);
cy.get(MACHINE_LEARNING_JOB_TITLE).should('have.text', 'Machine Learning job');
machineLearningJobIds.forEach((jobId, jobIndex) => {
cy.get(MACHINE_LEARNING_JOB_VALUE).eq(jobIndex).should('contain', mlJobNameById[jobId]);
});
};
export const assertThresholdPropertyShown = (thresholdValue: number) => {
cy.get(THRESHOLD_TITLE).should('have.text', 'Threshold');
cy.get(THRESHOLD_VALUE).should('contain', thresholdValue);
};
export const assertEqlQueryPropertyShown = (query: string) => {
cy.get(EQL_QUERY_TITLE).should('have.text', 'EQL query');
cy.get(EQL_QUERY_VALUE).should('have.text', query);
};
interface ThreatMatchQueryProperties {
threatIndex: string[];
threatMapping: ThreatMapping;
threatFilters: Filter[];
threatQuery: string;
}
export const assertThreatMatchQueryPropertiesShown = ({
threatIndex,
threatMapping,
threatFilters,
threatQuery,
}: ThreatMatchQueryProperties) => {
cy.get(THREAT_INDEX_TITLE).should('have.text', 'Indicator index patterns');
cy.get(THREAT_INDEX_VALUE_ITEM).then((items) => {
const indexItems = items.map((index, item) => item.textContent).toArray();
cy.wrap(indexItems).should('deep.equal', threatIndex);
});
cy.get(THREAT_MAPPING_TITLE).should('have.text', 'Indicator mapping');
threatMapping.forEach((threatMappingItem) => {
threatMappingItem.entries.forEach((entry) => {
cy.get(THREAT_MAPPING_VALUE).should('contain', entry.value);
});
});
cy.get(THREAT_FILTERS_TITLE).should('have.text', 'Indicator filters');
threatFilters.forEach((filter, filterIndex) => {
cy.get(THREAT_FILTERS_VALUE_ITEM)
.eq(filterIndex)
.invoke('attr', 'data-test-subj')
.should('contain', filter.meta.key);
});
cy.get(THREAT_QUERY_TITLE).should('have.text', 'Indicator index query');
cy.get(THREAT_QUERY_VALUE).should('have.text', threatQuery);
};
export const assertNewTermsFieldsPropertyShown = (newTermsFields: string[]) => {
cy.get(NEW_TERMS_FIELDS_TITLE).should('have.text', 'Fields');
cy.get(NEW_TERMS_FIELDS_VALUE_ITEM).then((items) => {
const fieldItems = items.map((index, item) => item.textContent).toArray();
cy.wrap(fieldItems).should('deep.equal', newTermsFields);
});
};
export const assertWindowSizePropertyShown = (historyWindowStart: string) => {
cy.get(NEW_TERMS_WINDOW_SIZE_TITLE).should('have.text', 'History Window Size');
cy.get(NEW_TERMS_WINDOW_SIZE_VALUE)
.invoke('attr', 'data-test-subj')
.should('contain', historyWindowStart);
};
export const assertEsqlQueryPropertyShown = (query: string) => {
cy.get(ESQL_QUERY_TITLE).should('have.text', 'ES|QL query');
cy.get(ESQL_QUERY_VALUE).should('have.text', query);
};

View file

@ -39,5 +39,7 @@
"@kbn/securitysolution-list-constants",
"@kbn/security-plugin",
"@kbn/management-settings-ids",
"@kbn/es-query",
"@kbn/ml-plugin"
]
}