mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Defend workflows] Add Cypress tests for Endpoint reassignment functi… (#151887)
This commit is contained in:
parent
b907c51e0e
commit
e7e66a8944
21 changed files with 330 additions and 124 deletions
|
@ -51,6 +51,8 @@ export function defineCypressConfig(options?: Cypress.ConfigOptions<any>) {
|
|||
|
||||
on(event, task);
|
||||
}, config);
|
||||
|
||||
return config;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -5,15 +5,91 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Agent } from '@kbn/fleet-plugin/common';
|
||||
import { ENDPOINT_VM_NAME } from '../../tasks/common';
|
||||
import {
|
||||
getAgentByHostName,
|
||||
getEndpointIntegrationVersion,
|
||||
reassignAgentPolicy,
|
||||
} from '../../tasks/fleet';
|
||||
import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
|
||||
import { getEndpointListPath } from '../../../common/routing';
|
||||
import { login } from '../../tasks/login';
|
||||
import {
|
||||
AGENT_HOSTNAME_CELL,
|
||||
TABLE_ROW_ACTIONS,
|
||||
TABLE_ROW_ACTIONS_MENU,
|
||||
AGENT_POLICY_CELL,
|
||||
} from '../../screens/endpoints';
|
||||
import {
|
||||
FLEET_REASSIGN_POLICY_MODAL,
|
||||
FLEET_REASSIGN_POLICY_MODAL_CONFIRM_BUTTON,
|
||||
} from '../../screens/fleet';
|
||||
|
||||
describe('Endpoints page', () => {
|
||||
const endpointHostname = Cypress.env(ENDPOINT_VM_NAME);
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
});
|
||||
|
||||
it('Loads the endpoints page', () => {
|
||||
cy.visit('/app/security/administration/endpoints');
|
||||
it('Shows endpoint on the list', () => {
|
||||
cy.visit(getEndpointListPath({ name: 'endpointList' }));
|
||||
cy.contains('Hosts running Elastic Defend').should('exist');
|
||||
cy.getByTestSubj(AGENT_HOSTNAME_CELL).should('have.text', endpointHostname);
|
||||
});
|
||||
|
||||
describe('Endpoint reassignment', () => {
|
||||
let response: IndexedFleetEndpointPolicyResponse;
|
||||
let initialAgentData: Agent;
|
||||
|
||||
before(() => {
|
||||
getAgentByHostName(endpointHostname).then((agentData) => {
|
||||
initialAgentData = agentData;
|
||||
});
|
||||
getEndpointIntegrationVersion().then((version) => {
|
||||
cy.task<IndexedFleetEndpointPolicyResponse>('indexFleetEndpointPolicy', {
|
||||
policyName: `Reassign ${Math.random().toString(36).substr(2, 5)}`,
|
||||
endpointPackageVersion: version,
|
||||
}).then((data) => {
|
||||
response = data;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
});
|
||||
|
||||
after(() => {
|
||||
if (initialAgentData?.policy_id) {
|
||||
reassignAgentPolicy(initialAgentData.id, initialAgentData.policy_id);
|
||||
}
|
||||
if (response) {
|
||||
cy.task('deleteIndexedFleetEndpointPolicies', response);
|
||||
}
|
||||
});
|
||||
|
||||
it('User can reassign a single endpoint to a different Agent Configuration', () => {
|
||||
cy.visit(getEndpointListPath({ name: 'endpointList' }));
|
||||
const hostname = cy
|
||||
.getByTestSubj(AGENT_HOSTNAME_CELL)
|
||||
.filter(`:contains("${endpointHostname}")`);
|
||||
const tableRow = hostname.parents('tr');
|
||||
tableRow.getByTestSubj(TABLE_ROW_ACTIONS).click();
|
||||
cy.getByTestSubj(TABLE_ROW_ACTIONS_MENU).contains('Reassign agent policy').click();
|
||||
cy.getByTestSubj(FLEET_REASSIGN_POLICY_MODAL)
|
||||
.find('select')
|
||||
.select(response.agentPolicies[0].name);
|
||||
cy.getByTestSubj(FLEET_REASSIGN_POLICY_MODAL_CONFIRM_BUTTON).click();
|
||||
cy.getByTestSubj(AGENT_HOSTNAME_CELL)
|
||||
.filter(`:contains("${endpointHostname}")`)
|
||||
.should('exist');
|
||||
cy.getByTestSubj(AGENT_HOSTNAME_CELL)
|
||||
.filter(`:contains("${endpointHostname}")`)
|
||||
.parents('tr')
|
||||
.getByTestSubj(AGENT_POLICY_CELL)
|
||||
.should('have.text', response.agentPolicies[0].name);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -64,8 +64,8 @@ const visitArtifactTab = (tabId: string) => {
|
|||
|
||||
const visitPolicyDetailsPage = () => {
|
||||
cy.visit('/app/security/administration/policy');
|
||||
cy.getBySel('policyNameCellLink').eq(0).click({ force: true });
|
||||
cy.getBySel('policyDetailsPage').should('exist');
|
||||
cy.getByTestSubj('policyNameCellLink').eq(0).click({ force: true });
|
||||
cy.getByTestSubj('policyDetailsPage').should('exist');
|
||||
cy.get('#settings').should('exist'); // waiting for Policy Settings tab
|
||||
};
|
||||
|
||||
|
@ -99,18 +99,18 @@ describe('Artifact tabs in Policy Details page', () => {
|
|||
loginWithPrivilegeRead(testData.privilegePrefix);
|
||||
visitArtifactTab(testData.tabId);
|
||||
|
||||
cy.getBySel('policy-artifacts-empty-unexisting').should('exist');
|
||||
cy.getByTestSubj('policy-artifacts-empty-unexisting').should('exist');
|
||||
|
||||
cy.getBySel('unexisting-manage-artifacts-button').should('not.exist');
|
||||
cy.getByTestSubj('unexisting-manage-artifacts-button').should('not.exist');
|
||||
});
|
||||
|
||||
it(`[ALL] User can add ${testData.title} artifact`, () => {
|
||||
loginWithPrivilegeAll();
|
||||
visitArtifactTab(testData.tabId);
|
||||
|
||||
cy.getBySel('policy-artifacts-empty-unexisting').should('exist');
|
||||
cy.getByTestSubj('policy-artifacts-empty-unexisting').should('exist');
|
||||
|
||||
cy.getBySel('unexisting-manage-artifacts-button').should('exist').click();
|
||||
cy.getByTestSubj('unexisting-manage-artifacts-button').should('exist').click();
|
||||
|
||||
const { formActions, checkResults } = testData.create;
|
||||
|
||||
|
@ -118,18 +118,18 @@ describe('Artifact tabs in Policy Details page', () => {
|
|||
|
||||
// Add a per policy artifact - but not assign it to any policy
|
||||
cy.get('[data-test-subj$="-perPolicy"]').click(); // test-subjects are generated in different formats, but all ends with -perPolicy
|
||||
cy.getBySel(`${testData.pagePrefix}-flyout-submitButton`).click();
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-flyout-submitButton`).click();
|
||||
|
||||
// Check new artifact is in the list
|
||||
for (const checkResult of checkResults) {
|
||||
cy.getBySel(checkResult.selector).should('have.text', checkResult.value);
|
||||
cy.getByTestSubj(checkResult.selector).should('have.text', checkResult.value);
|
||||
}
|
||||
|
||||
cy.getBySel('policyDetailsPage').should('not.exist');
|
||||
cy.getBySel('backToOrigin').contains(/^Back to .+ policy$/);
|
||||
cy.getByTestSubj('policyDetailsPage').should('not.exist');
|
||||
cy.getByTestSubj('backToOrigin').contains(/^Back to .+ policy$/);
|
||||
|
||||
cy.getBySel('backToOrigin').click();
|
||||
cy.getBySel('policyDetailsPage').should('exist');
|
||||
cy.getByTestSubj('backToOrigin').click();
|
||||
cy.getByTestSubj('policyDetailsPage').should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -144,34 +144,34 @@ describe('Artifact tabs in Policy Details page', () => {
|
|||
loginWithPrivilegeRead(testData.privilegePrefix);
|
||||
visitArtifactTab(testData.tabId);
|
||||
|
||||
cy.getBySel('policy-artifacts-empty-unassigned').should('exist');
|
||||
cy.getByTestSubj('policy-artifacts-empty-unassigned').should('exist');
|
||||
|
||||
cy.getBySel('unassigned-manage-artifacts-button').should('not.exist');
|
||||
cy.getBySel('unassigned-assign-artifacts-button').should('not.exist');
|
||||
cy.getByTestSubj('unassigned-manage-artifacts-button').should('not.exist');
|
||||
cy.getByTestSubj('unassigned-assign-artifacts-button').should('not.exist');
|
||||
});
|
||||
|
||||
it(`[ALL] User can Manage and Assign ${testData.title} artifacts`, () => {
|
||||
loginWithPrivilegeAll();
|
||||
visitArtifactTab(testData.tabId);
|
||||
|
||||
cy.getBySel('policy-artifacts-empty-unassigned').should('exist');
|
||||
cy.getByTestSubj('policy-artifacts-empty-unassigned').should('exist');
|
||||
|
||||
// Manage artifacts
|
||||
cy.getBySel('unassigned-manage-artifacts-button').should('exist').click();
|
||||
cy.getByTestSubj('unassigned-manage-artifacts-button').should('exist').click();
|
||||
cy.location('pathname').should(
|
||||
'equal',
|
||||
`/app/security/administration/${testData.urlPath}`
|
||||
);
|
||||
cy.getBySel('backToOrigin').click();
|
||||
cy.getByTestSubj('backToOrigin').click();
|
||||
|
||||
// Assign artifacts
|
||||
cy.getBySel('unassigned-assign-artifacts-button').should('exist').click();
|
||||
cy.getByTestSubj('unassigned-assign-artifacts-button').should('exist').click();
|
||||
|
||||
cy.getBySel('artifacts-assign-flyout').should('exist');
|
||||
cy.getBySel('artifacts-assign-confirm-button').should('be.disabled');
|
||||
cy.getByTestSubj('artifacts-assign-flyout').should('exist');
|
||||
cy.getByTestSubj('artifacts-assign-confirm-button').should('be.disabled');
|
||||
|
||||
cy.getBySel(`${testData.artifactName}_checkbox`).click();
|
||||
cy.getBySel('artifacts-assign-confirm-button').click();
|
||||
cy.getByTestSubj(`${testData.artifactName}_checkbox`).click();
|
||||
cy.getByTestSubj('artifacts-assign-confirm-button').click();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -189,17 +189,17 @@ describe('Artifact tabs in Policy Details page', () => {
|
|||
visitArtifactTab(testData.tabId);
|
||||
|
||||
// List of artifacts
|
||||
cy.getBySel('artifacts-collapsed-list-card').should('have.length', 1);
|
||||
cy.getBySel('artifacts-collapsed-list-card-header-titleHolder').contains(
|
||||
cy.getByTestSubj('artifacts-collapsed-list-card').should('have.length', 1);
|
||||
cy.getByTestSubj('artifacts-collapsed-list-card-header-titleHolder').contains(
|
||||
testData.artifactName
|
||||
);
|
||||
|
||||
// Cannot assign artifacts
|
||||
cy.getBySel('artifacts-assign-button').should('not.exist');
|
||||
cy.getByTestSubj('artifacts-assign-button').should('not.exist');
|
||||
|
||||
// Cannot remove from policy
|
||||
cy.getBySel('artifacts-collapsed-list-card-header-actions-button').click();
|
||||
cy.getBySel('remove-from-policy-action').should('not.exist');
|
||||
cy.getByTestSubj('artifacts-collapsed-list-card-header-actions-button').click();
|
||||
cy.getByTestSubj('remove-from-policy-action').should('not.exist');
|
||||
});
|
||||
|
||||
it(`[ALL] User can see ${testData.title} artifacts and can assign or remove artifacts from policy`, () => {
|
||||
|
@ -207,20 +207,20 @@ describe('Artifact tabs in Policy Details page', () => {
|
|||
visitArtifactTab(testData.tabId);
|
||||
|
||||
// List of artifacts
|
||||
cy.getBySel('artifacts-collapsed-list-card').should('have.length', 1);
|
||||
cy.getBySel('artifacts-collapsed-list-card-header-titleHolder').contains(
|
||||
cy.getByTestSubj('artifacts-collapsed-list-card').should('have.length', 1);
|
||||
cy.getByTestSubj('artifacts-collapsed-list-card-header-titleHolder').contains(
|
||||
testData.artifactName
|
||||
);
|
||||
|
||||
// Assign artifacts
|
||||
cy.getBySel('artifacts-assign-button').should('exist').click();
|
||||
cy.getBySel('artifacts-assign-flyout').should('exist');
|
||||
cy.getBySel('artifacts-assign-cancel-button').click();
|
||||
cy.getByTestSubj('artifacts-assign-button').should('exist').click();
|
||||
cy.getByTestSubj('artifacts-assign-flyout').should('exist');
|
||||
cy.getByTestSubj('artifacts-assign-cancel-button').click();
|
||||
|
||||
// Remove from policy
|
||||
cy.getBySel('artifacts-collapsed-list-card-header-actions-button').click();
|
||||
cy.getBySel('remove-from-policy-action').click();
|
||||
cy.getBySel('confirmModalConfirmButton').click();
|
||||
cy.getByTestSubj('artifacts-collapsed-list-card-header-actions-button').click();
|
||||
cy.getByTestSubj('remove-from-policy-action').click();
|
||||
cy.getByTestSubj('confirmModalConfirmButton').click();
|
||||
|
||||
cy.contains('Successfully removed');
|
||||
});
|
||||
|
|
|
@ -51,10 +51,10 @@ describe('Artifacts pages', () => {
|
|||
describe(`When on the ${testData.title} entries list`, () => {
|
||||
it(`no access - should show no privileges callout`, () => {
|
||||
loginWithoutAccess(`/app/security/administration/${testData.urlPath}`);
|
||||
cy.getBySel('noPrivilegesPage').should('exist');
|
||||
cy.getBySel('empty-page-feature-action').should('exist');
|
||||
cy.getBySel(testData.emptyState).should('not.exist');
|
||||
cy.getBySel(`${testData.pagePrefix}-emptyState-addButton`).should('not.exist');
|
||||
cy.getByTestSubj('noPrivilegesPage').should('exist');
|
||||
cy.getByTestSubj('empty-page-feature-action').should('exist');
|
||||
cy.getByTestSubj(testData.emptyState).should('not.exist');
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-emptyState-addButton`).should('not.exist');
|
||||
});
|
||||
|
||||
it(`read - should show empty state page if there is no ${testData.title} entry and the add button does not exist`, () => {
|
||||
|
@ -62,33 +62,33 @@ describe('Artifacts pages', () => {
|
|||
testData.privilegePrefix,
|
||||
`/app/security/administration/${testData.urlPath}`
|
||||
);
|
||||
cy.getBySel(testData.emptyState).should('exist');
|
||||
cy.getBySel(`${testData.pagePrefix}-emptyState-addButton`).should('not.exist');
|
||||
cy.getByTestSubj(testData.emptyState).should('exist');
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-emptyState-addButton`).should('not.exist');
|
||||
});
|
||||
|
||||
it(`write - should show empty state page if there is no ${testData.title} entry and the add button exists`, () => {
|
||||
loginWithWriteAccess(`/app/security/administration/${testData.urlPath}`);
|
||||
cy.getBySel(testData.emptyState).should('exist');
|
||||
cy.getBySel(`${testData.pagePrefix}-emptyState-addButton`).should('exist');
|
||||
cy.getByTestSubj(testData.emptyState).should('exist');
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-emptyState-addButton`).should('exist');
|
||||
});
|
||||
|
||||
it(`write - should create new ${testData.title} entry`, () => {
|
||||
loginWithWriteAccess(`/app/security/administration/${testData.urlPath}`);
|
||||
// Opens add flyout
|
||||
cy.getBySel(`${testData.pagePrefix}-emptyState-addButton`).click();
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-emptyState-addButton`).click();
|
||||
|
||||
performUserActions(testData.create.formActions);
|
||||
|
||||
// Submit create artifact form
|
||||
cy.getBySel(`${testData.pagePrefix}-flyout-submitButton`).click();
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-flyout-submitButton`).click();
|
||||
|
||||
// Check new artifact is in the list
|
||||
for (const checkResult of testData.create.checkResults) {
|
||||
cy.getBySel(checkResult.selector).should('have.text', checkResult.value);
|
||||
cy.getByTestSubj(checkResult.selector).should('have.text', checkResult.value);
|
||||
}
|
||||
|
||||
// Title is shown after adding an item
|
||||
cy.getBySel('header-page-title').contains(testData.title);
|
||||
cy.getByTestSubj('header-page-title').contains(testData.title);
|
||||
});
|
||||
|
||||
it(`read - should not be able to update/delete an existing ${testData.title} entry`, () => {
|
||||
|
@ -96,10 +96,10 @@ describe('Artifacts pages', () => {
|
|||
testData.privilegePrefix,
|
||||
`/app/security/administration/${testData.urlPath}`
|
||||
);
|
||||
cy.getBySel('header-page-title').contains(testData.title);
|
||||
cy.getBySel(`${testData.pagePrefix}-card-header-actions-button`).should('not.exist');
|
||||
cy.getBySel(`${testData.pagePrefix}-card-cardEditAction`).should('not.exist');
|
||||
cy.getBySel(`${testData.pagePrefix}-card-cardDeleteAction`).should('not.exist');
|
||||
cy.getByTestSubj('header-page-title').contains(testData.title);
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-card-header-actions-button`).should('not.exist');
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-card-cardEditAction`).should('not.exist');
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-card-cardDeleteAction`).should('not.exist');
|
||||
});
|
||||
|
||||
it(`read - should not be able to create a new ${testData.title} entry`, () => {
|
||||
|
@ -107,39 +107,39 @@ describe('Artifacts pages', () => {
|
|||
testData.privilegePrefix,
|
||||
`/app/security/administration/${testData.urlPath}`
|
||||
);
|
||||
cy.getBySel('header-page-title').contains(testData.title);
|
||||
cy.getBySel(`${testData.pagePrefix}-pageAddButton`).should('not.exist');
|
||||
cy.getByTestSubj('header-page-title').contains(testData.title);
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-pageAddButton`).should('not.exist');
|
||||
});
|
||||
|
||||
it(`write - should be able to update an existing ${testData.title} entry`, () => {
|
||||
loginWithWriteAccess(`/app/security/administration/${testData.urlPath}`);
|
||||
// Opens edit flyout
|
||||
cy.getBySel(`${testData.pagePrefix}-card-header-actions-button`).click();
|
||||
cy.getBySel(`${testData.pagePrefix}-card-cardEditAction`).click();
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-card-header-actions-button`).click();
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-card-cardEditAction`).click();
|
||||
|
||||
performUserActions(testData.update.formActions);
|
||||
|
||||
// Submit edit artifact form
|
||||
cy.getBySel(`${testData.pagePrefix}-flyout-submitButton`).click();
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-flyout-submitButton`).click();
|
||||
|
||||
for (const checkResult of testData.create.checkResults) {
|
||||
cy.getBySel(checkResult.selector).should('have.text', checkResult.value);
|
||||
cy.getByTestSubj(checkResult.selector).should('have.text', checkResult.value);
|
||||
}
|
||||
|
||||
// Title still shown after editing an item
|
||||
cy.getBySel('header-page-title').contains(testData.title);
|
||||
cy.getByTestSubj('header-page-title').contains(testData.title);
|
||||
});
|
||||
|
||||
it(`write - should be able to delete the existing ${testData.title} entry`, () => {
|
||||
loginWithWriteAccess(`/app/security/administration/${testData.urlPath}`);
|
||||
// Remove it
|
||||
cy.getBySel(`${testData.pagePrefix}-card-header-actions-button`).click();
|
||||
cy.getBySel(`${testData.pagePrefix}-card-cardDeleteAction`).click();
|
||||
cy.getBySel(`${testData.pagePrefix}-deleteModal-submitButton`).click();
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-card-header-actions-button`).click();
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-card-cardDeleteAction`).click();
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-deleteModal-submitButton`).click();
|
||||
// No card visible after removing it
|
||||
cy.getBySel(testData.delete.card).should('not.exist');
|
||||
cy.getByTestSubj(testData.delete.card).should('not.exist');
|
||||
// Empty state is displayed after removing last item
|
||||
cy.getBySel(testData.emptyState).should('exist');
|
||||
cy.getByTestSubj(testData.emptyState).should('exist');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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 AGENT_HOSTNAME_CELL = 'hostnameCellLink';
|
||||
export const AGENT_POLICY_CELL = 'policyNameCellLink';
|
||||
export const TABLE_ROW_ACTIONS = 'endpointTableRowActions';
|
||||
export const TABLE_ROW_ACTIONS_MENU = 'tableRowActionsMenuPanel';
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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 FLEET_REASSIGN_POLICY_MODAL = 'agentReassignPolicyModal';
|
||||
export const FLEET_REASSIGN_POLICY_MODAL_CONFIRM_BUTTON = 'confirmModalConfirmButton';
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// / <reference types="cypress" />
|
||||
|
||||
import { createKbnClient } from '../../../../scripts/endpoint/common/stack_services';
|
||||
import type { IndexedFleetEndpointPolicyResponse } from '../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
|
||||
import {
|
||||
indexFleetEndpointPolicy,
|
||||
deleteIndexedFleetEndpointPolicies,
|
||||
} from '../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
|
||||
|
||||
export const dataLoaders = (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => {
|
||||
const kbnClient = createKbnClient({
|
||||
url: config.env.KIBANA_URL,
|
||||
username: config.env.ELASTICSEARCH_USERNAME,
|
||||
password: config.env.ELASTICSEARCH_PASSWORD,
|
||||
});
|
||||
|
||||
on('task', {
|
||||
indexFleetEndpointPolicy: async ({
|
||||
policyName,
|
||||
endpointPackageVersion,
|
||||
}: {
|
||||
policyName: string;
|
||||
endpointPackageVersion: string;
|
||||
}) => {
|
||||
return indexFleetEndpointPolicy(kbnClient, policyName, endpointPackageVersion);
|
||||
},
|
||||
deleteIndexedFleetEndpointPolicies: async (indexData: IndexedFleetEndpointPolicyResponse) => {
|
||||
return deleteIndexedFleetEndpointPolicies(kbnClient, indexData);
|
||||
},
|
||||
});
|
||||
};
|
|
@ -31,13 +31,17 @@ declare global {
|
|||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
getBySel(...args: Parameters<Cypress.Chainable['get']>): Chainable<JQuery<HTMLElement>>;
|
||||
getByTestSubj(...args: Parameters<Cypress.Chainable['get']>): Chainable<JQuery<HTMLElement>>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cypress.Commands.add('getBySel', (selector, ...args) =>
|
||||
cy.get(`[data-test-subj="${selector}"]`, ...args)
|
||||
);
|
||||
Cypress.Commands.addQuery('getByTestSubj', function getByTestSubj(selector, options) {
|
||||
const getFn = cy.now('get', `[data-test-subj="${selector}"]`, options) as (
|
||||
subject: Cypress.Chainable<JQuery<HTMLElement>>
|
||||
) => Cypress.Chainable<JQuery<HTMLElement>>;
|
||||
|
||||
return (subject) => getFn(subject);
|
||||
});
|
||||
|
||||
Cypress.on('uncaught:exception', () => false);
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
*/
|
||||
|
||||
import { PACKAGE_POLICY_API_ROOT } from '@kbn/fleet-plugin/common';
|
||||
import type {
|
||||
ExceptionListItemSchema,
|
||||
ExceptionListSchema,
|
||||
} from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import {
|
||||
ENDPOINT_ARTIFACT_LISTS,
|
||||
|
@ -13,8 +17,7 @@ import {
|
|||
EXCEPTION_LIST_ITEM_URL,
|
||||
EXCEPTION_LIST_URL,
|
||||
} from '@kbn/securitysolution-list-constants';
|
||||
|
||||
const API_HEADER = { 'kbn-xsrf': 'kibana' };
|
||||
import { request } from './common';
|
||||
|
||||
export const removeAllArtifacts = () => {
|
||||
for (const listId of ENDPOINT_ARTIFACT_LIST_IDS) {
|
||||
|
@ -23,10 +26,9 @@ export const removeAllArtifacts = () => {
|
|||
};
|
||||
|
||||
export const removeExceptionsList = (listId: string) => {
|
||||
cy.request({
|
||||
request({
|
||||
method: 'DELETE',
|
||||
url: `${EXCEPTION_LIST_URL}?list_id=${listId}&namespace_type=agnostic`,
|
||||
headers: API_HEADER,
|
||||
failOnStatusCode: false,
|
||||
}).then(({ status }) => {
|
||||
expect(status).to.be.oneOf([200, 404]); // should either be success or not found
|
||||
|
@ -42,10 +44,9 @@ const ENDPOINT_ARTIFACT_LIST_TYPES = {
|
|||
};
|
||||
|
||||
export const createArtifactList = (listId: string) => {
|
||||
cy.request({
|
||||
request<ExceptionListSchema>({
|
||||
method: 'POST',
|
||||
url: EXCEPTION_LIST_URL,
|
||||
headers: API_HEADER,
|
||||
body: {
|
||||
name: listId,
|
||||
description: 'This is a test list',
|
||||
|
@ -61,11 +62,9 @@ export const createArtifactList = (listId: string) => {
|
|||
};
|
||||
|
||||
export const createPerPolicyArtifact = (name: string, body: object, policyId?: 'all' | string) => {
|
||||
cy.request({
|
||||
request<ExceptionListItemSchema>({
|
||||
method: 'POST',
|
||||
url: EXCEPTION_LIST_ITEM_URL,
|
||||
|
||||
headers: API_HEADER,
|
||||
body: {
|
||||
name,
|
||||
description: '',
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 ENDPOINT_VM_NAME = 'ENDPOINT_VM_NAME';
|
||||
|
||||
export const API_AUTH = {
|
||||
user: Cypress.env('ELASTICSEARCH_USERNAME'),
|
||||
pass: Cypress.env('ELASTICSEARCH_PASSWORD'),
|
||||
};
|
||||
|
||||
export const API_HEADERS = { 'kbn-xsrf': 'cypress' };
|
||||
|
||||
export const request = <T = unknown>(
|
||||
options: Partial<Cypress.RequestOptions>
|
||||
): Cypress.Chainable<Cypress.Response<T>> =>
|
||||
cy.request<T>({
|
||||
auth: API_AUTH,
|
||||
headers: API_HEADERS,
|
||||
...options,
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 type { Agent, GetAgentsResponse, GetInfoResponse } from '@kbn/fleet-plugin/common';
|
||||
import { agentRouteService, epmRouteService } from '@kbn/fleet-plugin/common';
|
||||
import type { PutAgentReassignResponse } from '@kbn/fleet-plugin/common/types';
|
||||
import { request } from './common';
|
||||
|
||||
export const getEndpointIntegrationVersion = (): Cypress.Chainable<string> =>
|
||||
request<GetInfoResponse>({
|
||||
url: epmRouteService.getInfoPath('endpoint'),
|
||||
method: 'GET',
|
||||
}).then((response) => response.body.item.version);
|
||||
|
||||
export const getAgentByHostName = (hostname: string): Cypress.Chainable<Agent> =>
|
||||
request<GetAgentsResponse>({
|
||||
url: agentRouteService.getListPath(),
|
||||
method: 'GET',
|
||||
qs: {
|
||||
kuery: `local_metadata.host.hostname: "${hostname}"`,
|
||||
},
|
||||
}).then((response) => response.body.items[0]);
|
||||
|
||||
export const reassignAgentPolicy = (
|
||||
agentId: string,
|
||||
agentPolicyId: string
|
||||
): Cypress.Chainable<Cypress.Response<PutAgentReassignResponse>> =>
|
||||
request<PutAgentReassignResponse>({
|
||||
url: agentRouteService.getReassignPath(agentId),
|
||||
method: 'PUT',
|
||||
body: {
|
||||
policy_id: agentPolicyId,
|
||||
},
|
||||
});
|
|
@ -8,17 +8,17 @@
|
|||
import { isEmpty } from 'lodash';
|
||||
import { BASE_ENDPOINT_ROUTE } from '../../../../common/endpoint/constants';
|
||||
import { runEndpointLoaderScript } from './run_endpoint_loader';
|
||||
import { request } from './common';
|
||||
|
||||
// Checks for Endpoint data and creates it if needed
|
||||
export const loadEndpointDataForEventFiltersIfNeeded = () => {
|
||||
cy.request({
|
||||
request({
|
||||
method: 'POST',
|
||||
url: `${BASE_ENDPOINT_ROUTE}/suggestions/eventFilters`,
|
||||
body: {
|
||||
field: 'agent.type',
|
||||
query: '',
|
||||
},
|
||||
headers: { 'kbn-xsrf': 'kibana' },
|
||||
failOnStatusCode: false,
|
||||
}).then(({ body }) => {
|
||||
if (isEmpty(body)) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import * as yaml from 'js-yaml';
|
|||
import type { UrlObject } from 'url';
|
||||
import Url from 'url';
|
||||
import type { Role } from '@kbn/security-plugin/common';
|
||||
import { request } from './common';
|
||||
import { getT1Analyst } from '../../../../scripts/endpoint/common/roles_users/t1_analyst';
|
||||
import { getT2Analyst } from '../../../../scripts/endpoint/common/roles_users/t2_analyst';
|
||||
import { getHunter } from '../../../../scripts/endpoint/common/roles_users/hunter';
|
||||
|
@ -79,13 +80,6 @@ const ELASTICSEARCH_PASSWORD = 'ELASTICSEARCH_PASSWORD';
|
|||
*/
|
||||
const LOGIN_API_ENDPOINT = '/internal/security/login';
|
||||
|
||||
const API_AUTH = {
|
||||
user: Cypress.env(ELASTICSEARCH_USERNAME),
|
||||
pass: Cypress.env(ELASTICSEARCH_PASSWORD),
|
||||
};
|
||||
|
||||
const API_HEADERS = { 'kbn-xsrf': 'cypress' };
|
||||
|
||||
/**
|
||||
* cy.visit will default to the baseUrl which uses the default kibana test user
|
||||
* This function will override that functionality in cy.visit by building the baseUrl
|
||||
|
@ -151,52 +145,40 @@ export const createRoleAndUser = (role: ROLE) => {
|
|||
};
|
||||
|
||||
export const createCustomRoleAndUser = (role: string, rolePrivileges: Omit<Role, 'name'>) => {
|
||||
const env = getCurlScriptEnvVars();
|
||||
|
||||
// post the role
|
||||
cy.request({
|
||||
request({
|
||||
method: 'PUT',
|
||||
url: `${env.KIBANA_URL}/api/security/role/${role}`,
|
||||
url: `/api/security/role/${role}`,
|
||||
body: rolePrivileges,
|
||||
headers: API_HEADERS,
|
||||
auth: API_AUTH,
|
||||
});
|
||||
|
||||
// post the user associated with the role to elasticsearch
|
||||
cy.request({
|
||||
request({
|
||||
method: 'POST',
|
||||
url: `${env.KIBANA_URL}/internal/security/users/${role}`,
|
||||
headers: API_HEADERS,
|
||||
url: `/internal/security/users/${role}`,
|
||||
body: {
|
||||
username: role,
|
||||
password: Cypress.env(ELASTICSEARCH_PASSWORD),
|
||||
roles: [role],
|
||||
},
|
||||
auth: API_AUTH,
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteRoleAndUser = (role: ROLE) => {
|
||||
const env = getCurlScriptEnvVars();
|
||||
|
||||
cy.request({
|
||||
request({
|
||||
method: 'DELETE',
|
||||
auth: API_AUTH,
|
||||
headers: API_HEADERS,
|
||||
url: `${env.KIBANA_URL}/internal/security/users/${role}`,
|
||||
url: `/internal/security/users/${role}`,
|
||||
});
|
||||
cy.request({
|
||||
request({
|
||||
method: 'DELETE',
|
||||
auth: API_AUTH,
|
||||
headers: API_HEADERS,
|
||||
url: `${env.KIBANA_URL}/api/security/role/${role}`,
|
||||
url: `/api/security/role/${role}`,
|
||||
});
|
||||
};
|
||||
|
||||
export const loginWithUser = (user: User) => {
|
||||
const url = Cypress.config().baseUrl;
|
||||
|
||||
cy.request({
|
||||
request({
|
||||
body: {
|
||||
providerType: 'basic',
|
||||
providerName: url && !url.includes('localhost') ? 'cloud-basic' : 'basic',
|
||||
|
@ -227,7 +209,7 @@ export const loginWithCustomRole = async (role: string, rolePrivileges: Omit<Rol
|
|||
port: Cypress.env('configport'),
|
||||
} as UrlObject);
|
||||
cy.log(`origin: ${theUrl}`);
|
||||
cy.request({
|
||||
request({
|
||||
body: {
|
||||
providerType: 'basic',
|
||||
providerName: 'basic',
|
||||
|
@ -282,7 +264,7 @@ const loginViaEnvironmentCredentials = () => {
|
|||
);
|
||||
|
||||
// programmatically authenticate without interacting with the Kibana login page
|
||||
cy.request({
|
||||
request({
|
||||
body: {
|
||||
providerType: 'basic',
|
||||
providerName: url && !url.includes('localhost') ? 'cloud-basic' : 'basic',
|
||||
|
@ -313,7 +295,7 @@ const loginViaConfig = () => {
|
|||
const config = yaml.safeLoad(kibanaDevYml);
|
||||
|
||||
// programmatically authenticate without interacting with the Kibana login page
|
||||
cy.request({
|
||||
request({
|
||||
body: {
|
||||
providerType: 'basic',
|
||||
providerName: 'basic',
|
||||
|
|
|
@ -25,7 +25,7 @@ const performAction = (action: FormAction) => {
|
|||
if (action.customSelector) {
|
||||
element = cy.get(action.customSelector);
|
||||
} else {
|
||||
element = cy.getBySel(action.selector || '');
|
||||
element = cy.getByTestSubj(action.selector || '');
|
||||
}
|
||||
|
||||
if (action.type === 'click') {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"extends": "../../../../../../tsconfig.base.json",
|
||||
"include": [
|
||||
"**/*"
|
||||
"**/*",
|
||||
"../cypress_endpoint.config.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
|
@ -26,5 +27,6 @@
|
|||
"@kbn/securitysolution-list-constants",
|
||||
"@kbn/fleet-plugin",
|
||||
"@kbn/securitysolution-io-ts-list-types",
|
||||
"@kbn/cypress-config",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
|
||||
import { defineCypressConfig } from '@kbn/cypress-config';
|
||||
// eslint-disable-next-line @kbn/imports/no_boundary_crossing
|
||||
import { dataLoaders } from './cypress/support/data_loaders';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default defineCypressConfig({
|
||||
|
@ -37,5 +39,8 @@ export default defineCypressConfig({
|
|||
supportFile: 'public/management/cypress/support/e2e.ts',
|
||||
specPattern: 'public/management/cypress/e2e/endpoint/*.cy.{js,jsx,ts,tsx}',
|
||||
experimentalRunAllSpecs: true,
|
||||
setupNodeEvents(on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) {
|
||||
dataLoaders(on, config);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@ import { useUserPrivileges } from '../../../../../common/components/user_privile
|
|||
import { useWithShowEndpointResponder } from '../../../../hooks';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
import { APP_UI_ID } from '../../../../../../common/constants';
|
||||
import { getEndpointDetailsPath } from '../../../../common/routing';
|
||||
import { getEndpointDetailsPath, getEndpointListPath } from '../../../../common/routing';
|
||||
import type { HostMetadata, MaybeImmutable } from '../../../../../../common/endpoint/types';
|
||||
import { useEndpointSelector } from './hooks';
|
||||
import { agentPolicies, uiQueryParams } from '../../store/selectors';
|
||||
|
@ -236,6 +236,12 @@ export const useEndpointActionItems = (
|
|||
agentId: fleetAgentId,
|
||||
})[1]
|
||||
}?openReassignFlyout=true`,
|
||||
state: {
|
||||
onDoneNavigateTo: [
|
||||
APP_UI_ID,
|
||||
{ path: getEndpointListPath({ name: 'endpointList' }) },
|
||||
],
|
||||
},
|
||||
},
|
||||
href: `${getAppUrl({ appId: 'fleet' })}${
|
||||
pagePathGetters.agent_details({
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"exclude": [
|
||||
"target/**/*",
|
||||
"**/cypress/**",
|
||||
"public/management/cypress_endpoint.config.ts",
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/core",
|
||||
|
|
|
@ -21,6 +21,8 @@ export class AgentManager extends Manager {
|
|||
|
||||
public async setup() {
|
||||
this.vmName = await enrollEndpointHost();
|
||||
|
||||
return this.vmName;
|
||||
}
|
||||
|
||||
public cleanup() {
|
||||
|
|
|
@ -14,9 +14,11 @@ import { AgentManager } from './agent';
|
|||
import { FleetManager } from './fleet_server';
|
||||
import { getLatestAvailableAgentVersion } from './utils';
|
||||
|
||||
type RunnerEnv = Record<string, string | undefined>;
|
||||
|
||||
async function withFleetAgent(
|
||||
{ getService }: FtrProviderContext,
|
||||
runner: (runnerEnv: Record<string, string>) => Promise<void>
|
||||
runner: (runnerEnv: RunnerEnv) => Promise<void>
|
||||
) {
|
||||
const log = getService('log');
|
||||
const config = getService('config');
|
||||
|
@ -40,9 +42,9 @@ async function withFleetAgent(
|
|||
const agentManager = new AgentManager(log);
|
||||
|
||||
await fleetManager.setup();
|
||||
await agentManager.setup();
|
||||
const agentVmName = await agentManager.setup();
|
||||
try {
|
||||
await runner({});
|
||||
await runner({ agentVmName });
|
||||
} finally {
|
||||
agentManager.cleanup();
|
||||
fleetManager.cleanup();
|
||||
|
@ -58,10 +60,16 @@ export async function DefendWorkflowsCypressVisualTestRunner(context: FtrProvide
|
|||
}
|
||||
|
||||
export async function DefendWorkflowsCypressEndpointTestRunner(context: FtrProviderContext) {
|
||||
await withFleetAgent(context, () => startDefendWorkflowsCypress(context, 'dw:endpoint:open'));
|
||||
await withFleetAgent(context, (runnerEnv) =>
|
||||
startDefendWorkflowsCypress(context, 'dw:endpoint:open', runnerEnv)
|
||||
);
|
||||
}
|
||||
|
||||
function startDefendWorkflowsCypress(context: FtrProviderContext, cypressCommand: string) {
|
||||
function startDefendWorkflowsCypress(
|
||||
context: FtrProviderContext,
|
||||
cypressCommand: 'dw:endpoint:open' | 'dw:open' | 'dw:run',
|
||||
runnerEnv?: RunnerEnv
|
||||
) {
|
||||
const log = context.getService('log');
|
||||
const config = context.getService('config');
|
||||
return withProcRunner(log, async (procs) => {
|
||||
|
@ -91,6 +99,7 @@ function startDefendWorkflowsCypress(context: FtrProviderContext, cypressCommand
|
|||
hostname: config.get('servers.kibana.hostname'),
|
||||
port: config.get('servers.kibana.port'),
|
||||
}),
|
||||
CYPRESS_ENDPOINT_VM_NAME: runnerEnv?.agentVmName,
|
||||
...process.env,
|
||||
},
|
||||
wait: true,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import axios from 'axios';
|
||||
import semver from 'semver';
|
||||
import { filter } from 'lodash';
|
||||
import { map } from 'lodash';
|
||||
import { KbnClient } from '@kbn/test';
|
||||
|
||||
/**
|
||||
|
@ -20,9 +20,7 @@ export const getLatestAvailableAgentVersion = async (kbnClient: KbnClient): Prom
|
|||
const kbnStatus = await kbnClient.status.get();
|
||||
const agentVersions = await axios
|
||||
.get('https://artifacts-api.elastic.co/v1/versions')
|
||||
.then((response) =>
|
||||
filter(response.data.versions, (versionString) => !versionString.includes('SNAPSHOT'))
|
||||
);
|
||||
.then((response) => map(response.data.versions, (version) => version.split('-SNAPSHOT')[0]));
|
||||
|
||||
let version =
|
||||
semver.maxSatisfying(agentVersions, `<=${kbnStatus.version.number}`) ??
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue