From 07d5f91182a1271b1d5cd87557222963efa0a6e6 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Fri, 4 Sep 2020 12:57:03 -0500 Subject: [PATCH 1/6] Add cypress test around editing a detection rule Right now this just navigates around and verifies that the form is correctly repopulated; next step will be to modify/asset some changes. --- .../alerts_detection_rules_custom.spec.ts | 21 ++++++- .../security_solution/cypress/objects/rule.ts | 25 ++++++++ .../cypress/screens/alerts_detection_rules.ts | 2 + .../cypress/screens/create_new_rule.ts | 29 +++++++++ .../cypress/tasks/alerts_detection_rules.ts | 6 ++ .../cypress/tasks/create_new_rule.ts | 59 ++++++++++++++++-- .../rules/schedule_item_form/index.tsx | 2 + .../detection_engine/rules/all/columns.tsx | 1 + .../detection_engine/rules/edit/index.tsx | 4 ++ .../es_archives/custom_rules/data.json.gz | Bin 2885 -> 2975 bytes 10 files changed, 144 insertions(+), 5 deletions(-) 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..5b125b9028c0b 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, @@ -53,6 +53,7 @@ import { selectNumberOfRules, waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, + editFirstRule, } from '../tasks/alerts_detection_rules'; import { createAndActivateRule, @@ -60,11 +61,18 @@ import { fillDefineCustomRuleWithImportedQueryAndContinue, expectDefineFormToRepopulateAndContinue, expectAboutFormToRepopulateAndContinue, + expectDefineStepForm, + goToAboutStepTab, + expectAboutStepForm, + expectScheduleStepForm, + goToScheduleStepTab, + goToActionsStepTab, } 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 } from '../screens/create_new_rule'; describe('Detection rules, custom', () => { before(() => { @@ -233,4 +241,15 @@ describe('Deletes custom rules', () => { .should('eql', `Custom rules (${expectedNumberOfRulesAfterDeletion})`); }); }); + + it('Allows a rule to be edited', () => { + editFirstRule(); + expectDefineStepForm(existingRule); + goToAboutStepTab(); + expectAboutStepForm(existingRule); + goToScheduleStepTab(); + expectScheduleStepForm(existingRule); + goToActionsStepTab(); + cy.get(ACTIONS_THROTTLE_INPUT).invoke('val').should('eql', 'no_actions'); + }); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index df6b792296f9d..074713daf4251 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 397d0c0142179..cc5076fcaa314 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 IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK = '[data-test-subj="importQueryFromSavedTimeline"]'; @@ -77,6 +89,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"]'; @@ -84,6 +110,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/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index 79756621ef502..3afc6e34ea9f8 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 3fa300ce9d8d0..e03c93f4b82d5 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,20 @@ import { } from '../objects/rule'; import { ABOUT_CONTINUE_BTN, - ANOMALY_THRESHOLD_INPUT, + ABOUT_EDIT_BUTTON, + 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_BUTTON, + DEFINE_EDIT_TAB, + DEFINE_INDEX_INPUT, FALSE_POSITIVES_INPUT, IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK, INPUT, @@ -32,8 +38,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 +47,9 @@ import { RULE_NAME_OVERRIDE, RULE_TIMESTAMP_OVERRIDE, SCHEDULE_CONTINUE_BUTTON, + SCHEDULE_EDIT_TAB, + SCHEDULE_INTERVAL_AMOUNT_INPUT, + SCHEDULE_INTERVAL_UNITS_INPUT, SEVERITY_DROPDOWN, SEVERITY_MAPPING_OVERRIDE_OPTION, SEVERITY_OVERRIDE_ROW, @@ -48,8 +57,7 @@ import { THRESHOLD_FIELD_SELECTION, THRESHOLD_INPUT_AREA, THRESHOLD_TYPE, - DEFINE_EDIT_BUTTON, - ABOUT_EDIT_BUTTON, + TAGS_FIELD, } from '../screens/create_new_rule'; import { TIMELINE } from '../screens/timeline'; @@ -191,6 +199,33 @@ export const expectAboutFormToRepopulateAndContinue = (rule: CustomRule) => { cy.get(ABOUT_CONTINUE_BTN).should('not.exist'); }; +export const expectDefineStepForm = (rule: CustomRule) => { + cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', rule.customQuery); + if (rule.index && rule.index.length > 0) { + cy.get(DEFINE_INDEX_INPUT).invoke('text').should('eq', rule.index.join('')); + } +}; + +export const expectAboutStepForm = (rule: CustomRule) => { + cy.get(RULE_NAME_INPUT).invoke('val').should('eql', rule.name); + cy.get(RULE_DESCRIPTION_INPUT).invoke('text').should('eql', rule.description); + cy.get(TAGS_FIELD).invoke('text').should('eql', rule.tags.join('')); + + cy.get(SEVERITY_DROPDOWN).invoke('text').should('eql', rule.severity); + cy.get(RISK_INPUT).invoke('val').should('eql', rule.riskScore); +}; + +export const expectScheduleStepForm = (rule: CustomRule) => { + const intervalParts = rule.interval && rule.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'); + } +}; + export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { const thresholdField = 0; const threshold = 1; @@ -220,6 +255,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/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..dcc4fbc26993a 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: ( diff --git a/x-pack/test/security_solution_cypress/es_archives/custom_rules/data.json.gz b/x-pack/test/security_solution_cypress/es_archives/custom_rules/data.json.gz index 8f31a1a4690539db8cb3d5e34354ac4e01ead88f..60c0550481d1e6008e70087e922c04380c5bfc7b 100644 GIT binary patch literal 2975 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 Date: Tue, 8 Sep 2020 22:34:52 -0500 Subject: [PATCH 2/6] Add assertions for editing a rule We already were asserting on the population of the Edit form after creation; this additionally makes modifications, saves them, and asserts the resulting values on the Rule Details page. --- .../alerts_detection_rules_custom.spec.ts | 14 ++++ .../cypress/screens/edit_rule.ts | 7 ++ .../cypress/tasks/create_new_rule.ts | 27 ++++--- .../cypress/tasks/edit_rule.ts | 73 +++++++++++++++++++ .../detection_engine/rules/edit/index.tsx | 1 + 5 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 x-pack/plugins/security_solution/cypress/screens/edit_rule.ts create mode 100644 x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts 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 5b125b9028c0b..bd961ca618ce3 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 @@ -67,12 +67,14 @@ import { expectScheduleStepForm, 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 } from '../screens/create_new_rule'; +import { saveEditedRule, expectRuleDetails } from '../tasks/edit_rule'; describe('Detection rules, custom', () => { before(() => { @@ -251,5 +253,17 @@ describe('Deletes custom rules', () => { expectScheduleStepForm(existingRule); 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(); + expectRuleDetails(editedRule); }); }); 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/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts index e03c93f4b82d5..fad014f4fabd2 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 @@ -67,11 +67,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(); @@ -85,12 +83,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 }); }); @@ -99,14 +100,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 }); }; 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..b7d088a2571d9 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts @@ -0,0 +1,73 @@ +/* + * 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 { CustomRule } from '../objects/rule'; +import { + RULE_NAME_HEADER, + ABOUT_RULE_DESCRIPTION, + ABOUT_STEP, + ABOUT_SEVERITY, + ABOUT_RISK, + RULE_ABOUT_DETAILS_HEADER_TOGGLE, + INVESTIGATION_NOTES_TOGGLE, + ABOUT_INVESTIGATION_NOTES, + INVESTIGATION_NOTES_MARKDOWN, + DEFINITION_INDEX_PATTERNS, + DEFINITION_STEP, + DEFINITION_CUSTOM_QUERY, + DEFINITION_TIMELINE, + SCHEDULE_STEP, + SCHEDULE_RUNS, + SCHEDULE_LOOPBACK, +} from '../screens/rule_details'; +import { EDIT_SUBMIT_BUTTON } from '../screens/edit_rule'; + +export const expectRuleDetails = (rule: CustomRule) => { + const expectedTags = rule.tags.join(''); + const expectedIndexPatterns = + rule.index && rule.index.length + ? rule.index + : [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ]; + + cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${rule.name} Beta`); + + cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', rule.description); + cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', rule.severity); + cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', rule.riskScore); + cy.get(ABOUT_STEP).eq(2).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', rule.note); + + 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_STEP) + .eq(DEFINITION_CUSTOM_QUERY) + .invoke('text') + .should('eql', `${rule.customQuery} `); + cy.get(DEFINITION_STEP).eq(2).invoke('text').should('eql', 'Query'); + cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); + + if (rule.interval) { + cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', rule.interval); + } +}; + +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/pages/detection_engine/rules/edit/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx index dcc4fbc26993a..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 @@ -391,6 +391,7 @@ const EditRulePageComponent: FC = () => { Date: Wed, 9 Sep 2020 12:59:10 -0500 Subject: [PATCH 3/6] Remove unused imports --- x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts index b7d088a2571d9..d49cc75486ac1 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts @@ -14,14 +14,12 @@ import { RULE_ABOUT_DETAILS_HEADER_TOGGLE, INVESTIGATION_NOTES_TOGGLE, ABOUT_INVESTIGATION_NOTES, - INVESTIGATION_NOTES_MARKDOWN, DEFINITION_INDEX_PATTERNS, DEFINITION_STEP, DEFINITION_CUSTOM_QUERY, DEFINITION_TIMELINE, SCHEDULE_STEP, SCHEDULE_RUNS, - SCHEDULE_LOOPBACK, } from '../screens/rule_details'; import { EDIT_SUBMIT_BUTTON } from '../screens/edit_rule'; From b174f442867774ebe0e41cd59dd9baa6c4014e45 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Thu, 10 Sep 2020 17:09:24 -0500 Subject: [PATCH 4/6] Inline our cypress expectations So that expectation failures are less obfuscated, the decision was previously made to abstract user navigation into functions, but to leave expectations directly within the test body. --- .../alerts_detection_rules_custom.spec.ts | 111 ++++++++++++++++-- .../cypress/tasks/create_new_rule.ts | 47 -------- .../cypress/tasks/edit_rule.ts | 59 ---------- 3 files changed, 98 insertions(+), 119 deletions(-) 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 bd961ca618ce3..37eadfb95b753 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 @@ -59,12 +59,7 @@ import { createAndActivateRule, fillAboutRuleAndContinue, fillDefineCustomRuleWithImportedQueryAndContinue, - expectDefineFormToRepopulateAndContinue, - expectAboutFormToRepopulateAndContinue, - expectDefineStepForm, goToAboutStepTab, - expectAboutStepForm, - expectScheduleStepForm, goToScheduleStepTab, goToActionsStepTab, fillAboutRule, @@ -73,8 +68,23 @@ import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; -import { ACTIONS_THROTTLE_INPUT } from '../screens/create_new_rule'; -import { saveEditedRule, expectRuleDetails } from '../tasks/edit_rule'; +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(() => { @@ -94,8 +104,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)'); @@ -246,12 +267,37 @@ describe('Deletes custom rules', () => { it('Allows a rule to be edited', () => { editFirstRule(); - expectDefineStepForm(existingRule); + + // 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(); - expectAboutStepForm(existingRule); + + // 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(); - expectScheduleStepForm(existingRule); + + // 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(); @@ -264,6 +310,45 @@ describe('Deletes custom rules', () => { fillAboutRule(editedRule); saveEditedRule(); - expectRuleDetails(editedRule); + + 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_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', editedRule.severity); + cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', editedRule.riskScore); + cy.get(ABOUT_STEP).eq(2).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_INDEX_PATTERNS).then((patterns) => { + cy.wrap(patterns).each((pattern, index) => { + cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]); + }); + }); + cy.get(DEFINITION_STEP) + .eq(DEFINITION_CUSTOM_QUERY) + .invoke('text') + .should('eql', `${editedRule.customQuery} `); + cy.get(DEFINITION_STEP).eq(2).invoke('text').should('eql', 'Query'); + cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); + + if (editedRule.interval) { + cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', editedRule.interval); + } }); }); 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 fad014f4fabd2..89c6ec61fbbd7 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,7 +13,6 @@ import { } from '../objects/rule'; import { ABOUT_CONTINUE_BTN, - ABOUT_EDIT_BUTTON, ABOUT_EDIT_TAB, ACTIONS_EDIT_TAB, ADD_FALSE_POSITIVE_BTN, @@ -24,9 +23,7 @@ import { CREATE_AND_ACTIVATE_BTN, CUSTOM_QUERY_INPUT, DEFINE_CONTINUE_BUTTON, - DEFINE_EDIT_BUTTON, DEFINE_EDIT_TAB, - DEFINE_INDEX_INPUT, FALSE_POSITIVES_INPUT, IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK, INPUT, @@ -48,8 +45,6 @@ import { RULE_TIMESTAMP_OVERRIDE, SCHEDULE_CONTINUE_BUTTON, SCHEDULE_EDIT_TAB, - SCHEDULE_INTERVAL_AMOUNT_INPUT, - SCHEDULE_INTERVAL_UNITS_INPUT, SEVERITY_DROPDOWN, SEVERITY_MAPPING_OVERRIDE_OPTION, SEVERITY_OVERRIDE_ROW, @@ -57,7 +52,6 @@ import { THRESHOLD_FIELD_SELECTION, THRESHOLD_INPUT_AREA, THRESHOLD_TYPE, - TAGS_FIELD, } from '../screens/create_new_rule'; import { TIMELINE } from '../screens/timeline'; @@ -194,47 +188,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 expectDefineStepForm = (rule: CustomRule) => { - cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', rule.customQuery); - if (rule.index && rule.index.length > 0) { - cy.get(DEFINE_INDEX_INPUT).invoke('text').should('eq', rule.index.join('')); - } -}; - -export const expectAboutStepForm = (rule: CustomRule) => { - cy.get(RULE_NAME_INPUT).invoke('val').should('eql', rule.name); - cy.get(RULE_DESCRIPTION_INPUT).invoke('text').should('eql', rule.description); - cy.get(TAGS_FIELD).invoke('text').should('eql', rule.tags.join('')); - - cy.get(SEVERITY_DROPDOWN).invoke('text').should('eql', rule.severity); - cy.get(RISK_INPUT).invoke('val').should('eql', rule.riskScore); -}; - -export const expectScheduleStepForm = (rule: CustomRule) => { - const intervalParts = rule.interval && rule.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'); - } -}; - export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { const thresholdField = 0; const threshold = 1; diff --git a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts index d49cc75486ac1..690a36058ec33 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts @@ -4,67 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CustomRule } from '../objects/rule'; -import { - RULE_NAME_HEADER, - ABOUT_RULE_DESCRIPTION, - ABOUT_STEP, - ABOUT_SEVERITY, - ABOUT_RISK, - RULE_ABOUT_DETAILS_HEADER_TOGGLE, - INVESTIGATION_NOTES_TOGGLE, - ABOUT_INVESTIGATION_NOTES, - DEFINITION_INDEX_PATTERNS, - DEFINITION_STEP, - DEFINITION_CUSTOM_QUERY, - DEFINITION_TIMELINE, - SCHEDULE_STEP, - SCHEDULE_RUNS, -} from '../screens/rule_details'; import { EDIT_SUBMIT_BUTTON } from '../screens/edit_rule'; -export const expectRuleDetails = (rule: CustomRule) => { - const expectedTags = rule.tags.join(''); - const expectedIndexPatterns = - rule.index && rule.index.length - ? rule.index - : [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ]; - - cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${rule.name} Beta`); - - cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', rule.description); - cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', rule.severity); - cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', rule.riskScore); - cy.get(ABOUT_STEP).eq(2).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', rule.note); - - 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_STEP) - .eq(DEFINITION_CUSTOM_QUERY) - .invoke('text') - .should('eql', `${rule.customQuery} `); - cy.get(DEFINITION_STEP).eq(2).invoke('text').should('eql', 'Query'); - cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); - - if (rule.interval) { - cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', rule.interval); - } -}; - export const saveEditedRule = () => { cy.get(EDIT_SUBMIT_BUTTON).should('exist').click({ force: true }); cy.get(EDIT_SUBMIT_BUTTON).should('not.exist'); From 569980b2928e10c67bcdff76761e41a1cbc1b67b Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Thu, 10 Sep 2020 20:34:18 -0500 Subject: [PATCH 5/6] Dynamically assert Rule Details based on titles Rule Details are unfortunately unstructured: they're an array of
s and
s without any hierarchy. To address this, tests were previously hardcoding the order of these fields, and assertions were performed by querying for all
s and then indexing with the hardcoded number (e.g. ABOUT_FALSE_POSITIVES). However, in addition to being unstructured, these fields are also _dynamic_, and will be present/absent depending on the data of the given rule. Thus, we started needing multiple orderings for the different combinations of rule fields/rule types. In the absence of refactoring how we build rule details, I'm introducing a simple helper function to fetch the relevant
by the corresponding
s text. This should be more robust to change and more declarative. --- .../alerts_detection_rules_custom.spec.ts | 94 +++++++-------- .../alerts_detection_rules_ml.spec.ts | 81 ++++++------- .../alerts_detection_rules_override.spec.ts | 112 ++++++++---------- .../alerts_detection_rules_threshold.spec.ts | 77 ++++++------ .../cypress/screens/rule_details.ts | 61 ++-------- 5 files changed, 177 insertions(+), 248 deletions(-) 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 37eadfb95b753..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 @@ -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 { @@ -173,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'); }); }); @@ -328,27 +321,30 @@ describe('Deletes custom rules', () => { 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_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', editedRule.severity); - cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', editedRule.riskScore); - cy.get(ABOUT_STEP).eq(2).invoke('text').should('eql', expectedTags); + 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_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', `${editedRule.customQuery} `); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); }); - cy.get(DEFINITION_STEP) - .eq(DEFINITION_CUSTOM_QUERY) - .invoke('text') - .should('eql', `${editedRule.customQuery} `); - cy.get(DEFINITION_STEP).eq(2).invoke('text').should('eql', 'Query'); - cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); if (editedRule.interval) { - cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', 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_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/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; From 85f55e9ce928d2ab60c2380c6696f1bf5d36ac58 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Tue, 15 Sep 2020 13:40:44 -0500 Subject: [PATCH 6/6] Fix bad merge conflict Lots of these variables no longer exist upstream and this new test needed to be refactored. --- .../alerts_detection_rules_eql.spec.ts | 64 ++++++++----------- 1 file changed, 28 insertions(+), 36 deletions(-) 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'); }); });