[Security Solution] Unskip persistent timeline cypress tests (#82972) (#85249)

This commit is contained in:
Patryk Kopyciński 2020-12-08 13:57:56 +01:00 committed by GitHub
parent 010101de43
commit 1a6d7308e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 272 additions and 276 deletions

View file

@ -341,7 +341,7 @@
"@babel/traverse": "^7.11.5",
"@babel/types": "^7.11.0",
"@cypress/snapshot": "^2.1.7",
"@cypress/webpack-preprocessor": "^5.4.10",
"@cypress/webpack-preprocessor": "^5.4.11",
"@elastic/apm-rum": "^5.6.1",
"@elastic/apm-rum-react": "^1.2.5",
"@elastic/charts": "24.3.0",
@ -604,7 +604,7 @@
"cpy": "^8.1.1",
"cronstrue": "^1.51.0",
"css-loader": "^3.4.2",
"cypress": "^5.5.0",
"cypress": "^6.0.1",
"cypress-cucumber-preprocessor": "^2.5.2",
"cypress-multi-reporters": "^1.4.0",
"d3": "3.5.17",

View file

@ -1,7 +1,6 @@
{
"baseUrl": "http://localhost:5601",
"defaultCommandTimeout": 120000,
"experimentalNetworkStubbing": true,
"defaultCommandTimeout": 60000,
"retries": {
"runMode": 2
},

View file

@ -20,10 +20,9 @@ const EXPECTED_EXPORTED_RULE_FILE_PATH = 'cypress/test_files/expected_rules_expo
describe('Export rules', () => {
before(() => {
esArchiverLoad('export_rule');
cy.server();
cy.route(
cy.intercept(
'POST',
'**api/detection_engine/rules/_export?exclude_export_details=false&file_name=rules_export.ndjson*'
'/api/detection_engine/rules/_export?exclude_export_details=false&file_name=rules_export.ndjson'
).as('export');
});
@ -37,9 +36,9 @@ describe('Export rules', () => {
waitForAlertsIndexToBeCreated();
goToManageAlertsDetectionRules();
exportFirstRule();
cy.wait('@export').then((xhr) => {
cy.wait('@export').then(({ response }) => {
cy.readFile(EXPECTED_EXPORTED_RULE_FILE_PATH).then(($expectedExportedJson) => {
cy.wrap(xhr.responseBody).should('eql', $expectedExportedJson);
cy.wrap(response!.body).should('eql', $expectedExportedJson);
});
});
});

View file

@ -28,21 +28,20 @@ import { CONNECTOR_CARD_DETAILS, CONNECTOR_TITLE } from '../screens/case_details
describe('Cases connector incident fields', () => {
before(() => {
cy.server();
cy.route('GET', '**/api/cases/configure/connectors/_find', mockConnectorsResponse);
cy.route2('POST', `**/api/actions/action/${connectorIds.jira}/_execute`, (req) => {
cy.intercept('GET', '/api/cases/configure/connectors/_find', mockConnectorsResponse);
cy.intercept('POST', `/api/actions/action/${connectorIds.jira}/_execute`, (req) => {
const response =
JSON.parse(req.body).params.subAction === 'issueTypes'
req.body.params.subAction === 'issueTypes'
? executeResponses.jira.issueTypes
: executeResponses.jira.fieldsByIssueType;
req.reply(JSON.stringify(response));
req.reply(response);
});
cy.route2('POST', `**/api/actions/action/${connectorIds.resilient}/_execute`, (req) => {
cy.intercept('POST', `/api/actions/action/${connectorIds.resilient}/_execute`, (req) => {
const response =
JSON.parse(req.body).params.subAction === 'incidentTypes'
req.body.params.subAction === 'incidentTypes'
? executeResponses.resilient.incidentTypes
: executeResponses.resilient.severity;
req.reply(JSON.stringify(response));
req.reply(response);
});
});

View file

@ -19,9 +19,8 @@ import { CASES_URL } from '../urls/navigation';
describe('Cases connectors', () => {
before(() => {
cy.server();
cy.route('POST', '**/api/actions/action').as('createConnector');
cy.route('POST', '**/api/cases/configure').as('saveConnector');
cy.intercept('POST', '/api/actions/action').as('createConnector');
cy.intercept('POST', '/api/cases/configure').as('saveConnector');
});
it('Configures a new connector', () => {
@ -30,13 +29,15 @@ describe('Cases connectors', () => {
openAddNewConnectorOption();
addServiceNowConnector(serviceNowConnector);
cy.wait('@createConnector').its('status').should('eql', 200);
cy.get(TOASTER).should('have.text', "Created 'New connector'");
cy.get(TOASTER).should('not.exist');
cy.wait('@createConnector').then(({ response }) => {
cy.wrap(response!.statusCode).should('eql', 200);
cy.get(TOASTER).should('have.text', "Created 'New connector'");
cy.get(TOASTER).should('not.exist');
selectLastConnectorCreated();
selectLastConnectorCreated(response!.body.id);
cy.wait('@saveConnector', { timeout: 10000 }).its('status').should('eql', 200);
cy.get(TOASTER).should('have.text', 'Saved external connection settings');
cy.wait('@saveConnector', { timeout: 10000 }).its('response.statusCode').should('eql', 200);
cy.get(TOASTER).should('have.text', 'Saved external connection settings');
});
});
});

View file

@ -9,8 +9,8 @@ import {
FAVORITE_TIMELINE,
LOCKED_ICON,
NOTES,
NOTES_BUTTON,
NOTES_COUNT,
NOTES_TAB_BUTTON,
// NOTES_COUNT,
NOTES_TEXT_AREA,
PIN_EVENT,
TIMELINE_DESCRIPTION,
@ -32,7 +32,6 @@ import {
addFilter,
addNameToTimeline,
addNotesToTimeline,
closeNotes,
closeTimeline,
createNewTimeline,
markAsFavorite,
@ -47,12 +46,9 @@ import { OVERVIEW_URL } from '../urls/navigation';
// FLAKY: https://github.com/elastic/kibana/issues/79389
describe.skip('Timelines', () => {
before(() => {
cy.server();
cy.route('PATCH', '**/api/timeline').as('timeline');
});
it('Creates a timeline', () => {
cy.intercept('PATCH', '/api/timeline').as('timeline');
it('Creates a timeline', async () => {
loginAndWaitForPage(OVERVIEW_URL);
openTimelineUsingToggle();
populateTimeline();
@ -64,37 +60,36 @@ describe.skip('Timelines', () => {
addNameToTimeline(timeline.title);
const response = await cy.wait('@timeline').promisify();
const timelineId = JSON.parse(response.xhr.responseText).data.persistTimeline.timeline
.savedObjectId;
cy.wait('@timeline').then(({ response }) => {
const timelineId = response!.body.data.persistTimeline.timeline.savedObjectId;
addDescriptionToTimeline(timeline.description);
addNotesToTimeline(timeline.notes);
closeNotes();
markAsFavorite();
waitForTimelineChanges();
createNewTimeline();
closeTimeline();
openTimelineFromSettings();
addDescriptionToTimeline(timeline.description);
addNotesToTimeline(timeline.notes);
markAsFavorite();
waitForTimelineChanges();
createNewTimeline();
closeTimeline();
openTimelineFromSettings();
cy.contains(timeline.title).should('exist');
cy.get(TIMELINES_DESCRIPTION).first().should('have.text', timeline.description);
cy.get(TIMELINES_PINNED_EVENT_COUNT).first().should('have.text', '1');
cy.get(TIMELINES_NOTES_COUNT).first().should('have.text', '1');
cy.get(TIMELINES_FAVORITE).first().should('exist');
cy.contains(timeline.title).should('exist');
cy.get(TIMELINES_DESCRIPTION).first().should('have.text', timeline.description);
cy.get(TIMELINES_PINNED_EVENT_COUNT).first().should('have.text', '1');
cy.get(TIMELINES_NOTES_COUNT).first().should('have.text', '1');
cy.get(TIMELINES_FAVORITE).first().should('exist');
openTimeline(timelineId);
openTimeline(timelineId);
cy.get(FAVORITE_TIMELINE).should('exist');
cy.get(TIMELINE_TITLE).should('have.attr', 'value', timeline.title);
cy.get(TIMELINE_DESCRIPTION).should('have.attr', 'value', timeline.description);
cy.get(TIMELINE_QUERY).should('have.text', timeline.query);
// Comments this assertion until we agreed what to do with the filters.
// cy.get(TIMELINE_FILTER(timeline.filter)).should('exist');
cy.get(NOTES_COUNT).should('have.text', '1');
cy.get(PIN_EVENT).should('have.attr', 'aria-label', 'Pinned event');
cy.get(NOTES_BUTTON).click();
cy.get(NOTES_TEXT_AREA).should('have.attr', 'placeholder', 'Add a Note');
cy.get(NOTES).should('have.text', timeline.notes);
cy.get(FAVORITE_TIMELINE).should('exist');
cy.get(TIMELINE_TITLE).should('have.text', timeline.title);
cy.get(TIMELINE_DESCRIPTION).should('have.text', timeline.description);
cy.get(TIMELINE_QUERY).should('have.text', timeline.query);
// Comments this assertion until we agreed what to do with the filters.
// cy.get(TIMELINE_FILTER(timeline.filter)).should('exist');
// cy.get(NOTES_COUNT).should('have.text', '1');
cy.get(PIN_EVENT).should('have.attr', 'aria-label', 'Pinned event');
cy.get(NOTES_TAB_BUTTON).click();
cy.get(NOTES_TEXT_AREA).should('exist');
cy.get(NOTES).should('have.text', timeline.notes);
});
});
});

View file

@ -20,12 +20,11 @@ import {
import { loginAndWaitForPage } from '../tasks/login';
import { openTimelineUsingToggle } from '../tasks/security_main';
import { createNewTimeline } from '../tasks/timeline';
import { closeTimeline, createNewTimeline } from '../tasks/timeline';
import { HOSTS_URL } from '../urls/navigation';
// FLAKY: https://github.com/elastic/kibana/issues/62060
describe.skip('timeline data providers', () => {
describe('timeline data providers', () => {
before(() => {
loginAndWaitForPage(HOSTS_URL);
waitForAllHostsToBeLoaded();
@ -33,6 +32,7 @@ describe.skip('timeline data providers', () => {
afterEach(() => {
createNewTimeline();
closeTimeline();
});
it('renders the data provider of a host dragged from the All Hosts widget on the hosts page', () => {

View file

@ -9,8 +9,8 @@ import {
FAVORITE_TIMELINE,
LOCKED_ICON,
NOTES,
NOTES_BUTTON,
NOTES_COUNT,
NOTES_TAB_BUTTON,
// NOTES_COUNT,
NOTES_TEXT_AREA,
PIN_EVENT,
TIMELINE_DESCRIPTION,
@ -31,7 +31,6 @@ import {
addFilter,
addNameToTimeline,
addNotesToTimeline,
closeNotes,
closeTimeline,
createNewTimelineTemplate,
markAsFavorite,
@ -43,11 +42,9 @@ import { openTimeline } from '../tasks/timelines';
import { OVERVIEW_URL } from '../urls/navigation';
// FLAKY: https://github.com/elastic/kibana/issues/79967
describe.skip('Timeline Templates', () => {
describe('Timeline Templates', () => {
before(() => {
cy.server();
cy.route('PATCH', '**/api/timeline').as('timeline');
cy.intercept('PATCH', '/api/timeline').as('timeline');
});
it('Creates a timeline template', async () => {
@ -65,36 +62,35 @@ describe.skip('Timeline Templates', () => {
addNameToTimeline(timeline.title);
const response = await cy.wait('@timeline').promisify();
const timelineId = JSON.parse(response.xhr.responseText).data.persistTimeline.timeline
.savedObjectId;
cy.wait('@timeline').then(({ response }) => {
const timelineId = response!.body.data.persistTimeline.timeline.savedObjectId;
addDescriptionToTimeline(timeline.description);
addNotesToTimeline(timeline.notes);
closeNotes();
markAsFavorite();
waitForTimelineChanges();
createNewTimelineTemplate();
closeTimeline();
openTimelineTemplateFromSettings(timelineId);
addDescriptionToTimeline(timeline.description);
addNotesToTimeline(timeline.notes);
markAsFavorite();
waitForTimelineChanges();
createNewTimelineTemplate();
closeTimeline();
openTimelineTemplateFromSettings(timelineId);
cy.contains(timeline.title).should('exist');
cy.get(TIMELINES_DESCRIPTION).first().should('have.text', timeline.description);
cy.get(TIMELINES_PINNED_EVENT_COUNT).first().should('have.text', '1');
cy.get(TIMELINES_NOTES_COUNT).first().should('have.text', '1');
cy.get(TIMELINES_FAVORITE).first().should('exist');
cy.contains(timeline.title).should('exist');
cy.get(TIMELINES_DESCRIPTION).first().should('have.text', timeline.description);
cy.get(TIMELINES_PINNED_EVENT_COUNT).first().should('have.text', '1');
cy.get(TIMELINES_NOTES_COUNT).first().should('have.text', '1');
cy.get(TIMELINES_FAVORITE).first().should('exist');
openTimeline(timelineId);
openTimeline(timelineId);
cy.get(FAVORITE_TIMELINE).should('exist');
cy.get(TIMELINE_TITLE).should('have.attr', 'value', timeline.title);
cy.get(TIMELINE_DESCRIPTION).should('have.attr', 'value', timeline.description);
cy.get(TIMELINE_QUERY).should('have.text', timeline.query);
// Comments this assertion until we agreed what to do with the filters.
// cy.get(TIMELINE_FILTER(timeline.filter)).should('exist');
cy.get(NOTES_COUNT).should('have.text', '1');
cy.get(NOTES_BUTTON).click();
cy.get(NOTES_TEXT_AREA).should('have.attr', 'placeholder', 'Add a Note');
cy.get(NOTES).should('have.text', timeline.notes);
cy.get(FAVORITE_TIMELINE).should('exist');
cy.get(TIMELINE_TITLE).should('have.text', timeline.title);
cy.get(TIMELINE_DESCRIPTION).should('have.text', timeline.description);
cy.get(TIMELINE_QUERY).should('have.text', timeline.query);
// Comments this assertion until we agreed what to do with the filters.
// cy.get(TIMELINE_FILTER(timeline.filter)).should('exist');
// cy.get(NOTES_COUNT).should('have.text', '1');
cy.get(NOTES_TAB_BUTTON).click();
cy.get(NOTES_TEXT_AREA).should('exist');
cy.get(NOTES).should('have.text', timeline.notes);
});
});
});

View file

@ -5,7 +5,7 @@
*/
import { exportTimeline } from '../tasks/timelines';
import { esArchiverLoad } from '../tasks/es_archiver';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
import { loginAndWaitForPageWithoutDateRange } from '../tasks/login';
import { timeline as timelineTemplate } from '../objects/timeline';
@ -15,31 +15,34 @@ import { addNameToTimeline, closeTimeline, createNewTimelineTemplate } from '../
describe('Export timelines', () => {
before(() => {
esArchiverLoad('timeline');
cy.server();
cy.route('PATCH', '**/api/timeline').as('timeline');
cy.route('POST', '**api/timeline/_export?file_name=timelines_export.ndjson*').as('export');
cy.intercept('PATCH', '/api/timeline').as('timeline');
cy.intercept('POST', '/api/timeline/_export?file_name=timelines_export.ndjson').as('export');
});
it('Exports a custom timeline template', async () => {
after(() => {
esArchiverUnload('timeline');
});
it('Exports a custom timeline template', () => {
loginAndWaitForPageWithoutDateRange(TIMELINE_TEMPLATES_URL);
createNewTimelineTemplate();
addNameToTimeline(timelineTemplate.title);
closeTimeline();
const result = await cy.wait('@timeline').promisify();
cy.wait('@timeline').then(({ response }) => {
const {
savedObjectId: timelineId,
templateTimelineId,
} = response!.body.data.persistTimeline.timeline;
const timelineId = JSON.parse(result.xhr.responseText).data.persistTimeline.timeline
.savedObjectId;
const templateTimelineId = JSON.parse(result.xhr.responseText).data.persistTimeline.timeline
.templateTimelineId;
exportTimeline(timelineId);
await exportTimeline(timelineId);
cy.wait('@export').then((response) => {
cy.wrap(JSON.parse(response.xhr.responseText).templateTimelineId).should(
'eql',
templateTimelineId
);
cy.wait('@export').then(({ response: exportResponse }) => {
cy.wrap(JSON.parse(exportResponse!.body as string).templateTimelineId).should(
'eql',
templateTimelineId
);
});
});
});
});

View file

@ -11,10 +11,12 @@ import {
TIMESTAMP_TOGGLE_FIELD,
} from '../screens/timeline';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
import { loginAndWaitForPage } from '../tasks/login';
import { openTimelineUsingToggle } from '../tasks/security_main';
import {
checkIdToggleField,
closeTimeline,
createNewTimeline,
dragAndDropIdToggleFieldToTimeline,
expandFirstTimelineEventDetails,
@ -26,9 +28,14 @@ import { HOSTS_URL } from '../urls/navigation';
describe('toggle column in timeline', () => {
before(() => {
esArchiverLoad('timeline');
loginAndWaitForPage(HOSTS_URL);
});
after(() => {
esArchiverUnload('timeline');
});
beforeEach(() => {
openTimelineUsingToggle();
populateTimeline();
@ -36,6 +43,7 @@ describe('toggle column in timeline', () => {
afterEach(() => {
createNewTimeline();
closeTimeline();
});
it('displays a checked Toggle field checkbox for `@timestamp`, a default timeline column', () => {
@ -44,6 +52,7 @@ describe('toggle column in timeline', () => {
});
it('displays an Unchecked Toggle field checkbox for `_id`, because it is NOT a default timeline column', () => {
expandFirstTimelineEventDetails();
cy.get(ID_TOGGLE_FIELD).should('not.be.checked');
});

View file

@ -15,8 +15,7 @@ const EXPECTED_EXPORTED_TIMELINE_PATH = 'cypress/test_files/expected_timelines_e
describe('Export timelines', () => {
before(() => {
esArchiverLoad('timeline');
cy.server();
cy.route('POST', '**api/timeline/_export?file_name=timelines_export.ndjson*').as('export');
cy.intercept('POST', '/api/timeline/_export?file_name=timelines_export.ndjson').as('export');
});
after(() => {
@ -32,9 +31,9 @@ describe('Export timelines', () => {
const timelineId = parsedJson.savedObjectId;
exportTimeline(timelineId);
cy.wait('@export').then((response) => {
cy.wrap(response.status).should('eql', 200);
cy.wrap(response.xhr.responseText).should('eql', $expectedExportedJson);
cy.wait('@export').then(({ response }) => {
cy.wrap(response!.statusCode).should('eql', 200);
cy.wrap(response!.body).should('eql', $expectedExportedJson);
});
});
});

View file

@ -31,12 +31,7 @@ import { openAllHosts } from '../tasks/hosts/main';
import { waitForIpsTableToBeLoaded } from '../tasks/network/flows';
import { clearSearchBar, kqlSearch, navigateFromHeaderTo } from '../tasks/security_header';
import { openTimelineUsingToggle } from '../tasks/security_main';
import {
addNameToTimeline,
closeTimeline,
populateTimeline,
waitForTimelineChanges,
} from '../tasks/timeline';
import { addNameToTimeline, closeTimeline, populateTimeline } from '../tasks/timeline';
import { HOSTS_URL } from '../urls/navigation';
import { ABSOLUTE_DATE_RANGE } from '../urls/state';
@ -225,18 +220,14 @@ describe('url state', () => {
openTimelineUsingToggle();
populateTimeline();
cy.server();
cy.route('PATCH', '**/api/timeline').as('timeline');
cy.intercept('PATCH', '/api/timeline').as('timeline');
waitForTimelineChanges();
addNameToTimeline(timeline.title);
waitForTimelineChanges();
cy.wait('@timeline').then((response) => {
cy.wait('@timeline').then(({ response }) => {
closeTimeline();
cy.wrap(response.status).should('eql', 200);
const JsonResponse = JSON.parse(response.xhr.responseText);
const timelineId = JsonResponse.data.persistTimeline.timeline.savedObjectId;
cy.wrap(response!.statusCode).should('eql', 200);
const timelineId = response!.body.data.persistTimeline.timeline.savedObjectId;
cy.visit('/app/home');
cy.visit(`/app/security/timelines?timeline=(id:'${timelineId}',isOpen:!t)`);
cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).should('exist');

View file

@ -160,62 +160,61 @@ describe('value lists', () => {
});
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';
cy.intercept('POST', `/api/lists/items/_export?list_id=${listName}`).as('exportList');
importValueList('value_list.txt', 'keyword');
openValueListsModal();
exportValueList();
cy.wait('@exportList').then((xhr) => {
cy.wait('@exportList').then(({ response }) => {
cy.fixture(listName).then((list: string) => {
const [lineOne, lineTwo] = list.split('\n');
expect(xhr.responseBody).to.contain(lineOne);
expect(xhr.responseBody).to.contain(lineTwo);
expect(response!.body).to.contain(lineOne);
expect(response!.body).to.contain(lineTwo);
});
});
});
it('exports a "text" list from an uploaded file', () => {
const listName = 'value_list.txt';
cy.intercept('POST', `/api/lists/items/_export?list_id=${listName}`).as('exportList');
importValueList(listName, 'text');
openValueListsModal();
exportValueList();
cy.wait('@exportList').then((xhr) => {
cy.wait('@exportList').then(({ response }) => {
cy.fixture(listName).then((list: string) => {
const [lineOne, lineTwo] = list.split('\n');
expect(xhr.responseBody).to.contain(lineOne);
expect(xhr.responseBody).to.contain(lineTwo);
expect(response!.body).to.contain(lineOne);
expect(response!.body).to.contain(lineTwo);
});
});
});
it('exports a "ip" list from an uploaded file', () => {
const listName = 'ip_list.txt';
cy.intercept('POST', `/api/lists/items/_export?list_id=${listName}`).as('exportList');
importValueList(listName, 'ip');
openValueListsModal();
exportValueList();
cy.wait('@exportList').then((xhr) => {
cy.wait('@exportList').then(({ response }) => {
cy.fixture(listName).then((list: string) => {
const [lineOne, lineTwo] = list.split('\n');
expect(xhr.responseBody).to.contain(lineOne);
expect(xhr.responseBody).to.contain(lineTwo);
expect(response!.body).to.contain(lineOne);
expect(response!.body).to.contain(lineTwo);
});
});
});
it('exports a "ip_range" list from an uploaded file', () => {
const listName = 'cidr_list.txt';
cy.intercept('POST', `/api/lists/items/_export?list_id=${listName}`).as('exportList');
importValueList(listName, 'ip_range', ['192.168.100.0']);
openValueListsModal();
exportValueList();
cy.wait('@exportList').then((xhr) => {
cy.wait('@exportList').then(({ response }) => {
cy.fixture(listName).then((list: string) => {
const [lineOne] = list.split('\n');
expect(xhr.responseBody).to.contain(lineOne);
expect(response!.body).to.contain(lineOne);
});
});
});

View file

@ -23,8 +23,6 @@ export const CASE = (id: string) => {
return `[data-test-subj="cases-table-row-${id}"]`;
};
export const CLOSE_NOTES_BTN = '[data-test-subj="notesModal"] .euiButtonIcon';
export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]';
export const COMBO_BOX = '.euiComboBoxOption__content';
@ -38,6 +36,8 @@ export const DRAGGABLE_HEADER =
export const FAVORITE_TIMELINE = '[data-test-subj="timeline-favorite-filled-star"]';
export const GRAPH_TAB_BUTTON = '[data-test-subj="timelineTabs-graph"]';
export const HEADER = '[data-test-subj="header"]';
export const HEADERS_GROUP = '[data-test-subj="headers-group"]';
@ -50,11 +50,11 @@ export const ID_TOGGLE_FIELD = '[data-test-subj="toggle-field-_id"]';
export const LOCKED_ICON = '[data-test-subj="timeline-date-picker-lock-button"]';
export const NOTES = '[data-test-subj="markdown-root"]';
export const NOTES = '[data-test-subj="note-card-body"]';
export const NOTES_TEXT_AREA = '[data-test-subj="add-a-note"]';
export const NOTES_TEXT_AREA = '[data-test-subj="add-a-note"] textarea';
export const NOTES_BUTTON = '[data-test-subj="timeline-notes-button-large"]';
export const NOTES_TAB_BUTTON = '[data-test-subj="timelineTabs-notes"]';
export const NOTES_COUNT = '[data-test-subj="timeline-notes-count"]';
@ -65,6 +65,8 @@ export const OPEN_TIMELINE_TEMPLATE_ICON =
export const PIN_EVENT = '[data-test-subj="pin"]';
export const PINNED_TAB_BUTTON = '[data-test-subj="timelineTabs-pinned"]';
export const PROVIDER_BADGE = '[data-test-subj="providerBadge"]';
export const REMOVE_COLUMN = '[data-test-subj="remove-column"]';
@ -92,7 +94,7 @@ export const TIMELINE_DATA_PROVIDERS_EMPTY =
export const TIMELINE_DESCRIPTION = '[data-test-subj="timeline-description"]';
export const TIMELINE_DESCRIPTION_INPUT = '[data-test-subj="timeline-description-input"]';
export const TIMELINE_DESCRIPTION_INPUT = '[data-test-subj="timeline-description-textarea"]';
export const TIMELINE_DROPPED_DATA_PROVIDERS = '[data-test-subj="providerContainer"]';
@ -137,3 +139,5 @@ export const TOGGLE_TIMELINE_EXPAND_EVENT = '[data-test-subj="expand-event"]';
export const TIMELINE_EDIT_MODAL_OPEN_BUTTON = '[data-test-subj="save-timeline-button-icon"]';
export const TIMELINE_EDIT_MODAL_SAVE_BUTTON = '[data-test-subj="save-button"]';
export const QUERY_TAB_BUTTON = '[data-test-subj="timelineTabs-query"]';

View file

@ -48,11 +48,9 @@ const getFindRequestConfig = (searchStrategyName, factoryQueryType) => {
Cypress.Commands.add(
'stubSearchStrategyApi',
function (stubObject, factoryQueryType, searchStrategyName = 'securitySolutionSearchStrategy') {
cy.route2('POST', '/internal/bsearch', (req) => {
const bodyObj = JSON.parse(req.body);
cy.intercept('POST', '/internal/bsearch', (req) => {
const findRequestConfig = getFindRequestConfig(searchStrategyName, factoryQueryType);
const requestIndex = findIndex(findRequestConfig, bodyObj.batch);
const requestIndex = findIndex(findRequestConfig, req.body.batch);
if (requestIndex > -1) {
return req.reply((res) => {

View file

@ -21,7 +21,6 @@
// Import commands.js using ES2015 syntax:
import './commands';
import 'cypress-promise/register';
Cypress.Cookies.defaults({
preserve: 'sid',
@ -33,10 +32,5 @@ Cypress.on('uncaught:exception', (err) => {
}
});
Cypress.on('window:before:load', (win) => {
win.fetch = null;
win.Blob = null;
});
// Alternatively you can use CommonJS syntax:
// require('./commands')

View file

@ -126,7 +126,7 @@ export const waitForRulesToBeLoaded = () => {
};
export const checkAutoRefresh = (ms: number, condition: string) => {
cy.get(ASYNC_LOADING_PROGRESS).should('not.be.visible');
cy.get(ASYNC_LOADING_PROGRESS).should('not.exist');
cy.tick(ms);
cy.get(ASYNC_LOADING_PROGRESS).should(condition);
};
@ -136,7 +136,7 @@ export const dismissAllRulesIdleModal = () => {
.eq(1)
.should('exist')
.click({ force: true, multiple: true });
cy.get(RULE_AUTO_REFRESH_IDLE_MODAL).should('not.be.visible');
cy.get(RULE_AUTO_REFRESH_IDLE_MODAL).should('not.exist');
};
export const checkAllRulesIdleModal = (condition: string) => {

View file

@ -38,11 +38,7 @@ export const openAddNewConnectorOption = () => {
});
};
export const selectLastConnectorCreated = () => {
export const selectLastConnectorCreated = (id: string) => {
cy.get(CONNECTORS_DROPDOWN).click({ force: true });
cy.get('@createConnector')
.its('response')
.then((response) => {
cy.get(CONNECTOR(response.body.id)).click();
});
cy.get(CONNECTOR(id)).click();
};

View file

@ -24,12 +24,11 @@ import {
} from '../screens/rule_details';
export const activatesRule = () => {
cy.server();
cy.route('PATCH', '**/api/detection_engine/rules/_bulk_update').as('bulk_update');
cy.intercept('PATCH', '/api/detection_engine/rules/_bulk_update').as('bulk_update');
cy.get(RULE_SWITCH).should('be.visible');
cy.get(RULE_SWITCH).click();
cy.wait('@bulk_update').then((response) => {
cy.wrap(response.status).should('eql', 200);
cy.wait('@bulk_update').then(({ response }) => {
cy.wrap(response!.statusCode).should('eql', 200);
});
};
@ -50,7 +49,7 @@ export const addsException = (exception: Exception) => {
cy.get(CLOSE_ALERTS_CHECKBOX).click({ force: true });
cy.get(CONFIRM_BTN).click();
cy.get(CONFIRM_BTN).should('have.attr', 'disabled');
cy.get(CONFIRM_BTN).should('not.have.attr', 'disabled');
cy.get(CONFIRM_BTN).should('not.exist');
};
export const addsExceptionFromRuleSettings = (exception: Exception) => {
@ -68,7 +67,7 @@ export const addsExceptionFromRuleSettings = (exception: Exception) => {
cy.get(CLOSE_ALERTS_CHECKBOX).click({ force: true });
cy.get(CONFIRM_BTN).click();
cy.get(CONFIRM_BTN).should('have.attr', 'disabled');
cy.get(CONFIRM_BTN).should('not.have.attr', 'disabled');
cy.get(CONFIRM_BTN).should('not.exist');
};
export const goToAlertsTab = () => {

View file

@ -16,14 +16,13 @@ import {
ATTACH_TIMELINE_TO_NEW_CASE_ICON,
CASE,
CLOSE_TIMELINE_BTN,
CLOSE_NOTES_BTN,
COMBO_BOX,
CREATE_NEW_TIMELINE,
HEADER,
ID_FIELD,
ID_HEADER_FIELD,
ID_TOGGLE_FIELD,
NOTES_BUTTON,
NOTES_TAB_BUTTON,
NOTES_TEXT_AREA,
OPEN_TIMELINE_ICON,
PIN_EVENT,
@ -34,7 +33,7 @@ import {
SERVER_SIDE_EVENT_COUNT,
STAR_ICON,
TIMELINE_CHANGES_IN_PROGRESS,
TIMELINE_DESCRIPTION,
TIMELINE_DESCRIPTION_INPUT,
TIMELINE_FIELDS_BUTTON,
TIMELINE_FILTER_FIELD,
TIMELINE_FILTER_OPERATOR,
@ -49,6 +48,7 @@ import {
OPEN_TIMELINE_TEMPLATE_ICON,
TIMELINE_EDIT_MODAL_OPEN_BUTTON,
TIMELINE_EDIT_MODAL_SAVE_BUTTON,
QUERY_TAB_BUTTON,
} from '../screens/timeline';
import { TIMELINES_TABLE } from '../screens/timelines';
@ -57,8 +57,11 @@ import { drag, drop } from '../tasks/common';
export const hostExistsQuery = 'host.name: *';
export const addDescriptionToTimeline = (description: string) => {
cy.get(TIMELINE_DESCRIPTION).type(`${description}{enter}`);
cy.get(TIMELINE_DESCRIPTION).should('have.attr', 'value', description);
cy.get(TIMELINE_EDIT_MODAL_OPEN_BUTTON).first().click();
cy.get(TIMELINE_DESCRIPTION_INPUT).type(`${description}{enter}`);
cy.get(TIMELINE_DESCRIPTION_INPUT).invoke('val').should('equal', description);
cy.get(TIMELINE_EDIT_MODAL_SAVE_BUTTON).click();
cy.get(TIMELINE_TITLE_INPUT).should('not.exist');
};
export const addNameToTimeline = (name: string) => {
@ -66,12 +69,14 @@ export const addNameToTimeline = (name: string) => {
cy.get(TIMELINE_TITLE_INPUT).type(`${name}{enter}`);
cy.get(TIMELINE_TITLE_INPUT).should('have.attr', 'value', name);
cy.get(TIMELINE_EDIT_MODAL_SAVE_BUTTON).click();
cy.get(TIMELINE_TITLE_INPUT).should('not.exist');
};
export const addNotesToTimeline = (notes: string) => {
cy.get(NOTES_BUTTON).click();
cy.get(NOTES_TAB_BUTTON).click();
cy.get(NOTES_TEXT_AREA).type(notes);
cy.get(ADD_NOTE_BUTTON).click();
cy.get(QUERY_TAB_BUTTON).click();
};
export const addFilter = (filter: TimelineFilter) => {
@ -107,10 +112,6 @@ export const checkIdToggleField = () => {
});
};
export const closeNotes = () => {
cy.get(CLOSE_NOTES_BTN).click();
};
export const closeTimeline = () => {
cy.get(CLOSE_TIMELINE_BTN).filter(':visible').click({ force: true });
};
@ -119,7 +120,6 @@ export const createNewTimeline = () => {
cy.get(TIMELINE_SETTINGS_ICON).filter(':visible').click({ force: true });
cy.get(CREATE_NEW_TIMELINE).should('be.visible');
cy.get(CREATE_NEW_TIMELINE).click();
cy.get(CLOSE_TIMELINE_BTN).filter(':visible').click({ force: true });
};
export const createNewTimelineTemplate = () => {
@ -149,7 +149,7 @@ export const openTimelineInspectButton = () => {
};
export const openTimelineFromSettings = () => {
cy.get(TIMELINE_SETTINGS_ICON).click({ force: true });
cy.get(TIMELINE_SETTINGS_ICON).filter(':visible').click({ force: true });
cy.get(OPEN_TIMELINE_ICON).click({ force: true });
};

View file

@ -136,12 +136,9 @@ const TimelineNameComponent: React.FC<FlyoutHeaderProps> = ({ timelineId }) => {
const content = useMemo(() => (title.length ? title : placeholder), [title, placeholder]);
return (
<>
<EuiText>
<h3 data-test-subj="timeline-title">{content}</h3>
</EuiText>
<SaveTimelineButton timelineId={timelineId} />
</>
<EuiText>
<h3 data-test-subj="timeline-title">{content}</h3>
</EuiText>
);
};
@ -158,12 +155,9 @@ const TimelineDescriptionComponent: React.FC<FlyoutHeaderProps> = ({ timelineId
]);
return (
<>
<EuiText size="s" data-test-subj="timeline-description">
{content}
</EuiText>
<SaveTimelineButton timelineId={timelineId} />
</>
<EuiText size="s" data-test-subj="timeline-description">
{content}
</EuiText>
);
};
@ -209,9 +203,11 @@ const FlyoutHeaderComponent: React.FC<FlyoutHeaderProps> = ({ timelineId }) => (
<EuiFlexGroup data-test-subj="properties-left" direction="column" gutterSize="none">
<RowFlexItem>
<TimelineName timelineId={timelineId} />
<SaveTimelineButton timelineId={timelineId} />
</RowFlexItem>
<RowFlexItem>
<TimelineDescription timelineId={timelineId} />
<SaveTimelineButton timelineId={timelineId} />
</RowFlexItem>
<EuiFlexItem>
<TimelineStatusInfo timelineId={timelineId} />

View file

@ -7,19 +7,13 @@
import React from 'react';
import { shallow } from 'enzyme';
import { TimelineTitleAndDescription } from './title_and_description';
import { useShallowEqualSelector } from '../../../../common/hooks/use_selector';
import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
import { useCreateTimelineButton } from '../properties/use_create_timeline';
import { TimelineType } from '../../../../../common/types/timeline';
import * as i18n from './translations';
jest.mock('../../../../common/hooks/use_selector', () => ({
useShallowEqualSelector: jest.fn(),
}));
jest.mock('../../../../timelines/store/timeline', () => ({
timelineSelectors: {
selectTimeline: jest.fn(),
},
useDeepEqualSelector: jest.fn(),
}));
jest.mock('../properties/use_create_timeline', () => ({
@ -47,7 +41,7 @@ describe('TimelineTitleAndDescription', () => {
const mockGetButton = jest.fn().mockReturnValue(<div data-test-subj="mock-discard-button" />);
beforeEach(() => {
(useShallowEqualSelector as jest.Mock).mockReturnValue({
(useDeepEqualSelector as jest.Mock).mockReturnValue({
description: '',
isSaving: true,
savedObjectId: null,
@ -60,7 +54,7 @@ describe('TimelineTitleAndDescription', () => {
});
afterEach(() => {
(useShallowEqualSelector as jest.Mock).mockReset();
(useDeepEqualSelector as jest.Mock).mockReset();
(useCreateTimelineButton as jest.Mock).mockReset();
mockGetButton.mockClear();
});
@ -78,7 +72,7 @@ describe('TimelineTitleAndDescription', () => {
});
test('Show correct header for save timeline template modal', () => {
(useShallowEqualSelector as jest.Mock).mockReturnValue({
(useDeepEqualSelector as jest.Mock).mockReturnValue({
description: '',
isSaving: true,
savedObjectId: null,
@ -124,7 +118,7 @@ describe('TimelineTitleAndDescription', () => {
const mockGetButton = jest.fn().mockReturnValue(<div data-test-subj="mock-discard-button" />);
beforeEach(() => {
(useShallowEqualSelector as jest.Mock).mockReturnValue({
(useDeepEqualSelector as jest.Mock).mockReturnValue({
description: 'xxxx',
isSaving: true,
savedObjectId: '1234',
@ -137,7 +131,7 @@ describe('TimelineTitleAndDescription', () => {
});
afterEach(() => {
(useShallowEqualSelector as jest.Mock).mockReset();
(useDeepEqualSelector as jest.Mock).mockReset();
(useCreateTimelineButton as jest.Mock).mockReset();
mockGetButton.mockClear();
});
@ -155,7 +149,7 @@ describe('TimelineTitleAndDescription', () => {
});
test('Show correct header for save timeline template modal', () => {
(useShallowEqualSelector as jest.Mock).mockReturnValue({
(useDeepEqualSelector as jest.Mock).mockReturnValue({
description: 'xxxx',
isSaving: true,
savedObjectId: '1234',
@ -197,7 +191,7 @@ describe('TimelineTitleAndDescription', () => {
const mockGetButton = jest.fn().mockReturnValue(<div data-test-subj="mock-discard-button" />);
beforeEach(() => {
(useShallowEqualSelector as jest.Mock).mockReturnValue({
(useDeepEqualSelector as jest.Mock).mockReturnValue({
description: '',
isSaving: true,
savedObjectId: null,
@ -211,7 +205,7 @@ describe('TimelineTitleAndDescription', () => {
});
afterEach(() => {
(useShallowEqualSelector as jest.Mock).mockReset();
(useDeepEqualSelector as jest.Mock).mockReset();
(useCreateTimelineButton as jest.Mock).mockReset();
mockGetButton.mockClear();
});
@ -237,7 +231,7 @@ describe('TimelineTitleAndDescription', () => {
});
test('get discardTimelineTemplateButton with correct props', () => {
(useShallowEqualSelector as jest.Mock).mockReturnValue({
(useDeepEqualSelector as jest.Mock).mockReturnValue({
description: 'xxxx',
isSaving: true,
savedObjectId: null,

View file

@ -15,11 +15,11 @@ import {
EuiProgress,
EuiCallOut,
} from '@elastic/eui';
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
import { TimelineType } from '../../../../../common/types/timeline';
import { useShallowEqualSelector } from '../../../../common/hooks/use_selector';
import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
import { timelineActions, timelineSelectors } from '../../../../timelines/store/timeline';
import { TimelineInput } from '../../../store/timeline/actions';
import { Description, Name } from '../properties/helpers';
@ -62,9 +62,10 @@ const usePrevious = (value: unknown) => {
// the unsaved timeline / template
export const TimelineTitleAndDescription = React.memo<TimelineTitleAndDescriptionProps>(
({ timelineId, toggleSaveTimeline, showWarning }) => {
const timeline = useShallowEqualSelector((state) =>
timelineSelectors.selectTimeline(state, timelineId)
);
// TODO: Refactor to use useForm() instead
const [isFormSubmitted, setFormSubmitted] = useState(false);
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
const timeline = useDeepEqualSelector((state) => getTimeline(state, timelineId));
const { isSaving, savedObjectId, title, timelineType } = timeline;
@ -76,10 +77,12 @@ export const TimelineTitleAndDescription = React.memo<TimelineTitleAndDescriptio
);
const handleClick = useCallback(() => {
// TODO: Refactor action to take only title and description as params not the whole timeline
onSaveTimeline({
...timeline,
id: timelineId,
});
setFormSubmitted(true);
}, [onSaveTimeline, timeline, timelineId]);
const { getButton } = useCreateTimelineButton({ timelineId, timelineType });
@ -99,10 +102,10 @@ export const TimelineTitleAndDescription = React.memo<TimelineTitleAndDescriptio
);
useEffect(() => {
if (!isSaving && prevIsSaving) {
if (isFormSubmitted && !isSaving && prevIsSaving) {
toggleSaveTimeline();
}
}, [isSaving, prevIsSaving, toggleSaveTimeline]);
}, [isFormSubmitted, isSaving, prevIsSaving, toggleSaveTimeline]);
const modalHeader =
savedObjectId == null

View file

@ -17,7 +17,7 @@ import { useSourcererScope } from '../../../common/containers/sourcerer';
import { SourcererScopeName } from '../../../common/store/sourcerer/model';
import { FlyoutHeader, FlyoutHeaderPanel } from '../flyout/header';
import { TimelineType } from '../../../../common/types/timeline';
import { useDeepEqualSelector } from '../../../common/hooks/use_selector';
import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector';
import { activeTimeline } from '../../containers/active_timeline_context';
import * as i18n from './translations';
import { TabsContent } from './tabs_content';
@ -40,13 +40,24 @@ export interface Props {
timelineId: string;
}
const TimelineSavingProgressComponent: React.FC<Props> = ({ timelineId }) => {
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
const isSaving = useShallowEqualSelector(
(state) => (getTimeline(state, timelineId) ?? timelineDefaults).isSaving
);
return isSaving ? <EuiProgress size="s" color="primary" position="absolute" /> : null;
};
const TimelineSavingProgress = React.memo(TimelineSavingProgressComponent);
const StatefulTimelineComponent: React.FC<Props> = ({ timelineId }) => {
const dispatch = useDispatch();
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
const { selectedPatterns } = useSourcererScope(SourcererScopeName.timeline);
const { graphEventId, isSaving, savedObjectId, timelineType } = useDeepEqualSelector((state) =>
const { graphEventId, savedObjectId, timelineType } = useDeepEqualSelector((state) =>
pick(
['graphEventId', 'isSaving', 'savedObjectId', 'timelineType'],
['graphEventId', 'savedObjectId', 'timelineType'],
getTimeline(state, timelineId) ?? timelineDefaults
)
);
@ -68,7 +79,7 @@ const StatefulTimelineComponent: React.FC<Props> = ({ timelineId }) => {
return (
<TimelineContainer data-test-subj="timeline">
{isSaving && <EuiProgress size="s" color="primary" position="absolute" />}
<TimelineSavingProgress timelineId={timelineId} />
{timelineType === TimelineType.template && (
<TimelineTemplateBadge>{i18n.TIMELINE_TEMPLATE}</TimelineTemplateBadge>
)}

View file

@ -79,6 +79,7 @@ const AddToFavoritesButtonComponent: React.FC<AddToFavoritesButtonProps> = ({ ti
fill={isFavorite}
iconType={isFavorite ? 'starFilled' : 'starEmpty'}
onClick={handleClick}
data-test-subj={`timeline-favorite-${isFavorite ? 'filled' : 'empty'}-star`}
>
{isFavorite ? i18n.REMOVE_FROM_FAVORITES : i18n.ADD_TO_FAVORITES}
</EuiButton>

View file

@ -61,41 +61,42 @@ const PinnedTab: React.FC<BasicTimelineTab> = memo(({ timelineId }) => (
));
PinnedTab.displayName = 'PinnedTab';
const ActiveTimelineTab: React.FC<BasicTimelineTab & { activeTimelineTab: TimelineTabs }> = memo(
({ activeTimelineTab, timelineId }) => {
const getTab = useCallback(
(tab: TimelineTabs) => {
switch (tab) {
case TimelineTabs.graph:
return <GraphTab timelineId={timelineId} />;
case TimelineTabs.notes:
return <NotesTab timelineId={timelineId} />;
case TimelineTabs.pinned:
return <PinnedTab timelineId={timelineId} />;
default:
return null;
}
},
[timelineId]
);
type ActiveTimelineTabProps = BasicTimelineTab & { activeTimelineTab: TimelineTabs };
const ActiveTimelineTab = memo<ActiveTimelineTabProps>(({ activeTimelineTab, timelineId }) => {
const getTab = useCallback(
(tab: TimelineTabs) => {
switch (tab) {
case TimelineTabs.graph:
return <GraphTab timelineId={timelineId} />;
case TimelineTabs.notes:
return <NotesTab timelineId={timelineId} />;
case TimelineTabs.pinned:
return <PinnedTab timelineId={timelineId} />;
default:
return null;
}
},
[timelineId]
);
/* Future developer -> why are we doing that
* It is really expansive to re-render the QueryTab because the drag/drop
* Therefore, we are only hiding its dom when switching to another tab
* to avoid mounting/un-mounting === re-render
*/
return (
<>
<HideShowContainer $isVisible={TimelineTabs.query === activeTimelineTab}>
<QueryTab timelineId={timelineId} />
</HideShowContainer>
<HideShowContainer $isVisible={TimelineTabs.query !== activeTimelineTab}>
{activeTimelineTab !== TimelineTabs.query && getTab(activeTimelineTab)}
</HideShowContainer>
</>
);
});
/* Future developer -> why are we doing that
* It is really expansive to re-render the QueryTab because the drag/drop
* Therefore, we are only hiding its dom when switching to another tab
* to avoid mounting/un-mounting === re-render
*/
return (
<>
<HideShowContainer $isVisible={TimelineTabs.query === activeTimelineTab}>
<QueryTab timelineId={timelineId} />
</HideShowContainer>
<HideShowContainer $isVisible={TimelineTabs.query !== activeTimelineTab}>
{activeTimelineTab !== TimelineTabs.query && getTab(activeTimelineTab)}
</HideShowContainer>
</>
);
}
);
ActiveTimelineTab.displayName = 'ActiveTimelineTab';
const TabsContentComponent: React.FC<BasicTimelineTab> = ({ timelineId, graphEventId }) => {
@ -145,6 +146,7 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({ timelineId, graphEve
<>
<EuiTabs>
<EuiTab
data-test-subj={`timelineTabs-${TimelineTabs.query}`}
onClick={setQueryAsActiveTab}
isSelected={activeTab === TimelineTabs.query}
disabled={false}
@ -153,6 +155,7 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({ timelineId, graphEve
{i18n.QUERY_TAB}
</EuiTab>
<EuiTab
data-test-subj={`timelineTabs-${TimelineTabs.graph}`}
onClick={setGraphAsActiveTab}
isSelected={activeTab === TimelineTabs.graph}
disabled={!graphEventId}
@ -161,6 +164,7 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({ timelineId, graphEve
{i18n.GRAPH_TAB}
</EuiTab>
<EuiTab
data-test-subj={`timelineTabs-${TimelineTabs.notes}`}
onClick={setNotesAsActiveTab}
isSelected={activeTab === TimelineTabs.notes}
disabled={false}
@ -169,6 +173,7 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({ timelineId, graphEve
{i18n.NOTES_TAB}
</EuiTab>
<EuiTab
data-test-subj={`timelineTabs-${TimelineTabs.pinned}`}
onClick={setPinnedAsActiveTab}
isSelected={activeTab === TimelineTabs.pinned}
disabled={true}

View file

@ -1369,10 +1369,10 @@
snap-shot-compare "2.8.3"
snap-shot-store "1.2.3"
"@cypress/webpack-preprocessor@^5.4.10":
version "5.4.10"
resolved "https://registry.yarnpkg.com/@cypress/webpack-preprocessor/-/webpack-preprocessor-5.4.10.tgz#ed0a3ed495f5455899f7c567830b37477c3f26f5"
integrity sha512-6j809mAQcZsAbpIaQFlKwFQPKv1Y+ZyLr9bpW7d2utMGVll0W8Up4RPhIuAf4q9Tx+DOBQoiCpy/n0cRPxporw==
"@cypress/webpack-preprocessor@^5.4.11":
version "5.4.11"
resolved "https://registry.yarnpkg.com/@cypress/webpack-preprocessor/-/webpack-preprocessor-5.4.11.tgz#77f86e399f04969d5d8692ada96c794c52d38f87"
integrity sha512-6kj0HsaWf1s0UT4qkABuwl676sW8S8lSTai3NUcF3BWj9BqhN4JPd2DdGcHNkNmYRkjpklPeGUHqAOyqMDj5+A==
dependencies:
bluebird "^3.7.1"
debug "^4.1.1"
@ -9978,6 +9978,11 @@ commander@^5.0.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-5.0.0.tgz#dbf1909b49e5044f8fdaf0adc809f0c0722bdfd0"
integrity sha512-JrDGPAKjMGSP1G0DUoaceEJ3DZgAfr/q6X7FVk4+U5KxUSKviYGM2k6zWkfyyBHy5rAtzgYJFa1ro2O9PtoxwQ==
commander@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
common-tags@1.8.0, common-tags@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
@ -10823,10 +10828,10 @@ cypress-promise@^1.1.0:
resolved "https://registry.yarnpkg.com/cypress-promise/-/cypress-promise-1.1.0.tgz#f2d66965945fe198431aaf692d5157cea9d47b25"
integrity sha512-DhIf5PJ/a0iY+Yii6n7Rbwq+9TJxU4pupXYzf9mZd8nPG0AzQrj9i+pqINv4xbI2EV1p+PKW3maCkR7oPG4GrA==
cypress@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-5.5.0.tgz#1da0355794a43247f8a80cb7f505e83e1cf847cb"
integrity sha512-UHEiTca8AUTevbT2pWkHQlxoHtXmbq+h6Eiu/Mz8DqpNkF98zjTBLv/HFiKJUU5rQzp9EwSWtms33p5TWCJ8tQ==
cypress@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-6.0.1.tgz#86857ca2f527c3723575737deab42fd8f2a209df"
integrity sha512-3xtQZ0YM65soLgKQUgn2wg2IbWsM6A2yBg6L4RF31mZHr5LNKdO2/9sgiwxEVMKu2C2m6+IQ75zHP41kZP5rPg==
dependencies:
"@cypress/listr-verbose-renderer" "^0.4.1"
"@cypress/request" "^2.88.5"
@ -10840,7 +10845,7 @@ cypress@^5.5.0:
chalk "^4.1.0"
check-more-types "^2.24.0"
cli-table3 "~0.6.0"
commander "^4.1.1"
commander "^5.1.0"
common-tags "^1.8.0"
debug "^4.1.1"
eventemitter2 "^6.4.2"