|
@@ -1,133 +1,198 @@
|
|
|
|
|
+// Licensed to Cloudera, Inc. under one
|
|
|
|
|
+// or more contributor license agreements. See the NOTICE file
|
|
|
|
|
+// distributed with this work for additional information
|
|
|
|
|
+// regarding copyright ownership. Cloudera, Inc. licenses this file
|
|
|
|
|
+// to you under the Apache License, Version 2.0 (the
|
|
|
|
|
+// "License"); you may not use this file except in compliance
|
|
|
|
|
+// with the License. You may obtain a copy of the License at
|
|
|
|
|
+//
|
|
|
|
|
+// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
+//
|
|
|
|
|
+// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
+// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
+// See the License for the specific language governing permissions and
|
|
|
|
|
+// limitations under the License.
|
|
|
|
|
+
|
|
|
import React from 'react';
|
|
import React from 'react';
|
|
|
-import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
|
|
|
|
|
|
|
+import { render, screen, fireEvent, waitFor, cleanup } from '@testing-library/react';
|
|
|
|
|
+import userEvent from '@testing-library/user-event';
|
|
|
import '@testing-library/jest-dom';
|
|
import '@testing-library/jest-dom';
|
|
|
import CreateAndUploadAction from './CreateAndUploadAction';
|
|
import CreateAndUploadAction from './CreateAndUploadAction';
|
|
|
import { CREATE_DIRECTORY_API_URL, CREATE_FILE_API_URL } from '../../../api';
|
|
import { CREATE_DIRECTORY_API_URL, CREATE_FILE_API_URL } from '../../../api';
|
|
|
|
|
+import * as storageUtils from '../../../../../utils/storageBrowserUtils';
|
|
|
|
|
|
|
|
const mockSave = jest.fn();
|
|
const mockSave = jest.fn();
|
|
|
jest.mock('../../../../../utils/hooks/useSaveData/useSaveData', () => ({
|
|
jest.mock('../../../../../utils/hooks/useSaveData/useSaveData', () => ({
|
|
|
__esModule: true,
|
|
__esModule: true,
|
|
|
default: jest.fn(() => ({
|
|
default: jest.fn(() => ({
|
|
|
- save: mockSave
|
|
|
|
|
|
|
+ save: mockSave,
|
|
|
|
|
+ loading: false,
|
|
|
|
|
+ error: undefined
|
|
|
}))
|
|
}))
|
|
|
}));
|
|
}));
|
|
|
|
|
|
|
|
-jest.mock('../../../../../utils/huePubSub', () => ({
|
|
|
|
|
- __esModule: true,
|
|
|
|
|
- publish: jest.fn()
|
|
|
|
|
-}));
|
|
|
|
|
-
|
|
|
|
|
describe('CreateAndUploadAction', () => {
|
|
describe('CreateAndUploadAction', () => {
|
|
|
- const currentPath = '/some/path';
|
|
|
|
|
|
|
+ const defaultPath = '/some/path';
|
|
|
const onActionSuccess = jest.fn();
|
|
const onActionSuccess = jest.fn();
|
|
|
const onActionError = jest.fn();
|
|
const onActionError = jest.fn();
|
|
|
const mockFilesUpload = jest.fn();
|
|
const mockFilesUpload = jest.fn();
|
|
|
|
|
|
|
|
- beforeEach(() => {
|
|
|
|
|
- jest.clearAllMocks();
|
|
|
|
|
-
|
|
|
|
|
|
|
+ const setup = (path = defaultPath) =>
|
|
|
render(
|
|
render(
|
|
|
<CreateAndUploadAction
|
|
<CreateAndUploadAction
|
|
|
- currentPath={currentPath}
|
|
|
|
|
|
|
+ currentPath={path}
|
|
|
onActionSuccess={onActionSuccess}
|
|
onActionSuccess={onActionSuccess}
|
|
|
onFilesUpload={mockFilesUpload}
|
|
onFilesUpload={mockFilesUpload}
|
|
|
onActionError={onActionError}
|
|
onActionError={onActionError}
|
|
|
/>
|
|
/>
|
|
|
);
|
|
);
|
|
|
|
|
+
|
|
|
|
|
+ const openDropdown = async user => {
|
|
|
|
|
+ await user.click(screen.getByRole('button', { name: 'New' }));
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ beforeEach(() => {
|
|
|
|
|
+ jest.clearAllMocks();
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isS3').mockReturnValue(false);
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isGS').mockReturnValue(false);
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isABFS').mockReturnValue(false);
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isOFS').mockReturnValue(false);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const clickMenuOption = async (label: string, user) => {
|
|
|
|
|
+ await openDropdown(user);
|
|
|
|
|
+ await user.click(screen.getByRole('menuitem', { name: label }));
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ afterEach(() => {
|
|
|
|
|
+ cleanup();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it('should render the dropdown with actions', async () => {
|
|
|
|
|
|
|
+ it('renders the New button', () => {
|
|
|
|
|
+ setup();
|
|
|
const newButton = screen.getByRole('button', { name: 'New' });
|
|
const newButton = screen.getByRole('button', { name: 'New' });
|
|
|
expect(newButton).toBeInTheDocument();
|
|
expect(newButton).toBeInTheDocument();
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- await act(async () => fireEvent.click(newButton));
|
|
|
|
|
-
|
|
|
|
|
|
|
+ it('should render the dropdown with CREATE and UPLOAD group actions', async () => {
|
|
|
|
|
+ const user = userEvent.setup();
|
|
|
|
|
+ setup();
|
|
|
|
|
+ await openDropdown(user);
|
|
|
// Check that the "Create" and "Upload" groups are in the dropdown
|
|
// Check that the "Create" and "Upload" groups are in the dropdown
|
|
|
expect(screen.getByText('CREATE')).toBeInTheDocument();
|
|
expect(screen.getByText('CREATE')).toBeInTheDocument();
|
|
|
expect(screen.getByText('UPLOAD')).toBeInTheDocument();
|
|
expect(screen.getByText('UPLOAD')).toBeInTheDocument();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it('should open the folder creation modal when "New Folder" is clicked', async () => {
|
|
|
|
|
- const newButton = screen.getByRole('button', { name: 'New' });
|
|
|
|
|
- await act(async () => fireEvent.click(newButton));
|
|
|
|
|
-
|
|
|
|
|
- const newFolderButton = screen.getByRole('menuitem', { name: 'New Folder' });
|
|
|
|
|
- await act(async () => fireEvent.click(newFolderButton));
|
|
|
|
|
|
|
+ describe('create actions', () => {
|
|
|
|
|
+ it.each([
|
|
|
|
|
+ { label: 'New Folder', modalTitle: 'Create Folder', api: CREATE_DIRECTORY_API_URL },
|
|
|
|
|
+ { label: 'New File', modalTitle: 'Create File', api: CREATE_FILE_API_URL }
|
|
|
|
|
+ ])('opens ${label} modal and calls correct API', async ({ label, modalTitle, api }) => {
|
|
|
|
|
+ const user = userEvent.setup();
|
|
|
|
|
+ setup();
|
|
|
|
|
+ await clickMenuOption(label, user);
|
|
|
|
|
|
|
|
- expect(screen.getByRole('dialog', { name: 'Create Folder' })).toBeInTheDocument();
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ expect(screen.getByText(modalTitle)).toBeInTheDocument();
|
|
|
|
|
|
|
|
- it('should open the file creation modal when "New File" is clicked', async () => {
|
|
|
|
|
- const newButton = screen.getByRole('button', { name: 'New' });
|
|
|
|
|
- await act(async () => fireEvent.click(newButton));
|
|
|
|
|
|
|
+ const input = screen.getByRole('textbox');
|
|
|
|
|
+ fireEvent.change(input, { target: { value: `Test ${label}` } });
|
|
|
|
|
|
|
|
- const newFileButton = screen.getByRole('menuitem', { name: 'New File' });
|
|
|
|
|
- await act(async () => fireEvent.click(newFileButton));
|
|
|
|
|
|
|
+ fireEvent.click(screen.getByRole('button', { name: 'Create' }));
|
|
|
|
|
|
|
|
- expect(screen.getByRole('dialog', { name: 'Create File' })).toBeInTheDocument();
|
|
|
|
|
|
|
+ await waitFor(() => {
|
|
|
|
|
+ expect(mockSave).toHaveBeenCalledWith(
|
|
|
|
|
+ { path: defaultPath, name: `Test ${label}` },
|
|
|
|
|
+ { url: api }
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it('should render hidden file input for upload functionality', async () => {
|
|
|
|
|
- const fileInput = document.querySelector('input[type="file"]');
|
|
|
|
|
- expect(fileInput).toBeInTheDocument();
|
|
|
|
|
- expect(fileInput).toHaveAttribute('hidden');
|
|
|
|
|
- expect(fileInput).toHaveAttribute('multiple');
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ describe('upload actions', () => {
|
|
|
|
|
+ it('should render hidden file input for upload functionality', async () => {
|
|
|
|
|
+ setup();
|
|
|
|
|
+ const fileInput = document.querySelector('input[type="file"]');
|
|
|
|
|
+ expect(fileInput).toBeInTheDocument();
|
|
|
|
|
+ expect(fileInput).toHaveAttribute('hidden');
|
|
|
|
|
+ expect(fileInput).toHaveAttribute('multiple');
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- it('should handle file selection and call onFilesUpload', async () => {
|
|
|
|
|
- const fileInput = document.querySelector('input[type="file"]');
|
|
|
|
|
- expect(fileInput).toBeInTheDocument();
|
|
|
|
|
|
|
+ it('should handle file selection and call onFilesUpload', async () => {
|
|
|
|
|
+ setup();
|
|
|
|
|
+ const fileInput = document.querySelector('input[type="file"]');
|
|
|
|
|
+ expect(fileInput).toBeInTheDocument();
|
|
|
|
|
|
|
|
- const file1 = new File(['test content 1'], 'test1.txt', { type: 'text/plain' });
|
|
|
|
|
- const file2 = new File(['test content 2'], 'test2.txt', { type: 'text/plain' });
|
|
|
|
|
|
|
+ const file1 = new File(['test content 1'], 'test1.txt', { type: 'text/plain' });
|
|
|
|
|
+ const file2 = new File(['test content 2'], 'test2.txt', { type: 'text/plain' });
|
|
|
|
|
|
|
|
- fireEvent.change(fileInput!, {
|
|
|
|
|
- target: { files: [file1, file2] }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ fireEvent.change(fileInput!, {
|
|
|
|
|
+ target: { files: [file1, file2] }
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- expect(mockFilesUpload).toHaveBeenCalledWith([file1, file2]);
|
|
|
|
|
|
|
+ expect(mockFilesUpload).toHaveBeenCalledWith([file1, file2]);
|
|
|
|
|
+ });
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it('should call the correct API for creating a folder', async () => {
|
|
|
|
|
- const newButton = screen.getByRole('button', { name: 'New' });
|
|
|
|
|
- await act(async () => fireEvent.click(newButton));
|
|
|
|
|
|
|
+ describe('storage-specific actions', () => {
|
|
|
|
|
+ it('shows New Bucket when S3 root', async () => {
|
|
|
|
|
+ const user = userEvent.setup();
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isS3').mockReturnValue(true);
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isS3Root').mockReturnValue(true);
|
|
|
|
|
|
|
|
- const newFolderButton = screen.getByRole('menuitem', { name: 'New Folder' });
|
|
|
|
|
- await act(async () => fireEvent.click(newFolderButton));
|
|
|
|
|
|
|
+ setup('/');
|
|
|
|
|
+ await openDropdown(user);
|
|
|
|
|
+ expect(screen.getByRole('menuitem', { name: 'New Bucket' })).toBeInTheDocument();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('shows New Bucket when OFS root', async () => {
|
|
|
|
|
+ const user = userEvent.setup();
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isOFS').mockReturnValue(true);
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isOFSRoot').mockReturnValue(true);
|
|
|
|
|
|
|
|
- const input = screen.getByRole('textbox');
|
|
|
|
|
- fireEvent.change(input, { target: { value: 'Test Folder' } });
|
|
|
|
|
|
|
+ setup('/');
|
|
|
|
|
+ await openDropdown(user);
|
|
|
|
|
+ expect(screen.getByRole('menuitem', { name: 'New Bucket' })).toBeInTheDocument();
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- const createButton = screen.getByRole('button', { name: 'Create' });
|
|
|
|
|
- fireEvent.click(createButton);
|
|
|
|
|
|
|
+ it('does not show New Bucket when not in S3 root', async () => {
|
|
|
|
|
+ const user = userEvent.setup();
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isS3').mockReturnValue(true);
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isS3Root').mockReturnValue(false);
|
|
|
|
|
|
|
|
- await waitFor(() => {
|
|
|
|
|
- expect(mockSave).toHaveBeenCalledWith(
|
|
|
|
|
- { path: currentPath, name: 'Test Folder' },
|
|
|
|
|
- { url: CREATE_DIRECTORY_API_URL }
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ setup('s3://user');
|
|
|
|
|
+ await openDropdown(user);
|
|
|
|
|
+ expect(screen.queryByRole('menuitem', { name: 'New Bucket' })).not.toBeInTheDocument();
|
|
|
});
|
|
});
|
|
|
- });
|
|
|
|
|
|
|
|
|
|
- it('should call the correct API for creating a file', async () => {
|
|
|
|
|
- const newButton = screen.getByRole('button', { name: 'New' });
|
|
|
|
|
- await act(async () => fireEvent.click(newButton));
|
|
|
|
|
|
|
+ it('shows New File System when ABFS root', async () => {
|
|
|
|
|
+ const user = userEvent.setup();
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isABFS').mockReturnValue(true);
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isABFSRoot').mockReturnValue(true);
|
|
|
|
|
+
|
|
|
|
|
+ setup('/');
|
|
|
|
|
+ await openDropdown(user);
|
|
|
|
|
+ expect(screen.getByRole('menuitem', { name: 'New File System' })).toBeInTheDocument();
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- const newFileButton = screen.getByRole('menuitem', { name: 'New File' });
|
|
|
|
|
- await act(async () => fireEvent.click(newFileButton));
|
|
|
|
|
|
|
+ it('shows New Volume when OFS service ID', async () => {
|
|
|
|
|
+ const user = userEvent.setup();
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isOFS').mockReturnValue(true);
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isOFSServiceID').mockReturnValue(true);
|
|
|
|
|
|
|
|
- // Simulate file name submission
|
|
|
|
|
- const input = screen.getByRole('textbox');
|
|
|
|
|
- fireEvent.change(input, { target: { value: 'Test File' } });
|
|
|
|
|
|
|
+ setup('/ofs-service');
|
|
|
|
|
+ await openDropdown(user);
|
|
|
|
|
+ expect(screen.getByRole('menuitem', { name: 'New Volume' })).toBeInTheDocument();
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- const createButton = screen.getByRole('button', { name: 'Create' });
|
|
|
|
|
- fireEvent.click(createButton);
|
|
|
|
|
|
|
+ it('does not show New Volume when OFS service ID', async () => {
|
|
|
|
|
+ const user = userEvent.setup();
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isOFS').mockReturnValue(true);
|
|
|
|
|
+ jest.spyOn(storageUtils, 'isOFSServiceID').mockReturnValue(false);
|
|
|
|
|
|
|
|
- await waitFor(() => {
|
|
|
|
|
- expect(mockSave).toHaveBeenCalledWith(
|
|
|
|
|
- { path: currentPath, name: 'Test File' },
|
|
|
|
|
- { url: CREATE_FILE_API_URL }
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ setup('/ofs-service');
|
|
|
|
|
+ await openDropdown(user);
|
|
|
|
|
+ expect(screen.queryByRole('menuitem', { name: 'New Volume' })).not.toBeInTheDocument();
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|