[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)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Maxim
Palenov","email":"maxim.palenov@elastic.co"},"sourceCommit":{"committedDate":"2023-08-23T14:58:04Z","message":"[Security
Solution] Reduce Rules Management e2e flakiness (#164099)\n\n**Relates
to: https://github.com/elastic/kibana/issues/161507**\r\n**Fixes:
https://github.com/elastic/kibana/issues/163704**\r\n**Fixes:
https://github.com/elastic/kibana/issues/163182**\r\n**Fixes:
https://github.com/elastic/kibana/issues/163558**\r\n**Fixes:
https://github.com/elastic/kibana/issues/163974**\r\n**Fixes:
https://github.com/elastic/kibana/issues/153914**\r\n**Fixes:
https://github.com/elastic/kibana/issues/164079**\r\n**Fixes:
https://github.com/elastic/kibana/issues/164279**\r\n\r\n##
Summary\r\n\r\nWhile working on fixing Rules Management flaky tests I've
noticed similar fails in different tests. This PR addresses common
pitfalls to reduce a number of reasons causing e2e tests flakiness and
as a result reduce a number of flaky tests.\r\n\r\n## Details\r\n\r\nThe
common reasons causing e2e tests flakiness for the rules tables
are\r\n\r\n- Auto-refresh\r\n\r\nAuto-refresh functionality is enabled
by default and the table gets auto-refreshed every 60 seconds. If a test
takes more than 60 seconds the table fetches updated rules. Having rules
enabled by default and sorted by `Enabled` column makes the sorting
order undetermined and as rules get updated due to execution ES return
them in undetermined order. This update can happen between commands
working on the same element and indexed access like `eq()` would access
different elements. \r\n\r\n- Missing selectors\r\n\r\nSome tests or
helper functions have expectations for an element absence like
`should('not.exist')` without checking an element is visible before like
`should('be.visible')`. This way a referenced element may disappear from
the codebase after refactoring and the expectation still
fulfils.\r\n\r\n- Checking for `should('not.visible')` while an element
is removed from the DOM\r\n\r\nIt most applicable to popovers as it
first animates to be hidden and then removed from the DOM. Cypress first
need to find an element to check its visibility. Replacing
`should('not.visible')` with `should('not.exist')` and taking into
concern from the account previous bullet fixes the problem.\r\n\r\n-
Modifying ES data without refreshing (`_delete_by_query` in
particular)\r\n\r\nDue to high performance ES architecture data isn't
updated instantly. Having such behavior in tests leads to undetermined
state depending on a number of environmental factors. As UI doesn't
always auto-refreshes to fetch the recent updates in short period of
time test fail. `_delete_by_query` may take some time to update the data
but it doesn't support `refresh=wait_for` as it stated in
[docs](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html#_refreshing_shards).
Adding `?refresh=true` or just `?refresh` to `_delete_by_query` ES
request urls fixes the problem.\r\n\r\n### What was done to address
mentioned reasons above?\r\n\r\n- Auto-refresh functionality disabled in
tests where it's not necessary.\r\n- `enabled: false` field was added to
rule creators to have disabled rules as the majority of tests don't need
enabled rules.\r\n- `waitForRulesTableToBeLoaded` was removed and
replaced with `expectManagementTableRules` at some tests.\r\n-
`should('not.visible')` replaced with `should('not.exist')` in
`deleteRuleFromDetailsPage()`\r\n- `?refresh` added to
`_delete_by_query` ES data update requests\r\n\r\nThe other changes get
rid of global constants and improve readability.\r\n\r\n## Flaky test
runs\r\n\r\n[All Cypress tests under `detection_response` folder (100
runs)](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2920)
(value lists export is flaky but it's out of scope of this
PR)","sha":"40df5219ea7165e89623afcc22bb0dbc3cc19152","branchLabelMapping":{"^v8.11.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["test","release_note:skip","Team:Detections
and Resp","Team: SecuritySolution","Feature:Rule
Management","Team:Detection Rule
Management","v8.10.0","v8.11.0"],"number":164099,"url":"https://github.com/elastic/kibana/pull/164099","mergeCommit":{"message":"[Security
Solution] Reduce Rules Management e2e flakiness (#164099)\n\n**Relates
to: https://github.com/elastic/kibana/issues/161507**\r\n**Fixes:
https://github.com/elastic/kibana/issues/163704**\r\n**Fixes:
https://github.com/elastic/kibana/issues/163182**\r\n**Fixes:
https://github.com/elastic/kibana/issues/163558**\r\n**Fixes:
https://github.com/elastic/kibana/issues/163974**\r\n**Fixes:
https://github.com/elastic/kibana/issues/153914**\r\n**Fixes:
https://github.com/elastic/kibana/issues/164079**\r\n**Fixes:
https://github.com/elastic/kibana/issues/164279**\r\n\r\n##
Summary\r\n\r\nWhile working on fixing Rules Management flaky tests I've
noticed similar fails in different tests. This PR addresses common
pitfalls to reduce a number of reasons causing e2e tests flakiness and
as a result reduce a number of flaky tests.\r\n\r\n## Details\r\n\r\nThe
common reasons causing e2e tests flakiness for the rules tables
are\r\n\r\n- Auto-refresh\r\n\r\nAuto-refresh functionality is enabled
by default and the table gets auto-refreshed every 60 seconds. If a test
takes more than 60 seconds the table fetches updated rules. Having rules
enabled by default and sorted by `Enabled` column makes the sorting
order undetermined and as rules get updated due to execution ES return
them in undetermined order. This update can happen between commands
working on the same element and indexed access like `eq()` would access
different elements. \r\n\r\n- Missing selectors\r\n\r\nSome tests or
helper functions have expectations for an element absence like
`should('not.exist')` without checking an element is visible before like
`should('be.visible')`. This way a referenced element may disappear from
the codebase after refactoring and the expectation still
fulfils.\r\n\r\n- Checking for `should('not.visible')` while an element
is removed from the DOM\r\n\r\nIt most applicable to popovers as it
first animates to be hidden and then removed from the DOM. Cypress first
need to find an element to check its visibility. Replacing
`should('not.visible')` with `should('not.exist')` and taking into
concern from the account previous bullet fixes the problem.\r\n\r\n-
Modifying ES data without refreshing (`_delete_by_query` in
particular)\r\n\r\nDue to high performance ES architecture data isn't
updated instantly. Having such behavior in tests leads to undetermined
state depending on a number of environmental factors. As UI doesn't
always auto-refreshes to fetch the recent updates in short period of
time test fail. `_delete_by_query` may take some time to update the data
but it doesn't support `refresh=wait_for` as it stated in
[docs](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html#_refreshing_shards).
Adding `?refresh=true` or just `?refresh` to `_delete_by_query` ES
request urls fixes the problem.\r\n\r\n### What was done to address
mentioned reasons above?\r\n\r\n- Auto-refresh functionality disabled in
tests where it's not necessary.\r\n- `enabled: false` field was added to
rule creators to have disabled rules as the majority of tests don't need
enabled rules.\r\n- `waitForRulesTableToBeLoaded` was removed and
replaced with `expectManagementTableRules` at some tests.\r\n-
`should('not.visible')` replaced with `should('not.exist')` in
`deleteRuleFromDetailsPage()`\r\n- `?refresh` added to
`_delete_by_query` ES data update requests\r\n\r\nThe other changes get
rid of global constants and improve readability.\r\n\r\n## Flaky test
runs\r\n\r\n[All Cypress tests under `detection_response` folder (100
runs)](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2920)
(value lists export is flaky but it's out of scope of this
PR)","sha":"40df5219ea7165e89623afcc22bb0dbc3cc19152"}},"sourceBranch":"main","suggestedTargetBranches":["8.10"],"targetPullRequestStates":[{"branch":"8.10","label":"v8.10.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.11.0","labelRegex":"^v8.11.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/164099","number":164099,"mergeCommit":{"message":"[Security
Solution] Reduce Rules Management e2e flakiness (#164099)\n\n**Relates
to: https://github.com/elastic/kibana/issues/161507**\r\n**Fixes:
https://github.com/elastic/kibana/issues/163704**\r\n**Fixes:
https://github.com/elastic/kibana/issues/163182**\r\n**Fixes:
https://github.com/elastic/kibana/issues/163558**\r\n**Fixes:
https://github.com/elastic/kibana/issues/163974**\r\n**Fixes:
https://github.com/elastic/kibana/issues/153914**\r\n**Fixes:
https://github.com/elastic/kibana/issues/164079**\r\n**Fixes:
https://github.com/elastic/kibana/issues/164279**\r\n\r\n##
Summary\r\n\r\nWhile working on fixing Rules Management flaky tests I've
noticed similar fails in different tests. This PR addresses common
pitfalls to reduce a number of reasons causing e2e tests flakiness and
as a result reduce a number of flaky tests.\r\n\r\n## Details\r\n\r\nThe
common reasons causing e2e tests flakiness for the rules tables
are\r\n\r\n- Auto-refresh\r\n\r\nAuto-refresh functionality is enabled
by default and the table gets auto-refreshed every 60 seconds. If a test
takes more than 60 seconds the table fetches updated rules. Having rules
enabled by default and sorted by `Enabled` column makes the sorting
order undetermined and as rules get updated due to execution ES return
them in undetermined order. This update can happen between commands
working on the same element and indexed access like `eq()` would access
different elements. \r\n\r\n- Missing selectors\r\n\r\nSome tests or
helper functions have expectations for an element absence like
`should('not.exist')` without checking an element is visible before like
`should('be.visible')`. This way a referenced element may disappear from
the codebase after refactoring and the expectation still
fulfils.\r\n\r\n- Checking for `should('not.visible')` while an element
is removed from the DOM\r\n\r\nIt most applicable to popovers as it
first animates to be hidden and then removed from the DOM. Cypress first
need to find an element to check its visibility. Replacing
`should('not.visible')` with `should('not.exist')` and taking into
concern from the account previous bullet fixes the problem.\r\n\r\n-
Modifying ES data without refreshing (`_delete_by_query` in
particular)\r\n\r\nDue to high performance ES architecture data isn't
updated instantly. Having such behavior in tests leads to undetermined
state depending on a number of environmental factors. As UI doesn't
always auto-refreshes to fetch the recent updates in short period of
time test fail. `_delete_by_query` may take some time to update the data
but it doesn't support `refresh=wait_for` as it stated in
[docs](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html#_refreshing_shards).
Adding `?refresh=true` or just `?refresh` to `_delete_by_query` ES
request urls fixes the problem.\r\n\r\n### What was done to address
mentioned reasons above?\r\n\r\n- Auto-refresh functionality disabled in
tests where it's not necessary.\r\n- `enabled: false` field was added to
rule creators to have disabled rules as the majority of tests don't need
enabled rules.\r\n- `waitForRulesTableToBeLoaded` was removed and
replaced with `expectManagementTableRules` at some tests.\r\n-
`should('not.visible')` replaced with `should('not.exist')` in
`deleteRuleFromDetailsPage()`\r\n- `?refresh` added to
`_delete_by_query` ES data update requests\r\n\r\nThe other changes get
rid of global constants and improve readability.\r\n\r\n## Flaky test
runs\r\n\r\n[All Cypress tests under `detection_response` folder (100
runs)](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2920)
(value lists export is flaky but it's out of scope of this
PR)","sha":"40df5219ea7165e89623afcc22bb0dbc3cc19152"}}]}] BACKPORT-->
This commit is contained in:
Maxim Palenov 2023-08-26 11:40:18 +02:00 committed by GitHub
parent 681661de29
commit 14535262b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 926 additions and 809 deletions

View file

@ -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', () => {

View file

@ -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();
});

View file

@ -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();
});

View file

@ -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',

View file

@ -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();
});
});

