import React from 'react'; import userEvent from '@testing-library/user-event'; import { render, screen, waitFor } from '../../../test-utils/testing-library'; import { ContentPickerComponent as ContentPicker } from '../ContentPicker'; import { mockRecentItems, mockRootFolder } from '../../common/__mocks__/mockRootFolder'; jest.mock('../../../utils/Xhr', () => { return jest.fn().mockImplementation(() => { return { get: jest.fn(({ url }) => { switch (url) { case 'https://api.box.com/2.0/folders/69083462919': return Promise.resolve({ data: mockRootFolder }); case 'https://api.box.com/2.0/recent_items': return Promise.resolve({ data: mockRecentItems }); default: return Promise.reject(new Error('Not Found')); } }), post: jest.fn(), put: jest.fn(), delete: jest.fn(), abort: jest.fn(), }; }); }); jest.mock( '@box/react-virtualized/dist/es/AutoSizer', () => ({ children }) => children({ height: 600, width: 1200 }), ); describe('elements/content-picker/ContentPicker', () => { let rootElement: HTMLDivElement; const renderComponent = (props = {}) => { return render(); }; beforeEach(() => { rootElement = document.createElement('div'); rootElement.appendChild(document.createElement('div')); document.body.appendChild(rootElement); }); afterEach(() => { jest.clearAllMocks(); document.body.removeChild(rootElement); }); describe('render', () => { test('should render the component', async () => { renderComponent(); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); expect(screen.getByText('Please wait while the items load...')).toBeInTheDocument(); }); expect(screen.getByRole('button', { name: 'Preview Test Folder' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Add' })).toBeInTheDocument(); expect(screen.getByRole('searchbox')).toBeInTheDocument(); expect(screen.getByText('An Ordered Folder')).toBeInTheDocument(); expect(screen.getByText('Modified Tue Apr 16 2019 by Preview')).toBeInTheDocument(); expect(screen.getByText('191.33 MB')).toBeInTheDocument(); }); }); describe('Upload', () => { test('should open upload dialog when clicking upload button', async () => { renderComponent({ canUpload: true }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); expect(screen.getByText('Please wait while the items load...')).toBeInTheDocument(); }); const addButton = screen.getByRole('button', { name: 'Add' }); await userEvent.click(addButton); const uploadButton = screen.getByText('Upload'); await userEvent.click(uploadButton); expect(screen.getByText('Drag and drop files')).toBeInTheDocument(); expect(screen.getByText('Browse your device')).toBeInTheDocument(); }); test('should not render upload button when canUpload is false', async () => { renderComponent({ canUpload: false }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); }); const addButton = screen.getByRole('button', { name: 'Add' }); await userEvent.click(addButton); expect(screen.queryByText('Upload')).not.toBeInTheDocument(); }); }); describe('Item Selection', () => { test('should select and choose items', async () => { const onChoose = jest.fn(); renderComponent({ onChoose }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); }); const itemRow = screen.getByRole('row', { name: 'PDF File Book Sample.pdf Modified Wed Dec 7 2022 by Preview 141.09 KB Book Sample.pdf', }); await userEvent.click(itemRow); const chooseButton = screen.getByRole('button', { name: 'Choose' }); await userEvent.click(chooseButton); expect(onChoose).toHaveBeenCalledWith([ expect.objectContaining({ name: 'Book Sample.pdf', }), ]); }); test('should respect maxSelectable limit', async () => { const onChoose = jest.fn(); renderComponent({ maxSelectable: 2, onChoose }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); }); const firstItemRow = screen.getByRole('row', { name: 'PDF File Book Sample.pdf Modified Wed Dec 7 2022 by Preview 141.09 KB Book Sample.pdf', }); await userEvent.click(firstItemRow); const secondItemRow = screen.getByRole('row', { name: 'PDF File Document (PDF).pdf Modified Sat Dec 17 2022 by Preview 774.11 KB Document (PDF).pdf', }); await userEvent.click(secondItemRow); const thirdItemRow = screen.getByRole('row', { name: 'PPTX File Document (Powerpoint).pptx Modified Mon Mar 4 2019 by Preview 56.59 KB', }); await userEvent.click(thirdItemRow); const chooseButton = screen.getByRole('button', { name: 'Choose' }); await userEvent.click(chooseButton); expect(onChoose).toHaveBeenCalledWith( expect.arrayContaining([ expect.objectContaining({ name: 'Book Sample.pdf', }), expect.objectContaining({ name: 'Document (PDF).pdf', }), ]), ); }); }); describe('Search', () => { test('should update search query when typing in search input', async () => { renderComponent(); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); }); const searchInput = screen.getByRole('searchbox'); await userEvent.type(searchInput, 'test'); expect(searchInput).toHaveValue('test'); }); }); describe('Cancel', () => { test('should call onCancel when clicking cancel button', async () => { const onCancel = jest.fn(); renderComponent({ onCancel }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); }); const cancelButton = screen.getByRole('button', { name: 'Cancel' }); await userEvent.click(cancelButton); expect(onCancel).toHaveBeenCalled(); }); }); describe('Create Folder', () => { test('should open create folder dialog when clicking new folder button', async () => { renderComponent({ canCreateNewFolder: true }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); }); const addButton = screen.getByRole('button', { name: 'Add' }); await userEvent.click(addButton); const newFolderButton = screen.getByText('New Folder'); await userEvent.click(newFolderButton); expect(screen.getByText('Please enter a name.')).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Create' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); }); test('should not render new folder button when canCreateNewFolder is false', async () => { renderComponent({ canCreateNewFolder: false }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); }); const addButton = screen.getByRole('button', { name: 'Add' }); await userEvent.click(addButton); expect(screen.queryByText('New Folder')).not.toBeInTheDocument(); }); }); describe('Keyboard Navigation', () => { test('should focus search input on "/" key press', async () => { renderComponent(); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); expect(screen.getByText('Please wait while the items load...')).toBeInTheDocument(); }); await userEvent.tab(); await userEvent.keyboard('/'); expect(screen.getByRole('searchbox')).toHaveFocus(); }); test('should choose items on "c" key press with global modifier', async () => { const onChoose = jest.fn(); renderComponent({ onChoose }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); expect(screen.getByText('Please wait while the items load...')).toBeInTheDocument(); }); await userEvent.tab(); await userEvent.keyboard('gc'); expect(onChoose).toHaveBeenCalled(); }); test('should cancel on "x" key press with global modifier', async () => { const onCancel = jest.fn(); renderComponent({ onCancel }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); expect(screen.getByText('Please wait while the items load...')).toBeInTheDocument(); }); await userEvent.tab(); await userEvent.keyboard('gx'); expect(onCancel).toHaveBeenCalled(); }); test('should show selected items on "s" key press with global modifier', async () => { renderComponent(); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); expect(screen.getByText('Please wait while the items load...')).toBeInTheDocument(); }); await userEvent.tab(); await userEvent.keyboard('gs'); expect(screen.getByText('Selected Items')).toBeInTheDocument(); }); test('should open upload dialog on "u" key press with global modifier', async () => { renderComponent({ canUpload: true }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); expect(screen.getByText('Please wait while the items load...')).toBeInTheDocument(); }); await userEvent.tab(); await userEvent.keyboard('gu'); expect(screen.getByLabelText('Upload')).toHaveFocus(); }); test('should show recents on "r" key press with global modifier', async () => { renderComponent(); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); expect(screen.getByText('Please wait while the items load...')).toBeInTheDocument(); }); await userEvent.tab(); await userEvent.keyboard('gr'); expect(screen.getByText('Recents')).toBeInTheDocument(); }); test('should open create folder dialog on "n" key press with global modifier', async () => { renderComponent({ canCreateNewFolder: true }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); expect(screen.getByText('Please wait while the items load...')).toBeInTheDocument(); }); await userEvent.tab(); await userEvent.keyboard('gn'); expect(screen.getByText('Please enter a name.')).toBeInTheDocument(); }); test('should navigate through items with arrow keys', async () => { renderComponent(); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); expect(screen.getByText('Please wait while the items load...')).toBeInTheDocument(); }); const contentPicker = screen.getByTestId('content-picker'); contentPicker.focus(); await userEvent.tab(); await userEvent.keyboard('[ArrowDown]'); expect(screen.getAllByRole('row')[0]).toHaveFocus(); await userEvent.keyboard('[ArrowDown]'); expect(screen.getAllByRole('row')[1]).toHaveFocus(); await userEvent.keyboard('[ArrowUp]'); expect(screen.getAllByRole('row')[0]).toHaveFocus(); }); }); describe('Item Actions', () => { test('should render item actions menu when itemActions prop is provided', async () => { const itemActions = [ { label: 'Test Action', onAction: jest.fn(), }, ]; renderComponent({ itemActions }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); }); const moreOptionsButtons = screen.getAllByRole('button', { name: 'More options' }); expect(moreOptionsButtons.length).toBeGreaterThan(0); }); test('should not render item actions menu when itemActions prop is not provided', async () => { renderComponent(); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); }); const moreOptionsButtons = screen.queryAllByRole('button', { name: 'More options' }); expect(moreOptionsButtons).toHaveLength(0); }); test('should call onAction when item action is clicked', async () => { const onAction = jest.fn(); const itemActions = [ { label: 'Test Action', onAction, }, ]; renderComponent({ itemActions }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); }); const moreOptionsButton = screen.getAllByRole('button', { name: 'More options' })[0]; await userEvent.click(moreOptionsButton); const menuItem = screen.getByRole('menuitem', { name: 'Test Action' }); await userEvent.click(menuItem); expect(onAction).toHaveBeenCalled(); }); test('should handle item actions with different item types', async () => { const onAction = jest.fn(); const itemActions = [ { label: 'Test Action', onAction, type: 'folder', }, ]; renderComponent({ itemActions }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); }); const moreOptionsButton = screen.getAllByRole('button', { name: 'More options' })[0]; await userEvent.click(moreOptionsButton); const menuItem = screen.getByRole('menuitem', { name: 'Test Action' }); await userEvent.click(menuItem); expect(onAction).toHaveBeenCalled(); }); test('should not render item actions on type that is not specified', async () => { const onAction = jest.fn(); const itemActions = [ { label: 'Test Action', onAction, type: 'file', }, ]; renderComponent({ itemActions }); await waitFor(() => { expect(screen.getByTestId('content-picker')).toBeInTheDocument(); }); const moreOptionsButtons = screen.queryAllByRole('button', { name: 'More options' }); const fileCount = mockRootFolder.item_collection.entries.filter(item => item.type !== 'folder').length; expect(moreOptionsButtons).toHaveLength(fileCount); }); }); });