Skip to content

Commit f534522

Browse files
amsiglanjovancvetkovic3006
authored andcommitted
* Feature/add role edit cypress tests (opensearch-project#377) * [FEATURE] Detector must have at least one alert set opensearch-project#288 Signed-off-by: Jovan Cvetkovic <[email protected]> * [FEATURE] Implement additional integration test cases opensearch-project#104 Signed-off-by: Jovan Cvetkovic <[email protected]> * [FEATURE] Implement additional integration test cases opensearch-project#104 Signed-off-by: Jovan Cvetkovic <[email protected]> * [FEATURE] Implement additional integration test cases opensearch-project#104 Signed-off-by: Jovan Cvetkovic <[email protected]> Signed-off-by: Jovan Cvetkovic <[email protected]> * Bugfix/interval field can be empty (opensearch-project#379) * [FEATURE] Detector must have at least one alert set opensearch-project#288 Signed-off-by: Jovan Cvetkovic <[email protected]> * [BUG] Create detector | Interval field can be empty opensearch-project#378 Signed-off-by: Jovan Cvetkovic <[email protected]> * fix PR failed tests Signed-off-by: Jovan Cvetkovic <[email protected]> * fix PR failed tests Signed-off-by: Jovan Cvetkovic <[email protected]> * unit tests Signed-off-by: Jovan Cvetkovic <[email protected]> * unit tests Signed-off-by: Jovan Cvetkovic <[email protected]> * testing github-action v5 Signed-off-by: Jovan Cvetkovic <[email protected]> * testing github-action v5 Signed-off-by: Jovan Cvetkovic <[email protected]> * testing github-action v5 Signed-off-by: Jovan Cvetkovic <[email protected]> --------- Signed-off-by: Jovan Cvetkovic <[email protected]> --------- Signed-off-by: Jovan Cvetkovic <[email protected]> Co-authored-by: Jovan Cvetkovic <[email protected]> Signed-off-by: AWSHurneyt <[email protected]>
1 parent c4168f5 commit f534522

File tree

11 files changed

+207
-84
lines changed

11 files changed

+207
-84
lines changed

.github/workflows/cypress-workflow.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,12 @@ jobs:
111111
with:
112112
path: ${{ matrix.cypress_cache_folder }}
113113
key: cypress-cache-v2-${{ runner.os }}-${{ hashFiles('**/package.json') }}
114+
restore-keys: |
115+
cypress-cache-v2-${{ runner.os }}-${{ hashFiles('**/package.json') }}
114116
115117
# for now just chrome, use matrix to do all browsers later
116118
- name: Cypress tests
117-
uses: cypress-io/github-action@v2
119+
uses: cypress-io/github-action@v5
118120
with:
119121
working-directory: OpenSearch-Dashboards/plugins/security-analytics-dashboards-plugin
120122
command: yarn run cypress run

cypress.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
{
22
"viewportHeight": 900,
33
"viewportWidth": 1440,
4-
"defaultCommandTimeout": 20000,
5-
"retries": 1,
4+
"defaultCommandTimeout": 30000,
5+
"requestTimeout": 300000,
6+
"responseTimeout": 300000,
7+
"baseUrl": "http://localhost:5601",
68
"env": {
79
"opensearch_url": "localhost:9200",
810
"opensearch_dashboards": "http://localhost:5601",

cypress/integration/1_detectors.spec.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,29 @@ describe('Detectors', () => {
3838
before(() => {
3939
cy.cleanUpTests();
4040
// Create test index
41-
cy.createIndex(indexName, sample_index_settings);
41+
cy.createIndex(indexName, sample_index_settings).then(() =>
42+
cy
43+
.request('POST', '_plugins/_security_analytics/rules/_search?prePackaged=true', {
44+
from: 0,
45+
size: 5000,
46+
query: {
47+
nested: {
48+
path: 'rule',
49+
query: { bool: { must: [{ match: { 'rule.category': 'windows' } }] } },
50+
},
51+
},
52+
})
53+
.should('have.property', 'status', 200)
54+
);
4255

4356
cy.contains(detectorName).should('not.exist');
4457
});
4558

4659
beforeEach(() => {
60+
cy.intercept('/detectors/_search').as('detectorsSearch');
4761
// Visit Detectors page
4862
cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/detectors`);
63+
cy.wait('@detectorsSearch').should('have.property', 'state', 'Complete');
4964

5065
// Check that correct page is showing
5166
cy.waitForPageLoad('detectors', {
@@ -85,6 +100,10 @@ describe('Detectors', () => {
85100
// Open Detection rules accordion
86101
cy.get('[data-test-subj="detection-rules-btn"]').click({ force: true, timeout: 5000 });
87102

103+
cy.contains('table tr', 'Windows', {
104+
timeout: 120000,
105+
});
106+
88107
// find search, type USB
89108
cy.get(`input[placeholder="Search..."]`).ospSearch('USB Device Plugged');
90109

cypress/integration/2_rules.spec.js

Lines changed: 137 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,93 @@ const YAML_RULE_LINES = [
4848
...SAMPLE_RULE.detection.replaceAll(' ', '').replaceAll('{backspace}', '').split('\n'),
4949
];
5050

51+
const checkRulesFlyout = () => {
52+
// Search for the rule
53+
cy.get(`input[placeholder="Search rules"]`).ospSearch(SAMPLE_RULE.name);
54+
55+
// Click the rule link to open the details flyout
56+
cy.get(`[data-test-subj="rule_link_${SAMPLE_RULE.name}"]`).click({ force: true });
57+
58+
// Confirm the flyout contains the expected values
59+
cy.get(`[data-test-subj="rule_flyout_${SAMPLE_RULE.name}"]`)
60+
.click({ force: true })
61+
.within(() => {
62+
// Validate name
63+
cy.get('[data-test-subj="rule_flyout_rule_name"]').contains(SAMPLE_RULE.name);
64+
65+
// Validate log type
66+
cy.get('[data-test-subj="rule_flyout_rule_log_type"]').contains(SAMPLE_RULE.logType);
67+
68+
// Validate description
69+
cy.get('[data-test-subj="rule_flyout_rule_description"]').contains(SAMPLE_RULE.description);
70+
71+
// Validate author
72+
cy.get('[data-test-subj="rule_flyout_rule_author"]').contains(SAMPLE_RULE.author);
73+
74+
// Validate source is "custom"
75+
cy.get('[data-test-subj="rule_flyout_rule_source"]').contains('Custom');
76+
77+
// Validate severity
78+
cy.get('[data-test-subj="rule_flyout_rule_severity"]').contains(SAMPLE_RULE.severity);
79+
80+
// Validate tags
81+
SAMPLE_RULE.tags.forEach((tag) =>
82+
cy.get('[data-test-subj="rule_flyout_rule_tags"]').contains(tag)
83+
);
84+
85+
// Validate references
86+
cy.get('[data-test-subj="rule_flyout_rule_references"]').contains(SAMPLE_RULE.references);
87+
88+
// Validate false positives
89+
cy.get('[data-test-subj="rule_flyout_rule_false_positives"]').contains(
90+
SAMPLE_RULE.falsePositive
91+
);
92+
93+
// Validate status
94+
cy.get('[data-test-subj="rule_flyout_rule_status"]').contains(SAMPLE_RULE.status);
95+
96+
// Validate detection
97+
SAMPLE_RULE.detectionLine.forEach((line) =>
98+
cy.get('[data-test-subj="rule_flyout_rule_detection"]').contains(line)
99+
);
100+
101+
cy.get('[data-test-subj="change-editor-type"] label:nth-child(2)').click({
102+
force: true,
103+
});
104+
105+
cy.get('[data-test-subj="rule_flyout_yaml_rule"]')
106+
.get('[class="euiCodeBlock__line"]')
107+
.each((lineElement, lineIndex) => {
108+
if (lineIndex >= YAML_RULE_LINES.length) {
109+
return;
110+
}
111+
let line = lineElement.text().replaceAll('\n', '').trim();
112+
let expectedLine = YAML_RULE_LINES[lineIndex];
113+
114+
// The document ID field is generated when the document is added to the index,
115+
// so this test just checks that the line starts with the ID key.
116+
if (expectedLine.startsWith('id:')) {
117+
expectedLine = 'id:';
118+
expect(line, `Sigma rule line ${lineIndex}`).to.contain(expectedLine);
119+
} else {
120+
expect(line, `Sigma rule line ${lineIndex}`).to.equal(expectedLine);
121+
}
122+
});
123+
124+
// Close the flyout
125+
cy.get('[data-test-subj="close-rule-details-flyout"]').click({
126+
force: true,
127+
});
128+
});
129+
};
130+
51131
describe('Rules', () => {
52132
before(() => cy.cleanUpTests());
53133
beforeEach(() => {
134+
cy.intercept('/rules/_search').as('rulesSearch');
54135
// Visit Rules page
55136
cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/rules`);
137+
cy.wait('@rulesSearch').should('have.property', 'state', 'Complete');
56138

57139
// Check that correct page is showing
58140
cy.waitForPageLoad('rules', {
@@ -114,89 +196,73 @@ describe('Rules', () => {
114196
force: true,
115197
});
116198

199+
cy.wait('@getRules');
200+
117201
cy.waitForPageLoad('rules', {
118202
contains: 'Rules',
119203
});
120204

121-
cy.wait('@getRules');
205+
checkRulesFlyout();
206+
});
122207

123-
// Search for the rule
124-
cy.get(`input[placeholder="Search rules"]`).ospSearch(SAMPLE_RULE.name);
208+
it('...can be edited', () => {
209+
cy.waitForPageLoad('rules', {
210+
contains: 'Rules',
211+
});
125212

126-
// Click the rule link to open the details flyout
213+
cy.get(`input[placeholder="Search rules"]`).ospSearch(SAMPLE_RULE.name);
127214
cy.get(`[data-test-subj="rule_link_${SAMPLE_RULE.name}"]`).click({ force: true });
128215

129-
// Confirm the flyout contains the expected values
130216
cy.get(`[data-test-subj="rule_flyout_${SAMPLE_RULE.name}"]`)
217+
.find('button')
218+
.contains('Action')
131219
.click({ force: true })
132-
.within(() => {
133-
// Validate name
134-
cy.get('[data-test-subj="rule_flyout_rule_name"]').contains(SAMPLE_RULE.name);
135-
136-
// Validate log type
137-
cy.get('[data-test-subj="rule_flyout_rule_log_type"]').contains(SAMPLE_RULE.logType);
138-
139-
// Validate description
140-
cy.get('[data-test-subj="rule_flyout_rule_description"]').contains(SAMPLE_RULE.description);
141-
142-
// Validate author
143-
cy.get('[data-test-subj="rule_flyout_rule_author"]').contains(SAMPLE_RULE.author);
220+
.then(() => {
221+
// Confirm arrival at detectors page
222+
cy.get('.euiPopover__panel').find('button').contains('Edit').click();
223+
});
144224

145-
// Validate source is "custom"
146-
cy.get('[data-test-subj="rule_flyout_rule_source"]').contains('Custom');
225+
const ruleNameSelector = '[data-test-subj="rule_name_field"]';
226+
cy.get(ruleNameSelector).clear();
147227

148-
// Validate severity
149-
cy.get('[data-test-subj="rule_flyout_rule_severity"]').contains(SAMPLE_RULE.severity);
228+
SAMPLE_RULE.name += ' edited';
229+
cy.get(ruleNameSelector).type(SAMPLE_RULE.name);
230+
cy.get(ruleNameSelector).should('have.value', SAMPLE_RULE.name);
150231

151-
// Validate tags
152-
SAMPLE_RULE.tags.forEach((tag) =>
153-
cy.get('[data-test-subj="rule_flyout_rule_tags"]').contains(tag)
154-
);
232+
// Enter the log type
233+
const logSelector = '[data-test-subj="rule_type_dropdown"]';
234+
cy.get(logSelector).within(() => cy.get('.euiFormControlLayoutClearButton').click());
235+
SAMPLE_RULE.logType = 'dns';
236+
YAML_RULE_LINES[2] = `product: ${SAMPLE_RULE.logType}`;
237+
YAML_RULE_LINES[3] = `title: ${SAMPLE_RULE.name}`;
238+
cy.get(logSelector).type(SAMPLE_RULE.logType).type('{enter}');
239+
cy.get(logSelector).contains(SAMPLE_RULE.logType, {
240+
matchCase: false,
241+
});
155242

156-
// Validate references
157-
cy.get('[data-test-subj="rule_flyout_rule_references"]').contains(SAMPLE_RULE.references);
243+
const ruleDescriptionSelector = '[data-test-subj="rule_description_field"]';
244+
SAMPLE_RULE.description += ' edited';
245+
YAML_RULE_LINES[4] = `description: ${SAMPLE_RULE.description}`;
246+
cy.get(ruleDescriptionSelector).clear();
247+
cy.get(ruleDescriptionSelector).type(SAMPLE_RULE.description);
248+
cy.get(ruleDescriptionSelector).should('have.value', SAMPLE_RULE.description);
158249

159-
// Validate false positives
160-
cy.get('[data-test-subj="rule_flyout_rule_false_positives"]').contains(
161-
SAMPLE_RULE.falsePositive
162-
);
250+
cy.intercept({
251+
url: '/rules',
252+
}).as('getRules');
163253

164-
// Validate status
165-
cy.get('[data-test-subj="rule_flyout_rule_status"]').contains(SAMPLE_RULE.status);
254+
// Click "create" button
255+
cy.get('[data-test-subj="submit_rule_form_button"]').click({
256+
force: true,
257+
});
166258

167-
// Validate detection
168-
SAMPLE_RULE.detectionLine.forEach((line) =>
169-
cy.get('[data-test-subj="rule_flyout_rule_detection"]').contains(line)
170-
);
259+
cy.waitForPageLoad('rules', {
260+
contains: 'Rules',
261+
});
171262

172-
cy.get('[data-test-subj="change-editor-type"] label:nth-child(2)').click({
173-
force: true,
174-
});
263+
cy.wait('@getRules');
175264

176-
cy.get('[data-test-subj="rule_flyout_yaml_rule"]')
177-
.get('[class="euiCodeBlock__line"]')
178-
.each((lineElement, lineIndex) => {
179-
if (lineIndex >= YAML_RULE_LINES.length) {
180-
return;
181-
}
182-
let line = lineElement.text().replaceAll('\n', '').trim();
183-
let expectedLine = YAML_RULE_LINES[lineIndex];
184-
185-
// The document ID field is generated when the document is added to the index,
186-
// so this test just checks that the line starts with the ID key.
187-
if (expectedLine.startsWith('id:')) {
188-
expectedLine = 'id:';
189-
expect(line, `Sigma rule line ${lineIndex}`).to.contain(expectedLine);
190-
} else {
191-
expect(line, `Sigma rule line ${lineIndex}`).to.equal(expectedLine);
192-
}
193-
});
194-
195-
// Close the flyout
196-
cy.get('[data-test-subj="close-rule-details-flyout"]').click({
197-
force: true,
198-
});
199-
});
265+
checkRulesFlyout();
200266
});
201267

202268
it('...can be deleted', () => {
@@ -209,20 +275,17 @@ describe('Rules', () => {
209275
// Click the rule link to open the details flyout
210276
cy.get(`[data-test-subj="rule_link_${SAMPLE_RULE.name}"]`).click({ force: true });
211277

212-
cy.get('.euiButton')
278+
cy.get(`[data-test-subj="rule_flyout_${SAMPLE_RULE.name}"]`)
279+
.find('button')
213280
.contains('Action')
214281
.click({ force: true })
215282
.then(() => {
216283
// Confirm arrival at detectors page
217-
cy.get(
218-
'.euiFlexGroup > :nth-child(3) > .euiButtonEmpty > .euiButtonContent > .euiButtonEmpty__text'
219-
)
220-
.click({
221-
force: true,
222-
})
223-
.then(() => {
224-
cy.get('.euiButton').contains('Delete').click();
225-
});
284+
cy.get('.euiPopover__panel')
285+
.find('button')
286+
.contains('Delete')
287+
.click()
288+
.then(() => cy.get('.euiModalFooter > .euiButton').contains('Delete').click());
226289

227290
cy.wait('@deleteRule');
228291

cypress/integration/3_alerts.spec.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ describe('Alerts', () => {
8484

8585
beforeEach(() => {
8686
// Visit Alerts table page
87+
cy.intercept('/detectors/_search').as('detectorsSearch');
88+
// Visit Detectors page
8789
cy.visit(`${OPENSEARCH_DASHBOARDS_URL}/alerts`);
90+
cy.wait('@detectorsSearch').should('have.property', 'state', 'Complete');
8891

8992
// Wait for page to load
9093
cy.waitForPageLoad('alerts', {

cypress/support/detectors.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,8 @@ Cypress.Commands.add('deleteAllDetectors', () => {
6868
method: 'DELETE',
6969
url: `${Cypress.env('opensearch')}/.opensearch-sap-detectors-config`,
7070
failOnStatusCode: false,
71+
}).as('deleteAllDetectors');
72+
cy.get('@deleteAllDetectors').should((response) => {
73+
expect(response.status).to.be.oneOf([200, 404]);
7174
});
7275
});

cypress/support/indexes.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
const { NODE_API } = require('./constants');
77

88
Cypress.Commands.add('createIndex', (index, settings = {}) => {
9-
cy.request('PUT', `${Cypress.env('opensearch')}/${index}`, settings);
9+
cy.request('PUT', `${Cypress.env('opensearch')}/${index}`, settings).should(
10+
'have.property',
11+
'status',
12+
200
13+
);
1014
});
1115

1216
Cypress.Commands.add('createIndexTemplate', (name, template) => {
@@ -43,5 +47,9 @@ Cypress.Commands.add('deleteAllIndices', () => {
4347
method: 'DELETE',
4448
url: `${Cypress.env('opensearch')}/index*,sample*,opensearch_dashboards*,test*,cypress*`,
4549
failOnStatusCode: false,
50+
}).as('deleteAllIndices');
51+
cy.get('@deleteAllIndices').should((response) => {
52+
// Both statuses are a pass, 200 means deleted successfully and 404 there was no index to delete
53+
expect(response.status).to.be.oneOf([200, 404]);
4654
});
4755
});

cypress/support/rules.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,14 @@ Cypress.Commands.add('deleteRule', (ruleName) => {
5151
});
5252

5353
Cypress.Commands.add('deleteAllCustomRules', () => {
54+
const url = `${Cypress.env('opensearch')}/.opensearch-sap-custom-rules-config`;
5455
cy.request({
5556
method: 'DELETE',
56-
url: `${Cypress.env('opensearch')}/.opensearch-sap-custom-rules-config`,
57+
url: url,
5758
failOnStatusCode: false,
5859
body: { query: { match_all: {} } },
60+
}).as('deleteAllCustomRules');
61+
cy.get('@deleteAllCustomRules').should((response) => {
62+
expect(response.status).to.be.oneOf([200, 404]);
5963
});
6064
});

cypress/support/typings.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Cypress.Commands.add(
99
prevSubject: true,
1010
},
1111
(subject, text) => {
12-
return cy.get(subject).ospType(text).realPress('Enter');
12+
return cy.get(subject).clear().ospType(text).realPress('Enter');
1313
}
1414
);
1515

0 commit comments

Comments
 (0)