Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions x-pack/plugins/cases/public/components/add_comment/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ import { TestProviders } from '../../common/mock';

import { CommentRequest, CommentType } from '../../../common/api';
import { SECURITY_SOLUTION_OWNER } from '../../../common/constants';
import { usePostComment } from '../../containers/use_post_comment';
import { useCreateAttachments } from '../../containers/use_create_attachments';
import { AddComment, AddCommentProps, AddCommentRefObject } from '.';
import { CasesTimelineIntegrationProvider } from '../timeline_context';
import { timelineIntegrationMock } from '../__mock__/timeline';

jest.mock('../../containers/use_post_comment');
jest.mock('../../containers/use_create_attachments');

const usePostCommentMock = usePostComment as jest.Mock;
const useCreateAttachmentsMock = useCreateAttachments as jest.Mock;
const onCommentSaving = jest.fn();
const onCommentPosted = jest.fn();
const postComment = jest.fn();
const createAttachments = jest.fn();

const addCommentProps: AddCommentProps = {
id: 'newComment',
Expand All @@ -36,10 +36,10 @@ const addCommentProps: AddCommentProps = {
statusActionButton: null,
};

const defaultPostComment = {
const defaultResponse = {
isLoading: false,
isError: false,
postComment,
createAttachments,
};

const sampleData: CommentRequest = {
Expand All @@ -51,7 +51,7 @@ const sampleData: CommentRequest = {
describe('AddComment ', () => {
beforeEach(() => {
jest.clearAllMocks();
usePostCommentMock.mockImplementation(() => defaultPostComment);
useCreateAttachmentsMock.mockImplementation(() => defaultResponse);
});

it('should post comment on submit click', async () => {
Expand All @@ -72,17 +72,20 @@ describe('AddComment ', () => {
wrapper.find(`[data-test-subj="submit-comment"]`).first().simulate('click');
await waitFor(() => {
expect(onCommentSaving).toBeCalled();
expect(postComment).toBeCalledWith({
expect(createAttachments).toBeCalledWith({
caseId: addCommentProps.caseId,
data: sampleData,
data: [sampleData],
updateCase: onCommentPosted,
});
expect(wrapper.find(`[data-test-subj="add-comment"] textarea`).text()).toBe('');
});
});

it('should render spinner and disable submit when loading', () => {
usePostCommentMock.mockImplementation(() => ({ ...defaultPostComment, isLoading: true }));
useCreateAttachmentsMock.mockImplementation(() => ({
...defaultResponse,
isLoading: true,
}));
const wrapper = mount(
<TestProviders>
<AddComment {...{ ...addCommentProps, showLoading: true }} />
Expand All @@ -96,7 +99,10 @@ describe('AddComment ', () => {
});

it('should disable submit button when isLoading is true', () => {
usePostCommentMock.mockImplementation(() => ({ ...defaultPostComment, isLoading: true }));
useCreateAttachmentsMock.mockImplementation(() => ({
...defaultResponse,
isLoading: true,
}));
const wrapper = mount(
<TestProviders>
<AddComment {...addCommentProps} />
Expand All @@ -109,7 +115,10 @@ describe('AddComment ', () => {
});

it('should hide the component when the user does not have crud permissions', () => {
usePostCommentMock.mockImplementation(() => ({ ...defaultPostComment, isLoading: true }));
useCreateAttachmentsMock.mockImplementation(() => ({
...defaultResponse,
isLoading: true,
}));
const wrapper = mount(
<TestProviders>
<AddComment {...{ ...addCommentProps, userCanCrud: false }} />
Expand Down
10 changes: 5 additions & 5 deletions x-pack/plugins/cases/public/components/add_comment/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import styled from 'styled-components';
import { isEmpty } from 'lodash';

import { CommentType } from '../../../common/api';
import { usePostComment } from '../../containers/use_post_comment';
import { useCreateAttachments } from '../../containers/use_create_attachments';
import { Case } from '../../containers/types';
import { EuiMarkdownEditorRef, MarkdownEditorForm } from '../markdown_editor';
import { Form, useForm, UseField, useFormData } from '../../common/shared_imports';
Expand Down Expand Up @@ -71,7 +71,7 @@ export const AddComment = React.memo(
const editorRef = useRef<EuiMarkdownEditorRef>(null);
const [focusOnContext, setFocusOnContext] = useState(false);
const { owner } = useCasesContext();
const { isLoading, postComment } = usePostComment();
const { isLoading, createAttachments } = useCreateAttachments();

const { form } = useForm<AddCommentFormSchema>({
defaultValue: initialCommentValue,
Expand Down Expand Up @@ -112,14 +112,14 @@ export const AddComment = React.memo(
if (onCommentSaving != null) {
onCommentSaving();
}
postComment({
createAttachments({
caseId,
data: { ...data, type: CommentType.user, owner: owner[0] },
data: [{ ...data, type: CommentType.user, owner: owner[0] }],
updateCase: onCommentPosted,
});
reset();
}
}, [submit, onCommentSaving, postComment, caseId, owner, onCommentPosted, reset]);
}, [submit, onCommentSaving, createAttachments, caseId, owner, onCommentPosted, reset]);

/**
* Focus on the text area when a quote has been added.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ import { triggersActionsUiMock } from '../../../../triggers_actions_ui/public/mo
import { registerConnectorsToMockActionRegistry } from '../../common/mock/register_connectors';
import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock';
import { waitForComponentToUpdate } from '../../common/test_utils';
import { usePostComment } from '../../containers/use_post_comment';
import { useCreateAttachments } from '../../containers/use_create_attachments';
import { useGetTags } from '../../containers/use_get_tags';
import { useGetReporters } from '../../containers/use_get_reporters';

jest.mock('../../containers/use_post_comment');
jest.mock('../../containers/use_create_attachments');
jest.mock('../../containers/use_bulk_update_case');
jest.mock('../../containers/use_delete_cases');
jest.mock('../../containers/use_get_cases');
Expand All @@ -61,7 +61,7 @@ const useGetTagsMock = useGetTags as jest.Mock;
const useGetReportersMock = useGetReporters as jest.Mock;
const useKibanaMock = useKibana as jest.MockedFunction<typeof useKibana>;
const useConnectorsMock = useConnectors as jest.Mock;
const usePostCommentMock = usePostComment as jest.Mock;
const useCreateAttachmentsMock = useCreateAttachments as jest.Mock;

const mockTriggersActionsUiService = triggersActionsUiMock.createStart();

Expand All @@ -88,7 +88,10 @@ describe('AllCasesListGeneric', () => {
const fetchCasesStatus = jest.fn();
const onRowClick = jest.fn();
const emptyTag = getEmptyTagValue().props.children;
usePostCommentMock.mockReturnValue({ status: { isLoading: false }, postComment: jest.fn() });
useCreateAttachmentsMock.mockReturnValue({
status: { isLoading: false },
createAttachments: jest.fn(),
});

const defaultGetCases = {
...useGetCasesMockState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import { Case, CaseStatuses, StatusAll } from '../../../../common';
import { AppMockRenderer, createAppMockRenderer } from '../../../common/mock';
import { useCasesToast } from '../../../common/use_cases_toast';
import { alertComment } from '../../../containers/mock';
import { usePostComment } from '../../../containers/use_post_comment';
import { useCreateAttachments } from '../../../containers/use_create_attachments';
import { SupportedCaseAttachment } from '../../../types';
import { CasesContext } from '../../cases_context';
import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer';
import { useCasesAddToExistingCaseModal } from './use_cases_add_to_existing_case_modal';

jest.mock('../../../common/use_cases_toast');
jest.mock('../../../containers/use_post_comment');
jest.mock('../../../containers/use_create_attachments');
// dummy mock, will call onRowclick when rendering
jest.mock('./all_cases_selector_modal', () => {
return {
Expand All @@ -46,11 +46,11 @@ const TestComponent: React.FC = () => {
return <button type="button" data-test-subj="open-modal" onClick={onClick} />;
};

const usePostCommentMock = usePostComment as jest.Mock;
const useCreateAttachmentsMock = useCreateAttachments as jest.Mock;

describe('use cases add to existing case modal hook', () => {
usePostCommentMock.mockReturnValue({
postComment: jest.fn(),
useCreateAttachmentsMock.mockReturnValue({
createAttachments: jest.fn(),
});

const dispatch = jest.fn();
Expand Down Expand Up @@ -130,10 +130,10 @@ describe('use cases add to existing case modal hook', () => {
);
});

it('should call postComment when a case is selected and show a toast message', async () => {
const mockedPostMessage = jest.fn();
usePostCommentMock.mockReturnValueOnce({
postComment: mockedPostMessage,
it('should call createAttachments when a case is selected and show a toast message', async () => {
const mockBulkCreateAttachments = jest.fn();
useCreateAttachmentsMock.mockReturnValueOnce({
createAttachments: mockBulkCreateAttachments,
});

const mockedToastSuccess = jest.fn();
Expand All @@ -150,19 +150,20 @@ describe('use cases add to existing case modal hook', () => {
userEvent.click(result.getByTestId('open-modal'));

await waitFor(() => {
expect(mockedPostMessage).toHaveBeenCalledWith({
expect(mockBulkCreateAttachments).toHaveBeenCalledTimes(1);
expect(mockBulkCreateAttachments).toHaveBeenCalledWith({
caseId: 'test',
data: alertComment,
data: [alertComment],
throwOnError: true,
});
});
expect(mockedToastSuccess).toHaveBeenCalled();
});

it('should not call postComment nor show toast success when a case is not selected', async () => {
const mockedPostMessage = jest.fn();
usePostCommentMock.mockReturnValueOnce({
postComment: mockedPostMessage,
it('should not call createAttachments nor show toast success when a case is not selected', async () => {
const mockBulkCreateAttachments = jest.fn();
useCreateAttachmentsMock.mockReturnValueOnce({
createAttachments: mockBulkCreateAttachments,
});

const mockedToastSuccess = jest.fn();
Expand All @@ -180,15 +181,15 @@ describe('use cases add to existing case modal hook', () => {
// give a small delay for the reducer to run

act(() => {
expect(mockedPostMessage).not.toHaveBeenCalled();
expect(mockBulkCreateAttachments).not.toHaveBeenCalled();
expect(mockedToastSuccess).not.toHaveBeenCalled();
});
});

it('should not show toast success when a case is selected with attachments and fails to update attachments', async () => {
const mockedPostMessage = jest.fn().mockRejectedValue(new Error('Impossible'));
usePostCommentMock.mockReturnValueOnce({
postComment: mockedPostMessage,
const mockBulkCreateAttachments = jest.fn().mockRejectedValue(new Error('Impossible'));
useCreateAttachmentsMock.mockReturnValueOnce({
createAttachments: mockBulkCreateAttachments,
});

const mockedToast = jest.fn();
Expand All @@ -206,15 +207,15 @@ describe('use cases add to existing case modal hook', () => {
userEvent.click(result.getByTestId('open-modal'));

await waitFor(() => {
expect(mockedPostMessage).toHaveBeenCalledWith({
expect(mockBulkCreateAttachments).toHaveBeenCalledWith({
caseId: 'test',
data: alertComment,
data: [alertComment],
throwOnError: true,
});
});

act(() => {
expect(mockedPostMessage).toHaveBeenCalled();
expect(mockBulkCreateAttachments).toHaveBeenCalled();
expect(mockedToast).not.toHaveBeenCalled();
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { CasesContextStoreActionsList } from '../../cases_context/cases_context_
import { useCasesContext } from '../../cases_context/use_cases_context';
import { useCasesAddToNewCaseFlyout } from '../../create/flyout/use_cases_add_to_new_case_flyout';
import { CaseAttachments } from '../../../types';
import { usePostComment } from '../../../containers/use_post_comment';
import { useCreateAttachments } from '../../../containers/use_create_attachments';

type AddToExistingFlyoutProps = AllCasesSelectorModalProps & {
toastTitle?: string;
Expand All @@ -39,7 +39,7 @@ export const useCasesAddToExistingCaseModal = (props: AddToExistingFlyoutProps)

const { dispatch } = useCasesContext();
const casesToasts = useCasesToast();
const { postComment } = usePostComment();
const { createAttachments } = useCreateAttachments();

const closeModal = useCallback(() => {
dispatch({
Expand All @@ -66,13 +66,12 @@ export const useCasesAddToExistingCaseModal = (props: AddToExistingFlyoutProps)
// add attachments to the case
const attachments = props.attachments;
if (attachments !== undefined && attachments.length > 0) {
for (const attachment of attachments) {
await postComment({
caseId: theCase.id,
data: attachment,
throwOnError: true,
});
}
await createAttachments({
caseId: theCase.id,
data: attachments,
throwOnError: true,
});

casesToasts.showSuccessAttach({
theCase,
attachments: props.attachments,
Expand All @@ -82,14 +81,14 @@ export const useCasesAddToExistingCaseModal = (props: AddToExistingFlyoutProps)
}
} catch (error) {
// error toast is handled
// inside the postComment method
// inside the createAttachments method
}

if (props.onRowClick) {
props.onRowClick(theCase);
}
},
[casesToasts, closeModal, createNewCaseFlyout, postComment, props]
[casesToasts, closeModal, createNewCaseFlyout, createAttachments, props]
);

const openModal = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ import { EuiFlyout, EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eu
import * as i18n from '../translations';
import { Case } from '../../../../common/ui/types';
import { CreateCaseForm } from '../form';
import { UsePostComment } from '../../../containers/use_post_comment';
import { UseCreateAttachments } from '../../../containers/use_create_attachments';
import { CaseAttachments } from '../../../types';

export interface CreateCaseFlyoutProps {
afterCaseCreated?: (theCase: Case, postComment: UsePostComment['postComment']) => Promise<void>;
afterCaseCreated?: (
theCase: Case,
createAttachments: UseCreateAttachments['createAttachments']
) => Promise<void>;
onClose?: () => void;
onSuccess?: (theCase: Case) => Promise<void>;
attachments?: CaseAttachments;
Expand Down
7 changes: 5 additions & 2 deletions x-pack/plugins/cases/public/components/create/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { ActionConnector } from '../../../common/api';
import { Case } from '../../containers/types';
import { CasesTimelineIntegration, CasesTimelineIntegrationProvider } from '../timeline_context';
import { InsertTimeline } from '../insert_timeline';
import { UsePostComment } from '../../containers/use_post_comment';
import { UseCreateAttachments } from '../../containers/use_create_attachments';
import { SubmitCaseButton } from './submit_button';
import { FormContext } from './form_context';
import { useCasesFeatures } from '../cases_context/use_cases_features';
Expand Down Expand Up @@ -61,7 +61,10 @@ export interface CreateCaseFormFieldsProps {
export interface CreateCaseFormProps extends Pick<Partial<CreateCaseFormFieldsProps>, 'withSteps'> {
onCancel: () => void;
onSuccess: (theCase: Case) => Promise<void>;
afterCaseCreated?: (theCase: Case, postComment: UsePostComment['postComment']) => Promise<void>;
afterCaseCreated?: (
theCase: Case,
createAttachments: UseCreateAttachments['createAttachments']
) => Promise<void>;
timelineIntegration?: CasesTimelineIntegration;
attachments?: CaseAttachments;
}
Expand Down
Loading