[Cases] Fix connector information disappearing (#110914)
* Move intialization to use effect * Fixing fields can't get test working * Fix tests Co-authored-by: Christos Nasikas <christos.nasikas@elastic.co>
This commit is contained in:
parent
b6ab15e9f4
commit
a2c848e1d2
|
@ -57,8 +57,8 @@ export const useGetFieldsByIssueType = ({
|
|||
});
|
||||
|
||||
if (!didCancel.current) {
|
||||
setIsLoading(false);
|
||||
setFields(res.data ?? {});
|
||||
setIsLoading(false);
|
||||
if (res.status && res.status === 'error') {
|
||||
toastNotifications.addDanger({
|
||||
title: i18n.FIELDS_API_ERROR,
|
||||
|
|
|
@ -56,13 +56,14 @@ export const useGetIssueTypes = ({
|
|||
});
|
||||
|
||||
if (!didCancel.current) {
|
||||
setIsLoading(false);
|
||||
const asOptions = (res.data ?? []).map((type) => ({
|
||||
text: type.name ?? '',
|
||||
value: type.id ?? '',
|
||||
}));
|
||||
|
||||
setIssueTypes(res.data ?? []);
|
||||
handleIssueType(asOptions);
|
||||
setIsLoading(false);
|
||||
if (res.status && res.status === 'error') {
|
||||
toastNotifications.addDanger({
|
||||
title: i18n.ISSUE_TYPES_API_ERROR,
|
||||
|
|
|
@ -7,16 +7,14 @@
|
|||
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import { render, waitFor, screen } from '@testing-library/react';
|
||||
|
||||
import { EditConnector, EditConnectorProps } from './index';
|
||||
import { getFormMock, useFormMock } from '../__mock__/form';
|
||||
import { TestProviders } from '../../common/mock';
|
||||
import { connectorsMock } from '../../containers/configure/mock';
|
||||
import { basicCase, basicPush, caseUserActions } from '../../containers/mock';
|
||||
import { useKibana } from '../../common/lib/kibana';
|
||||
|
||||
jest.mock('../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form');
|
||||
jest.mock('../../common/lib/kibana');
|
||||
const useKibanaMock = useKibana as jest.Mocked<typeof useKibana>;
|
||||
|
||||
|
@ -50,17 +48,32 @@ const defaultProps: EditConnectorProps = {
|
|||
};
|
||||
|
||||
describe('EditConnector ', () => {
|
||||
const sampleConnector = '123';
|
||||
const formHookMock = getFormMock({ connectorId: sampleConnector });
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
useFormMock.mockImplementation(() => ({ form: formHookMock }));
|
||||
useKibanaMock().services.triggersActionsUi.actionTypeRegistry.get = jest.fn().mockReturnValue({
|
||||
actionTypeTitle: '.servicenow',
|
||||
iconClass: 'logoSecurity',
|
||||
});
|
||||
});
|
||||
|
||||
it('Renders servicenow connector from case initially', async () => {
|
||||
const serviceNowProps = {
|
||||
...defaultProps,
|
||||
caseData: {
|
||||
...defaultProps.caseData,
|
||||
connector: { ...defaultProps.caseData.connector, id: 'servicenow-1' },
|
||||
},
|
||||
};
|
||||
|
||||
render(
|
||||
<TestProviders>
|
||||
<EditConnector {...serviceNowProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(await screen.findByText('My Connector')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders no connector, and then edit', async () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
|
@ -98,58 +111,81 @@ describe('EditConnector ', () => {
|
|||
expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().exists()).toBeTruthy();
|
||||
|
||||
wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click');
|
||||
await waitFor(() => expect(onSubmit.mock.calls[0][0]).toBe(sampleConnector));
|
||||
await waitFor(() => expect(onSubmit.mock.calls[0][0]).toBe('resilient-2'));
|
||||
});
|
||||
|
||||
it('Revert to initial external service on error', async () => {
|
||||
onSubmit.mockImplementation((connector, onSuccess, onError) => {
|
||||
onError(new Error('An error has occurred'));
|
||||
});
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<EditConnector {...defaultProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
wrapper.find('[data-test-subj="connector-edit"] button').simulate('click');
|
||||
|
||||
wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click');
|
||||
wrapper.update();
|
||||
wrapper.find('button[data-test-subj="dropdown-connector-resilient-2"]').simulate('click');
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().exists()).toBeTruthy();
|
||||
|
||||
wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click');
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
expect(formHookMock.setFieldValue).toHaveBeenCalledWith('connectorId', 'none');
|
||||
});
|
||||
});
|
||||
|
||||
it('Resets selector on cancel', async () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
caseData: {
|
||||
...defaultProps.caseData,
|
||||
connector: { ...defaultProps.caseData.connector, id: 'servicenow-1' },
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<EditConnector {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
wrapper.find('[data-test-subj="connector-edit"] button').simulate('click');
|
||||
|
||||
wrapper.find('[data-test-subj="connector-edit"] button').simulate('click');
|
||||
wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click');
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
wrapper.find('button[data-test-subj="dropdown-connector-resilient-2"]').simulate('click');
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().exists()
|
||||
).toBeTruthy();
|
||||
wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click');
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
/**
|
||||
* If an error is being throw on submit the selected connector should
|
||||
* be reverted to the initial one. In our test the initial one is the .servicenow-1
|
||||
* connector. The title of the .servicenow-1 connector is My Connector.
|
||||
*/
|
||||
expect(wrapper.text().includes('My Connector')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Resets selector on cancel', async () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
caseData: {
|
||||
...defaultProps.caseData,
|
||||
connector: { ...defaultProps.caseData.connector, id: 'servicenow-1' },
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<EditConnector {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
wrapper.find('[data-test-subj="connector-edit"] button').simulate('click');
|
||||
wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click');
|
||||
wrapper.update();
|
||||
wrapper.find('button[data-test-subj="dropdown-connector-resilient-2"]').simulate('click');
|
||||
wrapper.update();
|
||||
|
||||
wrapper.find(`[data-test-subj="edit-connectors-cancel"]`).last().simulate('click');
|
||||
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
expect(formHookMock.setFieldValue).toBeCalledWith(
|
||||
'connectorId',
|
||||
defaultProps.caseData.connector.id
|
||||
);
|
||||
expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
expect(wrapper.text().includes('My Connector')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Renders loading spinner', async () => {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useReducer } from 'react';
|
||||
import React, { useCallback, useEffect, useReducer } from 'react';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import {
|
||||
EuiText,
|
||||
|
@ -144,33 +144,54 @@ export const EditConnector = React.memo(
|
|||
{ ...initialState, fields: caseFields }
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize the current connector with the connector information attached to the case if we can find that
|
||||
// connector in the retrieved connectors from the API call
|
||||
if (!isLoading) {
|
||||
dispatch({
|
||||
type: 'SET_CURRENT_CONNECTOR',
|
||||
payload: getConnectorById(caseData.connector.id, connectors),
|
||||
});
|
||||
|
||||
// Set the fields initially to whatever is present in the case, this should match with
|
||||
// the latest user action for an update connector as well
|
||||
dispatch({
|
||||
type: 'SET_FIELDS',
|
||||
payload: caseFields,
|
||||
});
|
||||
}
|
||||
}, [caseData.connector.id, connectors, isLoading, caseFields]);
|
||||
|
||||
/**
|
||||
* There is a race condition with this callback. At some point during the initial mounting of this component, this
|
||||
* callback will be called. There are a couple problems with this:
|
||||
*
|
||||
* 1. If the call occurs before the above useEffect does its dispatches (aka while the connectors are still loading) this will
|
||||
* result in setting the current connector to null when in fact we might have a valid connector. It could also
|
||||
* cause issues when setting the fields because if there are no user actions then the getConnectorFieldsFromUserActions
|
||||
* will return null even when the caseData.connector.fields is valid and populated.
|
||||
*
|
||||
* 2. If the call occurs after the above useEffect then the currentConnector should === newConnectorId
|
||||
*
|
||||
* As far as I know dispatch is synchronous so if the useEffect runs first it should successfully set currentConnector. If
|
||||
* onChangeConnector runs first and sets stuff to null, then when useEffect runs it'll switch everything back to what we need it to be
|
||||
* initially.
|
||||
*/
|
||||
const onChangeConnector = useCallback(
|
||||
(newConnectorId) => {
|
||||
// Init
|
||||
if (currentConnector == null) {
|
||||
// change connector on dropdown action
|
||||
if (currentConnector?.id !== newConnectorId) {
|
||||
dispatch({
|
||||
type: 'SET_CURRENT_CONNECTOR',
|
||||
payload: getConnectorById(newConnectorId, connectors),
|
||||
});
|
||||
}
|
||||
// change connect on dropdown action
|
||||
else if (currentConnector.id !== newConnectorId) {
|
||||
dispatch({
|
||||
type: 'SET_CURRENT_CONNECTOR',
|
||||
payload: getConnectorById(newConnectorId, connectors),
|
||||
});
|
||||
dispatch({
|
||||
type: 'SET_FIELDS',
|
||||
payload: getConnectorFieldsFromUserActions(newConnectorId, userActions ?? []),
|
||||
});
|
||||
} else if (fields === null) {
|
||||
dispatch({
|
||||
type: 'SET_FIELDS',
|
||||
payload: getConnectorFieldsFromUserActions(newConnectorId, userActions ?? []),
|
||||
});
|
||||
}
|
||||
},
|
||||
[currentConnector, fields, userActions, connectors]
|
||||
[currentConnector, userActions, connectors]
|
||||
);
|
||||
|
||||
const onFieldsChange = useCallback(
|
||||
|
|
Loading…
Reference in a new issue