Skip to content

Commit

Permalink
Changed implementation of isRegisteredOwner.
Browse files Browse the repository at this point in the history
Created tests for AddFile component.
Added more tests for FilesTable component.
Created parseMimeType util.
Added a test for showDangerToast.
  • Loading branch information
adcoelho committed Mar 27, 2023
1 parent 81b41d1 commit 68c3047
Show file tree
Hide file tree
Showing 10 changed files with 317 additions and 12 deletions.
9 changes: 8 additions & 1 deletion x-pack/plugins/cases/public/common/mock/test_providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ window.scrollTo = jest.fn();

export const mockedFilesClient = createMockFilesClient() as unknown as ScopedFilesClient<unknown>;

// @ts-ignore
mockedFilesClient.getFileKind.mockImplementation(() => ({
id: 'test',
maxSizeBytes: 10000,
http: {},
}));

const mockGetFilesClient = () => mockedFilesClient;

export const mockedTestProvidersOwner = [SECURITY_SOLUTION_OWNER];
Expand Down Expand Up @@ -141,7 +148,7 @@ export const testQueryClient = new QueryClient({

export const createAppMockRenderer = ({
features,
owner = [SECURITY_SOLUTION_OWNER],
owner = mockedTestProvidersOwner,
permissions = allCasesPermissions(),
releasePhase = 'ga',
externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry(),
Expand Down
19 changes: 19 additions & 0 deletions x-pack/plugins/cases/public/common/use_cases_toast.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const useKibanaMock = useKibana as jest.Mocked<typeof useKibana>;
describe('Use cases toast hook', () => {
const successMock = jest.fn();
const errorMock = jest.fn();
const dangerMock = jest.fn();
const getUrlForApp = jest.fn().mockReturnValue(`/app/cases/${mockCase.id}`);
const navigateToUrl = jest.fn();

Expand Down Expand Up @@ -54,6 +55,7 @@ describe('Use cases toast hook', () => {
return {
addSuccess: successMock,
addError: errorMock,
addDanger: dangerMock,
};
});

Expand Down Expand Up @@ -352,4 +354,21 @@ describe('Use cases toast hook', () => {
});
});
});

describe('showDangerToast', () => {
it('should show a danger toast', () => {
const { result } = renderHook(
() => {
return useCasesToast();
},
{ wrapper: TestProviders }
);

result.current.showDangerToast('my danger toast');

expect(dangerMock).toHaveBeenCalledWith({
title: 'my danger toast',
});
});
});
});
2 changes: 1 addition & 1 deletion x-pack/plugins/cases/public/common/use_cases_toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export const useCasesToast = () => {
toasts.addSuccess({ title, className: 'eui-textBreakWord' });
},
showDangerToast: (title: string) => {
toasts.addDanger({ title, className: 'eui-textBreakWord' });
toasts.addDanger({ title });
},
};
};
Expand Down
217 changes: 217 additions & 0 deletions x-pack/plugins/cases/public/components/files/add_file.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/*
* 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 React from 'react';

import type { FileUploadProps } from '@kbn/shared-ux-file-upload';

import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import type { AppMockRenderer } from '../../common/mock';

import { constructFileKindIdByOwner } from '../../../common/constants';
import {
createAppMockRenderer,
mockedTestProvidersOwner,
mockedFilesClient,
} from '../../common/mock';
import { AddFile } from './add_file';
import { useToasts } from '../../common/lib/kibana';

import { useCreateAttachments } from '../../containers/use_create_attachments';
import { basicFileMock } from '../../containers/mock';

jest.mock('../../containers/use_create_attachments');
jest.mock('../../common/lib/kibana');

const useToastsMock = useToasts as jest.Mock;
const useCreateAttachmentsMock = useCreateAttachments as jest.Mock;

const mockedExternalReferenceId = 'externalReferenceId';
const validateMetadata = jest.fn();
const mockFileUpload = jest
.fn()
.mockImplementation(
({
kind,
onDone,
onError,
meta,
}: Required<Pick<FileUploadProps, 'kind' | 'onDone' | 'onError' | 'meta'>>) => (
<>
<button
data-test-subj="testOnDone"
type="button"
onClick={() =>
onDone([{ id: mockedExternalReferenceId, kind, fileJSON: { ...basicFileMock, meta } }])
}
>
{'test'}
</button>
<button
data-test-subj="testOnError"
type="button"
onClick={() => onError({ name: 'upload error name', message: 'upload error message' })}
>
{'test'}
</button>
<button data-test-subj="testMetadata" type="button" onClick={() => validateMetadata(meta)}>
{'test'}
</button>
</>
)
);

jest.mock('@kbn/shared-ux-file-upload', () => {
const original = jest.requireActual('@kbn/shared-ux-file-upload');
return {
...original,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
FileUpload: (props: any) => mockFileUpload(props),
};
});

describe('AddFile', () => {
let appMockRender: AppMockRenderer;

const successMock = jest.fn();
const errorMock = jest.fn();

useToastsMock.mockImplementation(() => {
return {
addSuccess: successMock,
addError: errorMock,
};
});

const createAttachmentsMock = jest.fn();

useCreateAttachmentsMock.mockReturnValue({
isLoading: false,
createAttachments: createAttachmentsMock,
});

beforeEach(() => {
jest.clearAllMocks();
appMockRender = createAppMockRenderer();
});

it('renders correctly', async () => {
appMockRender.render(<AddFile caseId={'foobar'} />);

expect(await screen.findByTestId('cases-files-add')).toBeInTheDocument();
});

it('clicking button renders modal', async () => {
appMockRender.render(<AddFile caseId={'foobar'} />);

userEvent.click(await screen.findByTestId('cases-files-add'));

expect(await screen.findByTestId('cases-files-add-modal')).toBeInTheDocument();
});

it('createAttachments called with right parameters', async () => {
appMockRender.render(<AddFile caseId={'foobar'} />);

userEvent.click(await screen.findByTestId('cases-files-add'));

expect(await screen.findByTestId('cases-files-add-modal')).toBeInTheDocument();

userEvent.click(await screen.findByTestId('testOnDone'));

await waitFor(() =>
expect(createAttachmentsMock).toBeCalledWith(
expect.objectContaining({
caseId: 'foobar',
caseOwner: mockedTestProvidersOwner[0],
data: [
{
externalReferenceAttachmentTypeId: '.files',
externalReferenceId: mockedExternalReferenceId,
externalReferenceMetadata: {
files: [
{
createdAt: '2020-02-19T23:06:33.798Z',
extension: 'png',
mimeType: 'image/png',
name: 'my-super-cool-screenshot',
},
],
},
externalReferenceStorage: { soType: 'file', type: 'savedObject' },
type: 'externalReference',
},
],
throwOnError: true,
})
)
);
await waitFor(() =>
expect(successMock).toHaveBeenCalledWith({
className: 'eui-textBreakWord',
title: `File ${basicFileMock.name} uploaded successfully`,
})
);
});

it('failed upload displays error toast', async () => {
appMockRender.render(<AddFile caseId={'foobar'} />);

userEvent.click(await screen.findByTestId('cases-files-add'));

expect(await screen.findByTestId('cases-files-add-modal')).toBeInTheDocument();

userEvent.click(await screen.findByTestId('testOnError'));

expect(errorMock).toHaveBeenCalledWith(
{ name: 'upload error name', message: 'upload error message' },
{
title: 'Failed to upload file',
}
);
});

it('correct metadata is passed to FileUpload component', async () => {
const caseId = 'foobar';

appMockRender.render(<AddFile caseId={caseId} />);

userEvent.click(await screen.findByTestId('cases-files-add'));

expect(await screen.findByTestId('cases-files-add-modal')).toBeInTheDocument();

userEvent.click(await screen.findByTestId('testMetadata'));

await waitFor(() =>
expect(validateMetadata).toHaveBeenCalledWith({ caseId, owner: mockedTestProvidersOwner[0] })
);
});

it('filesClient.delete is called correctly if createAttachments fails', async () => {
createAttachmentsMock.mockImplementation(() => {
throw new Error();
});

appMockRender.render(<AddFile caseId={'foobar'} />);

userEvent.click(await screen.findByTestId('cases-files-add'));

expect(await screen.findByTestId('cases-files-add-modal')).toBeInTheDocument();

userEvent.click(await screen.findByTestId('testOnDone'));

await waitFor(() =>
expect(mockedFilesClient.delete).toHaveBeenCalledWith({
id: mockedExternalReferenceId,
kind: constructFileKindIdByOwner(mockedTestProvidersOwner[0]),
})
);

createAttachmentsMock.mockRestore();
});
});
1 change: 0 additions & 1 deletion x-pack/plugins/cases/public/components/files/add_file.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ const AddFileComponent: React.FC<AddFileProps> = ({ caseId }) => {
const { owner } = useCasesContext();
const { client: filesClient } = useFilesContext();
const { showDangerToast, showErrorToast, showSuccessToast } = useCasesToast();

const { isLoading, createAttachments } = useCreateAttachments();
const refreshAttachmentsTable = useRefreshCaseViewPage();
const [isModalVisible, setIsModalVisible] = useState(false);
Expand Down
49 changes: 46 additions & 3 deletions x-pack/plugins/cases/public/components/files/files_table.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import { screen, within } from '@testing-library/react';

import { basicFileMock } from '../../containers/mock';
import type { AppMockRenderer } from '../../common/mock';
import { createAppMockRenderer } from '../../common/mock';

import { constructFileKindIdByOwner } from '../../../common/constants';
import {
createAppMockRenderer,
mockedFilesClient,
mockedTestProvidersOwner,
} from '../../common/mock';
import { FilesTable } from './files_table';
import userEvent from '@testing-library/user-event';

Expand Down Expand Up @@ -73,7 +79,7 @@ describe('FilesTable', () => {
)
);

expect(await screen.queryByTestId('case-files-image-preview')).not.toBeInTheDocument();
expect(await screen.queryByTestId('cases-files-image-preview')).not.toBeInTheDocument();
});

it('image rows open file preview', async () => {
Expand All @@ -84,6 +90,43 @@ describe('FilesTable', () => {
name: `${basicFileMock.name}.${basicFileMock.extension}`,
})
);
expect(await screen.findByTestId('case-files-image-preview')).toBeInTheDocument();

expect(await screen.findByTestId('cases-files-image-preview')).toBeInTheDocument();
});

it('different mimeTypes are displayed correctly', async () => {
const mockPagination = { pageIndex: 0, pageSize: 10, totalItemCount: 7 };
appMockRender.render(
<FilesTable
{...defaultProps}
pagination={mockPagination}
items={[
{ ...basicFileMock, mimeType: '' },
{ ...basicFileMock, mimeType: 'no-slash' },
{ ...basicFileMock, mimeType: '/slash-in-the-beginning' },
{ ...basicFileMock, mimeType: undefined },
{ ...basicFileMock, mimeType: 'application/gzip' },
{ ...basicFileMock, mimeType: 'text/csv' },
{ ...basicFileMock, mimeType: 'image/tiff' },
]}
/>
);

expect((await screen.findAllByText('Unknown')).length).toBe(4);
expect(await screen.findByText('Application')).toBeInTheDocument();
expect(await screen.findByText('Text')).toBeInTheDocument();
expect(await screen.findByText('Image')).toBeInTheDocument();
});

it('download button renders correctly', async () => {
appMockRender.render(<FilesTable {...defaultProps} />);

expect(mockedFilesClient.getDownloadHref).toBeCalledTimes(1);
expect(mockedFilesClient.getDownloadHref).toHaveBeenCalledWith({
fileKind: constructFileKindIdByOwner(mockedTestProvidersOwner[0]),
id: basicFileMock.id,
});

expect(await screen.findByTestId('cases-files-table-action-download')).toBeInTheDocument();
});
});
10 changes: 7 additions & 3 deletions x-pack/plugins/cases/public/components/files/translations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,20 @@ export const TYPE = i18n.translate('xpack.cases.caseView.files.type', {
defaultMessage: 'Type',
});

export const SEARCH_PLACEHOLDER = i18n.translate('xpack.cases.caseViewFiles.searchPlaceholder', {
export const SEARCH_PLACEHOLDER = i18n.translate('xpack.cases.caseView.files.searchPlaceholder', {
defaultMessage: 'Search files',
});

export const FAILED_UPLOAD = i18n.translate('xpack.cases.caseView.failedUpload', {
export const FAILED_UPLOAD = i18n.translate('xpack.cases.caseView.files.failedUpload', {
defaultMessage: 'Failed to upload file',
});

export const UNKNOWN_MIME_TYPE = i18n.translate('xpack.cases.caseView.files.unknownMimeType', {
defaultMessage: 'Unknown',
});

export const SUCCESSFUL_UPLOAD_FILE_NAME = (fileName: string) =>
i18n.translate('xpack.cases.caseView.successfulUploadFileName', {
i18n.translate('xpack.cases.caseView.files.successfulUploadFileName', {
defaultMessage: 'File {fileName} uploaded successfully',
values: { fileName },
});
Expand Down
Loading

0 comments on commit 68c3047

Please sign in to comment.