View file

@ -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);
});
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})`
);
cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => {
const numberOfRules = body.data.length;
expect(numberOfRules).to.eql(initialNumberOfRules);
});
deleteFirstRule();
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;
getRulesManagementTableRows().then((rules) => {
const rulesToDelete = ['New Rule Test', 'Override Rule'] as const;
const initialNumberOfRules = rules.length;
const numberOfRulesToBeDeleted = 2;
const expectedNumberOfRulesAfterDeletion =
initialNumberOfRules - numberOfRulesToBeDeleted;
selectNumberOfRules(numberOfRulesToBeDeleted);
deleteSelectedRules();
selectRulesByName(rulesToDelete);
deleteSelectedRules();
cy.get(RULES_MANAGEMENT_TABLE)
.get(RULES_ROW)
.first()
.within(() => {
cy.get(RULE_SWITCH).should('not.exist');
});
getRulesManagementTableRows()
.first()
.within(() => {
cy.get(RULE_SWITCH).should('not.exist');
});
cy.get(RULES_MANAGEMENT_TABLE)
.find(RULES_ROW)
.should('have.length', 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);
});
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', () => {

View file

@ -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');

View file

@ -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();
});

View file

@ -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);
});

View file

@ -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,
},
]);
}
);
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,
},
]);
});
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
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]`);

View file

@ -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' }));
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 })
);
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
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);
getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => {
checkPrebuiltRulesCannotBeModified(availablePrebuiltRulesCount);
});
// 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 });
});
// user can proceed with custom rule editing
cy.get(MODAL_CONFIRMATION_BTN)
.should('have.text', `Edit ${expectedNumberOfCustomRulesToBeEdited} custom rules`)
.click();
// action should finish
typeTags(['test-tag']);
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited });
});
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',
`Youre about to overwrite tags for ${expectedNumberOfCustomRulesToBeEdited} selected rules, press Save to apply changes.`
);
cy.get(RULES_BULK_EDIT_TAGS_WARNING).should(
'have.text',
`Youre 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];
getRulesManagementTableRows().then((rows) => {
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);
// 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();
openBulkEditAddIndexPatternsForm();
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('Display success toast after editing the index pattern', () => {
const indexPattersToBeAdded = ['index-to-add-1-*', 'index-to-add-2-*'];
getRulesManagementTableRows().then((rows) => {
const indexPattersToBeAdded = ['index-to-add-1-*', 'index-to-add-2-*'];
// select only rules that are not ML
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
unselectRuleByName(getMachineLearningRule().name);
// 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();
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(indexPattersToBeAdded);
submitBulkEditForm();
waitForBulkEditActionToFinish({
updatedCount: expectedNumberOfNotMLRules,
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',
`Youre about to overwrite index patterns for ${expectedNumberOfNotMLRules} selected rules, press Save to apply changes.`
`Youre 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',
});
});
});
});

