[Security Solution] Skip all Detection & Response Cypress tests in Serverless (#165966)

**Resolves: https://github.com/elastic/kibana/issues/164441**

## Summary

Skips all Cypress tests owned by
@elastic/security-detection-rule-management and
@elastic/security-detection-engine teams in Serverless using the new
`@skipInServerless` tag.

- Adds a new `@skipInServerless` tag
- Updates `x-pack/test/security_solution_cypress/cypress/README.md` to
explain when to use what tags
- Explicitly adds missing tags to all D&R tests
- Adds `// TODO:` comments to tests with links to follow-up tickets
- Fixes the
`x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts`
script (see below)

Follow-up work:

- https://github.com/elastic/kibana/issues/161540
- https://github.com/elastic/kibana/issues/161539

## Context

> Serverless test failures will soon block PR merge

> During the development of the serverless test infrastructure, we
decided that serverless tests will only soft-fail. That means you see
the test failure in your PR but you're still able to merge. We did that
mainly in order to not block delivery of stateful features and bug fixes
while serverless tests and pipelines were implemented and stabilized.
We now have the major building blocks for the serverless test
infrastructure in place and will integrate serverless tests in our
regular pipelines. As part of this process, we're skipping failing and
flaky serverless tests that came in during the soft-fail phase. Once
this is done, a PR with serverless test failures can no longer be
merged, similar to how we have it for stateful test failures.

> We plan to merge this in the next few days and we'll send out another
notification when it's done.

## Fixing `parallel.ts`

There were two problems with the
`x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts`
script we use for running Cypress tests:

- The script was broken in https://github.com/elastic/kibana/pull/162673
(here on [this
line](https://github.com/elastic/kibana/pull/162673/files#diff-9f40ced6d29c4fc2709af881680400293d8ce1bc9ebb07b9138d6d99c83c09c9R67))
- I think it has never supported situations when all tests matching a
spec pattern (such as
`./cypress/e2e/!(investigations|explore)/**/*.cy.ts`) end up being
skipped via Cypress tags (such as `@skipInServerless`)

Both the issues are fixed in this PR.

Code owners are added for this script in the CODEOWNERS file to prevent
breaking this script in future PRs.
This commit is contained in:
Georgii Gorbachev 2023-09-09 02:09:18 +02:00 committed by GitHub
parent aa6ad19335
commit 1d65e7886e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
69 changed files with 1359 additions and 1183 deletions

1
.github/CODEOWNERS vendored
View file

@ -1316,6 +1316,7 @@ x-pack/test/security_solution_cypress/config.ts @elastic/security-engineering-pr
x-pack/test/security_solution_cypress/runner.ts @elastic/security-engineering-productivity
x-pack/test/security_solution_cypress/serverless_config.ts @elastic/security-engineering-productivity
x-pack/test/security_solution_cypress/cypress/tags.ts @elastic/security-engineering-productivity
x-pack/plugins/security_solution/scripts/run_cypress @MadameSheema @patrykkopycinski @oatkiller @maximpn @banderror
## Security Solution sub teams - adaptive-workload-protection
x-pack/plugins/security_solution/public/common/components/sessions_viewer @elastic/kibana-cloud-security-posture

View file

@ -63,8 +63,14 @@ const retrieveIntegrations = (integrationsPaths: string[]) => {
export const cli = () => {
run(
async () => {
const log = new ToolingLog({
level: 'info',
writeTo: process.stdout,
});
const { argv } = yargs(process.argv.slice(2))
.coerce('spec', (arg) => (_.isArray(arg) ? [_.last(arg)] : [arg]))
.coerce('configFile', (arg) => (_.isArray(arg) ? _.last(arg) : arg))
.coerce('spec', (arg) => (_.isArray(arg) ? _.last(arg) : arg))
.coerce('env', (arg: string) =>
arg.split(',').reduce((acc, curr) => {
const [key, value] = curr.split('=');
@ -77,22 +83,72 @@ export const cli = () => {
}, {} as Record<string, string | number>)
);
log.info(`
----------------------------------------------
Script arguments:
----------------------------------------------
${JSON.stringify(argv, null, 2)}
----------------------------------------------
`);
const isOpen = argv._[0] === 'open';
const cypressConfigFilePath = require.resolve(
`../../${_.isArray(argv.configFile) ? _.last(argv.configFile) : argv.configFile}`
) as string;
const cypressConfigFilePath = require.resolve(`../../${argv.configFile}`) as string;
const cypressConfigFile = await import(cypressConfigFilePath);
log.info(`
----------------------------------------------
Cypress config for file: ${cypressConfigFilePath}:
----------------------------------------------
${JSON.stringify(cypressConfigFile, null, 2)}
----------------------------------------------
`);
const specConfig = cypressConfigFile.e2e.specPattern;
const specArg = argv.spec;
const specPattern = specArg ?? specConfig;
log.info('Config spec pattern:', specConfig);
log.info('Arguments spec pattern:', specArg);
log.info('Resulting spec pattern:', specPattern);
// The grep function will filter Cypress specs by tags: it will include and exclude
// spec files according to the tags configuration.
const grepSpecPattern = grep({
...cypressConfigFile,
specPattern: argv.spec ?? cypressConfigFile.e2e.specPattern,
specPattern,
excludeSpecPattern: [],
}).specPattern;
let files = retrieveIntegrations(
_.isArray(grepSpecPattern)
? grepSpecPattern
: globby.sync(argv.spec ?? cypressConfigFile.e2e.specPattern)
);
log.info('Resolved spec files or pattern after grep:', grepSpecPattern);
const isGrepReturnedFilePaths = _.isArray(grepSpecPattern);
const isGrepReturnedSpecPattern = !isGrepReturnedFilePaths && grepSpecPattern === specPattern;
// IMPORTANT!
// When grep returns the same spec pattern as it gets in its arguments, we treat it as
// it couldn't find any concrete specs to execute (maybe because all of them are skipped).
// In this case, we do an early return - it's important to do that.
// If we don't return early, these specs will start executing, and Cypress will be skipping
// tests at runtime: those that should be excluded according to the tags passed in the config.
// This can take so much time that the job can fail by timeout in CI.
if (isGrepReturnedSpecPattern) {
log.info('No tests found - all tests could have been skipped via Cypress tags');
// eslint-disable-next-line no-process-exit
return process.exit(0);
}
const concreteFilePaths = isGrepReturnedFilePaths
? grepSpecPattern // use the returned concrete file paths
: globby.sync(specPattern); // convert the glob pattern to concrete file paths
let files = retrieveIntegrations(concreteFilePaths);
log.info('Resolved spec files after retrieveIntegrations:', files);
if (argv.changedSpecsOnly) {
files = (findChangedFiles('main', false) as string[]).reduce((acc, itemPath) => {
@ -108,11 +164,6 @@ export const cli = () => {
files = files.slice(0, 3);
}
const log = new ToolingLog({
level: 'info',
writeTo: process.stdout,
});
if (!files?.length) {
log.info('No tests found');
// eslint-disable-next-line no-process-exit

View file

@ -40,7 +40,12 @@ of data for your test, [**Running the tests**](#running-the-tests) to know how t
Please, before opening a PR with the new test, please make sure that the test fails. If you never see your test fail you dont know if your test is actually testing the right thing, or testing anything at all.
Note that we use tags in order to select which tests we want to execute: @serverless, @ess and @brokenInServerless
Note that we use tags in order to select which tests we want to execute:
- `@serverless` includes a test in the Serverless test suite. You need to explicitly add this tag to any test you want to run against a Serverless environment.
- `@ess` includes a test in the normal, non-Serverless test suite. You need to explicitly add this tag to any test you want to run against a non-Serverless environment.
- `@brokenInServerless` excludes a test from the Serverless test suite (even if it's tagged as `@serverless`). Indicates that a test should run in Serverless, but currently is broken.
- `@skipInServerless` excludes a test from the Serverless test suite (even if it's tagged as `@serverless`). Could indicate many things, e.g. "the test is flaky in Serverless", "the test is Flaky in any type of environemnt", "the test has been temporarily excluded, see the comment above why".
Please, before opening a PR with a new test, make sure that the test fails. If you never see your test fail you dont know if your test is actually testing the right thing, or testing anything at all.

View file

@ -18,7 +18,7 @@ export default defineCypressConfig({
env: {
grepFilterSpecs: true,
grepOmitFiltered: true,
grepTags: '@serverless --@brokenInServerless',
grepTags: '@serverless --@brokenInServerless --@skipInServerless',
},
execTimeout: 150000,
pageLoadTimeout: 150000,

View file

@ -23,7 +23,7 @@ export default defineCypressConfig({
numTestsKeptInMemory: 10,
env: {
grepFilterSpecs: true,
grepTags: '@serverless --@brokenInServerless',
grepTags: '@serverless --@brokenInServerless --@skipInServerless',
},
e2e: {
experimentalRunAllSpecs: true,

View file

@ -25,6 +25,7 @@ import { GET_TIMELINE_HEADER } from '../../screens/timeline';
const alertRunTimeField = 'field.name.alert.page';
const timelineRuntimeField = 'field.name.timeline';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'Create DataView runtime field',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },

View file

@ -35,12 +35,14 @@ const rolesToCreate = [secReadCasesAll];
const siemDataViewTitle = 'Security Default Data View';
const dataViews = ['auditbeat-*,fakebeat-*', 'auditbeat-*,*beat*,siem-read*,.kibana*,fakebeat-*'];
describe('Sourcerer', () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Sourcerer', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
before(() => {
cy.task('esArchiverResetKibana');
dataViews.forEach((dataView: string) => postDataView(dataView));
});
// TODO: https://github.com/elastic/kibana/issues/161539
describe('permissions', { tags: ['@ess', '@brokenInServerless'] }, () => {
before(() => {
createUsersAndRoles(usersToCreate, rolesToCreate);
@ -52,6 +54,7 @@ describe('Sourcerer', () => {
});
});
// TODO: https://github.com/elastic/kibana/issues/161539
// FLAKY: https://github.com/elastic/kibana/issues/165766
describe('Default scope', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
beforeEach(() => {

View file

@ -38,7 +38,8 @@ import { closeTimeline, openTimelineById } from '../../tasks/timeline';
const siemDataViewTitle = 'Security Default Data View';
const dataViews = ['auditbeat-*,fakebeat-*', 'auditbeat-*,*beat*,siem-read*,.kibana*,fakebeat-*'];
describe('Timeline scope', { tags: '@brokenInServerless' }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe.skip('Timeline scope', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
beforeEach(() => {
cy.clearLocalStorage();
login();

View file

@ -38,7 +38,8 @@ import { closeTimeline, openTimelineById } from '../../tasks/timeline';
const siemDataViewTitle = 'Security Default Data View';
const dataViews = ['auditbeat-*,fakebeat-*', 'auditbeat-*,*beat*,siem-read*,.kibana*,fakebeat-*'];
describe('Timeline scope', { tags: '@brokenInServerless' }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Timeline scope', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
beforeEach(() => {
cy.clearLocalStorage();
login();

View file

@ -24,6 +24,7 @@ import {
UNSELECTED_ALERT_TAG,
} from '../../screens/alerts';
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Alert tagging', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
before(() => {
cleanKibana();

View file

@ -24,6 +24,7 @@ import {
} from '../../screens/search_bar';
import { TOASTER } from '../../screens/alerts_detection_rules';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'Histogram legend hover actions',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },

View file

@ -29,9 +29,10 @@ const waitForPageTitleToBeShown = () => {
cy.get(PAGE_TITLE).should('be.visible');
};
// TODO: https://github.com/elastic/kibana/issues/161539 Does it need to run in Serverless?
describe(
'Detections > Need Admin Callouts indicating an admin is needed to migrate the alert data set',
{ tags: '@ess' },
{ tags: ['@ess', '@skipInServerless'] },
() => {
before(() => {
// First, we have to open the app on behalf of a privileged user in order to initialize it.

View file

@ -27,6 +27,7 @@ import { openJsonView, openThreatIndicatorDetails } from '../../tasks/alerts_det
import { ruleDetailsUrl } from '../../urls/navigation';
import { addsFieldsToTimeline } from '../../tasks/rule_details';
// TODO: https://github.com/elastic/kibana/issues/161539
describe('CTI Enrichment', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
before(() => {
cleanKibana();
@ -50,6 +51,7 @@ describe('CTI Enrichment', { tags: ['@ess', '@serverless', '@brokenInServerless'
);
});
// TODO: https://github.com/elastic/kibana/issues/161539
// Skipped: https://github.com/elastic/kibana/issues/162818
it.skip('Displays enrichment matched.* fields on the timeline', () => {
const expectedFields = {

View file

@ -30,6 +30,7 @@ import { login, visit } from '../../tasks/login';
import { ALERTS_URL } from '../../urls/navigation';
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Enrichment', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
before(() => {
cleanKibana();

View file

@ -41,7 +41,8 @@ const waitForPageTitleToBeShown = () => {
cy.get(PAGE_TITLE).should('be.visible');
};
describe('Detections > Callouts', { tags: '@ess' }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Detections > Callouts', { tags: ['@ess', '@skipInServerless'] }, () => {
before(() => {
// First, we have to open the app on behalf of a privileged user in order to initialize it.
// Otherwise the app will be disabled and show a "welcome"-like page.

View file

@ -14,6 +14,7 @@ import { TIMELINE_QUERY, TIMELINE_VIEW_IN_ANALYZER } from '../../screens/timelin
import { selectAlertsHistogram } from '../../tasks/alerts';
import { createTimeline } from '../../tasks/timelines';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'Ransomware Detection Alerts',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },

View file

@ -15,6 +15,7 @@ import { selectAlertsHistogram } from '../../tasks/alerts';
import { createTimeline } from '../../tasks/timelines';
import { cleanKibana } from '../../tasks/common';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'Ransomware Prevention Alerts',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },

View file

@ -55,9 +55,11 @@ const loadPageAsReadOnlyUser = (url: string) => {
waitForPageWithoutDateRange(url, ROLES.reader);
};
// TODO: https://github.com/elastic/kibana/issues/164451 We should find a way to make this spec work in Serverless
// TODO: https://github.com/elastic/kibana/issues/161540
describe(
'Detection rules, Prebuilt Rules Installation and Update - Authorization/RBAC',
{ tags: '@ess' },
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
beforeEach(() => {
login();

View file

@ -22,9 +22,10 @@ import {
ruleUpdatesTabClick,
} from '../../../tasks/prebuilt_rules';
// TODO: https://github.com/elastic/kibana/issues/161540
describe(
'Detection rules, Prebuilt Rules Installation and Update - Error handling',
{ tags: '@ess' },
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
beforeEach(() => {
login();

View file

@ -39,9 +39,10 @@ import {
ruleUpdatesTabClick,
} from '../../../tasks/prebuilt_rules';
// TODO: https://github.com/elastic/kibana/issues/161540
describe(
'Detection rules, Prebuilt Rules Installation and Update workflow',
{ tags: ['@ess', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
beforeEach(() => {
login();

View file

@ -50,7 +50,8 @@ const rules = Array.from(Array(5)).map((_, i) => {
});
});
describe('Prebuilt rules', { tags: ['@ess', '@serverless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161540
describe('Prebuilt rules', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
before(() => {
cleanKibana();
});

View file

@ -29,9 +29,10 @@ const RULE_1 = createRuleAssetSavedObject({
rule_id: 'rule_1',
});
// TODO: https://github.com/elastic/kibana/issues/161540
describe(
'Detection rules, Prebuilt Rules Installation and Update Notifications',
{ tags: ['@ess', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
beforeEach(() => {
login();

View file

@ -27,9 +27,10 @@ import { login, visit } from '../../../tasks/login';
import { RULE_CREATION } from '../../../urls/navigation';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'Rule actions during detection rule creation',
{ tags: ['@ess', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
const indexConnector = getIndexConnector();

View file

@ -115,7 +115,8 @@ import {
import { enablesRule, getDetails, waitForTheRuleToBeExecuted } from '../../../tasks/rule_details';
import { ruleDetailsUrl, ruleEditUrl, RULE_CREATION } from '../../../urls/navigation';
describe('Custom query rules', { tags: ['@ess', '@brokenInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Custom query rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
beforeEach(() => {
deleteAlertsAndRules();
});

View file

@ -67,7 +67,8 @@ import { getDetails, waitForTheRuleToBeExecuted } from '../../../tasks/rule_deta
import { RULE_CREATION } from '../../../urls/navigation';
describe('Custom query rules', { tags: ['@ess', '@brokenInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Custom query rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
describe('Custom detection rules creation with data views', () => {
const rule = getDataViewRule();
const expectedUrls = rule.references?.join('');

View file

@ -50,7 +50,8 @@ const savedQueryName = 'custom saved query';
const savedQueryQuery = 'process.name: test';
const savedQueryFilterKey = 'testAgent.value';
describe('Custom saved_query rules', { tags: ['@ess', '@brokenInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Saved query rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
before(() => {
cleanKibana();
});

View file

@ -56,7 +56,8 @@ import { login, visit } from '../../../tasks/login';
import { RULE_CREATION } from '../../../urls/navigation';
describe('EQL rules', { tags: ['@ess', '@brokenInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('EQL rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
before(() => {
cleanKibana();
});

View file

@ -116,7 +116,8 @@ import {
const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d/d"';
describe('indicator match', { tags: ['@ess', '@brokenInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('indicator match', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
describe('Detection rules, Indicator Match', () => {
const expectedUrls = getNewThreatIndicatorRule().references?.join('');
const expectedFalsePositives = getNewThreatIndicatorRule().false_positives?.join('');

View file

@ -53,7 +53,8 @@ import { login, visitWithoutDateRange } from '../../../tasks/login';
import { RULE_CREATION } from '../../../urls/navigation';
describe('Detection rules, machine learning', { tags: ['@ess', '@brokenInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Machine Learning rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
const expectedUrls = (getMachineLearningRule().references ?? []).join('');
const expectedFalsePositives = (getMachineLearningRule().false_positives ?? []).join('');
const expectedTags = (getMachineLearningRule().tags ?? []).join('');

View file

@ -58,7 +58,8 @@ import { login, visit } from '../../../tasks/login';
import { RULE_CREATION } from '../../../urls/navigation';
describe('New Terms rules', { tags: ['@ess', '@brokenInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('New Terms rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
before(() => {
cleanKibana();
login();

View file

@ -61,7 +61,8 @@ import { getDetails, waitForTheRuleToBeExecuted } from '../../../tasks/rule_deta
import { RULE_CREATION } from '../../../urls/navigation';
describe('Detection rules, override', { tags: ['@ess', '@brokenInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Rules override', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
const rule = getNewOverrideRule();
const expectedUrls = rule.references?.join('');
const expectedFalsePositives = rule.false_positives?.join('');

View file

@ -58,7 +58,8 @@ import { login, visitWithoutDateRange } from '../../../tasks/login';
import { RULE_CREATION } from '../../../urls/navigation';
describe('Detection rules, threshold', { tags: ['@ess', '@brokenInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Threshold rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
const rule = getNewThresholdRule();
const expectedUrls = rule.references?.join('');
const expectedFalsePositives = rule.false_positives?.join('');

View file

@ -24,7 +24,9 @@ import {
} from '../../../../tasks/common/callouts';
import { login, visitSecurityDetectionRulesPage } from '../../../../tasks/login';
describe('All rules - read only', { tags: '@ess' }, () => {
// TODO: https://github.com/elastic/kibana/issues/164451 We should find a way to make this spec work in Serverless
// TODO: https://github.com/elastic/kibana/issues/161540
describe('All rules - read only', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
before(() => {
cleanKibana();
createRule(getNewRule({ rule_id: '1', enabled: false }));

View file

@ -12,47 +12,52 @@ import { cleanKibana } from '../../../../tasks/common';
import { login, visit } from '../../../../tasks/login';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../../urls/navigation';
describe('Maintenance window callout on Rule Management page', { tags: ['@ess'] }, () => {
let maintenanceWindowId = '';
// TODO: https://github.com/elastic/kibana/issues/161540
describe(
'Maintenance window callout on Rule Management page',
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
let maintenanceWindowId = '';
before(() => {
cleanKibana();
login();
before(() => {
cleanKibana();
login();
const body: AsApiContract<MaintenanceWindowCreateBody> = {
title: 'My maintenance window',
duration: 60000, // 1 minute
r_rule: {
dtstart: new Date().toISOString(),
tzid: 'Europe/Amsterdam',
freq: 0,
count: 1,
},
};
const body: AsApiContract<MaintenanceWindowCreateBody> = {
title: 'My maintenance window',
duration: 60000, // 1 minute
r_rule: {
dtstart: new Date().toISOString(),
tzid: 'Europe/Amsterdam',
freq: 0,
count: 1,
},
};
// Create a test maintenance window
cy.request({
method: 'POST',
url: INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH,
headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' },
body,
}).then((response) => {
maintenanceWindowId = response.body.id;
// Create a test maintenance window
cy.request({
method: 'POST',
url: INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH,
headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' },
body,
}).then((response) => {
maintenanceWindowId = response.body.id;
});
});
});
after(() => {
// Delete a test maintenance window
cy.request({
method: 'DELETE',
url: `${INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH}/${maintenanceWindowId}`,
headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' },
after(() => {
// Delete a test maintenance window
cy.request({
method: 'DELETE',
url: `${INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH}/${maintenanceWindowId}`,
headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' },
});
});
});
it('Displays the callout when there are running maintenance windows', () => {
visit(DETECTIONS_RULE_MANAGEMENT_URL);
it('Displays the callout when there are running maintenance windows', () => {
visit(DETECTIONS_RULE_MANAGEMENT_URL);
cy.contains('Maintenance window is running');
});
});
cy.contains('Maintenance window is running');
});
}
);

View file

@ -47,7 +47,8 @@ import {
import { ruleDetailsUrl } from '../../../../urls/navigation';
import { enablesRule, waitForPageToBeLoaded } from '../../../../tasks/rule_details';
describe('Related integrations', { tags: ['@ess', '@brokenInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161540
describe('Related integrations', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
const DATA_STREAM_NAME = 'logs-related-integrations-test';
const PREBUILT_RULE_NAME = 'Prebuilt rule with related integrations';
const RULE_RELATED_INTEGRATIONS: IntegrationDefinition[] = [
@ -189,6 +190,7 @@ describe('Related integrations', { tags: ['@ess', '@brokenInServerless'] }, () =
});
});
// TODO: https://github.com/elastic/kibana/issues/161540
// Flaky in serverless tests
// @brokenInServerless tag is not working so a skip was needed
describe.skip('rule details', { tags: ['@brokenInServerless'] }, () => {

View file

@ -52,10 +52,11 @@ const EXPIRED_EXCEPTION_ITEM_NAME = 'Sample exception item';
const NON_EXPIRED_EXCEPTION_ITEM_NAME = 'Sample exception item with future expiration';
// TODO: https://github.com/elastic/kibana/issues/161540
// Flaky on serverless
describe(
'Detection rules, bulk duplicate',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
before(() => {
cleanKibana();

View file

@ -73,9 +73,10 @@ const ruleNameToAssert = 'Custom rule name with actions';
const expectedExistingSlackMessage = 'Existing slack action';
const expectedSlackMessage = 'Slack action test message';
// TODO: https://github.com/elastic/kibana/issues/161540
describe(
'Detection rules, bulk edit of rule actions',
{ tags: ['@ess', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
beforeEach(() => {
cleanKibana();

View file

@ -52,9 +52,10 @@ const DATA_VIEW_ID = 'auditbeat';
const expectedIndexPatterns = ['index-1-*', 'index-2-*'];
// TODO: https://github.com/elastic/kibana/issues/161540
describe(
'Bulk editing index patterns of rules with a data view only',
{ tags: ['@ess', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
const TESTED_CUSTOM_QUERY_RULE_DATA = getNewRule({
index: undefined,

View file

@ -55,7 +55,8 @@ const prebuiltRules = Array.from(Array(7)).map((_, i) => {
});
});
describe('Export rules', { tags: ['@ess', '@brokenInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161540
describe('Export rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
const downloadsFolder = Cypress.config('downloadsFolder');
before(() => {

View file

@ -17,7 +17,8 @@ import { login, visitWithoutDateRange } from '../../../../../tasks/login';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../../../urls/navigation';
const RULES_TO_IMPORT_FILENAME = 'cypress/fixtures/7_16_rules.ndjson';
describe('Import rules', { tags: ['@ess', '@brokenInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161540
describe('Import rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
before(() => {
cleanKibana();
});

View file

@ -47,8 +47,9 @@ import { TOOLTIP } from '../../../../../screens/common';
const RULES_TO_IMPORT_FILENAME = 'cypress/fixtures/7_16_rules.ndjson';
// TODO: https://github.com/elastic/kibana/issues/161540
// Flaky in serverless tests
describe('rule snoozing', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
describe('rule snoozing', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
before(() => {
cleanKibana();
});

View file

@ -33,86 +33,91 @@ import { getNewRule } from '../../../../objects/rule';
const DEFAULT_RULE_REFRESH_INTERVAL_VALUE = 60000;
const NUM_OF_TEST_RULES = 6;
describe('Rules table: auto-refresh', { tags: ['@ess', '@brokenInServerless'] }, () => {
before(() => {
cleanKibana();
login();
// TODO: https://github.com/elastic/kibana/issues/161540
describe(
'Rules table: auto-refresh',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
before(() => {
cleanKibana();
login();
for (let i = 1; i <= NUM_OF_TEST_RULES; ++i) {
createRule(getNewRule({ name: `Test rule ${i}`, rule_id: `${i}`, enabled: false }));
}
});
for (let i = 1; i <= NUM_OF_TEST_RULES; ++i) {
createRule(getNewRule({ name: `Test rule ${i}`, rule_id: `${i}`, enabled: false }));
}
});
beforeEach(() => {
login();
});
beforeEach(() => {
login();
});
it('Auto refreshes rules', () => {
mockGlobalClock();
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
it('Auto refreshes rules', () => {
mockGlobalClock();
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
expectNumberOfRules(RULES_MANAGEMENT_TABLE, NUM_OF_TEST_RULES);
expectNumberOfRules(RULES_MANAGEMENT_TABLE, NUM_OF_TEST_RULES);
// // mock 1 minute passing to make sure refresh is conducted
cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should('not.exist');
cy.tick(DEFAULT_RULE_REFRESH_INTERVAL_VALUE);
cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should('be.visible');
// // mock 1 minute passing to make sure refresh is conducted
cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should('not.exist');
cy.tick(DEFAULT_RULE_REFRESH_INTERVAL_VALUE);
cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should('be.visible');
cy.contains(REFRESH_RULES_STATUS, 'Updated now');
});
cy.contains(REFRESH_RULES_STATUS, 'Updated now');
});
it('should prevent table from rules refetch if any rule selected', () => {
mockGlobalClock();
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
it('should prevent table from rules refetch if any rule selected', () => {
mockGlobalClock();
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
expectNumberOfRules(RULES_MANAGEMENT_TABLE, NUM_OF_TEST_RULES);
expectNumberOfRules(RULES_MANAGEMENT_TABLE, NUM_OF_TEST_RULES);
selectRulesByName(['Test rule 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');
cy.tick(DEFAULT_RULE_REFRESH_INTERVAL_VALUE);
cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should('not.exist');
// mock 1 minute passing to make sure refresh is not conducted
cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should('not.exist');
cy.tick(DEFAULT_RULE_REFRESH_INTERVAL_VALUE);
cy.get(RULES_TABLE_AUTOREFRESH_INDICATOR).should('not.exist');
// ensure rule is still selected
getRuleRow('Test rule 1').find(EUI_CHECKBOX).should('be.checked');
// ensure rule is still selected
getRuleRow('Test rule 1').find(EUI_CHECKBOX).should('be.checked');
cy.get(REFRESH_RULES_STATUS).should('have.not.text', 'Updated now');
});
cy.get(REFRESH_RULES_STATUS).should('have.not.text', 'Updated now');
});
it('should disable auto refresh when any rule selected and enable it after rules unselected', () => {
visit(DETECTIONS_RULE_MANAGEMENT_URL);
it('should disable auto refresh when any rule selected and enable it after rules unselected', () => {
visit(DETECTIONS_RULE_MANAGEMENT_URL);
expectNumberOfRules(RULES_MANAGEMENT_TABLE, NUM_OF_TEST_RULES);
expectNumberOfRules(RULES_MANAGEMENT_TABLE, NUM_OF_TEST_RULES);
// check refresh settings if it's enabled before selecting
expectAutoRefreshIsEnabled();
// check refresh settings if it's enabled before selecting
expectAutoRefreshIsEnabled();
selectAllRules();
selectAllRules();
// auto refresh should be deactivated (which means disabled without an ability to enable it) after rules selected
expectAutoRefreshIsDeactivated();
// auto refresh should be deactivated (which means disabled without an ability to enable it) after rules selected
expectAutoRefreshIsDeactivated();
clearAllRuleSelection();
clearAllRuleSelection();
// after all rules unselected, auto refresh should be reset to its previous state
expectAutoRefreshIsEnabled();
});
// after all rules unselected, auto refresh should be reset to its previous state
expectAutoRefreshIsEnabled();
});
it('should not enable auto refresh after rules were unselected if auto refresh was disabled', () => {
visit(DETECTIONS_RULE_MANAGEMENT_URL);
it('should not enable auto refresh after rules were unselected if auto refresh was disabled', () => {
visit(DETECTIONS_RULE_MANAGEMENT_URL);
expectNumberOfRules(RULES_MANAGEMENT_TABLE, NUM_OF_TEST_RULES);
expectNumberOfRules(RULES_MANAGEMENT_TABLE, NUM_OF_TEST_RULES);
disableAutoRefresh();
disableAutoRefresh();
selectAllRules();
selectAllRules();
expectAutoRefreshIsDeactivated();
expectAutoRefreshIsDeactivated();
clearAllRuleSelection();
clearAllRuleSelection();
// after all rules unselected, auto refresh should still be disabled
expectAutoRefreshIsDisabled();
});
});
// after all rules unselected, auto refresh should still be disabled
expectAutoRefreshIsDisabled();
});
}
);

View file

@ -28,8 +28,9 @@ import {
import { disableAutoRefresh } from '../../../../tasks/alerts_detection_rules';
import { getNewRule } from '../../../../objects/rule';
// TODO: https://github.com/elastic/kibana/issues/161540
// Flaky in serverless tests
describe('Rules table: filtering', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
describe('Rules table: filtering', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
before(() => {
cleanKibana();
});
@ -42,6 +43,7 @@ describe('Rules table: filtering', { tags: ['@ess', '@serverless', '@brokenInSer
cy.task('esArchiverResetKibana');
});
// TODO: https://github.com/elastic/kibana/issues/161540
describe.skip('Last response filter', () => {
// Flaky in serverless tests
// @brokenInServerless tag is not working so a skip was needed

View file

@ -12,8 +12,9 @@ import { cleanKibana, deleteAlertsAndRules } from '../../../../tasks/common';
import { login, visitWithoutDateRange } from '../../../../tasks/login';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../../urls/navigation';
// TODO: https://github.com/elastic/kibana/issues/161540
// Flaky in serverless tests
describe('Rules table: links', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
describe('Rules table: links', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
before(() => {
cleanKibana();
});

View file

@ -99,124 +99,248 @@ function expectDefaultRulesTableState(): void {
expectTablePage(1);
}
describe('Rules table: persistent state', { tags: ['@ess', '@serverless'] }, () => {
before(() => {
cleanKibana();
createTestRules();
});
// TODO: https://github.com/elastic/kibana/issues/161540
describe(
'Rules table: persistent state',
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
before(() => {
cleanKibana();
createTestRules();
});
beforeEach(() => {
login();
resetRulesTableState();
});
beforeEach(() => {
login();
resetRulesTableState();
});
// Flaky on serverless
// FLAKY: https://github.com/elastic/kibana/issues/165740
describe(
'while on a happy path',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
it('activates management tab by default', () => {
visit(SECURITY_DETECTIONS_RULES_URL);
// Flaky on serverless
// FLAKY: https://github.com/elastic/kibana/issues/165740
describe(
'while on a happy path',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
it('activates management tab by default', () => {
visit(SECURITY_DETECTIONS_RULES_URL);
expectRulesManagementTab();
});
it('leads to displaying a rule according to the specified filters', () => {
visitRulesTableWithState({
searchTerm: 'rule',
tags: ['tag-b'],
source: 'custom',
enabled: false,
field: 'name',
order: 'asc',
perPage: 5,
page: 2,
expectRulesManagementTab();
});
expectManagementTableRules(['rule 6']);
});
it('leads to displaying a rule according to the specified filters', () => {
visitRulesTableWithState({
searchTerm: 'rule',
tags: ['tag-b'],
source: 'custom',
enabled: false,
field: 'name',
order: 'asc',
perPage: 5,
page: 2,
});
it('loads from the url', () => {
visitRulesTableWithState({
searchTerm: 'rule',
tags: ['tag-b'],
source: 'custom',
enabled: false,
field: 'name',
order: 'asc',
perPage: 5,
page: 2,
expectManagementTableRules(['rule 6']);
});
expectRulesManagementTab();
expectFilterSearchTerm('rule');
expectFilterByTags(['tag-b']);
expectFilterByCustomRules();
expectFilterByDisabledRules();
expectTableSorting('Rule', 'asc');
expectRowsPerPage(5);
expectTablePage(2);
});
it('loads from the url', () => {
visitRulesTableWithState({
searchTerm: 'rule',
tags: ['tag-b'],
source: 'custom',
enabled: false,
field: 'name',
order: 'asc',
perPage: 5,
page: 2,
});
it('loads from the session storage', () => {
setStorageState({
searchTerm: 'test',
tags: ['tag-a'],
source: 'prebuilt',
enabled: true,
field: 'severity',
order: 'desc',
perPage: 10,
expectRulesManagementTab();
expectFilterSearchTerm('rule');
expectFilterByTags(['tag-b']);
expectFilterByCustomRules();
expectFilterByDisabledRules();
expectTableSorting('Rule', 'asc');
expectRowsPerPage(5);
expectTablePage(2);
});
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
it('loads from the session storage', () => {
setStorageState({
searchTerm: 'test',
tags: ['tag-a'],
source: 'prebuilt',
enabled: true,
field: 'severity',
order: 'desc',
perPage: 10,
});
expectRulesManagementTab();
expectFilterSearchTerm('test');
expectFilterByTags(['tag-a']);
expectFilterByPrebuiltRules();
expectFilterByEnabledRules();
expectTableSorting('Severity', 'desc');
});
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
it('prefers url state over storage state', () => {
setStorageState({
searchTerm: 'test',
tags: ['tag-c'],
source: 'prebuilt',
enabled: true,
field: 'severity',
order: 'desc',
perPage: 10,
expectRulesManagementTab();
expectFilterSearchTerm('test');
expectFilterByTags(['tag-a']);
expectFilterByPrebuiltRules();
expectFilterByEnabledRules();
expectTableSorting('Severity', 'desc');
});
visitRulesTableWithState({
searchTerm: 'rule',
tags: ['tag-b'],
source: 'custom',
enabled: false,
field: 'name',
order: 'asc',
perPage: 5,
page: 2,
it('prefers url state over storage state', () => {
setStorageState({
searchTerm: 'test',
tags: ['tag-c'],
source: 'prebuilt',
enabled: true,
field: 'severity',
order: 'desc',
perPage: 10,
});
visitRulesTableWithState({
searchTerm: 'rule',
tags: ['tag-b'],
source: 'custom',
enabled: false,
field: 'name',
order: 'asc',
perPage: 5,
page: 2,
});
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(2);
});
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(2);
describe('and on the rules management tab', () => {
beforeEach(() => {
login();
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
});
it('persists after reloading the page', () => {
changeRulesTableState();
goToTablePage(2);
cy.reload();
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(2);
});
it('persists after navigating back from a rule details page', () => {
changeRulesTableState();
goToTablePage(2);
goToRuleDetailsOf('rule 6');
cy.go('back');
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(2);
});
it('persists after navigating to another page inside Security Solution', () => {
changeRulesTableState();
goToTablePage(2);
visit(DASHBOARDS_URL);
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(1);
});
it('persists after navigating to another page outside Security Solution', () => {
changeRulesTableState();
goToTablePage(2);
visit(KIBANA_HOME);
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(1);
});
});
describe('and on the rules monitoring tab', () => {
beforeEach(() => {
login();
visit(SECURITY_DETECTIONS_RULES_MONITORING_URL);
});
it('persists the selected tab', () => {
changeRulesTableState();
cy.reload();
expectRulesMonitoringTab();
});
});
}
);
describe('upon state format upgrade', async () => {
beforeEach(() => {
login();
});
describe('and having state in the url', () => {
it('ignores unsupported state key', () => {
visitRulesTableWithState({
someKey: 10,
searchTerm: 'rule',
tags: ['tag-b'],
source: 'custom',
enabled: false,
field: 'name',
order: 'asc',
perPage: 5,
page: 2,
});
expectRulesTableState();
expectTablePage(2);
});
});
describe('and having state in the session storage', () => {
it('ignores unsupported state key', () => {
setStorageState({
someKey: 10,
searchTerm: 'rule',
tags: ['tag-b'],
source: 'custom',
enabled: false,
field: 'name',
order: 'asc',
perPage: 5,
});
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
expectRulesTableState();
expectTablePage(1);
});
});
});
describe('when persisted state is partially unavailable', () => {
describe('and on the rules management tab', () => {
beforeEach(() => {
login();
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
});
it('persists after reloading the page', () => {
it('persists after clearing the session storage', () => {
changeRulesTableState();
goToTablePage(2);
cy.window().then((win) => {
win.sessionStorage.clear();
});
cy.reload();
expectRulesManagementTab();
@ -224,35 +348,10 @@ describe('Rules table: persistent state', { tags: ['@ess', '@serverless'] }, ()
expectTablePage(2);
});
it('persists after navigating back from a rule details page', () => {
it('persists after clearing the url state', () => {
changeRulesTableState();
goToTablePage(2);
goToRuleDetailsOf('rule 6');
cy.go('back');
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(2);
});
it('persists after navigating to another page inside Security Solution', () => {
changeRulesTableState();
goToTablePage(2);
visit(DASHBOARDS_URL);
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(1);
});
it('persists after navigating to another page outside Security Solution', () => {
changeRulesTableState();
goToTablePage(2);
visit(KIBANA_HOME);
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
expectRulesManagementTab();
@ -260,149 +359,55 @@ describe('Rules table: persistent state', { tags: ['@ess', '@serverless'] }, ()
expectTablePage(1);
});
});
});
describe('and on the rules monitoring tab', () => {
describe('when corrupted', () => {
describe('and on the rules management tab', () => {
beforeEach(() => {
login();
visit(SECURITY_DETECTIONS_RULES_MONITORING_URL);
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
});
it('persists the selected tab', () => {
it('persists after corrupting the session storage data', () => {
changeRulesTableState();
goToTablePage(2);
cy.reload();
cy.window().then((win) => {
win.sessionStorage.setItem('securitySolution.rulesTable', '!invalid');
cy.reload();
expectRulesMonitoringTab();
});
});
}
);
describe('upon state format upgrade', async () => {
beforeEach(() => {
login();
});
describe('and having state in the url', () => {
it('ignores unsupported state key', () => {
visitRulesTableWithState({
someKey: 10,
searchTerm: 'rule',
tags: ['tag-b'],
source: 'custom',
enabled: false,
field: 'name',
order: 'asc',
perPage: 5,
page: 2,
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(2);
});
});
expectRulesTableState();
expectTablePage(2);
});
});
it('persists after corrupting the url param data', () => {
changeRulesTableState();
goToTablePage(2);
describe('and having state in the session storage', () => {
it('ignores unsupported state key', () => {
setStorageState({
someKey: 10,
searchTerm: 'rule',
tags: ['tag-b'],
source: 'custom',
enabled: false,
field: 'name',
order: 'asc',
perPage: 5,
});
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
expectRulesTableState();
expectTablePage(1);
});
});
});
describe('when persisted state is partially unavailable', () => {
describe('and on the rules management tab', () => {
beforeEach(() => {
login();
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
});
it('persists after clearing the session storage', () => {
changeRulesTableState();
goToTablePage(2);
cy.window().then((win) => {
win.sessionStorage.clear();
});
cy.reload();
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(2);
});
it('persists after clearing the url state', () => {
changeRulesTableState();
goToTablePage(2);
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(1);
});
});
});
describe('when corrupted', () => {
describe('and on the rules management tab', () => {
beforeEach(() => {
login();
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL);
});
it('persists after corrupting the session storage data', () => {
changeRulesTableState();
goToTablePage(2);
cy.window().then((win) => {
win.sessionStorage.setItem('securitySolution.rulesTable', '!invalid');
cy.reload();
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL, { qs: { rulesTable: '(!invalid)' } });
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(2);
});
});
it('persists after corrupting the url param data', () => {
changeRulesTableState();
goToTablePage(2);
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL, { qs: { rulesTable: '(!invalid)' } });
expectRulesManagementTab();
expectRulesTableState();
expectTablePage(1);
});
it('DOES NOT persist after corrupting the session storage and url param data', () => {
changeRulesTableState();
goToTablePage(2);
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL, {
qs: { rulesTable: '(!invalid)' },
onBeforeLoad: (win) => {
win.sessionStorage.setItem('securitySolution.rulesTable', '!invalid');
},
expectTablePage(1);
});
expectRulesManagementTab();
expectDefaultRulesTableState();
it('DOES NOT persist after corrupting the session storage and url param data', () => {
changeRulesTableState();
goToTablePage(2);
visit(SECURITY_DETECTIONS_RULES_MANAGEMENT_URL, {
qs: { rulesTable: '(!invalid)' },
onBeforeLoad: (win) => {
win.sessionStorage.setItem('securitySolution.rulesTable', '!invalid');
},
});
expectRulesManagementTab();
expectDefaultRulesTableState();
});
});
});
});
});
}
);

View file

@ -33,69 +33,74 @@ const RULE_2 = createRuleAssetSavedObject({
rule_id: 'rule_2',
});
// TODO: https://github.com/elastic/kibana/issues/161540
// FLAKY: https://github.com/elastic/kibana/issues/165643
describe.skip('Rules table: selection', { tags: ['@ess', '@serverless'] }, () => {
before(() => {
cleanKibana();
});
beforeEach(() => {
login();
/* Create and install two mock rules */
createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2] });
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPrebuiltDetectionRulesToBeLoaded();
});
it('should correctly update the selection label when rules are individually selected and unselected', () => {
waitForPrebuiltDetectionRulesToBeLoaded();
selectRulesByName(['Test rule 1', 'Test rule 2']);
cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', '2');
unselectRulesByName(['Test rule 1', 'Test rule 2']);
cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', '0');
});
it('should correctly update the selection label when rules are bulk selected and then bulk un-selected', () => {
waitForPrebuiltDetectionRulesToBeLoaded();
cy.get(SELECT_ALL_RULES_BTN).click();
getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => {
cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', availablePrebuiltRulesCount);
describe.skip(
'Rules table: selection',
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
before(() => {
cleanKibana();
});
// Un-select all rules via the Bulk Selection button from the Utility bar
cy.get(SELECT_ALL_RULES_BTN).click();
// Current selection should be 0 rules
cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', '0');
// Bulk selection button should be back to displaying all rules
getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => {
cy.get(SELECT_ALL_RULES_BTN).should('contain.text', availablePrebuiltRulesCount);
});
});
it('should correctly update the selection label when rules are bulk selected and then unselected via the table select all checkbox', () => {
waitForPrebuiltDetectionRulesToBeLoaded();
cy.get(SELECT_ALL_RULES_BTN).click();
getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => {
cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', availablePrebuiltRulesCount);
beforeEach(() => {
login();
/* Create and install two mock rules */
createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2] });
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPrebuiltDetectionRulesToBeLoaded();
});
// Un-select all rules via the Un-select All checkbox from the table
cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click();
it('should correctly update the selection label when rules are individually selected and unselected', () => {
waitForPrebuiltDetectionRulesToBeLoaded();
// Current selection should be 0 rules
cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', '0');
// Bulk selection button should be back to displaying all rules
getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => {
cy.get(SELECT_ALL_RULES_BTN).should('contain.text', availablePrebuiltRulesCount);
selectRulesByName(['Test rule 1', 'Test rule 2']);
cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', '2');
unselectRulesByName(['Test rule 1', 'Test rule 2']);
cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', '0');
});
});
});
it('should correctly update the selection label when rules are bulk selected and then bulk un-selected', () => {
waitForPrebuiltDetectionRulesToBeLoaded();
cy.get(SELECT_ALL_RULES_BTN).click();
getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => {
cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', availablePrebuiltRulesCount);
});
// Un-select all rules via the Bulk Selection button from the Utility bar
cy.get(SELECT_ALL_RULES_BTN).click();
// Current selection should be 0 rules
cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', '0');
// Bulk selection button should be back to displaying all rules
getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => {
cy.get(SELECT_ALL_RULES_BTN).should('contain.text', availablePrebuiltRulesCount);
});
});
it('should correctly update the selection label when rules are bulk selected and then unselected via the table select all checkbox', () => {
waitForPrebuiltDetectionRulesToBeLoaded();
cy.get(SELECT_ALL_RULES_BTN).click();
getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => {
cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', availablePrebuiltRulesCount);
});
// Un-select all rules via the Un-select All checkbox from the table
cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click();
// Current selection should be 0 rules
cy.get(SELECTED_RULES_NUMBER_LABEL).should('contain.text', '0');
// Bulk selection button should be back to displaying all rules
getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => {
cy.get(SELECT_ALL_RULES_BTN).should('contain.text', availablePrebuiltRulesCount);
});
});
}
);

View file

@ -36,6 +36,7 @@ import {
} from '../../../../tasks/table_pagination';
import { TABLE_FIRST_PAGE, TABLE_SECOND_PAGE } from '../../../../screens/table_pagination';
// TODO: https://github.com/elastic/kibana/issues/161540
describe('Rules table: sorting', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
before(() => {
cleanKibana();

View file

@ -35,8 +35,10 @@ const TEXT_LIST_FILE_NAME = 'value_list.txt';
const IPS_LIST_FILE_NAME = 'ip_list.txt';
const CIDRS_LIST_FILE_NAME = 'cidr_list.txt';
// TODO: https://github.com/elastic/kibana/issues/161539
// FLAKY: https://github.com/elastic/kibana/issues/165699
describe('value lists', () => {
describe('value lists', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('management modal', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
beforeEach(() => {
login();
@ -56,6 +58,7 @@ describe('value lists', () => {
closeValueListsModal();
});
// TODO: https://github.com/elastic/kibana/issues/161539
// Flaky in serverless tests
describe('create list types', { tags: ['@brokenInServerless'] }, () => {
beforeEach(() => {
@ -115,6 +118,7 @@ describe('value lists', () => {
});
});
// TODO: https://github.com/elastic/kibana/issues/161539
// Flaky in serverless tests
describe('delete list types', { tags: ['@brokenInServerless'] }, () => {
it('deletes a "keyword" list from an uploaded file', () => {
@ -162,6 +166,7 @@ describe('value lists', () => {
});
});
// TODO: https://github.com/elastic/kibana/issues/161539
// Flaky in serverless tests
describe('export list types', { tags: ['@brokenInServerless'] }, () => {
it('exports a "keyword" list from an uploaded file', () => {
@ -259,11 +264,17 @@ describe('value lists', () => {
});
});
describe('user with restricted access role', { tags: '@ess' }, () => {
it('Does not allow a t1 analyst user to upload a value list', () => {
login(ROLES.t1_analyst);
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL, ROLES.t1_analyst);
cy.get(VALUE_LISTS_MODAL_ACTIVATOR).should('have.attr', 'disabled');
});
});
// TODO: https://github.com/elastic/kibana/issues/164451 We should find a way to make this spec work in Serverless
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'user with restricted access role',
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
it('Does not allow a t1 analyst user to upload a value list', () => {
login(ROLES.t1_analyst);
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL, ROLES.t1_analyst);
cy.get(VALUE_LISTS_MODAL_ACTIVATOR).should('have.attr', 'disabled');
});
}
);
});

View file

@ -39,13 +39,14 @@ import {
previewErrorButtonClick,
} from '../../tasks/entity_analytics';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'Entity analytics management page',
{
env: {
ftrConfig: { enableExperimental: ['riskScoringRoutesEnabled', 'riskScoringPersistence'] },
},
tags: ['@ess', '@brokenInServerless'],
tags: ['@ess', '@serverless', '@brokenInServerless'],
},
() => {
before(() => {

View file

@ -36,10 +36,11 @@ import {
} from '../../../screens/exceptions';
import { goToEndpointExceptionsTab, waitForTheRuleToBeExecuted } from '../../../tasks/rule_details';
// TODO: https://github.com/elastic/kibana/issues/161539
// See https://github.com/elastic/kibana/issues/163967
describe.skip(
'Endpoint Exceptions workflows from Alert',
{ tags: ['@ess', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
const ITEM_NAME = 'Sample Exception List Item';
const ITEM_NAME_EDIT = 'Sample Exception List Item';

View file

@ -37,10 +37,11 @@ import {
} from '../../../../screens/exceptions';
import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule';
// TODO: https://github.com/elastic/kibana/issues/161539
// See https://github.com/elastic/kibana/issues/163967
describe.skip(
'Auto populate exception with Alert data',
{ tags: ['@ess', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
const ITEM_NAME = 'Sample Exception Item';
const ITEM_NAME_EDIT = 'Sample Exception Item Edit';

View file

@ -26,8 +26,9 @@ import {
submitNewExceptionItem,
} from '../../../../tasks/exceptions';
// TODO: https://github.com/elastic/kibana/issues/161539
// See https://github.com/elastic/kibana/issues/163967
describe('Close matching Alerts ', () => {
describe('Close matching Alerts ', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
const ITEM_NAME = 'Sample Exception Item';
beforeEach(() => {
@ -53,7 +54,8 @@ describe('Close matching Alerts ', () => {
cy.task('esArchiverUnload', 'exceptions');
});
it('Should create a Rule exception item from alert actions overflow menu and close all matching alerts', () => {
// TODO: https://github.com/elastic/kibana/issues/161539
it.skip('Should create a Rule exception item from alert actions overflow menu and close all matching alerts', () => {
cy.get(LOADING_INDICATOR).should('not.exist');
addExceptionFromFirstAlert();

View file

@ -56,6 +56,7 @@ import {
} from '../../../tasks/api_calls/exceptions';
import { getExceptionList } from '../../../objects/exception';
// TODO: https://github.com/elastic/kibana/issues/161539
// Test Skipped until we fix the Flyout rerendering issue
// https://github.com/elastic/kibana/issues/154994
@ -64,7 +65,7 @@ import { getExceptionList } from '../../../objects/exception';
// to test in enzyme and very small changes can inadvertently add
// bugs. As the complexity within the builder grows, these should
// ensure the most basic logic holds.
describe.skip('Exceptions flyout', { tags: ['@ess', '@serverless'] }, () => {
describe.skip('Exceptions flyout', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
before(() => {
cy.task('esArchiverResetKibana');
// this is a made-up index that has just the necessary

View file

@ -25,12 +25,13 @@ import {
import { ruleDetailsUrl } from '../../../urls/navigation';
import { deleteAlertsAndRules } from '../../../tasks/common';
// TODO: https://github.com/elastic/kibana/issues/161539
// FLAKY: https://github.com/elastic/kibana/issues/165651
// FLAKY: https://github.com/elastic/kibana/issues/165734
// FLAKY: https://github.com/elastic/kibana/issues/165652
describe(
'Add multiple conditions and validate the generated exceptions',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
beforeEach(() => {
cy.task('esArchiverResetKibana');

View file

@ -43,10 +43,11 @@ const goToRulesAndOpenValueListModal = () => {
openValueListsModal();
};
// TODO: https://github.com/elastic/kibana/issues/161539
// Flaky on serverless
describe(
'Use Value list in exception entry',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
beforeEach(() => {
cleanKibana();

View file

@ -46,10 +46,11 @@ import {
createEndpointExceptionListItem,
} from '../../../tasks/api_calls/exceptions';
// TODO: https://github.com/elastic/kibana/issues/161539
// FLAKY: https://github.com/elastic/kibana/issues/165736
describe(
'Add endpoint exception from rule details',
{ tags: ['@ess', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
const ITEM_NAME = 'Sample Exception List Item';
const NEW_ITEM_NAME = 'Exception item-EDITED';

View file

@ -58,149 +58,227 @@ import {
} from '../../../tasks/api_calls/exceptions';
import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
describe('Add/edit exception from rule details', { tags: ['@ess', '@brokenInServerless'] }, () => {
const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert';
const FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD = 'agent.name';
const ITEM_FIELD = 'unique_value.test';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'Add/edit exception from rule details',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert';
const FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD = 'agent.name';
const ITEM_FIELD = 'unique_value.test';
before(() => {
cy.task('esArchiverResetKibana');
cy.task('esArchiverLoad', { archiveName: 'exceptions' });
});
before(() => {
cy.task('esArchiverResetKibana');
cy.task('esArchiverLoad', { archiveName: 'exceptions' });
});
after(() => {
cy.task('esArchiverUnload', 'exceptions');
});
after(() => {
cy.task('esArchiverUnload', 'exceptions');
});
beforeEach(() => {
login();
deleteAlertsAndRules();
const exceptionList = getExceptionList();
deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type);
});
describe('existing list and items', () => {
const exceptionList = getExceptionList();
beforeEach(() => {
// create rule with exceptions
createExceptionList(exceptionList, exceptionList.list_id).then((response) => {
createExceptionListItem(exceptionList.list_id, {
list_id: exceptionList.list_id,
item_id: 'simple_list_item',
tags: [],
type: 'simple',
description: 'Test exception item 2',
name: 'Sample Exception List Item 2',
namespace_type: 'single',
entries: [
{
field: ITEM_FIELD,
operator: 'included',
type: 'match_any',
value: ['foo'],
},
],
login();
deleteAlertsAndRules();
const exceptionList = getExceptionList();
deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type);
});
describe('existing list and items', () => {
const exceptionList = getExceptionList();
beforeEach(() => {
// create rule with exceptions
createExceptionList(exceptionList, exceptionList.list_id).then((response) => {
createExceptionListItem(exceptionList.list_id, {
list_id: exceptionList.list_id,
item_id: 'simple_list_item',
tags: [],
type: 'simple',
description: 'Test exception item 2',
name: 'Sample Exception List Item 2',
namespace_type: 'single',
entries: [
{
field: ITEM_FIELD,
operator: 'included',
type: 'match_any',
value: ['foo'],
},
],
});
createRule(
getNewRule({
query: 'agent.name:*',
index: ['exceptions*'],
exceptions_list: [
{
id: response.body.id,
list_id: exceptionList.list_id,
type: exceptionList.type,
namespace_type: exceptionList.namespace_type,
},
],
rule_id: '2',
})
).then((rule) => visitWithoutDateRange(ruleDetailsUrl(rule.body.id, 'rule_exceptions')));
});
});
it('Edits an exception item', () => {
const NEW_ITEM_NAME = 'Exception item-EDITED';
const ITEM_NAME = 'Sample Exception List Item 2';
// displays existing exception items
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist');
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', ITEM_NAME);
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).should(
'have.text',
' unique_value.testis one of foo'
);
// open edit exception modal
openEditException();
// edit exception item name
editExceptionFlyoutItemName(NEW_ITEM_NAME);
// check that the existing item's field is being populated
cy.get(EXCEPTION_ITEM_CONTAINER)
.eq(0)
.find(FIELD_INPUT_PARENT)
.eq(0)
.should('have.text', ITEM_FIELD);
cy.get(VALUES_MATCH_ANY_INPUT).should('have.text', 'foo');
// edit conditions
editException(FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD, 0, 0);
// submit
submitEditedExceptionItem();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
// check that updates stuck
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', NEW_ITEM_NAME);
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).should('have.text', ' agent.nameIS foo');
});
describe('rule with existing shared exceptions', () => {
it('Creates an exception item to add to shared list', () => {
// displays existing exception items
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist');
// open add exception modal
addExceptionFlyoutFromViewerHeader();
// add exception item conditions
addExceptionConditions(getException());
// Name is required so want to check that submit is still disabled
cy.get(CONFIRM_BTN).should('have.attr', 'disabled');
// add exception item name
addExceptionFlyoutItemName('My item name');
// select to add exception item to a shared list
selectSharedListToAddExceptionTo(1);
// not testing close alert functionality here, just ensuring that the options appear as expected
cy.get(CLOSE_ALERTS_CHECKBOX).should('exist');
cy.get(CLOSE_ALERTS_CHECKBOX).should('not.have.attr', 'disabled');
// submit
submitNewExceptionItem();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 2);
});
it('Creates an exception item to add to rule only', () => {
// displays existing exception items
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist');
// open add exception modal
addExceptionFlyoutFromViewerHeader();
// add exception item conditions
addExceptionConditions(getException());
// Name is required so want to check that submit is still disabled
cy.get(CONFIRM_BTN).should('have.attr', 'disabled');
// add exception item name
addExceptionFlyoutItemName('My item name');
// select to add exception item to rule only
selectAddToRuleRadio();
// not testing close alert functionality here, just ensuring that the options appear as expected
cy.get(CLOSE_ALERTS_CHECKBOX).should('exist');
cy.get(CLOSE_ALERTS_CHECKBOX).should('not.have.attr', 'disabled');
// submit
submitNewExceptionItem();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 2);
});
// Trying to figure out with EUI why the search won't trigger
it('Can search for items', () => {
// displays existing exception items
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist');
// can search for an exception value
searchForExceptionItem('foo');
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
// displays empty search result view if no matches found
searchForExceptionItem('abc');
// new exception item displays
cy.get(NO_EXCEPTIONS_SEARCH_RESULTS_PROMPT).should('exist');
});
});
});
describe('rule without existing exceptions', () => {
beforeEach(() => {
createRule(
getNewRule({
query: 'agent.name:*',
index: ['exceptions*'],
exceptions_list: [
{
id: response.body.id,
list_id: exceptionList.list_id,
type: exceptionList.type,
namespace_type: exceptionList.namespace_type,
},
],
rule_id: '2',
interval: '10s',
rule_id: 'rule_testing',
})
).then((rule) => visitWithoutDateRange(ruleDetailsUrl(rule.body.id, 'rule_exceptions')));
});
});
it('Edits an exception item', () => {
const NEW_ITEM_NAME = 'Exception item-EDITED';
const ITEM_NAME = 'Sample Exception List Item 2';
// displays existing exception items
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist');
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', ITEM_NAME);
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).should('have.text', ' unique_value.testis one of foo');
// open edit exception modal
openEditException();
// edit exception item name
editExceptionFlyoutItemName(NEW_ITEM_NAME);
// check that the existing item's field is being populated
cy.get(EXCEPTION_ITEM_CONTAINER)
.eq(0)
.find(FIELD_INPUT_PARENT)
.eq(0)
.should('have.text', ITEM_FIELD);
cy.get(VALUES_MATCH_ANY_INPUT).should('have.text', 'foo');
// edit conditions
editException(FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD, 0, 0);
// submit
submitEditedExceptionItem();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
// check that updates stuck
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', NEW_ITEM_NAME);
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).should('have.text', ' agent.nameIS foo');
});
describe('rule with existing shared exceptions', () => {
it('Creates an exception item to add to shared list', () => {
// displays existing exception items
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist');
// open add exception modal
addExceptionFlyoutFromViewerHeader();
// add exception item conditions
addExceptionConditions(getException());
// Name is required so want to check that submit is still disabled
cy.get(CONFIRM_BTN).should('have.attr', 'disabled');
// add exception item name
addExceptionFlyoutItemName('My item name');
// select to add exception item to a shared list
selectSharedListToAddExceptionTo(1);
// not testing close alert functionality here, just ensuring that the options appear as expected
cy.get(CLOSE_ALERTS_CHECKBOX).should('exist');
cy.get(CLOSE_ALERTS_CHECKBOX).should('not.have.attr', 'disabled');
// submit
submitNewExceptionItem();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 2);
afterEach(() => {
cy.task('esArchiverUnload', 'exceptions_2');
});
it('Creates an exception item to add to rule only', () => {
// displays existing exception items
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist');
it('Cannot create an item to add to rule but not shared list as rule has no lists attached', () => {
// when no exceptions exist, empty component shows with action to add exception
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist');
// open add exception modal
addExceptionFlyoutFromViewerHeader();
openExceptionFlyoutFromEmptyViewerPrompt();
// add exception item conditions
addExceptionConditions(getException());
addExceptionConditions({
field: 'agent.name',
operator: 'is',
values: ['foo'],
});
// Name is required so want to check that submit is still disabled
cy.get(CONFIRM_BTN).should('have.attr', 'disabled');
@ -211,120 +289,50 @@ describe('Add/edit exception from rule details', { tags: ['@ess', '@brokenInServ
// select to add exception item to rule only
selectAddToRuleRadio();
// not testing close alert functionality here, just ensuring that the options appear as expected
cy.get(CLOSE_ALERTS_CHECKBOX).should('exist');
cy.get(CLOSE_ALERTS_CHECKBOX).should('not.have.attr', 'disabled');
// Check that add to shared list is disabled, should be unless
// rule has shared lists attached to it already
cy.get(ADD_TO_SHARED_LIST_RADIO_INPUT).should('have.attr', 'disabled');
// Close matching alerts
selectBulkCloseAlerts();
// submit
submitNewExceptionItem();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 2);
});
// Trying to figure out with EUI why the search won't trigger
it('Can search for items', () => {
// displays existing exception items
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist');
// can search for an exception value
searchForExceptionItem('foo');
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
// displays empty search result view if no matches found
searchForExceptionItem('abc');
// Alerts table should now be empty from having added exception and closed
// matching alert
goToAlertsTab();
cy.get(EMPTY_ALERT_TABLE).should('exist');
// new exception item displays
cy.get(NO_EXCEPTIONS_SEARCH_RESULTS_PROMPT).should('exist');
// Closed alert should appear in table
goToClosedAlertsOnRuleDetailsPage();
cy.get(ALERTS_COUNT).should('exist');
cy.get(ALERTS_COUNT).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`);
// Remove the exception and load an event that would have matched that exception
// to show that said exception now starts to show up again
goToExceptionsTab();
// when removing exception and again, no more exist, empty screen shows again
removeException();
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist');
// load more docs
cy.task('esArchiverLoad', { archiveName: 'exceptions_2' });
// now that there are no more exceptions, the docs should match and populate alerts
goToAlertsTab();
waitForAlertsToPopulate();
goToOpenedAlertsOnRuleDetailsPage();
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();
cy.get(ALERTS_COUNT).should('exist');
cy.get(ALERTS_COUNT).should('have.text', '2 alerts');
});
});
});
describe('rule without existing exceptions', () => {
beforeEach(() => {
createRule(
getNewRule({
query: 'agent.name:*',
index: ['exceptions*'],
interval: '10s',
rule_id: 'rule_testing',
})
).then((rule) => visitWithoutDateRange(ruleDetailsUrl(rule.body.id, 'rule_exceptions')));
});
afterEach(() => {
cy.task('esArchiverUnload', 'exceptions_2');
});
it('Cannot create an item to add to rule but not shared list as rule has no lists attached', () => {
// when no exceptions exist, empty component shows with action to add exception
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist');
// open add exception modal
openExceptionFlyoutFromEmptyViewerPrompt();
// add exception item conditions
addExceptionConditions({
field: 'agent.name',
operator: 'is',
values: ['foo'],
});
// Name is required so want to check that submit is still disabled
cy.get(CONFIRM_BTN).should('have.attr', 'disabled');
// add exception item name
addExceptionFlyoutItemName('My item name');
// select to add exception item to rule only
selectAddToRuleRadio();
// Check that add to shared list is disabled, should be unless
// rule has shared lists attached to it already
cy.get(ADD_TO_SHARED_LIST_RADIO_INPUT).should('have.attr', 'disabled');
// Close matching alerts
selectBulkCloseAlerts();
// submit
submitNewExceptionItem();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
// Alerts table should now be empty from having added exception and closed
// matching alert
goToAlertsTab();
cy.get(EMPTY_ALERT_TABLE).should('exist');
// Closed alert should appear in table
goToClosedAlertsOnRuleDetailsPage();
cy.get(ALERTS_COUNT).should('exist');
cy.get(ALERTS_COUNT).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`);
// Remove the exception and load an event that would have matched that exception
// to show that said exception now starts to show up again
goToExceptionsTab();
// when removing exception and again, no more exist, empty screen shows again
removeException();
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist');
// load more docs
cy.task('esArchiverLoad', { archiveName: 'exceptions_2' });
// now that there are no more exceptions, the docs should match and populate alerts
goToAlertsTab();
waitForAlertsToPopulate();
goToOpenedAlertsOnRuleDetailsPage();
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();
cy.get(ALERTS_COUNT).should('exist');
cy.get(ALERTS_COUNT).should('have.text', '2 alerts');
});
});
});
}
);

View file

@ -40,9 +40,10 @@ import {
} from '../../../screens/exceptions';
import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'Add exception using data views from rule details',
{ tags: ['@ess', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert';
const ITEM_NAME = 'Sample Exception List Item';

View file

@ -26,7 +26,8 @@ import {
deleteExceptionList,
} from '../../../tasks/api_calls/exceptions';
describe('Exceptions viewer read only', { tags: '@ess' }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539 Do we need this to run in Serverless?
describe('Exceptions viewer read only', { tags: ['@ess', '@skipInServerless'] }, () => {
const exceptionList = getExceptionList();
beforeEach(() => {

View file

@ -40,10 +40,11 @@ const getExceptionList1 = () => ({
const EXCEPTION_LIST_NAME = 'Newly created list';
// TODO: https://github.com/elastic/kibana/issues/161539
// FLAKY: https://github.com/elastic/kibana/issues/165640
describe(
'Exception list detail page',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
before(() => {
cy.task('esArchiverResetKibana');

View file

@ -37,10 +37,11 @@ import {
waitForExceptionsTableToBeLoaded,
} from '../../../tasks/exceptions_table';
// TODO: https://github.com/elastic/kibana/issues/161539
// FLAKY: https://github.com/elastic/kibana/issues/165795
describe(
'Add, edit and delete exception',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
beforeEach(() => {
cy.task('esArchiverResetKibana');

View file

@ -40,8 +40,9 @@ const getExceptionList2 = () => ({
list_id: 'exception_list_2',
});
// TODO: https://github.com/elastic/kibana/issues/161539
// Flaky in serverless tests
describe('Duplicate List', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
describe('Duplicate List', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
beforeEach(() => {
cy.task('esArchiverResetKibana');
login();

View file

@ -35,7 +35,9 @@ const getExceptionList2 = () => ({
name: EXCEPTION_LIST_NAME_TWO,
list_id: 'exception_list_2',
});
describe('Filter Lists', { tags: ['@ess', '@serverless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Filter Lists', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
beforeEach(() => {
cy.task('esArchiverResetKibana');
login();

View file

@ -20,8 +20,9 @@ import {
import { login, visitWithoutDateRange } from '../../../../tasks/login';
import { EXCEPTIONS_URL } from '../../../../urls/navigation';
// TODO: https://github.com/elastic/kibana/issues/161539
// Flaky in serverless
describe('Import Lists', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
describe('Import Lists', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
const LIST_TO_IMPORT_FILENAME = 'cypress/fixtures/7_16_exception_list.ndjson';
before(() => {
cy.task('esArchiverResetKibana');

View file

@ -47,10 +47,11 @@ const getExceptionList2 = () => ({
let exceptionListResponse: Cypress.Response<ExceptionListSchema>;
// TODO: https://github.com/elastic/kibana/issues/161539
// FLAKY: https://github.com/elastic/kibana/issues/165690
describe(
'Manage lists from "Shared Exception Lists" page',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
describe('Create/Export/Delete List', () => {
before(() => {

View file

@ -21,7 +21,8 @@ import {
import { login, visitWithoutDateRange } from '../../../../tasks/login';
import { EXCEPTIONS_URL } from '../../../../urls/navigation';
describe('Shared exception lists - read only', { tags: '@ess' }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539 Do we need to run it in Serverless?
describe('Shared exception lists - read only', { tags: ['@ess', '@skipInServerless'] }, () => {
before(() => {
cy.task('esArchiverResetKibana');
});

View file

@ -15,8 +15,9 @@ import {
import { login, visit } from '../../tasks/login';
import { OVERVIEW_URL } from '../../urls/navigation';
// TODO: https://github.com/elastic/kibana/issues/161539
// FLAKY: https://github.com/elastic/kibana/issues/165709
describe.skip('CTI Link Panel', { tags: ['@ess', '@serverless'] }, () => {
describe.skip('CTI Link Panel', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
beforeEach(() => {
login();
});
@ -31,35 +32,40 @@ describe.skip('CTI Link Panel', { tags: ['@ess', '@serverless'] }, () => {
.and('match', /app\/integrations\/browse\/threat_intel/);
});
describe('enabled threat intel module', { tags: ['@brokenInServerless'] }, () => {
before(() => {
// illegal_argument_exception: unknown setting [index.lifecycle.name]
cy.task('esArchiverLoad', { archiveName: 'threat_indicator' });
});
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'enabled threat intel module',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
before(() => {
// illegal_argument_exception: unknown setting [index.lifecycle.name]
cy.task('esArchiverLoad', { archiveName: 'threat_indicator' });
});
beforeEach(() => {
login();
});
beforeEach(() => {
login();
});
after(() => {
cy.task('esArchiverUnload', 'threat_indicator');
});
after(() => {
cy.task('esArchiverUnload', 'threat_indicator');
});
it('renders disabled dashboard module as expected when there are no events in the selected time period', () => {
visit(
`${OVERVIEW_URL}?sourcerer=(timerange:(from:%272021-07-08T04:00:00.000Z%27,kind:absolute,to:%272021-07-09T03:59:59.999Z%27))`
);
cy.get(`${OVERVIEW_CTI_LINKS}`).should('exist');
cy.get(`${OVERVIEW_CTI_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 indicators');
});
it('renders disabled dashboard module as expected when there are no events in the selected time period', () => {
visit(
`${OVERVIEW_URL}?sourcerer=(timerange:(from:%272021-07-08T04:00:00.000Z%27,kind:absolute,to:%272021-07-09T03:59:59.999Z%27))`
);
cy.get(`${OVERVIEW_CTI_LINKS}`).should('exist');
cy.get(`${OVERVIEW_CTI_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 indicators');
});
it('renders dashboard module as expected when there are events in the selected time period', () => {
visit(OVERVIEW_URL);
it('renders dashboard module as expected when there are events in the selected time period', () => {
visit(OVERVIEW_URL);
cy.get(`${OVERVIEW_CTI_LINKS}`).should('exist');
cy.get(OVERVIEW_CTI_LINKS).should('not.contain.text', 'Anomali');
cy.get(OVERVIEW_CTI_LINKS).should('contain.text', 'AbuseCH malware');
cy.get(`${OVERVIEW_CTI_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 1 indicator');
});
});
cy.get(`${OVERVIEW_CTI_LINKS}`).should('exist');
cy.get(OVERVIEW_CTI_LINKS).should('not.contain.text', 'Anomali');
cy.get(OVERVIEW_CTI_LINKS).should('contain.text', 'AbuseCH malware');
cy.get(`${OVERVIEW_CTI_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 1 indicator');
});
}
);
});

View file

@ -19,7 +19,7 @@
"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 ../../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:open:serverless": "yarn cypress:serverless open --config-file ../../test/security_solution_cypress/cypress/cypress_serverless.config.ts --spec './cypress/e2e/**/*.cy.ts'",
"cypress:run:serverless": "yarn cypress:serverless --spec '**/cypress/e2e/!(investigations|explore)/**/*.cy.ts'",
"cypress:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/!(investigations|explore)/**/*.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:changed-specs-only:serverless": "yarn cypress:serverless --changed-specs-only --env burn=2",