mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
## Summary Adds additional cypress tests and utils around value lists so that the percent of test driven development (TDD) possibilities will be easier for us to accomplish around bug fixes towards the next release. * Changes and adds data test subjects within value based lists * Fixes a bug where the list fixtures were not being converted to base64 before uploads within Cypress * Adds a [CIDR](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) fixture * Adds tests for export, delete, close and open the dialog, and all the value based list types for the current modal. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
b16bb24d8c
commit
05286a6190
9 changed files with 295 additions and 17 deletions
|
@ -0,0 +1 @@
|
|||
192.168.100.14/24
|
|
@ -0,0 +1,3 @@
|
|||
127.0.0.1
|
||||
127.0.0.2
|
||||
127.0.0.3
|
|
@ -17,27 +17,207 @@ import {
|
|||
openValueListsModal,
|
||||
selectValueListsFile,
|
||||
uploadValueList,
|
||||
selectValueListType,
|
||||
deleteAllValueListsFromUI,
|
||||
closeValueListsModal,
|
||||
importValueList,
|
||||
deleteValueListsFile,
|
||||
exportValueList,
|
||||
} from '../tasks/lists';
|
||||
import { VALUE_LISTS_TABLE, VALUE_LISTS_ROW } from '../screens/lists';
|
||||
|
||||
describe('value lists', () => {
|
||||
describe('management modal', () => {
|
||||
it('creates a keyword list from an uploaded file', () => {
|
||||
beforeEach(() => {
|
||||
loginAndWaitForPageWithoutDateRange(DETECTIONS_URL);
|
||||
waitForAlertsPanelToBeLoaded();
|
||||
waitForAlertsIndexToBeCreated();
|
||||
waitForListsIndexToBeCreated();
|
||||
goToManageAlertsDetectionRules();
|
||||
waitForValueListsModalToBeLoaded();
|
||||
openValueListsModal();
|
||||
selectValueListsFile();
|
||||
uploadValueList();
|
||||
});
|
||||
|
||||
cy.get(VALUE_LISTS_TABLE)
|
||||
.find(VALUE_LISTS_ROW)
|
||||
.should(($row) => {
|
||||
expect($row.text()).to.contain('value_list.txt');
|
||||
afterEach(() => {
|
||||
deleteAllValueListsFromUI();
|
||||
});
|
||||
|
||||
it('can open and close the modal', () => {
|
||||
openValueListsModal();
|
||||
closeValueListsModal();
|
||||
});
|
||||
|
||||
describe('create list types', () => {
|
||||
beforeEach(() => {
|
||||
openValueListsModal();
|
||||
});
|
||||
|
||||
it('creates a "keyword" list from an uploaded file', () => {
|
||||
const listName = 'value_list.txt';
|
||||
selectValueListType('keyword');
|
||||
selectValueListsFile(listName);
|
||||
uploadValueList();
|
||||
|
||||
cy.get(VALUE_LISTS_TABLE)
|
||||
.find(VALUE_LISTS_ROW)
|
||||
.should(($row) => {
|
||||
expect($row.text()).to.contain(listName);
|
||||
expect($row.text()).to.contain('Keywords');
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a "text" list from an uploaded file', () => {
|
||||
const listName = 'value_list.txt';
|
||||
selectValueListType('text');
|
||||
selectValueListsFile(listName);
|
||||
uploadValueList();
|
||||
|
||||
cy.get(VALUE_LISTS_TABLE)
|
||||
.find(VALUE_LISTS_ROW)
|
||||
.should(($row) => {
|
||||
expect($row.text()).to.contain(listName);
|
||||
expect($row.text()).to.contain('Text');
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a "ip" list from an uploaded file', () => {
|
||||
const listName = 'ip_list.txt';
|
||||
selectValueListType('ip');
|
||||
selectValueListsFile(listName);
|
||||
uploadValueList();
|
||||
|
||||
cy.get(VALUE_LISTS_TABLE)
|
||||
.find(VALUE_LISTS_ROW)
|
||||
.should(($row) => {
|
||||
expect($row.text()).to.contain(listName);
|
||||
expect($row.text()).to.contain('IP addresses');
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a "ip_range" list from an uploaded file', () => {
|
||||
const listName = 'cidr_list.txt';
|
||||
selectValueListType('ip_range');
|
||||
selectValueListsFile(listName);
|
||||
uploadValueList();
|
||||
|
||||
cy.get(VALUE_LISTS_TABLE)
|
||||
.find(VALUE_LISTS_ROW)
|
||||
.should(($row) => {
|
||||
expect($row.text()).to.contain(listName);
|
||||
expect($row.text()).to.contain('IP ranges');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete list types', () => {
|
||||
it('deletes a "keyword" list from an uploaded file', () => {
|
||||
const listName = 'value_list.txt';
|
||||
importValueList(listName, 'keyword');
|
||||
openValueListsModal();
|
||||
deleteValueListsFile(listName);
|
||||
cy.get(VALUE_LISTS_TABLE)
|
||||
.find(VALUE_LISTS_ROW)
|
||||
.should(($row) => {
|
||||
expect($row.text()).not.to.contain(listName);
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes a "text" list from an uploaded file', () => {
|
||||
const listName = 'value_list.txt';
|
||||
importValueList(listName, 'text');
|
||||
openValueListsModal();
|
||||
deleteValueListsFile(listName);
|
||||
cy.get(VALUE_LISTS_TABLE)
|
||||
.find(VALUE_LISTS_ROW)
|
||||
.should(($row) => {
|
||||
expect($row.text()).not.to.contain(listName);
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes a "ip" from an uploaded file', () => {
|
||||
const listName = 'ip_list.txt';
|
||||
importValueList(listName, 'ip');
|
||||
openValueListsModal();
|
||||
deleteValueListsFile(listName);
|
||||
cy.get(VALUE_LISTS_TABLE)
|
||||
.find(VALUE_LISTS_ROW)
|
||||
.should(($row) => {
|
||||
expect($row.text()).not.to.contain(listName);
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes a "ip_range" from an uploaded file', () => {
|
||||
const listName = 'cidr_list.txt';
|
||||
importValueList(listName, 'ip_range');
|
||||
openValueListsModal();
|
||||
deleteValueListsFile(listName);
|
||||
cy.get(VALUE_LISTS_TABLE)
|
||||
.find(VALUE_LISTS_ROW)
|
||||
.should(($row) => {
|
||||
expect($row.text()).not.to.contain(listName);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('export list types', () => {
|
||||
beforeEach(() => {
|
||||
cy.server();
|
||||
cy.route('POST', '**/api/lists/items/_export?list_id=*').as('exportList');
|
||||
});
|
||||
|
||||
it('exports a "keyword" list from an uploaded file', () => {
|
||||
const listName = 'value_list.txt';
|
||||
importValueList('value_list.txt', 'keyword');
|
||||
openValueListsModal();
|
||||
exportValueList();
|
||||
cy.wait('@exportList').then((xhr) => {
|
||||
cy.fixture(listName).then((list: string) => {
|
||||
const [lineOne, lineTwo] = list.split('\n');
|
||||
expect(xhr.responseBody).to.contain(lineOne);
|
||||
expect(xhr.responseBody).to.contain(lineTwo);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('exports a "text" list from an uploaded file', () => {
|
||||
const listName = 'value_list.txt';
|
||||
importValueList(listName, 'text');
|
||||
openValueListsModal();
|
||||
exportValueList();
|
||||
cy.wait('@exportList').then((xhr) => {
|
||||
cy.fixture(listName).then((list: string) => {
|
||||
const [lineOne, lineTwo] = list.split('\n');
|
||||
expect(xhr.responseBody).to.contain(lineOne);
|
||||
expect(xhr.responseBody).to.contain(lineTwo);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('exports a "ip" list from an uploaded file', () => {
|
||||
const listName = 'ip_list.txt';
|
||||
importValueList(listName, 'ip');
|
||||
openValueListsModal();
|
||||
exportValueList();
|
||||
cy.wait('@exportList').then((xhr) => {
|
||||
cy.fixture(listName).then((list: string) => {
|
||||
const [lineOne, lineTwo] = list.split('\n');
|
||||
expect(xhr.responseBody).to.contain(lineOne);
|
||||
expect(xhr.responseBody).to.contain(lineTwo);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('exports a "ip_range" list from an uploaded file', () => {
|
||||
const listName = 'cidr_list.txt';
|
||||
importValueList(listName, 'ip_range');
|
||||
openValueListsModal();
|
||||
exportValueList();
|
||||
cy.wait('@exportList').then((xhr) => {
|
||||
cy.fixture(listName).then((list: string) => {
|
||||
const [lineOne] = list.split('\n');
|
||||
expect(xhr.responseBody).to.contain(lineOne);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,3 +9,9 @@ export const VALUE_LISTS_TABLE = '[data-test-subj="value-lists-table"]';
|
|||
export const VALUE_LISTS_ROW = '.euiTableRow';
|
||||
export const VALUE_LIST_FILE_PICKER = '[data-test-subj="value-list-file-picker"]';
|
||||
export const VALUE_LIST_FILE_UPLOAD_BUTTON = '[data-test-subj="value-lists-form-import-action"]';
|
||||
export const VALUE_LIST_TYPE_SELECTOR = '[data-test-subj="value-lists-form-select-type-action"]';
|
||||
export const VALUE_LIST_DELETE_BUTTON = (name: string) =>
|
||||
`[data-test-subj="action-delete-value-list-${name}"]`;
|
||||
export const VALUE_LIST_FILES = '[data-test-subj*="action-delete-value-list-"]';
|
||||
export const VALUE_LIST_CLOSE_BUTTON = '[data-test-subj="value-lists-modal-close-action"]';
|
||||
export const VALUE_LIST_EXPORT_BUTTON = '[data-test-subj="action-export-value-list"]';
|
||||
|
|
|
@ -58,7 +58,7 @@ Cypress.Commands.add(
|
|||
},
|
||||
(input, fileName, fileType = 'text/plain') => {
|
||||
cy.fixture(fileName).then((content) => {
|
||||
const blob = Cypress.Blob.base64StringToBlob(content, fileType);
|
||||
const blob = Cypress.Blob.base64StringToBlob(btoa(content), fileType);
|
||||
const testFile = new File([blob], fileName, { type: fileType });
|
||||
const dataTransfer = new DataTransfer();
|
||||
|
||||
|
|
|
@ -6,8 +6,13 @@
|
|||
|
||||
import {
|
||||
VALUE_LISTS_MODAL_ACTIVATOR,
|
||||
VALUE_LIST_CLOSE_BUTTON,
|
||||
VALUE_LIST_DELETE_BUTTON,
|
||||
VALUE_LIST_EXPORT_BUTTON,
|
||||
VALUE_LIST_FILES,
|
||||
VALUE_LIST_FILE_PICKER,
|
||||
VALUE_LIST_FILE_UPLOAD_BUTTON,
|
||||
VALUE_LIST_TYPE_SELECTOR,
|
||||
} from '../screens/lists';
|
||||
|
||||
export const waitForListsIndexToBeCreated = () => {
|
||||
|
@ -23,14 +28,96 @@ export const waitForValueListsModalToBeLoaded = () => {
|
|||
cy.get(VALUE_LISTS_MODAL_ACTIVATOR).should('not.be.disabled');
|
||||
};
|
||||
|
||||
export const openValueListsModal = () => {
|
||||
cy.get(VALUE_LISTS_MODAL_ACTIVATOR).click();
|
||||
export const openValueListsModal = (): Cypress.Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.get(VALUE_LISTS_MODAL_ACTIVATOR).click();
|
||||
};
|
||||
|
||||
export const selectValueListsFile = () => {
|
||||
cy.get(VALUE_LIST_FILE_PICKER).attachFile('value_list.txt').trigger('change', { force: true });
|
||||
export const closeValueListsModal = (): Cypress.Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.get(VALUE_LIST_CLOSE_BUTTON).click();
|
||||
};
|
||||
|
||||
export const uploadValueList = () => {
|
||||
cy.get(VALUE_LIST_FILE_UPLOAD_BUTTON).click();
|
||||
export const selectValueListsFile = (file: string): Cypress.Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.get(VALUE_LIST_FILE_PICKER).attachFile(file).trigger('change', { force: true });
|
||||
};
|
||||
|
||||
export const deleteValueListsFile = (file: string): Cypress.Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.get(VALUE_LIST_DELETE_BUTTON(file)).click();
|
||||
};
|
||||
|
||||
export const selectValueListType = (type: string): Cypress.Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.get(VALUE_LIST_TYPE_SELECTOR).select(type);
|
||||
};
|
||||
|
||||
export const uploadValueList = (): Cypress.Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.get(VALUE_LIST_FILE_UPLOAD_BUTTON).click();
|
||||
};
|
||||
|
||||
export const exportValueList = (): Cypress.Chainable<JQuery<HTMLElement>> => {
|
||||
return cy.get(VALUE_LIST_EXPORT_BUTTON).click();
|
||||
};
|
||||
|
||||
/**
|
||||
* Given an array of value lists this will delete them all using Cypress Request and the lists REST API
|
||||
* Ref: https://www.elastic.co/guide/en/security/current/lists-api-delete-container.html
|
||||
*/
|
||||
export const deleteValueLists = (lists: string[]): Array<Cypress.Chainable<Cypress.Response>> => {
|
||||
return lists.map((list) => deleteValueList(list));
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a single value list this will delete it using Cypress Request and lists REST API
|
||||
* Ref: https://www.elastic.co/guide/en/security/current/lists-api-delete-container.html
|
||||
*/
|
||||
export const deleteValueList = (list: string): Cypress.Chainable<Cypress.Response> => {
|
||||
return cy.request({
|
||||
method: 'DELETE',
|
||||
url: `api/lists?id=${list}`,
|
||||
headers: { 'kbn-xsrf': 'delete-lists' },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Imports a single value list file this using Cypress Request and lists REST API
|
||||
* Ref: https://www.elastic.co/guide/en/security/current/lists-api-import-list-items.html
|
||||
*/
|
||||
export const importValueList = (
|
||||
file: string,
|
||||
type: string
|
||||
): Cypress.Chainable<Cypress.Response> => {
|
||||
return cy.fixture(file).then((data) => {
|
||||
return cy.request({
|
||||
method: 'POST',
|
||||
url: `api/lists/items/_import?type=${type}`,
|
||||
encoding: 'binary',
|
||||
headers: {
|
||||
'kbn-xsrf': 'upload-value-lists',
|
||||
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryJLrRH89J8QVArZyv',
|
||||
},
|
||||
body: `------WebKitFormBoundaryJLrRH89J8QVArZyv\nContent-Disposition: form-data; name="file"; filename="${file}"\n\n${data}`,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* If you are on the value lists from the UI, this will loop over all the HTML elements
|
||||
* that have action-delete-value-list-${list_name} and delete all of those value lists
|
||||
* using Cypress Request and the lists REST API.
|
||||
* If the UI does not contain any value based lists this will not fail. If the UI does
|
||||
* contain value based lists but the backend does not return a success on DELETE then this
|
||||
* will cause errors.
|
||||
* Ref: https://www.elastic.co/guide/en/security/current/lists-api-delete-container.html
|
||||
*/
|
||||
export const deleteAllValueListsFromUI = (): Array<Cypress.Chainable<Cypress.Response>> => {
|
||||
const lists = Cypress.$(VALUE_LIST_FILES)
|
||||
.toArray()
|
||||
.reduce<string[]>((accum, $el) => {
|
||||
const attribute = $el.getAttribute('data-test-subj');
|
||||
if (attribute != null) {
|
||||
const list = attribute.substr('data-test-subj-value-list'.length);
|
||||
return [...accum, list];
|
||||
} else {
|
||||
return accum;
|
||||
}
|
||||
}, []);
|
||||
return deleteValueLists(lists);
|
||||
};
|
||||
|
|
|
@ -145,6 +145,7 @@ export const ValueListsFormComponent: React.FC<ValueListsFormProps> = ({ onError
|
|||
<EuiFlexItem>
|
||||
<EuiFormRow label={i18n.LIST_TYPES_RADIO_LABEL}>
|
||||
<EuiSelect
|
||||
data-test-subj="value-lists-form-select-type-action"
|
||||
options={listFormOptions}
|
||||
value={type}
|
||||
onChange={handleRadioChange}
|
||||
|
|
|
@ -114,7 +114,7 @@ describe('ValueListsModal', () => {
|
|||
|
||||
await waitFor(() => {
|
||||
container
|
||||
.find('button[data-test-subj="action-delete-value-list"]')
|
||||
.find('button[data-test-subj="action-delete-value-list-some name"]')
|
||||
.first()
|
||||
.simulate('click');
|
||||
});
|
||||
|
|
|
@ -81,7 +81,7 @@ export const buildColumns = (
|
|||
) : (
|
||||
<EuiButtonIcon
|
||||
aria-label={i18n.ACTION_DELETE_DESCRIPTION}
|
||||
data-test-subj="action-delete-value-list"
|
||||
data-test-subj={`action-delete-value-list-${item.name}`}
|
||||
iconType="trash"
|
||||
onClick={() => onDelete(item)}
|
||||
/>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue