diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts index bedbccadc797f..ecfa96d59170f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts @@ -80,7 +80,7 @@ import { waitForAlertsPanelToBeLoaded, } from '../../tasks/alerts'; import { - changeToThreeHundredRowsPerPage, + changeRowsPerPageTo300, deleteFirstRule, deleteSelectedRules, editFirstRule, @@ -88,8 +88,8 @@ import { goToCreateNewRule, goToRuleDetails, selectNumberOfRules, - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, - waitForRulesToBeLoaded, + waitForRulesTableToBeLoaded, + waitForRulesTableToBeRefreshed, } from '../../tasks/alerts_detection_rules'; import { createCustomRuleActivated } from '../../tasks/api_calls/rules'; import { createTimeline } from '../../tasks/api_calls/timelines'; @@ -136,7 +136,7 @@ describe('Custom detection rules creation', () => { waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + waitForRulesTableToBeLoaded(); goToCreateNewRule(); fillDefineCustomRuleWithImportedQueryAndContinue(this.rule); fillAboutRuleAndContinue(this.rule); @@ -158,8 +158,7 @@ describe('Custom detection rules creation', () => { cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); @@ -245,7 +244,7 @@ describe('Custom detection rules deletion and edition', () => { cy.get(SHOWING_RULES_TEXT).should('have.text', `Showing ${initialNumberOfRules} rules`); deleteFirstRule(); - waitForRulesToBeLoaded(); + waitForRulesTableToBeRefreshed(); cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should( @@ -275,7 +274,7 @@ describe('Custom detection rules deletion and edition', () => { selectNumberOfRules(numberOfRulesToBeDeleted); deleteSelectedRules(); - waitForRulesToBeLoaded(); + waitForRulesTableToBeRefreshed(); cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should( diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts index fac1056310523..93c200309fff6 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts @@ -56,12 +56,11 @@ import { waitForAlertsPanelToBeLoaded, } from '../../tasks/alerts'; import { - changeToThreeHundredRowsPerPage, + changeRowsPerPageTo300, filterByCustomRules, goToCreateNewRule, goToRuleDetails, - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, - waitForRulesToBeLoaded, + waitForRulesTableToBeLoaded, } from '../../tasks/alerts_detection_rules'; import { createTimeline } from '../../tasks/api_calls/timelines'; import { cleanKibana } from '../../tasks/common'; @@ -104,7 +103,7 @@ describe('Detection rules, EQL', () => { waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + waitForRulesTableToBeLoaded(); goToCreateNewRule(); selectEqlRuleType(); fillDefineEqlRuleAndContinue(this.rule); @@ -114,8 +113,7 @@ describe('Detection rules, EQL', () => { cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); @@ -200,7 +198,7 @@ describe('Detection rules, sequence EQL', () => { waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + waitForRulesTableToBeLoaded(); goToCreateNewRule(); selectEqlRuleType(); fillDefineEqlRuleAndContinue(this.rule); @@ -210,8 +208,7 @@ describe('Detection rules, sequence EQL', () => { cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts index db29f44ceb98c..966ce3098d6a7 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts @@ -59,12 +59,11 @@ import { waitForAlertsPanelToBeLoaded, } from '../../tasks/alerts'; import { - changeToThreeHundredRowsPerPage, + changeRowsPerPageTo300, filterByCustomRules, goToCreateNewRule, goToRuleDetails, - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, - waitForRulesToBeLoaded, + waitForRulesTableToBeLoaded, } from '../../tasks/alerts_detection_rules'; import { cleanKibana } from '../../tasks/common'; import { @@ -375,7 +374,7 @@ describe('indicator match', () => { waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + waitForRulesTableToBeLoaded(); goToCreateNewRule(); selectIndicatorMatchType(); }); @@ -388,8 +387,7 @@ describe('indicator match', () => { cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/machine_learning_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/machine_learning_rule.spec.ts index f4e5aaf513190..e420b970ad85f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/machine_learning_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/machine_learning_rule.spec.ts @@ -46,12 +46,11 @@ import { waitForAlertsPanelToBeLoaded, } from '../../tasks/alerts'; import { - changeToThreeHundredRowsPerPage, + changeRowsPerPageTo300, filterByCustomRules, goToCreateNewRule, goToRuleDetails, - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, - waitForRulesToBeLoaded, + waitForRulesTableToBeLoaded, } from '../../tasks/alerts_detection_rules'; import { cleanKibana } from '../../tasks/common'; import { @@ -81,7 +80,7 @@ describe('Detection rules, machine learning', () => { waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + waitForRulesTableToBeLoaded(); goToCreateNewRule(); selectMachineLearningRuleType(); fillDefineMachineLearningRuleAndContinue(machineLearningRule); @@ -91,8 +90,7 @@ describe('Detection rules, machine learning', () => { cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/override.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/override.spec.ts index 8bd30e54c77a5..82402019fa1e2 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/override.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/override.spec.ts @@ -68,12 +68,11 @@ import { waitForAlertsPanelToBeLoaded, } from '../../tasks/alerts'; import { - changeToThreeHundredRowsPerPage, + changeRowsPerPageTo300, filterByCustomRules, goToCreateNewRule, goToRuleDetails, - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, - waitForRulesToBeLoaded, + waitForRulesTableToBeLoaded, } from '../../tasks/alerts_detection_rules'; import { createTimeline } from '../../tasks/api_calls/timelines'; import { cleanKibana } from '../../tasks/common'; @@ -113,7 +112,7 @@ describe('Detection rules, override', () => { waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + waitForRulesTableToBeLoaded(); goToCreateNewRule(); fillDefineCustomRuleWithImportedQueryAndContinue(this.rule); fillAboutRuleWithOverrideAndContinue(this.rule); @@ -122,8 +121,7 @@ describe('Detection rules, override', () => { cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); const expectedNumberOfRules = 1; cy.get(RULES_TABLE).then(($table) => { diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts index 14954f4811139..d290773d425e2 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts @@ -20,16 +20,15 @@ import { waitForAlertsPanelToBeLoaded, } from '../../tasks/alerts'; import { - changeToThreeHundredRowsPerPage, + changeRowsPerPageTo300, deleteFirstRule, deleteSelectedRules, loadPrebuiltDetectionRules, - paginate, + goToNextPage, reloadDeletedRules, selectNumberOfRules, - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, + waitForRulesTableToBeLoaded, waitForPrebuiltDetectionRulesToBeLoaded, - waitForRulesToBeLoaded, } from '../../tasks/alerts_detection_rules'; import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; @@ -51,20 +50,18 @@ describe('Alerts rules, prebuilt rules', () => { waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + waitForRulesTableToBeLoaded(); loadPrebuiltDetectionRules(); waitForPrebuiltDetectionRulesToBeLoaded(); cy.get(ELASTIC_RULES_BTN).should('have.text', expectedElasticRulesBtnText); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); cy.get(SHOWING_RULES_TEXT).should('have.text', `Showing ${expectedNumberOfRules} rules`); cy.get(RULES_TABLE).then(($table1) => { const firstScreenRules = $table1.find(RULES_ROW).length; - paginate(); - waitForRulesToBeLoaded(); + goToNextPage(); cy.get(RULES_TABLE).then(($table2) => { const secondScreenRules = $table2.find(RULES_ROW).length; const totalNumberOfRules = firstScreenRules + secondScreenRules; @@ -85,14 +82,13 @@ describe('Deleting prebuilt rules', () => { waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + waitForRulesTableToBeLoaded(); loadPrebuiltDetectionRules(); waitForPrebuiltDetectionRulesToBeLoaded(); cy.get(ELASTIC_RULES_BTN).should('have.text', expectedElasticRulesBtnText); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); }); it('Does not allow to delete one rule when more than one is selected', () => { @@ -110,8 +106,7 @@ describe('Deleting prebuilt rules', () => { deleteFirstRule(); cy.reload(); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); cy.get(ELASTIC_RULES_BTN).should( 'have.text', @@ -125,8 +120,7 @@ describe('Deleting prebuilt rules', () => { cy.get(RELOAD_PREBUILT_RULES_BTN).should('not.exist'); cy.reload(); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); cy.get(ELASTIC_RULES_BTN).should( 'have.text', @@ -142,8 +136,7 @@ describe('Deleting prebuilt rules', () => { selectNumberOfRules(numberOfRulesToBeSelected); deleteSelectedRules(); cy.reload(); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); cy.get(RELOAD_PREBUILT_RULES_BTN).should('exist'); cy.get(RELOAD_PREBUILT_RULES_BTN).should( @@ -160,8 +153,7 @@ describe('Deleting prebuilt rules', () => { cy.get(RELOAD_PREBUILT_RULES_BTN).should('not.exist'); cy.reload(); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); cy.get(ELASTIC_RULES_BTN).should( 'have.text', diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts index 26d87f9ce9e17..0cf3caa09814c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts @@ -12,6 +12,8 @@ import { SECOND_RULE, RULE_AUTO_REFRESH_IDLE_MODAL, FOURTH_RULE, + RULES_TABLE, + pageSelector, } from '../../screens/alerts_detection_rules'; import { @@ -21,12 +23,14 @@ import { } from '../../tasks/alerts'; import { activateRule, + changeRowsPerPageTo, checkAllRulesIdleModal, checkAutoRefresh, dismissAllRulesIdleModal, + goToPage, resetAllRulesIdleModalTimeout, sortByActivatedRules, - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, + waitForRulesTableToBeLoaded, waitForRuleToBeActivated, } from '../../tasks/alerts_detection_rules'; import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; @@ -49,12 +53,9 @@ describe('Alerts detection rules', () => { createCustomRule(newThresholdRule, '4'); }); - after(() => { - cy.clock().invoke('restore'); - }); - it('Sorts by activated rules', () => { goToManageAlertsDetectionRules(); + waitForRulesTableToBeLoaded(); cy.get(RULE_NAME) .eq(SECOND_RULE) @@ -88,15 +89,50 @@ describe('Alerts detection rules', () => { }); }); - // FIXME: UI hangs on loading - it.skip('Auto refreshes rules', () => { + it('Pagination updates page number and results', () => { + createCustomRule({ ...newRule, name: 'Test a rule' }, '5'); + createCustomRule({ ...newRule, name: 'Not same as first rule' }, '6'); + + goToManageAlertsDetectionRules(); + waitForRulesTableToBeLoaded(); + + changeRowsPerPageTo(5); + + const FIRST_PAGE_SELECTOR = pageSelector(1); + const SECOND_PAGE_SELECTOR = pageSelector(2); + + cy.get(RULES_TABLE) + .find(FIRST_PAGE_SELECTOR) + .should('have.class', 'euiPaginationButton-isActive'); + + cy.get(RULES_TABLE) + .find(RULE_NAME) + .first() + .invoke('text') + .then((ruleNameFirstPage) => { + goToPage(2); + cy.get(RULES_TABLE) + .find(RULE_NAME) + .first() + .invoke('text') + .should((ruleNameSecondPage) => { + expect(ruleNameFirstPage).not.to.eq(ruleNameSecondPage); + }); + }); + + cy.get(RULES_TABLE) + .find(FIRST_PAGE_SELECTOR) + .should('not.have.class', 'euiPaginationButton-isActive'); + cy.get(RULES_TABLE) + .find(SECOND_PAGE_SELECTOR) + .should('have.class', 'euiPaginationButton-isActive'); + }); + + it('Auto refreshes rules', () => { cy.clock(Date.now()); - loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); - waitForAlertsPanelToBeLoaded(); - waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + waitForRulesTableToBeLoaded(); // mock 1 minute passing to make sure refresh // is conducted @@ -105,7 +141,7 @@ describe('Alerts detection rules', () => { // mock 45 minutes passing to check that idle modal shows // and refreshing is paused checkAllRulesIdleModal('be.visible'); - checkAutoRefresh(DEFAULT_RULE_REFRESH_INTERVAL_VALUE, 'not.be.visible'); + checkAutoRefresh(DEFAULT_RULE_REFRESH_INTERVAL_VALUE, 'not.exist'); // clicking on modal to continue, should resume refreshing dismissAllRulesIdleModal(); @@ -115,7 +151,5 @@ describe('Alerts detection rules', () => { // show after 45 min resetAllRulesIdleModalTimeout(); cy.get(RULE_AUTO_REFRESH_IDLE_MODAL).should('not.exist'); - - cy.clock().invoke('restore'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/threshold_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/threshold_rule.spec.ts index 3c188345111c8..572422a4936df 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/threshold_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/threshold_rule.spec.ts @@ -58,12 +58,11 @@ import { waitForAlertsPanelToBeLoaded, } from '../../tasks/alerts'; import { - changeToThreeHundredRowsPerPage, + changeRowsPerPageTo300, filterByCustomRules, goToCreateNewRule, goToRuleDetails, - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, - waitForRulesToBeLoaded, + waitForRulesTableToBeLoaded, } from '../../tasks/alerts_detection_rules'; import { createTimeline } from '../../tasks/api_calls/timelines'; import { cleanKibana } from '../../tasks/common'; @@ -102,7 +101,7 @@ describe.skip('Threshold Rules', () => { waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + waitForRulesTableToBeLoaded(); goToCreateNewRule(); selectThresholdRuleType(); fillDefineThresholdRuleAndContinue(rule); @@ -112,8 +111,7 @@ describe.skip('Threshold Rules', () => { cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - changeToThreeHundredRowsPerPage(); - waitForRulesToBeLoaded(); + changeRowsPerPageTo300(); const expectedNumberOfRules = 1; cy.get(RULES_TABLE).then(($table) => { diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/alerts_detection_exceptions_table.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_table.spec.ts similarity index 97% rename from x-pack/plugins/security_solution/cypress/integration/exceptions/alerts_detection_exceptions_table.spec.ts rename to x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_table.spec.ts index aa469a0cb2531..fdc8a268ca368 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/alerts_detection_exceptions_table.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_table.spec.ts @@ -12,7 +12,7 @@ import { RULE_STATUS } from '../../screens/create_new_rule'; import { goToManageAlertsDetectionRules, waitForAlertsIndexToBeCreated } from '../../tasks/alerts'; import { createCustomRule } from '../../tasks/api_calls/rules'; -import { goToRuleDetails, waitForRulesToBeLoaded } from '../../tasks/alerts_detection_rules'; +import { goToRuleDetails, waitForRulesTableToBeLoaded } from '../../tasks/alerts_detection_rules'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { @@ -61,7 +61,7 @@ describe('Exceptions Table', () => { createExceptionList(exceptionList).as('exceptionListResponse'); goBackToAllRulesTable(); - waitForRulesToBeLoaded(); + waitForRulesTableToBeLoaded(); }); after(() => { diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts index 1a22d14e396ed..68baad7d3d259 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts @@ -31,14 +31,12 @@ export const FOURTH_RULE = 3; export const LOAD_PREBUILT_RULES_BTN = '[data-test-subj="load-prebuilt-rules"]'; -export const LOADING_INITIAL_PREBUILT_RULES_TABLE = +export const RULES_TABLE_INITIAL_LOADING_INDICATOR = '[data-test-subj="initialLoadingPanelAllRulesTable"]'; -export const ASYNC_LOADING_PROGRESS = '[data-test-subj="loadingRulesInfoProgress"]'; +export const RULES_TABLE_REFRESH_INDICATOR = '[data-test-subj="loading-spinner"]'; -export const NEXT_BTN = '[data-test-subj="pagination-button-next"]'; - -export const PAGINATION_POPOVER_BTN = '[data-test-subj="tablePaginationPopoverButton"]'; +export const RULES_TABLE_AUTOREFRESH_INDICATOR = '[data-test-subj="loadingRulesInfoProgress"]'; export const RISK_SCORE = '[data-test-subj="riskScore"]'; @@ -66,8 +64,16 @@ export const SHOWING_RULES_TEXT = '[data-test-subj="showingRules"]'; export const SORT_RULES_BTN = '[data-test-subj="tableHeaderSortButton"]'; -export const THREE_HUNDRED_ROWS = '[data-test-subj="tablePagination-300-rows"]'; - export const RULE_AUTO_REFRESH_IDLE_MODAL = '[data-test-subj="allRulesIdleModal"]'; export const RULE_AUTO_REFRESH_IDLE_MODAL_CONTINUE = '[data-test-subj="allRulesIdleModal"] button'; + +export const PAGINATION_POPOVER_BTN = '[data-test-subj="tablePaginationPopoverButton"]'; + +export const rowsPerPageSelector = (count: number) => + `[data-test-subj="tablePagination-${count}-rows"]`; + +export const pageSelector = (pageNumber: number) => + `[data-test-subj="pagination-button-${pageNumber - 1}"]`; + +export const NEXT_BTN = '[data-test-subj="pagination-button-next"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index 799124190b18d..3553889449e6d 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -13,7 +13,9 @@ import { DELETE_RULE_ACTION_BTN, DELETE_RULE_BULK_BTN, LOAD_PREBUILT_RULES_BTN, - LOADING_INITIAL_PREBUILT_RULES_TABLE, + RULES_TABLE_INITIAL_LOADING_INDICATOR, + RULES_TABLE_REFRESH_INDICATOR, + RULES_TABLE_AUTOREFRESH_INDICATOR, PAGINATION_POPOVER_BTN, RELOAD_PREBUILT_RULES_BTN, RULE_CHECKBOX, @@ -22,13 +24,13 @@ import { RULE_SWITCH_LOADER, RULES_TABLE, SORT_RULES_BTN, - THREE_HUNDRED_ROWS, EXPORT_ACTION_BTN, EDIT_RULE_ACTION_BTN, NEXT_BTN, - ASYNC_LOADING_PROGRESS, RULE_AUTO_REFRESH_IDLE_MODAL, RULE_AUTO_REFRESH_IDLE_MODAL_CONTINUE, + rowsPerPageSelector, + pageSelector, } from '../screens/alerts_detection_rules'; import { ALL_ACTIONS, DELETE_RULE } from '../screens/rule_details'; @@ -36,11 +38,6 @@ export const activateRule = (rulePosition: number) => { cy.get(RULE_SWITCH).eq(rulePosition).click({ force: true }); }; -export const changeToThreeHundredRowsPerPage = () => { - cy.get(PAGINATION_POPOVER_BTN).click({ force: true }); - cy.get(THREE_HUNDRED_ROWS).click(); -}; - export const editFirstRule = () => { cy.get(COLLAPSED_ACTION_BTN).should('be.visible'); cy.get(COLLAPSED_ACTION_BTN).first().click({ force: true }); @@ -71,8 +68,7 @@ export const exportFirstRule = () => { export const filterByCustomRules = () => { cy.get(CUSTOM_RULES_BTN).click({ force: true }); - cy.get(ASYNC_LOADING_PROGRESS).should('exist'); - cy.get(ASYNC_LOADING_PROGRESS).should('not.exist'); + waitForRulesTableToBeRefreshed(); }; export const goToCreateNewRule = () => { @@ -87,10 +83,6 @@ export const loadPrebuiltDetectionRules = () => { cy.get(LOAD_PREBUILT_RULES_BTN).should('exist').click({ force: true }); }; -export const paginate = () => { - cy.get(NEXT_BTN).click(); -}; - export const reloadDeletedRules = () => { cy.get(RELOAD_PREBUILT_RULES_BTN).click({ force: true }); }; @@ -103,14 +95,24 @@ export const selectNumberOfRules = (numberOfRules: number) => { export const sortByActivatedRules = () => { cy.get(SORT_RULES_BTN).contains('Activated').click({ force: true }); - waitForRulesToBeLoaded(); + waitForRulesTableToBeRefreshed(); cy.get(SORT_RULES_BTN).contains('Activated').click({ force: true }); - waitForRulesToBeLoaded(); + waitForRulesTableToBeRefreshed(); }; -export const waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded = () => { - cy.get(LOADING_INITIAL_PREBUILT_RULES_TABLE).should('exist'); - cy.get(LOADING_INITIAL_PREBUILT_RULES_TABLE).should('not.exist'); +export const waitForRulesTableToBeLoaded = () => { + cy.get(RULES_TABLE_INITIAL_LOADING_INDICATOR).should('exist'); + cy.get(RULES_TABLE_INITIAL_LOADING_INDICATOR).should('not.exist'); +}; + +export const waitForRulesTableToBeRefreshed = () => { + cy.get(RULES_TABLE_REFRESH_INDICATOR).should('exist'); + cy.get(RULES_TABLE_REFRESH_INDICATOR).should('not.exist'); +}; + +export const waitForRulesTableToBeAutoRefreshed = () => { + cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should('exist'); + cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should('not.exist'); }; export const waitForPrebuiltDetectionRulesToBeLoaded = () => { @@ -123,15 +125,10 @@ export const waitForRuleToBeActivated = () => { cy.get(RULE_SWITCH_LOADER).should('not.exist'); }; -export const waitForRulesToBeLoaded = () => { - cy.get(ASYNC_LOADING_PROGRESS).should('exist'); - cy.get(ASYNC_LOADING_PROGRESS).should('not.exist'); -}; - export const checkAutoRefresh = (ms: number, condition: string) => { - cy.get(ASYNC_LOADING_PROGRESS).should('not.exist'); + cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should('not.exist'); cy.tick(ms); - cy.get(ASYNC_LOADING_PROGRESS).should(condition); + cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should(condition); }; export const dismissAllRulesIdleModal = () => { @@ -152,3 +149,25 @@ export const resetAllRulesIdleModalTimeout = () => { cy.window().trigger('mousemove', { force: true }); cy.tick(700000); }; + +export const changeRowsPerPageTo = (rowsCount: number) => { + cy.get(PAGINATION_POPOVER_BTN).click({ force: true }); + cy.get(rowsPerPageSelector(rowsCount)).click(); + waitForRulesTableToBeRefreshed(); +}; + +export const changeRowsPerPageTo300 = () => { + changeRowsPerPageTo(300); +}; + +export const goToPage = (pageNumber: number) => { + cy.get(RULES_TABLE_REFRESH_INDICATOR).should('not.exist'); + cy.get(pageSelector(pageNumber)).last().click({ force: true }); + waitForRulesTableToBeRefreshed(); +}; + +export const goToNextPage = () => { + cy.get(RULES_TABLE_REFRESH_INDICATOR).should('not.exist'); + cy.get(NEXT_BTN).click({ force: true }); + waitForRulesTableToBeRefreshed(); +}; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_facade.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_facade.ts index 77c327c9f7939..e9fec425d467e 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_facade.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_facade.ts @@ -18,6 +18,7 @@ export interface RulesTableFacade { setShowIdleModal(show: boolean): void; setLastRefreshDate(): void; setAutoRefreshOn(on: boolean): void; + setIsRefreshing(isRefreshing: boolean): void; } export const createRulesTableFacade = (dispatch: Dispatch): RulesTableFacade => { @@ -80,5 +81,12 @@ export const createRulesTableFacade = (dispatch: Dispatch): Ru on, }); }, + + setIsRefreshing: (isRefreshing: boolean) => { + dispatch({ + type: 'setIsRefreshing', + isRefreshing, + }); + }, }; }; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.test.ts index 1a45c60dba58a..c90bfe2b0267f 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.test.ts @@ -30,6 +30,7 @@ const initialState: RulesTableState = { exportRuleIds: [], lastUpdated: 0, isRefreshOn: false, + isRefreshing: false, showIdleModal: false, }; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.ts index edcf4f6395d89..92f21f6b508aa 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/rules_table_reducer.ts @@ -28,6 +28,7 @@ export interface RulesTableState { exportRuleIds: string[]; lastUpdated: number; isRefreshOn: boolean; + isRefreshing: boolean; showIdleModal: boolean; } @@ -44,6 +45,7 @@ export type RulesTableAction = | { type: 'exportRuleIds'; ids: string[] } | { type: 'setLastRefreshDate' } | { type: 'setAutoRefreshOn'; on: boolean } + | { type: 'setIsRefreshing'; isRefreshing: boolean } | { type: 'setShowIdleModal'; show: boolean } | { type: 'failure' }; @@ -53,11 +55,7 @@ export const createRulesTableReducer = ( const rulesTableReducer = (state: RulesTableState, action: RulesTableAction): RulesTableState => { switch (action.type) { case 'setRules': { - if ( - tableRef != null && - tableRef.current != null && - tableRef.current.changeSelection != null - ) { + if (tableRef?.current?.changeSelection != null) { // for future devs: eui basic table is not giving us a prop to set the value, so // we are using the ref in setTimeout to reset on the next loop so that we // do not get a warning telling us we are trying to update during a render @@ -142,6 +140,12 @@ export const createRulesTableReducer = ( isRefreshOn: action.on, }; } + case 'setIsRefreshing': { + return { + ...state, + isRefreshing: action.isRefreshing, + }; + } case 'setShowIdleModal': { return { ...state, diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules_table.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules_table.ts index f31b2894301ba..e36474a2fdddd 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules_table.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/rules_table/use_rules_table.ts @@ -38,6 +38,7 @@ const initialStateDefaults: RulesTableState = { exportRuleIds: [], lastUpdated: 0, isRefreshOn: true, + isRefreshing: false, showIdleModal: false, }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx index 7f9061b6abc83..d552afdd9f13f 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx @@ -11,10 +11,9 @@ import { waitFor } from '@testing-library/react'; import '../../../../../common/mock/match_media'; import '../../../../../common/mock/formatted_relative'; -import { AllRules } from './index'; -import { useKibana, useUiSetting$ } from '../../../../../common/lib/kibana'; -import { useRulesTable, useRulesStatuses } from '../../../../containers/detection_engine/rules'; import { TestProviders } from '../../../../../common/mock'; + +import { useKibana, useUiSetting$ } from '../../../../../common/lib/kibana'; import { createUseUiSetting$Mock } from '../../../../../common/lib/kibana/kibana_react.mock'; import { DEFAULT_RULE_REFRESH_INTERVAL_ON, @@ -23,6 +22,14 @@ import { DEFAULT_RULES_TABLE_REFRESH_SETTING, } from '../../../../../../common/constants'; +import { + useRulesTable, + useRulesStatuses, + RulesTableState, +} from '../../../../containers/detection_engine/rules'; + +import { AllRules } from './index'; + jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); @@ -64,10 +71,11 @@ describe('AllRules', () => { }); mockUseRulesTable.mockImplementation(({ initialStateOverride }) => { - const initialState = { + const initialState: RulesTableState = { rules: [ { actions: [], + author: [], created_at: '2020-02-14T19:49:28.178Z', created_by: 'elastic', description: 'jibber jabber', @@ -86,8 +94,10 @@ describe('AllRules', () => { query: 'host.name:*', references: [], risk_score: 73, + risk_score_mapping: [], rule_id: '571afc56-5ed9-465d-a2a9-045f099f6e7e', severity: 'high', + severity_mapping: [], tags: ['Elastic', 'Endpoint'], threat: [], throttle: null, @@ -117,6 +127,7 @@ describe('AllRules', () => { exportRuleIds: [], lastUpdated: 0, isRefreshOn: true, + isRefreshing: false, showIdleModal: false, }; @@ -132,6 +143,7 @@ describe('AllRules', () => { setShowIdleModal: jest.fn(), setLastRefreshDate: jest.fn(), setAutoRefreshOn: jest.fn(), + setIsRefreshing: jest.fn(), }; }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx index a40833d8d14ac..411e817c4407f 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx @@ -5,6 +5,8 @@ * 2.0. */ +/* eslint-disable complexity */ + import { EuiBasicTable, EuiLoadingContent, @@ -130,6 +132,7 @@ export const RulesTables = React.memo( lastUpdated, showIdleModal, isRefreshOn, + isRefreshing, } = rulesTable.state; const { @@ -139,11 +142,10 @@ export const RulesTables = React.memo( setShowIdleModal, setLastRefreshDate, setAutoRefreshOn, + setIsRefreshing, reFetchRules, } = rulesTable; - const isLoadingRules = loadingRulesAction === 'load'; - const { loading: isLoadingRulesStatuses, rulesStatuses } = useRulesStatuses(rules); const [, dispatchToaster] = useStateToaster(); const mlCapabilities = useMlCapabilities(); @@ -151,6 +153,19 @@ export const RulesTables = React.memo( // TODO: Refactor license check + hasMlAdminPermissions to common check const hasMlPermissions = hasMlLicense(mlCapabilities) && hasMlAdminPermissions(mlCapabilities); + const isLoadingRules = loadingRulesAction === 'load'; + const isLoadingAnActionOnRule = useMemo(() => { + if ( + loadingRuleIds.length > 0 && + (loadingRulesAction === 'disable' || loadingRulesAction === 'enable') + ) { + return false; + } else if (loadingRuleIds.length > 0) { + return true; + } + return false; + }, [loadingRuleIds, loadingRulesAction]); + const sorting = useMemo( (): SortingType => ({ sort: { @@ -225,8 +240,9 @@ export const RulesTables = React.memo( }, { page: page.index + 1, perPage: page.size } ); + setLastRefreshDate(); }, - [updateOptions] + [updateOptions, setLastRefreshDate] ); const rulesColumns = useMemo(() => { @@ -292,25 +308,41 @@ export const RulesTables = React.memo( [loadingRuleIds, dispatch] ); - const isLoadingAnActionOnRule = useMemo(() => { - if ( - loadingRuleIds.length > 0 && - (loadingRulesAction === 'disable' || loadingRulesAction === 'enable') - ) { - return false; - } else if (loadingRuleIds.length > 0) { - return true; - } - return false; - }, [loadingRuleIds, loadingRulesAction]); + const refreshTable = useCallback( + async (mode: 'auto' | 'manual' = 'manual'): Promise => { + if (isLoadingAnActionOnRule) { + return; + } + + const isAutoRefresh = mode === 'auto'; + if (isAutoRefresh) { + setIsRefreshing(true); + } - const handleRefreshData = useCallback(async (): Promise => { - if (!isLoadingAnActionOnRule) { await reFetchRules(); await refetchPrePackagedRulesStatus(); setLastRefreshDate(); - } - }, [reFetchRules, isLoadingAnActionOnRule, setLastRefreshDate, refetchPrePackagedRulesStatus]); + + if (isAutoRefresh) { + setIsRefreshing(false); + } + }, + [ + isLoadingAnActionOnRule, + setIsRefreshing, + reFetchRules, + refetchPrePackagedRulesStatus, + setLastRefreshDate, + ] + ); + + const handleAutoRefresh = useCallback(async (): Promise => { + await refreshTable('auto'); + }, [refreshTable]); + + const handleManualRefresh = useCallback(async (): Promise => { + await refreshTable(); + }, [refreshTable]); const handleResetIdleTimer = useCallback((): void => { if (isRefreshOn) { @@ -326,29 +358,29 @@ export const RulesTables = React.memo( useEffect(() => { const interval = setInterval(() => { if (isRefreshOn) { - handleRefreshData(); + handleAutoRefresh(); } }, defaultAutoRefreshSetting.value); return () => { clearInterval(interval); }; - }, [isRefreshOn, handleRefreshData, defaultAutoRefreshSetting.value]); + }, [isRefreshOn, handleAutoRefresh, defaultAutoRefreshSetting.value]); const handleIdleModalContinue = useCallback((): void => { setShowIdleModal(false); - handleRefreshData(); + handleAutoRefresh(); setAutoRefreshOn(true); - }, [setShowIdleModal, setAutoRefreshOn, handleRefreshData]); + }, [setShowIdleModal, setAutoRefreshOn, handleAutoRefresh]); const handleAutoRefreshSwitch = useCallback( (refreshOn: boolean) => { if (refreshOn) { - handleRefreshData(); + handleAutoRefresh(); } setAutoRefreshOn(refreshOn); }, - [setAutoRefreshOn, handleRefreshData] + [setAutoRefreshOn, handleAutoRefresh] ); const shouldShowRulesTable = useMemo( @@ -401,14 +433,16 @@ export const RulesTables = React.memo( data-test-subj="allRulesPanel" > <> - {(isLoadingRules || isLoadingRulesStatuses) && ( - - )} + {!initLoading && + (loading || isLoadingRules || isLoadingAnActionOnRule) && + isRefreshing && ( + + )} ( )} - {isLoadingAnActionOnRule && !initLoading && ( - - )} + {!initLoading && + (loading || isLoadingRules || isLoadingAnActionOnRule) && + !isRefreshing && ( + + )} + {shouldShowPrepackagedRulesPrompt && ( ( paginationTotal={pagination.total ?? 0} numberSelectedItems={selectedRuleIds.length} onGetBatchItemsPopoverContent={getBatchItemsPopoverContent} - onRefresh={handleRefreshData} + onRefresh={handleManualRefresh} isAutoRefreshOn={isRefreshOn} onRefreshSwitch={handleAutoRefreshSwitch} showBulkActions