mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 10:40:07 -04:00
[DE][Cypress] Re-enable cypress serverless tests subset (#166501)
## Summary Re-enable a subset of cypress serverless tests: - Restructured `x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule.cy.ts` to break it down into and enable the following tests: - `rule_creation/common_components.cy.ts` - `rule_creation/custom_query_rule.cy.ts` - `rule_details/common_components.cy.ts` - `rule_details/custom_query_rule.cy.ts` - `rule_edit/custom_query_rule.cy.ts`
This commit is contained in:
parent
07f1c36df7
commit
50ddc979ca
13 changed files with 579 additions and 449 deletions
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
|
@ -1230,7 +1230,7 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
|
||||||
|
|
||||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules @elastic/security-detection-rule-management
|
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules @elastic/security-detection-rule-management
|
||||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management @elastic/security-detection-rule-management
|
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management @elastic/security-detection-rule-management
|
||||||
|
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_details @elastic/security-detection-rule-management
|
||||||
/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules @elastic/security-detection-rule-management
|
/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules @elastic/security-detection-rule-management
|
||||||
/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/rule_management @elastic/security-detection-rule-management
|
/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/rule_management @elastic/security-detection-rule-management
|
||||||
|
|
||||||
|
@ -1294,6 +1294,7 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
|
||||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts @elastic/security-detection-engine
|
/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts @elastic/security-detection-engine
|
||||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions @elastic/security-detection-engine
|
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions @elastic/security-detection-engine
|
||||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation @elastic/security-detection-engine
|
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation @elastic/security-detection-engine
|
||||||
|
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_edit @elastic/security-detection-engine
|
||||||
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/value_lists @elastic/security-detection-engine
|
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/value_lists @elastic/security-detection-engine
|
||||||
/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics @elastic/security-detection-engine
|
/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics @elastic/security-detection-engine
|
||||||
/x-pack/test/security_solution_cypress/cypress/e2e/exceptions @elastic/security-detection-engine
|
/x-pack/test/security_solution_cypress/cypress/e2e/exceptions @elastic/security-detection-engine
|
||||||
|
|
|
@ -19,6 +19,7 @@ interface MultiSelectAutocompleteProps {
|
||||||
field: FieldHook;
|
field: FieldHook;
|
||||||
fullWidth?: boolean;
|
fullWidth?: boolean;
|
||||||
disabledText?: string;
|
disabledText?: string;
|
||||||
|
dataTestSubj?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FIELD_COMBO_BOX_WIDTH = 410;
|
const FIELD_COMBO_BOX_WIDTH = 410;
|
||||||
|
@ -31,6 +32,7 @@ export const MultiSelectAutocompleteComponent: React.FC<MultiSelectAutocompleteP
|
||||||
isDisabled,
|
isDisabled,
|
||||||
field,
|
field,
|
||||||
fullWidth = false,
|
fullWidth = false,
|
||||||
|
dataTestSubj,
|
||||||
}: MultiSelectAutocompleteProps) => {
|
}: MultiSelectAutocompleteProps) => {
|
||||||
const fieldEuiFieldProps = useMemo(
|
const fieldEuiFieldProps = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
@ -45,7 +47,12 @@ export const MultiSelectAutocompleteComponent: React.FC<MultiSelectAutocompleteP
|
||||||
[browserFields, isDisabled, fullWidth]
|
[browserFields, isDisabled, fullWidth]
|
||||||
);
|
);
|
||||||
const fieldComponent = (
|
const fieldComponent = (
|
||||||
<Field field={field} idAria={fieldDescribedByIds} euiFieldProps={fieldEuiFieldProps} />
|
<Field
|
||||||
|
field={field}
|
||||||
|
idAria={fieldDescribedByIds}
|
||||||
|
euiFieldProps={fieldEuiFieldProps}
|
||||||
|
data-test-subj={dataTestSubj}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
return isDisabled ? (
|
return isDisabled ? (
|
||||||
<EuiToolTip position="right" content={disabledText}>
|
<EuiToolTip position="right" content={disabledText}>
|
||||||
|
|
|
@ -249,6 +249,7 @@ const StepAboutRuleComponent: FC<StepAboutRuleProps> = ({
|
||||||
browserFields: indexPattern.fields,
|
browserFields: indexPattern.fields,
|
||||||
isDisabled: isLoading || indexPatternLoading,
|
isDisabled: isLoading || indexPatternLoading,
|
||||||
fullWidth: true,
|
fullWidth: true,
|
||||||
|
dataTestSubj: 'detectionEngineStepAboutRuleInvestigationFields',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<EuiSpacer size="l" />
|
<EuiSpacer size="l" />
|
||||||
|
|
|
@ -18,6 +18,7 @@ import type {
|
||||||
import type {
|
import type {
|
||||||
IndexPatternArray,
|
IndexPatternArray,
|
||||||
InvestigationGuide,
|
InvestigationGuide,
|
||||||
|
InvestigationFields,
|
||||||
RuleDescription,
|
RuleDescription,
|
||||||
RuleFalsePositiveArray,
|
RuleFalsePositiveArray,
|
||||||
RuleQuery,
|
RuleQuery,
|
||||||
|
@ -30,6 +31,7 @@ interface RuleFields {
|
||||||
defaultIndexPatterns: IndexPatternArray;
|
defaultIndexPatterns: IndexPatternArray;
|
||||||
falsePositives: RuleFalsePositiveArray;
|
falsePositives: RuleFalsePositiveArray;
|
||||||
investigationGuide: InvestigationGuide;
|
investigationGuide: InvestigationGuide;
|
||||||
|
investigationFields: InvestigationFields;
|
||||||
referenceUrls: RuleReferenceArray;
|
referenceUrls: RuleReferenceArray;
|
||||||
riskScore: RiskScore;
|
riskScore: RiskScore;
|
||||||
ruleDescription: RuleDescription;
|
ruleDescription: RuleDescription;
|
||||||
|
@ -58,6 +60,9 @@ export const ruleFields: RuleFields = {
|
||||||
],
|
],
|
||||||
falsePositives: ['False1', 'False2'],
|
falsePositives: ['False1', 'False2'],
|
||||||
investigationGuide: '# test markdown',
|
investigationGuide: '# test markdown',
|
||||||
|
investigationFields: {
|
||||||
|
field_names: ['agent.hostname'],
|
||||||
|
},
|
||||||
referenceUrls: ['http://example.com/', 'https://example.com/'],
|
referenceUrls: ['http://example.com/', 'https://example.com/'],
|
||||||
riskScore: 17,
|
riskScore: 17,
|
||||||
ruleDescription: 'The rule description',
|
ruleDescription: 'The rule description',
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ruleFields } from '../../../data/detection_engine';
|
||||||
|
import { getTimeline } from '../../../objects/timeline';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ABOUT_CONTINUE_BTN,
|
||||||
|
ABOUT_EDIT_BUTTON,
|
||||||
|
CUSTOM_QUERY_INPUT,
|
||||||
|
DEFINE_CONTINUE_BUTTON,
|
||||||
|
DEFINE_EDIT_BUTTON,
|
||||||
|
RULE_NAME_INPUT,
|
||||||
|
SCHEDULE_CONTINUE_BUTTON,
|
||||||
|
} from '../../../screens/create_new_rule';
|
||||||
|
import { RULE_NAME_HEADER } from '../../../screens/rule_details';
|
||||||
|
import { createTimeline } from '../../../tasks/api_calls/timelines';
|
||||||
|
import { deleteAlertsAndRules } from '../../../tasks/common';
|
||||||
|
import {
|
||||||
|
createAndEnableRuleOnly,
|
||||||
|
expandAdvancedSettings,
|
||||||
|
fillCustomInvestigationFields,
|
||||||
|
fillDescription,
|
||||||
|
fillFalsePositiveExamples,
|
||||||
|
fillFrom,
|
||||||
|
fillNote,
|
||||||
|
fillReferenceUrls,
|
||||||
|
fillRiskScore,
|
||||||
|
fillRuleName,
|
||||||
|
fillRuleTags,
|
||||||
|
fillSeverity,
|
||||||
|
fillThreat,
|
||||||
|
fillThreatSubtechnique,
|
||||||
|
fillThreatTechnique,
|
||||||
|
importSavedQuery,
|
||||||
|
} from '../../../tasks/create_new_rule';
|
||||||
|
import { login } from '../../../tasks/login';
|
||||||
|
import { CREATE_RULE_URL } from '../../../urls/navigation';
|
||||||
|
import { visit } from '../../../tasks/navigation';
|
||||||
|
|
||||||
|
// This test is meant to test touching all the common various components in rule creation
|
||||||
|
// to ensure we don't miss any changes that maybe affect one of these more obscure UI components
|
||||||
|
// in the creation form. For any rule type specific functionalities, please include
|
||||||
|
// them in the relevant /rule_creation/[RULE_TYPE].cy.ts test.
|
||||||
|
describe('Common rule creation flows', { tags: ['@ess', '@serverless'] }, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
deleteAlertsAndRules();
|
||||||
|
createTimeline(getTimeline())
|
||||||
|
.then((response) => {
|
||||||
|
return response.body.data.persistTimeline.timeline.savedObjectId;
|
||||||
|
})
|
||||||
|
.as('timelineId');
|
||||||
|
login();
|
||||||
|
visit(CREATE_RULE_URL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Creates and enables a rule', function () {
|
||||||
|
cy.log('Filling define section');
|
||||||
|
importSavedQuery(this.timelineId);
|
||||||
|
cy.get(DEFINE_CONTINUE_BUTTON).click();
|
||||||
|
|
||||||
|
cy.log('Filling about section');
|
||||||
|
fillRuleName();
|
||||||
|
fillDescription();
|
||||||
|
fillSeverity();
|
||||||
|
fillRiskScore();
|
||||||
|
fillRuleTags();
|
||||||
|
expandAdvancedSettings();
|
||||||
|
fillReferenceUrls();
|
||||||
|
fillFalsePositiveExamples();
|
||||||
|
fillThreat();
|
||||||
|
fillThreatTechnique();
|
||||||
|
fillThreatSubtechnique();
|
||||||
|
fillCustomInvestigationFields();
|
||||||
|
fillNote();
|
||||||
|
cy.get(ABOUT_CONTINUE_BTN).click();
|
||||||
|
|
||||||
|
cy.log('Filling schedule section');
|
||||||
|
fillFrom();
|
||||||
|
|
||||||
|
// expect define step to repopulate
|
||||||
|
cy.get(DEFINE_EDIT_BUTTON).click();
|
||||||
|
cy.get(CUSTOM_QUERY_INPUT).should('have.value', ruleFields.ruleQuery);
|
||||||
|
cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click();
|
||||||
|
|
||||||
|
// expect about step to populate
|
||||||
|
cy.get(ABOUT_EDIT_BUTTON).click();
|
||||||
|
cy.get(RULE_NAME_INPUT).invoke('val').should('eql', ruleFields.ruleName);
|
||||||
|
cy.get(ABOUT_CONTINUE_BTN).should('exist').click();
|
||||||
|
cy.get(SCHEDULE_CONTINUE_BUTTON).click();
|
||||||
|
|
||||||
|
createAndEnableRuleOnly();
|
||||||
|
|
||||||
|
cy.get(RULE_NAME_HEADER).should('contain', ruleFields.ruleName);
|
||||||
|
});
|
||||||
|
});
|
|
@ -5,464 +5,42 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ruleFields } from '../../../data/detection_engine';
|
import { getNewRule } from '../../../objects/rule';
|
||||||
import {
|
import { RULE_NAME_HEADER } from '../../../screens/rule_details';
|
||||||
getNewRule,
|
|
||||||
getExistingRule,
|
|
||||||
getEditedRule,
|
|
||||||
getNewOverrideRule,
|
|
||||||
} from '../../../objects/rule';
|
|
||||||
import { getTimeline } from '../../../objects/timeline';
|
|
||||||
import { ALERTS_COUNT, ALERT_GRID_CELL } from '../../../screens/alerts';
|
|
||||||
|
|
||||||
|
import { deleteAlertsAndRules } from '../../../tasks/common';
|
||||||
import {
|
import {
|
||||||
CUSTOM_RULES_BTN,
|
createAndEnableRuleOnly,
|
||||||
RISK_SCORE,
|
fillScheduleRuleAndContinue,
|
||||||
RULE_NAME,
|
fillAboutRuleMinimumAndContinue,
|
||||||
RULES_MANAGEMENT_TABLE,
|
fillDefineCustomRuleAndContinue,
|
||||||
RULE_SWITCH,
|
|
||||||
SEVERITY,
|
|
||||||
} from '../../../screens/alerts_detection_rules';
|
|
||||||
import {
|
|
||||||
ACTIONS_NOTIFY_WHEN_BUTTON,
|
|
||||||
ACTIONS_SUMMARY_BUTTON,
|
|
||||||
} from '../../../screens/common/rule_actions';
|
|
||||||
import {
|
|
||||||
ABOUT_CONTINUE_BTN,
|
|
||||||
ABOUT_EDIT_BUTTON,
|
|
||||||
CUSTOM_QUERY_INPUT,
|
|
||||||
DEFINE_CONTINUE_BUTTON,
|
|
||||||
DEFINE_EDIT_BUTTON,
|
|
||||||
DEFINE_INDEX_INPUT,
|
|
||||||
DEFAULT_RISK_SCORE_INPUT,
|
|
||||||
RULE_DESCRIPTION_INPUT,
|
|
||||||
RULE_NAME_INPUT,
|
|
||||||
SCHEDULE_INTERVAL_AMOUNT_INPUT,
|
|
||||||
SCHEDULE_INTERVAL_UNITS_INPUT,
|
|
||||||
SCHEDULE_CONTINUE_BUTTON,
|
|
||||||
SEVERITY_DROPDOWN,
|
|
||||||
TAGS_CLEAR_BUTTON,
|
|
||||||
TAGS_FIELD,
|
|
||||||
} from '../../../screens/create_new_rule';
|
|
||||||
import {
|
|
||||||
ADDITIONAL_LOOK_BACK_DETAILS,
|
|
||||||
ABOUT_DETAILS,
|
|
||||||
ABOUT_INVESTIGATION_NOTES,
|
|
||||||
ABOUT_RULE_DESCRIPTION,
|
|
||||||
CUSTOM_QUERY_DETAILS,
|
|
||||||
DEFINITION_DETAILS,
|
|
||||||
FALSE_POSITIVES_DETAILS,
|
|
||||||
removeExternalLinkText,
|
|
||||||
INDEX_PATTERNS_DETAILS,
|
|
||||||
INVESTIGATION_NOTES_MARKDOWN,
|
|
||||||
INVESTIGATION_NOTES_TOGGLE,
|
|
||||||
REFERENCE_URLS_DETAILS,
|
|
||||||
RISK_SCORE_DETAILS,
|
|
||||||
RULE_NAME_HEADER,
|
|
||||||
RULE_TYPE_DETAILS,
|
|
||||||
RUNS_EVERY_DETAILS,
|
|
||||||
SCHEDULE_DETAILS,
|
|
||||||
SEVERITY_DETAILS,
|
|
||||||
TAGS_DETAILS,
|
|
||||||
TIMELINE_TEMPLATE_DETAILS,
|
|
||||||
THREAT_TACTIC,
|
|
||||||
THREAT_TECHNIQUE,
|
|
||||||
THREAT_SUBTECHNIQUE,
|
|
||||||
} from '../../../screens/rule_details';
|
|
||||||
|
|
||||||
import {
|
|
||||||
deleteFirstRule,
|
|
||||||
deleteRuleFromDetailsPage,
|
|
||||||
expectManagementTableRules,
|
|
||||||
getRulesManagementTableRows,
|
|
||||||
goToRuleDetailsOf,
|
|
||||||
selectRulesByName,
|
|
||||||
} from '../../../tasks/alerts_detection_rules';
|
|
||||||
import { deleteSelectedRules } from '../../../tasks/rules_bulk_actions';
|
|
||||||
import { createRule, findAllRules } from '../../../tasks/api_calls/rules';
|
|
||||||
import { createTimeline } from '../../../tasks/api_calls/timelines';
|
|
||||||
import { deleteAlertsAndRules, deleteConnectors } from '../../../tasks/common';
|
|
||||||
import { addEmailConnectorAndRuleAction } from '../../../tasks/common/rule_actions';
|
|
||||||
import {
|
|
||||||
createAndEnableRule,
|
|
||||||
expandAdvancedSettings,
|
|
||||||
fillAboutRule,
|
|
||||||
fillDescription,
|
|
||||||
fillFalsePositiveExamples,
|
|
||||||
fillFrom,
|
|
||||||
fillNote,
|
|
||||||
fillReferenceUrls,
|
|
||||||
fillRiskScore,
|
|
||||||
fillRuleName,
|
|
||||||
fillRuleTags,
|
|
||||||
fillSeverity,
|
|
||||||
fillThreat,
|
|
||||||
fillThreatSubtechnique,
|
|
||||||
fillThreatTechnique,
|
|
||||||
goToAboutStepTab,
|
|
||||||
goToActionsStepTab,
|
|
||||||
goToScheduleStepTab,
|
|
||||||
importSavedQuery,
|
|
||||||
waitForAlertsToPopulate,
|
|
||||||
} from '../../../tasks/create_new_rule';
|
} from '../../../tasks/create_new_rule';
|
||||||
import { saveEditedRule, visitEditRulePage } from '../../../tasks/edit_rule';
|
|
||||||
import { login } from '../../../tasks/login';
|
import { login } from '../../../tasks/login';
|
||||||
import { visitWithTimeRange } from '../../../tasks/navigation';
|
import { visit } from '../../../tasks/navigation';
|
||||||
import {
|
|
||||||
enablesRule,
|
|
||||||
getDetails,
|
|
||||||
visitRuleDetailsPage,
|
|
||||||
waitForTheRuleToBeExecuted,
|
|
||||||
} from '../../../tasks/rule_details';
|
|
||||||
import { CREATE_RULE_URL } from '../../../urls/navigation';
|
import { CREATE_RULE_URL } from '../../../urls/navigation';
|
||||||
import { visitRulesManagementTable } from '../../../tasks/rules_management';
|
|
||||||
|
|
||||||
// TODO: https://github.com/elastic/kibana/issues/161539
|
describe('Create custom query rule', { tags: ['@ess', '@serverless'] }, () => {
|
||||||
describe('Custom query rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
|
const rule = getNewRule();
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
deleteAlertsAndRules();
|
deleteAlertsAndRules();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Custom detection rules creation', () => {
|
describe('Custom detection rules creation', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createTimeline(getTimeline())
|
deleteAlertsAndRules();
|
||||||
.then((response) => {
|
|
||||||
return response.body.data.persistTimeline.timeline.savedObjectId;
|
|
||||||
})
|
|
||||||
.as('timelineId');
|
|
||||||
login();
|
login();
|
||||||
|
visit(CREATE_RULE_URL);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Creates and enables a new rule', function () {
|
it('Creates and enables a rule', function () {
|
||||||
visitWithTimeRange(CREATE_RULE_URL);
|
fillDefineCustomRuleAndContinue(rule);
|
||||||
|
fillAboutRuleMinimumAndContinue(rule);
|
||||||
cy.log('Filling define section');
|
fillScheduleRuleAndContinue(rule);
|
||||||
importSavedQuery(this.timelineId);
|
createAndEnableRuleOnly();
|
||||||
cy.get(DEFINE_CONTINUE_BUTTON).click();
|
|
||||||
|
|
||||||
cy.log('Filling about section');
|
|
||||||
fillRuleName('Test Rule');
|
|
||||||
fillDescription();
|
|
||||||
fillSeverity();
|
|
||||||
fillRiskScore();
|
|
||||||
fillRuleTags();
|
|
||||||
expandAdvancedSettings();
|
|
||||||
fillReferenceUrls();
|
|
||||||
fillFalsePositiveExamples();
|
|
||||||
fillThreat();
|
|
||||||
fillThreatTechnique();
|
|
||||||
fillThreatSubtechnique();
|
|
||||||
fillNote();
|
|
||||||
cy.get(ABOUT_CONTINUE_BTN).click();
|
|
||||||
|
|
||||||
cy.log('Filling schedule section');
|
|
||||||
fillFrom();
|
|
||||||
|
|
||||||
// expect define step to repopulate
|
|
||||||
cy.get(DEFINE_EDIT_BUTTON).click();
|
|
||||||
cy.get(CUSTOM_QUERY_INPUT).should('have.value', ruleFields.ruleQuery);
|
|
||||||
cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click();
|
|
||||||
|
|
||||||
// expect about step to populate
|
|
||||||
cy.get(ABOUT_EDIT_BUTTON).click();
|
|
||||||
cy.get(RULE_NAME_INPUT).invoke('val').should('eql', ruleFields.ruleName);
|
|
||||||
cy.get(ABOUT_CONTINUE_BTN).should('exist').click();
|
|
||||||
cy.get(SCHEDULE_CONTINUE_BUTTON).click();
|
|
||||||
|
|
||||||
createAndEnableRule();
|
|
||||||
|
|
||||||
cy.log('Asserting we have a new rule created');
|
cy.log('Asserting we have a new rule created');
|
||||||
cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)');
|
cy.get(RULE_NAME_HEADER).should('contain', rule.name);
|
||||||
|
|
||||||
cy.log('Asserting rule view in rules list');
|
|
||||||
expectManagementTableRules(['Test Rule']);
|
|
||||||
cy.get(RULE_NAME).should('have.text', ruleFields.ruleName);
|
|
||||||
cy.get(RISK_SCORE).should('have.text', ruleFields.riskScore);
|
|
||||||
cy.get(SEVERITY)
|
|
||||||
.invoke('text')
|
|
||||||
.then((text) => {
|
|
||||||
cy.wrap(text.toLowerCase()).should('equal', ruleFields.ruleSeverity);
|
|
||||||
});
|
|
||||||
cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true');
|
|
||||||
|
|
||||||
goToRuleDetailsOf(ruleFields.ruleName);
|
|
||||||
|
|
||||||
cy.log('Asserting rule details');
|
|
||||||
cy.get(RULE_NAME_HEADER).should('contain', ruleFields.ruleName);
|
|
||||||
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', ruleFields.ruleDescription);
|
|
||||||
cy.get(ABOUT_DETAILS).within(() => {
|
|
||||||
getDetails(SEVERITY_DETAILS)
|
|
||||||
.invoke('text')
|
|
||||||
.then((text) => {
|
|
||||||
cy.wrap(text.toLowerCase()).should('equal', ruleFields.ruleSeverity);
|
|
||||||
});
|
|
||||||
getDetails(RISK_SCORE_DETAILS).should('have.text', ruleFields.riskScore);
|
|
||||||
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
|
|
||||||
expect(removeExternalLinkText(details.text())).equal(ruleFields.referenceUrls.join(''));
|
|
||||||
});
|
|
||||||
getDetails(FALSE_POSITIVES_DETAILS).should('have.text', ruleFields.falsePositives.join(''));
|
|
||||||
getDetails(TAGS_DETAILS).should('have.text', ruleFields.ruleTags.join(''));
|
|
||||||
});
|
|
||||||
cy.get(THREAT_TACTIC).should(
|
|
||||||
'contain',
|
|
||||||
`${ruleFields.threat.tactic.name} (${ruleFields.threat.tactic.id})`
|
|
||||||
);
|
|
||||||
cy.get(THREAT_TECHNIQUE).should(
|
|
||||||
'contain',
|
|
||||||
`${ruleFields.threatTechnique.name} (${ruleFields.threatTechnique.id})`
|
|
||||||
);
|
|
||||||
cy.get(THREAT_SUBTECHNIQUE).should(
|
|
||||||
'contain',
|
|
||||||
`${ruleFields.threatSubtechnique.name} (${ruleFields.threatSubtechnique.id})`
|
|
||||||
);
|
|
||||||
cy.get(INVESTIGATION_NOTES_TOGGLE).click();
|
|
||||||
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN);
|
|
||||||
cy.get(DEFINITION_DETAILS).within(() => {
|
|
||||||
getDetails(INDEX_PATTERNS_DETAILS).should('have.text', 'auditbeat-*');
|
|
||||||
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', ruleFields.ruleQuery);
|
|
||||||
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
|
|
||||||
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
|
|
||||||
});
|
|
||||||
cy.get(SCHEDULE_DETAILS).within(() => {
|
|
||||||
getDetails(RUNS_EVERY_DETAILS).should('have.text', ruleFields.ruleInterval);
|
|
||||||
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', ruleFields.ruleIntervalFrom);
|
|
||||||
});
|
|
||||||
|
|
||||||
waitForTheRuleToBeExecuted();
|
|
||||||
waitForAlertsToPopulate();
|
|
||||||
|
|
||||||
cy.log('Asserting that alerts have been generated after the creation');
|
|
||||||
cy.get(ALERTS_COUNT)
|
|
||||||
.invoke('text')
|
|
||||||
.should('match', /^[1-9].+$/); // Any number of alerts
|
|
||||||
cy.get(ALERT_GRID_CELL).contains(ruleFields.ruleName);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Custom detection rules deletion and edition', () => {
|
|
||||||
context('Deletion', () => {
|
|
||||||
const TESTED_RULE_DATA = getNewRule({
|
|
||||||
rule_id: 'rule1',
|
|
||||||
name: 'New Rule Test',
|
|
||||||
enabled: false,
|
|
||||||
max_signals: 500,
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
createRule(TESTED_RULE_DATA);
|
|
||||||
createRule(
|
|
||||||
getNewOverrideRule({
|
|
||||||
rule_id: 'rule2',
|
|
||||||
name: 'Override Rule',
|
|
||||||
enabled: false,
|
|
||||||
max_signals: 500,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
createRule(getExistingRule({ rule_id: 'rule3', name: 'Rule 1', enabled: false }));
|
|
||||||
login();
|
|
||||||
visitRulesManagementTable();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Deletes one rule', () => {
|
|
||||||
getRulesManagementTableRows().then((rules) => {
|
|
||||||
const initialNumberOfRules = rules.length;
|
|
||||||
const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1;
|
|
||||||
|
|
||||||
findAllRules().then(({ body }) => {
|
|
||||||
const numberOfRules = body.data.length;
|
|
||||||
expect(numberOfRules).to.eql(initialNumberOfRules);
|
|
||||||
});
|
|
||||||
|
|
||||||
deleteFirstRule();
|
|
||||||
|
|
||||||
getRulesManagementTableRows().should('have.length', expectedNumberOfRulesAfterDeletion);
|
|
||||||
findAllRules().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', () => {
|
|
||||||
getRulesManagementTableRows().then((rules) => {
|
|
||||||
const rulesToDelete = [TESTED_RULE_DATA.name, 'Override Rule'] as const;
|
|
||||||
const initialNumberOfRules = rules.length;
|
|
||||||
const numberOfRulesToBeDeleted = 2;
|
|
||||||
const expectedNumberOfRulesAfterDeletion =
|
|
||||||
initialNumberOfRules - numberOfRulesToBeDeleted;
|
|
||||||
|
|
||||||
selectRulesByName(rulesToDelete);
|
|
||||||
deleteSelectedRules();
|
|
||||||
|
|
||||||
getRulesManagementTableRows()
|
|
||||||
.first()
|
|
||||||
.within(() => {
|
|
||||||
cy.get(RULE_SWITCH).should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
getRulesManagementTableRows().should('have.length', expectedNumberOfRulesAfterDeletion);
|
|
||||||
findAllRules().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;
|
|
||||||
|
|
||||||
goToRuleDetailsOf(TESTED_RULE_DATA.name);
|
|
||||||
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);
|
|
||||||
findAllRules().then(({ body }) => {
|
|
||||||
const numberOfRules = body.data.length;
|
|
||||||
expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion);
|
|
||||||
});
|
|
||||||
cy.get(CUSTOM_RULES_BTN).should(
|
|
||||||
'have.text',
|
|
||||||
`Custom rules (${expectedNumberOfRulesAfterDeletion})`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('Edition', () => {
|
|
||||||
const editedRuleData = getEditedRule();
|
|
||||||
const expectedEditedTags = editedRuleData.tags?.join('');
|
|
||||||
const expectedEditedIndexPatterns = editedRuleData.index;
|
|
||||||
|
|
||||||
describe('on rule details page', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
deleteConnectors();
|
|
||||||
login();
|
|
||||||
createRule(getExistingRule({ rule_id: 'rule1', enabled: true })).then((rule) =>
|
|
||||||
visitRuleDetailsPage(rule.body.id)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Only modifies rule active status on enable/disable', () => {
|
|
||||||
enablesRule();
|
|
||||||
|
|
||||||
cy.intercept('GET', `/api/detection_engine/rules?id=*`).as('fetchRuleDetails');
|
|
||||||
|
|
||||||
cy.wait('@fetchRuleDetails').then(({ response }) => {
|
|
||||||
cy.wrap(response?.statusCode).should('eql', 200);
|
|
||||||
|
|
||||||
cy.wrap(response?.body.max_signals).should('eql', getExistingRule().max_signals);
|
|
||||||
cy.wrap(response?.body.enabled).should('eql', false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('on rule editing page', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
deleteConnectors();
|
|
||||||
login();
|
|
||||||
createRule(getExistingRule({ rule_id: 'rule1', enabled: true })).then((rule) =>
|
|
||||||
visitEditRulePage(rule.body.id)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Allows a rule to be edited', () => {
|
|
||||||
const existingRule = getExistingRule();
|
|
||||||
|
|
||||||
// expect define step to populate
|
|
||||||
cy.get(CUSTOM_QUERY_INPUT).should('have.value', existingRule.query);
|
|
||||||
|
|
||||||
cy.get(DEFINE_INDEX_INPUT).should('have.text', existingRule.index?.join(''));
|
|
||||||
|
|
||||||
goToAboutStepTab();
|
|
||||||
|
|
||||||
// expect about step to populate
|
|
||||||
cy.get(RULE_NAME_INPUT).invoke('val').should('eql', existingRule.name);
|
|
||||||
cy.get(RULE_DESCRIPTION_INPUT).should('have.text', existingRule.description);
|
|
||||||
cy.get(TAGS_FIELD).should('have.text', existingRule.tags?.join(''));
|
|
||||||
cy.get(SEVERITY_DROPDOWN).should('have.text', 'High');
|
|
||||||
cy.get(DEFAULT_RISK_SCORE_INPUT)
|
|
||||||
.invoke('val')
|
|
||||||
.should('eql', `${existingRule.risk_score}`);
|
|
||||||
|
|
||||||
goToScheduleStepTab();
|
|
||||||
|
|
||||||
// expect schedule step to populate
|
|
||||||
const interval = existingRule.interval;
|
|
||||||
const intervalParts = interval != null && interval.match(/[0-9]+|[a-zA-Z]+/g);
|
|
||||||
if (intervalParts) {
|
|
||||||
const [amount, unit] = intervalParts;
|
|
||||||
cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', amount);
|
|
||||||
cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', unit);
|
|
||||||
} else {
|
|
||||||
throw new Error('Cannot assert scheduling info on a rule without an interval');
|
|
||||||
}
|
|
||||||
|
|
||||||
goToActionsStepTab();
|
|
||||||
|
|
||||||
addEmailConnectorAndRuleAction('test@example.com', 'Subject');
|
|
||||||
|
|
||||||
cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'Summary of alerts');
|
|
||||||
cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Per rule run');
|
|
||||||
|
|
||||||
goToAboutStepTab();
|
|
||||||
cy.get(TAGS_CLEAR_BUTTON).click();
|
|
||||||
fillAboutRule(getEditedRule());
|
|
||||||
|
|
||||||
cy.intercept('GET', '/api/detection_engine/rules?id*').as('getRule');
|
|
||||||
|
|
||||||
saveEditedRule();
|
|
||||||
|
|
||||||
cy.wait('@getRule').then(({ response }) => {
|
|
||||||
cy.wrap(response?.statusCode).should('eql', 200);
|
|
||||||
// ensure that editing rule does not modify max_signals
|
|
||||||
cy.wrap(response?.body.max_signals).should('eql', existingRule.max_signals);
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(RULE_NAME_HEADER).should('contain', `${getEditedRule().name}`);
|
|
||||||
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getEditedRule().description);
|
|
||||||
cy.get(ABOUT_DETAILS).within(() => {
|
|
||||||
getDetails(SEVERITY_DETAILS).should('have.text', 'Medium');
|
|
||||||
getDetails(RISK_SCORE_DETAILS).should('have.text', `${getEditedRule().risk_score}`);
|
|
||||||
getDetails(TAGS_DETAILS).should('have.text', expectedEditedTags);
|
|
||||||
});
|
|
||||||
cy.get(INVESTIGATION_NOTES_TOGGLE).click();
|
|
||||||
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', getEditedRule().note);
|
|
||||||
cy.get(DEFINITION_DETAILS).within(() => {
|
|
||||||
getDetails(INDEX_PATTERNS_DETAILS).should(
|
|
||||||
'have.text',
|
|
||||||
expectedEditedIndexPatterns?.join('')
|
|
||||||
);
|
|
||||||
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', getEditedRule().query);
|
|
||||||
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
|
|
||||||
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
|
|
||||||
});
|
|
||||||
if (getEditedRule().interval) {
|
|
||||||
cy.get(SCHEDULE_DETAILS).within(() => {
|
|
||||||
getDetails(RUNS_EVERY_DETAILS).should('have.text', getEditedRule().interval);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { deleteRuleFromDetailsPage } from '../../../tasks/alerts_detection_rules';
|
||||||
|
import {
|
||||||
|
CUSTOM_RULES_BTN,
|
||||||
|
RULES_MANAGEMENT_TABLE,
|
||||||
|
RULES_ROW,
|
||||||
|
} from '../../../screens/alerts_detection_rules';
|
||||||
|
import { createRule } from '../../../tasks/api_calls/rules';
|
||||||
|
import { getDetails } from '../../../tasks/rule_details';
|
||||||
|
import { ruleFields } from '../../../data/detection_engine';
|
||||||
|
import { getTimeline } from '../../../objects/timeline';
|
||||||
|
import { getExistingRule, getNewRule } from '../../../objects/rule';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ABOUT_DETAILS,
|
||||||
|
ABOUT_INVESTIGATION_NOTES,
|
||||||
|
ABOUT_RULE_DESCRIPTION,
|
||||||
|
ADDITIONAL_LOOK_BACK_DETAILS,
|
||||||
|
CUSTOM_QUERY_DETAILS,
|
||||||
|
DEFINITION_DETAILS,
|
||||||
|
FALSE_POSITIVES_DETAILS,
|
||||||
|
INDEX_PATTERNS_DETAILS,
|
||||||
|
INVESTIGATION_NOTES_MARKDOWN,
|
||||||
|
INVESTIGATION_NOTES_TOGGLE,
|
||||||
|
REFERENCE_URLS_DETAILS,
|
||||||
|
removeExternalLinkText,
|
||||||
|
RISK_SCORE_DETAILS,
|
||||||
|
RULE_NAME_HEADER,
|
||||||
|
RULE_SWITCH,
|
||||||
|
RULE_TYPE_DETAILS,
|
||||||
|
RUNS_EVERY_DETAILS,
|
||||||
|
SCHEDULE_DETAILS,
|
||||||
|
SEVERITY_DETAILS,
|
||||||
|
TAGS_DETAILS,
|
||||||
|
THREAT_SUBTECHNIQUE,
|
||||||
|
THREAT_TACTIC,
|
||||||
|
THREAT_TECHNIQUE,
|
||||||
|
TIMELINE_TEMPLATE_DETAILS,
|
||||||
|
} from '../../../screens/rule_details';
|
||||||
|
|
||||||
|
import { createTimeline } from '../../../tasks/api_calls/timelines';
|
||||||
|
import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../../tasks/common';
|
||||||
|
import { login } from '../../../tasks/login';
|
||||||
|
import { visit } from '../../../tasks/navigation';
|
||||||
|
import { ruleDetailsUrl } from '../../../urls/rule_details';
|
||||||
|
|
||||||
|
// This test is meant to test all common aspects of the rule details page that should function
|
||||||
|
// the same regardless of rule type. For any rule type specific functionalities, please include
|
||||||
|
// them in the relevant /rule_details/[RULE_TYPE].cy.ts test.
|
||||||
|
describe('Common rule detail flows', { tags: ['@ess', '@serverless'] }, () => {
|
||||||
|
before(() => {
|
||||||
|
cleanKibana();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
deleteAlertsAndRules();
|
||||||
|
deleteConnectors();
|
||||||
|
login();
|
||||||
|
createTimeline(getTimeline()).then((response) => {
|
||||||
|
createRule({
|
||||||
|
...getNewRule({
|
||||||
|
rule_id: 'rulez',
|
||||||
|
description: ruleFields.ruleDescription,
|
||||||
|
name: ruleFields.ruleName,
|
||||||
|
severity: ruleFields.ruleSeverity,
|
||||||
|
risk_score: ruleFields.riskScore,
|
||||||
|
tags: ruleFields.ruleTags,
|
||||||
|
false_positives: ruleFields.falsePositives,
|
||||||
|
note: ruleFields.investigationGuide,
|
||||||
|
timeline_id: response.body.data.persistTimeline.timeline.savedObjectId,
|
||||||
|
timeline_title: response.body.data.persistTimeline.timeline.title ?? '',
|
||||||
|
interval: ruleFields.ruleInterval,
|
||||||
|
from: `now-1h`,
|
||||||
|
query: ruleFields.ruleQuery,
|
||||||
|
enabled: false,
|
||||||
|
max_signals: 500,
|
||||||
|
threat: [
|
||||||
|
{
|
||||||
|
...ruleFields.threat,
|
||||||
|
technique: [
|
||||||
|
{
|
||||||
|
...ruleFields.threatTechnique,
|
||||||
|
subtechnique: [ruleFields.threatSubtechnique],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}).then((rule) => {
|
||||||
|
visit(ruleDetailsUrl(rule.body.id));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Only modifies rule active status on enable/disable', () => {
|
||||||
|
cy.get(RULE_NAME_HEADER).should('contain', ruleFields.ruleName);
|
||||||
|
|
||||||
|
cy.intercept('POST', '/api/detection_engine/rules/_bulk_action?dry_run=false').as(
|
||||||
|
'bulk_action'
|
||||||
|
);
|
||||||
|
cy.get(RULE_SWITCH).should('be.visible');
|
||||||
|
cy.get(RULE_SWITCH).click();
|
||||||
|
cy.wait('@bulk_action').then(({ response }) => {
|
||||||
|
cy.wrap(response?.statusCode).should('eql', 200);
|
||||||
|
cy.wrap(response?.body.attributes.results.updated[0].max_signals).should(
|
||||||
|
'eql',
|
||||||
|
getExistingRule().max_signals
|
||||||
|
);
|
||||||
|
cy.wrap(response?.body.attributes.results.updated[0].enabled).should('eql', true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Displays rule details', function () {
|
||||||
|
cy.get(RULE_NAME_HEADER).should('contain', ruleFields.ruleName);
|
||||||
|
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', ruleFields.ruleDescription);
|
||||||
|
cy.get(ABOUT_DETAILS).within(() => {
|
||||||
|
getDetails(SEVERITY_DETAILS)
|
||||||
|
.invoke('text')
|
||||||
|
.then((text) => {
|
||||||
|
cy.wrap(text.toLowerCase()).should('equal', ruleFields.ruleSeverity);
|
||||||
|
});
|
||||||
|
getDetails(RISK_SCORE_DETAILS).should('have.text', ruleFields.riskScore);
|
||||||
|
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
|
||||||
|
expect(removeExternalLinkText(details.text())).equal(ruleFields.referenceUrls.join(''));
|
||||||
|
});
|
||||||
|
getDetails(FALSE_POSITIVES_DETAILS).should('have.text', ruleFields.falsePositives.join(''));
|
||||||
|
getDetails(TAGS_DETAILS).should('have.text', ruleFields.ruleTags.join(''));
|
||||||
|
});
|
||||||
|
cy.get(THREAT_TACTIC).should(
|
||||||
|
'contain',
|
||||||
|
`${ruleFields.threat.tactic.name} (${ruleFields.threat.tactic.id})`
|
||||||
|
);
|
||||||
|
cy.get(THREAT_TECHNIQUE).should(
|
||||||
|
'contain',
|
||||||
|
`${ruleFields.threatTechnique.name} (${ruleFields.threatTechnique.id})`
|
||||||
|
);
|
||||||
|
cy.get(THREAT_SUBTECHNIQUE).should(
|
||||||
|
'contain',
|
||||||
|
`${ruleFields.threatSubtechnique.name} (${ruleFields.threatSubtechnique.id})`
|
||||||
|
);
|
||||||
|
cy.get(INVESTIGATION_NOTES_TOGGLE).click();
|
||||||
|
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN);
|
||||||
|
cy.get(DEFINITION_DETAILS).within(() => {
|
||||||
|
getDetails(INDEX_PATTERNS_DETAILS).should(
|
||||||
|
'have.text',
|
||||||
|
ruleFields.defaultIndexPatterns.join('')
|
||||||
|
);
|
||||||
|
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', ruleFields.ruleQuery);
|
||||||
|
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
|
||||||
|
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'Security Timeline');
|
||||||
|
});
|
||||||
|
cy.get(SCHEDULE_DETAILS).within(() => {
|
||||||
|
getDetails(RUNS_EVERY_DETAILS).should('have.text', ruleFields.ruleInterval);
|
||||||
|
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', '55m');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Deletes one rule from detail page', () => {
|
||||||
|
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', 0);
|
||||||
|
cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => {
|
||||||
|
const numberOfRules = body.data.length;
|
||||||
|
expect(numberOfRules).to.eql(0);
|
||||||
|
});
|
||||||
|
cy.get(CUSTOM_RULES_BTN).should('have.text', `Custom rules (${0})`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { getExistingRule, getEditedRule } from '../../../objects/rule';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ACTIONS_NOTIFY_WHEN_BUTTON,
|
||||||
|
ACTIONS_SUMMARY_BUTTON,
|
||||||
|
} from '../../../screens/common/rule_actions';
|
||||||
|
import {
|
||||||
|
CUSTOM_QUERY_INPUT,
|
||||||
|
DEFINE_INDEX_INPUT,
|
||||||
|
DEFAULT_RISK_SCORE_INPUT,
|
||||||
|
RULE_DESCRIPTION_INPUT,
|
||||||
|
RULE_NAME_INPUT,
|
||||||
|
SCHEDULE_INTERVAL_AMOUNT_INPUT,
|
||||||
|
SCHEDULE_INTERVAL_UNITS_INPUT,
|
||||||
|
SEVERITY_DROPDOWN,
|
||||||
|
TAGS_CLEAR_BUTTON,
|
||||||
|
TAGS_FIELD,
|
||||||
|
} from '../../../screens/create_new_rule';
|
||||||
|
import {
|
||||||
|
ABOUT_DETAILS,
|
||||||
|
ABOUT_INVESTIGATION_NOTES,
|
||||||
|
ABOUT_RULE_DESCRIPTION,
|
||||||
|
CUSTOM_QUERY_DETAILS,
|
||||||
|
DEFINITION_DETAILS,
|
||||||
|
INDEX_PATTERNS_DETAILS,
|
||||||
|
INVESTIGATION_NOTES_TOGGLE,
|
||||||
|
RISK_SCORE_DETAILS,
|
||||||
|
RULE_NAME_HEADER,
|
||||||
|
RULE_TYPE_DETAILS,
|
||||||
|
RUNS_EVERY_DETAILS,
|
||||||
|
SCHEDULE_DETAILS,
|
||||||
|
SEVERITY_DETAILS,
|
||||||
|
TAGS_DETAILS,
|
||||||
|
TIMELINE_TEMPLATE_DETAILS,
|
||||||
|
} from '../../../screens/rule_details';
|
||||||
|
|
||||||
|
import { createRule } from '../../../tasks/api_calls/rules';
|
||||||
|
import { deleteAlertsAndRules, deleteConnectors } from '../../../tasks/common';
|
||||||
|
import { addEmailConnectorAndRuleAction } from '../../../tasks/common/rule_actions';
|
||||||
|
import {
|
||||||
|
fillAboutRule,
|
||||||
|
goToAboutStepTab,
|
||||||
|
goToActionsStepTab,
|
||||||
|
goToScheduleStepTab,
|
||||||
|
} from '../../../tasks/create_new_rule';
|
||||||
|
import { saveEditedRule, visitEditRulePage } from '../../../tasks/edit_rule';
|
||||||
|
import { login } from '../../../tasks/login';
|
||||||
|
import { getDetails } from '../../../tasks/rule_details';
|
||||||
|
|
||||||
|
describe('Custom query rules', { tags: ['@ess', '@serverless'] }, () => {
|
||||||
|
const rule = getEditedRule();
|
||||||
|
const expectedEditedtags = rule.tags?.join('');
|
||||||
|
const expectedEditedIndexPatterns = rule.index;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
deleteConnectors();
|
||||||
|
deleteAlertsAndRules();
|
||||||
|
login();
|
||||||
|
createRule(getExistingRule({ rule_id: 'rule1', enabled: true })).then((createdRule) => {
|
||||||
|
visitEditRulePage(createdRule.body.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Allows a rule to be edited', () => {
|
||||||
|
const existingRule = getExistingRule();
|
||||||
|
|
||||||
|
// expect define step to populate
|
||||||
|
cy.get(CUSTOM_QUERY_INPUT).should('have.value', existingRule.query);
|
||||||
|
|
||||||
|
cy.get(DEFINE_INDEX_INPUT).should('have.text', existingRule.index?.join(''));
|
||||||
|
|
||||||
|
goToAboutStepTab();
|
||||||
|
|
||||||
|
// expect about step to populate
|
||||||
|
cy.get(RULE_NAME_INPUT).invoke('val').should('eql', existingRule.name);
|
||||||
|
cy.get(RULE_DESCRIPTION_INPUT).should('have.text', existingRule.description);
|
||||||
|
cy.get(TAGS_FIELD).should('have.text', existingRule.tags?.join(''));
|
||||||
|
cy.get(SEVERITY_DROPDOWN).should('have.text', 'High');
|
||||||
|
cy.get(DEFAULT_RISK_SCORE_INPUT).invoke('val').should('eql', `${existingRule.risk_score}`);
|
||||||
|
|
||||||
|
goToScheduleStepTab();
|
||||||
|
|
||||||
|
// expect schedule step to populate
|
||||||
|
const interval = existingRule.interval;
|
||||||
|
const intervalParts = interval != null && interval.match(/[0-9]+|[a-zA-Z]+/g);
|
||||||
|
if (intervalParts) {
|
||||||
|
const [amount, unit] = intervalParts;
|
||||||
|
cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', amount);
|
||||||
|
cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', unit);
|
||||||
|
} else {
|
||||||
|
throw new Error('Cannot assert scheduling info on a rule without an interval');
|
||||||
|
}
|
||||||
|
|
||||||
|
goToActionsStepTab();
|
||||||
|
|
||||||
|
addEmailConnectorAndRuleAction('test@example.com', 'Subject');
|
||||||
|
|
||||||
|
cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'Summary of alerts');
|
||||||
|
cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Per rule run');
|
||||||
|
|
||||||
|
goToAboutStepTab();
|
||||||
|
cy.get(TAGS_CLEAR_BUTTON).click();
|
||||||
|
fillAboutRule(getEditedRule());
|
||||||
|
|
||||||
|
cy.intercept('GET', '/api/detection_engine/rules?id*').as('getRule');
|
||||||
|
|
||||||
|
saveEditedRule();
|
||||||
|
|
||||||
|
cy.wait('@getRule').then(({ response }) => {
|
||||||
|
cy.wrap(response?.statusCode).should('eql', 200);
|
||||||
|
// ensure that editing rule does not modify max_signals
|
||||||
|
cy.wrap(response?.body.max_signals).should('eql', existingRule.max_signals);
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get(RULE_NAME_HEADER).should('contain', `${getEditedRule().name}`);
|
||||||
|
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getEditedRule().description);
|
||||||
|
cy.get(ABOUT_DETAILS).within(() => {
|
||||||
|
getDetails(SEVERITY_DETAILS).should('have.text', 'Medium');
|
||||||
|
getDetails(RISK_SCORE_DETAILS).should('have.text', `${getEditedRule().risk_score}`);
|
||||||
|
getDetails(TAGS_DETAILS).should('have.text', expectedEditedtags);
|
||||||
|
});
|
||||||
|
cy.get(INVESTIGATION_NOTES_TOGGLE).click();
|
||||||
|
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', getEditedRule().note);
|
||||||
|
cy.get(DEFINITION_DETAILS).within(() => {
|
||||||
|
getDetails(INDEX_PATTERNS_DETAILS).should('have.text', expectedEditedIndexPatterns?.join(''));
|
||||||
|
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', getEditedRule().query);
|
||||||
|
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
|
||||||
|
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
|
||||||
|
});
|
||||||
|
if (getEditedRule().interval) {
|
||||||
|
cy.get(SCHEDULE_DETAILS).within(() => {
|
||||||
|
getDetails(RUNS_EVERY_DETAILS).should('have.text', getEditedRule().interval);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { visitRulesManagementTable } from '../../../../../tasks/rules_management';
|
||||||
|
import { getNewRule } from '../../../../../objects/rule';
|
||||||
|
|
||||||
|
import { RULE_SWITCH } from '../../../../../screens/alerts_detection_rules';
|
||||||
|
|
||||||
|
import {
|
||||||
|
deleteFirstRule,
|
||||||
|
getRulesManagementTableRows,
|
||||||
|
selectRulesByName,
|
||||||
|
} from '../../../../../tasks/alerts_detection_rules';
|
||||||
|
import { deleteSelectedRules } from '../../../../../tasks/rules_bulk_actions';
|
||||||
|
import { createRule, findAllRules } from '../../../../../tasks/api_calls/rules';
|
||||||
|
import { deleteAlertsAndRules } from '../../../../../tasks/common';
|
||||||
|
import { login } from '../../../../../tasks/login';
|
||||||
|
|
||||||
|
describe('Rule deletion', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
|
||||||
|
const testRules = [
|
||||||
|
getNewRule({ rule_id: 'rule1', name: 'Rule 1', enabled: false }),
|
||||||
|
getNewRule({ rule_id: 'rule2', name: 'Rule 2', enabled: false }),
|
||||||
|
getNewRule({ rule_id: 'rule3', name: 'Rule 3', enabled: false }),
|
||||||
|
];
|
||||||
|
beforeEach(() => {
|
||||||
|
deleteAlertsAndRules();
|
||||||
|
createRule(testRules[0]);
|
||||||
|
createRule(testRules[1]);
|
||||||
|
createRule(testRules[2]);
|
||||||
|
login();
|
||||||
|
visitRulesManagementTable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('User can delete an individual rule', () => {
|
||||||
|
getRulesManagementTableRows().then((rules) => {
|
||||||
|
const initialNumberOfRules = rules.length;
|
||||||
|
const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1;
|
||||||
|
|
||||||
|
findAllRules().then(({ body }) => {
|
||||||
|
const numberOfRules = body.data.length;
|
||||||
|
expect(numberOfRules).to.eql(initialNumberOfRules);
|
||||||
|
});
|
||||||
|
|
||||||
|
deleteFirstRule();
|
||||||
|
|
||||||
|
getRulesManagementTableRows().should('have.length', expectedNumberOfRulesAfterDeletion);
|
||||||
|
findAllRules().then(({ body }) => {
|
||||||
|
const numberOfRules = body.data.length;
|
||||||
|
expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('User can delete multiple selected rules via a bulk action', () => {
|
||||||
|
getRulesManagementTableRows().then((rules) => {
|
||||||
|
const rulesToDelete = ['Rule 1', 'Rule 2'] as const;
|
||||||
|
const initialNumberOfRules = rules.length;
|
||||||
|
const numberOfRulesToBeDeleted = 2;
|
||||||
|
const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - numberOfRulesToBeDeleted;
|
||||||
|
|
||||||
|
selectRulesByName(rulesToDelete);
|
||||||
|
deleteSelectedRules();
|
||||||
|
|
||||||
|
// During deletion, rule switch is not shown and instead there's loading spinner
|
||||||
|
getRulesManagementTableRows()
|
||||||
|
.first()
|
||||||
|
.within(() => {
|
||||||
|
cy.get(RULE_SWITCH).should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
getRulesManagementTableRows().should('have.length', expectedNumberOfRulesAfterDeletion);
|
||||||
|
findAllRules().then(({ body }) => {
|
||||||
|
const numberOfRules = body.data.length;
|
||||||
|
expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Once bulk delete is done and one rule remains, checking for enable/disable switch to exist
|
||||||
|
getRulesManagementTableRows()
|
||||||
|
.first()
|
||||||
|
.within(() => {
|
||||||
|
cy.get(RULE_SWITCH).should('exist');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -204,6 +204,9 @@ export const TAGS_INPUT =
|
||||||
export const TAGS_CLEAR_BUTTON =
|
export const TAGS_CLEAR_BUTTON =
|
||||||
'[data-test-subj="detectionEngineStepAboutRuleTags"] [data-test-subj="comboBoxClearButton"]';
|
'[data-test-subj="detectionEngineStepAboutRuleTags"] [data-test-subj="comboBoxClearButton"]';
|
||||||
|
|
||||||
|
export const INVESTIGATIONS_INPUT =
|
||||||
|
'[data-test-subj="detectionEngineStepAboutRuleInvestigationFields"] [data-test-subj="comboBoxSearchInput"]';
|
||||||
|
|
||||||
export const THRESHOLD_INPUT_AREA = '[data-test-subj="thresholdInput"]';
|
export const THRESHOLD_INPUT_AREA = '[data-test-subj="thresholdInput"]';
|
||||||
|
|
||||||
export const THRESHOLD_TYPE = '[data-test-subj="thresholdRuleType"]';
|
export const THRESHOLD_TYPE = '[data-test-subj="thresholdRuleType"]';
|
||||||
|
|
|
@ -142,7 +142,8 @@ export const THREAT_TECHNIQUE = '[data-test-subj="threatTechniqueLink"]';
|
||||||
|
|
||||||
export const THREAT_SUBTECHNIQUE = '[data-test-subj="threatSubtechniqueLink"]';
|
export const THREAT_SUBTECHNIQUE = '[data-test-subj="threatSubtechniqueLink"]';
|
||||||
|
|
||||||
export const BACK_TO_RULES_TABLE = '[data-test-subj="breadcrumb"][title="Detection rules (SIEM)"]';
|
export const BACK_TO_RULES_TABLE =
|
||||||
|
'[data-test-subj="breadcrumbs"] a[title="Detection rules (SIEM)"]';
|
||||||
|
|
||||||
export const HIGHLIGHTED_ROWS_IN_TABLE =
|
export const HIGHLIGHTED_ROWS_IN_TABLE =
|
||||||
'[data-test-subj="euiDataGridBody"] .alertsTableHighlightedRow';
|
'[data-test-subj="euiDataGridBody"] .alertsTableHighlightedRow';
|
||||||
|
|
|
@ -114,6 +114,7 @@ import {
|
||||||
CREATE_WITHOUT_ENABLING_BTN,
|
CREATE_WITHOUT_ENABLING_BTN,
|
||||||
RULE_INDICES,
|
RULE_INDICES,
|
||||||
ALERTS_INDEX_BUTTON,
|
ALERTS_INDEX_BUTTON,
|
||||||
|
INVESTIGATIONS_INPUT,
|
||||||
} from '../screens/create_new_rule';
|
} from '../screens/create_new_rule';
|
||||||
import {
|
import {
|
||||||
INDEX_SELECTOR,
|
INDEX_SELECTOR,
|
||||||
|
@ -131,11 +132,15 @@ import { waitForAlerts } from './alerts';
|
||||||
import { refreshPage } from './security_header';
|
import { refreshPage } from './security_header';
|
||||||
import { EMPTY_ALERT_TABLE } from '../screens/alerts';
|
import { EMPTY_ALERT_TABLE } from '../screens/alerts';
|
||||||
|
|
||||||
|
export const createAndEnableRuleOnly = () => {
|
||||||
|
cy.get(CREATE_AND_ENABLE_BTN).click({ force: true });
|
||||||
|
cy.get(CREATE_AND_ENABLE_BTN).should('not.exist');
|
||||||
|
};
|
||||||
|
|
||||||
export const createAndEnableRule = () => {
|
export const createAndEnableRule = () => {
|
||||||
cy.get(CREATE_AND_ENABLE_BTN).click({ force: true });
|
cy.get(CREATE_AND_ENABLE_BTN).click({ force: true });
|
||||||
cy.get(CREATE_AND_ENABLE_BTN).should('not.exist');
|
cy.get(CREATE_AND_ENABLE_BTN).should('not.exist');
|
||||||
cy.get(BACK_TO_RULES_TABLE).click({ force: true });
|
cy.get(BACK_TO_RULES_TABLE).click({ force: true });
|
||||||
cy.get(BACK_TO_RULES_TABLE).should('not.exist');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const pressRuleCreateBtn = () => {
|
export const pressRuleCreateBtn = () => {
|
||||||
|
@ -303,6 +308,23 @@ export const fillReferenceUrls = (referenceUrls: string[] = ruleFields.reference
|
||||||
return referenceUrls;
|
return referenceUrls;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fillCustomInvestigationFields = (
|
||||||
|
fields: string[] = ruleFields.investigationFields.field_names
|
||||||
|
) => {
|
||||||
|
fields.forEach((field) => {
|
||||||
|
cy.get(INVESTIGATIONS_INPUT).type(`${field}{enter}`, { force: true });
|
||||||
|
});
|
||||||
|
return fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fillAboutRuleMinimumAndContinue = (rule: RuleCreateProps) => {
|
||||||
|
cy.get(RULE_NAME_INPUT).clear();
|
||||||
|
cy.get(RULE_NAME_INPUT).type(rule.name);
|
||||||
|
cy.get(RULE_DESCRIPTION_INPUT).clear();
|
||||||
|
cy.get(RULE_DESCRIPTION_INPUT).type(rule.description);
|
||||||
|
getAboutContinueButton().should('exist').click();
|
||||||
|
};
|
||||||
|
|
||||||
export const fillAboutRuleAndContinue = (rule: RuleCreateProps) => {
|
export const fillAboutRuleAndContinue = (rule: RuleCreateProps) => {
|
||||||
fillAboutRule(rule);
|
fillAboutRule(rule);
|
||||||
getAboutContinueButton().should('exist').click({ force: true });
|
getAboutContinueButton().should('exist').click({ force: true });
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
"cypress:run:respops:ess": "yarn cypress:ess --spec './cypress/e2e/(detection_alerts|detection_rules|exceptions)/*.cy.ts'",
|
"cypress:run:respops:ess": "yarn cypress:ess --spec './cypress/e2e/(detection_alerts|detection_rules|exceptions)/*.cy.ts'",
|
||||||
"cypress:investigations:run:ess": "yarn cypress:ess --spec './cypress/e2e/investigations/**/*.cy.ts'",
|
"cypress:investigations:run:ess": "yarn cypress:ess --spec './cypress/e2e/investigations/**/*.cy.ts'",
|
||||||
"cypress:explore:run:ess": "yarn cypress:ess --spec './cypress/e2e/explore/**/*.cy.ts'",
|
"cypress:explore:run:ess": "yarn cypress:ess --spec './cypress/e2e/explore/**/*.cy.ts'",
|
||||||
"cypress:changed-specs-only:ess": "yarn cypress:ess --changed-specs-only --env burn=2",
|
"cypress:changed-specs-only:ess": "yarn cypress:ess --changed-specs-only --env burn=5",
|
||||||
"cypress:burn:ess": "yarn cypress:ess --env burn=2",
|
"cypress:burn:ess": "yarn cypress:ess --env burn=5",
|
||||||
"junit:merge": "../../../node_modules/.bin/mochawesome-merge ../../../target/kibana-security-solution/cypress/results/mochawesome*.json > ../../../target/kibana-security-solution/cypress/results/output.json && ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results && yarn junit:transform && mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/",
|
"junit:merge": "../../../node_modules/.bin/mochawesome-merge ../../../target/kibana-security-solution/cypress/results/mochawesome*.json > ../../../target/kibana-security-solution/cypress/results/output.json && ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results && yarn junit:transform && mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/",
|
||||||
"junit:transform": "node ../../plugins/security_solution/scripts/junit_transformer --pathPattern '../../../target/kibana-security-solution/cypress/results/*.xml' --rootDirectory ../../../ --reportName 'Security Solution Cypress' --writeInPlace",
|
"junit:transform": "node ../../plugins/security_solution/scripts/junit_transformer --pathPattern '../../../target/kibana-security-solution/cypress/results/*.xml' --rootDirectory ../../../ --reportName 'Security Solution Cypress' --writeInPlace",
|
||||||
"cypress:serverless": "TZ=UTC NODE_OPTIONS=--openssl-legacy-provider node ../../plugins/security_solution/scripts/start_cypress_parallel --config-file ../../test/security_solution_cypress/cypress/cypress_ci_serverless.config.ts --ftr-config-file ../../test/security_solution_cypress/serverless_config",
|
"cypress:serverless": "TZ=UTC NODE_OPTIONS=--openssl-legacy-provider node ../../plugins/security_solution/scripts/start_cypress_parallel --config-file ../../test/security_solution_cypress/cypress/cypress_ci_serverless.config.ts --ftr-config-file ../../test/security_solution_cypress/serverless_config",
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
"cypress:run:cloud:serverless": "yarn cypress:cloud:serverless run --config-file ./cypress/cypress_ci_serverless.config.ts --env CLOUD_SERVERLESS=true",
|
"cypress:run:cloud:serverless": "yarn cypress:cloud:serverless run --config-file ./cypress/cypress_ci_serverless.config.ts --env CLOUD_SERVERLESS=true",
|
||||||
"cypress:investigations:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/investigations/**/*.cy.ts'",
|
"cypress:investigations:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/investigations/**/*.cy.ts'",
|
||||||
"cypress:explore:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/explore/**/*.cy.ts'",
|
"cypress:explore:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/explore/**/*.cy.ts'",
|
||||||
"cypress:changed-specs-only:serverless": "yarn cypress:serverless --changed-specs-only --env burn=2",
|
"cypress:changed-specs-only:serverless": "yarn cypress:serverless --changed-specs-only --env burn=5",
|
||||||
"cypress:burn:serverless": "yarn cypress:serverless --env burn=2"
|
"cypress:burn:serverless": "yarn cypress:serverless --env burn=5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue