From 14535262b7dc222af2b499f1109b455b1a96cb4e Mon Sep 17 00:00:00 2001 From: Maxim Palenov Date: Sat, 26 Aug 2023 11:40:18 +0200 Subject: [PATCH] [8.10] [Security Solution] Reduce Rules Management e2e flakiness (#164099) (#164903) # Backport This will backport the following commits from `main` to `8.10`: - [[Security Solution] Reduce Rules Management e2e flakiness (#164099)](https://github.com/elastic/kibana/pull/164099) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) --- ...t_rules_install_update_authorization.cy.ts | 4 - ..._rules_install_update_error_handling.cy.ts | 8 +- ...built_rules_install_update_workflows.cy.ts | 9 +- .../prebuilt_rules_management.cy.ts | 16 +- .../prebuilt_rules_notifications.cy.ts | 32 +- .../rule_creation/custom_query_rule.cy.ts | 192 +++--- .../custom_query_rule_data_view.cy.ts | 9 +- .../rule_creation/indicator_match_rule.cy.ts | 4 +- .../authorization/all_rules_read_only.cy.ts | 9 +- .../bulk_actions/bulk_duplicate_rules.cy.ts | 88 ++- .../bulk_actions/bulk_edit_rules.cy.ts | 563 ++++++++++-------- .../bulk_edit_rules_actions.cy.ts | 181 ++++-- .../bulk_edit_rules_data_view.cy.ts | 308 ++++++---- .../import_export/export_rule.cy.ts | 6 +- .../rule_actions/snoozing/rule_snoozing.cy.ts | 53 +- .../rules_table_auto_refresh.cy.ts | 11 +- .../rules_table/rules_table_filtering.cy.ts | 16 +- .../rules_table/rules_table_links.cy.ts | 2 +- .../rules_table/rules_table_selection.cy.ts | 8 +- .../rules_table/rules_table_sorting.cy.ts | 19 +- .../rule_details_flow/read_only_view.cy.ts | 31 +- .../alert_details_right_panel_json_tab.cy.ts | 3 +- .../cypress/screens/alerts_detection_rules.ts | 3 - .../cypress/screens/common/page.ts | 2 + .../cypress/screens/rule_details.ts | 3 +- .../cypress/tasks/alerts_detection_rules.ts | 97 ++- .../cypress/tasks/api_calls/prebuilt_rules.ts | 51 +- .../cypress/tasks/common.ts | 12 +- .../cypress/tasks/login.ts | 13 +- 29 files changed, 935 insertions(+), 818 deletions(-) diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_authorization.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_authorization.cy.ts index 8f97d240488e..61b663723adf 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_authorization.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_authorization.cy.ts @@ -14,7 +14,6 @@ import { ROLES } from '@kbn/security-solution-plugin/common/test'; import { tag } from '../../../tags'; import { createRuleAssetSavedObject } from '../../../helpers/rules'; -import { waitForRulesTableToBeLoaded } from '../../../tasks/alerts_detection_rules'; import { createAndInstallMockedPrebuiltRules } from '../../../tasks/api_calls/prebuilt_rules'; import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common'; import { login, waitForPageWithoutDateRange } from '../../../tasks/login'; @@ -66,7 +65,6 @@ describe( resetRulesTableState(); deleteAlertsAndRules(); cy.task('esArchiverResetKibana'); - waitForRulesTableToBeLoaded(); createAndInstallMockedPrebuiltRules({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] }); }); @@ -83,7 +81,6 @@ describe( // Now login with read-only user in preparation for test createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2], installToKibana: false }); loadPageAsReadOnlyUser(SECURITY_DETECTIONS_RULES_URL); - waitForRulesTableToBeLoaded(); }); it('should not be able to install prebuilt rules', () => { @@ -111,7 +108,6 @@ describe( }); // Now login with read-only user in preparation for test loadPageAsReadOnlyUser(SECURITY_DETECTIONS_RULES_URL); - waitForRulesTableToBeLoaded(); }); it('should not be able to upgrade prebuilt rules', () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_error_handling.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_error_handling.cy.ts index a5be1d78c537..583bf384032f 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_error_handling.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_error_handling.cy.ts @@ -8,11 +8,9 @@ import { tag } from '../../../tags'; import { createRuleAssetSavedObject } from '../../../helpers/rules'; -import { waitForRulesTableToBeLoaded } from '../../../tasks/alerts_detection_rules'; import { createAndInstallMockedPrebuiltRules } from '../../../tasks/api_calls/prebuilt_rules'; import { resetRulesTableState, deleteAlertsAndRules, reload } from '../../../tasks/common'; -import { login, visitWithoutDateRange } from '../../../tasks/login'; -import { SECURITY_DETECTIONS_RULES_URL } from '../../../urls/navigation'; +import { login, visitSecurityDetectionRulesPage } from '../../../tasks/login'; import { addElasticRulesButtonClick, assertRuleAvailableForInstallAndInstallOne, @@ -36,7 +34,7 @@ describe( deleteAlertsAndRules(); cy.task('esArchiverResetKibana'); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + visitSecurityDetectionRulesPage(); }); describe('Installation of prebuilt rules - Should fail gracefully with toast error message when', () => { @@ -50,7 +48,6 @@ describe( }); beforeEach(() => { createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2], installToKibana: false }); - waitForRulesTableToBeLoaded(); }); it('installing prebuilt rules one by one', () => { @@ -114,7 +111,6 @@ describe( rules: [UPDATED_RULE_1, UPDATED_RULE_2], installToKibana: false, }); - waitForRulesTableToBeLoaded(); reload(); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_workflows.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_workflows.cy.ts index 8863dbf2f123..d9b821479013 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_workflows.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_workflows.cy.ts @@ -20,14 +20,12 @@ import { SELECT_ALL_RULES_ON_PAGE_CHECKBOX, TOASTER, } from '../../../screens/alerts_detection_rules'; -import { waitForRulesTableToBeLoaded } from '../../../tasks/alerts_detection_rules'; import { getRuleAssets, createAndInstallMockedPrebuiltRules, } from '../../../tasks/api_calls/prebuilt_rules'; import { resetRulesTableState, deleteAlertsAndRules, reload } from '../../../tasks/common'; -import { login, visitWithoutDateRange } from '../../../tasks/login'; -import { SECURITY_DETECTIONS_RULES_URL } from '../../../urls/navigation'; +import { login, visitSecurityDetectionRulesPage } from '../../../tasks/login'; import { addElasticRulesButtonClick, assertRuleAvailableForInstallAndInstallOne, @@ -51,7 +49,7 @@ describe( deleteAlertsAndRules(); cy.task('esArchiverResetKibana'); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + visitSecurityDetectionRulesPage(); }); describe('Installation of prebuilt rules package via Fleet', () => { @@ -60,7 +58,6 @@ describe( cy.intercept('POST', '/api/fleet/epm/packages/security_detection_engine/*').as( 'installPackage' ); - waitForRulesTableToBeLoaded(); }); it('should install package from Fleet in the background', () => { @@ -150,7 +147,6 @@ describe( }); beforeEach(() => { createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2], installToKibana: false }); - waitForRulesTableToBeLoaded(); cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform').as( 'installPrebuiltRules' ); @@ -232,7 +228,6 @@ describe( rules: [UPDATED_RULE_1, UPDATED_RULE_2], installToKibana: false, }); - waitForRulesTableToBeLoaded(); reload(); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_management.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_management.cy.ts index 128f8d27b5da..fba4a5ab6ec7 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_management.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_management.cy.ts @@ -14,16 +14,15 @@ import { ADD_ELASTIC_RULES_BTN, RULES_EMPTY_PROMPT, RULES_MONITORING_TAB, - RULES_ROW, - RULES_MANAGEMENT_TABLE, RULE_SWITCH, SELECT_ALL_RULES_ON_PAGE_CHECKBOX, INSTALL_ALL_RULES_BUTTON, } from '../../../screens/alerts_detection_rules'; import { deleteFirstRule, + getRulesManagementTableRows, selectAllRules, - selectNumberOfRules, + selectRulesByName, waitForPrebuiltDetectionRulesToBeLoaded, waitForRuleToUpdate, } from '../../../tasks/alerts_detection_rules'; @@ -69,7 +68,7 @@ describe('Prebuilt rules', { tags: [tag.ESS, tag.SERVERLESS] }, () => { describe('Alerts rules, prebuilt rules', () => { it('Loads prebuilt rules', () => { // Check that the rules table contains rules - cy.get(RULES_MANAGEMENT_TABLE).find(RULES_ROW).should('have.length.gte', 1); + getRulesManagementTableRows().should('have.length.gte', 1); // Check the correct count of prebuilt rules is displayed getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => { @@ -111,8 +110,7 @@ describe('Prebuilt rules', { tags: [tag.ESS, tag.SERVERLESS] }, () => { }); it('Does not allow to delete one rule when more than one is selected', () => { - const numberOfRulesToBeSelected = 2; - selectNumberOfRules(numberOfRulesToBeSelected); + selectAllRules(); cy.get(COLLAPSED_ACTION_BTN).each((collapsedItemActionBtn) => { cy.wrap(collapsedItemActionBtn).should('have.attr', 'disabled'); @@ -153,16 +151,16 @@ describe('Prebuilt rules', { tags: [tag.ESS, tag.SERVERLESS] }, () => { it('Deletes and recovers more than one rule', () => { getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => { - const numberOfRulesToBeSelected = 2; + const rulesToDelete = ['Test rule 1', 'Test rule 2'] as const; const expectedNumberOfRulesAfterDeletion = availablePrebuiltRulesCount - 2; const expectedNumberOfRulesAfterRecovering = availablePrebuiltRulesCount; - selectNumberOfRules(numberOfRulesToBeSelected); + selectRulesByName(rulesToDelete); deleteSelectedRules(); cy.get(ADD_ELASTIC_RULES_BTN).should( 'have.text', - `Add Elastic rules${numberOfRulesToBeSelected}` + `Add Elastic rules${rulesToDelete.length}` ); cy.get(ELASTIC_RULES_BTN).should( 'have.text', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_notifications.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_notifications.cy.ts index abeaee082055..8371d3c9164d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_notifications.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_notifications.cy.ts @@ -8,11 +8,12 @@ import { tag } from '../../../tags'; import { createRuleAssetSavedObject } from '../../../helpers/rules'; -import { ADD_ELASTIC_RULES_BTN, RULES_UPDATES_TAB } from '../../../screens/alerts_detection_rules'; import { - deleteFirstRule, - waitForRulesTableToBeLoaded, -} from '../../../tasks/alerts_detection_rules'; + ADD_ELASTIC_RULES_BTN, + ADD_ELASTIC_RULES_EMPTY_PROMPT_BTN, + RULES_UPDATES_TAB, +} from '../../../screens/alerts_detection_rules'; +import { deleteFirstRule } from '../../../tasks/alerts_detection_rules'; import { installAllPrebuiltRulesRequest, createAndInstallMockedPrebuiltRules, @@ -23,8 +24,7 @@ import { reload, deletePrebuiltRulesAssets, } from '../../../tasks/common'; -import { login, visitWithoutDateRange } from '../../../tasks/login'; -import { SECURITY_DETECTIONS_RULES_URL } from '../../../urls/navigation'; +import { login, visitSecurityDetectionRulesPage } from '../../../tasks/login'; const RULE_1 = createRuleAssetSavedObject({ name: 'Test rule 1', @@ -45,8 +45,10 @@ describe( describe('No notifications', () => { it('should NOT display install or update notifications when no prebuilt assets and no rules are installed', () => { - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); - waitForRulesTableToBeLoaded(); + visitSecurityDetectionRulesPage(); + + cy.get(ADD_ELASTIC_RULES_EMPTY_PROMPT_BTN).should('be.visible'); + // TODO: test plan asserts "should NOT see a CTA to install prebuilt rules" // but current behavior is to always show the CTA, even with no prebuilt rule assets installed // Update that behaviour and then update this test. @@ -55,8 +57,7 @@ describe( it('should NOT display install or update notifications when latest rules are installed', () => { createAndInstallMockedPrebuiltRules({ rules: [RULE_1], installToKibana: true }); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); - waitForRulesTableToBeLoaded(); + visitSecurityDetectionRulesPage(); /* Assert that there are no installation or update notifications */ /* Add Elastic Rules button should not contain a number badge */ @@ -73,7 +74,7 @@ describe( describe('Rules installation notification when no rules have been installed', () => { beforeEach(() => { - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + visitSecurityDetectionRulesPage(); }); it('should notify user about prebuilt rules available for installation', () => { @@ -101,8 +102,7 @@ describe( rules: [RULE_2, RULE_3], installToKibana: false, }); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); - waitForRulesTableToBeLoaded(); + visitSecurityDetectionRulesPage(); }); }); @@ -139,8 +139,7 @@ describe( version: 2, }); createAndInstallMockedPrebuiltRules({ rules: [UPDATED_RULE], installToKibana: false }); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); - waitForRulesTableToBeLoaded(); + visitSecurityDetectionRulesPage(); reload(); }); }); @@ -174,8 +173,7 @@ describe( rules: [RULE_2, UPDATED_RULE], installToKibana: false, }); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); - waitForRulesTableToBeLoaded(); + visitSecurityDetectionRulesPage(); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule.cy.ts index 5658a0d4aee3..1b5efe200c3c 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule.cy.ts @@ -21,7 +21,6 @@ import { CUSTOM_RULES_BTN, RISK_SCORE, RULE_NAME, - RULES_ROW, RULES_MANAGEMENT_TABLE, RULE_SWITCH, SEVERITY, @@ -77,13 +76,16 @@ import { deleteFirstRule, deleteRuleFromDetailsPage, editFirstRule, + expectManagementTableRules, + getRulesManagementTableRows, goToRuleDetails, - selectNumberOfRules, + goToTheRuleDetailsOf, + selectRulesByName, } from '../../../tasks/alerts_detection_rules'; import { deleteSelectedRules } from '../../../tasks/rules_bulk_actions'; import { createRule } from '../../../tasks/api_calls/rules'; import { createTimeline } from '../../../tasks/api_calls/timelines'; -import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../../tasks/common'; +import { deleteAlertsAndRules, deleteConnectors } from '../../../tasks/common'; import { addEmailConnectorAndRuleAction } from '../../../tasks/common/rule_actions'; import { createAndEnableRule, @@ -109,21 +111,18 @@ import { waitForTheRuleToBeExecuted, } from '../../../tasks/create_new_rule'; import { saveEditedRule } from '../../../tasks/edit_rule'; -import { login, visit } from '../../../tasks/login'; +import { login, visit, visitSecurityDetectionRulesPage } from '../../../tasks/login'; import { enablesRule, getDetails } from '../../../tasks/rule_details'; -import { RULE_CREATION, DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation'; +import { RULE_CREATION } from '../../../urls/navigation'; describe('Custom query rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => { - before(() => { - cleanKibana(); + beforeEach(() => { + deleteAlertsAndRules(); }); describe('Custom detection rules creation', () => { - const expectedNumberOfRules = 1; - beforeEach(() => { - deleteAlertsAndRules(); createTimeline(getTimeline()) .then((response) => { return response.body.data.persistTimeline.timeline.savedObjectId; @@ -140,7 +139,7 @@ describe('Custom query rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () cy.get(DEFINE_CONTINUE_BUTTON).click(); cy.log('Filling about section'); - fillRuleName(); + fillRuleName('Test Rule'); fillDescription(); fillSeverity(); fillRiskScore(); @@ -174,7 +173,7 @@ describe('Custom query rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); cy.log('Asserting rule view in rules list'); - cy.get(RULES_MANAGEMENT_TABLE).find(RULES_ROW).should('have.length', expectedNumberOfRules); + expectManagementTableRules(['Test Rule']); cy.get(RULE_NAME).should('have.text', ruleFields.ruleName); cy.get(RISK_SCORE).should('have.text', ruleFields.riskScore); cy.get(SEVERITY) @@ -241,109 +240,104 @@ describe('Custom query rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () describe('Custom detection rules deletion and edition', () => { context('Deletion', () => { beforeEach(() => { - deleteAlertsAndRules(); - createRule(getNewRule({ rule_id: 'rule1', enabled: true, max_signals: 500 })); - createRule(getNewOverrideRule({ rule_id: 'rule2', enabled: true, max_signals: 500 })); - createRule(getExistingRule({ rule_id: 'rule3', enabled: true })); + createRule( + getNewRule({ rule_id: 'rule1', name: 'New Rule Test', enabled: false, max_signals: 500 }) + ); + createRule( + getNewOverrideRule({ + rule_id: 'rule2', + name: 'Override Rule', + enabled: false, + max_signals: 500, + }) + ); + createRule(getExistingRule({ rule_id: 'rule3', name: 'Rule 1', enabled: false })); login(); - visit(DETECTIONS_RULE_MANAGEMENT_URL); + visitSecurityDetectionRulesPage(); }); it('Deletes one rule', () => { - cy.get(RULES_MANAGEMENT_TABLE) - .find(RULES_ROW) - .then((rules) => { - const initialNumberOfRules = rules.length; - const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1; + getRulesManagementTableRows().then((rules) => { + const initialNumberOfRules = rules.length; + const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1; - cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => { - const numberOfRules = body.data.length; - expect(numberOfRules).to.eql(initialNumberOfRules); - }); + cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => { + const numberOfRules = body.data.length; + expect(numberOfRules).to.eql(initialNumberOfRules); + }); - deleteFirstRule(); + deleteFirstRule(); - cy.get(RULES_MANAGEMENT_TABLE) - .find(RULES_ROW) - .should('have.length', expectedNumberOfRulesAfterDeletion); - cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => { - const numberOfRules = body.data.length; - expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion); - }); - cy.get(CUSTOM_RULES_BTN).should( - 'have.text', - `Custom rules (${expectedNumberOfRulesAfterDeletion})` - ); + getRulesManagementTableRows().should('have.length', expectedNumberOfRulesAfterDeletion); + cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => { + const numberOfRules = body.data.length; + expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion); }); + cy.get(CUSTOM_RULES_BTN).should( + 'have.text', + `Custom rules (${expectedNumberOfRulesAfterDeletion})` + ); + }); }); it('Deletes more than one rule', () => { - cy.get(RULES_MANAGEMENT_TABLE) - .find(RULES_ROW) - .then((rules) => { - const initialNumberOfRules = rules.length; - const numberOfRulesToBeDeleted = 2; - const expectedNumberOfRulesAfterDeletion = - initialNumberOfRules - numberOfRulesToBeDeleted; - - selectNumberOfRules(numberOfRulesToBeDeleted); - deleteSelectedRules(); - - cy.get(RULES_MANAGEMENT_TABLE) - .get(RULES_ROW) - .first() - .within(() => { - cy.get(RULE_SWITCH).should('not.exist'); - }); - - cy.get(RULES_MANAGEMENT_TABLE) - .find(RULES_ROW) - .should('have.length', expectedNumberOfRulesAfterDeletion); + getRulesManagementTableRows().then((rules) => { + const rulesToDelete = ['New Rule Test', 'Override Rule'] as const; + const initialNumberOfRules = rules.length; + const numberOfRulesToBeDeleted = 2; + const expectedNumberOfRulesAfterDeletion = + initialNumberOfRules - numberOfRulesToBeDeleted; + + selectRulesByName(rulesToDelete); + deleteSelectedRules(); + + getRulesManagementTableRows() + .first() + .within(() => { + cy.get(RULE_SWITCH).should('not.exist'); + }); + + getRulesManagementTableRows().should('have.length', expectedNumberOfRulesAfterDeletion); + cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => { + const numberOfRules = body.data.length; + expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion); + }); + getRulesManagementTableRows() + .first() + .within(() => { + cy.get(RULE_SWITCH).should('exist'); + }); + cy.get(CUSTOM_RULES_BTN).should( + 'have.text', + `Custom rules (${expectedNumberOfRulesAfterDeletion})` + ); + }); + }); + + it('Deletes one rule from detail page', () => { + getRulesManagementTableRows().then((rules) => { + const initialNumberOfRules = rules.length; + const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1; + + goToTheRuleDetailsOf('New Rule Test'); + cy.intercept('POST', '/api/detection_engine/rules/_bulk_delete').as('deleteRule'); + + deleteRuleFromDetailsPage(); + + // @ts-expect-error update types + cy.waitFor('@deleteRule').then(() => { + cy.get(RULES_MANAGEMENT_TABLE).should('exist'); + getRulesManagementTableRows().should('have.length', expectedNumberOfRulesAfterDeletion); cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => { const numberOfRules = body.data.length; expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion); }); - cy.get(RULES_MANAGEMENT_TABLE) - .get(RULES_ROW) - .first() - .within(() => { - cy.get(RULE_SWITCH).should('exist'); - }); cy.get(CUSTOM_RULES_BTN).should( 'have.text', `Custom rules (${expectedNumberOfRulesAfterDeletion})` ); }); - }); - - it('Deletes one rule from detail page', () => { - cy.get(RULES_MANAGEMENT_TABLE) - .find(RULES_ROW) - .then((rules) => { - const initialNumberOfRules = rules.length; - const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1; - - goToRuleDetails(); - cy.intercept('POST', '/api/detection_engine/rules/_bulk_delete').as('deleteRule'); - - deleteRuleFromDetailsPage(); - - // @ts-expect-error update types - cy.waitFor('@deleteRule').then(() => { - cy.get(RULES_MANAGEMENT_TABLE).should('exist'); - cy.get(RULES_MANAGEMENT_TABLE) - .find(RULES_ROW) - .should('have.length', expectedNumberOfRulesAfterDeletion); - cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => { - const numberOfRules = body.data.length; - expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion); - }); - cy.get(CUSTOM_RULES_BTN).should( - 'have.text', - `Custom rules (${expectedNumberOfRulesAfterDeletion})` - ); - }); - }); + }); }); }); @@ -352,15 +346,11 @@ describe('Custom query rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () const expectedEditedtags = rule.tags?.join(''); const expectedEditedIndexPatterns = rule.index; - before(() => { - deleteAlertsAndRules(); + beforeEach(() => { deleteConnectors(); createRule(getExistingRule({ rule_id: 'rule1', enabled: true })); - }); - - beforeEach(() => { login(); - visit(DETECTIONS_RULE_MANAGEMENT_URL); + visitSecurityDetectionRulesPage(); }); it('Only modifies rule active status on enable/disable', () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule_data_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule_data_view.cy.ts index e352af5671ae..d22d7e01efc7 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule_data_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule_data_view.cy.ts @@ -15,8 +15,6 @@ import { CUSTOM_RULES_BTN, RISK_SCORE, RULE_NAME, - RULES_ROW, - RULES_MANAGEMENT_TABLE, RULE_SWITCH, SEVERITY, } from '../../../screens/alerts_detection_rules'; @@ -52,7 +50,10 @@ import { EDIT_RULE_SETTINGS_LINK, } from '../../../screens/rule_details'; -import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; +import { + getRulesManagementTableRows, + goToRuleDetails, +} from '../../../tasks/alerts_detection_rules'; import { postDataView } from '../../../tasks/common'; import { createAndEnableRule, @@ -100,7 +101,7 @@ describe('Custom query rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - cy.get(RULES_MANAGEMENT_TABLE).find(RULES_ROW).should('have.length', expectedNumberOfRules); + getRulesManagementTableRows().should('have.length', expectedNumberOfRules); cy.get(RULE_NAME).should('have.text', rule.name); cy.get(RISK_SCORE).should('have.text', rule.risk_score); cy.get(SEVERITY).should('have.text', 'High'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/indicator_match_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/indicator_match_rule.cy.ts index 9f2e8a796ee9..59bd8b581d70 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/indicator_match_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/indicator_match_rule.cy.ts @@ -62,9 +62,9 @@ import { duplicateFirstRule, duplicateRuleFromMenu, goToRuleDetails, - selectNumberOfRules, checkDuplicatedRule, expectNumberOfRules, + selectAllRules, } from '../../../tasks/alerts_detection_rules'; import { duplicateSelectedRulesWithExceptions } from '../../../tasks/rules_bulk_actions'; import { createRule } from '../../../tasks/api_calls/rules'; @@ -555,7 +555,7 @@ describe('indicator match', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => }); it("Allows the rule to be duplicated from the table's bulk actions", () => { - selectNumberOfRules(1); + selectAllRules(); duplicateSelectedRulesWithExceptions(); checkDuplicatedRule(); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/authorization/all_rules_read_only.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/authorization/all_rules_read_only.cy.ts index bd8c5743d37b..3d68c2065de2 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/authorization/all_rules_read_only.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/authorization/all_rules_read_only.cy.ts @@ -15,7 +15,6 @@ import { RULE_NAME, } from '../../../../screens/alerts_detection_rules'; import { VALUE_LISTS_MODAL_ACTIVATOR } from '../../../../screens/lists'; -import { waitForRulesTableToBeLoaded } from '../../../../tasks/alerts_detection_rules'; import { createRule } from '../../../../tasks/api_calls/rules'; import { cleanKibana } from '../../../../tasks/common'; import { @@ -24,19 +23,17 @@ import { waitForCallOutToBeShown, MISSING_PRIVILEGES_CALLOUT, } from '../../../../tasks/common/callouts'; -import { login, visitWithoutDateRange } from '../../../../tasks/login'; -import { SECURITY_DETECTIONS_RULES_URL } from '../../../../urls/navigation'; +import { login, visitSecurityDetectionRulesPage } from '../../../../tasks/login'; describe('All rules - read only', { tags: tag.ESS }, () => { before(() => { cleanKibana(); - createRule(getNewRule({ rule_id: '1' })); + createRule(getNewRule({ rule_id: '1', enabled: false })); }); beforeEach(() => { login(ROLES.reader); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL, ROLES.reader); - waitForRulesTableToBeLoaded(); + visitSecurityDetectionRulesPage(ROLES.reader); cy.get(RULE_NAME).should('have.text', getNewRule().name); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_duplicate_rules.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_duplicate_rules.cy.ts index bc228b7160a8..fc560cf7fb58 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_duplicate_rules.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_duplicate_rules.cy.ts @@ -8,10 +8,10 @@ import { tag } from '../../../../../tags'; import { - waitForRulesTableToBeLoaded, goToTheRuleDetailsOf, - selectNumberOfRules, expectManagementTableRules, + selectAllRules, + disableAutoRefresh, } from '../../../../../tasks/alerts_detection_rules'; import { duplicateSelectedRulesWithoutExceptions, @@ -19,9 +19,8 @@ import { duplicateSelectedRulesWithNonExpiredExceptions, } from '../../../../../tasks/rules_bulk_actions'; import { goToExceptionsTab, viewExpiredExceptionItems } from '../../../../../tasks/rule_details'; -import { login, visitWithoutDateRange } from '../../../../../tasks/login'; +import { login, visitSecurityDetectionRulesPage } from '../../../../../tasks/login'; -import { SECURITY_DETECTIONS_RULES_URL } from '../../../../../urls/navigation'; import { createRule } from '../../../../../tasks/api_calls/rules'; import { cleanKibana, @@ -66,55 +65,54 @@ describe('Detection rules, bulk duplicate', { tags: [tag.ESS, tag.SERVERLESS] }, resetRulesTableState(); deleteAlertsAndRules(); cy.task('esArchiverResetKibana'); - createRule(getNewRule({ name: RULE_NAME, ...defaultRuleData, rule_id: '1' })).then( - (response) => { - createRuleExceptionItem(response.body.id, [ - { - description: 'Exception item for rule default exception list', - entries: [ - { - field: 'user.name', - operator: 'included', - type: 'match', - value: 'some value', - }, - ], - name: EXPIRED_EXCEPTION_ITEM_NAME, - type: 'simple', - expire_time: expiredDate, - }, - { - description: 'Exception item for rule default exception list', - entries: [ - { - field: 'user.name', - operator: 'included', - type: 'match', - value: 'some value', - }, - ], - name: NON_EXPIRED_EXCEPTION_ITEM_NAME, - type: 'simple', - expire_time: futureDate, - }, - ]); - } - ); - - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); - - waitForRulesTableToBeLoaded(); + createRule( + getNewRule({ name: RULE_NAME, ...defaultRuleData, rule_id: '1', enabled: false }) + ).then((response) => { + createRuleExceptionItem(response.body.id, [ + { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'user.name', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: EXPIRED_EXCEPTION_ITEM_NAME, + type: 'simple', + expire_time: expiredDate, + }, + { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'user.name', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: NON_EXPIRED_EXCEPTION_ITEM_NAME, + type: 'simple', + expire_time: futureDate, + }, + ]); + }); + + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); }); it('Duplicates rules', () => { - selectNumberOfRules(1); + selectAllRules(); duplicateSelectedRulesWithoutExceptions(); expectManagementTableRules([`${RULE_NAME} [Duplicate]`]); }); describe('With exceptions', () => { it('Duplicates rules with expired exceptions', () => { - selectNumberOfRules(1); + selectAllRules(); duplicateSelectedRulesWithExceptions(); expectManagementTableRules([`${RULE_NAME} [Duplicate]`]); goToTheRuleDetailsOf(`${RULE_NAME} [Duplicate]`); @@ -125,7 +123,7 @@ describe('Detection rules, bulk duplicate', { tags: [tag.ESS, tag.SERVERLESS] }, }); it('Duplicates rules with exceptions, excluding expired exceptions', () => { - selectNumberOfRules(1); + selectAllRules(); duplicateSelectedRulesWithNonExpiredExceptions(); expectManagementTableRules([`${RULE_NAME} [Duplicate]`]); goToTheRuleDetailsOf(`${RULE_NAME} [Duplicate]`); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts index 9aea185783e7..86561ccd3ef0 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts @@ -5,12 +5,9 @@ * 2.0. */ -import { tag } from '../../../../../tags'; - import { MODAL_CONFIRMATION_BTN, MODAL_CONFIRMATION_BODY, - RULE_CHECKBOX, RULES_TAGS_POPOVER_BTN, MODAL_ERROR_BODY, } from '../../../../../screens/alerts_detection_rules'; @@ -26,20 +23,22 @@ import { import { TIMELINE_TEMPLATE_DETAILS } from '../../../../../screens/rule_details'; -import { EUI_FILTER_SELECT_ITEM } from '../../../../../screens/common/controls'; +import { EUI_CHECKBOX, EUI_FILTER_SELECT_ITEM } from '../../../../../screens/common/controls'; import { - waitForRulesTableToBeLoaded, selectAllRules, goToTheRuleDetailsOf, - selectNumberOfRules, testAllTagsBadges, testTagsBadge, testMultipleSelectedRulesLabel, filterByElasticRules, clickErrorToastBtn, - unselectRuleByName, cancelConfirmationModal, + selectRulesByName, + getRulesManagementTableRows, + selectAllRulesOnPage, + getRuleRow, + disableAutoRefresh, } from '../../../../../tasks/alerts_detection_rules'; import { @@ -73,10 +72,10 @@ import { assertDefaultValuesAreAppliedToScheduleFields, } from '../../../../../tasks/rules_bulk_actions'; +import { createRuleAssetSavedObject } from '../../../../../helpers/rules'; +import { tag } from '../../../../../tags'; import { hasIndexPatterns, getDetails } from '../../../../../tasks/rule_details'; -import { login, visitWithoutDateRange } from '../../../../../tasks/login'; - -import { SECURITY_DETECTIONS_RULES_URL } from '../../../../../urls/navigation'; +import { login, visitSecurityDetectionRulesPage } from '../../../../../tasks/login'; import { createRule } from '../../../../../tasks/api_calls/rules'; import { loadPrepackagedTimelineTemplates } from '../../../../../tasks/api_calls/timelines'; import { @@ -95,10 +94,10 @@ import { } from '../../../../../objects/rule'; import { + createAndInstallMockedPrebuiltRules, getAvailablePrebuiltRulesCount, - excessivelyInstallAllPrebuiltRules, } from '../../../../../tasks/api_calls/prebuilt_rules'; -import { setRowsPerPageTo } from '../../../../../tasks/table_pagination'; +import { setRowsPerPageTo, sortByTableColumn } from '../../../../../tasks/table_pagination'; const RULE_NAME = 'Custom rule for bulk actions'; const EUI_SELECTABLE_LIST_ITEM_SR_TEXT = '. To check this option, press Enter.'; @@ -106,16 +105,8 @@ const EUI_SELECTABLE_LIST_ITEM_SR_TEXT = '. To check this option, press Enter.'; const prePopulatedIndexPatterns = ['index-1-*', 'index-2-*']; const prePopulatedTags = ['test-default-tag-1', 'test-default-tag-2']; -const expectedNumberOfCustomRulesToBeEdited = 6; const expectedNumberOfMachineLearningRulesToBeEdited = 1; -/** - * total number of custom rules that are not Machine learning - */ -const expectedNumberOfNotMLRules = - expectedNumberOfCustomRulesToBeEdited - expectedNumberOfMachineLearningRulesToBeEdited; -const numberOfRulesPerPage = 5; - const defaultRuleData = { index: prePopulatedIndexPatterns, tags: prePopulatedTags, @@ -133,19 +124,53 @@ describe('Detection rules, bulk edit', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLES resetRulesTableState(); deleteAlertsAndRules(); cy.task('esArchiverResetKibana'); - createRule(getNewRule({ name: RULE_NAME, ...defaultRuleData, rule_id: '1' })); - createRule(getEqlRule({ ...defaultRuleData, rule_id: '2' })); - createRule(getMachineLearningRule({ tags: ['test-default-tag-1', 'test-default-tag-2'] })); - createRule(getNewThreatIndicatorRule({ ...defaultRuleData, rule_id: '4' })); - createRule(getNewThresholdRule({ ...defaultRuleData, rule_id: '5' })); - createRule(getNewTermsRule({ ...defaultRuleData, rule_id: '6' })); - - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); - - waitForRulesTableToBeLoaded(); + createRule(getNewRule({ name: RULE_NAME, ...defaultRuleData, rule_id: '1', enabled: false })); + createRule( + getEqlRule({ ...defaultRuleData, rule_id: '2', name: 'New EQL Rule', enabled: false }) + ); + createRule( + getMachineLearningRule({ + name: 'New ML Rule Test', + tags: ['test-default-tag-1', 'test-default-tag-2'], + enabled: false, + }) + ); + createRule( + getNewThreatIndicatorRule({ + ...defaultRuleData, + rule_id: '4', + name: 'Threat Indicator Rule Test', + enabled: false, + }) + ); + createRule( + getNewThresholdRule({ + ...defaultRuleData, + rule_id: '5', + name: 'Threshold Rule', + enabled: false, + }) + ); + createRule( + getNewTermsRule({ ...defaultRuleData, rule_id: '6', name: 'New Terms Rule', enabled: false }) + ); + + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); }); describe('Prerequisites', () => { + const PREBUILT_RULES = [ + createRuleAssetSavedObject({ + name: 'Prebuilt rule 1', + rule_id: 'rule_1', + }), + createRuleAssetSavedObject({ + name: 'Prebuilt rule 2', + rule_id: 'rule_2', + }), + ]; + it('No rules selected', () => { openBulkActionsMenu(); @@ -156,80 +181,87 @@ describe('Detection rules, bulk edit', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLES }); it('Only prebuilt rules selected', () => { - const expectedNumberOfSelectedRules = 10; - - excessivelyInstallAllPrebuiltRules(); + createAndInstallMockedPrebuiltRules({ rules: PREBUILT_RULES }); // select Elastic(prebuilt) rules, check if we can't proceed further, as Elastic rules are not editable filterByElasticRules(); - selectNumberOfRules(expectedNumberOfSelectedRules); + selectAllRulesOnPage(); clickApplyTimelineTemplatesMenuItem(); - // check modal window for Elastic rule that can't be edited - checkPrebuiltRulesCannotBeModified(expectedNumberOfSelectedRules); + getRulesManagementTableRows().then((rows) => { + // check modal window for Elastic rule that can't be edited + checkPrebuiltRulesCannotBeModified(rows.length); - // the confirm button closes modal - cy.get(MODAL_CONFIRMATION_BTN).should('have.text', 'Close').click(); - cy.get(MODAL_CONFIRMATION_BODY).should('not.exist'); + // the confirm button closes modal + cy.get(MODAL_CONFIRMATION_BTN).should('have.text', 'Close').click(); + cy.get(MODAL_CONFIRMATION_BODY).should('not.exist'); + }); }); it('Prebuilt and custom rules selected: user proceeds with custom rules editing', () => { - excessivelyInstallAllPrebuiltRules(); + getRulesManagementTableRows().then((existedRulesRows) => { + createAndInstallMockedPrebuiltRules({ rules: PREBUILT_RULES }); - // modal window should show how many rules can be edit, how many not - selectAllRules(); - clickAddTagsMenuItem(); - waitForMixedRulesBulkEditModal(expectedNumberOfCustomRulesToBeEdited); + // modal window should show how many rules can be edit, how many not + selectAllRules(); + clickAddTagsMenuItem(); - getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => { - checkPrebuiltRulesCannotBeModified(availablePrebuiltRulesCount); - }); + waitForMixedRulesBulkEditModal(existedRulesRows.length); - // user can proceed with custom rule editing - cy.get(MODAL_CONFIRMATION_BTN) - .should('have.text', `Edit ${expectedNumberOfCustomRulesToBeEdited} custom rules`) - .click(); + getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => { + checkPrebuiltRulesCannotBeModified(availablePrebuiltRulesCount); + }); - // action should finish - typeTags(['test-tag']); - submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); + // user can proceed with custom rule editing + cy.get(MODAL_CONFIRMATION_BTN) + .should('have.text', `Edit ${existedRulesRows.length} custom rules`) + .click(); + + // action should finish + typeTags(['test-tag']); + submitBulkEditForm(); + waitForBulkEditActionToFinish({ updatedCount: existedRulesRows.length }); + }); }); it('Prebuilt and custom rules selected: user cancels action', () => { - excessivelyInstallAllPrebuiltRules(); + createAndInstallMockedPrebuiltRules({ rules: PREBUILT_RULES }); - // modal window should show how many rules can be edit, how many not - selectAllRules(); - clickAddTagsMenuItem(); - waitForMixedRulesBulkEditModal(expectedNumberOfCustomRulesToBeEdited); + getRulesManagementTableRows().then((rows) => { + // modal window should show how many rules can be edit, how many not + selectAllRules(); + clickAddTagsMenuItem(); + waitForMixedRulesBulkEditModal(rows.length); - getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => { - checkPrebuiltRulesCannotBeModified(availablePrebuiltRulesCount); - }); + getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => { + checkPrebuiltRulesCannotBeModified(availablePrebuiltRulesCount); + }); - // user cancels action and modal disappears - cancelConfirmationModal(); + // user cancels action and modal disappears + cancelConfirmationModal(); + }); }); it('should not lose rules selection after edit action', () => { - const rulesCount = 4; + const rulesToUpdate = [RULE_NAME, 'New EQL Rule', 'New Terms Rule'] as const; // Switch to 5 rules per page, to have few pages in pagination(ideal way to test auto refresh and selection of few items) - setRowsPerPageTo(numberOfRulesPerPage); - selectNumberOfRules(rulesCount); + setRowsPerPageTo(5); + // and make the rules order isn't changing (set sorting by rule name) over time if rules are run + sortByTableColumn('Rule'); + selectRulesByName(rulesToUpdate); // open add tags form and add 2 new tags openBulkEditAddTagsForm(); typeTags(['new-tag-1']); submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: rulesCount }); + waitForBulkEditActionToFinish({ updatedCount: rulesToUpdate.length }); - testMultipleSelectedRulesLabel(rulesCount); + testMultipleSelectedRulesLabel(rulesToUpdate.length); // check if first four(rulesCount) rules still selected and tags are updated - for (let i = 0; i < rulesCount; i += 1) { - cy.get(RULE_CHECKBOX).eq(i).should('be.checked'); - cy.get(RULES_TAGS_POPOVER_BTN) - .eq(i) + for (const ruleName of rulesToUpdate) { + getRuleRow(ruleName).find(EUI_CHECKBOX).should('be.checked'); + getRuleRow(ruleName) + .find(RULES_TAGS_POPOVER_BTN) .each(($el) => { testTagsBadge($el, prePopulatedTags.concat(['new-tag-1'])); }); @@ -239,7 +271,7 @@ describe('Detection rules, bulk edit', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLES describe('Tags actions', () => { it('Display list of tags in tags select', () => { - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + selectAllRules(); openBulkEditAddTagsForm(); openTagsSelect(); @@ -252,119 +284,131 @@ describe('Detection rules, bulk edit', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLES }); it('Add tags to custom rules', () => { - const tagsToBeAdded = ['tag-to-add-1', 'tag-to-add-2']; - const resultingTags = [...prePopulatedTags, ...tagsToBeAdded]; + getRulesManagementTableRows().then((rows) => { + const tagsToBeAdded = ['tag-to-add-1', 'tag-to-add-2']; + const resultingTags = [...prePopulatedTags, ...tagsToBeAdded]; - // check if only pre-populated tags exist in the tags filter - checkTagsInTagsFilter(prePopulatedTags, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); + // check if only pre-populated tags exist in the tags filter + checkTagsInTagsFilter(prePopulatedTags, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + selectAllRules(); - // open add tags form and add 2 new tags - openBulkEditAddTagsForm(); - typeTags(tagsToBeAdded); - submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); + // open add tags form and add 2 new tags + openBulkEditAddTagsForm(); + typeTags(tagsToBeAdded); + submitBulkEditForm(); + waitForBulkEditActionToFinish({ updatedCount: rows.length }); - // check if all rules have been updated with new tags - testAllTagsBadges(resultingTags); + // check if all rules have been updated with new tags + testAllTagsBadges(resultingTags); - // check that new tags were added to tags filter - // tags in tags filter sorted alphabetically - const resultingTagsInFilter = [...resultingTags].sort(); - checkTagsInTagsFilter(resultingTagsInFilter, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); + // check that new tags were added to tags filter + // tags in tags filter sorted alphabetically + const resultingTagsInFilter = [...resultingTags].sort(); + checkTagsInTagsFilter(resultingTagsInFilter, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); + }); }); it('Display success toast after adding tags', () => { - const tagsToBeAdded = ['tag-to-add-1', 'tag-to-add-2']; + getRulesManagementTableRows().then((rows) => { + const tagsToBeAdded = ['tag-to-add-1', 'tag-to-add-2']; - // check if only pre-populated tags exist in the tags filter - checkTagsInTagsFilter(prePopulatedTags, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); + // check if only pre-populated tags exist in the tags filter + checkTagsInTagsFilter(prePopulatedTags, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + selectAllRules(); - // open add tags form and add 2 new tags - openBulkEditAddTagsForm(); - typeTags(tagsToBeAdded); - submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); + // open add tags form and add 2 new tags + openBulkEditAddTagsForm(); + typeTags(tagsToBeAdded); + submitBulkEditForm(); + waitForBulkEditActionToFinish({ updatedCount: rows.length }); + }); }); it('Overwrite tags in custom rules', () => { - const tagsToOverwrite = ['overwrite-tag-1']; + getRulesManagementTableRows().then((rows) => { + const tagsToOverwrite = ['overwrite-tag-1']; - // check if only pre-populated tags exist in the tags filter - checkTagsInTagsFilter(prePopulatedTags, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); + // check if only pre-populated tags exist in the tags filter + checkTagsInTagsFilter(prePopulatedTags, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + selectAllRules(); - // open add tags form, check overwrite tags and warning message, type tags - openBulkEditAddTagsForm(); - checkOverwriteTagsCheckbox(); + // open add tags form, check overwrite tags and warning message, type tags + openBulkEditAddTagsForm(); + checkOverwriteTagsCheckbox(); - cy.get(RULES_BULK_EDIT_TAGS_WARNING).should( - 'have.text', - `You’re about to overwrite tags for ${expectedNumberOfCustomRulesToBeEdited} selected rules, press Save to apply changes.` - ); + cy.get(RULES_BULK_EDIT_TAGS_WARNING).should( + 'have.text', + `You’re about to overwrite tags for ${rows.length} selected rules, press Save to apply changes.` + ); - typeTags(tagsToOverwrite); - submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); + typeTags(tagsToOverwrite); + submitBulkEditForm(); + waitForBulkEditActionToFinish({ updatedCount: rows.length }); - // check if all rules have been updated with new tags - testAllTagsBadges(tagsToOverwrite); + // check if all rules have been updated with new tags + testAllTagsBadges(tagsToOverwrite); - // check that only new tags are in the tag filter - checkTagsInTagsFilter(tagsToOverwrite, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); + // check that only new tags are in the tag filter + checkTagsInTagsFilter(tagsToOverwrite, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); + }); }); it('Delete tags from custom rules', () => { - const tagsToDelete = prePopulatedTags.slice(0, 1); - const resultingTags = prePopulatedTags.slice(1); + getRulesManagementTableRows().then((rows) => { + const tagsToDelete = prePopulatedTags.slice(0, 1); + const resultingTags = prePopulatedTags.slice(1); - // check if only pre-populated tags exist in the tags filter - checkTagsInTagsFilter(prePopulatedTags, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); + // check if only pre-populated tags exist in the tags filter + checkTagsInTagsFilter(prePopulatedTags, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + selectAllRules(); - // open add tags form, check overwrite tags, type tags - openBulkEditDeleteTagsForm(); - typeTags(tagsToDelete); - submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); + // open add tags form, check overwrite tags, type tags + openBulkEditDeleteTagsForm(); + typeTags(tagsToDelete); + submitBulkEditForm(); + waitForBulkEditActionToFinish({ updatedCount: rows.length }); - // check tags has been removed from all rules - testAllTagsBadges(resultingTags); + // check tags has been removed from all rules + testAllTagsBadges(resultingTags); - // check that tags were removed from the tag filter - checkTagsInTagsFilter(resultingTags, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); + // check that tags were removed from the tag filter + checkTagsInTagsFilter(resultingTags, EUI_SELECTABLE_LIST_ITEM_SR_TEXT); + }); }); }); describe('Index patterns', () => { it('Index pattern action applied to custom rules, including machine learning: user proceeds with edit of custom non machine learning rule', () => { - const indexPattersToBeAdded = ['index-to-add-1-*', 'index-to-add-2-*']; - const resultingIndexPatterns = [...prePopulatedIndexPatterns, ...indexPattersToBeAdded]; + getRulesManagementTableRows().then((rows) => { + const indexPattersToBeAdded = ['index-to-add-1-*', 'index-to-add-2-*']; + const resultingIndexPatterns = [...prePopulatedIndexPatterns, ...indexPattersToBeAdded]; - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); - clickAddIndexPatternsMenuItem(); + selectAllRules(); + clickAddIndexPatternsMenuItem(); - // confirm editing custom rules, that are not Machine Learning - checkMachineLearningRulesCannotBeModified(expectedNumberOfMachineLearningRulesToBeEdited); - cy.get(MODAL_CONFIRMATION_BTN).click(); + // confirm editing custom rules, that are not Machine Learning + checkMachineLearningRulesCannotBeModified(expectedNumberOfMachineLearningRulesToBeEdited); + cy.get(MODAL_CONFIRMATION_BTN).click(); - typeIndexPatterns(indexPattersToBeAdded); - submitBulkEditForm(); + typeIndexPatterns(indexPattersToBeAdded); + submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfNotMLRules }); + waitForBulkEditActionToFinish({ + updatedCount: rows.length - expectedNumberOfMachineLearningRulesToBeEdited, + }); - // check if rule has been updated - goToTheRuleDetailsOf(RULE_NAME); - hasIndexPatterns(resultingIndexPatterns.join('')); + // check if rule has been updated + goToTheRuleDetailsOf(RULE_NAME); + hasIndexPatterns(resultingIndexPatterns.join('')); + }); }); it('Index pattern action applied to custom rules, including machine learning: user cancels action', () => { - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + selectAllRules(); clickAddIndexPatternsMenuItem(); // confirm editing custom rules, that are not Machine Learning @@ -375,46 +419,68 @@ describe('Detection rules, bulk edit', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLES }); it('Add index patterns to custom rules', () => { - const indexPattersToBeAdded = ['index-to-add-1-*', 'index-to-add-2-*']; - const resultingIndexPatterns = [...prePopulatedIndexPatterns, ...indexPattersToBeAdded]; - - // select only rules that are not ML - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); - unselectRuleByName(getMachineLearningRule().name); - - openBulkEditAddIndexPatternsForm(); - typeIndexPatterns(indexPattersToBeAdded); - submitBulkEditForm(); - - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfNotMLRules }); + getRulesManagementTableRows().then((rows) => { + const indexPattersToBeAdded = ['index-to-add-1-*', 'index-to-add-2-*']; + const resultingIndexPatterns = [...prePopulatedIndexPatterns, ...indexPattersToBeAdded]; + + // select only rules that are not ML + selectRulesByName([ + RULE_NAME, + 'New EQL Rule', + 'Threat Indicator Rule Test', + 'Threshold Rule', + 'New Terms Rule', + ]); + + openBulkEditAddIndexPatternsForm(); + typeIndexPatterns(indexPattersToBeAdded); + submitBulkEditForm(); + + waitForBulkEditActionToFinish({ + updatedCount: rows.length - expectedNumberOfMachineLearningRulesToBeEdited, + }); - // check if rule has been updated - goToTheRuleDetailsOf(RULE_NAME); - hasIndexPatterns(resultingIndexPatterns.join('')); + // check if rule has been updated + goToTheRuleDetailsOf(RULE_NAME); + hasIndexPatterns(resultingIndexPatterns.join('')); + }); }); it('Display success toast after editing the index pattern', () => { - const indexPattersToBeAdded = ['index-to-add-1-*', 'index-to-add-2-*']; - - // select only rules that are not ML - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); - unselectRuleByName(getMachineLearningRule().name); - - openBulkEditAddIndexPatternsForm(); - typeIndexPatterns(indexPattersToBeAdded); - submitBulkEditForm(); - - waitForBulkEditActionToFinish({ - updatedCount: expectedNumberOfNotMLRules, + getRulesManagementTableRows().then((rows) => { + const indexPattersToBeAdded = ['index-to-add-1-*', 'index-to-add-2-*']; + + // select only rules that are not ML + selectRulesByName([ + RULE_NAME, + 'New EQL Rule', + 'Threat Indicator Rule Test', + 'Threshold Rule', + 'New Terms Rule', + ]); + + openBulkEditAddIndexPatternsForm(); + typeIndexPatterns(indexPattersToBeAdded); + submitBulkEditForm(); + + waitForBulkEditActionToFinish({ + updatedCount: rows.length - expectedNumberOfMachineLearningRulesToBeEdited, + }); }); }); it('Overwrite index patterns in custom rules', () => { + const rulesToSelect = [ + RULE_NAME, + 'New EQL Rule', + 'Threat Indicator Rule Test', + 'Threshold Rule', + 'New Terms Rule', + ] as const; const indexPattersToWrite = ['index-to-write-1-*', 'index-to-write-2-*']; // select only rules that are not ML - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); - unselectRuleByName(getMachineLearningRule().name); + selectRulesByName(rulesToSelect); openBulkEditAddIndexPatternsForm(); @@ -422,13 +488,13 @@ describe('Detection rules, bulk edit', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLES checkOverwriteIndexPatternsCheckbox(); cy.get(RULES_BULK_EDIT_INDEX_PATTERNS_WARNING).should( 'have.text', - `You’re about to overwrite index patterns for ${expectedNumberOfNotMLRules} selected rules, press Save to apply changes.` + `You’re about to overwrite index patterns for ${rulesToSelect.length} selected rules, press Save to apply changes.` ); typeIndexPatterns(indexPattersToWrite); submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfNotMLRules }); + waitForBulkEditActionToFinish({ updatedCount: rulesToSelect.length }); // check if rule has been updated goToTheRuleDetailsOf(RULE_NAME); @@ -436,18 +502,24 @@ describe('Detection rules, bulk edit', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLES }); it('Delete index patterns from custom rules', () => { + const rulesToSelect = [ + RULE_NAME, + 'New EQL Rule', + 'Threat Indicator Rule Test', + 'Threshold Rule', + 'New Terms Rule', + ] as const; const indexPatternsToDelete = prePopulatedIndexPatterns.slice(0, 1); const resultingIndexPatterns = prePopulatedIndexPatterns.slice(1); // select only not ML rules - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); - unselectRuleByName(getMachineLearningRule().name); + selectRulesByName(rulesToSelect); openBulkEditDeleteIndexPatternsForm(); typeIndexPatterns(indexPatternsToDelete); submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfNotMLRules }); + waitForBulkEditActionToFinish({ updatedCount: rulesToSelect.length }); // check if rule has been updated goToTheRuleDetailsOf(RULE_NAME); @@ -455,16 +527,23 @@ describe('Detection rules, bulk edit', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLES }); it('Delete all index patterns from custom rules', () => { + const rulesToSelect = [ + RULE_NAME, + 'New EQL Rule', + 'Threat Indicator Rule Test', + 'Threshold Rule', + 'New Terms Rule', + ] as const; + // select only rules that are not ML - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); - unselectRuleByName(getMachineLearningRule().name); + selectRulesByName(rulesToSelect); openBulkEditDeleteIndexPatternsForm(); typeIndexPatterns(prePopulatedIndexPatterns); submitBulkEditForm(); // error toast should be displayed that that rules edit failed - waitForBulkEditActionToFinish({ failedCount: expectedNumberOfNotMLRules }); + waitForBulkEditActionToFinish({ failedCount: rulesToSelect.length }); // on error toast button click display error that index patterns can't be empty clickErrorToastBtn(); @@ -478,97 +557,107 @@ describe('Detection rules, bulk edit', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLES }); it('Apply timeline template to custom rules', () => { - const timelineTemplateName = 'Generic Endpoint Timeline'; + getRulesManagementTableRows().then((rows) => { + const timelineTemplateName = 'Generic Endpoint Timeline'; - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + selectAllRules(); - // open Timeline template form, check warning, select timeline template - clickApplyTimelineTemplatesMenuItem(); - cy.get(RULES_BULK_EDIT_TIMELINE_TEMPLATES_WARNING).contains( - `You're about to apply changes to ${expectedNumberOfCustomRulesToBeEdited} selected rules. If you previously applied Timeline templates to these rules, they will be overwritten or (if you select 'None') reset to none.` - ); - selectTimelineTemplate(timelineTemplateName); + // open Timeline template form, check warning, select timeline template + clickApplyTimelineTemplatesMenuItem(); + cy.get(RULES_BULK_EDIT_TIMELINE_TEMPLATES_WARNING).contains( + `You're about to apply changes to ${rows.length} selected rules. If you previously applied Timeline templates to these rules, they will be overwritten or (if you select 'None') reset to none.` + ); + selectTimelineTemplate(timelineTemplateName); - submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); + submitBulkEditForm(); + waitForBulkEditActionToFinish({ updatedCount: rows.length }); - // check if timeline template has been updated to selected one - goToTheRuleDetailsOf(RULE_NAME); - getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', timelineTemplateName); + // check if timeline template has been updated to selected one + goToTheRuleDetailsOf(RULE_NAME); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', timelineTemplateName); + }); }); it('Reset timeline template to None for custom rules', () => { - const noneTimelineTemplate = 'None'; + getRulesManagementTableRows().then((rows) => { + const noneTimelineTemplate = 'None'; - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + selectAllRules(); - // open Timeline template form, submit form without picking timeline template as None is selected by default - clickApplyTimelineTemplatesMenuItem(); + // open Timeline template form, submit form without picking timeline template as None is selected by default + clickApplyTimelineTemplatesMenuItem(); - submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); + submitBulkEditForm(); + waitForBulkEditActionToFinish({ updatedCount: rows.length }); - // check if timeline template has been updated to selected one, by opening rule that have had timeline prior to editing - goToTheRuleDetailsOf(RULE_NAME); - getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', noneTimelineTemplate); + // check if timeline template has been updated to selected one, by opening rule that have had timeline prior to editing + goToTheRuleDetailsOf(RULE_NAME); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', noneTimelineTemplate); + }); }); }); describe('Schedule', () => { it('Default values are applied to bulk edit schedule fields', () => { - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); - clickUpdateScheduleMenuItem(); + getRulesManagementTableRows().then((rows) => { + selectAllRules(); + clickUpdateScheduleMenuItem(); - assertUpdateScheduleWarningExists(expectedNumberOfCustomRulesToBeEdited); + assertUpdateScheduleWarningExists(rows.length); - assertDefaultValuesAreAppliedToScheduleFields({ - interval: 5, - lookback: 1, + assertDefaultValuesAreAppliedToScheduleFields({ + interval: 5, + lookback: 1, + }); }); }); it('Updates schedule for custom rules', () => { - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); - clickUpdateScheduleMenuItem(); + getRulesManagementTableRows().then((rows) => { + selectAllRules(); + clickUpdateScheduleMenuItem(); - assertUpdateScheduleWarningExists(expectedNumberOfCustomRulesToBeEdited); + assertUpdateScheduleWarningExists(rows.length); - typeScheduleInterval('20'); - setScheduleIntervalTimeUnit('Hours'); + typeScheduleInterval('20'); + setScheduleIntervalTimeUnit('Hours'); - typeScheduleLookback('10'); - setScheduleLookbackTimeUnit('Minutes'); + typeScheduleLookback('10'); + setScheduleLookbackTimeUnit('Minutes'); - submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); + submitBulkEditForm(); + waitForBulkEditActionToFinish({ updatedCount: rows.length }); - goToTheRuleDetailsOf(RULE_NAME); + goToTheRuleDetailsOf(RULE_NAME); - assertRuleScheduleValues({ - interval: '20h', - lookback: '10m', + assertRuleScheduleValues({ + interval: '20h', + lookback: '10m', + }); }); }); it('Validates invalid inputs when scheduling for custom rules', () => { - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); - clickUpdateScheduleMenuItem(); + getRulesManagementTableRows().then((rows) => { + selectAllRules(); + clickUpdateScheduleMenuItem(); - // Validate invalid values are corrected to minimumValue - for 0 and negative values - typeScheduleInterval('0'); - setScheduleIntervalTimeUnit('Hours'); + // Validate invalid values are corrected to minimumValue - for 0 and negative values + typeScheduleInterval('0'); + setScheduleIntervalTimeUnit('Hours'); - typeScheduleLookback('-5'); - setScheduleLookbackTimeUnit('Seconds'); + typeScheduleLookback('-5'); + setScheduleLookbackTimeUnit('Seconds'); - submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); + submitBulkEditForm(); + waitForBulkEditActionToFinish({ updatedCount: rows.length }); - goToTheRuleDetailsOf(RULE_NAME); + goToTheRuleDetailsOf(RULE_NAME); - assertRuleScheduleValues({ - interval: '1h', - lookback: '1s', + assertRuleScheduleValues({ + interval: '1h', + lookback: '1s', + }); }); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts index 3b3755dc66d3..c1dbc783fad8 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts @@ -36,9 +36,11 @@ import { pickSummaryOfAlertsOption, } from '../../../../../tasks/common/rule_actions'; import { - waitForRulesTableToBeLoaded, - selectNumberOfRules, goToEditRuleActionsSettingsOf, + expectManagementTableRules, + selectAllRules, + selectRulesByName, + getRulesManagementTableRows, disableAutoRefresh, } from '../../../../../tasks/alerts_detection_rules'; import { @@ -48,9 +50,7 @@ import { openBulkEditRuleActionsForm, openBulkActionsMenu, } from '../../../../../tasks/rules_bulk_actions'; -import { login, visitWithoutDateRange } from '../../../../../tasks/login'; - -import { SECURITY_DETECTIONS_RULES_URL } from '../../../../../urls/navigation'; +import { login, visitSecurityDetectionRulesPage } from '../../../../../tasks/login'; import { createRule } from '../../../../../tasks/api_calls/rules'; import { createSlackConnector } from '../../../../../tasks/api_calls/connectors'; @@ -70,10 +70,6 @@ import { } from '../../../../../tasks/api_calls/prebuilt_rules'; const ruleNameToAssert = 'Custom rule name with actions'; -const expectedNumberOfCustomRulesToBeEdited = 7; -// 7 custom rules of different types + 2 prebuilt. -// number of selected rules doesn't matter, we only want to make sure they will be edited an no modal window displayed as for other actions -const expectedNumberOfRulesToBeEdited = expectedNumberOfCustomRulesToBeEdited + 2; const expectedExistingSlackMessage = 'Existing slack action'; const expectedSlackMessage = 'Slack action test message'; @@ -106,15 +102,33 @@ describe( }, ]; - createRule(getNewRule({ name: ruleNameToAssert, rule_id: '1', max_signals: 500, actions })); + createRule( + getNewRule({ + rule_id: '1', + name: ruleNameToAssert, + max_signals: 500, + actions, + enabled: false, + }) + ); }); - createRule(getEqlRule({ rule_id: '2' })); - createRule(getMachineLearningRule({ rule_id: '3' })); - createRule(getNewThreatIndicatorRule({ rule_id: '4' })); - createRule(getNewThresholdRule({ rule_id: '5' })); - createRule(getNewTermsRule({ rule_id: '6' })); - createRule(getNewRule({ saved_id: 'mocked', rule_id: '7' })); + createRule(getEqlRule({ rule_id: '2', name: 'New EQL Rule', enabled: false })); + createRule( + getMachineLearningRule({ rule_id: '3', name: 'New ML Rule Test', enabled: false }) + ); + createRule( + getNewThreatIndicatorRule({ + rule_id: '4', + name: 'Threat Indicator Rule Test', + enabled: false, + }) + ); + createRule(getNewThresholdRule({ rule_id: '5', name: 'Threshold Rule', enabled: false })); + createRule(getNewTermsRule({ rule_id: '6', name: 'New Terms Rule', enabled: false })); + createRule( + getNewRule({ saved_id: 'mocked', rule_id: '7', name: 'New Rule Test', enabled: false }) + ); createSlackConnector(); @@ -136,11 +150,22 @@ describe( context('Restricted action privileges', () => { it("User with no privileges can't add rule actions", () => { login(ROLES.hunter_no_actions); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL, ROLES.hunter_no_actions); + visitSecurityDetectionRulesPage(ROLES.hunter_no_actions); + + expectManagementTableRules([ + ruleNameToAssert, + 'New EQL Rule', + 'New ML Rule Test', + 'Threat Indicator Rule Test', + 'Threshold Rule', + 'New Terms Rule', + 'New Rule Test', + 'Test rule 1', + 'Test rule 2', + ]); waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); - waitForRulesTableToBeLoaded(); - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + selectAllRules(); openBulkActionsMenu(); @@ -151,9 +176,20 @@ describe( context('All actions privileges', () => { beforeEach(() => { login(); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); - waitForRulesTableToBeLoaded(); + visitSecurityDetectionRulesPage(); disableAutoRefresh(); + + expectManagementTableRules([ + ruleNameToAssert, + 'New EQL Rule', + 'New ML Rule Test', + 'Threat Indicator Rule Test', + 'Threshold Rule', + 'New Terms Rule', + 'New Rule Test', + 'Test rule 1', + 'Test rule 2', + ]); }); it('Add a rule action to rules (existing connector)', () => { @@ -164,62 +200,75 @@ describe( excessivelyInstallAllPrebuiltRules(); - // select both custom and prebuilt rules - selectNumberOfRules(expectedNumberOfRulesToBeEdited); - openBulkEditRuleActionsForm(); + getRulesManagementTableRows().then((rows) => { + // select both custom and prebuilt rules + selectAllRules(); + openBulkEditRuleActionsForm(); - // ensure rule actions info callout displayed on the form - cy.get(RULES_BULK_EDIT_ACTIONS_INFO).should('be.visible'); + // ensure rule actions info callout displayed on the form + cy.get(RULES_BULK_EDIT_ACTIONS_INFO).should('be.visible'); - addSlackRuleAction(expectedSlackMessage); - pickSummaryOfAlertsOption(); - pickCustomFrequencyOption(expectedActionFrequency); + addSlackRuleAction(expectedSlackMessage); + pickSummaryOfAlertsOption(); + pickCustomFrequencyOption(expectedActionFrequency); - submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfRulesToBeEdited }); + submitBulkEditForm(); + waitForBulkEditActionToFinish({ updatedCount: rows.length }); - // check if rule has been updated - goToEditRuleActionsSettingsOf(ruleNameToAssert); + // check if rule has been updated + goToEditRuleActionsSettingsOf(ruleNameToAssert); - assertSelectedSummaryOfAlertsOption(); - assertSelectedCustomFrequencyOption(expectedActionFrequency, 1); - assertSlackRuleAction(expectedExistingSlackMessage, 0); - assertSlackRuleAction(expectedSlackMessage, 1); - // ensure there is no third action - cy.get(actionFormSelector(2)).should('not.exist'); + assertSelectedSummaryOfAlertsOption(); + assertSelectedCustomFrequencyOption(expectedActionFrequency, 1); + assertSlackRuleAction(expectedExistingSlackMessage, 0); + assertSlackRuleAction(expectedSlackMessage, 1); + // ensure there is no third action + cy.get(actionFormSelector(2)).should('not.exist'); + }); }); it('Overwrite rule actions in rules', () => { excessivelyInstallAllPrebuiltRules(); - // select both custom and prebuilt rules - selectNumberOfRules(expectedNumberOfRulesToBeEdited); - openBulkEditRuleActionsForm(); - - addSlackRuleAction(expectedSlackMessage); - pickSummaryOfAlertsOption(); - pickPerRuleRunFrequencyOption(); - - // check overwrite box, ensure warning is displayed - checkOverwriteRuleActionsCheckbox(); - cy.get(RULES_BULK_EDIT_ACTIONS_WARNING).contains( - `You're about to overwrite rule actions for ${expectedNumberOfRulesToBeEdited} selected rules` - ); - - submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfRulesToBeEdited }); - - // check if rule has been updated - goToEditRuleActionsSettingsOf(ruleNameToAssert); - - assertSelectedSummaryOfAlertsOption(); - assertSelectedPerRuleRunFrequencyOption(); - assertSlackRuleAction(expectedSlackMessage); - // ensure existing action was overwritten - cy.get(actionFormSelector(1)).should('not.exist'); + getRulesManagementTableRows().then((rows) => { + // select both custom and prebuilt rules + selectAllRules(); + openBulkEditRuleActionsForm(); + + addSlackRuleAction(expectedSlackMessage); + pickSummaryOfAlertsOption(); + pickPerRuleRunFrequencyOption(); + + // check overwrite box, ensure warning is displayed + checkOverwriteRuleActionsCheckbox(); + cy.get(RULES_BULK_EDIT_ACTIONS_WARNING).contains( + `You're about to overwrite rule actions for ${rows.length} selected rules` + ); + + submitBulkEditForm(); + waitForBulkEditActionToFinish({ updatedCount: rows.length }); + + // check if rule has been updated + goToEditRuleActionsSettingsOf(ruleNameToAssert); + + assertSelectedSummaryOfAlertsOption(); + assertSelectedPerRuleRunFrequencyOption(); + assertSlackRuleAction(expectedSlackMessage); + // ensure existing action was overwritten + cy.get(actionFormSelector(1)).should('not.exist'); + }); }); it('Add a rule action to rules (new connector)', () => { + const rulesToSelect = [ + ruleNameToAssert, + 'New EQL Rule', + 'New ML Rule Test', + 'Threat Indicator Rule Test', + 'Threshold Rule', + 'New Terms Rule', + 'New Rule Test', + ] as const; const expectedActionFrequency: RuleActionCustomFrequency = { throttle: 2, throttleUnit: 'h', @@ -227,7 +276,7 @@ describe( const expectedEmail = 'test@example.com'; const expectedSubject = 'Subject'; - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + selectRulesByName(rulesToSelect); openBulkEditRuleActionsForm(); addEmailConnectorAndRuleAction(expectedEmail, expectedSubject); @@ -235,7 +284,7 @@ describe( pickCustomFrequencyOption(expectedActionFrequency); submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); + waitForBulkEditActionToFinish({ updatedCount: rulesToSelect.length }); // check if rule has been updated goToEditRuleActionsSettingsOf(ruleNameToAssert); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_data_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_data_view.cy.ts index 9ac8cfbe5ddd..86b63148eeb9 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_data_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_data_view.cy.ts @@ -15,10 +15,12 @@ import { import { DATA_VIEW_DETAILS, INDEX_PATTERNS_DETAILS } from '../../../../../screens/rule_details'; import { - waitForRulesTableToBeLoaded, goToRuleDetails, - selectNumberOfRules, goToTheRuleDetailsOf, + expectManagementTableRules, + selectAllRules, + getRulesManagementTableRows, + disableAutoRefresh, } from '../../../../../tasks/alerts_detection_rules'; import { @@ -36,9 +38,8 @@ import { getDetails, assertDetailsNotExist, } from '../../../../../tasks/rule_details'; -import { login, visitWithoutDateRange } from '../../../../../tasks/login'; +import { login, visitSecurityDetectionRulesPage } from '../../../../../tasks/login'; -import { SECURITY_DETECTIONS_RULES_URL } from '../../../../../urls/navigation'; import { createRule } from '../../../../../tasks/api_calls/rules'; import { cleanKibana, deleteAlertsAndRules, postDataView } from '../../../../../tasks/common'; @@ -54,8 +55,6 @@ const DATA_VIEW_ID = 'auditbeat'; const expectedIndexPatterns = ['index-1-*', 'index-2-*']; -const expectedNumberOfCustomRulesToBeEdited = 6; - describe( 'Bulk editing index patterns of rules with a data view only', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, @@ -71,185 +70,244 @@ describe( postDataView(DATA_VIEW_ID); - createRule(getNewRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '1' })); - createRule(getEqlRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '2' })); createRule( - getNewThreatIndicatorRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '3' }) + getNewRule({ + index: undefined, + data_view_id: DATA_VIEW_ID, + rule_id: '1', + name: 'New Rule Test 1', + enabled: false, + }) + ); + createRule( + getEqlRule({ + index: undefined, + data_view_id: DATA_VIEW_ID, + rule_id: '2', + name: 'New EQL Rule', + enabled: false, + }) ); createRule( - getNewThresholdRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '4' }) + getNewThreatIndicatorRule({ + index: undefined, + data_view_id: DATA_VIEW_ID, + rule_id: '3', + name: 'Threat Indicator Rule Test', + enabled: false, + }) + ); + createRule( + getNewThresholdRule({ + index: undefined, + data_view_id: DATA_VIEW_ID, + rule_id: '4', + name: 'Threshold Rule', + enabled: false, + }) + ); + createRule( + getNewTermsRule({ + index: undefined, + data_view_id: DATA_VIEW_ID, + rule_id: '5', + name: 'New Terms Rule', + enabled: false, + }) ); - createRule(getNewTermsRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '5' })); createRule( getNewRule({ index: undefined, data_view_id: DATA_VIEW_ID, saved_id: 'mocked', rule_id: '6', + name: 'New Rule Test 2', + enabled: false, }) ); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); - - waitForRulesTableToBeLoaded(); + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); + + expectManagementTableRules([ + 'New Rule Test 1', + 'New EQL Rule', + 'Threat Indicator Rule Test', + 'Threshold Rule', + 'New Terms Rule', + 'New Rule Test 2', + ]); }); it('Add index patterns to custom rules with configured data view: all rules are skipped', () => { - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); - - openBulkEditAddIndexPatternsForm(); - typeIndexPatterns(expectedIndexPatterns); - submitBulkEditForm(); - - waitForBulkEditActionToFinish({ - skippedCount: expectedNumberOfCustomRulesToBeEdited, - showDataViewsWarning: true, + getRulesManagementTableRows().then((rows) => { + selectAllRules(); + + openBulkEditAddIndexPatternsForm(); + typeIndexPatterns(expectedIndexPatterns); + submitBulkEditForm(); + + waitForBulkEditActionToFinish({ + skippedCount: rows.length, + showDataViewsWarning: true, + }); + + // check if rule still has data view and index patterns field does not exist + goToRuleDetails(); + getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID); + assertDetailsNotExist(INDEX_PATTERNS_DETAILS); }); - - // check if rule still has data view and index patterns field does not exist - goToRuleDetails(); - getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID); - assertDetailsNotExist(INDEX_PATTERNS_DETAILS); }); it('Add index patterns to custom rules with configured data view when data view checkbox is checked: rules are updated', () => { - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + getRulesManagementTableRows().then((rows) => { + selectAllRules(); - openBulkEditAddIndexPatternsForm(); - typeIndexPatterns(expectedIndexPatterns); + openBulkEditAddIndexPatternsForm(); + typeIndexPatterns(expectedIndexPatterns); - // click on data view overwrite checkbox, ensure warning is displayed - cy.get(RULES_BULK_EDIT_DATA_VIEWS_WARNING).should('not.exist'); - checkOverwriteDataViewCheckbox(); - cy.get(RULES_BULK_EDIT_DATA_VIEWS_WARNING).should('be.visible'); + // click on data view overwrite checkbox, ensure warning is displayed + cy.get(RULES_BULK_EDIT_DATA_VIEWS_WARNING).should('not.exist'); + checkOverwriteDataViewCheckbox(); + cy.get(RULES_BULK_EDIT_DATA_VIEWS_WARNING).should('be.visible'); - submitBulkEditForm(); + submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); + waitForBulkEditActionToFinish({ updatedCount: rows.length }); - // check if rule has been updated with index patterns and data view does not exist - goToRuleDetails(); - hasIndexPatterns(expectedIndexPatterns.join('')); - assertDetailsNotExist(DATA_VIEW_DETAILS); + // check if rule has been updated with index patterns and data view does not exist + goToRuleDetails(); + hasIndexPatterns(expectedIndexPatterns.join('')); + assertDetailsNotExist(DATA_VIEW_DETAILS); + }); }); it('Overwrite index patterns in custom rules with configured data view when overwrite data view checkbox is NOT checked:: rules are skipped', () => { - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); - - openBulkEditAddIndexPatternsForm(); - typeIndexPatterns(expectedIndexPatterns); - checkOverwriteIndexPatternsCheckbox(); - submitBulkEditForm(); - - waitForBulkEditActionToFinish({ - skippedCount: expectedNumberOfCustomRulesToBeEdited, - showDataViewsWarning: true, + getRulesManagementTableRows().then((rows) => { + selectAllRules(); + + openBulkEditAddIndexPatternsForm(); + typeIndexPatterns(expectedIndexPatterns); + checkOverwriteIndexPatternsCheckbox(); + submitBulkEditForm(); + + waitForBulkEditActionToFinish({ + skippedCount: rows.length, + showDataViewsWarning: true, + }); + + // check if rule still has data view and index patterns field does not exist + goToRuleDetails(); + getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID); + assertDetailsNotExist(INDEX_PATTERNS_DETAILS); }); - - // check if rule still has data view and index patterns field does not exist - goToRuleDetails(); - getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID); - assertDetailsNotExist(INDEX_PATTERNS_DETAILS); }); it('Overwrite index patterns in custom rules with configured data view when overwrite data view checkbox is checked: rules are updated', () => { - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + getRulesManagementTableRows().then((rows) => { + selectAllRules(); - openBulkEditAddIndexPatternsForm(); - typeIndexPatterns(expectedIndexPatterns); - checkOverwriteIndexPatternsCheckbox(); - checkOverwriteDataViewCheckbox(); + openBulkEditAddIndexPatternsForm(); + typeIndexPatterns(expectedIndexPatterns); + checkOverwriteIndexPatternsCheckbox(); + checkOverwriteDataViewCheckbox(); - submitBulkEditForm(); + submitBulkEditForm(); - waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); + waitForBulkEditActionToFinish({ updatedCount: rows.length }); - // check if rule has been overwritten with index patterns and data view does not exist - goToRuleDetails(); - hasIndexPatterns(expectedIndexPatterns.join('')); - assertDetailsNotExist(DATA_VIEW_DETAILS); + // check if rule has been overwritten with index patterns and data view does not exist + goToRuleDetails(); + hasIndexPatterns(expectedIndexPatterns.join('')); + assertDetailsNotExist(DATA_VIEW_DETAILS); + }); }); it('Delete index patterns in custom rules with configured data view: rules are skipped', () => { - selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); + getRulesManagementTableRows().then((rows) => { + selectAllRules(); - openBulkEditDeleteIndexPatternsForm(); - typeIndexPatterns(expectedIndexPatterns); + openBulkEditDeleteIndexPatternsForm(); + typeIndexPatterns(expectedIndexPatterns); - // in delete form data view checkbox is absent - cy.get(RULES_BULK_EDIT_OVERWRITE_DATA_VIEW_CHECKBOX).should('not.exist'); + // in delete form data view checkbox is absent + cy.get(RULES_BULK_EDIT_OVERWRITE_DATA_VIEW_CHECKBOX).should('not.exist'); - submitBulkEditForm(); + submitBulkEditForm(); - waitForBulkEditActionToFinish({ - skippedCount: expectedNumberOfCustomRulesToBeEdited, - showDataViewsWarning: true, - }); + waitForBulkEditActionToFinish({ + skippedCount: rows.length, + showDataViewsWarning: true, + }); - // check if rule still has data view and index patterns field does not exist - goToRuleDetails(); - getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID); + // check if rule still has data view and index patterns field does not exist + goToRuleDetails(); + getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID); + }); }); } ); -describe('Bulk editing index patterns of rules with index patterns and rules with a data view', () => { - const customRulesNumber = 2; +describe( + 'Bulk editing index patterns of rules with index patterns and rules with a data view', + { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, + () => { + before(() => { + cleanKibana(); + }); - before(() => { - cleanKibana(); - }); + beforeEach(() => { + login(); + deleteAlertsAndRules(); + cy.task('esArchiverResetKibana'); - beforeEach(() => { - login(); - deleteAlertsAndRules(); - cy.task('esArchiverResetKibana'); + postDataView(DATA_VIEW_ID); - postDataView(DATA_VIEW_ID); + createRule( + getNewRule({ name: 'with dataview', index: [], data_view_id: DATA_VIEW_ID, rule_id: '1' }) + ); + createRule(getNewRule({ name: 'no data view', index: ['test-index-1-*'], rule_id: '2' })); - createRule( - getNewRule({ name: 'with dataview', index: [], data_view_id: DATA_VIEW_ID, rule_id: '1' }) - ); - createRule(getNewRule({ name: 'no data view', index: ['test-index-1-*'], rule_id: '2' })); + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + expectManagementTableRules(['with dataview', 'no data view']); + }); - waitForRulesTableToBeLoaded(); - }); + it('Add index patterns to custom rules: one rule is updated, one rule is skipped', () => { + selectAllRules(); - it('Add index patterns to custom rules: one rule is updated, one rule is skipped', () => { - selectNumberOfRules(customRulesNumber); + openBulkEditAddIndexPatternsForm(); + typeIndexPatterns(expectedIndexPatterns); + submitBulkEditForm(); - openBulkEditAddIndexPatternsForm(); - typeIndexPatterns(expectedIndexPatterns); - submitBulkEditForm(); + waitForBulkEditActionToFinish({ + updatedCount: 1, + skippedCount: 1, + showDataViewsWarning: true, + }); - waitForBulkEditActionToFinish({ - updatedCount: 1, - skippedCount: 1, - showDataViewsWarning: true, + // check if rule still has data view and index patterns field does not exist + goToTheRuleDetailsOf('with dataview'); + getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID); + assertDetailsNotExist(INDEX_PATTERNS_DETAILS); }); - // check if rule still has data view and index patterns field does not exist - goToTheRuleDetailsOf('with dataview'); - getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID); - assertDetailsNotExist(INDEX_PATTERNS_DETAILS); - }); + it('Add index patterns to custom rules when overwrite data view checkbox is checked: all rules are updated', () => { + selectAllRules(); - it('Add index patterns to custom rules when overwrite data view checkbox is checked: all rules are updated', () => { - selectNumberOfRules(customRulesNumber); + openBulkEditAddIndexPatternsForm(); + typeIndexPatterns(expectedIndexPatterns); + checkOverwriteDataViewCheckbox(); + submitBulkEditForm(); - openBulkEditAddIndexPatternsForm(); - typeIndexPatterns(expectedIndexPatterns); - checkOverwriteDataViewCheckbox(); - submitBulkEditForm(); + waitForBulkEditActionToFinish({ + updatedCount: 2, + }); - waitForBulkEditActionToFinish({ - updatedCount: 2, + // check if rule still has data view and index patterns field does not exist + goToRuleDetails(); + assertDetailsNotExist(DATA_VIEW_DETAILS); }); - - // check if rule still has data view and index patterns field does not exist - goToRuleDetails(); - assertDetailsNotExist(DATA_VIEW_DETAILS); - }); -}); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/export_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/export_rule.cy.ts index e88920efd972..ce3b3c044658 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/export_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/export_rule.cy.ts @@ -17,7 +17,6 @@ import { } from '../../../../../screens/alerts_detection_rules'; import { filterByElasticRules, - selectNumberOfRules, selectAllRules, waitForRuleExecution, exportRule, @@ -74,7 +73,7 @@ describe('Export rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => { // Prevent installation of whole prebuilt rules package, use mock prebuilt rules instead preventPrebuiltRulesPackageInstallation(); visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); - createRule(getNewRule({ name: 'Rule to export' })).as('ruleResponse'); + createRule(getNewRule({ name: 'Rule to export', enabled: false })).as('ruleResponse'); }); it('exports a custom rule', function () { @@ -106,7 +105,7 @@ describe('Export rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => { createAndInstallMockedPrebuiltRules({ rules: prebuiltRules }); filterByElasticRules(); - selectNumberOfRules(prebuiltRules.length); + selectAllRules(); bulkExportRules(); cy.get(MODAL_CONFIRMATION_BODY).contains( @@ -160,6 +159,7 @@ describe('Export rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => { }, ], rule_id: '2', + enabled: false, }) ) ); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/snoozing/rule_snoozing.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/snoozing/rule_snoozing.cy.ts index c16ca1a33762..f7297c99b42e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/snoozing/rule_snoozing.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/snoozing/rule_snoozing.cy.ts @@ -11,13 +11,13 @@ import { tag } from '../../../../../tags'; import { createRule, snoozeRule as snoozeRuleViaAPI } from '../../../../../tasks/api_calls/rules'; import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../../../../tasks/common'; -import { login, visitWithoutDateRange } from '../../../../../tasks/login'; -import { getNewRule } from '../../../../../objects/rule'; import { - ruleDetailsUrl, - ruleEditUrl, - SECURITY_DETECTIONS_RULES_URL, -} from '../../../../../urls/navigation'; + login, + visitSecurityDetectionRulesPage, + visitWithoutDateRange, +} from '../../../../../tasks/login'; +import { getNewRule } from '../../../../../objects/rule'; +import { ruleDetailsUrl, ruleEditUrl } from '../../../../../urls/navigation'; import { internalAlertingSnoozeRule } from '../../../../../urls/routes'; import { RULES_MANAGEMENT_TABLE, RULE_NAME } from '../../../../../screens/alerts_detection_rules'; import { @@ -33,7 +33,11 @@ import { unsnoozeRuleInTable, } from '../../../../../tasks/rule_snoozing'; import { createSlackConnector } from '../../../../../tasks/api_calls/connectors'; -import { duplicateFirstRule, importRules } from '../../../../../tasks/alerts_detection_rules'; +import { + disableAutoRefresh, + duplicateFirstRule, + importRules, +} from '../../../../../tasks/alerts_detection_rules'; import { goToActionsStepTab } from '../../../../../tasks/create_new_rule'; import { goToRuleEditSettings } from '../../../../../tasks/rule_details'; import { actionFormSelector } from '../../../../../screens/common/rule_actions'; @@ -55,9 +59,10 @@ describe('rule snoozing', { tags: [tag.ESS, tag.SERVERLESS] }, () => { }); it('ensures the rule is snoozed on the rules management page, rule details page and rule editing page', () => { - createRule(getNewRule({ name: 'Test on all pages' })); + createRule(getNewRule({ name: 'Test on all pages', enabled: false })); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); snoozeRuleInTable({ tableSelector: RULES_MANAGEMENT_TABLE, @@ -79,9 +84,10 @@ describe('rule snoozing', { tags: [tag.ESS, tag.SERVERLESS] }, () => { describe('Rules management table', () => { it('snoozes a rule without actions for 3 hours', () => { - createRule(getNewRule({ name: 'Test rule without actions' })); + createRule(getNewRule({ name: 'Test rule without actions', enabled: false })); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); snoozeRuleInTable({ tableSelector: RULES_MANAGEMENT_TABLE, @@ -100,7 +106,8 @@ describe('rule snoozing', { tags: [tag.ESS, tag.SERVERLESS] }, () => { it('snoozes a rule with actions for 2 days', () => { createRuleWithActions({ name: 'Test rule with actions' }, createRule); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); snoozeRuleInTable({ tableSelector: RULES_MANAGEMENT_TABLE, @@ -119,7 +126,8 @@ describe('rule snoozing', { tags: [tag.ESS, tag.SERVERLESS] }, () => { it('unsnoozes a rule with actions', () => { createSnoozedRule(getNewRule({ name: 'Snoozed rule' })); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); unsnoozeRuleInTable({ tableSelector: RULES_MANAGEMENT_TABLE, @@ -134,9 +142,10 @@ describe('rule snoozing', { tags: [tag.ESS, tag.SERVERLESS] }, () => { }); it('ensures snooze settings persist after page reload', () => { - createRule(getNewRule({ name: 'Test persistence' })); + createRule(getNewRule({ name: 'Test persistence', enabled: false })); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); snoozeRuleInTable({ tableSelector: RULES_MANAGEMENT_TABLE, @@ -154,14 +163,16 @@ describe('rule snoozing', { tags: [tag.ESS, tag.SERVERLESS] }, () => { }); it('ensures a duplicated rule is not snoozed', () => { - createRule(getNewRule({ name: 'Test rule' })); + createRule(getNewRule({ name: 'Test rule', enabled: false })); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); duplicateFirstRule(); // Make sure rules table is shown as it navigates to rule editing page after successful duplication - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); expectRuleUnsnoozedInTable({ tableSelector: RULES_MANAGEMENT_TABLE, @@ -197,7 +208,7 @@ describe('rule snoozing', { tags: [tag.ESS, tag.SERVERLESS] }, () => { }); it('ensures imported rules are unsnoozed', () => { - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + visitSecurityDetectionRulesPage(); importRules(RULES_TO_IMPORT_FILENAME); @@ -214,7 +225,7 @@ describe('rule snoozing', { tags: [tag.ESS, tag.SERVERLESS] }, () => { describe('Handling errors', () => { it('shows an error if unable to load snooze settings', () => { - createRule(getNewRule({ name: 'Test rule' })).then(({ body: rule }) => { + createRule(getNewRule({ name: 'Test rule', enabled: false })).then(({ body: rule }) => { cy.intercept('GET', `${INTERNAL_ALERTING_API_FIND_RULES_PATH}*`, { statusCode: 500, }); @@ -228,7 +239,7 @@ describe('rule snoozing', { tags: [tag.ESS, tag.SERVERLESS] }, () => { }); it('shows an error if unable to save snooze settings', () => { - createRule(getNewRule({ name: 'Test rule' })).then(({ body: rule }) => { + createRule(getNewRule({ name: 'Test rule', enabled: false })).then(({ body: rule }) => { cy.intercept('POST', internalAlertingSnoozeRule(rule.id), { forceNetworkError: true }); visitWithoutDateRange(ruleDetailsUrl(rule.id)); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_auto_refresh.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_auto_refresh.cy.ts index d56beb1fec0e..cc7d01aef67e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_auto_refresh.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_auto_refresh.cy.ts @@ -8,21 +8,22 @@ import { tag } from '../../../../tags'; import { - RULE_CHECKBOX, REFRESH_RULES_STATUS, RULES_TABLE_AUTOREFRESH_INDICATOR, RULES_MANAGEMENT_TABLE, } from '../../../../screens/alerts_detection_rules'; +import { EUI_CHECKBOX } from '../../../../screens/common/controls'; import { selectAllRules, clearAllRuleSelection, - selectNumberOfRules, mockGlobalClock, disableAutoRefresh, expectAutoRefreshIsDisabled, expectAutoRefreshIsEnabled, expectAutoRefreshIsDeactivated, expectNumberOfRules, + selectRulesByName, + getRuleRow, } from '../../../../tasks/alerts_detection_rules'; import { login, visit, visitWithoutDateRange } from '../../../../tasks/login'; @@ -40,7 +41,7 @@ describe('Rules table: auto-refresh', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS login(); for (let i = 1; i <= NUM_OF_TEST_RULES; ++i) { - createRule(getNewRule({ name: `Test rule ${i}`, rule_id: `${i}` })); + createRule(getNewRule({ name: `Test rule ${i}`, rule_id: `${i}`, enabled: false })); } }); @@ -68,7 +69,7 @@ describe('Rules table: auto-refresh', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS expectNumberOfRules(RULES_MANAGEMENT_TABLE, NUM_OF_TEST_RULES); - selectNumberOfRules(1); + selectRulesByName(['Test rule 1']); // mock 1 minute passing to make sure refresh is not conducted cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should('not.exist'); @@ -76,7 +77,7 @@ describe('Rules table: auto-refresh', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should('not.exist'); // ensure rule is still selected - cy.get(RULE_CHECKBOX).first().should('be.checked'); + getRuleRow('Test rule 1').find(EUI_CHECKBOX).should('be.checked'); cy.get(REFRESH_RULES_STATUS).should('have.not.text', 'Updated now'); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_filtering.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_filtering.cy.ts index 3c113648e2a2..0a11fcb6097c 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_filtering.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_filtering.cy.ts @@ -8,24 +8,20 @@ import { tag } from '../../../../tags'; import { cleanKibana, resetRulesTableState, deleteAlertsAndRules } from '../../../../tasks/common'; -import { login, visitWithoutDateRange } from '../../../../tasks/login'; +import { login, visitSecurityDetectionRulesPage } from '../../../../tasks/login'; import { expectRulesWithExecutionStatus, filterByExecutionStatus, expectNumberOfRulesShownOnPage, } from '../../../../tasks/rule_filters'; -import { SECURITY_DETECTIONS_RULES_URL } from '../../../../urls/navigation'; - -import { waitForRulesTableToBeLoaded } from '../../../../tasks/alerts_detection_rules'; - import { createRule, waitForRulesToFinishExecution } from '../../../../tasks/api_calls/rules'; import { deleteIndex, createIndex, createDocument, } from '../../../../tasks/api_calls/elasticsearch'; - +import { disableAutoRefresh } from '../../../../tasks/alerts_detection_rules'; import { getNewRule } from '../../../../objects/rule'; describe('Rules table: filtering', { tags: [tag.ESS, tag.SERVERLESS] }, () => { @@ -58,6 +54,7 @@ describe('Rules table: filtering', { tags: [tag.ESS, tag.SERVERLESS] }, () => { name: 'Successful rule', rule_id: 'successful_rule', index: ['test_index'], + enabled: true, }) ); @@ -66,6 +63,7 @@ describe('Rules table: filtering', { tags: [tag.ESS, tag.SERVERLESS] }, () => { name: 'Warning rule', rule_id: 'warning_rule', index: ['non_existent_index'], + enabled: true, }) ); @@ -76,14 +74,14 @@ describe('Rules table: filtering', { tags: [tag.ESS, tag.SERVERLESS] }, () => { index: ['test_index'], // Setting a crazy large "Additional look-back time" to force a failure from: 'now-9007199254746990s', + enabled: true, }) ); waitForRulesToFinishExecution(['successful_rule', 'warning_rule', 'failed_rule'], new Date()); - visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); - - waitForRulesTableToBeLoaded(); + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); // Initial table state - before filtering by status expectNumberOfRulesShownOnPage(3); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_links.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_links.cy.ts index fe638d41f658..7d097b85abdb 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_links.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_links.cy.ts @@ -22,7 +22,7 @@ describe('Rules table: links', { tags: [tag.ESS, tag.SERVERLESS] }, () => { beforeEach(() => { login(); deleteAlertsAndRules(); - createRule(getNewRule({ rule_id: 'rule1' })); + createRule(getNewRule({ rule_id: 'rule1', enabled: false })); visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_selection.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_selection.cy.ts index 3ff965be8c66..d74404ec1590 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_selection.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_selection.cy.ts @@ -14,8 +14,8 @@ import { SELECT_ALL_RULES_ON_PAGE_CHECKBOX, } from '../../../../screens/alerts_detection_rules'; import { - selectNumberOfRules, - unselectNumberOfRules, + selectRulesByName, + unselectRulesByName, waitForPrebuiltDetectionRulesToBeLoaded, } from '../../../../tasks/alerts_detection_rules'; import { @@ -51,11 +51,11 @@ describe('Rules table: selection', { tags: [tag.ESS, tag.SERVERLESS] }, () => { it('should correctly update the selection label when rules are individually selected and unselected', () => { waitForPrebuiltDetectionRulesToBeLoaded(); - selectNumberOfRules(2); + selectRulesByName(['Test rule 1', 'Test rule 2']); cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', '2'); - unselectNumberOfRules(2); + unselectRulesByName(['Test rule 1', 'Test rule 2']); cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', '0'); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_sorting.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_sorting.cy.ts index 99b7cc9a3a8e..302bea049cc9 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_sorting.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_sorting.cy.ts @@ -14,11 +14,10 @@ import { SECOND_RULE, FOURTH_RULE, RULES_MANAGEMENT_TABLE, - RULES_ROW, } from '../../../../screens/alerts_detection_rules'; import { enableRule, - waitForRulesTableToBeLoaded, + getRulesManagementTableRows, waitForRuleToUpdate, } from '../../../../tasks/alerts_detection_rules'; import { login, visit } from '../../../../tasks/login'; @@ -43,10 +42,10 @@ describe('Rules table: sorting', { tags: [tag.ESS, tag.SERVERLESS] }, () => { before(() => { cleanKibana(); login(); - createRule(getNewRule({ rule_id: '1' })); - createRule(getExistingRule({ rule_id: '2' })); - createRule(getNewOverrideRule({ rule_id: '3' })); - createRule(getNewThresholdRule({ rule_id: '4' })); + createRule(getNewRule({ rule_id: '1', enabled: false })); + createRule(getExistingRule({ rule_id: '2', enabled: false })); + createRule(getNewOverrideRule({ rule_id: '3', enabled: false })); + createRule(getNewThresholdRule({ rule_id: '4', enabled: false })); }); beforeEach(() => { @@ -55,7 +54,6 @@ describe('Rules table: sorting', { tags: [tag.ESS, tag.SERVERLESS] }, () => { it('Sorts by enabled rules', () => { visit(DETECTIONS_RULE_MANAGEMENT_URL); - waitForRulesTableToBeLoaded(); enableRule(SECOND_RULE); waitForRuleToUpdate(); @@ -72,11 +70,10 @@ describe('Rules table: sorting', { tags: [tag.ESS, tag.SERVERLESS] }, () => { }); it('Pagination updates page number and results', () => { - createRule(getNewRule({ name: 'Test a rule', rule_id: '5' })); - createRule(getNewRule({ name: 'Not same as first rule', rule_id: '6' })); + createRule(getNewRule({ name: 'Test a rule', rule_id: '5', enabled: false })); + createRule(getNewRule({ name: 'Not same as first rule', rule_id: '6', enabled: false })); visit(DETECTIONS_RULE_MANAGEMENT_URL); - waitForRulesTableToBeLoaded(); setRowsPerPageTo(5); cy.get(RULES_MANAGEMENT_TABLE).find(TABLE_FIRST_PAGE).should('have.attr', 'aria-current'); @@ -88,7 +85,7 @@ describe('Rules table: sorting', { tags: [tag.ESS, tag.SERVERLESS] }, () => { .then((ruleNameFirstPage) => { goToTablePage(2); // Check that the rules table shows at least one row - cy.get(RULES_MANAGEMENT_TABLE).find(RULES_ROW).should('have.length.gte', 1); + getRulesManagementTableRows().should('have.length.gte', 1); // Check that the rules table doesn't show the rule from the first page cy.get(RULES_MANAGEMENT_TABLE).should('not.contain', ruleNameFirstPage); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/read_only_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/read_only_view.cy.ts index 626bb6ff80bc..71883a90a344 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/read_only_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/read_only_view.cy.ts @@ -10,15 +10,10 @@ import { tag } from '../../../tags'; import { getExceptionList } from '../../../objects/exception'; import { getNewRule } from '../../../objects/rule'; import { createRule } from '../../../tasks/api_calls/rules'; -import { login, visitWithoutDateRange } from '../../../tasks/login'; +import { login, visitSecurityDetectionRulesPage } from '../../../tasks/login'; import { goToExceptionsTab, goToAlertsTab } from '../../../tasks/rule_details'; -import { - disableAutoRefresh, - goToRuleDetails, - waitForRulesTableToBeLoaded, -} from '../../../tasks/alerts_detection_rules'; -import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation'; -import { cleanKibana, deleteAlertsAndRules } from '../../../tasks/common'; +import { goToTheRuleDetailsOf } from '../../../tasks/alerts_detection_rules'; +import { deleteAlertsAndRules } from '../../../tasks/common'; import { NO_EXCEPTIONS_EXIST_PROMPT, EXCEPTION_ITEM_VIEWER_CONTAINER, @@ -35,12 +30,15 @@ import { describe('Exceptions viewer read only', { tags: tag.ESS }, () => { const exceptionList = getExceptionList(); - before(() => { - cleanKibana(); + beforeEach(() => { + deleteAlertsAndRules(); + deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type); + // create rule with exceptions createExceptionList(exceptionList, exceptionList.list_id).then((response) => { createRule( getNewRule({ + name: 'Test exceptions rule', query: 'agent.name:*', index: ['exceptions*'], exceptions_list: [ @@ -55,22 +53,13 @@ describe('Exceptions viewer read only', { tags: tag.ESS }, () => { }) ); }); - }); - beforeEach(() => { login(ROLES.reader); - visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL, ROLES.reader); - waitForRulesTableToBeLoaded(); - disableAutoRefresh(); - goToRuleDetails(); + visitSecurityDetectionRulesPage(ROLES.reader); + goToTheRuleDetailsOf('Test exceptions rule'); goToExceptionsTab(); }); - after(() => { - deleteAlertsAndRules(); - deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type); - }); - it('Cannot add an exception from empty viewer screen', () => { // when no exceptions exist, empty component shows with action to add exception cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_json_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_json_tab.cy.ts index 482ffd4a1641..b5050140f027 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_json_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_json_tab.cy.ts @@ -17,7 +17,8 @@ import { getNewRule } from '../../../../objects/rule'; import { ALERTS_URL } from '../../../../urls/navigation'; import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule'; -describe( +// Skipped as a part of https://github.com/elastic/kibana/pull/164903 +describe.skip( 'Alert details expandable flyout right panel json tab', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => { diff --git a/x-pack/test/security_solution_cypress/cypress/screens/alerts_detection_rules.ts b/x-pack/test/security_solution_cypress/cypress/screens/alerts_detection_rules.ts index ab5a2697d4be..1ae1ed9d95db 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/alerts_detection_rules.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/alerts_detection_rules.ts @@ -63,9 +63,6 @@ export const UPGRADE_SELECTED_RULES_BUTTON = '[data-test-subj="upgradeSelectedRu export const GO_BACK_TO_RULES_TABLE_BUTTON = '[data-test-subj="addRulesGoBackToRulesTableBtn"]'; -export const RULES_TABLE_INITIAL_LOADING_INDICATOR = - '[data-test-subj="initialLoadingPanelAllRulesTable"]'; - export const RULES_TABLE_REFRESH_INDICATOR = '[data-test-subj="loading-spinner"]'; export const RULES_TABLE_AUTOREFRESH_INDICATOR = '[data-test-subj="loadingRulesInfoProgress"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/screens/common/page.ts b/x-pack/test/security_solution_cypress/cypress/screens/common/page.ts index f81d5543b830..eb5b384ddf52 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/common/page.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/common/page.ts @@ -10,3 +10,5 @@ export const PAGE_TITLE = '[data-test-subj="header-page-title"]'; export const NOT_FOUND = '[data-test-subj="notFoundPage"]'; export const LOADING_SPINNER = '.euiLoadingSpinner'; + +export const PAGE_CONTENT_SPINNER = `[data-test-subj="pageContainer"] ${LOADING_SPINNER}`; diff --git a/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts b/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts index 057ff0ab16a2..97149957448b 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts @@ -5,7 +5,8 @@ * 2.0. */ -export const ALL_ACTIONS = '[data-test-subj="rules-details-popover-button-icon"]'; +export const POPOVER_ACTIONS_TRIGGER_BUTTON = + '[data-test-subj="rules-details-popover-button-icon"]'; export const ABOUT_INVESTIGATION_NOTES = '[data-test-subj="stepAboutDetailsNoteContent"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/alerts_detection_rules.ts b/x-pack/test/security_solution_cypress/cypress/tasks/alerts_detection_rules.ts index 6604cdfe1ba9..3170084e3977 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/alerts_detection_rules.ts @@ -11,8 +11,6 @@ import { CUSTOM_RULES_BTN, DELETE_RULE_ACTION_BTN, RULES_SELECTED_TAG, - RULES_TABLE_INITIAL_LOADING_INDICATOR, - RULE_CHECKBOX, RULE_NAME, RULE_SWITCH, RULE_SWITCH_LOADER, @@ -56,16 +54,20 @@ import { ADD_ELASTIC_RULES_EMPTY_PROMPT_BTN, CONFIRM_DELETE_RULE_BTN, AUTO_REFRESH_POPOVER_TRIGGER_BUTTON, + SELECT_ALL_RULES_ON_PAGE_CHECKBOX, } from '../screens/alerts_detection_rules'; import type { RULES_MONITORING_TABLE } from '../screens/alerts_detection_rules'; import { EUI_CHECKBOX } from '../screens/common/controls'; -import { ALL_ACTIONS } from '../screens/rule_details'; +import { POPOVER_ACTIONS_TRIGGER_BUTTON, RULE_NAME_HEADER } from '../screens/rule_details'; import { EDIT_SUBMIT_BUTTON } from '../screens/edit_rule'; import { LOADING_INDICATOR } from '../screens/security_header'; +import { PAGE_CONTENT_SPINNER } from '../screens/common/page'; import { goToRuleEditSettings } from './rule_details'; import { goToActionsStepTab } from './create_new_rule'; +export const getRulesManagementTableRows = () => cy.get(RULES_MANAGEMENT_TABLE).find(RULES_ROW); + export const enableRule = (rulePosition: number) => { cy.get(RULE_SWITCH).eq(rulePosition).click(); }; @@ -93,7 +95,7 @@ export const duplicateFirstRule = () => { */ export const duplicateRuleFromMenu = () => { cy.get(LOADING_INDICATOR).should('not.exist'); - cy.get(ALL_ACTIONS).click({ force: true }); + cy.get(POPOVER_ACTIONS_TRIGGER_BUTTON).click({ force: true }); cy.get(DUPLICATE_RULE_MENU_PANEL_BTN).should('be.visible'); // Because of a fade effect and fast clicking this can produce more than one click @@ -119,15 +121,7 @@ export const deleteFirstRule = () => { }; export const deleteRuleFromDetailsPage = () => { - cy.get(ALL_ACTIONS).should('be.visible'); - // We cannot use cy.root().pipe($el) withing this function and instead have to use a cy.wait() - // for the click handler to be registered. If you see flake here because of click handler issues - // increase the cy.wait(). The reason we cannot use cypress pipe is because multiple clicks on ALL_ACTIONS - // causes the pop up to show and then the next click for it to hide. Multiple clicks can cause - // the DOM to queue up and once we detect that the element is visible it can then become invisible later - // eslint-disable-next-line cypress/no-unnecessary-waiting - cy.wait(1000); - cy.get(ALL_ACTIONS).click(); + cy.get(POPOVER_ACTIONS_TRIGGER_BUTTON).click(); cy.get(RULE_DETAILS_DELETE_BTN).click(); cy.get(RULE_DETAILS_DELETE_BTN).should('not.exist'); cy.get(CONFIRM_DELETE_RULE_BTN).click(); @@ -185,48 +179,34 @@ export const filterByDisabledRules = () => { cy.get(DISABLED_RULES_BTN).click(); }; +/** + * @deprecated use goToTheRuleDetailsOf + */ export const goToRuleDetails = () => { cy.get(RULE_NAME).first().click(); }; export const goToTheRuleDetailsOf = (ruleName: string) => { cy.contains(RULE_NAME, ruleName).click(); + + cy.get(PAGE_CONTENT_SPINNER).should('be.visible'); + cy.contains(RULE_NAME_HEADER, ruleName).should('be.visible'); + cy.get(PAGE_CONTENT_SPINNER).should('not.exist'); }; export const openIntegrationsPopover = () => { cy.get(INTEGRATIONS_POPOVER).click(); }; -/** - * Selects the number of rules. Since there can be missing click handlers - * when the page loads at first, we use a pipe and a trigger of click - * on it and then check to ensure that it is checked before continuing - * with the tests. - * @param numberOfRules The number of rules to click/check - */ -export const selectNumberOfRules = (numberOfRules: number) => { - for (let i = 0; i < numberOfRules; i++) { - cy.get(RULE_CHECKBOX).eq(i).check(); - cy.get(RULE_CHECKBOX).eq(i).should('be.checked'); +export const selectRulesByName = (ruleNames: Readonly) => { + for (const ruleName of ruleNames) { + selectRuleByName(ruleName); } }; -export const unselectRuleByName = (ruleName: string) => { - cy.contains(RULE_NAME, ruleName).parents(RULES_ROW).find(EUI_CHECKBOX).uncheck(); - cy.contains(RULE_NAME, ruleName).parents(RULES_ROW).find(EUI_CHECKBOX).should('not.be.checked'); -}; - -/** - * Unselects a passed number of rules. To use together with selectNumberOfRules - * as this utility will expect and check the passed number of rules - * to have been previously checked. - * @param numberOfRules The number of rules to click/check - */ -export const unselectNumberOfRules = (numberOfRules: number) => { - for (let i = 0; i < numberOfRules; i++) { - cy.get(RULE_CHECKBOX).eq(i).should('be.checked'); - cy.get(RULE_CHECKBOX).eq(i).uncheck(); - cy.get(RULE_CHECKBOX).eq(i).should('not.be.checked'); +export const unselectRulesByName = (ruleNames: Readonly) => { + for (const ruleName of ruleNames) { + unselectRuleByName(ruleName); } }; @@ -236,6 +216,12 @@ export const selectAllRules = () => { cy.get(SELECT_ALL_RULES_BTN).contains('Clear'); }; +export const selectAllRulesOnPage = () => { + cy.log('Select all rules on page'); + cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).check(); + cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).should('be.checked'); +}; + export const clearAllRuleSelection = () => { cy.log('Clear all rules selection'); cy.get(SELECT_ALL_RULES_BTN).contains('Clear').click(); @@ -266,21 +252,6 @@ export const waitForRulesTableToShow = () => { cy.get(RULES_MANAGEMENT_TABLE, { timeout: 300000 }).should('exist'); }; -/** - * Because the Rule Management page is relatively slow, in order to avoid timeouts and flakiness, - * we almost always want to wait until the Rules table is "loaded" before we do anything with it. - * - * This task can be needed for some tests that e.g. check the table load/refetch/pagination logic. - * It waits for the table's own loading indicator to show up and disappear. - * - * NOTE: Normally, we should not rely on loading indicators in tests, because due to their - * dynamic nature it's possible to introduce race conditions and flakiness. - */ -export const waitForRulesTableToBeLoaded = () => { - // Wait up to 5 minutes for the rules to load as in CI containers this can be very slow - cy.get(RULES_TABLE_INITIAL_LOADING_INDICATOR, { timeout: 300000 }).should('not.exist'); -}; - export const waitForRulesTableToBeRefreshed = () => { cy.get(RULES_TABLE_REFRESH_INDICATOR).should('exist'); cy.get(RULES_TABLE_REFRESH_INDICATOR).should('not.exist'); @@ -523,3 +494,19 @@ export const goToEditRuleActionsSettingsOf = (name: string) => { cy.get(EDIT_SUBMIT_BUTTON).should('be.enabled'); goToActionsStepTab(); }; + +export const getRuleRow = (ruleName: string) => cy.contains(RULE_NAME, ruleName).parents(RULES_ROW); + +const selectRuleByName = (ruleName: string) => { + cy.log(`Select rule "${ruleName}"`); + getRuleRow(ruleName).find(EUI_CHECKBOX).check(); + cy.log(`Make sure rule "${ruleName}" has been selected`); + getRuleRow(ruleName).find(EUI_CHECKBOX).should('be.checked'); +}; + +const unselectRuleByName = (ruleName: string) => { + cy.log(`Unselect rule "${ruleName}"`); + getRuleRow(ruleName).find(EUI_CHECKBOX).uncheck(); + cy.log(`Make sure rule "${ruleName}" has been unselected`); + getRuleRow(ruleName).find(EUI_CHECKBOX).should('not.be.checked'); +}; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts index c11c1afb0140..665c062b5809 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts @@ -74,45 +74,6 @@ export const excessivelyInstallAllPrebuiltRules = () => { installAllPrebuiltRulesRequest(); }; -export const waitUntilAllRuleAssetsCreated = ( - rules: Array, - index = '.kibana_security_solution' -) => - cy.waitUntil( - () => { - return cy - .request({ - method: 'GET', - url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_search`, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'Content-Type': 'application/json', - }, - failOnStatusCode: false, - body: { - query: { - match: { - type: 'security-rule', - }, - }, - }, - }) - .then((response) => { - const areAllRulesCreated = rules.every((rule) => - // Checking that all the expected rules are stored in ES - response.body.hits.hits.some( - (storedRule: { _source: typeof SAMPLE_PREBUILT_RULE }) => - storedRule._source['security-rule'].rule_id === rule['security-rule'].rule_id - ) - ); - - return areAllRulesCreated; - }); - }, - { interval: 500, timeout: 12000 } - ); - export const createNewRuleAsset = ({ index = '.kibana_security_solution', rule = SAMPLE_PREBUILT_RULE, @@ -122,7 +83,7 @@ export const createNewRuleAsset = ({ }) => { const url = `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_doc/security-rule:${ rule['security-rule'].rule_id - }`; + }?refresh`; cy.log('URL', url); cy.waitUntil( () => { @@ -168,7 +129,7 @@ export const bulkCreateRuleAssets = ({ cy.request({ method: 'PUT', - url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_mapping`, + url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_mapping?refresh`, body: { dynamic: true, }, @@ -241,21 +202,17 @@ export const createAndInstallMockedPrebuiltRules = ({ rules, installToKibana = true, }: { - rules?: Array; + rules: Array; installToKibana?: boolean; }) => { cy.log('Install prebuilt rules', rules?.length); preventPrebuiltRulesPackageInstallation(); // TODO: use this bulk method once the issue with Cypress is fixed // bulkCreateRuleAssets({ rules }); - rules?.forEach((rule) => { + rules.forEach((rule) => { createNewRuleAsset({ rule }); }); - if (rules?.length) { - waitUntilAllRuleAssetsCreated(rules); - } - if (installToKibana) { return installAllPrebuiltRulesRequest(); } diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/common.ts b/x-pack/test/security_solution_cypress/cypress/tasks/common.ts index a1a6cf8a476f..c9a4c4ccad8d 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/common.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/common.ts @@ -114,7 +114,7 @@ export const deleteAlertsAndRules = () => { rootRequest({ method: 'POST', - url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed`, + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, body: { query: { bool: { @@ -134,7 +134,7 @@ export const deleteAlertsAndRules = () => { method: 'POST', url: `${Cypress.env( 'ELASTICSEARCH_URL' - )}/.lists-*,.items-*,.alerts-security.alerts-*/_delete_by_query?conflicts=proceed&scroll_size=10000`, + )}/.lists-*,.items-*,.alerts-security.alerts-*/_delete_by_query?conflicts=proceed&scroll_size=10000&refresh`, body: { query: { match_all: {}, @@ -147,7 +147,7 @@ export const deleteTimelines = () => { const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; rootRequest({ method: 'POST', - url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed`, + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, body: { query: { bool: { @@ -177,7 +177,7 @@ export const deleteCases = () => { const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; rootRequest({ method: 'POST', - url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed`, + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, body: { query: { bool: { @@ -198,7 +198,7 @@ export const deleteConnectors = () => { const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; rootRequest({ method: 'POST', - url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed`, + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, body: { query: { bool: { @@ -219,7 +219,7 @@ export const deletePrebuiltRulesAssets = () => { const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; rootRequest({ method: 'POST', - url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed`, + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, body: { query: { bool: { diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/login.ts b/x-pack/test/security_solution_cypress/cypress/tasks/login.ts index 0769c8172402..f0b7832e488c 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/login.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/login.ts @@ -12,7 +12,13 @@ import Url from 'url'; import type { ROLES } from '@kbn/security-solution-plugin/common/test'; import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '@kbn/security-solution-plugin/common/constants'; -import { hostDetailsUrl, LOGOUT_URL, userDetailsUrl } from '../urls/navigation'; +import { + hostDetailsUrl, + LOGOUT_URL, + SECURITY_DETECTIONS_RULES_URL, + userDetailsUrl, +} from '../urls/navigation'; +import { resetRulesTableState } from './common'; /** * Credentials in the `kibana.dev.yml` config file will be used to authenticate @@ -393,6 +399,11 @@ export const visitHostDetailsPage = (hostName = 'suricata-iowa') => { cy.get('[data-test-subj="loading-spinner"]').should('not.exist'); }; +export const visitSecurityDetectionRulesPage = (role?: ROLES) => { + resetRulesTableState(); // Clear persistent rules filter data before page loading + visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL, role); +}; + export const visitUserDetailsPage = (userName = 'test') => { visit(userDetailsUrl(userName)); };