mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[DE][Cypress] Re-enable cypress serverless tests subset (#166501)
## Summary Re-enable a subset of cypress serverless tests: - Restructured `x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule.cy.ts` to break it down into and enable the following tests: - `rule_creation/common_components.cy.ts` - `rule_creation/custom_query_rule.cy.ts` - `rule_details/common_components.cy.ts` - `rule_details/custom_query_rule.cy.ts` - `rule_edit/custom_query_rule.cy.ts`
This commit is contained in:
parent
07f1c36df7
commit
50ddc979ca
13 changed files with 579 additions and 449 deletions
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
|
@ -1230,7 +1230,7 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
|
|||
|
||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules @elastic/security-detection-rule-management
|
||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management @elastic/security-detection-rule-management
|
||||
|
||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_details @elastic/security-detection-rule-management
|
||||
/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules @elastic/security-detection-rule-management
|
||||
/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/rule_management @elastic/security-detection-rule-management
|
||||
|
||||
|
@ -1294,6 +1294,7 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
|
|||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts @elastic/security-detection-engine
|
||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions @elastic/security-detection-engine
|
||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation @elastic/security-detection-engine
|
||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_edit @elastic/security-detection-engine
|
||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/value_lists @elastic/security-detection-engine
|
||||
/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics @elastic/security-detection-engine
|
||||
/x-pack/test/security_solution_cypress/cypress/e2e/exceptions @elastic/security-detection-engine
|
||||
|
|
|
@ -19,6 +19,7 @@ interface MultiSelectAutocompleteProps {
|
|||
field: FieldHook;
|
||||
fullWidth?: boolean;
|
||||
disabledText?: string;
|
||||
dataTestSubj?: string;
|
||||
}
|
||||
|
||||
const FIELD_COMBO_BOX_WIDTH = 410;
|
||||
|
@ -31,6 +32,7 @@ export const MultiSelectAutocompleteComponent: React.FC<MultiSelectAutocompleteP
|
|||
isDisabled,
|
||||
field,
|
||||
fullWidth = false,
|
||||
dataTestSubj,
|
||||
}: MultiSelectAutocompleteProps) => {
|
||||
const fieldEuiFieldProps = useMemo(
|
||||
() => ({
|
||||
|
@ -45,7 +47,12 @@ export const MultiSelectAutocompleteComponent: React.FC<MultiSelectAutocompleteP
|
|||
[browserFields, isDisabled, fullWidth]
|
||||
);
|
||||
const fieldComponent = (
|
||||
<Field field={field} idAria={fieldDescribedByIds} euiFieldProps={fieldEuiFieldProps} />
|
||||
<Field
|
||||
field={field}
|
||||
idAria={fieldDescribedByIds}
|
||||
euiFieldProps={fieldEuiFieldProps}
|
||||
data-test-subj={dataTestSubj}
|
||||
/>
|
||||
);
|
||||
return isDisabled ? (
|
||||
<EuiToolTip position="right" content={disabledText}>
|
||||
|
|
|
@ -249,6 +249,7 @@ const StepAboutRuleComponent: FC<StepAboutRuleProps> = ({
|
|||
browserFields: indexPattern.fields,
|
||||
isDisabled: isLoading || indexPatternLoading,
|
||||
fullWidth: true,
|
||||
dataTestSubj: 'detectionEngineStepAboutRuleInvestigationFields',
|
||||
}}
|
||||
/>
|
||||
<EuiSpacer size="l" />
|
||||
|
|
|
@ -18,6 +18,7 @@ import type {
|
|||
import type {
|
||||
IndexPatternArray,
|
||||
InvestigationGuide,
|
||||
InvestigationFields,
|
||||
RuleDescription,
|
||||
RuleFalsePositiveArray,
|
||||
RuleQuery,
|
||||
|
@ -30,6 +31,7 @@ interface RuleFields {
|
|||
defaultIndexPatterns: IndexPatternArray;
|
||||
falsePositives: RuleFalsePositiveArray;
|
||||
investigationGuide: InvestigationGuide;
|
||||
investigationFields: InvestigationFields;
|
||||
referenceUrls: RuleReferenceArray;
|
||||
riskScore: RiskScore;
|
||||
ruleDescription: RuleDescription;
|
||||
|
@ -58,6 +60,9 @@ export const ruleFields: RuleFields = {
|
|||
],
|
||||
falsePositives: ['False1', 'False2'],
|
||||
investigationGuide: '# test markdown',
|
||||
investigationFields: {
|
||||
field_names: ['agent.hostname'],
|
||||
},
|
||||
referenceUrls: ['http://example.com/', 'https://example.com/'],
|
||||
riskScore: 17,
|
||||
ruleDescription: 'The rule description',
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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 { ruleFields } from '../../../data/detection_engine';
|
||||
import { getTimeline } from '../../../objects/timeline';
|
||||
|
||||
import {
|
||||
ABOUT_CONTINUE_BTN,
|
||||
ABOUT_EDIT_BUTTON,
|
||||
CUSTOM_QUERY_INPUT,
|
||||
DEFINE_CONTINUE_BUTTON,
|
||||
DEFINE_EDIT_BUTTON,
|
||||
RULE_NAME_INPUT,
|
||||
SCHEDULE_CONTINUE_BUTTON,
|
||||
} from '../../../screens/create_new_rule';
|
||||
import { RULE_NAME_HEADER } from '../../../screens/rule_details';
|
||||
import { createTimeline } from '../../../tasks/api_calls/timelines';
|
||||
import { deleteAlertsAndRules } from '../../../tasks/common';
|
||||
import {
|
||||
createAndEnableRuleOnly,
|
||||
expandAdvancedSettings,
|
||||
fillCustomInvestigationFields,
|
||||
fillDescription,
|
||||
fillFalsePositiveExamples,
|
||||
fillFrom,
|
||||
fillNote,
|
||||
fillReferenceUrls,
|
||||
fillRiskScore,
|
||||
fillRuleName,
|
||||
fillRuleTags,
|
||||
fillSeverity,
|
||||
fillThreat,
|
||||
fillThreatSubtechnique,
|
||||
fillThreatTechnique,
|
||||
importSavedQuery,
|
||||
} from '../../../tasks/create_new_rule';
|
||||
import { login } from '../../../tasks/login';
|
||||
import { CREATE_RULE_URL } from '../../../urls/navigation';
|
||||
import { visit } from '../../../tasks/navigation';
|
||||
|
||||
// This test is meant to test touching all the common various components in rule creation
|
||||
// to ensure we don't miss any changes that maybe affect one of these more obscure UI components
|
||||
// in the creation form. For any rule type specific functionalities, please include
|
||||
// them in the relevant /rule_creation/[RULE_TYPE].cy.ts test.
|
||||
describe('Common rule creation flows', { tags: ['@ess', '@serverless'] }, () => {
|
||||
beforeEach(() => {
|
||||
deleteAlertsAndRules();
|
||||
createTimeline(getTimeline())
|
||||
.then((response) => {
|
||||
return response.body.data.persistTimeline.timeline.savedObjectId;
|
||||
})
|
||||
.as('timelineId');
|
||||
login();
|
||||
visit(CREATE_RULE_URL);
|
||||
});
|
||||
|
||||
it('Creates and enables a rule', function () {
|
||||
cy.log('Filling define section');
|
||||
importSavedQuery(this.timelineId);
|
||||
cy.get(DEFINE_CONTINUE_BUTTON).click();
|
||||
|
||||
cy.log('Filling about section');
|
||||
fillRuleName();
|
||||
fillDescription();
|
||||
fillSeverity();
|
||||
fillRiskScore();
|
||||
fillRuleTags();
|
||||
expandAdvancedSettings();
|
||||
fillReferenceUrls();
|
||||
fillFalsePositiveExamples();
|
||||
fillThreat();
|
||||
fillThreatTechnique();
|
||||
fillThreatSubtechnique();
|
||||
fillCustomInvestigationFields();
|
||||
fillNote();
|
||||
cy.get(ABOUT_CONTINUE_BTN).click();
|
||||
|
||||
cy.log('Filling schedule section');
|
||||
fillFrom();
|
||||
|
||||
// expect define step to repopulate
|
||||
cy.get(DEFINE_EDIT_BUTTON).click();
|
||||
cy.get(CUSTOM_QUERY_INPUT).should('have.value', ruleFields.ruleQuery);
|
||||
cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click();
|
||||
|
||||
// expect about step to populate
|
||||
cy.get(ABOUT_EDIT_BUTTON).click();
|
||||
cy.get(RULE_NAME_INPUT).invoke('val').should('eql', ruleFields.ruleName);
|
||||
cy.get(ABOUT_CONTINUE_BTN).should('exist').click();
|
||||
cy.get(SCHEDULE_CONTINUE_BUTTON).click();
|
||||
|
||||
createAndEnableRuleOnly();
|
||||
|
||||
cy.get(RULE_NAME_HEADER).should('contain', ruleFields.ruleName);
|
||||
});
|
||||
});
|
|
@ -5,464 +5,42 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ruleFields } from '../../../data/detection_engine';
|
||||
import {
|
||||
getNewRule,
|
||||
getExistingRule,
|
||||
getEditedRule,
|
||||
getNewOverrideRule,
|
||||
} from '../../../objects/rule';
|
||||
import { getTimeline } from '../../../objects/timeline';
|
||||
import { ALERTS_COUNT, ALERT_GRID_CELL } from '../../../screens/alerts';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
import { RULE_NAME_HEADER } from '../../../screens/rule_details';
|
||||
|
||||
import { deleteAlertsAndRules } from '../../../tasks/common';
|
||||
import {
|
||||
CUSTOM_RULES_BTN,
|
||||
RISK_SCORE,
|
||||
RULE_NAME,
|
||||
RULES_MANAGEMENT_TABLE,
|
||||
RULE_SWITCH,
|
||||
SEVERITY,
|
||||
} from '../../../screens/alerts_detection_rules';
|
||||
import {
|
||||
ACTIONS_NOTIFY_WHEN_BUTTON,
|
||||
ACTIONS_SUMMARY_BUTTON,
|
||||
} from '../../../screens/common/rule_actions';
|
||||
import {
|
||||
ABOUT_CONTINUE_BTN,
|
||||
ABOUT_EDIT_BUTTON,
|
||||
CUSTOM_QUERY_INPUT,
|
||||
DEFINE_CONTINUE_BUTTON,
|
||||
DEFINE_EDIT_BUTTON,
|
||||
DEFINE_INDEX_INPUT,
|
||||
DEFAULT_RISK_SCORE_INPUT,
|
||||
RULE_DESCRIPTION_INPUT,
|
||||
RULE_NAME_INPUT,
|
||||
SCHEDULE_INTERVAL_AMOUNT_INPUT,
|
||||
SCHEDULE_INTERVAL_UNITS_INPUT,
|
||||
SCHEDULE_CONTINUE_BUTTON,
|
||||
SEVERITY_DROPDOWN,
|
||||
TAGS_CLEAR_BUTTON,
|
||||
TAGS_FIELD,
|
||||
} from '../../../screens/create_new_rule';
|
||||
import {
|
||||
ADDITIONAL_LOOK_BACK_DETAILS,
|
||||
ABOUT_DETAILS,
|
||||
ABOUT_INVESTIGATION_NOTES,
|
||||
ABOUT_RULE_DESCRIPTION,
|
||||
CUSTOM_QUERY_DETAILS,
|
||||
DEFINITION_DETAILS,
|
||||
FALSE_POSITIVES_DETAILS,
|
||||
removeExternalLinkText,
|
||||
INDEX_PATTERNS_DETAILS,
|
||||
INVESTIGATION_NOTES_MARKDOWN,
|
||||
INVESTIGATION_NOTES_TOGGLE,
|
||||
REFERENCE_URLS_DETAILS,
|
||||
RISK_SCORE_DETAILS,
|
||||
RULE_NAME_HEADER,
|
||||
RULE_TYPE_DETAILS,
|
||||
RUNS_EVERY_DETAILS,
|
||||
SCHEDULE_DETAILS,
|
||||
SEVERITY_DETAILS,
|
||||
TAGS_DETAILS,
|
||||
TIMELINE_TEMPLATE_DETAILS,
|
||||
THREAT_TACTIC,
|
||||
THREAT_TECHNIQUE,
|
||||
THREAT_SUBTECHNIQUE,
|
||||
} from '../../../screens/rule_details';
|
||||
|
||||
import {
|
||||
deleteFirstRule,
|
||||
deleteRuleFromDetailsPage,
|
||||
expectManagementTableRules,
|
||||
getRulesManagementTableRows,
|
||||
goToRuleDetailsOf,
|
||||
selectRulesByName,
|
||||
} from '../../../tasks/alerts_detection_rules';
|
||||
import { deleteSelectedRules } from '../../../tasks/rules_bulk_actions';
|
||||
import { createRule, findAllRules } from '../../../tasks/api_calls/rules';
|
||||
import { createTimeline } from '../../../tasks/api_calls/timelines';
|
||||
import { deleteAlertsAndRules, deleteConnectors } from '../../../tasks/common';
|
||||
import { addEmailConnectorAndRuleAction } from '../../../tasks/common/rule_actions';
|
||||
import {
|
||||
createAndEnableRule,
|
||||
expandAdvancedSettings,
|
||||
fillAboutRule,
|
||||
fillDescription,
|
||||
fillFalsePositiveExamples,
|
||||
fillFrom,
|
||||
fillNote,
|
||||
fillReferenceUrls,
|
||||
fillRiskScore,
|
||||
fillRuleName,
|
||||
fillRuleTags,
|
||||
fillSeverity,
|
||||
fillThreat,
|
||||
fillThreatSubtechnique,
|
||||
fillThreatTechnique,
|
||||
goToAboutStepTab,
|
||||
goToActionsStepTab,
|
||||
goToScheduleStepTab,
|
||||
importSavedQuery,
|
||||
waitForAlertsToPopulate,
|
||||
createAndEnableRuleOnly,
|
||||
fillScheduleRuleAndContinue,
|
||||
fillAboutRuleMinimumAndContinue,
|
||||
fillDefineCustomRuleAndContinue,
|
||||
} from '../../../tasks/create_new_rule';
|
||||
import { saveEditedRule, visitEditRulePage } from '../../../tasks/edit_rule';
|
||||
import { login } from '../../../tasks/login';
|
||||
import { visitWithTimeRange } from '../../../tasks/navigation';
|
||||
import {
|
||||
enablesRule,
|
||||
getDetails,
|
||||
visitRuleDetailsPage,
|
||||
waitForTheRuleToBeExecuted,
|
||||
} from '../../../tasks/rule_details';
|
||||
import { visit } from '../../../tasks/navigation';
|
||||
import { CREATE_RULE_URL } from '../../../urls/navigation';
|
||||
import { visitRulesManagementTable } from '../../../tasks/rules_management';
|
||||
|
||||
// TODO: https://github.com/elastic/kibana/issues/161539
|
||||
describe('Custom query rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
|
||||
describe('Create custom query rule', { tags: ['@ess', '@serverless'] }, () => {
|
||||
const rule = getNewRule();
|
||||
|
||||
beforeEach(() => {
|
||||
deleteAlertsAndRules();
|
||||
});
|
||||
|
||||
describe('Custom detection rules creation', () => {
|
||||
beforeEach(() => {
|
||||
createTimeline(getTimeline())
|
||||
.then((response) => {
|
||||
return response.body.data.persistTimeline.timeline.savedObjectId;
|
||||
})
|
||||
.as('timelineId');
|
||||
deleteAlertsAndRules();
|
||||
login();
|
||||
visit(CREATE_RULE_URL);
|
||||
});
|
||||
|
||||
it('Creates and enables a new rule', function () {
|
||||
visitWithTimeRange(CREATE_RULE_URL);
|
||||
|
||||
cy.log('Filling define section');
|
||||
importSavedQuery(this.timelineId);
|
||||
cy.get(DEFINE_CONTINUE_BUTTON).click();
|
||||
|
||||
cy.log('Filling about section');
|
||||
fillRuleName('Test Rule');
|
||||
fillDescription();
|
||||
fillSeverity();
|
||||
fillRiskScore();
|
||||
fillRuleTags();
|
||||
expandAdvancedSettings();
|
||||
fillReferenceUrls();
|
||||
fillFalsePositiveExamples();
|
||||
fillThreat();
|
||||
fillThreatTechnique();
|
||||
fillThreatSubtechnique();
|
||||
fillNote();
|
||||
cy.get(ABOUT_CONTINUE_BTN).click();
|
||||
|
||||
cy.log('Filling schedule section');
|
||||
fillFrom();
|
||||
|
||||
// expect define step to repopulate
|
||||
cy.get(DEFINE_EDIT_BUTTON).click();
|
||||
cy.get(CUSTOM_QUERY_INPUT).should('have.value', ruleFields.ruleQuery);
|
||||
cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click();
|
||||
|
||||
// expect about step to populate
|
||||
cy.get(ABOUT_EDIT_BUTTON).click();
|
||||
cy.get(RULE_NAME_INPUT).invoke('val').should('eql', ruleFields.ruleName);
|
||||
cy.get(ABOUT_CONTINUE_BTN).should('exist').click();
|
||||
cy.get(SCHEDULE_CONTINUE_BUTTON).click();
|
||||
|
||||
createAndEnableRule();
|
||||
it('Creates and enables a rule', function () {
|
||||
fillDefineCustomRuleAndContinue(rule);
|
||||
fillAboutRuleMinimumAndContinue(rule);
|
||||
fillScheduleRuleAndContinue(rule);
|
||||
createAndEnableRuleOnly();
|
||||
|
||||
cy.log('Asserting we have a new rule created');
|
||||
cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)');
|
||||
|
||||
cy.log('Asserting rule view in rules list');
|
||||
expectManagementTableRules(['Test Rule']);
|
||||
cy.get(RULE_NAME).should('have.text', ruleFields.ruleName);
|
||||
cy.get(RISK_SCORE).should('have.text', ruleFields.riskScore);
|
||||
cy.get(SEVERITY)
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
cy.wrap(text.toLowerCase()).should('equal', ruleFields.ruleSeverity);
|
||||
});
|
||||
cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true');
|
||||
|
||||
goToRuleDetailsOf(ruleFields.ruleName);
|
||||
|
||||
cy.log('Asserting rule details');
|
||||
cy.get(RULE_NAME_HEADER).should('contain', ruleFields.ruleName);
|
||||
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', ruleFields.ruleDescription);
|
||||
cy.get(ABOUT_DETAILS).within(() => {
|
||||
getDetails(SEVERITY_DETAILS)
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
cy.wrap(text.toLowerCase()).should('equal', ruleFields.ruleSeverity);
|
||||
});
|
||||
getDetails(RISK_SCORE_DETAILS).should('have.text', ruleFields.riskScore);
|
||||
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
|
||||
expect(removeExternalLinkText(details.text())).equal(ruleFields.referenceUrls.join(''));
|
||||
});
|
||||
getDetails(FALSE_POSITIVES_DETAILS).should('have.text', ruleFields.falsePositives.join(''));
|
||||
getDetails(TAGS_DETAILS).should('have.text', ruleFields.ruleTags.join(''));
|
||||
});
|
||||
cy.get(THREAT_TACTIC).should(
|
||||
'contain',
|
||||
`${ruleFields.threat.tactic.name} (${ruleFields.threat.tactic.id})`
|
||||
);
|
||||
cy.get(THREAT_TECHNIQUE).should(
|
||||
'contain',
|
||||
`${ruleFields.threatTechnique.name} (${ruleFields.threatTechnique.id})`
|
||||
);
|
||||
cy.get(THREAT_SUBTECHNIQUE).should(
|
||||
'contain',
|
||||
`${ruleFields.threatSubtechnique.name} (${ruleFields.threatSubtechnique.id})`
|
||||
);
|
||||
cy.get(INVESTIGATION_NOTES_TOGGLE).click();
|
||||
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN);
|
||||
cy.get(DEFINITION_DETAILS).within(() => {
|
||||
getDetails(INDEX_PATTERNS_DETAILS).should('have.text', 'auditbeat-*');
|
||||
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', ruleFields.ruleQuery);
|
||||
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
|
||||
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
|
||||
});
|
||||
cy.get(SCHEDULE_DETAILS).within(() => {
|
||||
getDetails(RUNS_EVERY_DETAILS).should('have.text', ruleFields.ruleInterval);
|
||||
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', ruleFields.ruleIntervalFrom);
|
||||
});
|
||||
|
||||
waitForTheRuleToBeExecuted();
|
||||
waitForAlertsToPopulate();
|
||||
|
||||
cy.log('Asserting that alerts have been generated after the creation');
|
||||
cy.get(ALERTS_COUNT)
|
||||
.invoke('text')
|
||||
.should('match', /^[1-9].+$/); // Any number of alerts
|
||||
cy.get(ALERT_GRID_CELL).contains(ruleFields.ruleName);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Custom detection rules deletion and edition', () => {
|
||||
context('Deletion', () => {
|
||||
const TESTED_RULE_DATA = getNewRule({
|
||||
rule_id: 'rule1',
|
||||
name: 'New Rule Test',
|
||||
enabled: false,
|
||||
max_signals: 500,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
createRule(TESTED_RULE_DATA);
|
||||
createRule(
|
||||
getNewOverrideRule({
|
||||
rule_id: 'rule2',
|
||||
name: 'Override Rule',
|
||||
enabled: false,
|
||||
max_signals: 500,
|
||||
})
|
||||
);
|
||||
createRule(getExistingRule({ rule_id: 'rule3', name: 'Rule 1', enabled: false }));
|
||||
login();
|
||||
visitRulesManagementTable();
|
||||
});
|
||||
|
||||
it('Deletes one rule', () => {
|
||||
getRulesManagementTableRows().then((rules) => {
|
||||
const initialNumberOfRules = rules.length;
|
||||
const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1;
|
||||
|
||||
findAllRules().then(({ body }) => {
|
||||
const numberOfRules = body.data.length;
|
||||
expect(numberOfRules).to.eql(initialNumberOfRules);
|
||||
});
|
||||
|
||||
deleteFirstRule();
|
||||
|
||||
getRulesManagementTableRows().should('have.length', expectedNumberOfRulesAfterDeletion);
|
||||
findAllRules().then(({ body }) => {
|
||||
const numberOfRules = body.data.length;
|
||||
expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion);
|
||||
});
|
||||
cy.get(CUSTOM_RULES_BTN).should(
|
||||
'have.text',
|
||||
`Custom rules (${expectedNumberOfRulesAfterDeletion})`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Deletes more than one rule', () => {
|
||||
getRulesManagementTableRows().then((rules) => {
|
||||
const rulesToDelete = [TESTED_RULE_DATA.name, 'Override Rule'] as const;
|
||||
const initialNumberOfRules = rules.length;
|
||||
const numberOfRulesToBeDeleted = 2;
|
||||
const expectedNumberOfRulesAfterDeletion =
|
||||
initialNumberOfRules - numberOfRulesToBeDeleted;
|
||||
|
||||
selectRulesByName(rulesToDelete);
|
||||
deleteSelectedRules();
|
||||
|
||||
getRulesManagementTableRows()
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(RULE_SWITCH).should('not.exist');
|
||||
});
|
||||
|
||||
getRulesManagementTableRows().should('have.length', expectedNumberOfRulesAfterDeletion);
|
||||
findAllRules().then(({ body }) => {
|
||||
const numberOfRules = body.data.length;
|
||||
expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion);
|
||||
});
|
||||
getRulesManagementTableRows()
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(RULE_SWITCH).should('exist');
|
||||
});
|
||||
cy.get(CUSTOM_RULES_BTN).should(
|
||||
'have.text',
|
||||
`Custom rules (${expectedNumberOfRulesAfterDeletion})`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Deletes one rule from detail page', () => {
|
||||
getRulesManagementTableRows().then((rules) => {
|
||||
const initialNumberOfRules = rules.length;
|
||||
const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1;
|
||||
|
||||
goToRuleDetailsOf(TESTED_RULE_DATA.name);
|
||||
cy.intercept('POST', '/api/detection_engine/rules/_bulk_delete').as('deleteRule');
|
||||
|
||||
deleteRuleFromDetailsPage();
|
||||
|
||||
// @ts-expect-error update types
|
||||
cy.waitFor('@deleteRule').then(() => {
|
||||
cy.get(RULES_MANAGEMENT_TABLE).should('exist');
|
||||
getRulesManagementTableRows().should('have.length', expectedNumberOfRulesAfterDeletion);
|
||||
findAllRules().then(({ body }) => {
|
||||
const numberOfRules = body.data.length;
|
||||
expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion);
|
||||
});
|
||||
cy.get(CUSTOM_RULES_BTN).should(
|
||||
'have.text',
|
||||
`Custom rules (${expectedNumberOfRulesAfterDeletion})`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Edition', () => {
|
||||
const editedRuleData = getEditedRule();
|
||||
const expectedEditedTags = editedRuleData.tags?.join('');
|
||||
const expectedEditedIndexPatterns = editedRuleData.index;
|
||||
|
||||
describe('on rule details page', () => {
|
||||
beforeEach(() => {
|
||||
deleteConnectors();
|
||||
login();
|
||||
createRule(getExistingRule({ rule_id: 'rule1', enabled: true })).then((rule) =>
|
||||
visitRuleDetailsPage(rule.body.id)
|
||||
);
|
||||
});
|
||||
|
||||
it('Only modifies rule active status on enable/disable', () => {
|
||||
enablesRule();
|
||||
|
||||
cy.intercept('GET', `/api/detection_engine/rules?id=*`).as('fetchRuleDetails');
|
||||
|
||||
cy.wait('@fetchRuleDetails').then(({ response }) => {
|
||||
cy.wrap(response?.statusCode).should('eql', 200);
|
||||
|
||||
cy.wrap(response?.body.max_signals).should('eql', getExistingRule().max_signals);
|
||||
cy.wrap(response?.body.enabled).should('eql', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('on rule editing page', () => {
|
||||
beforeEach(() => {
|
||||
deleteConnectors();
|
||||
login();
|
||||
createRule(getExistingRule({ rule_id: 'rule1', enabled: true })).then((rule) =>
|
||||
visitEditRulePage(rule.body.id)
|
||||
);
|
||||
});
|
||||
|
||||
it('Allows a rule to be edited', () => {
|
||||
const existingRule = getExistingRule();
|
||||
|
||||
// expect define step to populate
|
||||
cy.get(CUSTOM_QUERY_INPUT).should('have.value', existingRule.query);
|
||||
|
||||
cy.get(DEFINE_INDEX_INPUT).should('have.text', existingRule.index?.join(''));
|
||||
|
||||
goToAboutStepTab();
|
||||
|
||||
// expect about step to populate
|
||||
cy.get(RULE_NAME_INPUT).invoke('val').should('eql', existingRule.name);
|
||||
cy.get(RULE_DESCRIPTION_INPUT).should('have.text', existingRule.description);
|
||||
cy.get(TAGS_FIELD).should('have.text', existingRule.tags?.join(''));
|
||||
cy.get(SEVERITY_DROPDOWN).should('have.text', 'High');
|
||||
cy.get(DEFAULT_RISK_SCORE_INPUT)
|
||||
.invoke('val')
|
||||
.should('eql', `${existingRule.risk_score}`);
|
||||
|
||||
goToScheduleStepTab();
|
||||
|
||||
// expect schedule step to populate
|
||||
const interval = existingRule.interval;
|
||||
const intervalParts = interval != null && interval.match(/[0-9]+|[a-zA-Z]+/g);
|
||||
if (intervalParts) {
|
||||
const [amount, unit] = intervalParts;
|
||||
cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', amount);
|
||||
cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', unit);
|
||||
} else {
|
||||
throw new Error('Cannot assert scheduling info on a rule without an interval');
|
||||
}
|
||||
|
||||
goToActionsStepTab();
|
||||
|
||||
addEmailConnectorAndRuleAction('test@example.com', 'Subject');
|
||||
|
||||
cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'Summary of alerts');
|
||||
cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Per rule run');
|
||||
|
||||
goToAboutStepTab();
|
||||
cy.get(TAGS_CLEAR_BUTTON).click();
|
||||
fillAboutRule(getEditedRule());
|
||||
|
||||
cy.intercept('GET', '/api/detection_engine/rules?id*').as('getRule');
|
||||
|
||||
saveEditedRule();
|
||||
|
||||
cy.wait('@getRule').then(({ response }) => {
|
||||
cy.wrap(response?.statusCode).should('eql', 200);
|
||||
// ensure that editing rule does not modify max_signals
|
||||
cy.wrap(response?.body.max_signals).should('eql', existingRule.max_signals);
|
||||
});
|
||||
|
||||
cy.get(RULE_NAME_HEADER).should('contain', `${getEditedRule().name}`);
|
||||
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getEditedRule().description);
|
||||
cy.get(ABOUT_DETAILS).within(() => {
|
||||
getDetails(SEVERITY_DETAILS).should('have.text', 'Medium');
|
||||
getDetails(RISK_SCORE_DETAILS).should('have.text', `${getEditedRule().risk_score}`);
|
||||
getDetails(TAGS_DETAILS).should('have.text', expectedEditedTags);
|
||||
});
|
||||
cy.get(INVESTIGATION_NOTES_TOGGLE).click();
|
||||
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', getEditedRule().note);
|
||||
cy.get(DEFINITION_DETAILS).within(() => {
|
||||
getDetails(INDEX_PATTERNS_DETAILS).should(
|
||||
'have.text',
|
||||
expectedEditedIndexPatterns?.join('')
|
||||
);
|
||||
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', getEditedRule().query);
|
||||
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
|
||||
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
|
||||
});
|
||||
if (getEditedRule().interval) {
|
||||
cy.get(SCHEDULE_DETAILS).within(() => {
|
||||
getDetails(RUNS_EVERY_DETAILS).should('have.text', getEditedRule().interval);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
cy.get(RULE_NAME_HEADER).should('contain', rule.name);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* 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 { deleteRuleFromDetailsPage } from '../../../tasks/alerts_detection_rules';
|
||||
import {
|
||||
CUSTOM_RULES_BTN,
|
||||
RULES_MANAGEMENT_TABLE,
|
||||
RULES_ROW,
|
||||
} from '../../../screens/alerts_detection_rules';
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { getDetails } from '../../../tasks/rule_details';
|
||||
import { ruleFields } from '../../../data/detection_engine';
|
||||
import { getTimeline } from '../../../objects/timeline';
|
||||
import { getExistingRule, getNewRule } from '../../../objects/rule';
|
||||
|
||||
import {
|
||||
ABOUT_DETAILS,
|
||||
ABOUT_INVESTIGATION_NOTES,
|
||||
ABOUT_RULE_DESCRIPTION,
|
||||
ADDITIONAL_LOOK_BACK_DETAILS,
|
||||
CUSTOM_QUERY_DETAILS,
|
||||
DEFINITION_DETAILS,
|
||||
FALSE_POSITIVES_DETAILS,
|
||||
INDEX_PATTERNS_DETAILS,
|
||||
INVESTIGATION_NOTES_MARKDOWN,
|
||||
INVESTIGATION_NOTES_TOGGLE,
|
||||
REFERENCE_URLS_DETAILS,
|
||||
removeExternalLinkText,
|
||||
RISK_SCORE_DETAILS,
|
||||
RULE_NAME_HEADER,
|
||||
RULE_SWITCH,
|
||||
RULE_TYPE_DETAILS,
|
||||
RUNS_EVERY_DETAILS,
|
||||
SCHEDULE_DETAILS,
|
||||
SEVERITY_DETAILS,
|
||||
TAGS_DETAILS,
|
||||
THREAT_SUBTECHNIQUE,
|
||||
THREAT_TACTIC,
|
||||
THREAT_TECHNIQUE,
|
||||
TIMELINE_TEMPLATE_DETAILS,
|
||||
} from '../../../screens/rule_details';
|
||||
|
||||
import { createTimeline } from '../../../tasks/api_calls/timelines';
|
||||
import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../../tasks/common';
|
||||
import { login } from '../../../tasks/login';
|
||||
import { visit } from '../../../tasks/navigation';
|
||||
import { ruleDetailsUrl } from '../../../urls/rule_details';
|
||||
|
||||
// This test is meant to test all common aspects of the rule details page that should function
|
||||
// the same regardless of rule type. For any rule type specific functionalities, please include
|
||||
// them in the relevant /rule_details/[RULE_TYPE].cy.ts test.
|
||||
describe('Common rule detail flows', { tags: ['@ess', '@serverless'] }, () => {
|
||||
before(() => {
|
||||
cleanKibana();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
deleteAlertsAndRules();
|
||||
deleteConnectors();
|
||||
login();
|
||||
createTimeline(getTimeline()).then((response) => {
|
||||
createRule({
|
||||
...getNewRule({
|
||||
rule_id: 'rulez',
|
||||
description: ruleFields.ruleDescription,
|
||||
name: ruleFields.ruleName,
|
||||
severity: ruleFields.ruleSeverity,
|
||||
risk_score: ruleFields.riskScore,
|
||||
tags: ruleFields.ruleTags,
|
||||
false_positives: ruleFields.falsePositives,
|
||||
note: ruleFields.investigationGuide,
|
||||
timeline_id: response.body.data.persistTimeline.timeline.savedObjectId,
|
||||
timeline_title: response.body.data.persistTimeline.timeline.title ?? '',
|
||||
interval: ruleFields.ruleInterval,
|
||||
from: `now-1h`,
|
||||
query: ruleFields.ruleQuery,
|
||||
enabled: false,
|
||||
max_signals: 500,
|
||||
threat: [
|
||||
{
|
||||
...ruleFields.threat,
|
||||
technique: [
|
||||
{
|
||||
...ruleFields.threatTechnique,
|
||||
subtechnique: [ruleFields.threatSubtechnique],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
}).then((rule) => {
|
||||
visit(ruleDetailsUrl(rule.body.id));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Only modifies rule active status on enable/disable', () => {
|
||||
cy.get(RULE_NAME_HEADER).should('contain', ruleFields.ruleName);
|
||||
|
||||
cy.intercept('POST', '/api/detection_engine/rules/_bulk_action?dry_run=false').as(
|
||||
'bulk_action'
|
||||
);
|
||||
cy.get(RULE_SWITCH).should('be.visible');
|
||||
cy.get(RULE_SWITCH).click();
|
||||
cy.wait('@bulk_action').then(({ response }) => {
|
||||
cy.wrap(response?.statusCode).should('eql', 200);
|
||||
cy.wrap(response?.body.attributes.results.updated[0].max_signals).should(
|
||||
'eql',
|
||||
getExistingRule().max_signals
|
||||
);
|
||||
cy.wrap(response?.body.attributes.results.updated[0].enabled).should('eql', true);
|
||||
});
|
||||
});
|
||||
|
||||
it('Displays rule details', function () {
|
||||
cy.get(RULE_NAME_HEADER).should('contain', ruleFields.ruleName);
|
||||
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', ruleFields.ruleDescription);
|
||||
cy.get(ABOUT_DETAILS).within(() => {
|
||||
getDetails(SEVERITY_DETAILS)
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
cy.wrap(text.toLowerCase()).should('equal', ruleFields.ruleSeverity);
|
||||
});
|
||||
getDetails(RISK_SCORE_DETAILS).should('have.text', ruleFields.riskScore);
|
||||
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
|
||||
expect(removeExternalLinkText(details.text())).equal(ruleFields.referenceUrls.join(''));
|
||||
});
|
||||
getDetails(FALSE_POSITIVES_DETAILS).should('have.text', ruleFields.falsePositives.join(''));
|
||||
getDetails(TAGS_DETAILS).should('have.text', ruleFields.ruleTags.join(''));
|
||||
});
|
||||
cy.get(THREAT_TACTIC).should(
|
||||
'contain',
|
||||
`${ruleFields.threat.tactic.name} (${ruleFields.threat.tactic.id})`
|
||||
);
|
||||
cy.get(THREAT_TECHNIQUE).should(
|
||||
'contain',
|
||||
`${ruleFields.threatTechnique.name} (${ruleFields.threatTechnique.id})`
|
||||
);
|
||||
cy.get(THREAT_SUBTECHNIQUE).should(
|
||||
'contain',
|
||||
`${ruleFields.threatSubtechnique.name} (${ruleFields.threatSubtechnique.id})`
|
||||
);
|
||||
cy.get(INVESTIGATION_NOTES_TOGGLE).click();
|
||||
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN);
|
||||
cy.get(DEFINITION_DETAILS).within(() => {
|
||||
getDetails(INDEX_PATTERNS_DETAILS).should(
|
||||
'have.text',
|
||||
ruleFields.defaultIndexPatterns.join('')
|
||||
);
|
||||
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', ruleFields.ruleQuery);
|
||||
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
|
||||
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'Security Timeline');
|
||||
});
|
||||
cy.get(SCHEDULE_DETAILS).within(() => {
|
||||
getDetails(RUNS_EVERY_DETAILS).should('have.text', ruleFields.ruleInterval);
|
||||
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', '55m');
|
||||
});
|
||||
});
|
||||
|
||||
it('Deletes one rule from detail page', () => {
|
||||
cy.intercept('POST', '/api/detection_engine/rules/_bulk_delete').as('deleteRule');
|
||||
|
||||
deleteRuleFromDetailsPage();
|
||||
|
||||
// @ts-expect-error update types
|
||||
cy.waitFor('@deleteRule').then(() => {
|
||||
cy.get(RULES_MANAGEMENT_TABLE).should('exist');
|
||||
cy.get(RULES_MANAGEMENT_TABLE).find(RULES_ROW).should('have.length', 0);
|
||||
cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => {
|
||||
const numberOfRules = body.data.length;
|
||||
expect(numberOfRules).to.eql(0);
|
||||
});
|
||||
cy.get(CUSTOM_RULES_BTN).should('have.text', `Custom rules (${0})`);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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 { getExistingRule, getEditedRule } from '../../../objects/rule';
|
||||
|
||||
import {
|
||||
ACTIONS_NOTIFY_WHEN_BUTTON,
|
||||
ACTIONS_SUMMARY_BUTTON,
|
||||
} from '../../../screens/common/rule_actions';
|
||||
import {
|
||||
CUSTOM_QUERY_INPUT,
|
||||
DEFINE_INDEX_INPUT,
|
||||
DEFAULT_RISK_SCORE_INPUT,
|
||||
RULE_DESCRIPTION_INPUT,
|
||||
RULE_NAME_INPUT,
|
||||
SCHEDULE_INTERVAL_AMOUNT_INPUT,
|
||||
SCHEDULE_INTERVAL_UNITS_INPUT,
|
||||
SEVERITY_DROPDOWN,
|
||||
TAGS_CLEAR_BUTTON,
|
||||
TAGS_FIELD,
|
||||
} from '../../../screens/create_new_rule';
|
||||
import {
|
||||
ABOUT_DETAILS,
|
||||
ABOUT_INVESTIGATION_NOTES,
|
||||
ABOUT_RULE_DESCRIPTION,
|
||||
CUSTOM_QUERY_DETAILS,
|
||||
DEFINITION_DETAILS,
|
||||
INDEX_PATTERNS_DETAILS,
|
||||
INVESTIGATION_NOTES_TOGGLE,
|
||||
RISK_SCORE_DETAILS,
|
||||
RULE_NAME_HEADER,
|
||||
RULE_TYPE_DETAILS,
|
||||
RUNS_EVERY_DETAILS,
|
||||
SCHEDULE_DETAILS,
|
||||
SEVERITY_DETAILS,
|
||||
TAGS_DETAILS,
|
||||
TIMELINE_TEMPLATE_DETAILS,
|
||||
} from '../../../screens/rule_details';
|
||||
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { deleteAlertsAndRules, deleteConnectors } from '../../../tasks/common';
|
||||
import { addEmailConnectorAndRuleAction } from '../../../tasks/common/rule_actions';
|
||||
import {
|
||||
fillAboutRule,
|
||||
goToAboutStepTab,
|
||||
goToActionsStepTab,
|
||||
goToScheduleStepTab,
|
||||
} from '../../../tasks/create_new_rule';
|
||||
import { saveEditedRule, visitEditRulePage } from '../../../tasks/edit_rule';
|
||||
import { login } from '../../../tasks/login';
|
||||
import { getDetails } from '../../../tasks/rule_details';
|
||||
|
||||
describe('Custom query rules', { tags: ['@ess', '@serverless'] }, () => {
|
||||
const rule = getEditedRule();
|
||||
const expectedEditedtags = rule.tags?.join('');
|
||||
const expectedEditedIndexPatterns = rule.index;
|
||||
|
||||
beforeEach(() => {
|
||||
deleteConnectors();
|
||||
deleteAlertsAndRules();
|
||||
login();
|
||||
createRule(getExistingRule({ rule_id: 'rule1', enabled: true })).then((createdRule) => {
|
||||
visitEditRulePage(createdRule.body.id);
|
||||
});
|
||||
});
|
||||
|
||||
it('Allows a rule to be edited', () => {
|
||||
const existingRule = getExistingRule();
|
||||
|
||||
// expect define step to populate
|
||||
cy.get(CUSTOM_QUERY_INPUT).should('have.value', existingRule.query);
|
||||
|
||||
cy.get(DEFINE_INDEX_INPUT).should('have.text', existingRule.index?.join(''));
|
||||
|
||||
goToAboutStepTab();
|
||||
|
||||
// expect about step to populate
|
||||
cy.get(RULE_NAME_INPUT).invoke('val').should('eql', existingRule.name);
|
||||
cy.get(RULE_DESCRIPTION_INPUT).should('have.text', existingRule.description);
|
||||
cy.get(TAGS_FIELD).should('have.text', existingRule.tags?.join(''));
|
||||
cy.get(SEVERITY_DROPDOWN).should('have.text', 'High');
|
||||
cy.get(DEFAULT_RISK_SCORE_INPUT).invoke('val').should('eql', `${existingRule.risk_score}`);
|
||||
|
||||
goToScheduleStepTab();
|
||||
|
||||
// expect schedule step to populate
|
||||
const interval = existingRule.interval;
|
||||
const intervalParts = interval != null && interval.match(/[0-9]+|[a-zA-Z]+/g);
|
||||
if (intervalParts) {
|
||||
const [amount, unit] = intervalParts;
|
||||
cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', amount);
|
||||
cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', unit);
|
||||
} else {
|
||||
throw new Error('Cannot assert scheduling info on a rule without an interval');
|
||||
}
|
||||
|
||||
goToActionsStepTab();
|
||||
|
||||
addEmailConnectorAndRuleAction('test@example.com', 'Subject');
|
||||
|
||||
cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'Summary of alerts');
|
||||
cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Per rule run');
|
||||
|
||||
goToAboutStepTab();
|
||||
cy.get(TAGS_CLEAR_BUTTON).click();
|
||||
fillAboutRule(getEditedRule());
|
||||
|
||||
cy.intercept('GET', '/api/detection_engine/rules?id*').as('getRule');
|
||||
|
||||
saveEditedRule();
|
||||
|
||||
cy.wait('@getRule').then(({ response }) => {
|
||||
cy.wrap(response?.statusCode).should('eql', 200);
|
||||
// ensure that editing rule does not modify max_signals
|
||||
cy.wrap(response?.body.max_signals).should('eql', existingRule.max_signals);
|
||||
});
|
||||
|
||||
cy.get(RULE_NAME_HEADER).should('contain', `${getEditedRule().name}`);
|
||||
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getEditedRule().description);
|
||||
cy.get(ABOUT_DETAILS).within(() => {
|
||||
getDetails(SEVERITY_DETAILS).should('have.text', 'Medium');
|
||||
getDetails(RISK_SCORE_DETAILS).should('have.text', `${getEditedRule().risk_score}`);
|
||||
getDetails(TAGS_DETAILS).should('have.text', expectedEditedtags);
|
||||
});
|
||||
cy.get(INVESTIGATION_NOTES_TOGGLE).click();
|
||||
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', getEditedRule().note);
|
||||
cy.get(DEFINITION_DETAILS).within(() => {
|
||||
getDetails(INDEX_PATTERNS_DETAILS).should('have.text', expectedEditedIndexPatterns?.join(''));
|
||||
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', getEditedRule().query);
|
||||
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
|
||||
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
|
||||
});
|
||||
if (getEditedRule().interval) {
|
||||
cy.get(SCHEDULE_DETAILS).within(() => {
|
||||
getDetails(RUNS_EVERY_DETAILS).should('have.text', getEditedRule().interval);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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 { visitRulesManagementTable } from '../../../../../tasks/rules_management';
|
||||
import { getNewRule } from '../../../../../objects/rule';
|
||||
|
||||
import { RULE_SWITCH } from '../../../../../screens/alerts_detection_rules';
|
||||
|
||||
import {
|
||||
deleteFirstRule,
|
||||
getRulesManagementTableRows,
|
||||
selectRulesByName,
|
||||
} from '../../../../../tasks/alerts_detection_rules';
|
||||
import { deleteSelectedRules } from '../../../../../tasks/rules_bulk_actions';
|
||||
import { createRule, findAllRules } from '../../../../../tasks/api_calls/rules';
|
||||
import { deleteAlertsAndRules } from '../../../../../tasks/common';
|
||||
import { login } from '../../../../../tasks/login';
|
||||
|
||||
describe('Rule deletion', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
|
||||
const testRules = [
|
||||
getNewRule({ rule_id: 'rule1', name: 'Rule 1', enabled: false }),
|
||||
getNewRule({ rule_id: 'rule2', name: 'Rule 2', enabled: false }),
|
||||
getNewRule({ rule_id: 'rule3', name: 'Rule 3', enabled: false }),
|
||||
];
|
||||
beforeEach(() => {
|
||||
deleteAlertsAndRules();
|
||||
createRule(testRules[0]);
|
||||
createRule(testRules[1]);
|
||||
createRule(testRules[2]);
|
||||
login();
|
||||
visitRulesManagementTable();
|
||||
});
|
||||
|
||||
it('User can delete an individual rule', () => {
|
||||
getRulesManagementTableRows().then((rules) => {
|
||||
const initialNumberOfRules = rules.length;
|
||||
const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1;
|
||||
|
||||
findAllRules().then(({ body }) => {
|
||||
const numberOfRules = body.data.length;
|
||||
expect(numberOfRules).to.eql(initialNumberOfRules);
|
||||
});
|
||||
|
||||
deleteFirstRule();
|
||||
|
||||
getRulesManagementTableRows().should('have.length', expectedNumberOfRulesAfterDeletion);
|
||||
findAllRules().then(({ body }) => {
|
||||
const numberOfRules = body.data.length;
|
||||
expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('User can delete multiple selected rules via a bulk action', () => {
|
||||
getRulesManagementTableRows().then((rules) => {
|
||||
const rulesToDelete = ['Rule 1', 'Rule 2'] as const;
|
||||
const initialNumberOfRules = rules.length;
|
||||
const numberOfRulesToBeDeleted = 2;
|
||||
const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - numberOfRulesToBeDeleted;
|
||||
|
||||
selectRulesByName(rulesToDelete);
|
||||
deleteSelectedRules();
|
||||
|
||||
// During deletion, rule switch is not shown and instead there's loading spinner
|
||||
getRulesManagementTableRows()
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(RULE_SWITCH).should('not.exist');
|
||||
});
|
||||
|
||||
getRulesManagementTableRows().should('have.length', expectedNumberOfRulesAfterDeletion);
|
||||
findAllRules().then(({ body }) => {
|
||||
const numberOfRules = body.data.length;
|
||||
expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion);
|
||||
});
|
||||
|
||||
// Once bulk delete is done and one rule remains, checking for enable/disable switch to exist
|
||||
getRulesManagementTableRows()
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(RULE_SWITCH).should('exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -204,6 +204,9 @@ export const TAGS_INPUT =
|
|||
export const TAGS_CLEAR_BUTTON =
|
||||
'[data-test-subj="detectionEngineStepAboutRuleTags"] [data-test-subj="comboBoxClearButton"]';
|
||||
|
||||
export const INVESTIGATIONS_INPUT =
|
||||
'[data-test-subj="detectionEngineStepAboutRuleInvestigationFields"] [data-test-subj="comboBoxSearchInput"]';
|
||||
|
||||
export const THRESHOLD_INPUT_AREA = '[data-test-subj="thresholdInput"]';
|
||||
|
||||
export const THRESHOLD_TYPE = '[data-test-subj="thresholdRuleType"]';
|
||||
|
|
|
@ -142,7 +142,8 @@ export const THREAT_TECHNIQUE = '[data-test-subj="threatTechniqueLink"]';
|
|||
|
||||
export const THREAT_SUBTECHNIQUE = '[data-test-subj="threatSubtechniqueLink"]';
|
||||
|
||||
export const BACK_TO_RULES_TABLE = '[data-test-subj="breadcrumb"][title="Detection rules (SIEM)"]';
|
||||
export const BACK_TO_RULES_TABLE =
|
||||
'[data-test-subj="breadcrumbs"] a[title="Detection rules (SIEM)"]';
|
||||
|
||||
export const HIGHLIGHTED_ROWS_IN_TABLE =
|
||||
'[data-test-subj="euiDataGridBody"] .alertsTableHighlightedRow';
|
||||
|
|
|
@ -114,6 +114,7 @@ import {
|
|||
CREATE_WITHOUT_ENABLING_BTN,
|
||||
RULE_INDICES,
|
||||
ALERTS_INDEX_BUTTON,
|
||||
INVESTIGATIONS_INPUT,
|
||||
} from '../screens/create_new_rule';
|
||||
import {
|
||||
INDEX_SELECTOR,
|
||||
|
@ -131,11 +132,15 @@ import { waitForAlerts } from './alerts';
|
|||
import { refreshPage } from './security_header';
|
||||
import { EMPTY_ALERT_TABLE } from '../screens/alerts';
|
||||
|
||||
export const createAndEnableRuleOnly = () => {
|
||||
cy.get(CREATE_AND_ENABLE_BTN).click({ force: true });
|
||||
cy.get(CREATE_AND_ENABLE_BTN).should('not.exist');
|
||||
};
|
||||
|
||||
export const createAndEnableRule = () => {
|
||||
cy.get(CREATE_AND_ENABLE_BTN).click({ force: true });
|
||||
cy.get(CREATE_AND_ENABLE_BTN).should('not.exist');
|
||||
cy.get(BACK_TO_RULES_TABLE).click({ force: true });
|
||||
cy.get(BACK_TO_RULES_TABLE).should('not.exist');
|
||||
};
|
||||
|
||||
export const pressRuleCreateBtn = () => {
|
||||
|
@ -303,6 +308,23 @@ export const fillReferenceUrls = (referenceUrls: string[] = ruleFields.reference
|
|||
return referenceUrls;
|
||||
};
|
||||
|
||||
export const fillCustomInvestigationFields = (
|
||||
fields: string[] = ruleFields.investigationFields.field_names
|
||||
) => {
|
||||
fields.forEach((field) => {
|
||||
cy.get(INVESTIGATIONS_INPUT).type(`${field}{enter}`, { force: true });
|
||||
});
|
||||
return fields;
|
||||
};
|
||||
|
||||
export const fillAboutRuleMinimumAndContinue = (rule: RuleCreateProps) => {
|
||||
cy.get(RULE_NAME_INPUT).clear();
|
||||
cy.get(RULE_NAME_INPUT).type(rule.name);
|
||||
cy.get(RULE_DESCRIPTION_INPUT).clear();
|
||||
cy.get(RULE_DESCRIPTION_INPUT).type(rule.description);
|
||||
getAboutContinueButton().should('exist').click();
|
||||
};
|
||||
|
||||
export const fillAboutRuleAndContinue = (rule: RuleCreateProps) => {
|
||||
fillAboutRule(rule);
|
||||
getAboutContinueButton().should('exist').click({ force: true });
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
"cypress:run:respops:ess": "yarn cypress:ess --spec './cypress/e2e/(detection_alerts|detection_rules|exceptions)/*.cy.ts'",
|
||||
"cypress:investigations:run:ess": "yarn cypress:ess --spec './cypress/e2e/investigations/**/*.cy.ts'",
|
||||
"cypress:explore:run:ess": "yarn cypress:ess --spec './cypress/e2e/explore/**/*.cy.ts'",
|
||||
"cypress:changed-specs-only:ess": "yarn cypress:ess --changed-specs-only --env burn=2",
|
||||
"cypress:burn:ess": "yarn cypress:ess --env burn=2",
|
||||
"cypress:changed-specs-only:ess": "yarn cypress:ess --changed-specs-only --env burn=5",
|
||||
"cypress:burn:ess": "yarn cypress:ess --env burn=5",
|
||||
"junit:merge": "../../../node_modules/.bin/mochawesome-merge ../../../target/kibana-security-solution/cypress/results/mochawesome*.json > ../../../target/kibana-security-solution/cypress/results/output.json && ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results && yarn junit:transform && mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/",
|
||||
"junit:transform": "node ../../plugins/security_solution/scripts/junit_transformer --pathPattern '../../../target/kibana-security-solution/cypress/results/*.xml' --rootDirectory ../../../ --reportName 'Security Solution Cypress' --writeInPlace",
|
||||
"cypress:serverless": "TZ=UTC NODE_OPTIONS=--openssl-legacy-provider node ../../plugins/security_solution/scripts/start_cypress_parallel --config-file ../../test/security_solution_cypress/cypress/cypress_ci_serverless.config.ts --ftr-config-file ../../test/security_solution_cypress/serverless_config",
|
||||
|
@ -25,7 +25,7 @@
|
|||
"cypress:run:cloud:serverless": "yarn cypress:cloud:serverless run --config-file ./cypress/cypress_ci_serverless.config.ts --env CLOUD_SERVERLESS=true",
|
||||
"cypress:investigations:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/investigations/**/*.cy.ts'",
|
||||
"cypress:explore:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/explore/**/*.cy.ts'",
|
||||
"cypress:changed-specs-only:serverless": "yarn cypress:serverless --changed-specs-only --env burn=2",
|
||||
"cypress:burn:serverless": "yarn cypress:serverless --env burn=2"
|
||||
"cypress:changed-specs-only:serverless": "yarn cypress:serverless --changed-specs-only --env burn=5",
|
||||
"cypress:burn:serverless": "yarn cypress:serverless --env burn=5"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue