diff --git a/cypress.json b/cypress.json
index 676ad4cb2..56235296e 100644
--- a/cypress.json
+++ b/cypress.json
@@ -1,7 +1,8 @@
{
"viewportHeight": 900,
"viewportWidth": 1440,
- "defaultCommandTimeout": 10000,
+ "defaultCommandTimeout": 20000,
+ "retries": 1,
"env": {
"opensearch_url": "localhost:9200",
"opensearch_dashboards": "http://localhost:5601",
diff --git a/cypress/integration/1_detectors.spec.js b/cypress/integration/1_detectors.spec.js
index 65d1027f2..75573f160 100644
--- a/cypress/integration/1_detectors.spec.js
+++ b/cypress/integration/1_detectors.spec.js
@@ -3,34 +3,31 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { PLUGIN_NAME } from '../support/constants';
+import { OPENSEARCH_DASHBOARDS_URL } from '../support/constants';
import sample_field_mappings from '../fixtures/sample_field_mappings.json';
import sample_index_settings from '../fixtures/sample_index_settings.json';
describe('Detectors', () => {
const indexName = 'cypress-test-windows';
+ const detectorName = 'test detector';
before(() => {
- cy.deleteAllIndices();
+ cy.cleanUpTests();
// Create test index
cy.createIndex(indexName, sample_index_settings);
- cy.contains('test detector').should('not.exist');
+ cy.contains(detectorName).should('not.exist');
});
beforeEach(() => {
// Visit Detectors page
- cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/detectors`);
-
- //wait for page to load
- cy.wait(7000);
+ cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/detectors`);
// Check that correct page is showing
- cy.url().should(
- 'eq',
- 'http://localhost:5601/app/opensearch_security_analytics_dashboards#/detectors'
- );
+ cy.waitForPageLoad('detectors', {
+ contains: 'Threat detectors',
+ });
});
it('...can be created', () => {
@@ -38,19 +35,15 @@ describe('Detectors', () => {
cy.contains('Create detector').click({ force: true });
// Check to ensure process started
- cy.contains('Define detector');
- cy.url().should(
- 'eq',
- 'http://localhost:5601/app/opensearch_security_analytics_dashboards#/create-detector'
- );
+ cy.waitForPageLoad('create-detector', {
+ contains: 'Define detector',
+ });
// Enter a name for the detector in the appropriate input
cy.get(`input[placeholder="Enter a name for the detector."]`).type('test detector{enter}');
- // Select our pre-seeded data source (cypress-test-windows)
- cy.get(`[data-test-subj="define-detector-select-data-source"]`).type(
- 'cypress-test-windows{enter}'
- );
+ // Select our pre-seeded data source (check indexName)
+ cy.get(`[data-test-subj="define-detector-select-data-source"]`).type(`${indexName}{enter}`);
// Select threat detector type (Windows logs)
cy.get(`input[id="windows"]`).click({ force: true });
@@ -59,7 +52,7 @@ describe('Detectors', () => {
cy.get('[data-test-subj="detection-rules-btn"]').click({ timeout: 5000 });
// find search, type USB
- cy.get(`[placeholder="Search..."]`).type('USB Device Plugged').trigger('search');
+ cy.triggerSearchField('Search...', 'USB Device Plugged');
// Disable all rules
cy.contains('tr', 'USB Device Plugged', { timeout: 20000 });
@@ -121,21 +114,23 @@ describe('Detectors', () => {
// Confirm entries user has made
cy.contains('Detector details');
- cy.contains('test detector');
+ cy.contains(detectorName);
cy.contains('windows');
cy.contains(indexName);
cy.contains('Alert on test_trigger');
// Create the detector
cy.get('button').contains('Create').click({ force: true });
-
- cy.wait(10000);
+ cy.waitForPageLoad('detector-details', {
+ contains: detectorName,
+ });
// Confirm detector active
- cy.contains('There are no existing detectors.', { timeout: 20000 }).should('not.exist');
- cy.contains('test detector');
+ cy.contains(detectorName);
cy.contains('Active');
+ cy.contains('View Alerts');
cy.contains('View Findings');
+ cy.contains('Actions');
cy.contains('Detector configuration');
cy.contains('Field mappings');
cy.contains('Alert triggers');
@@ -146,21 +141,19 @@ describe('Detectors', () => {
it('...basic details can be edited', () => {
// Click on detector name
- cy.contains('test detector').click({ force: true });
-
- // Confirm on detector details page
- cy.contains('test detector');
+ cy.contains(detectorName).click({ force: true });
+ cy.waitForPageLoad('detector-details', {
+ contains: detectorName,
+ });
// Click "Edit" button in detector details
cy.get(`[data-test-subj="edit-detector-basic-details"]`).click({ force: true });
// Confirm arrival at "Edit detector details" page
- cy.url().should(
- 'include',
- 'http://localhost:5601/app/opensearch_security_analytics_dashboards#/edit-detector-details'
- );
+ cy.waitForPageLoad('edit-detector-details', {
+ contains: 'Edit detector details',
+ });
- cy.wait(5000);
// Change detector name
cy.get(`[data-test-subj="define-detector-detector-name"]`).type('_edited');
@@ -173,18 +166,16 @@ describe('Detectors', () => {
);
// Change detector scheduling
- cy.get(`[data-test-subj="detector-schedule-number-select"]`).type('0');
+ cy.get(`[data-test-subj="detector-schedule-number-select"]`).type('{selectall}10');
cy.get(`[data-test-subj="detector-schedule-unit-select"]`).select('Hours');
- cy.wait(7000);
// Save changes to detector details
cy.get(`[data-test-subj="save-basic-details-edits"]`).click({ force: true });
// Confirm taken to detector details page
- cy.url({ timeout: 20000 }).should(
- 'include',
- 'http://localhost:5601/app/opensearch_security_analytics_dashboards#/detector-details'
- );
+ cy.waitForPageLoad('detector-details', {
+ contains: detectorName,
+ });
// Verify edits are applied
cy.contains('test detector_edited');
@@ -195,13 +186,15 @@ describe('Detectors', () => {
it('...rules can be edited', () => {
// Ensure start on main detectors page
- cy.url().should(
- 'eq',
- 'http://localhost:5601/app/opensearch_security_analytics_dashboards#/detectors'
- );
+ cy.waitForPageLoad('detectors', {
+ contains: 'Threat detectors',
+ });
// Click on detector name
- cy.contains('test detector').click({ force: true });
+ cy.contains(detectorName).click({ force: true });
+ cy.waitForPageLoad('detector-details', {
+ contains: detectorName,
+ });
// Confirm number of rules before edit
cy.contains('Active rules (1)');
@@ -216,7 +209,7 @@ describe('Detectors', () => {
);
// Search for specific rule
- cy.get(`[placeholder="Search..."]`).type('USB Device').trigger('search', { timeout: 5000 });
+ cy.triggerSearchField('Search...', 'USB Device');
// Toggle single search result to unchecked
cy.contains('tr', 'USB Device Plugged').within(() => {
@@ -235,13 +228,12 @@ describe('Detectors', () => {
cy.get(`[data-test-subj="edit-detector-rules"]`).click({ force: true });
// Confirm arrival on "Edit detector rules" page
- cy.url().should(
- 'include',
- 'http://localhost:5601/app/opensearch_security_analytics_dashboards#/edit-detector-rules'
- );
+ cy.waitForPageLoad('edit-detector-rules', {
+ contains: 'Edit detector rules',
+ });
// Search for specific rule
- cy.get(`[placeholder="Search..."]`).focus().type('USB').trigger('search', { timeout: 5000 });
+ cy.triggerSearchField('Search...', 'USB');
// Toggle single search result to checked
cy.contains('tr', 'USB Device Plugged').within(() => {
@@ -251,6 +243,9 @@ describe('Detectors', () => {
// Save changes
cy.get(`[data-test-subj="save-detector-rules-edits"]`).click({ force: true });
+ cy.waitForPageLoad('detector-details', {
+ contains: detectorName,
+ });
// Confirm 1 rule has been added to detector
cy.contains('Active rules (1)');
@@ -261,7 +256,9 @@ describe('Detectors', () => {
cy.contains('test detector_edited').click({ force: true });
// Confirm page
- cy.contains('Detector details');
+ cy.waitForPageLoad('detector-details', {
+ contains: 'Detector details',
+ });
// Click "Actions" button, the click "Delete"
cy.contains('Actions').click({ force: true });
@@ -270,4 +267,6 @@ describe('Detectors', () => {
// Confirm detector is deleted
cy.contains('There are no existing detectors');
});
+
+ after(() => cy.cleanUpTests());
});
diff --git a/cypress/integration/2_rules.spec.js b/cypress/integration/2_rules.spec.js
index 36fef1ecb..05bf9c77f 100644
--- a/cypress/integration/2_rules.spec.js
+++ b/cypress/integration/2_rules.spec.js
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { PLUGIN_NAME, TWENTY_SECONDS_TIMEOUT } from '../support/constants';
+import { OPENSEARCH_DASHBOARDS_URL } from '../support/constants';
const uniqueId = Cypress._.random(0, 1e6);
const SAMPLE_RULE = {
@@ -49,212 +49,189 @@ const YAML_RULE_LINES = [
];
describe('Rules', () => {
- before(() => {
- // Deleting pre-existing test rules
- cy.deleteRule(SAMPLE_RULE.name);
- });
+ before(() => cy.cleanUpTests());
beforeEach(() => {
// Visit Rules page
- cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/rules`);
+ cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/rules`);
+
+ // Check that correct page is showing
+ cy.waitForPageLoad('rules', {
+ contains: 'Rules',
+ });
});
- describe('Can be created', () => {
- it('manually using UI', () => {
- // Click "create new rule" button
- cy.get('[data-test-subj="create_rule_button"]', TWENTY_SECONDS_TIMEOUT).click({
- force: true,
- });
+ it('...can be created', () => {
+ // Click "create new rule" button
+ cy.get('[data-test-subj="create_rule_button"]').click({
+ force: true,
+ });
- // Enter the name
- cy.get('[data-test-subj="rule_name_field"]', TWENTY_SECONDS_TIMEOUT).type(SAMPLE_RULE.name);
-
- // Enter the log type
- cy.get('[data-test-subj="rule_type_dropdown"]', TWENTY_SECONDS_TIMEOUT).select(
- SAMPLE_RULE.logType
- );
-
- // Enter the description
- cy.get('[data-test-subj="rule_description_field"]', TWENTY_SECONDS_TIMEOUT).type(
- SAMPLE_RULE.description
- );
-
- // Enter the detection
- cy.get('[data-test-subj="rule_detection_field"]', TWENTY_SECONDS_TIMEOUT).type(
- SAMPLE_RULE.detection
- );
-
- // Enter the severity
- cy.get('[data-test-subj="rule_severity_dropdown"]', TWENTY_SECONDS_TIMEOUT).select(
- SAMPLE_RULE.severity
- );
-
- // Enter the tags
- SAMPLE_RULE.tags.forEach((tag) =>
- cy
- .get('[data-test-subj="rule_tags_dropdown"]', TWENTY_SECONDS_TIMEOUT)
- .type(`${tag}{enter}{esc}`)
- );
-
- // Enter the reference
- cy.get('[data-test-subj="rule_references_field_0"]', TWENTY_SECONDS_TIMEOUT).type(
- SAMPLE_RULE.references
- );
-
- // Enter the false positive cases
- cy.get('[data-test-subj="rule_false_positive_cases_field_0"]', TWENTY_SECONDS_TIMEOUT).type(
- SAMPLE_RULE.falsePositive
- );
-
- // Enter the author
- cy.get('[data-test-subj="rule_author_field"]', TWENTY_SECONDS_TIMEOUT).type(
- SAMPLE_RULE.author
- );
-
- // Enter the log type
- cy.get('[data-test-subj="rule_status_dropdown"]', TWENTY_SECONDS_TIMEOUT).select(
- SAMPLE_RULE.status
- );
-
- // Switch to YAML editor
- cy.get(
- '[data-test-subj="change-editor-type"] label:nth-child(2)',
- TWENTY_SECONDS_TIMEOUT
- ).click({
- force: true,
- });
+ // Enter the name
+ cy.get('[data-test-subj="rule_name_field"]').type(SAMPLE_RULE.name);
- YAML_RULE_LINES.forEach((line) =>
- cy
- .get('[data-test-subj="rule_yaml_editor"]', TWENTY_SECONDS_TIMEOUT)
- .contains(line, TWENTY_SECONDS_TIMEOUT)
- );
+ // Enter the log type
+ cy.get('[data-test-subj="rule_type_dropdown"]').select(SAMPLE_RULE.logType);
- // Click "create" button
- cy.get('[data-test-subj="create_rule_button"]', TWENTY_SECONDS_TIMEOUT).click({
- force: true,
- });
+ // Enter the description
+ cy.get('[data-test-subj="rule_description_field"]').type(SAMPLE_RULE.description);
- // Wait for the page to finish loading
- cy.wait(5000);
- cy.contains('No items found', TWENTY_SECONDS_TIMEOUT).should('not.exist');
-
- // Search for the rule
- cy.get(`input[type="search"]`, TWENTY_SECONDS_TIMEOUT).type(`${SAMPLE_RULE.name}{enter}`);
-
- // Click the rule link to open the details flyout
- cy.get(`[data-test-subj="rule_link_${SAMPLE_RULE.name}"]`, TWENTY_SECONDS_TIMEOUT).click();
-
- // Confirm the flyout contains the expected values
- cy.get(`[data-test-subj="rule_flyout_${SAMPLE_RULE.name}"]`, TWENTY_SECONDS_TIMEOUT)
- .click({ force: true })
- .within(() => {
- // Validate name
- cy.get('[data-test-subj="rule_flyout_rule_name"]', TWENTY_SECONDS_TIMEOUT).contains(
- SAMPLE_RULE.name,
- TWENTY_SECONDS_TIMEOUT
- );
-
- // Validate log type
- cy.get('[data-test-subj="rule_flyout_rule_log_type"]', TWENTY_SECONDS_TIMEOUT).contains(
- SAMPLE_RULE.logType,
- TWENTY_SECONDS_TIMEOUT
- );
-
- // Validate description
- cy.get(
- '[data-test-subj="rule_flyout_rule_description"]',
- TWENTY_SECONDS_TIMEOUT
- ).contains(SAMPLE_RULE.description, TWENTY_SECONDS_TIMEOUT);
-
- // Validate author
- cy.get('[data-test-subj="rule_flyout_rule_author"]', TWENTY_SECONDS_TIMEOUT).contains(
- SAMPLE_RULE.author,
- TWENTY_SECONDS_TIMEOUT
- );
-
- // Validate source is "custom"
- cy.get('[data-test-subj="rule_flyout_rule_source"]', TWENTY_SECONDS_TIMEOUT).contains(
- 'Custom',
- TWENTY_SECONDS_TIMEOUT
- );
-
- // Validate severity
- cy.get('[data-test-subj="rule_flyout_rule_severity"]', TWENTY_SECONDS_TIMEOUT).contains(
- SAMPLE_RULE.severity,
- TWENTY_SECONDS_TIMEOUT
- );
-
- // Validate tags
- SAMPLE_RULE.tags.forEach((tag) =>
- cy
- .get('[data-test-subj="rule_flyout_rule_tags"]', TWENTY_SECONDS_TIMEOUT)
- .contains(tag, TWENTY_SECONDS_TIMEOUT)
- );
-
- // Validate references
- cy.get('[data-test-subj="rule_flyout_rule_references"]', TWENTY_SECONDS_TIMEOUT).contains(
- SAMPLE_RULE.references,
- TWENTY_SECONDS_TIMEOUT
- );
-
- // Validate false positives
- cy.get(
- '[data-test-subj="rule_flyout_rule_false_positives"]',
- TWENTY_SECONDS_TIMEOUT
- ).contains(SAMPLE_RULE.falsePositive, TWENTY_SECONDS_TIMEOUT);
-
- // Validate status
- cy.get('[data-test-subj="rule_flyout_rule_status"]', TWENTY_SECONDS_TIMEOUT).contains(
- SAMPLE_RULE.status,
- TWENTY_SECONDS_TIMEOUT
- );
-
- // Validate detection
- SAMPLE_RULE.detectionLine.forEach((line) =>
- cy
- .get('[data-test-subj="rule_flyout_rule_detection"]', TWENTY_SECONDS_TIMEOUT)
- .contains(line, TWENTY_SECONDS_TIMEOUT)
- );
-
- cy.get(
- '[data-test-subj="change-editor-type"] label:nth-child(2)',
- TWENTY_SECONDS_TIMEOUT
- ).click({
- force: true,
+ // Enter the detection
+ cy.get('[data-test-subj="rule_detection_field"]').type(SAMPLE_RULE.detection);
+
+ // Enter the severity
+ cy.get('[data-test-subj="rule_severity_dropdown"]').select(SAMPLE_RULE.severity);
+
+ // Enter the tags
+ SAMPLE_RULE.tags.forEach((tag) =>
+ cy.get('[data-test-subj="rule_tags_dropdown"]').type(`${tag}{enter}{esc}`)
+ );
+
+ // Enter the reference
+ cy.get('[data-test-subj="rule_references_field_0"]').type(SAMPLE_RULE.references);
+
+ // Enter the false positive cases
+ cy.get('[data-test-subj="rule_false_positive_cases_field_0"]').type(SAMPLE_RULE.falsePositive);
+
+ // Enter the author
+ cy.get('[data-test-subj="rule_author_field"]').type(SAMPLE_RULE.author);
+
+ // Enter the log type
+ cy.get('[data-test-subj="rule_status_dropdown"]').select(SAMPLE_RULE.status);
+
+ // Switch to YAML editor
+ cy.get('[data-test-subj="change-editor-type"] label:nth-child(2)').click({
+ force: true,
+ });
+
+ YAML_RULE_LINES.forEach((line) => cy.get('[data-test-subj="rule_yaml_editor"]').contains(line));
+
+ cy.intercept({
+ url: '/rules',
+ }).as('getRules');
+
+ // Click "create" button
+ cy.get('[data-test-subj="create_rule_button"]').click({
+ force: true,
+ });
+
+ cy.waitForPageLoad('rules', {
+ contains: 'Rules',
+ });
+
+ cy.wait('@getRules');
+
+ // Search for the rule
+ cy.triggerSearchField('Search rules', SAMPLE_RULE.name);
+
+ // Click the rule link to open the details flyout
+ cy.get(`[data-test-subj="rule_link_${SAMPLE_RULE.name}"]`).click({ force: true });
+
+ // Confirm the flyout contains the expected values
+ cy.get(`[data-test-subj="rule_flyout_${SAMPLE_RULE.name}"]`)
+ .click({ force: true })
+ .within(() => {
+ // Validate name
+ cy.get('[data-test-subj="rule_flyout_rule_name"]').contains(SAMPLE_RULE.name);
+
+ // Validate log type
+ cy.get('[data-test-subj="rule_flyout_rule_log_type"]').contains(SAMPLE_RULE.logType);
+
+ // Validate description
+ cy.get('[data-test-subj="rule_flyout_rule_description"]').contains(SAMPLE_RULE.description);
+
+ // Validate author
+ cy.get('[data-test-subj="rule_flyout_rule_author"]').contains(SAMPLE_RULE.author);
+
+ // Validate source is "custom"
+ cy.get('[data-test-subj="rule_flyout_rule_source"]').contains('Custom');
+
+ // Validate severity
+ cy.get('[data-test-subj="rule_flyout_rule_severity"]').contains(SAMPLE_RULE.severity);
+
+ // Validate tags
+ SAMPLE_RULE.tags.forEach((tag) =>
+ cy.get('[data-test-subj="rule_flyout_rule_tags"]').contains(tag)
+ );
+
+ // Validate references
+ cy.get('[data-test-subj="rule_flyout_rule_references"]').contains(SAMPLE_RULE.references);
+
+ // Validate false positives
+ cy.get('[data-test-subj="rule_flyout_rule_false_positives"]').contains(
+ SAMPLE_RULE.falsePositive
+ );
+
+ // Validate status
+ cy.get('[data-test-subj="rule_flyout_rule_status"]').contains(SAMPLE_RULE.status);
+
+ // Validate detection
+ SAMPLE_RULE.detectionLine.forEach((line) =>
+ cy.get('[data-test-subj="rule_flyout_rule_detection"]').contains(line)
+ );
+
+ cy.get('[data-test-subj="change-editor-type"] label:nth-child(2)').click({
+ force: true,
+ });
+
+ cy.get('[data-test-subj="rule_flyout_yaml_rule"]')
+ .get('[class="euiCodeBlock__line"]')
+ .each((lineElement, lineIndex) => {
+ if (lineIndex >= YAML_RULE_LINES.length) {
+ return;
+ }
+ let line = lineElement.text().replaceAll('\n', '').trim();
+ let expectedLine = YAML_RULE_LINES[lineIndex];
+
+ // The document ID field is generated when the document is added to the index,
+ // so this test just checks that the line starts with the ID key.
+ if (expectedLine.startsWith('id:')) {
+ expectedLine = 'id:';
+ expect(line, `Sigma rule line ${lineIndex}`).to.contain(expectedLine);
+ } else {
+ expect(line, `Sigma rule line ${lineIndex}`).to.equal(expectedLine);
+ }
});
- cy.get('[data-test-subj="rule_flyout_yaml_rule"]')
- .get('[class="euiCodeBlock__line"]')
- .each((lineElement, lineIndex) => {
- if (lineIndex >= YAML_RULE_LINES.length) {
- return;
- }
- let line = lineElement.text().replaceAll('\n', '').trim();
- let expectedLine = YAML_RULE_LINES[lineIndex];
-
- // The document ID field is generated when the document is added to the index,
- // so this test just checks that the line starts with the ID key.
- if (expectedLine.startsWith('id:')) {
- expectedLine = 'id:';
- expect(line, `Sigma rule line ${lineIndex}`).to.contain(expectedLine);
- } else {
- expect(line, `Sigma rule line ${lineIndex}`).to.equal(expectedLine);
- }
- });
-
- // Close the flyout
- cy.get('[data-test-subj="close-rule-details-flyout"]', TWENTY_SECONDS_TIMEOUT).click({
+ // Close the flyout
+ cy.get('[data-test-subj="close-rule-details-flyout"]').click({
+ force: true,
+ });
+ });
+ });
+
+ it('...can be deleted', () => {
+ cy.intercept({
+ url: '/rules',
+ }).as('deleteRule');
+
+ cy.triggerSearchField('Search rules', SAMPLE_RULE.name);
+
+ // Click the rule link to open the details flyout
+ cy.get(`[data-test-subj="rule_link_${SAMPLE_RULE.name}"]`).click({ force: true });
+
+ cy.get('.euiButton')
+ .contains('Action')
+ .click({ force: true })
+ .then(() => {
+ // Confirm arrival at detectors page
+ cy.get(
+ '.euiFlexGroup > :nth-child(3) > .euiButtonEmpty > .euiButtonContent > .euiButtonEmpty__text'
+ )
+ .click({
force: true,
+ })
+ .then(() => {
+ cy.get('.euiButton').contains('Delete').click();
});
- });
- // Confirm flyout closed
- cy.contains(`[data-test-subj="rule_flyout_${SAMPLE_RULE.name}"]`).should('not.exist');
- });
- });
+ cy.wait('@deleteRule');
- after(() => {
- // Deleting test rules
- cy.deleteRule(SAMPLE_RULE.name);
+ // Search for sample_detector, presumably deleted
+ cy.triggerSearchField('Search rules', SAMPLE_RULE.name);
+ // Click the rule link to open the details flyout
+ cy.get('tbody').contains(SAMPLE_RULE.name).should('not.exist');
+ });
});
+
+ after(() => cy.cleanUpTests());
});
diff --git a/cypress/integration/3_alerts.spec.js b/cypress/integration/3_alerts.spec.js
index 71ed9a628..332328c82 100644
--- a/cypress/integration/3_alerts.spec.js
+++ b/cypress/integration/3_alerts.spec.js
@@ -4,12 +4,7 @@
*/
import moment from 'moment';
-import {
- PLUGIN_NAME,
- NINETY_SECONDS,
- TWENTY_SECONDS_TIMEOUT,
- FEATURE_SYSTEM_INDICES,
-} from '../support/constants';
+import { DETECTOR_TRIGGER_TIMEOUT, OPENSEARCH_DASHBOARDS_URL } from '../support/constants';
import sample_index_settings from '../fixtures/sample_index_settings.json';
import sample_alias_mappings from '../fixtures/sample_alias_mappings.json';
import sample_detector from '../fixtures/sample_detector.json';
@@ -44,20 +39,11 @@ const testDetector = {
// but all of the alert time fields should all contain the date in this format.
const date = moment(moment.now()).format('MM/DD/YY');
+const docCount = 4;
describe('Alerts', () => {
before(() => {
// Delete any pre-existing test detectors
- cy.deleteDetector(testDetectorName)
-
- // Delete any pre-existing test indices
- .then(() => cy.deleteIndex(testIndex))
-
- // Delete any pre-existing windows alerts and findings
- .then(() => {
- cy.deleteIndex(FEATURE_SYSTEM_INDICES.WINDOWS_ALERTS_INDEX);
- cy.deleteIndex(FEATURE_SYSTEM_INDICES.WINDOWS_FINDINGS_INDEX);
- })
-
+ cy.cleanUpTests()
// Create test index
.then(() => cy.createIndex(testIndex, sample_index_settings))
@@ -71,25 +57,42 @@ describe('Alerts', () => {
.then(() => {
// Go to the detectors table page
- cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/detectors`);
+ cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/detectors`);
+
+ // Check that correct page is showing
+ cy.waitForPageLoad('detectors', {
+ contains: 'Threat detectors',
+ });
// Filter table to only show the test detector
- cy.get(`input[type="search"]`, TWENTY_SECONDS_TIMEOUT).type(`${testDetector.name}{enter}`);
+ cy.get(`input[type="search"]`).type(`${testDetector.name}{enter}`);
// Confirm detector was created
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT).should(($tr) => {
- expect($tr, '1 row').to.have.length(1);
+ cy.get('tbody > tr').should(($tr) => {
expect($tr, 'detector name').to.contain(testDetector.name);
});
});
+
+ // Ingest documents to the test index
+ for (let i = 0; i < docCount; i++) {
+ cy.insertDocumentToIndex(testIndex, '', sample_document);
+ }
+
+ // Wait for the detector to execute
+ cy.wait(DETECTOR_TRIGGER_TIMEOUT);
});
beforeEach(() => {
// Visit Alerts table page
- cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/alerts`);
+ cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/alerts`);
+
+ // Wait for page to load
+ cy.waitForPageLoad('alerts', {
+ contains: 'Security alerts',
+ });
// Filter table to only show alerts for the test detector
- cy.get(`input[type="search"]`, TWENTY_SECONDS_TIMEOUT).type(`${testDetector.name}{enter}`);
+ cy.get(`input[type="search"]`).type(`${testDetector.name}{enter}`);
// Adjust the date range picker to display alerts from today
cy.get('[class="euiButtonEmpty__text euiQuickSelectPopover__buttonText"]').click({
@@ -99,37 +102,28 @@ describe('Alerts', () => {
});
it('are generated', () => {
- // Ingest documents to the test index
- const docCount = 4;
- for (let i = 0; i < docCount; i++) {
- cy.insertDocumentToIndex(testIndex, '', sample_document);
- }
-
- // Wait for the detector to execute
- cy.wait(NINETY_SECONDS);
-
// Refresh the table
cy.get('[data-test-subj="superDatePickerApplyTimeButton"]').click({ force: true });
- // Confirm the table contains 1 row
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT).should(($tr) =>
- expect($tr, `${docCount} rows`).to.have.length(docCount)
- );
+ // Confirm there are alerts created
+ cy.get('tbody > tr')
+ .filter(`:contains(${testDetectorAlertCondition})`)
+ .should('have.length', docCount);
});
it('contain expected values in table', () => {
- // Confirm each row contains the expected values
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT).each(($el, $index) => {
- expect($el, `row number ${$index} start time`).to.contain(date);
- expect($el, `row number ${$index} trigger name`).to.contain(testDetector.triggers[0].name);
- expect($el, `row number ${$index} detector name`).to.contain(testDetector.name);
- expect($el, `row number ${$index} status`).to.contain('Active');
- expect($el, `row number ${$index} severity`).to.contain('4 (Low)');
+ // Confirm there is a row containing the expected values
+ cy.get('tbody > tr').should(($tr) => {
+ expect($tr, 'start time').to.contain(date);
+ expect($tr, 'trigger name').to.contain(testDetector.triggers[0].name);
+ expect($tr, 'detector name').to.contain(testDetector.name);
+ expect($tr, 'status').to.contain('Active');
+ expect($tr, 'severity').to.contain('4 (Low)');
});
});
it('contain expected values in alert details flyout', () => {
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
+ cy.get('tbody > tr')
.first()
.within(() => {
// Click the "View details" button for the first alert
@@ -159,18 +153,16 @@ describe('Alerts', () => {
cy.get('[data-test-subj="text-details-group-content-detector"]').contains(testDetector.name);
// Wait for the findings table to finish loading
- cy.contains('Findings (1)', TWENTY_SECONDS_TIMEOUT);
- cy.contains('USB Device Plugged', TWENTY_SECONDS_TIMEOUT);
+ cy.contains('Findings (1)');
+ cy.contains('USB Device Plugged');
// Confirm alert findings contain expected values
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
- .should(($tr) => expect($tr, '1 row').to.have.length(1))
- .each(($el, $index) => {
- expect($el, `row number ${$index} timestamp`).to.contain(date);
- expect($el, `row number ${$index} rule name`).to.contain('USB Device Plugged');
- expect($el, `row number ${$index} detector name`).to.contain(testDetector.name);
- expect($el, `row number ${$index} log type`).to.contain('Windows');
- });
+ cy.get('tbody > tr').should(($tr) => {
+ expect($tr, `timestamp`).to.contain(date);
+ expect($tr, `rule name`).to.contain('USB Device Plugged');
+ expect($tr, `detector name`).to.contain(testDetector.name);
+ expect($tr, `log type`).to.contain('Windows');
+ });
// Close the flyout
cy.get('[data-test-subj="alert-details-flyout-close-button"]').click({ force: true });
@@ -182,22 +174,22 @@ describe('Alerts', () => {
it('contain expected values in finding details flyout', () => {
// Open first alert details flyout
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
+ cy.get('tbody > tr')
.first()
.within(() => {
// Click the "View details" button for the first alert
cy.get('[aria-label="View details"]').click({ force: true });
});
- cy.get('[data-test-subj="alert-details-flyout"]', TWENTY_SECONDS_TIMEOUT).within(() => {
+ cy.get('[data-test-subj="alert-details-flyout"]').within(() => {
// Wait for findings table to finish loading
- cy.contains('USB Device Plugged', TWENTY_SECONDS_TIMEOUT);
+ cy.contains('USB Device Plugged');
// Click the details button for the first finding
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
+ cy.get('tbody > tr')
.first()
.within(() => {
- cy.get('[data-test-subj="finding-details-flyout-button"]', TWENTY_SECONDS_TIMEOUT).click({
+ cy.get('[data-test-subj="finding-details-flyout-button"]').click({
force: true,
});
});
@@ -289,17 +281,14 @@ describe('Alerts', () => {
});
// Press the "back" button
- cy.get(
- '[data-test-subj="finding-details-flyout-back-button"]',
- TWENTY_SECONDS_TIMEOUT
- ).click({ force: true });
+ cy.get('[data-test-subj="finding-details-flyout-back-button"]').click({ force: true });
});
// Confirm finding details flyout closed
cy.get('[data-test-subj="finding-details-flyout"]').should('not.exist');
// Confirm the expected alert details flyout rendered
- cy.get('[data-test-subj="alert-details-flyout"]', TWENTY_SECONDS_TIMEOUT).within(() => {
+ cy.get('[data-test-subj="alert-details-flyout"]').within(() => {
cy.get('[data-test-subj="text-details-group-content-alert-trigger-name"]').contains(
testDetector.triggers[0].name
);
@@ -310,30 +299,23 @@ describe('Alerts', () => {
// Confirm the "Acknowledge" button is disabled when no alerts are selected
cy.get('[data-test-subj="acknowledge-button"]').should('be.disabled');
- // Confirm all 4 alerts are currently "Active"
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
- .should(($tr) => expect($tr, '4 rows').to.have.length(4))
- .each(($el, $index) => {
- expect($el, `row number ${$index} status`).to.contain('Active');
- });
+ // Confirm there is alert which is currently "Active"
+ cy.get('tbody > tr').should(($tr) => {
+ expect($tr, `status`).to.contain('Active');
+ });
// Click the checkboxes for the first and last alerts.
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
+ cy.get('tbody > tr')
.first()
.within(() => {
cy.get('[class="euiCheckbox__input"]').click({ force: true });
});
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
- .last()
- .within(() => {
- cy.get('[class="euiCheckbox__input"]').click({ force: true });
- });
// Press the "Acknowledge" button
cy.get('[data-test-subj="acknowledge-button"]').click({ force: true });
// Wait for acknowledge API to finish executing
- cy.contains('Acknowledged', TWENTY_SECONDS_TIMEOUT);
+ cy.contains('Acknowledged');
// Filter the table to show only "Acknowledged" alerts
cy.get('[data-text="Status"]').click({ force: true });
@@ -341,26 +323,23 @@ describe('Alerts', () => {
cy.contains('Acknowledged').click({ force: true });
});
- // Confirm there are now 2 "Acknowledged" alerts
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
- .should(($tr) => expect($tr, '2 rows').to.have.length(2))
- .each(($el, $index) => {
- expect($el, `row number ${$index} status`).to.contain('Acknowledged');
- });
+ // Confirm there is an "Acknowledged" alert
+ cy.get('tbody > tr').should(($tr) => {
+ expect($tr, `alert name`).to.contain(testDetectorAlertCondition);
+ expect($tr, `status`).to.contain('Acknowledged');
+ });
// Filter the table to show only "Active" alerts
cy.get('[data-text="Status"]');
cy.get('[class="euiFilterSelect__items"]').within(() => {
cy.contains('Acknowledged').click({ force: true });
- cy.contains('Active').click({ force: true });
});
// Confirm there are now 2 "Acknowledged" alerts
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
- .should(($tr) => expect($tr, '2 rows').to.have.length(2))
- .each(($el, $index) => {
- expect($el, `row number ${$index} status`).to.contain('Active');
- });
+ cy.get('tbody > tr')
+ .filter(`:contains(${testDetectorAlertCondition})`)
+ .should('contain', 'Active')
+ .should('contain', 'Acknowledged');
});
it('can be acknowledged via row button', () => {
@@ -370,19 +349,20 @@ describe('Alerts', () => {
cy.contains('Active').click({ force: true });
});
- // Confirm there are 2 "Active" alerts
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
- .should(($tr) => expect($tr, '2 rows').to.have.length(2))
+ cy.get('tbody > tr')
+ .filter(`:contains(${testDetectorAlertCondition})`)
+ .should('have.length', 3);
+
+ cy.get('tbody > tr')
// Click the "Acknowledge" icon button in the first row
.first()
.within(() => {
cy.get('[aria-label="Acknowledge"]').click({ force: true });
});
- // Confirm there is 1 "Active" alert
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT).should(($tr) =>
- expect($tr, '1 row').to.have.length(1)
- );
+ cy.get('tbody > tr')
+ .filter(`:contains(${testDetectorAlertCondition})`)
+ .should('have.length', 2);
// Filter the table to show only "Acknowledged" alerts
cy.get('[data-text="Status"]');
@@ -392,9 +372,9 @@ describe('Alerts', () => {
});
// Confirm there are now 3 "Acknowledged" alerts
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT).should(($tr) =>
- expect($tr, '3 rows').to.have.length(3)
- );
+ cy.get('tbody > tr')
+ .filter(`:contains(${testDetectorAlertCondition})`)
+ .should('have.length', 2);
});
it('can be acknowledged via flyout button', () => {
@@ -404,7 +384,7 @@ describe('Alerts', () => {
cy.contains('Active').click({ force: true });
});
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
+ cy.get('tbody > tr')
.first()
.within(() => {
// Click the "View details" button for the first alert
@@ -419,10 +399,7 @@ describe('Alerts', () => {
cy.get('[data-test-subj="alert-details-flyout-acknowledge-button"]').click({ force: true });
// Confirm the alert is now "Acknowledged"
- cy.get(
- '[data-test-subj="text-details-group-content-alert-status"]',
- TWENTY_SECONDS_TIMEOUT
- ).contains('Active');
+ cy.get('[data-test-subj="text-details-group-content-alert-status"]').contains('Active');
// Confirm the "Acknowledge" button is disabled
cy.get('[data-test-subj="alert-details-flyout-acknowledge-button"]').should('be.disabled');
@@ -431,22 +408,22 @@ describe('Alerts', () => {
it('detector name hyperlink on finding details flyout redirects to the detector details page', () => {
// Open first alert details flyout
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
+ cy.get('tbody > tr')
.first()
.within(() => {
// Click the "View details" button for the first alert
cy.get('[aria-label="View details"]').click({ force: true });
});
- cy.get('[data-test-subj="alert-details-flyout"]', TWENTY_SECONDS_TIMEOUT).within(() => {
+ cy.get('[data-test-subj="alert-details-flyout"]').within(() => {
// Wait for findings table to finish loading
- cy.contains('USB Device Plugged', TWENTY_SECONDS_TIMEOUT);
+ cy.contains('USB Device Plugged');
// Click the details button for the first finding
- cy.get('tbody > tr', TWENTY_SECONDS_TIMEOUT)
+ cy.get('tbody > tr')
.first()
.within(() => {
- cy.get('[data-test-subj="finding-details-flyout-button"]', TWENTY_SECONDS_TIMEOUT).click({
+ cy.get('[data-test-subj="finding-details-flyout-button"]').click({
force: true,
});
});
@@ -461,17 +438,8 @@ describe('Alerts', () => {
});
// Confirm the detector details page is for the expected detector
- cy.get('[data-test-subj="detector-details-detector-name"]', TWENTY_SECONDS_TIMEOUT).contains(
- testDetector.name,
- TWENTY_SECONDS_TIMEOUT
- );
+ cy.get('[data-test-subj="detector-details-detector-name"]').contains(testDetector.name);
});
- after(() => {
- // Clean up test resources
- cy.deleteDetector(testDetectorName);
- cy.deleteIndex(FEATURE_SYSTEM_INDICES.WINDOWS_ALERTS_INDEX);
- cy.deleteIndex(FEATURE_SYSTEM_INDICES.WINDOWS_FINDINGS_INDEX);
- cy.deleteIndex(testIndex);
- });
+ after(() => cy.cleanUpTests());
});
diff --git a/cypress/integration/4_findings.spec.js b/cypress/integration/4_findings.spec.js
index 6eedc94f9..cd8025433 100644
--- a/cypress/integration/4_findings.spec.js
+++ b/cypress/integration/4_findings.spec.js
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { PLUGIN_NAME, TWENTY_SECONDS_TIMEOUT } from '../support/constants';
+import { DETECTOR_TRIGGER_TIMEOUT, OPENSEARCH_DASHBOARDS_URL } from '../support/constants';
import sample_document from '../fixtures/sample_document.json';
import sample_index_settings from '../fixtures/sample_index_settings.json';
import sample_field_mappings from '../fixtures/sample_field_mappings.json';
@@ -14,30 +14,34 @@ describe('Findings', () => {
const indexName = 'cypress-test-windows';
before(() => {
- cy.deleteAllIndices();
+ cy.cleanUpTests();
// Visit Findings page
- cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/findings`);
+ cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/findings`);
// create test index, mappings, and detector
cy.createIndex(indexName, sample_index_settings);
cy.createAliasMappings(indexName, 'windows', sample_field_mappings, true);
cy.createDetector(sample_detector);
- });
-
- it('displays findings based on recently ingested data', () => {
- // Confirm arrival at Findings page
- cy.url({ timeout: 2000 }).should(
- 'include',
- 'opensearch_security_analytics_dashboards#/findings'
- );
// Ingest a new document
cy.ingestDocument(indexName, sample_document);
// wait for detector interval to pass
- cy.wait(60000);
+ cy.wait(DETECTOR_TRIGGER_TIMEOUT);
+ });
+
+ beforeEach(() => {
+ // Visit Alerts table page
+ cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/findings`);
+ // Wait for page to load
+ cy.waitForPageLoad('findings', {
+ contains: 'Findings',
+ });
+ });
+
+ it('displays findings based on recently ingested data', () => {
// Click refresh
cy.get('button').contains('Refresh').click({ force: true });
@@ -50,41 +54,45 @@ describe('Findings', () => {
cy.contains('Low');
});
- it('displays finding details flyout when user clicks on Finding ID or View details icon', () => {
+ it('displays finding details flyout when user clicks on View details icon', () => {
// filter table to show only sample_detector findings
- cy.get(`[placeholder="Search findings"]`).type('sample_detector').trigger('Search');
+ cy.triggerSearchField('Search findings', 'sample_detector');
- // Click findingId to trigger Finding details flyout
- cy.get(`[data-test-subj="finding-details-flyout-button"]`, { timeout: 2000 }).eq(0).click();
+ // Click View details icon
+ cy.getTableFirstRow('[data-test-subj="view-details-icon"]').then(($el) => {
+ cy.get($el).click({ force: true });
+ });
// Confirm flyout contents
cy.contains('Finding details');
cy.contains('Rule details');
// Close Flyout
- cy.get(`[data-test-subj="close-finding-details-flyout"]`).then(($el) => {
- cy.get($el).click({ force: true });
- });
+ cy.get('.euiFlexItem--flexGrowZero > .euiButtonIcon').click({ force: true });
+ });
- // wait for icon to become clickable - in this case, timeout insufficient.
- cy.wait(1000);
+ it('displays finding details flyout when user clicks on Finding ID', () => {
+ // filter table to show only sample_detector findings
+ cy.triggerSearchField('Search findings', 'sample_detector');
- // Click View details icon
- cy.get(`[data-test-subj="view-details-icon"]`).eq(0).click({ force: true });
+ // Click findingId to trigger Finding details flyout
+ cy.getTableFirstRow('[data-test-subj="finding-details-flyout-button"]').then(($el) => {
+ cy.get($el).click({ force: true });
+ });
// Confirm flyout contents
cy.contains('Finding details');
cy.contains('Rule details');
// Close Flyout
- cy.get(`[data-test-subj="close-finding-details-flyout"]`).then(($el) => {
- cy.get($el).click({ force: true });
- });
+ cy.get('.euiFlexItem--flexGrowZero > .euiButtonIcon').click({ force: true });
});
it('allows user to view details about rules that were triggered', () => {
+ // filter table to show only sample_detector findings
+ cy.triggerSearchField('Search findings', 'sample_detector');
+
// open Finding details flyout via finding id link. cy.wait essential, timeout insufficient.
- cy.wait(1000);
cy.get(`[data-test-subj="view-details-icon"]`).eq(0).click({ force: true });
// open rule details inside flyout
@@ -107,49 +115,64 @@ describe('Findings', () => {
// see github issue #124 at https://github.com/opensearch-project/security-analytics-dashboards-plugin/issues/124
it('opens rule details flyout when rule name inside accordion drop down is clicked', () => {
+ // filter table to show only sample_detector findings
+ cy.triggerSearchField('Search findings', 'sample_detector');
+
+ // open Finding details flyout via finding id link. cy.wait essential, timeout insufficient.
+ cy.getTableFirstRow('[data-test-subj="view-details-icon"]').then(($el) => {
+ cy.get($el).click({ force: true });
+ });
+
// Click rule link
cy.get(`[data-test-subj="finding-details-flyout-USB Device Plugged-details"]`).click({
force: true,
});
// Validate flyout appearance
- cy.get('[data-test-subj="rule_flyout_USB Device Plugged"]', TWENTY_SECONDS_TIMEOUT).within(
- () => {
- cy.get('[data-test-subj="rule_flyout_rule_name"]', TWENTY_SECONDS_TIMEOUT).contains(
- 'USB Device Plugged',
- TWENTY_SECONDS_TIMEOUT
- );
- }
- );
+ cy.get('[data-test-subj="rule_flyout_USB Device Plugged"]').within(() => {
+ cy.get('[data-test-subj="rule_flyout_rule_name"]').contains('USB Device Plugged');
+ });
});
- after(() => {
+ it('...can delete detector', () => {
// Visit Detectors page
- cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/detectors`, {
- timeout: 5000,
+ cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/detectors`);
+ cy.waitForPageLoad('detectors', {
+ contains: 'Threat detectors',
});
- // Confirm arrival at detectors page
- cy.url().should('include', 'opensearch_security_analytics_dashboards#/detectors');
-
- // Click on detector to be removed
- cy.contains('sample_detector').click({ force: true }, { timeout: 2000 });
-
- cy.url().should('include', 'opensearch_security_analytics_dashboards#/detector-details');
+ // filter table to show only sample_detector findings
+ cy.triggerSearchField('Search threat detectors', 'sample_detector');
- // Click "Actions" button, the click "Delete"
- cy.get('button')
- .contains('Actions')
- .click({ force: true }, { timeout: 2000 })
- .then(() => {
- // Confirm arrival at detectors page
- cy.contains('Delete').click({ force: true });
+ // intercept detectors and rules requests
+ cy.intercept('detectors/_search').as('getDetector');
+ cy.intercept('rules/_search?prePackaged=true').as('getPrePackagedRules');
+ cy.intercept('rules/_search?prePackaged=false').as('getRules');
- // Search for sample_detector, presumably deleted
- cy.get(`[placeholder="Search threat detectors"]`).type('sample_detector').trigger('search');
+ // Click on detector to be removed
+ cy.contains('sample_detector').click({ force: true });
+ cy.waitForPageLoad('detector-details', {
+ contains: sample_detector.name,
+ });
- // Confirm sample_detector no longer exists
- cy.contains('There are no existing detectors.');
- });
+ // wait for detector details to load before continuing
+ cy.wait(['@getDetector', '@getPrePackagedRules', '@getRules']).then(() => {
+ // Click "Actions" button, the click "Delete"
+ cy.get('button.euiButton')
+ .contains('Actions')
+ .click({ force: true })
+ .then(() => {
+ // Confirm arrival at detectors page
+ cy.get('[data-test-subj="editButton"]').contains('Delete').click({ force: true });
+
+ // Search for sample_detector, presumably deleted
+ cy.triggerSearchField('Search threat detectors', 'sample_detector');
+
+ // Confirm sample_detector no longer exists
+ cy.contains('There are no existing detectors.');
+ });
+ });
});
+
+ after(() => cy.cleanUpTests());
});
diff --git a/cypress/integration/5_integrations.spec.js b/cypress/integration/5_integrations.spec.js
index ee82d4ab9..a38ea3ecb 100644
--- a/cypress/integration/5_integrations.spec.js
+++ b/cypress/integration/5_integrations.spec.js
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { PLUGIN_NAME } from '../support/constants';
+import { DETECTOR_TRIGGER_TIMEOUT, OPENSEARCH_DASHBOARDS_URL } from '../support/constants';
import sample_index_settings from '../fixtures/sample_index_settings.json';
import sample_dns_settings from '../fixtures/integration_tests/index/create_dns_settings.json';
import windows_usb_rule_data from '../fixtures/integration_tests/rule/create_windows_usb_rule.json';
@@ -19,14 +19,8 @@ describe('Integration tests', () => {
const indexName = 'cypress-index-windows';
const dnsName = 'cypress-index-dns';
- const cleanUpTests = () => {
- cy.deleteAllCustomRules();
- cy.deleteAllDetectors();
- cy.deleteAllIndices();
- };
-
before(() => {
- cleanUpTests();
+ cy.cleanUpTests();
// Create custom rules
cy.createRule(windows_usb_rule_data).then((response) => {
@@ -66,25 +60,17 @@ describe('Integration tests', () => {
cy.request('POST', `${Cypress.env('opensearch')}/${dnsName}/_doc/101`, add_dns_index_data);
// Wait for detector interval to pass
- cy.wait(60000);
+ cy.wait(DETECTOR_TRIGGER_TIMEOUT);
});
beforeEach(() => {
// Visit Detectors page
- cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/detectors`);
+ cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/detectors`);
// Wait for page to load
- cy.wait(7000);
-
- // Check that correct page is showing
- cy.url().should(
- 'eq',
- 'http://localhost:5601/app/opensearch_security_analytics_dashboards#/detectors'
- );
+ cy.waitForPageLoad('detectors', 'Threat detectors');
});
- after(() => cleanUpTests());
-
it('...can navigate to findings page', () => {
cy.intercept({
method: 'GET',
@@ -151,4 +137,6 @@ describe('Integration tests', () => {
});
});
});
+
+ after(() => cy.cleanUpTests());
});
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 011ce6aae..cb5c8824d 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -3,8 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-const { NODE_API } = require('./constants');
-
+const { NODE_API, OPENSEARCH_DASHBOARDS, OPENSEARCH_DASHBOARDS_URL } = require('./constants');
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
@@ -84,6 +83,33 @@ Cypress.Commands.overwrite('request', (originalFn, ...args) => {
return originalFn(Object.assign({}, defaults, options));
});
+Cypress.Commands.add('cleanUpTests', () => {
+ cy.deleteAllCustomRules();
+ cy.deleteAllDetectors();
+ cy.deleteAllIndices();
+});
+
+Cypress.Commands.add('getTableFirstRow', (selector) => {
+ if (!selector) return cy.get('tbody > tr').first();
+ return cy.get('tbody > tr:first').find(selector);
+});
+
+Cypress.Commands.add('triggerSearchField', (placeholder, text) => {
+ cy.get(`[placeholder="${placeholder}"]`).type(`{selectall}${text}`).trigger('search');
+});
+
+Cypress.Commands.add('waitForPageLoad', (url, { timeout = 10000, contains = null }) => {
+ const fullUrl = `${OPENSEARCH_DASHBOARDS_URL}/${url}`;
+ Cypress.log({
+ message: `Wait for url: ${fullUrl} to be loaded.`,
+ });
+ cy.url({ timeout: timeout })
+ .should('include', fullUrl)
+ .then(() => {
+ contains && cy.contains(contains);
+ });
+});
+
Cypress.Commands.add('deleteAllIndices', () => {
cy.request({
method: 'DELETE',
@@ -170,9 +196,7 @@ Cypress.Commands.add(
Cypress.Commands.add('createRule', (ruleJSON) => {
return cy.request({
method: 'POST',
- url: `${Cypress.env('opensearch_dashboards')}${NODE_API.RULES_BASE}?category=${
- ruleJSON.category
- }`,
+ url: `${OPENSEARCH_DASHBOARDS}${NODE_API.RULES_BASE}?category=${ruleJSON.category}`,
body: JSON.stringify(ruleJSON),
});
});
diff --git a/cypress/support/constants.js b/cypress/support/constants.js
index dd8156067..a5ca5beb4 100644
--- a/cypress/support/constants.js
+++ b/cypress/support/constants.js
@@ -5,9 +5,10 @@
import { API } from '../../server/utils/constants';
-export const NINETY_SECONDS = 90000;
export const TWENTY_SECONDS_TIMEOUT = { timeout: 20000 };
+export const DETECTOR_TRIGGER_TIMEOUT = 65000;
+
export const FEATURE_SYSTEM_INDICES = {
DETECTORS_INDEX: '.opensearch-detectors-config',
DETECTOR_QUERIES_INDEX: '.opensearch-sap-windows-detectors-queries',
@@ -23,3 +24,6 @@ export const NODE_API = {
...API,
INDEX_TEMPLATE_BASE: '/_index_template',
};
+
+export const { opensearch_dashboards: OPENSEARCH_DASHBOARDS } = Cypress.env();
+export const OPENSEARCH_DASHBOARDS_URL = `${OPENSEARCH_DASHBOARDS}/app/${PLUGIN_NAME}#`;
diff --git a/cypress/support/index.d.ts b/cypress/support/index.d.ts
index 5dc4c2a8d..2af6cf0b6 100644
--- a/cypress/support/index.d.ts
+++ b/cypress/support/index.d.ts
@@ -3,10 +3,20 @@
* SPDX-License-Identifier: Apache-2.0
*/
-///
+///
declare namespace Cypress {
interface Chainable {
+ /**
+ * Wait for page to be loaded
+ * @param {string} url
+ * @param {number} timeout
+ * @example
+ * cy.waitForPageLoad('detectors')
+ * cy.waitForPageLoad('detectors', 20000)
+ */
+ waitForPageLoad(url: string, timeout?: number): Chainable;
+
/**
* Deletes all indices in cluster
* @example
@@ -14,6 +24,33 @@ declare namespace Cypress {
*/
deleteAllIndices(): Chainable;
+ /**
+ * Removes custom rules, detectors and indices
+ * @example
+ * cy.cleanUpTests()
+ */
+ cleanUpTests(): Chainable;
+
+ /**
+ * Returns table first row
+ * Can find elements deeper in a row with selector
+ * @param {string} selector
+ * @example
+ * cy.getTableFirstRow()
+ * cy.getTableFirstRow('td')
+ */
+ getTableFirstRow(selector: string): Chainable;
+
+ /**
+ * Returns table first row
+ * Can find elements deeper in a row with selector
+ * @param {string} placeholder
+ * @param {string} text
+ * @example
+ * cy.triggerSearchField('Search rules', 'USB Detection Rule')
+ */
+ triggerSearchField(placeholder: string, text: string): Chainable;
+
/**
* Deletes all custom rules in cluster
* @example