[Threat Intelligence] Switch to parallel cypress (#158801)

This pull request introduces parallel functionality to the TI Cypress
tests, which was recently implemented in the Security Solution. In
essence, this allows us to run multiple test files simultaneously, each
within its own Kibana/ES environment. The PR is here
https://github.com/elastic/kibana/pull/157387

The changes in the scope of the TI Cypress e2es include:

- Modified `package.json` scripts; `:run` is now being used both locally
and in CI. Note that Firefox is currently not supported.
- Removed all `{testIsolation: false}` flags, as no test should depend
on non-pure state.
- All actions required to set up the test environment (archiver, login)
have been moved to beforeEach blocks to ensure that each test starts
with a fresh state.
- Introduced ESLint configuration to help us keep up with the best
practices when writing tests.

TODO:
- The `timeline.cy.ts` test is flaky and requires refactoring. There are
too many actions being fired against elements that are not visible to
the user (`{force: true}`). I've added some waits to address race
conditions in the UI, but this should be considered a temporary
solution.

---------

Co-authored-by: Patryk Kopyciński <contact@patrykkopycinski.com>
Co-authored-by: PhilippeOberti <philippe.oberti@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Konrad Szwarc 2023-06-07 15:58:26 +02:00 committed by GitHub
parent 3aa1b34394
commit 627b3547a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 398 additions and 660 deletions

View file

@ -43,7 +43,6 @@ disabled:
- x-pack/test/security_solution_cypress/config.ts
- x-pack/test/security_solution_cypress/response_ops_cli_config.ts
- x-pack/test/security_solution_cypress/upgrade_config.ts
- x-pack/test/threat_intelligence_cypress/visual_config.ts
- x-pack/test/threat_intelligence_cypress/cli_config_parallel.ts
- x-pack/test/threat_intelligence_cypress/config.ts
- x-pack/test/functional_enterprise_search/visual_config.ts

View file

@ -5,7 +5,7 @@ steps:
queue: n2-4-spot
depends_on: build
timeout_in_minutes: 120
parallelism: 4
parallelism: 2
retry:
automatic:
- exit_status: '-1'

View file

@ -5,12 +5,12 @@ set -euo pipefail
source .buildkite/scripts/steps/functional/common.sh
export JOB=kibana-threat-intelligence-chrome
export CLI_NUMBER=${CLI_NUMBER:-$((BUILDKITE_PARALLEL_JOB+1))}
export CLI_COUNT=${CLI_COUNT:-$BUILDKITE_PARALLEL_JOB_COUNT}
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
Xvfb :99 -screen 0 1600x1200x24 &
export DISPLAY=:99
echo "--- Threat Intelligence tests (Chrome)"
node scripts/functional_tests \
--debug --bail \
--kibana-install-dir "$KIBANA_BUILD_LOCATION" \
--config x-pack/test/threat_intelligence_cypress/cli_config_parallel.ts
yarn --cwd x-pack/plugins/threat_intelligence cypress:run

View file

@ -0,0 +1,13 @@
{
"plugins": ["cypress"],
"extends": [
"plugin:cypress/recommended"
],
"env": {
"cypress/globals": true
},
"rules": {
"cypress/no-force": "warn",
"import/no-extraneous-dependencies": "off"
}
}

View file

@ -29,7 +29,7 @@ export default defineCypressConfig({
execTimeout: 120000,
pageLoadTimeout: 120000,
retries: {
runMode: 2,
runMode: 1,
},
screenshotsFolder: '../../../target/kibana-threat-intelligence/cypress/screenshots',
trashAssetsBeforeRuns: false,
@ -44,5 +44,7 @@ export default defineCypressConfig({
},
e2e: {
baseUrl: 'http://localhost:5601',
experimentalMemoryManagement: true,
specPattern: './cypress/e2e/**/*.cy.ts',
},
});

View file

@ -7,20 +7,19 @@
import {
closeFlyout,
navigateToThreatIntelligence,
openFlyout,
openFlyoutTakeAction,
openIndicatorsTableMoreActions,
} from '../tasks/common';
import {
deleteBlocklistEntry,
fillBlocklistForm,
openAddToBlockListFlyoutFromTable,
openAddToBlocklistFromFlyout,
} from '../tasks/blocklist';
import { navigateToBlocklist, navigateToThreatIntelligence } from '../tasks/common';
import { login } from '../tasks/login';
import { navigateToBlocklist } from '../tasks/common';
import { login, visit } from '../tasks/login';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
import { selectRange } from '../tasks/select_range';
import {
BLOCK_LIST_VALUE_INPUT,
FLYOUT_ADD_TO_BLOCK_LIST_ITEM,
@ -31,88 +30,76 @@ import {
const THREAT_INTELLIGENCE = '/app/security/threat_intelligence/indicators';
const BLOCK_LIST_NEW_NAME = 'new blocklist entry';
const BLOCK_LIST_NEW_DESCRIPTION = 'the best description';
const FIRST_BLOCK_LIST_NEW_NAME = 'first blocklist entry';
const FIRST_BLOCK_LIST_NEW_DESCRIPTION = 'the first description';
const SECOND_BLOCK_LIST_NEW_NAME = 'second blocklist entry';
const SECOND_BLOCK_LIST_NEW_DESCRIPTION = 'the second description';
describe('Block list with invalid indicators', { testIsolation: false }, () => {
before(() => {
describe('Block list with invalid indicators', () => {
beforeEach(() => {
esArchiverLoad('threat_intelligence/invalid_indicators_data');
login();
cy.visit(THREAT_INTELLIGENCE);
selectRange();
visit(THREAT_INTELLIGENCE);
});
after(() => {
afterEach(() => {
esArchiverUnload('threat_intelligence/invalid_indicators_data');
});
it('should disabled the indicators table context menu item if invalid indicator', () => {
it('should disabled blocklist in the indicators table context menu item and flyout context menu items', () => {
openIndicatorsTableMoreActions(3);
cy.get(INDICATORS_TABLE_ADD_TO_BLOCK_LIST_BUTTON_ICON).should('be.disabled');
});
it('should disable the flyout context menu items if invalid indicator', () => {
openFlyout(3);
openFlyoutTakeAction();
cy.get(FLYOUT_ADD_TO_BLOCK_LIST_ITEM).should('be.disabled');
});
});
describe('Block list interactions', { testIsolation: false }, () => {
before(() => {
describe('Block list interactions', () => {
beforeEach(() => {
esArchiverLoad('threat_intelligence/indicators_data');
login();
cy.visit(THREAT_INTELLIGENCE);
selectRange();
visit(THREAT_INTELLIGENCE);
});
after(() => {
afterEach(() => {
esArchiverUnload('threat_intelligence/indicators_data');
});
it('should add to block list from the indicators table', () => {
openIndicatorsTableMoreActions(0);
openAddToBlockListFlyoutFromTable();
fillBlocklistForm(BLOCK_LIST_NEW_NAME, BLOCK_LIST_NEW_DESCRIPTION);
navigateToBlocklist();
cy.get(SAVED_BLOCK_LIST_NAME).should('have.text', BLOCK_LIST_NEW_NAME);
cy.get(SAVED_BLOCK_LIST_DESCRIPTION).should('have.text', BLOCK_LIST_NEW_DESCRIPTION);
deleteBlocklistEntry();
navigateToThreatIntelligence();
});
it('should add to block list from the indicator flyout', () => {
openFlyout(0);
openFlyoutTakeAction();
openAddToBlocklistFromFlyout();
fillBlocklistForm(BLOCK_LIST_NEW_NAME, BLOCK_LIST_NEW_DESCRIPTION);
closeFlyout();
navigateToBlocklist();
cy.get(SAVED_BLOCK_LIST_NAME).should('have.text', BLOCK_LIST_NEW_NAME);
cy.get(SAVED_BLOCK_LIST_DESCRIPTION).should('have.text', BLOCK_LIST_NEW_DESCRIPTION);
deleteBlocklistEntry();
navigateToThreatIntelligence();
});
it('add to blocklist flyout should have the correct IoC id', () => {
it('should add to block list from the indicators table and from flyout', () => {
// first indicator is a valid indicator for add to blocklist feature
const firstIndicatorId = 'd86e656455f985357df3063dff6637f7f3b95bb27d1769a6b88c7adecaf7763f';
openIndicatorsTableMoreActions(0);
openAddToBlockListFlyoutFromTable();
cy.get(BLOCK_LIST_VALUE_INPUT(firstIndicatorId)).should('exist');
cy.get(BLOCK_LIST_VALUE_INPUT(firstIndicatorId));
closeFlyout();
fillBlocklistForm(FIRST_BLOCK_LIST_NEW_NAME, FIRST_BLOCK_LIST_NEW_DESCRIPTION);
navigateToBlocklist();
cy.get(SAVED_BLOCK_LIST_NAME).eq(0).should('have.text', FIRST_BLOCK_LIST_NEW_NAME);
cy.get(SAVED_BLOCK_LIST_DESCRIPTION)
.eq(0)
.should('have.text', FIRST_BLOCK_LIST_NEW_DESCRIPTION);
navigateToThreatIntelligence();
// second indicator is a valid indicator for add to blocklist feature
const secondIndicatorId = 'd3e2cf87eabf84ef929aaf8dad1431b3387f5a26de8ffb7a0c3c2a13f973c0ab';
openIndicatorsTableMoreActions(1);
openAddToBlockListFlyoutFromTable();
openFlyout(1);
openFlyoutTakeAction();
openAddToBlocklistFromFlyout();
cy.get(BLOCK_LIST_VALUE_INPUT(secondIndicatorId)).should('exist');
cy.get(BLOCK_LIST_VALUE_INPUT(secondIndicatorId));
fillBlocklistForm(SECOND_BLOCK_LIST_NEW_NAME, SECOND_BLOCK_LIST_NEW_DESCRIPTION);
closeFlyout();
navigateToBlocklist();
cy.get(SAVED_BLOCK_LIST_NAME).eq(0).should('have.text', SECOND_BLOCK_LIST_NEW_NAME);
cy.get(SAVED_BLOCK_LIST_DESCRIPTION)
.eq(0)
.should('have.text', SECOND_BLOCK_LIST_NEW_DESCRIPTION);
});
});

View file

@ -6,16 +6,13 @@
*/
import {
navigateToCases,
navigateToThreatIntelligence,
openFlyout,
openFlyoutTakeAction,
openIndicatorsTableMoreActions,
} from '../tasks/common';
import {
createNewCaseFromCases,
createNewCaseFromTI,
deleteCase,
navigateToCaseViaToaster,
openAddToExistingCaseFlyoutFromTable,
openAddToExistingCaseFromFlyout,
@ -30,34 +27,29 @@ import {
INDICATORS_TABLE_ADD_TO_EXISTING_CASE_BUTTON_ICON,
INDICATORS_TABLE_ADD_TO_NEW_CASE_BUTTON_ICON,
} from '../screens/cases';
import { login } from '../tasks/login';
import { login, visit } from '../tasks/login';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
import { selectRange } from '../tasks/select_range';
const THREAT_INTELLIGENCE = '/app/security/threat_intelligence/indicators';
describe('Cases with invalid indicators', { testIsolation: false }, () => {
before(() => {
describe('Cases with invalid indicators', () => {
beforeEach(() => {
esArchiverLoad('threat_intelligence/invalid_indicators_data');
login();
cy.visit(THREAT_INTELLIGENCE);
selectRange();
visit(THREAT_INTELLIGENCE);
});
after(() => {
afterEach(() => {
esArchiverUnload('threat_intelligence/invalid_indicators_data');
});
it('should disable the indicators table context menu items if invalid indicator', () => {
it('should disable the indicators table context menu items and flyout context menu items', () => {
const documentsNumber = 22;
openIndicatorsTableMoreActions(documentsNumber - 1);
cy.get(INDICATORS_TABLE_ADD_TO_EXISTING_CASE_BUTTON_ICON).should('be.disabled');
cy.get(INDICATORS_TABLE_ADD_TO_NEW_CASE_BUTTON_ICON).should('be.disabled');
});
it('should disable the flyout context menu items if invalid indicator', () => {
const documentsNumber = 22;
openFlyout(documentsNumber - 1);
openFlyoutTakeAction();
@ -66,24 +58,36 @@ describe('Cases with invalid indicators', { testIsolation: false }, () => {
});
});
describe('Cases interactions', { testIsolation: false }, () => {
before(() => {
describe('Cases interactions', () => {
beforeEach(() => {
esArchiverLoad('threat_intelligence/indicators_data');
login();
cy.visit(THREAT_INTELLIGENCE);
selectRange();
visit(THREAT_INTELLIGENCE);
});
after(() => {
afterEach(() => {
esArchiverUnload('threat_intelligence/indicators_data');
});
it('should add to existing case when clicking on the button in the indicators table', () => {
navigateToCases();
createNewCaseFromCases();
it('should add to new case and to existing case from the indicators table and the flyout', () => {
cy.log('should add to new case when clicking on the button in the indicators table');
openIndicatorsTableMoreActions(0);
openAddToNewCaseFlyoutFromTable();
createNewCaseFromTI();
navigateToCaseViaToaster();
cy.get(CASE_COMMENT_EXTERNAL_REFERENCE)
.should('exist')
.and('contain.text', 'added an indicator of compromise')
.and('contain.text', 'Indicator name')
.and('contain.text', 'Indicator type')
.and('contain.text', 'Feed name');
navigateToThreatIntelligence();
cy.log('should add to existing case when clicking on the button in the indicators table');
cy.visit(THREAT_INTELLIGENCE);
selectRange();
openIndicatorsTableMoreActions(0);
openAddToExistingCaseFlyoutFromTable();
selectExistingCase();
@ -96,51 +100,10 @@ describe('Cases interactions', { testIsolation: false }, () => {
.and('contain.text', 'Indicator type')
.and('contain.text', 'Feed name');
deleteCase();
navigateToThreatIntelligence();
});
it('should add to new case when clicking on the button in the indicators table', () => {
openIndicatorsTableMoreActions(0);
openAddToNewCaseFlyoutFromTable();
createNewCaseFromTI();
cy.log('should add to new case when clicking on the button in the indicators flyout');
navigateToCaseViaToaster();
cy.get(CASE_COMMENT_EXTERNAL_REFERENCE)
.should('exist')
.and('contain.text', 'added an indicator of compromise')
.and('contain.text', 'Indicator name')
.and('contain.text', 'Indicator type')
.and('contain.text', 'Feed name');
deleteCase();
navigateToThreatIntelligence();
});
it('should add to existing case when clicking on the button in the indicators flyout', () => {
navigateToCases();
createNewCaseFromCases();
cy.visit(THREAT_INTELLIGENCE);
selectRange();
openFlyout(0);
openFlyoutTakeAction();
openAddToExistingCaseFromFlyout();
selectExistingCase();
navigateToCaseViaToaster();
cy.get(CASE_COMMENT_EXTERNAL_REFERENCE)
.should('exist')
.and('contain.text', 'added an indicator of compromise')
.and('contain.text', 'Indicator name')
.and('contain.text', 'Indicator type')
.and('contain.text', 'Feed name');
deleteCase();
navigateToThreatIntelligence();
});
it('should add to new case when clicking on the button in the indicators flyout', () => {
openFlyout(0);
openFlyoutTakeAction();
openAddToNewCaseFromFlyout();
@ -154,7 +117,21 @@ describe('Cases interactions', { testIsolation: false }, () => {
.and('contain.text', 'Indicator type')
.and('contain.text', 'Feed name');
deleteCase();
navigateToThreatIntelligence();
cy.log('should add to existing case when clicking on the button in the indicators flyout');
openFlyout(0);
openFlyoutTakeAction();
openAddToExistingCaseFromFlyout();
selectExistingCase();
navigateToCaseViaToaster();
cy.get(CASE_COMMENT_EXTERNAL_REFERENCE)
.should('exist')
.and('contain.text', 'added an indicator of compromise')
.and('contain.text', 'Indicator name')
.and('contain.text', 'Indicator type')
.and('contain.text', 'Feed name');
});
});

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { login } from '../tasks/login';
import { login, visit } from '../tasks/login';
import {
EMPTY_PAGE_BODY,
EMPTY_PAGE_DOCS_LINK,
@ -14,19 +14,17 @@ import {
const THREAT_INTEL_PATH = '/app/security/threat_intelligence/';
describe('Empty Page', { testIsolation: false }, () => {
before(() => {
describe('Empty Page', () => {
beforeEach(() => {
login();
cy.visit(THREAT_INTEL_PATH);
visit(THREAT_INTEL_PATH);
});
it('should render the empty page with link to docs and integrations', () => {
it('should render the empty page with link to docs and integrations, and navigate to integrations page', () => {
cy.get(EMPTY_PAGE_BODY).should('be.visible');
cy.get(EMPTY_PAGE_DOCS_LINK).should('be.visible');
cy.get(EMPTY_PAGE_INTEGRATIONS_LINK).should('be.visible');
});
it('should navigate to the integrations page', () => {
cy.get(EMPTY_PAGE_INTEGRATIONS_LINK).click();
cy.url().should('include', '/app/integrations/browse/threat_intel');
cy.get('h1').first().should('contain', 'Integrations');

View file

@ -41,9 +41,8 @@ import {
TIME_RANGE_PICKER,
REFRESH_BUTTON,
} from '../screens/indicators';
import { login } from '../tasks/login';
import { login, visit, waitForPageToBeLoaded } from '../tasks/login';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
import { selectRange } from '../tasks/select_range';
import {
closeFlyout,
navigateToFlyoutJsonTab,
@ -58,15 +57,11 @@ const URL_WITH_CONTRADICTORY_FILTERS =
'/app/security/threat_intelligence/indicators?indicators=(filterQuery:(language:kuery,query:%27%27),filters:!((%27$state%27:(store:appState),meta:(alias:!n,disabled:!f,index:%27%27,key:threat.indicator.type,negate:!f,params:(query:file),type:phrase),query:(match_phrase:(threat.indicator.type:file))),(%27$state%27:(store:appState),meta:(alias:!n,disabled:!f,index:%27%27,key:threat.indicator.type,negate:!f,params:(query:url),type:phrase),query:(match_phrase:(threat.indicator.type:url)))),timeRange:(from:now/d,to:now/d))';
describe('Invalid Indicators', () => {
before(() => {
login();
});
describe('verify the grid loads even with missing fields', () => {
before(() => {
beforeEach(() => {
esArchiverLoad('threat_intelligence/invalid_indicators_data');
cy.visit(THREAT_INTELLIGENCE);
selectRange();
login();
visit(THREAT_INTELLIGENCE);
});
after(() => {
@ -114,10 +109,10 @@ describe('Invalid Indicators', () => {
});
describe('verify the grid loads even with missing mappings and missing fields', () => {
before(() => {
beforeEach(() => {
esArchiverLoad('threat_intelligence/missing_mappings_indicators_data');
cy.visit(THREAT_INTELLIGENCE);
selectRange();
login();
visit(THREAT_INTELLIGENCE);
});
after(() => {
@ -140,7 +135,6 @@ describe('Invalid Indicators', () => {
describe('Indicators', () => {
before(() => {
esArchiverLoad('threat_intelligence/indicators_data');
login();
});
after(() => {
@ -148,6 +142,11 @@ describe('Indicators', () => {
});
describe('Indicators page loading', () => {
beforeEach(() => {
login();
visit(THREAT_INTELLIGENCE);
});
it('verify the fleet plugin integrations endpoint exists', () => {
cy.request({
method: 'GET',
@ -156,10 +155,10 @@ describe('Indicators', () => {
});
});
describe('Indicators page basics', { testIsolation: false }, () => {
before(() => {
cy.visit(THREAT_INTELLIGENCE);
selectRange();
describe('Indicators page basics', () => {
beforeEach(() => {
login();
visit(THREAT_INTELLIGENCE);
});
it('should render the basic page elements', () => {
@ -184,11 +183,8 @@ describe('Indicators', () => {
cy.get(`${FILTERS_GLOBAL_CONTAINER} ${TIME_RANGE_PICKER}`).should('exist');
cy.get(`${FIELD_SELECTOR}`).should('exist');
});
it('should show the indicator flyout on ioc click', () => {
// Just to know that the data is loaded. This will be replaced with some better mechanism.
cy.get(TABLE_CONTROLS).should('contain.text', 'Showing 1-25 of');
cy.log('should show the indicator flyout on ioc click');
openFlyout(1);
@ -208,13 +204,15 @@ describe('Indicators', () => {
});
});
describe('Indicator page search', { testIsolation: false }, () => {
before(() => {
cy.visit(THREAT_INTELLIGENCE);
selectRange();
describe('Indicator page search', () => {
beforeEach(() => {
login();
visit(THREAT_INTELLIGENCE);
});
it('should narrow the results to url indicators when respective KQL search is executed', () => {
it('should handle all search actions', () => {
cy.log('should narrow the results to url indicators when respective KQL search is executed');
enterQuery('threat.indicator.type: "url"{enter}');
// Check if query results are narrowed after search
@ -226,47 +224,45 @@ describe('Indicators', () => {
cy.get(INDICATOR_TYPE_CELL).should('not.contain.text', 'url');
clearQuery();
});
it('should go to the 2nd page', () => {
cy.log('should go to the 2nd page');
navigateToIndicatorsTablePage(1);
cy.get(TABLE_CONTROLS).should('contain.text', 'Showing 26-50 of');
});
it('should go to page 1 when search input is cleared', () => {
cy.get(QUERY_INPUT).should('exist').focus().clear().type('{enter}');
cy.log('should go to page 1 when search input is cleared');
cy.get(QUERY_INPUT).should('exist').focus();
cy.get(QUERY_INPUT).clear();
cy.get(QUERY_INPUT).type('{enter}');
cy.get(TABLE_CONTROLS).should('contain.text', 'Showing 1-25 of');
});
it('should reload the data when refresh button is pressed', () => {
cy.log('should reload the data when refresh button is pressed');
cy.intercept(/bsearch/).as('search');
cy.get(REFRESH_BUTTON).should('exist').click();
cy.wait('@search');
});
});
cy.get(REFRESH_BUTTON).should('exist').click();
cy.wait('@search');
describe('No items match search criteria', () => {
beforeEach(() => {
login();
cy.visit(URL_WITH_CONTRADICTORY_FILTERS);
waitForPageToBeLoaded();
});
describe('No items match search criteria', () => {
before(() => {
// Contradictory filter set
cy.visit(URL_WITH_CONTRADICTORY_FILTERS);
selectRange();
});
it('should handle no match search criterie', () => {
cy.log('not display the table when contradictory filters are set');
it('should not display the table when contradictory filters are set', () => {
cy.get(FLYOUT_TABLE).should('not.exist');
cy.get(FLYOUT_TABLE).should('not.exist');
cy.get(EMPTY_STATE).should('exist').and('contain.text', 'No results');
});
});
cy.get(EMPTY_STATE).should('exist').and('contain.text', 'No results');
cy.log('have the default selected field, then update when user selects');
it('should have the default selected field, then update when user selects', () => {
const threatFeedName = 'threat.feed.name';
cy.get(`${FIELD_SELECTOR_INPUT}`).eq(0).should('have.text', threatFeedName);
@ -279,14 +275,15 @@ describe('Indicators', () => {
});
describe('Field browser', () => {
before(() => {
cy.visit(THREAT_INTELLIGENCE);
selectRange();
beforeEach(() => {
login();
visit(THREAT_INTELLIGENCE);
});
describe('when field browser is triggered', () => {
it('should render proper modal window', () => {
cy.get(FIELD_BROWSER).last().click({ force: true });
cy.get('[data-test-subj="tiIndicatorsTable"]').within(() => {
cy.get(FIELD_BROWSER).last().click();
});
cy.get(FIELD_BROWSER_MODAL).should('be.visible');
});
@ -294,32 +291,28 @@ describe('Indicators', () => {
});
describe('Request inspector', () => {
before(() => {
cy.visit(THREAT_INTELLIGENCE);
selectRange();
beforeEach(() => {
login();
visit(THREAT_INTELLIGENCE);
});
describe('when inspector button is clicked', () => {
it('should render the inspector flyout', () => {
cy.get(INSPECTOR_BUTTON).last().click({ force: true });
it('when inspector button is clicked it should render the inspector flyout', () => {
cy.get(INSPECTOR_BUTTON).last().click();
cy.get(INSPECTOR_PANEL).contains('Indicators search requests');
});
cy.get(INSPECTOR_PANEL).contains('Indicators search requests');
});
});
describe('Add integrations', () => {
before(() => {
cy.visit(THREAT_INTELLIGENCE);
selectRange();
beforeEach(() => {
login();
visit(THREAT_INTELLIGENCE);
});
describe('when the global header add integrations button is clicked', () => {
it('should navigate to the Integrations page with Threat Intelligence category selected', () => {
cy.get(ADD_INTEGRATIONS_BUTTON).click();
it('when the global header add integrations button is clicked it should navigate to the Integrations page with Threat Intelligence category selected', () => {
cy.get(ADD_INTEGRATIONS_BUTTON).click();
cy.url().should('include', 'threat_intel');
});
cy.url().should('include', 'threat_intel');
});
});
});

View file

@ -8,7 +8,6 @@
import {
closeFlyout,
navigateToFlyoutTableTab,
openBarchartPopoverMenu,
openFlyout,
waitForViewToBeUpdated,
} from '../tasks/common';
@ -27,152 +26,163 @@ import {
} from '../tasks/query_bar';
import { INDICATOR_TYPE_CELL } from '../screens/indicators';
import { KQL_FILTER } from '../screens/query_bar';
import { selectRange } from '../tasks/select_range';
import { login } from '../tasks/login';
import { login, visit } from '../tasks/login';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
const THREAT_INTELLIGENCE = '/app/security/threat_intelligence/indicators';
describe('Indicators query bar interaction', { testIsolation: false }, () => {
before(() => {
describe('Indicators query bar interaction', () => {
beforeEach(() => {
esArchiverLoad('threat_intelligence/indicators_data');
login();
cy.visit(THREAT_INTELLIGENCE);
selectRange();
visit(THREAT_INTELLIGENCE);
});
after(() => {
afterEach(() => {
esArchiverUnload('threat_intelligence/indicators_data');
});
it('should add filter to kql and filter in values when clicking in the barchart legend', () => {
it('should add filter to kql', () => {
cy.log('filter in values when clicking in the barchart legend');
waitForViewToBeUpdated();
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
openBarchartPopoverMenu();
filterInFromBarChartLegend();
waitForViewToBeUpdated();
cy.get(KQL_FILTER).should('exist');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
clearKQLBar();
});
it('should add negated filter to kql and filter out values when clicking in the barchart legend', () => {
waitForViewToBeUpdated();
cy.log('filter out values when clicking in the barchart legend');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
openBarchartPopoverMenu();
filterOutFromBarChartLegend();
waitForViewToBeUpdated();
cy.get(KQL_FILTER).should('exist');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
clearKQLBar();
});
it('should add filter to kql and filter in and out values when clicking in an indicators table cell', () => {
waitForViewToBeUpdated();
cy.log('filter in values when clicking in an indicators table cell');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
filterInFromTableCell();
waitForViewToBeUpdated();
cy.get(KQL_FILTER).should('exist');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
clearKQLBar();
});
waitForViewToBeUpdated();
cy.log('filter out and out values when clicking in an indicators table cell');
it('should add negated filter and filter out and out values when clicking in an indicators table cell', () => {
waitForViewToBeUpdated();
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
filterOutFromTableCell();
waitForViewToBeUpdated();
cy.get(KQL_FILTER).should('exist');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
clearKQLBar();
});
it('should add filter to kql and filter in values when clicking in an indicators flyout overview tab block', () => {
waitForViewToBeUpdated();
cy.log('filter in values when clicking in an indicators flyout overview tab block');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
openFlyout(0);
filterInFromFlyoutBlockItem();
closeFlyout();
waitForViewToBeUpdated();
cy.get(KQL_FILTER).should('exist');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
clearKQLBar();
});
it('should add negated filter to kql filter out values when clicking in an indicators flyout overview block', () => {
waitForViewToBeUpdated();
cy.log('filter out values when clicking in an indicators flyout overview block');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
openFlyout(0);
filterOutFromFlyoutBlockItem();
closeFlyout();
waitForViewToBeUpdated();
cy.get(KQL_FILTER).should('exist');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
clearKQLBar();
});
it('should add filter to kql and filter in values when clicking in an indicators flyout overview tab table row', () => {
waitForViewToBeUpdated();
cy.log('filter in values when clicking in an indicators flyout overview tab table row');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
openFlyout(0);
filterInFromFlyoutOverviewTable();
closeFlyout();
waitForViewToBeUpdated();
cy.get(KQL_FILTER).should('exist');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
clearKQLBar();
});
it('should add negated filter to kql filter out values when clicking in an indicators flyout overview tab row', () => {
waitForViewToBeUpdated();
cy.log('filter out values when clicking in an indicators flyout overview tab row');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
openFlyout(0);
filterOutFromFlyoutOverviewTable();
closeFlyout();
waitForViewToBeUpdated();
cy.get(KQL_FILTER).should('exist');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
clearKQLBar();
});
it('should add filter to kql and filter in values when clicking in an indicators flyout table tab action column', () => {
waitForViewToBeUpdated();
cy.log('filter in values when clicking in an indicators flyout table tab action column');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
openFlyout(0);
navigateToFlyoutTableTab();
filterInFromFlyoutTableTab();
closeFlyout();
waitForViewToBeUpdated();
cy.get(KQL_FILTER).should('exist');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
clearKQLBar();
});
it('should add negated filter to kql filter out values when clicking in an indicators flyout table tab action column', () => {
waitForViewToBeUpdated();
cy.log('filter out values when clicking in an indicators flyout table tab action column');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);
openFlyout(0);
navigateToFlyoutTableTab();
filterOutFromFlyoutTableTab();
closeFlyout();
waitForViewToBeUpdated();
cy.get(KQL_FILTER).should('exist');
cy.get(INDICATOR_TYPE_CELL).its('length').should('be.gte', 0);

View file

@ -15,87 +15,101 @@ import {
investigateInTimelineFromTable,
openTimeline,
} from '../tasks/timeline';
import { closeFlyout, openFlyout, openFlyoutTakeAction } from '../tasks/common';
import {
closeFlyout,
openBarchartPopoverMenu,
openFlyout,
openFlyoutTakeAction,
} from '../tasks/common';
import { TIMELINE_DRAGGABLE_ITEM } from '../screens/timeline';
TIMELINE_AND_OR_BADGE,
TIMELINE_DATA_PROVIDERS_WRAPPER,
TIMELINE_DRAGGABLE_ITEM,
} from '../screens/timeline';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
import { login } from '../tasks/login';
import { selectRange } from '../tasks/select_range';
import { login, visit } from '../tasks/login';
const THREAT_INTELLIGENCE = '/app/security/threat_intelligence/indicators';
describe('Timeline', { testIsolation: false }, () => {
before(() => {
describe('Timeline', () => {
beforeEach(() => {
esArchiverLoad('threat_intelligence/indicators_data');
login();
cy.visit(THREAT_INTELLIGENCE);
selectRange();
visit(THREAT_INTELLIGENCE);
});
after(() => {
afterEach(() => {
esArchiverUnload('threat_intelligence/indicators_data');
});
it('should add entry in timeline when clicking in the barchart legend', () => {
openBarchartPopoverMenu();
it('should verify add to timeline and investigate in timeline work from various places', () => {
cy.log('add to timeline when clicking in the barchart legend');
addToTimelineFromBarchartLegend();
openTimeline();
cy.get(TIMELINE_DRAGGABLE_ITEM).should('exist');
cy.get(TIMELINE_DATA_PROVIDERS_WRAPPER).within(() => {
cy.get(TIMELINE_DRAGGABLE_ITEM).should('exist');
cy.get(TIMELINE_AND_OR_BADGE).should('be.visible').and('have.length', 3);
});
closeTimeline();
});
it('should add entry in timeline when clicking in an indicator table cell', () => {
addToTimelineFromTableCell();
openTimeline();
cy.log('add to timeline when clicking in an indicator flyout overview tab table row');
cy.get(TIMELINE_DRAGGABLE_ITEM).should('exist');
closeTimeline();
});
it('should add entry in timeline when clicking in an indicator flyout overview tab table row', () => {
openFlyout(0);
addToTimelineFromFlyoutOverviewTabTable();
closeFlyout();
openTimeline();
cy.get(TIMELINE_DRAGGABLE_ITEM).should('exist');
cy.get(TIMELINE_DATA_PROVIDERS_WRAPPER).within(() => {
cy.get(TIMELINE_DRAGGABLE_ITEM).should('exist');
cy.get(TIMELINE_AND_OR_BADGE).should('be.visible').and('have.length', 5);
});
closeTimeline();
});
it('should add entry in timeline when clicking in an indicator flyout overview block', () => {
cy.log('add to timeline when clicking in an indicator flyout overview block');
openFlyout(0);
addToTimelineFromFlyoutOverviewTabBlock();
closeFlyout();
openTimeline();
cy.get(TIMELINE_DRAGGABLE_ITEM).should('exist');
cy.get(TIMELINE_DATA_PROVIDERS_WRAPPER).within(() => {
cy.get(TIMELINE_DRAGGABLE_ITEM).should('exist');
cy.get(TIMELINE_AND_OR_BADGE).should('be.visible').and('have.length', 7);
});
closeTimeline();
});
it('should investigate in timeline when clicking in an indicator table action row', () => {
cy.log('add to timeline when clicking in an indicator table cell');
addToTimelineFromTableCell();
openTimeline();
cy.get(TIMELINE_DATA_PROVIDERS_WRAPPER).within(() => {
cy.get(TIMELINE_DRAGGABLE_ITEM).should('exist');
cy.get(TIMELINE_AND_OR_BADGE).should('be.visible').and('have.length', 9);
});
closeTimeline();
cy.log('investigate in timeline when clicking in an indicator table action row');
investigateInTimelineFromTable();
cy.get(TIMELINE_DRAGGABLE_ITEM).should('exist');
cy.get(TIMELINE_DATA_PROVIDERS_WRAPPER).within(() => {
cy.get(TIMELINE_DRAGGABLE_ITEM).should('exist');
cy.get(TIMELINE_AND_OR_BADGE).should('be.visible').and('have.length', 5);
});
closeTimeline();
});
it('should investigate in timeline when clicking in an indicator flyout', () => {
cy.log('investigate in timeline when clicking in an indicator flyout');
openFlyout(0);
openFlyoutTakeAction();
investigateInTimelineFromFlyout();
cy.get(TIMELINE_DRAGGABLE_ITEM).should('exist');
closeTimeline();
cy.get(TIMELINE_DATA_PROVIDERS_WRAPPER).within(() => {
cy.get(TIMELINE_DRAGGABLE_ITEM).should('exist');
cy.get(TIMELINE_AND_OR_BADGE).should('be.visible').and('have.length', 5);
});
});
});

View file

@ -18,6 +18,3 @@ export const BLOCK_LIST_VALUE_INPUT = (iocId: string) =>
`[data-test-subj="blocklist-form-values-input-${iocId}"]`;
export const SAVED_BLOCK_LIST_NAME = `[data-test-subj="blocklistPage-card-header-title"]`;
export const SAVED_BLOCK_LIST_DESCRIPTION = `[data-test-subj="blocklistPage-card-description"]`;
export const SAVED_BLOCK_LIST_ACTION_MENU = `[data-test-subj="blocklistPage-card-header-actions-button"]`;
export const SAVED_BLOCK_LIST_DELETE_BUTTON = `[data-test-subj="blocklistPage-card-cardDeleteAction"]`;
export const SAVED_BLOCK_LIST_CONFIRM_DELETE_BUTTON = `[data-test-subj="blocklistPage-deleteModal-submitButton"]`;

View file

@ -18,14 +18,9 @@ export const INDICATORS_TABLE_ADD_TO_NEW_CASE_BUTTON_ICON = `[data-test-subj="${
export const INDICATORS_TABLE_ADD_TO_EXISTING_CASE_BUTTON_ICON = `[data-test-subj="${INDICATORS_TABLE_ADD_TO_EXISTING_TEST_ID}"]`;
export const FLYOUT_ADD_TO_EXISTING_CASE_ITEM = `[data-test-subj="${INDICATOR_FLYOUT_TAKE_ACTION_ADD_TO_EXISTING_CASE_TEST_ID}"]`;
export const FLYOUT_ADD_TO_NEW_CASE_ITEM = `[data-test-subj="${INDICATOR_FLYOUT_TAKE_ACTION_ADD_TO_NEW_CASE_TEST_ID}"]`;
export const CREATE_CASE_BUTTON = `[data-test-subj="createNewCaseBtn"]`;
export const SELECT_EXISTING_CASE = `[class="eui-textTruncate"]`;
export const VIEW_CASE_TOASTER_LINK = `[data-test-subj="toaster-content-case-view-link"]`;
export const CASE_COMMENT_EXTERNAL_REFERENCE = `[data-test-subj="comment-externalReference-indicator"]`;
export const CASE_ACTION_WRAPPER = `[data-test-subj="case-action-bar-wrapper"]`;
export const CASE_ELLIPSE_BUTTON = `[data-test-subj="property-actions-case-ellipses"]`;
export const CASE_ELLIPSE_DELETE_CASE_OPTION = `[data-test-subj="property-actions-case-trash"]`;
export const CASE_ELLIPSE_DELETE_CASE_CONFIRMATION_BUTTON = `[data-test-subj="confirmModalConfirmButton"]`;
export const NEW_CASE_NAME_INPUT = `[data-test-subj="input"][aria-describedby="caseTitle"]`;
export const NEW_CASE_DESCRIPTION_INPUT = `[data-test-subj="euiMarkdownEditorTextArea"]`;
export const NEW_CASE_CREATE_BUTTON = `[data-test-subj="create-case-submit"]`;

View file

@ -8,5 +8,4 @@
export const UPDATE_STATUS = `[data-test-subj="updateStatus"]`;
export const SECURITY_SOLUTION_NAVBAR_MANAGE_ITEM = `[data-test-subj="solutionSideNavItemLink-administration"]`;
export const SECURITY_SOLUTION_NAVBAR_THREAT_INTELLIGENCE_ITEM = `[data-test-subj="solutionSideNavItemLink-threat_intelligence-indicators"]`;
export const SECURITY_SOLUTION_NAVBAR_CASES_ITEM = `[data-test-subj="solutionSideNavItemLink-cases"]`;
export const MANAGE_NAVIGATION_ITEMS = `.euiLink`;

View file

@ -105,3 +105,4 @@ export const INSPECTOR_BUTTON = `[data-test-subj="${INSPECT_BUTTON_TEST_ID}"]`;
export const INSPECTOR_PANEL = `[data-test-subj="inspectorPanel"]`;
export const ADD_INTEGRATIONS_BUTTON = `[data-test-subj="add-data"]`;
export const REFRESH_BUTTON = `[data-test-subj="querySubmitButton"]`;
export const ADDED_TO_TIMELINE_TOAST = `[data-test-subj="add-to-timeline-toast-success"]`;

View file

@ -6,7 +6,6 @@
*/
import {
FLYOUT_TABLE_TEST_ID,
INDICATORS_FLYOUT_OVERVIEW_HIGH_LEVEL_BLOCKS,
INDICATORS_FLYOUT_OVERVIEW_TABLE,
INVESTIGATE_IN_TIMELINE_TEST_ID as INDICATOR_FLYOUT_TAKE_ACTION_INVESTIGATE_IN_TIMELINE_TEST_ID,
@ -20,10 +19,10 @@ import {
export const INDICATORS_TABLE_INVESTIGATE_IN_TIMELINE_BUTTON_ICON = `[data-test-subj="${CELL_INVESTIGATE_IN_TIMELINE_TEST_ID}"]`;
export const UNTITLED_TIMELINE_BUTTON = `[data-test-subj="flyoutOverlay"]`;
export const INDICATORS_TABLE_CELL_TIMELINE_BUTTON = `[data-test-subj="${CELL_TIMELINE_BUTTON_TEST_ID}"] button`;
export const TIMELINE_DATA_PROVIDERS_WRAPPER = `[data-test-subj="dataProviders"]`;
export const TIMELINE_DRAGGABLE_ITEM = `[data-test-subj="providerContainer"]`;
export const TIMELINE_AND_OR_BADGE = `[data-test-subj="and-or-badge"]`;
export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]';
export const QUERY_TAB_BUTTON = '[data-test-subj="timelineTabs-query"]';
export const FLYOUT_OVERVIEW_TAB_TABLE_ROW_TIMELINE_BUTTON = `[data-test-subj="${INDICATORS_FLYOUT_OVERVIEW_TABLE}${VALUE_ACTION_TIMELINE_BUTTON_TEST_ID}"]`;
export const FLYOUT_OVERVIEW_TAB_BLOCKS_TIMELINE_BUTTON = `[data-test-subj="${INDICATORS_FLYOUT_OVERVIEW_HIGH_LEVEL_BLOCKS}${VALUE_ACTION_TIMELINE_BUTTON_TEST_ID}"]`;
export const FLYOUT_TABLE_TAB_ROW_TIMELINE_BUTTON = `[data-test-subj="${FLYOUT_TABLE_TEST_ID}${VALUE_ACTION_TIMELINE_BUTTON_TEST_ID}"]`;
export const FLYOUT_INVESTIGATE_IN_TIMELINE_ITEM = `[data-test-subj="${INDICATOR_FLYOUT_TAKE_ACTION_INVESTIGATE_IN_TIMELINE_TEST_ID}"]`;

View file

@ -1,8 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export const INDICATORS_URL = '/app/security/indicators';

View file

@ -9,10 +9,7 @@ import {
BLOCK_LIST_ADD_BUTTON,
BLOCK_LIST_DESCRIPTION,
BLOCK_LIST_NAME,
SAVED_BLOCK_LIST_ACTION_MENU,
BLOCK_LIST_TOAST_LIST,
SAVED_BLOCK_LIST_CONFIRM_DELETE_BUTTON,
SAVED_BLOCK_LIST_DELETE_BUTTON,
FLYOUT_ADD_TO_BLOCK_LIST_ITEM,
INDICATORS_TABLE_ADD_TO_BLOCK_LIST_BUTTON_ICON,
} from '../screens/blocklist';
@ -42,12 +39,3 @@ export const fillBlocklistForm = (title: string, description: string) => {
const text: string = `"${title}" has been added`;
cy.get(BLOCK_LIST_TOAST_LIST).should('exist').and('contain.text', text);
};
/**
* Remove the blocklist entry
*/
export const deleteBlocklistEntry = () => {
cy.get(SAVED_BLOCK_LIST_ACTION_MENU).click();
cy.get(SAVED_BLOCK_LIST_DELETE_BUTTON).click();
cy.get(SAVED_BLOCK_LIST_CONFIRM_DELETE_BUTTON).click();
};

View file

@ -10,11 +10,6 @@ import {
FLYOUT_ADD_TO_NEW_CASE_ITEM,
INDICATORS_TABLE_ADD_TO_EXISTING_CASE_BUTTON_ICON,
INDICATORS_TABLE_ADD_TO_NEW_CASE_BUTTON_ICON,
CASE_ACTION_WRAPPER,
CASE_ELLIPSE_BUTTON,
CASE_ELLIPSE_DELETE_CASE_CONFIRMATION_BUTTON,
CASE_ELLIPSE_DELETE_CASE_OPTION,
CREATE_CASE_BUTTON,
NEW_CASE_CREATE_BUTTON,
NEW_CASE_DESCRIPTION_INPUT,
NEW_CASE_NAME_INPUT,
@ -52,16 +47,6 @@ export const openAddToExistingCaseFromFlyout = () => {
cy.get(FLYOUT_ADD_TO_EXISTING_CASE_ITEM).first().click();
};
/**
* Create a new case by filling out the form from the Cases page
*/
export const createNewCaseFromCases = () => {
cy.get(CREATE_CASE_BUTTON).click();
cy.get(NEW_CASE_NAME_INPUT).click().type('case');
cy.get(NEW_CASE_DESCRIPTION_INPUT).click().type('case description');
cy.get(NEW_CASE_CREATE_BUTTON).click();
};
/**
* Create a new case from the Threat Intelligence page
*/
@ -78,15 +63,6 @@ export const navigateToCaseViaToaster = () => {
cy.get(VIEW_CASE_TOASTER_LINK).click();
};
/**
* Delete case from the Cases page
*/
export const deleteCase = () => {
cy.get(CASE_ACTION_WRAPPER).find(CASE_ELLIPSE_BUTTON).click();
cy.get(CASE_ELLIPSE_DELETE_CASE_OPTION).click();
cy.get(CASE_ELLIPSE_DELETE_CASE_CONFIRMATION_BUTTON).click();
};
/**
* Select existing case from cases modal
*/

View file

@ -7,7 +7,6 @@
import {
MANAGE_NAVIGATION_ITEMS,
SECURITY_SOLUTION_NAVBAR_CASES_ITEM,
SECURITY_SOLUTION_NAVBAR_MANAGE_ITEM,
SECURITY_SOLUTION_NAVBAR_THREAT_INTELLIGENCE_ITEM,
UPDATE_STATUS,
@ -26,7 +25,8 @@ import {
* Navigate to Blocklist screen via the Security Solution navbar and Manage menu item
*/
export const navigateToBlocklist = () => {
cy.get(SECURITY_SOLUTION_NAVBAR_MANAGE_ITEM).scrollIntoView().click();
cy.get(SECURITY_SOLUTION_NAVBAR_MANAGE_ITEM).scrollIntoView();
cy.get(SECURITY_SOLUTION_NAVBAR_MANAGE_ITEM).click();
cy.get(MANAGE_NAVIGATION_ITEMS).contains('Blocklist').click();
};
@ -37,13 +37,6 @@ export const navigateToThreatIntelligence = () => {
cy.get(SECURITY_SOLUTION_NAVBAR_THREAT_INTELLIGENCE_ITEM).click();
};
/**
* Navigate to Cases screen via the Security Solution navbar
*/
export const navigateToCases = () => {
cy.get(SECURITY_SOLUTION_NAVBAR_CASES_ITEM).click();
};
/**
* Close the opened flyout
*/
@ -87,12 +80,14 @@ export const navigateToFlyoutJsonTab = () => {
};
export const waitForViewToBeUpdated = () => {
cy.get(UPDATE_STATUS).scrollIntoView().should('contain.text', 'Updated');
cy.get(UPDATE_STATUS).scrollIntoView();
cy.get(UPDATE_STATUS).should('contain.text', 'Updated');
};
/**
* Open barchart 3-dot popover menu
*/
export const openBarchartPopoverMenu = () => {
cy.get(BARCHART_POPOVER_BUTTON).first().scrollIntoView();
cy.get(BARCHART_POPOVER_BUTTON).should('exist').first().click();
};

View file

@ -8,7 +8,7 @@
import { QUERY_INPUT } from '../screens/indicators';
/**
* Nvigate to specific page in indicators table
* Navigate to specific page in indicators table
*/
export const navigateToIndicatorsTablePage = (index: number) => {
cy.get(`[data-test-subj="pagination-button-${index}"]`).click();
@ -18,12 +18,14 @@ export const navigateToIndicatorsTablePage = (index: number) => {
* Clears text in KQL bar
*/
export const enterQuery = (text: string) => {
cy.get(QUERY_INPUT).should('exist').focus().type(text);
cy.get(QUERY_INPUT).should('exist').focus();
cy.get(QUERY_INPUT).should('exist').type(text);
};
/**
* Clears text in KQL bar
*/
export const clearQuery = () => {
cy.get(QUERY_INPUT).should('exist').focus().clear();
cy.get(QUERY_INPUT).should('exist').focus();
cy.get(QUERY_INPUT).should('exist').clear();
};

View file

@ -10,13 +10,15 @@ import Url from 'url';
import * as yaml from 'js-yaml';
import {
LOADING_INDICATOR,
LOADING_INDICATOR_HIDDEN,
} from '@kbn/security-solution-plugin/cypress/screens/security_header';
import { encode } from '@kbn/rison';
import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '@kbn/security-solution-plugin/common/constants';
import type { ROLES } from './privileges';
const LOGIN_API_ENDPOINT = '/internal/security/login';
const LOGOUT_URL = '/logout';
export const hostDetailsUrl = (hostName: string) =>
`/app/security/hosts/${hostName}/authentications`;
/**
* Credentials in the `kibana.dev.yml` config file will be used to authenticate
@ -124,33 +126,6 @@ export const postRoleAndUser = (role: ROLES) => {
});
};
export const deleteRoleAndUser = (role: ROLES) => {
const env = getCurlScriptEnvVars();
const detectionsUserDeleteScriptPath = `./server/lib/detection_engine/scripts/roles_users/${role}/delete_detections_user.sh`;
// delete the role
cy.exec(`bash ${detectionsUserDeleteScriptPath}`, {
env,
});
};
export const loginWithUser = (user: User) => {
cy.request({
body: {
providerType: 'basic',
providerName: 'basic',
currentURL: '/',
params: {
username: user.username,
password: user.password,
},
},
headers: { 'kbn-xsrf': 'cypress-creds-via-config' },
method: 'POST',
url: constructUrlWithUser(user, LOGIN_API_ENDPOINT),
});
};
export const loginWithRole = async (role: ROLES) => {
postRoleAndUser(role);
const theUrl = Url.format({
@ -297,57 +272,53 @@ export const getEnvAuth = (): User => {
}
};
/**
* Authenticates with Kibana, visits the specified `url`, and waits for the
* Kibana global nav to be displayed before continuing
*/
export const loginAndWaitForPage = (
url: string,
role?: ROLES,
onBeforeLoadCallback?: (win: Cypress.AUTWindow) => void
) => {
login(role);
cy.visit(
`${url}?timerange=(global:(linkTo:!(timeline),timerange:(from:1547914976217,fromStr:'2019-01-19T16:22:56.217Z',kind:relative,to:1579537385745,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1547914976217,fromStr:'2019-01-19T16:22:56.217Z',kind:relative,to:1579537385745,toStr:now)))`,
{
onBeforeLoad(win) {
if (onBeforeLoadCallback) {
onBeforeLoadCallback(win);
}
},
}
);
cy.get('[data-test-subj="headerGlobalNav"]');
};
export const waitForPage = (url: string) => {
cy.visit(
`${url}?timerange=(global:(linkTo:!(timeline),timerange:(from:1547914976217,fromStr:'2019-01-19T16:22:56.217Z',kind:relative,to:1579537385745,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1547914976217,fromStr:'2019-01-19T16:22:56.217Z',kind:relative,to:1579537385745,toStr:now)))`
);
cy.get('[data-test-subj="headerGlobalNav"]');
export const visit = (url: string, options: Partial<Cypress.VisitOptions> = {}, role?: ROLES) => {
const timerangeConfig = {
from: 1547914976217,
fromStr: '2019-01-19T16:22:56.217Z',
kind: 'relative',
to: 1579537385745,
toStr: 'now',
};
const timerange = encode({
global: {
linkTo: ['timeline'],
timerange: timerangeConfig,
},
timeline: {
linkTo: ['global'],
timerange: timerangeConfig,
},
});
cy.visit(role ? getUrlWithRoute(role, url) : url, {
...options,
qs: {
...options.qs,
timerange,
},
onBeforeLoad: (win) => {
options.onBeforeLoad?.(win);
disableNewFeaturesTours(win);
},
});
waitForPageToBeLoaded();
};
export const loginAndWaitForPageWithoutDateRange = (url: string, role?: ROLES) => {
login(role);
cy.visit(role ? getUrlWithRoute(role, url) : url);
cy.get('[data-test-subj="headerGlobalNav"]', { timeout: 120000 });
const disableNewFeaturesTours = (window: Window) => {
const tourStorageKeys = Object.values(NEW_FEATURES_TOUR_STORAGE_KEYS);
const tourConfig = {
isTourActive: false,
};
tourStorageKeys.forEach((key) => {
window.localStorage.setItem(key, JSON.stringify(tourConfig));
});
};
export const loginWithUserAndWaitForPage = (url: string, user: User) => {
loginWithUser(user);
cy.visit(constructUrlWithUser(user, url));
cy.get('[data-test-subj="headerGlobalNav"]', { timeout: 120000 });
};
export const loginAndWaitForHostDetailsPage = (hostName = 'suricata-iowa') => {
loginAndWaitForPage(hostDetailsUrl(hostName));
cy.get('[data-test-subj="loading-spinner"]', { timeout: 12000 }).should('not.exist');
};
export const waitForPageWithoutDateRange = (url: string, role?: ROLES) => {
cy.visit(role ? getUrlWithRoute(role, url) : url);
cy.get('[data-test-subj="headerGlobalNav"]', { timeout: 120000 });
};
export const logout = () => {
cy.visit(LOGOUT_URL);
export const waitForPageToBeLoaded = () => {
cy.get(LOADING_INDICATOR_HIDDEN).should('exist');
cy.get(LOADING_INDICATOR).should('not.exist');
};

View file

@ -48,118 +48,6 @@ interface Role {
};
}
// Create roles with allowed combinations of Fleet and Integrations
export const FleetAllIntegrAllRole: Role = {
name: 'fleet_all_int_all_role',
privileges: {
elasticsearch: {
indices: [
{
names: ['*'],
privileges: ['all'],
},
],
},
kibana: [
{
feature: {
fleetv2: ['all'],
fleet: ['all'],
},
spaces: ['*'],
},
],
},
};
export const FleetAllIntegrAllUser: User = {
username: 'fleet_all_int_all_user',
password: 'password',
roles: [FleetAllIntegrAllRole.name],
};
export const FleetAllIntegrReadRole: Role = {
name: 'fleet_all_int_read_user',
privileges: {
elasticsearch: {
indices: [
{
names: ['*'],
privileges: ['all'],
},
],
},
kibana: [
{
feature: {
fleetv2: ['all'],
fleet: ['read'],
},
spaces: ['*'],
},
],
},
};
export const FleetAllIntegrReadUser: User = {
username: 'fleet_all_int_read_user',
password: 'password',
roles: [FleetAllIntegrReadRole.name],
};
export const FleetAllIntegrNoneRole: Role = {
name: 'fleet_all_int_none_role',
privileges: {
elasticsearch: {
indices: [
{
names: ['*'],
privileges: ['all'],
},
],
},
kibana: [
{
feature: {
fleetv2: ['all'],
fleet: ['none'],
},
spaces: ['*'],
},
],
},
};
export const FleetAllIntegrNoneUser: User = {
username: 'fleet_all_int_none_user',
password: 'password',
roles: [FleetAllIntegrNoneRole.name],
};
export const FleetNoneIntegrAllRole: Role = {
name: 'fleet_none_int_all_role',
privileges: {
elasticsearch: {
indices: [
{
names: ['*'],
privileges: ['all'],
},
],
},
kibana: [
{
feature: {
fleetv2: ['none'],
fleet: ['all'],
},
spaces: ['*'],
},
],
},
};
export const FleetNoneIntegrAllUser: User = {
username: 'fleet_none_int_all_user',
password: 'password',
roles: [FleetNoneIntegrAllRole.name],
};
const getUserInfo = (user: User): UserInfo => ({
username: user.username,
full_name: user.username.replace('_', ' '),

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import { openBarchartPopoverMenu } from './common';
import {
QUERY_BAR,
QUERY_BAR_MENU_REMOVE_ALL_FILTERS_BUTTON,
@ -29,6 +30,7 @@ import {
* Filter in value by clicking on the menu item within barchart popover
*/
export const filterInFromBarChartLegend = () => {
openBarchartPopoverMenu();
cy.get(BARCHART_FILTER_IN_BUTTON).should('exist').click();
};
@ -36,6 +38,7 @@ export const filterInFromBarChartLegend = () => {
* Filter out value by clicking on the menu item within barchart popover
*/
export const filterOutFromBarChartLegend = () => {
openBarchartPopoverMenu();
cy.get(BARCHART_FILTER_OUT_BUTTON).should('exist').click();
};
@ -43,10 +46,10 @@ export const filterOutFromBarChartLegend = () => {
* Filter in value by clicking on the menu item within an indicators table cell
*/
export const filterInFromTableCell = () => {
cy.get(INDICATOR_TYPE_CELL).first().should('be.visible');
cy.get(INDICATOR_TYPE_CELL).first().trigger('mouseover');
cy.get(INDICATOR_TYPE_CELL)
.first()
.should('be.visible')
.trigger('mouseover')
.within((_cell) => {
cy.get(INDICATORS_TABLE_CELL_FILTER_IN_BUTTON).should('exist').click({
force: true,
@ -58,9 +61,9 @@ export const filterInFromTableCell = () => {
* Filter out value by clicking on the menu item within an indicators table cell
*/
export const filterOutFromTableCell = () => {
cy.get(INDICATOR_TYPE_CELL).first().trigger('mouseover');
cy.get(INDICATOR_TYPE_CELL)
.first()
.trigger('mouseover')
.within((_cell) => {
cy.get(INDICATORS_TABLE_CELL_FILTER_OUT_BUTTON).should('exist').click({ force: true });
});
@ -70,7 +73,10 @@ export const filterOutFromTableCell = () => {
* Clears all filters within KQL bar
*/
export const clearKQLBar = () => {
cy.get(QUERY_BAR).scrollIntoView();
cy.get(QUERY_BAR).within(() => cy.get(QUERY_BAR_MENU).click());
cy.get(QUERY_BAR_MENU_REMOVE_ALL_FILTERS_BUTTON).scrollIntoView();
cy.get(QUERY_BAR_MENU_REMOVE_ALL_FILTERS_BUTTON).click();
};

View file

@ -1,18 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EMPTY_STATE, TIME_RANGE_PICKER } from '../screens/indicators';
export const selectRange = () => {
cy.get(EMPTY_STATE);
cy.get(TIME_RANGE_PICKER).first().click({ force: true });
cy.get('[aria-label="Time unit"]').select('y');
cy.get('[data-test-subj="superDatePickerQuickMenu"] .euiQuickSelect__applyButton').click({
force: true,
});
};

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { recurse } from 'cypress-recurse';
import { openBarchartPopoverMenu } from './common';
import {
CLOSE_TIMELINE_BTN,
FLYOUT_INVESTIGATE_IN_TIMELINE_ITEM,
@ -15,6 +17,7 @@ import {
UNTITLED_TIMELINE_BUTTON,
} from '../screens/timeline';
import {
ADDED_TO_TIMELINE_TOAST,
BARCHART_TIMELINE_BUTTON,
FLYOUT_BLOCK_MORE_ACTIONS_BUTTON,
FLYOUT_TABLE_MORE_ACTIONS_BUTTON,
@ -25,7 +28,15 @@ import {
* Add data to timeline from barchart legend menu item
*/
export const addToTimelineFromBarchartLegend = () => {
cy.get(BARCHART_TIMELINE_BUTTON).should('exist').first().click();
recurse(
() => {
openBarchartPopoverMenu();
cy.get(BARCHART_TIMELINE_BUTTON).first().click();
openBarchartPopoverMenu();
return cy.get(ADDED_TO_TIMELINE_TOAST).should(Cypress._.noop);
},
($el) => !!$el.length
);
};
/**
* Add data to timeline from indicators table cell menu
@ -53,16 +64,33 @@ export const closeTimeline = () => {
* Add data to timeline from flyout overview tab table
*/
export const addToTimelineFromFlyoutOverviewTabTable = () => {
cy.get(FLYOUT_TABLE_MORE_ACTIONS_BUTTON).first().click({ force: true });
cy.get(FLYOUT_OVERVIEW_TAB_TABLE_ROW_TIMELINE_BUTTON).should('exist').first().click();
recurse(
() => {
cy.get(FLYOUT_TABLE_MORE_ACTIONS_BUTTON).first().click({ force: true });
cy.get(FLYOUT_OVERVIEW_TAB_TABLE_ROW_TIMELINE_BUTTON).first().click();
cy.get(FLYOUT_TABLE_MORE_ACTIONS_BUTTON).first().click({ force: true });
return cy.get(ADDED_TO_TIMELINE_TOAST).should(Cypress._.noop);
},
($el) => !!$el.length
);
};
/**
* Add data to timeline from flyout overview tab block
*/
export const addToTimelineFromFlyoutOverviewTabBlock = () => {
cy.get(FLYOUT_BLOCK_MORE_ACTIONS_BUTTON).first().click({ force: true });
cy.get(FLYOUT_OVERVIEW_TAB_BLOCKS_TIMELINE_BUTTON).should('exist').first().click();
recurse(
() => {
cy.get(FLYOUT_BLOCK_MORE_ACTIONS_BUTTON).first().click({ force: true });
cy.get(FLYOUT_OVERVIEW_TAB_BLOCKS_TIMELINE_BUTTON).first().click();
cy.get(FLYOUT_BLOCK_MORE_ACTIONS_BUTTON).first().click({ force: true });
return cy.get(ADDED_TO_TIMELINE_TOAST).should(Cypress._.noop);
},
($el) => !!$el.length
);
};
/**

View file

@ -5,16 +5,12 @@
"license": "Elastic License 2.0",
"scripts": {
"cypress": "../../../node_modules/.bin/cypress",
"cypress:open": "yarn cypress open --config-file ./cypress/cypress.config.ts",
"cypress:open-as-ci": "node ../../../scripts/functional_tests --config ../../test/threat_intelligence_cypress/visual_config.ts",
"cypress:open": "TZ=UTC node ../security_solution/scripts/start_cypress_parallel open --spec './cypress/e2e/**/*.cy.ts' --config-file ./cypress/cypress.config.ts --ftr-config-file ../../../../../../x-pack/test/threat_intelligence_cypress/cli_config_parallel",
"cypress:run": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/**/*.cy.ts'; status=$?; yarn junit:merge && exit $status",
"cypress:run:spec": "yarn cypress:run:reporter --browser chrome --spec ${SPEC_LIST:-'./cypress/e2e/**/*.cy.ts'}; status=$?; yarn junit:merge && exit $status",
"cypress:run:cases": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/cases/*.cy.ts'; status=$?; yarn junit:merge && exit $status",
"cypress:run:firefox": "yarn cypress:run:reporter --browser firefox --spec './cypress/e2e/**/*.cy.ts'; status=$?; yarn junit:merge && exit $status",
"cypress:run:reporter": "yarn cypress run --config-file ./cypress/cypress.config.ts --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json",
"cypress:run:respops": "yarn cypress:run:reporter --browser chrome --spec ./cypress/e2e/detection_alerts/*.cy.ts,./cypress/e2e/detection_rules/*.cy.ts,./cypress/e2e/exceptions/*.cy.ts; status=$?; yarn junit:merge && exit $status",
"cypress:run-as-ci": "node --max-old-space-size=2048 ../../../scripts/functional_tests --config ../../test/threat_intelligence_cypress/cli_config_parallel.ts",
"cypress:run-as-ci:firefox": "node --max-old-space-size=2048 ../../../scripts/functional_tests --config ../../test/threat_intelligence_cypress/config.firefox.ts",
"cypress:run:cases": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/cases/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cases_cli_config; status=$?; yarn junit:merge && exit $status",
"cypress:run:reporter": "TZ=UTC node ../security_solution/scripts/start_cypress_parallel run --config-file ./cypress/cypress.config.ts --ftr-config-file ../../../../../../x-pack/test/threat_intelligence_cypress/cli_config_parallel --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json",
"cypress:run:respops": "yarn cypress:run:reporter --browser chrome --spec ./cypress/e2e/detection_alerts/*.cy.ts,./cypress/e2e/detection_rules/*.cy.ts,./cypress/e2e/exceptions/*.cy.ts --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/response_ops_cli_config; status=$?; yarn junit:merge && exit $status",
"junit:merge": "../../../node_modules/.bin/mochawesome-merge ../../../target/kibana-threat-intelligence/cypress/results/mochawesome*.json > ../../../target/kibana-threat-intelligence/cypress/results/output.json && ../../../node_modules/.bin/marge ../../../target/kibana-threat-intelligence/cypress/results/output.json --reportDir ../../../target/kibana-threat-intelligence/cypress/results && mkdir -p ../../../target/junit && cp ../../../target/kibana-threat-intelligence/cypress/results/*.xml ../../../target/junit/"
}
}

View file

@ -8,10 +8,7 @@
import { FtrConfigProviderContext } from '@kbn/test';
import { FtrProviderContext } from './ftr_provider_context';
import { ThreatIntelligenceCypressCliTestRunnerCI } from './runner';
const cliNumber = parseInt(process.env.CLI_NUMBER ?? '1', 10);
const cliCount = parseInt(process.env.CLI_COUNT ?? '1', 10);
import { ThreatIntelligenceConfigurableCypressTestRunner } from './runner';
// eslint-disable-next-line import/no-default-export
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
@ -20,6 +17,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
...securitySolutionCypressConfig.getAll(),
testRunner: (context: FtrProviderContext) =>
ThreatIntelligenceCypressCliTestRunnerCI(context, cliCount, cliNumber),
ThreatIntelligenceConfigurableCypressTestRunner(context),
};
}

View file

@ -5,9 +5,7 @@
* 2.0.
*/
import { chunk } from 'lodash';
import { resolve } from 'path';
import globby from 'globby';
import Url from 'url';
@ -21,17 +19,8 @@ import { tiAbusechMalware } from './pipelines/ti_abusech_malware';
import { tiAbusechMalwareBazaar } from './pipelines/ti_abusech_malware_bazaar';
import { tiAbusechUrl } from './pipelines/ti_abusech_url';
const retrieveIntegrations = (chunksTotal: number, chunkIndex: number) => {
const pattern = resolve(__dirname, '../../plugins/threat_intelligence/cypress/e2e/**/*.cy.ts');
const integrationsPaths = globby.sync(pattern);
const chunkSize = Math.ceil(integrationsPaths.length / chunksTotal);
return chunk(integrationsPaths, chunkSize)[chunkIndex - 1] || [];
};
export async function ThreatIntelligenceConfigurableCypressTestRunner(
{ getService }: FtrProviderContext,
command: string,
envVars?: Record<string, string>
) {
const log = getService('log');
@ -55,56 +44,19 @@ export async function ThreatIntelligenceConfigurableCypressTestRunner(
log.info(`PUT pipeline ${pipeline.name}: ${res.statusCode}`);
}
await withProcRunner(log, async (procs) => {
await procs.run('cypress', {
cmd: 'yarn',
args: [command],
cwd: resolve(__dirname, '../../plugins/threat_intelligence'),
env: {
FORCE_COLOR: '1',
CYPRESS_BASE_URL: Url.format(config.get('servers.kibana')),
CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')),
CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'),
CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'),
...process.env,
...envVars,
},
wait: true,
});
});
}
export async function ThreatIntelligenceCypressCliTestRunnerCI(
context: FtrProviderContext,
totalCiJobs: number,
ciJobNumber: number
) {
const integrations = retrieveIntegrations(totalCiJobs, ciJobNumber);
return ThreatIntelligenceConfigurableCypressTestRunner(context, 'cypress:run:spec', {
SPEC_LIST: integrations.join(','),
});
}
export async function ThreatIntelligenceCypressCliResponseOpsTestRunner(
context: FtrProviderContext
) {
return ThreatIntelligenceConfigurableCypressTestRunner(context, 'cypress:run:respops');
}
export async function ThreatIntelligenceCypressCliCasesTestRunner(context: FtrProviderContext) {
return ThreatIntelligenceConfigurableCypressTestRunner(context, 'cypress:run:cases');
}
export async function ThreatIntelligenceCypressCliTestRunner(context: FtrProviderContext) {
return ThreatIntelligenceConfigurableCypressTestRunner(context, 'cypress:run');
}
export async function ThreatIntelligenceCypressCliFirefoxTestRunner(context: FtrProviderContext) {
return ThreatIntelligenceConfigurableCypressTestRunner(context, 'cypress:run:firefox');
}
export async function ThreatIntelligenceCypressVisualTestRunner(context: FtrProviderContext) {
return ThreatIntelligenceConfigurableCypressTestRunner(context, 'cypress:open');
return {
FORCE_COLOR: '1',
CYPRESS_BASE_URL: Url.format(config.get('servers.kibana')),
CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')),
CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'),
CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'),
...envVars,
baseUrl: Url.format(config.get('servers.kibana')),
BASE_URL: Url.format(config.get('servers.kibana')),
ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')),
ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'),
ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'),
};
}
export async function ThreatIntelligenceCypressCcsTestRunner({ getService }: FtrProviderContext) {

View file

@ -1,19 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrConfigProviderContext } from '@kbn/test';
import { ThreatIntelligenceCypressVisualTestRunner } from './runner';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const tiCypressConfig = await readConfigFile(require.resolve('./config.ts'));
return {
...tiCypressConfig.getAll(),
testRunner: ThreatIntelligenceCypressVisualTestRunner,
};
}