diff --git a/specifyweb/frontend/js_src/lib/components/WbImportAttachments/__tests__/WbImportAttachments.test.tsx b/specifyweb/frontend/js_src/lib/components/WbImportAttachments/__tests__/WbImportAttachments.test.tsx
new file mode 100644
index 00000000000..3a5df12689f
--- /dev/null
+++ b/specifyweb/frontend/js_src/lib/components/WbImportAttachments/__tests__/WbImportAttachments.test.tsx
@@ -0,0 +1,219 @@
+import React from 'react';
+import * as Router from 'react-router-dom';
+
+import { clearIdStore } from '../../../hooks/useId';
+import { overrideAjax } from '../../../tests/ajax';
+import { requireContext } from '../../../tests/helpers';
+import { mount } from '../../../tests/reactUtils';
+import * as Attachments from '../../Attachments/attachments';
+import { testAttachment } from '../../Attachments/__tests__/utils';
+import { deserializeResource } from '../../DataModel/serializers';
+import { SetMenuContext } from '../../Header/MenuContext';
+import { WbImportAttachmentsView } from '..';
+import { UnloadProtectsContext } from '../../Router/UnloadProtect';
+
+requireContext();
+
+const dataSetId = 123;
+const dataSetAttachmentId = 456;
+
+const dataSetResponse = {
+ id: dataSetId,
+ name: 'Attachments',
+ importedfilename: 'attachments',
+ columns: ['attachments'],
+ data: [[]],
+ resource_uri: `/api/specify/spdataset/${dataSetId}/`,
+};
+
+const createDataSetRequest = jest.fn(() => dataSetResponse);
+
+const createDataSetAttachmentsRequest = jest.fn(() => [
+ {
+ id: dataSetAttachmentId,
+ attachment: testAttachment,
+ spdataset: `/api/specify/spdataset/${dataSetId}/`,
+ resource_uri: `/api/specify/spdatasetattachment/${dataSetAttachmentId}/`,
+ },
+]);
+
+const updateDataSetRequest = jest.fn(() => dataSetResponse);
+
+overrideAjax('/api/workbench/dataset/', []);
+
+overrideAjax('/api/specify/spdataset/', createDataSetRequest, {
+ method: 'POST',
+});
+
+overrideAjax(
+ '/bulk_copy/bulk/spdatasetattachment/',
+ createDataSetAttachmentsRequest,
+ {
+ method: 'POST',
+ }
+);
+
+overrideAjax(`/api/specify/spdataset/${dataSetId}/`, updateDataSetRequest, {
+ method: 'PUT',
+});
+
+beforeEach(() => {
+ jest.clearAllMocks();
+ clearIdStore();
+});
+
+afterEach(() => {
+ jest.restoreAllMocks();
+});
+
+// [Import Attachments] Select multiple image files
+test('selects multiple image files', async () => {
+ const { container, getByRole, user } = mount(
+
+
+
+
+
+ );
+
+ const input = container.querySelector('input[type="file"]');
+
+ if (input === null) {
+ throw new Error('Unable to find the attachment file picker');
+ }
+
+ const files = [
+ new File(['png image'], 'first-image.png', {
+ type: 'image/png',
+ }),
+ new File(['webp image'], 'second-image.webp', {
+ type: 'image/webp',
+ }),
+ ];
+
+ expect(input).toHaveAttribute('multiple');
+
+ await user.upload(input, files);
+
+ expect(getByRole('cell', { name: 'first-image.png' })).toBeInTheDocument();
+
+ expect(getByRole('cell', { name: 'second-image.webp' })).toBeInTheDocument();
+});
+
+// [Import Attachments] Select different image file types
+test('selects different image file types', async () => {
+ const { container, getByRole, user } = mount(
+
+
+
+
+
+ );
+
+ const input = container.querySelector('input[type="file"]');
+
+ if (input === null) {
+ throw new Error('Unable to find the attachment file picker');
+ }
+
+ const files = [
+ new File(['png image'], 'image.png', {
+ type: 'image/png',
+ }),
+ new File(['webp image'], 'image.webp', {
+ type: 'image/webp',
+ }),
+ new File(['tiff image'], 'image.tiff', {
+ type: 'image/tiff',
+ }),
+ new File(['jpeg image'], 'image.jpeg', {
+ type: 'image/jpeg',
+ }),
+ new File(['gif image'], 'image.gif', {
+ type: 'image/gif',
+ }),
+ ];
+
+ expect(input).not.toHaveAttribute('accept');
+
+ await user.upload(input, files);
+
+ for (const file of files) {
+ expect(getByRole('cell', { name: file.name })).toBeInTheDocument();
+ }
+});
+
+// [Import Attachments] Import selected files into a WorkBench data set
+test('imports selected attachment files', async () => {
+ const uploadFile = jest
+ .spyOn(Attachments, 'uploadFile')
+ .mockResolvedValue(deserializeResource(testAttachment));
+
+ const { container, findByText, getByRole, user } = mount(
+
+
+
+
+
+
+ }
+ />
+ WorkBench data set created}
+ />
+
+
+
+ );
+
+ const input = container.querySelector('input[type="file"]');
+
+ if (input === null) {
+ throw new Error('Unable to find the attachment file picker');
+ }
+
+ const file = new File(['image'], 'attachment.png', {
+ type: 'image/png',
+ });
+
+ await user.upload(input, file);
+
+ await user.click(
+ getByRole('button', {
+ name: 'Import Attachments',
+ })
+ );
+
+ expect(await findByText('WorkBench data set created')).toBeInTheDocument();
+
+ expect(uploadFile).toHaveBeenCalledTimes(1);
+ expect(uploadFile).toHaveBeenCalledWith(
+ expect.objectContaining({
+ file,
+ })
+ );
+
+ expect(createDataSetRequest).toHaveBeenCalledTimes(1);
+ expect(createDataSetAttachmentsRequest).toHaveBeenCalledTimes(1);
+ expect(updateDataSetRequest).toHaveBeenCalledTimes(1);
+});