View file

@ -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);
waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary');
waitForRulesTableToBeLoaded();
visitSecurityDetectionRulesPage(ROLES.hunter_no_actions);
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
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');
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();
getRulesManagementTableRows().then((rows) => {
// select both custom and prebuilt rules
selectAllRules();
openBulkEditRuleActionsForm();
addSlackRuleAction(expectedSlackMessage);
pickSummaryOfAlertsOption();
pickPerRuleRunFrequencyOption();
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`
);
// 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: expectedNumberOfRulesToBeEdited });
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: rows.length });
// check if rule has been updated
goToEditRuleActionsSettingsOf(ruleNameToAssert);
// check if rule has been updated
goToEditRuleActionsSettingsOf(ruleNameToAssert);
assertSelectedSummaryOfAlertsOption();
assertSelectedPerRuleRunFrequencyOption();
assertSlackRuleAction(expectedSlackMessage);
// ensure existing action was overwritten
cy.get(actionFormSelector(1)).should('not.exist');
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);

View file

@ -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(
getNewThresholdRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '4' })
getEqlRule({
index: undefined,
data_view_id: DATA_VIEW_ID,
rule_id: '2',
name: 'New EQL Rule',
enabled: false,
})
);
createRule(
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);
visitSecurityDetectionRulesPage();
disableAutoRefresh();
waitForRulesTableToBeLoaded();
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);
getRulesManagementTableRows().then((rows) => {
selectAllRules();
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
submitBulkEditForm();
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
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);
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);
getRulesManagementTableRows().then((rows) => {
selectAllRules();
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
checkOverwriteIndexPatternsCheckbox();
submitBulkEditForm();
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
checkOverwriteIndexPatternsCheckbox();
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);
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;
before(() => {
cleanKibana();
});
beforeEach(() => {
login();
deleteAlertsAndRules();
cy.task('esArchiverResetKibana');
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' }));
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
it('Add index patterns to custom rules: one rule is updated, one rule is skipped', () => {
selectNumberOfRules(customRulesNumber);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
submitBulkEditForm();
waitForBulkEditActionToFinish({
updatedCount: 1,
skippedCount: 1,
showDataViewsWarning: true,
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();
});
// 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);
});
beforeEach(() => {
login();
deleteAlertsAndRules();
cy.task('esArchiverResetKibana');
it('Add index patterns to custom rules when overwrite data view checkbox is checked: all rules are updated', () => {
selectNumberOfRules(customRulesNumber);
postDataView(DATA_VIEW_ID);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
checkOverwriteDataViewCheckbox();
submitBulkEditForm();
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' }));
waitForBulkEditActionToFinish({
updatedCount: 2,
visitSecurityDetectionRulesPage();
disableAutoRefresh();
expectManagementTableRules(['with dataview', 'no data view']);
});
// check if rule still has data view and index patterns field does not exist
goToRuleDetails();
assertDetailsNotExist(DATA_VIEW_DETAILS);
});
});
it('Add index patterns to custom rules: one rule is updated, one rule is skipped', () => {
selectAllRules();
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
submitBulkEditForm();
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);
});
it('Add index patterns to custom rules when overwrite data view checkbox is checked: all rules are updated', () => {
selectAllRules();
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
checkOverwriteDataViewCheckbox();
submitBulkEditForm();
waitForBulkEditActionToFinish({
updatedCount: 2,
});
// check if rule still has data view and index patterns field does not exist
goToRuleDetails();
assertDetailsNotExist(DATA_VIEW_DETAILS);
});
}
);

View file

@ -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,
})
)
);

View file

@ -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));

View file

@ -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');
});

View file

@ -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);

View file

@ -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);
});

View file

@ -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');
});

View file

@ -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);
});

View file

@ -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');

View file

@ -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] },
() => {

View file

@ -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"]';

View file

@ -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}`;

View file

@ -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"]';

View file

@ -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<string[]>) => {
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<string[]>) => {
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');
};

View file

@ -74,45 +74,6 @@ export const excessivelyInstallAllPrebuiltRules = () => {
installAllPrebuiltRulesRequest();
};
export const waitUntilAllRuleAssetsCreated = (
rules: Array<typeof SAMPLE_PREBUILT_RULE>,
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<typeof SAMPLE_PREBUILT_RULE>;
rules: Array<typeof SAMPLE_PREBUILT_RULE>;
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();
}

View file

@ -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: {

View file

@ -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));
};