diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts index d9d9fde8fc8cc..17ff1dad79960 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { newRule } from '../objects/rule'; +import { newRule, existingRule } from '../objects/rule'; import { CUSTOM_RULES_BTN, @@ -16,26 +16,16 @@ import { SHOWING_RULES_TEXT, } from '../screens/alerts_detection_rules'; import { - ABOUT_FALSE_POSITIVES, ABOUT_INVESTIGATION_NOTES, - ABOUT_MITRE, - ABOUT_RISK, ABOUT_RULE_DESCRIPTION, - ABOUT_SEVERITY, - ABOUT_STEP, - ABOUT_TAGS, - ABOUT_URLS, - DEFINITION_CUSTOM_QUERY, - DEFINITION_INDEX_PATTERNS, - DEFINITION_TIMELINE, - DEFINITION_STEP, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - SCHEDULE_LOOPBACK, - SCHEDULE_RUNS, - SCHEDULE_STEP, + getDescriptionForTitle, + ABOUT_DETAILS, + DEFINITION_DETAILS, + SCHEDULE_DETAILS, } from '../screens/rule_details'; import { @@ -53,18 +43,38 @@ import { selectNumberOfRules, waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, + editFirstRule, } from '../tasks/alerts_detection_rules'; import { createAndActivateRule, fillAboutRuleAndContinue, fillDefineCustomRuleWithImportedQueryAndContinue, - expectDefineFormToRepopulateAndContinue, - expectAboutFormToRepopulateAndContinue, + goToAboutStepTab, + goToScheduleStepTab, + goToActionsStepTab, + fillAboutRule, } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; +import { + ACTIONS_THROTTLE_INPUT, + CUSTOM_QUERY_INPUT, + DEFINE_INDEX_INPUT, + RULE_NAME_INPUT, + RULE_DESCRIPTION_INPUT, + TAGS_FIELD, + SEVERITY_DROPDOWN, + RISK_INPUT, + SCHEDULE_INTERVAL_AMOUNT_INPUT, + SCHEDULE_INTERVAL_UNITS_INPUT, + DEFINE_EDIT_BUTTON, + DEFINE_CONTINUE_BUTTON, + ABOUT_EDIT_BUTTON, + ABOUT_CONTINUE_BTN, +} from '../screens/create_new_rule'; +import { saveEditedRule } from '../tasks/edit_rule'; describe('Detection rules, custom', () => { before(() => { @@ -84,8 +94,19 @@ describe('Detection rules, custom', () => { goToCreateNewRule(); fillDefineCustomRuleWithImportedQueryAndContinue(newRule); fillAboutRuleAndContinue(newRule); - expectDefineFormToRepopulateAndContinue(newRule); - expectAboutFormToRepopulateAndContinue(newRule); + + // expect define step to repopulate + cy.get(DEFINE_EDIT_BUTTON).click(); + cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', newRule.customQuery); + cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); + cy.get(DEFINE_CONTINUE_BUTTON).should('not.exist'); + + // expect about step to populate + cy.get(ABOUT_EDIT_BUTTON).click(); + cy.get(RULE_NAME_INPUT).invoke('val').should('eq', newRule.name); + cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); + cy.get(ABOUT_CONTINUE_BTN).should('not.exist'); + createAndActivateRule(); cy.get(CUSTOM_RULES_BTN).invoke('text').should('eql', 'Custom rules (1)'); @@ -142,32 +163,35 @@ describe('Detection rules, custom', () => { cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${newRule.name} Beta`); cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', newRule.description); - cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', newRule.severity); - cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', newRule.riskScore); - cy.get(ABOUT_STEP).eq(ABOUT_URLS).invoke('text').should('eql', expectedUrls); - cy.get(ABOUT_STEP) - .eq(ABOUT_FALSE_POSITIVES) - .invoke('text') - .should('eql', expectedFalsePositives); - cy.get(ABOUT_STEP).eq(ABOUT_MITRE).invoke('text').should('eql', expectedMitre); - cy.get(ABOUT_STEP).eq(ABOUT_TAGS).invoke('text').should('eql', expectedTags); + cy.get(ABOUT_DETAILS).within(() => { + getDescriptionForTitle('Severity').invoke('text').should('eql', newRule.severity); + getDescriptionForTitle('Risk score').invoke('text').should('eql', newRule.riskScore); + getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); + getDescriptionForTitle('False positive examples') + .invoke('text') + .should('eql', expectedFalsePositives); + getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); + getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + }); cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_INDEX_PATTERNS).then((patterns) => { - cy.wrap(patterns).each((pattern, index) => { - cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]); - }); + cy.get(DEFINITION_DETAILS).within(() => { + getDescriptionForTitle('Index patterns') + .invoke('text') + .should('eql', expectedIndexPatterns.join('')); + getDescriptionForTitle('Custom query') + .invoke('text') + .should('eql', `${newRule.customQuery} `); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + }); + + cy.get(SCHEDULE_DETAILS).within(() => { + getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); + getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); }); - cy.get(DEFINITION_STEP) - .eq(DEFINITION_CUSTOM_QUERY) - .invoke('text') - .should('eql', `${newRule.customQuery} `); - cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); - - cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m'); - cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m'); }); }); @@ -233,4 +257,94 @@ describe('Deletes custom rules', () => { .should('eql', `Custom rules (${expectedNumberOfRulesAfterDeletion})`); }); }); + + it('Allows a rule to be edited', () => { + editFirstRule(); + + // expect define step to populate + cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', existingRule.customQuery); + if (existingRule.index && existingRule.index.length > 0) { + cy.get(DEFINE_INDEX_INPUT).invoke('text').should('eq', 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).invoke('text').should('eql', existingRule.description); + cy.get(TAGS_FIELD).invoke('text').should('eql', existingRule.tags.join('')); + + cy.get(SEVERITY_DROPDOWN).invoke('text').should('eql', existingRule.severity); + cy.get(RISK_INPUT).invoke('val').should('eql', existingRule.riskScore); + + goToScheduleStepTab(); + + // expect schedule step to populate + const intervalParts = existingRule.interval && existingRule.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(); + + cy.get(ACTIONS_THROTTLE_INPUT).invoke('val').should('eql', 'no_actions'); + + goToAboutStepTab(); + + const editedRule = { + ...existingRule, + severity: 'Medium', + description: 'Edited Rule description', + }; + + fillAboutRule(editedRule); + saveEditedRule(); + + const expectedTags = editedRule.tags.join(''); + const expectedIndexPatterns = + editedRule.index && editedRule.index.length + ? editedRule.index + : [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ]; + + cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${editedRule.name} Beta`); + + cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', editedRule.description); + cy.get(ABOUT_DETAILS).within(() => { + getDescriptionForTitle('Severity').invoke('text').should('eql', editedRule.severity); + getDescriptionForTitle('Risk score').invoke('text').should('eql', editedRule.riskScore); + getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + }); + + cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); + cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', editedRule.note); + + cy.get(DEFINITION_DETAILS).within(() => { + getDescriptionForTitle('Index patterns') + .invoke('text') + .should('eql', expectedIndexPatterns.join('')); + getDescriptionForTitle('Custom query') + .invoke('text') + .should('eql', `${editedRule.customQuery} `); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + }); + + if (editedRule.interval) { + cy.get(SCHEDULE_DETAILS).within(() => { + getDescriptionForTitle('Runs every').invoke('text').should('eql', editedRule.interval); + }); + } + }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts index c65cd8406099a..76871929fe050 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts @@ -15,26 +15,16 @@ import { SEVERITY, } from '../screens/alerts_detection_rules'; import { - ABOUT_FALSE_POSITIVES, + ABOUT_DETAILS, ABOUT_INVESTIGATION_NOTES, - ABOUT_MITRE, - ABOUT_RISK, ABOUT_RULE_DESCRIPTION, - ABOUT_SEVERITY, - ABOUT_STEP, - ABOUT_TAGS, - ABOUT_URLS, - DEFINITION_CUSTOM_QUERY, - DEFINITION_INDEX_PATTERNS, - DEFINITION_TIMELINE, - DEFINITION_STEP, + DEFINITION_DETAILS, + getDescriptionForTitle, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - SCHEDULE_LOOPBACK, - SCHEDULE_RUNS, - SCHEDULE_STEP, + SCHEDULE_DETAILS, } from '../screens/rule_details'; import { @@ -136,32 +126,34 @@ describe('Detection rules, EQL', () => { cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${eqlRule.name} Beta`); cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', eqlRule.description); - cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', eqlRule.severity); - cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', eqlRule.riskScore); - cy.get(ABOUT_STEP).eq(ABOUT_URLS).invoke('text').should('eql', expectedUrls); - cy.get(ABOUT_STEP) - .eq(ABOUT_FALSE_POSITIVES) - .invoke('text') - .should('eql', expectedFalsePositives); - cy.get(ABOUT_STEP).eq(ABOUT_MITRE).invoke('text').should('eql', expectedMitre); - cy.get(ABOUT_STEP).eq(ABOUT_TAGS).invoke('text').should('eql', expectedTags); + cy.get(ABOUT_DETAILS).within(() => { + getDescriptionForTitle('Severity').invoke('text').should('eql', eqlRule.severity); + getDescriptionForTitle('Risk score').invoke('text').should('eql', eqlRule.riskScore); + getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); + getDescriptionForTitle('False positive examples') + .invoke('text') + .should('eql', expectedFalsePositives); + getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); + getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + }); cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_INDEX_PATTERNS).then((patterns) => { - cy.wrap(patterns).each((pattern, index) => { - cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]); - }); + cy.get(DEFINITION_DETAILS).within(() => { + getDescriptionForTitle('Index patterns') + .invoke('text') + .should('eql', expectedIndexPatterns.join('')); + getDescriptionForTitle('Custom query') + .invoke('text') + .should('eql', `${eqlRule.customQuery} `); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Event Correlation'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + }); + + cy.get(SCHEDULE_DETAILS).within(() => { + getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); + getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); }); - cy.get(DEFINITION_STEP) - .eq(DEFINITION_CUSTOM_QUERY) - .invoke('text') - .should('eql', `${eqlRule.customQuery} `); - cy.get(DEFINITION_STEP).eq(2).invoke('text').should('eql', 'Event Correlation'); - cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); - - cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m'); - cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts index b6b30ef550eb1..47e49d48e2aec 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts @@ -16,24 +16,14 @@ import { SEVERITY, } from '../screens/alerts_detection_rules'; import { - ABOUT_FALSE_POSITIVES, - ABOUT_MITRE, - ABOUT_RISK, ABOUT_RULE_DESCRIPTION, - ABOUT_SEVERITY, - ABOUT_STEP, - ABOUT_TAGS, - ABOUT_URLS, - ANOMALY_SCORE, - DEFINITION_TIMELINE, - DEFINITION_STEP, MACHINE_LEARNING_JOB_ID, MACHINE_LEARNING_JOB_STATUS, RULE_NAME_HEADER, - SCHEDULE_LOOPBACK, - SCHEDULE_RUNS, - SCHEDULE_STEP, - RULE_TYPE, + getDescriptionForTitle, + ABOUT_DETAILS, + DEFINITION_DETAILS, + SCHEDULE_DETAILS, } from '../screens/rule_details'; import { @@ -126,36 +116,37 @@ describe('Detection rules, machine learning', () => { cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${machineLearningRule.name} Beta`); cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', machineLearningRule.description); - cy.get(ABOUT_STEP) - .eq(ABOUT_SEVERITY) - .invoke('text') - .should('eql', machineLearningRule.severity); - cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', machineLearningRule.riskScore); - cy.get(ABOUT_STEP).eq(ABOUT_URLS).invoke('text').should('eql', expectedUrls); - cy.get(ABOUT_STEP) - .eq(ABOUT_FALSE_POSITIVES) - .invoke('text') - .should('eql', expectedFalsePositives); - cy.get(ABOUT_STEP).eq(ABOUT_MITRE).invoke('text').should('eql', expectedMitre); - cy.get(ABOUT_STEP).eq(ABOUT_TAGS).invoke('text').should('eql', expectedTags); - - cy.get(DEFINITION_STEP).eq(RULE_TYPE).invoke('text').should('eql', 'Machine Learning'); - cy.get(DEFINITION_STEP) - .eq(ANOMALY_SCORE) - .invoke('text') - .should('eql', machineLearningRule.anomalyScoreThreshold); - cy.get(DEFINITION_STEP) - .get(MACHINE_LEARNING_JOB_STATUS) - .invoke('text') - .should('eql', 'Stopped'); - cy.get(DEFINITION_STEP) - .get(MACHINE_LEARNING_JOB_ID) - .invoke('text') - .should('eql', machineLearningRule.machineLearningJob); - - cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); - - cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m'); - cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m'); + cy.get(ABOUT_DETAILS).within(() => { + getDescriptionForTitle('Severity').invoke('text').should('eql', machineLearningRule.severity); + getDescriptionForTitle('Risk score') + .invoke('text') + .should('eql', machineLearningRule.riskScore); + getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); + getDescriptionForTitle('False positive examples') + .invoke('text') + .should('eql', expectedFalsePositives); + getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); + getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + }); + + cy.get(DEFINITION_DETAILS).within(() => { + getDescriptionForTitle('Anomaly score') + .invoke('text') + .should('eql', machineLearningRule.anomalyScoreThreshold); + getDescriptionForTitle('Anomaly score') + .invoke('text') + .should('eql', machineLearningRule.anomalyScoreThreshold); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Machine Learning'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + cy.get(MACHINE_LEARNING_JOB_STATUS).invoke('text').should('eql', 'Stopped'); + cy.get(MACHINE_LEARNING_JOB_ID) + .invoke('text') + .should('eql', machineLearningRule.machineLearningJob); + }); + + cy.get(SCHEDULE_DETAILS).within(() => { + getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); + getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); + }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts index e3526c63e2310..4edf5e1866087 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts @@ -16,29 +16,17 @@ import { } from '../screens/alerts_detection_rules'; import { ABOUT_INVESTIGATION_NOTES, - ABOUT_OVERRIDE_FALSE_POSITIVES, - ABOUT_OVERRIDE_MITRE, - ABOUT_OVERRIDE_NAME_OVERRIDE, - ABOUT_OVERRIDE_RISK, - ABOUT_OVERRIDE_RISK_OVERRIDE, - ABOUT_OVERRIDE_SEVERITY_OVERRIDE, - ABOUT_OVERRIDE_TAGS, - ABOUT_OVERRIDE_TIMESTAMP_OVERRIDE, - ABOUT_OVERRIDE_URLS, ABOUT_RULE_DESCRIPTION, - ABOUT_SEVERITY, - ABOUT_STEP, - DEFINITION_CUSTOM_QUERY, - DEFINITION_INDEX_PATTERNS, - DEFINITION_TIMELINE, - DEFINITION_STEP, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - SCHEDULE_LOOPBACK, - SCHEDULE_RUNS, - SCHEDULE_STEP, + ABOUT_DETAILS, + getDescriptionForTitle, + DEFINITION_DETAILS, + SCHEDULE_DETAILS, + DETAILS_TITLE, + DETAILS_DESCRIPTION, } from '../screens/rule_details'; import { @@ -141,56 +129,56 @@ describe('Detection rules, override', () => { const expectedOverrideSeverities = ['Low', 'Medium', 'High', 'Critical']; - cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', newOverrideRule.severity); - newOverrideRule.severityOverride.forEach((severity, i) => { - cy.get(ABOUT_STEP) - .eq(ABOUT_OVERRIDE_SEVERITY_OVERRIDE + i) + cy.get(ABOUT_DETAILS).within(() => { + getDescriptionForTitle('Severity').invoke('text').should('eql', newOverrideRule.severity); + getDescriptionForTitle('Risk score').invoke('text').should('eql', newOverrideRule.riskScore); + getDescriptionForTitle('Risk score override') .invoke('text') - .should( - 'eql', - `${severity.sourceField}:${severity.sourceValue}${expectedOverrideSeverities[i]}` - ); + .should('eql', `${newOverrideRule.riskOverride}signal.rule.risk_score`); + getDescriptionForTitle('Rule name override') + .invoke('text') + .should('eql', newOverrideRule.nameOverride); + getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); + getDescriptionForTitle('False positive examples') + .invoke('text') + .should('eql', expectedFalsePositives); + getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); + getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + getDescriptionForTitle('Timestamp override') + .invoke('text') + .should('eql', newOverrideRule.timestampOverride); + cy.contains(DETAILS_TITLE, 'Severity override') + .invoke('index', DETAILS_TITLE) // get index relative to other titles, not all siblings + .then((severityOverrideIndex) => { + newOverrideRule.severityOverride.forEach((severity, i) => { + cy.get(DETAILS_DESCRIPTION) + .eq(severityOverrideIndex + i) + .invoke('text') + .should( + 'eql', + `${severity.sourceField}:${severity.sourceValue}${expectedOverrideSeverities[i]}` + ); + }); + }); }); - cy.get(ABOUT_STEP) - .eq(ABOUT_OVERRIDE_RISK) - .invoke('text') - .should('eql', newOverrideRule.riskScore); - cy.get(ABOUT_STEP) - .eq(ABOUT_OVERRIDE_RISK_OVERRIDE) - .invoke('text') - .should('eql', `${newOverrideRule.riskOverride}signal.rule.risk_score`); - cy.get(ABOUT_STEP).eq(ABOUT_OVERRIDE_URLS).invoke('text').should('eql', expectedUrls); - cy.get(ABOUT_STEP) - .eq(ABOUT_OVERRIDE_FALSE_POSITIVES) - .invoke('text') - .should('eql', expectedFalsePositives); - cy.get(ABOUT_STEP) - .eq(ABOUT_OVERRIDE_NAME_OVERRIDE) - .invoke('text') - .should('eql', newOverrideRule.nameOverride); - cy.get(ABOUT_STEP).eq(ABOUT_OVERRIDE_MITRE).invoke('text').should('eql', expectedMitre); - cy.get(ABOUT_STEP) - .eq(ABOUT_OVERRIDE_TIMESTAMP_OVERRIDE) - .invoke('text') - .should('eql', newOverrideRule.timestampOverride); - cy.get(ABOUT_STEP).eq(ABOUT_OVERRIDE_TAGS).invoke('text').should('eql', expectedTags); - cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_INDEX_PATTERNS).then((patterns) => { - cy.wrap(patterns).each((pattern, index) => { - cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]); - }); + cy.get(DEFINITION_DETAILS).within(() => { + getDescriptionForTitle('Index patterns') + .invoke('text') + .should('eql', expectedIndexPatterns.join('')); + getDescriptionForTitle('Custom query') + .invoke('text') + .should('eql', `${newOverrideRule.customQuery} `); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + }); + + cy.get(SCHEDULE_DETAILS).within(() => { + getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); + getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); }); - cy.get(DEFINITION_STEP) - .eq(DEFINITION_CUSTOM_QUERY) - .invoke('text') - .should('eql', `${newOverrideRule.customQuery} `); - cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); - - cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m'); - cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts index 10f9ebb5623df..00175ed3baeb8 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts @@ -15,27 +15,16 @@ import { SEVERITY, } from '../screens/alerts_detection_rules'; import { - ABOUT_FALSE_POSITIVES, ABOUT_INVESTIGATION_NOTES, - ABOUT_MITRE, - ABOUT_RISK, ABOUT_RULE_DESCRIPTION, - ABOUT_SEVERITY, - ABOUT_STEP, - ABOUT_TAGS, - ABOUT_URLS, - DEFINITION_CUSTOM_QUERY, - DEFINITION_INDEX_PATTERNS, - DEFINITION_THRESHOLD, - DEFINITION_TIMELINE, - DEFINITION_STEP, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - SCHEDULE_LOOPBACK, - SCHEDULE_RUNS, - SCHEDULE_STEP, + getDescriptionForTitle, + ABOUT_DETAILS, + DEFINITION_DETAILS, + SCHEDULE_DETAILS, } from '../screens/rule_details'; import { @@ -137,38 +126,40 @@ describe('Detection rules, threshold', () => { cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${newThresholdRule.name} Beta`); cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', newThresholdRule.description); - cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', newThresholdRule.severity); - cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', newThresholdRule.riskScore); - cy.get(ABOUT_STEP).eq(ABOUT_URLS).invoke('text').should('eql', expectedUrls); - cy.get(ABOUT_STEP) - .eq(ABOUT_FALSE_POSITIVES) - .invoke('text') - .should('eql', expectedFalsePositives); - cy.get(ABOUT_STEP).eq(ABOUT_MITRE).invoke('text').should('eql', expectedMitre); - cy.get(ABOUT_STEP).eq(ABOUT_TAGS).invoke('text').should('eql', expectedTags); + cy.get(ABOUT_DETAILS).within(() => { + getDescriptionForTitle('Severity').invoke('text').should('eql', newThresholdRule.severity); + getDescriptionForTitle('Risk score').invoke('text').should('eql', newThresholdRule.riskScore); + getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); + getDescriptionForTitle('False positive examples') + .invoke('text') + .should('eql', expectedFalsePositives); + getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); + getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + }); cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_INDEX_PATTERNS).then((patterns) => { - cy.wrap(patterns).each((pattern, index) => { - cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]); - }); + cy.get(DEFINITION_DETAILS).within(() => { + getDescriptionForTitle('Index patterns') + .invoke('text') + .should('eql', expectedIndexPatterns.join('')); + getDescriptionForTitle('Custom query') + .invoke('text') + .should('eql', `${newThresholdRule.customQuery} `); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Threshold'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + getDescriptionForTitle('Threshold') + .invoke('text') + .should( + 'eql', + `Results aggregated by ${newThresholdRule.thresholdField} >= ${newThresholdRule.threshold}` + ); + }); + + cy.get(SCHEDULE_DETAILS).within(() => { + getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); + getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); }); - cy.get(DEFINITION_STEP) - .eq(DEFINITION_CUSTOM_QUERY) - .invoke('text') - .should('eql', `${newThresholdRule.customQuery} `); - cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); - cy.get(DEFINITION_STEP) - .eq(DEFINITION_THRESHOLD) - .invoke('text') - .should( - 'eql', - `Results aggregated by ${newThresholdRule.thresholdField} >= ${newThresholdRule.threshold}` - ); - - cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m'); - cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index 0624606fe8481..2a5c60815f450 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -27,6 +27,8 @@ export interface CustomRule { customQuery: string; name: string; description: string; + index?: string[]; + interval?: string; severity: string; riskScore: string; tags: string[]; @@ -109,6 +111,29 @@ export const newRule: CustomRule = { timelineId: '0162c130-78be-11ea-9718-118a926974a4', }; +export const existingRule: CustomRule = { + customQuery: 'host.name:*', + name: 'Rule 1', + description: 'Description for Rule 1', + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + interval: '4m', + severity: 'High', + riskScore: '19', + tags: ['rule1'], + referenceUrls: [], + falsePositivesExamples: [], + mitre: [], + note: 'This is my note', + timelineId: '', +}; + export const newOverrideRule: OverrideRule = { customQuery: 'host.name:*', name: 'New Rule Test', diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts index a41b8296f83e4..14f5383939a94 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts @@ -14,6 +14,8 @@ export const CUSTOM_RULES_BTN = '[data-test-subj="show-custom-rules-filter-butto export const DELETE_RULE_ACTION_BTN = '[data-test-subj="deleteRuleAction"]'; +export const EDIT_RULE_ACTION_BTN = '[data-test-subj="editRuleAction"]'; + export const DELETE_RULE_BULK_BTN = '[data-test-subj="deleteRuleBulk"]'; export const ELASTIC_RULES_BTN = '[data-test-subj="show-elastic-rules-filter-button"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts index 1c25ed88c3bee..dda371126d5aa 100644 --- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts @@ -8,6 +8,13 @@ export const ABOUT_CONTINUE_BTN = '[data-test-subj="about-continue"]'; export const ABOUT_EDIT_BUTTON = '[data-test-subj="edit-about-rule"]'; +export const ABOUT_EDIT_TAB = '[data-test-subj="edit-rule-about-tab"]'; + +export const ACTIONS_EDIT_TAB = '[data-test-subj="edit-rule-actions-tab"]'; + +export const ACTIONS_THROTTLE_INPUT = + '[data-test-subj="stepRuleActions"] [data-test-subj="select"]'; + export const ADD_FALSE_POSITIVE_BTN = '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] .euiButtonEmpty__text'; @@ -30,6 +37,11 @@ export const DEFINE_CONTINUE_BUTTON = '[data-test-subj="define-continue"]'; export const DEFINE_EDIT_BUTTON = '[data-test-subj="edit-define-rule"]'; +export const DEFINE_EDIT_TAB = '[data-test-subj="edit-rule-define-tab"]'; + +export const DEFINE_INDEX_INPUT = + '[data-test-subj="detectionEngineStepDefineRuleIndices"] [data-test-subj="input"]'; + export const EQL_TYPE = '[data-test-subj="eqlRuleType"]'; export const EQL_QUERY_INPUT = '[data-test-subj="eqlQueryBarTextInput"]'; @@ -81,6 +93,20 @@ export const RULE_TIMESTAMP_OVERRIDE = export const SCHEDULE_CONTINUE_BUTTON = '[data-test-subj="schedule-continue"]'; +export const SCHEDULE_EDIT_TAB = '[data-test-subj="edit-rule-schedule-tab"]'; + +export const SCHEDULE_INTERVAL_AMOUNT_INPUT = + '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="schedule-amount-input"]'; + +export const SCHEDULE_INTERVAL_UNITS_INPUT = + '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="schedule-units-input"]'; + +export const SCHEDULE_LOOKBACK_AMOUNT_INPUT = + '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="schedule-amount-input"]'; + +export const SCHEDULE_LOOKBACK_UNITS_INPUT = + '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="schedule-units-input"]'; + export const SEVERITY_DROPDOWN = '[data-test-subj="detectionEngineStepAboutRuleSeverity"] [data-test-subj="select"]'; @@ -88,6 +114,9 @@ export const SEVERITY_MAPPING_OVERRIDE_OPTION = '#severity-mapping-override'; export const SEVERITY_OVERRIDE_ROW = '[data-test-subj="severityOverrideRow"]'; +export const TAGS_FIELD = + '[data-test-subj="detectionEngineStepAboutRuleTags"] [data-test-subj="comboBoxInput"]'; + export const TAGS_INPUT = '[data-test-subj="detectionEngineStepAboutRuleTags"] [data-test-subj="comboBoxSearchInput"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/edit_rule.ts b/x-pack/plugins/security_solution/cypress/screens/edit_rule.ts new file mode 100644 index 0000000000000..1bf0ff34ebd94 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/screens/edit_rule.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const EDIT_SUBMIT_BUTTON = '[data-test-subj="ruleEditSubmitButton"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts index b221709966943..98fc7b06a9908 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -4,55 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -export const ABOUT_FALSE_POSITIVES = 3; +export const getDescriptionForTitle = (title: string) => + cy.get(DETAILS_TITLE).contains(title).next(DETAILS_DESCRIPTION); -export const ABOUT_INVESTIGATION_NOTES = '[data-test-subj="stepAboutDetailsNoteContent"]'; - -export const ABOUT_MITRE = 4; - -export const ABOUT_OVERRIDE_FALSE_POSITIVES = 8; - -export const ABOUT_OVERRIDE_MITRE = 10; - -export const ABOUT_OVERRIDE_NAME_OVERRIDE = 9; - -export const ABOUT_OVERRIDE_RISK = 5; +export const DETAILS_DESCRIPTION = '.euiDescriptionList__description'; +export const DETAILS_TITLE = '.euiDescriptionList__title'; -export const ABOUT_OVERRIDE_RISK_OVERRIDE = 6; - -export const ABOUT_OVERRIDE_SEVERITY_OVERRIDE = 1; - -export const ABOUT_OVERRIDE_TAGS = 12; - -export const ABOUT_OVERRIDE_TIMESTAMP_OVERRIDE = 11; - -export const ABOUT_OVERRIDE_URLS = 7; +export const ABOUT_INVESTIGATION_NOTES = '[data-test-subj="stepAboutDetailsNoteContent"]'; export const ABOUT_RULE_DESCRIPTION = '[data-test-subj=stepAboutRuleDetailsToggleDescriptionText]'; -export const ABOUT_RISK = 1; +export const ABOUT_DETAILS = + '[data-test-subj="aboutRule"] [data-test-subj="listItemColumnStepRuleDescription"]'; -export const ABOUT_SEVERITY = 0; - -export const ABOUT_STEP = '[data-test-subj="aboutRule"] .euiDescriptionList__description'; - -export const ABOUT_TAGS = 5; - -export const ABOUT_URLS = 2; - -export const ANOMALY_SCORE = 1; - -export const DEFINITION_CUSTOM_QUERY = 1; - -export const DEFINITION_THRESHOLD = 4; - -export const DEFINITION_TIMELINE = 3; - -export const DEFINITION_INDEX_PATTERNS = - '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description .euiBadge__text'; - -export const DEFINITION_STEP = - '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description'; +export const DEFINITION_DETAILS = + '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"]'; export const INVESTIGATION_NOTES_MARKDOWN = 'test markdown'; @@ -60,16 +26,13 @@ export const INVESTIGATION_NOTES_TOGGLE = 1; export const MACHINE_LEARNING_JOB_ID = '[data-test-subj="machineLearningJobId"]'; -export const MACHINE_LEARNING_JOB_STATUS = '[data-test-subj="machineLearningJobStatus" ]'; +export const MACHINE_LEARNING_JOB_STATUS = '[data-test-subj="machineLearningJobStatus"]'; export const RULE_ABOUT_DETAILS_HEADER_TOGGLE = '[data-test-subj="stepAboutDetailsToggle"]'; export const RULE_NAME_HEADER = '[data-test-subj="header-page-title"]'; -export const RULE_TYPE = 0; +export const SCHEDULE_DETAILS = + '[data-test-subj=schedule] [data-test-subj="listItemColumnStepRuleDescription"]'; export const SCHEDULE_STEP = '[data-test-subj="schedule"] .euiDescriptionList__description'; - -export const SCHEDULE_RUNS = 0; - -export const SCHEDULE_LOOPBACK = 1; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index 5ec5bb97250db..c530594508f95 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -24,6 +24,7 @@ import { SORT_RULES_BTN, THREE_HUNDRED_ROWS, EXPORT_ACTION_BTN, + EDIT_RULE_ACTION_BTN, } from '../screens/alerts_detection_rules'; export const activateRule = (rulePosition: number) => { @@ -35,6 +36,11 @@ export const changeToThreeHundredRowsPerPage = () => { cy.get(THREE_HUNDRED_ROWS).click(); }; +export const editFirstRule = () => { + cy.get(COLLAPSED_ACTION_BTN).first().click({ force: true }); + cy.get(EDIT_RULE_ACTION_BTN).click(); +}; + export const deleteFirstRule = () => { cy.get(COLLAPSED_ACTION_BTN).first().click({ force: true }); cy.get(DELETE_RULE_ACTION_BTN).click(); diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts index f26a77171462c..0daff52de7063 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts @@ -13,14 +13,17 @@ import { } from '../objects/rule'; import { ABOUT_CONTINUE_BTN, - ANOMALY_THRESHOLD_INPUT, + ABOUT_EDIT_TAB, + ACTIONS_EDIT_TAB, ADD_FALSE_POSITIVE_BTN, ADD_REFERENCE_URL_BTN, ADVANCED_SETTINGS_BTN, + ANOMALY_THRESHOLD_INPUT, COMBO_BOX_INPUT, CREATE_AND_ACTIVATE_BTN, CUSTOM_QUERY_INPUT, DEFINE_CONTINUE_BUTTON, + DEFINE_EDIT_TAB, FALSE_POSITIVES_INPUT, IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK, INPUT, @@ -32,8 +35,8 @@ import { MITRE_TACTIC, MITRE_TACTIC_DROPDOWN, MITRE_TECHNIQUES_INPUT, - RISK_INPUT, REFERENCE_URLS_INPUT, + RISK_INPUT, RISK_MAPPING_OVERRIDE_OPTION, RISK_OVERRIDE, RULE_DESCRIPTION_INPUT, @@ -41,6 +44,7 @@ import { RULE_NAME_OVERRIDE, RULE_TIMESTAMP_OVERRIDE, SCHEDULE_CONTINUE_BUTTON, + SCHEDULE_EDIT_TAB, SEVERITY_DROPDOWN, SEVERITY_MAPPING_OVERRIDE_OPTION, SEVERITY_OVERRIDE_ROW, @@ -48,8 +52,6 @@ import { THRESHOLD_FIELD_SELECTION, THRESHOLD_INPUT_AREA, THRESHOLD_TYPE, - DEFINE_EDIT_BUTTON, - ABOUT_EDIT_BUTTON, EQL_TYPE, EQL_QUERY_INPUT, } from '../screens/create_new_rule'; @@ -61,11 +63,9 @@ export const createAndActivateRule = () => { cy.get(CREATE_AND_ACTIVATE_BTN).should('not.exist'); }; -export const fillAboutRuleAndContinue = ( - rule: CustomRule | MachineLearningRule | ThresholdRule -) => { - cy.get(RULE_NAME_INPUT).type(rule.name, { force: true }); - cy.get(RULE_DESCRIPTION_INPUT).type(rule.description, { force: true }); +export const fillAboutRule = (rule: CustomRule | MachineLearningRule | ThresholdRule) => { + cy.get(RULE_NAME_INPUT).clear({ force: true }).type(rule.name, { force: true }); + cy.get(RULE_DESCRIPTION_INPUT).clear({ force: true }).type(rule.description, { force: true }); cy.get(SEVERITY_DROPDOWN).click({ force: true }); cy.get(`#${rule.severity.toLowerCase()}`).click(); @@ -79,12 +79,15 @@ export const fillAboutRuleAndContinue = ( cy.get(ADVANCED_SETTINGS_BTN).click({ force: true }); rule.referenceUrls.forEach((url, index) => { - cy.get(REFERENCE_URLS_INPUT).eq(index).type(url, { force: true }); + cy.get(REFERENCE_URLS_INPUT).eq(index).clear({ force: true }).type(url, { force: true }); cy.get(ADD_REFERENCE_URL_BTN).click({ force: true }); }); rule.falsePositivesExamples.forEach((falsePositive, index) => { - cy.get(FALSE_POSITIVES_INPUT).eq(index).type(falsePositive, { force: true }); + cy.get(FALSE_POSITIVES_INPUT) + .eq(index) + .clear({ force: true }) + .type(falsePositive, { force: true }); cy.get(ADD_FALSE_POSITIVE_BTN).click({ force: true }); }); @@ -93,14 +96,22 @@ export const fillAboutRuleAndContinue = ( cy.contains(MITRE_TACTIC, mitre.tactic).click(); mitre.techniques.forEach((technique) => { - cy.get(MITRE_TECHNIQUES_INPUT).eq(index).type(`${technique}{enter}`, { force: true }); + cy.get(MITRE_TECHNIQUES_INPUT) + .eq(index) + .clear({ force: true }) + .type(`${technique}{enter}`, { force: true }); }); cy.get(MITRE_BTN).click({ force: true }); }); - cy.get(INVESTIGATION_NOTES_TEXTAREA).type(rule.note, { force: true }); + cy.get(INVESTIGATION_NOTES_TEXTAREA).clear({ force: true }).type(rule.note, { force: true }); +}; +export const fillAboutRuleAndContinue = ( + rule: CustomRule | MachineLearningRule | ThresholdRule +) => { + fillAboutRule(rule); cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); }; @@ -179,20 +190,6 @@ export const fillDefineCustomRuleWithImportedQueryAndContinue = ( cy.get(CUSTOM_QUERY_INPUT).should('not.exist'); }; -export const expectDefineFormToRepopulateAndContinue = (rule: CustomRule) => { - cy.get(DEFINE_EDIT_BUTTON).click(); - cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', rule.customQuery); - cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); - cy.get(DEFINE_CONTINUE_BUTTON).should('not.exist'); -}; - -export const expectAboutFormToRepopulateAndContinue = (rule: CustomRule) => { - cy.get(ABOUT_EDIT_BUTTON).click(); - cy.get(RULE_NAME_INPUT).invoke('val').should('eq', rule.name); - cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); - cy.get(ABOUT_CONTINUE_BTN).should('not.exist'); -}; - export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { const thresholdField = 0; const threshold = 1; @@ -230,6 +227,22 @@ export const fillDefineMachineLearningRuleAndContinue = (rule: MachineLearningRu cy.get(MACHINE_LEARNING_DROPDOWN).should('not.exist'); }; +export const goToDefineStepTab = () => { + cy.get(DEFINE_EDIT_TAB).click({ force: true }); +}; + +export const goToAboutStepTab = () => { + cy.get(ABOUT_EDIT_TAB).click({ force: true }); +}; + +export const goToScheduleStepTab = () => { + cy.get(SCHEDULE_EDIT_TAB).click({ force: true }); +}; + +export const goToActionsStepTab = () => { + cy.get(ACTIONS_EDIT_TAB).click({ force: true }); +}; + export const selectMachineLearningRuleType = () => { cy.get(MACHINE_LEARNING_TYPE).click({ force: true }); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts new file mode 100644 index 0000000000000..690a36058ec33 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EDIT_SUBMIT_BUTTON } from '../screens/edit_rule'; + +export const saveEditedRule = () => { + cy.get(EDIT_SUBMIT_BUTTON).should('exist').click({ force: true }); + cy.get(EDIT_SUBMIT_BUTTON).should('not.exist'); +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/schedule_item_form/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/schedule_item_form/index.tsx index bb33767f4f5d5..867be1c1270e8 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/schedule_item_form/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/schedule_item_form/index.tsx @@ -145,6 +145,7 @@ export const ScheduleItem = ({ void ) => [ { + 'data-test-subj': 'editRuleAction', description: i18n.EDIT_RULE_SETTINGS, icon: 'controlsHorizontal', name: i18n.EDIT_RULE_SETTINGS, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx index 5f4fd59669103..e2772af72da06 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx @@ -118,6 +118,7 @@ const EditRulePageComponent: FC = () => { const tabs = useMemo( () => [ { + 'data-test-subj': 'edit-rule-define-tab', id: RuleStep.defineRule, name: ruleI18n.DEFINITION, disabled: rule?.immutable, @@ -140,6 +141,7 @@ const EditRulePageComponent: FC = () => { ), }, { + 'data-test-subj': 'edit-rule-about-tab', id: RuleStep.aboutRule, name: ruleI18n.ABOUT, disabled: rule?.immutable, @@ -163,6 +165,7 @@ const EditRulePageComponent: FC = () => { ), }, { + 'data-test-subj': 'edit-rule-schedule-tab', id: RuleStep.scheduleRule, name: ruleI18n.SCHEDULE, disabled: rule?.immutable, @@ -185,6 +188,7 @@ const EditRulePageComponent: FC = () => { ), }, { + 'data-test-subj': 'edit-rule-actions-tab', id: RuleStep.ruleActions, name: ruleI18n.ACTIONS, content: ( @@ -387,6 +391,7 @@ const EditRulePageComponent: FC = () => {