179 lines
6.5 KiB
TypeScript
179 lines
6.5 KiB
TypeScript
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
|
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
|
|
|
|
import UploaderView from './UploaderView'
|
|
|
|
const controllerMock = vi.hoisted(() => ({
|
|
useUploaderController: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('./uploader-controller', () => ({
|
|
default: controllerMock.useUploaderController,
|
|
}))
|
|
|
|
function makeFile(name: string, type: string) {
|
|
return new File(['x'], name, { type })
|
|
}
|
|
|
|
function makeController(overrides: Record<string, unknown> = {}) {
|
|
const setSel = vi.fn()
|
|
const setBulkDesc = vi.fn()
|
|
const setGlobalDate = vi.fn()
|
|
const setLib = vi.fn()
|
|
const setSub = vi.fn()
|
|
const setNewFolderRaw = vi.fn()
|
|
const refresh = vi.fn()
|
|
return {
|
|
mobile: false,
|
|
me: { username: 'brad' },
|
|
libs: ['alpha', 'beta'],
|
|
lib: 'alpha',
|
|
sub: 'videos',
|
|
rootDirs: ['archive', 'videos'],
|
|
rows: [],
|
|
status: 'Ready',
|
|
globalDate: '2026-04-10',
|
|
uploading: false,
|
|
sel: [
|
|
{ file: makeFile('photo.jpg', 'image/jpeg'), desc: '', date: '2026-04-10', finalName: '2026.04.10.upload.photo.jpg', progress: 0 },
|
|
{ file: makeFile('clip.mp4', 'video/mp4'), desc: '', date: '2026-04-10', finalName: '2026.04.10.upload.clip.mp4', progress: 0 },
|
|
{ file: makeFile('note.pdf', 'application/pdf'), desc: '', date: '2026-04-10', finalName: '2026.04.10.upload.note.pdf', progress: 0 },
|
|
],
|
|
bulkDesc: 'holiday',
|
|
folderInputRef: { current: document.createElement('input') },
|
|
newFolderRaw: 'new-folder',
|
|
setGlobalDate,
|
|
setLib,
|
|
setSub,
|
|
setNewFolderRaw,
|
|
setBulkDesc,
|
|
setSel,
|
|
handleChoose: vi.fn(),
|
|
applyDescToAllVideos: vi.fn(),
|
|
doUpload: vi.fn(),
|
|
createSubfolder: vi.fn(),
|
|
renameFolder: vi.fn(),
|
|
deleteFolder: vi.fn(),
|
|
renamePath: vi.fn(),
|
|
deletePath: vi.fn(),
|
|
refresh,
|
|
sortedRows: [
|
|
{ name: 'archive', path: 'archive', is_dir: true, size: 0, mtime: 0 },
|
|
{ name: 'clip.mp4', path: 'clip.mp4', is_dir: false, size: 2048, mtime: 1713000000 },
|
|
],
|
|
existingNames: new Set(['clip.mp4']),
|
|
duplicateNamesInSelection: new Set(['2026.04.10.upload.clip.mp4']),
|
|
hasNameIssues: false,
|
|
destPath: '/alpha/videos',
|
|
videosNeedingDesc: 0,
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
beforeAll(() => {
|
|
if (!('createObjectURL' in URL)) {
|
|
Object.defineProperty(URL, 'createObjectURL', { value: vi.fn(() => 'blob:thumb'), configurable: true })
|
|
} else {
|
|
vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:thumb')
|
|
}
|
|
if (!('revokeObjectURL' in URL)) {
|
|
Object.defineProperty(URL, 'revokeObjectURL', { value: vi.fn(), configurable: true })
|
|
} else {
|
|
vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => {})
|
|
}
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
describe('UploaderView', () => {
|
|
it('renders populated state and forwards interactions', async () => {
|
|
const controller = makeController()
|
|
controllerMock.useUploaderController.mockReturnValue(controller)
|
|
|
|
render(<UploaderView />)
|
|
|
|
await waitFor(() => expect(screen.getByText('Signed in: brad')).toBeTruthy())
|
|
expect(screen.getByText(/Destination:/)).toBeTruthy()
|
|
expect(screen.getByText('Ready')).toBeTruthy()
|
|
await waitFor(() => expect(document.querySelector('img')).not.toBeNull())
|
|
expect(document.querySelector('video')).not.toBeNull()
|
|
expect(screen.getByText('📄')).toBeTruthy()
|
|
|
|
fireEvent.change(screen.getByLabelText('Default date'), { target: { value: '2026-04-11' } })
|
|
fireEvent.change(screen.getByPlaceholderText('Short video description'), { target: { value: 'family trip' } })
|
|
|
|
const optionalImageInputs = screen.getAllByPlaceholderText('Optional for image')
|
|
fireEvent.change(optionalImageInputs[0], { target: { value: 'photo desc' } })
|
|
fireEvent.change(optionalImageInputs[1], { target: { value: 'note desc' } })
|
|
fireEvent.change(screen.getByPlaceholderText('Required for video'), { target: { value: 'clip desc' } })
|
|
fireEvent.change(screen.getAllByDisplayValue('2026-04-10')[1], { target: { value: '2026-04-12' } })
|
|
fireEvent.change(screen.getByRole('combobox'), { target: { value: 'beta' } })
|
|
|
|
expect(controller.setGlobalDate).toHaveBeenCalledWith('2026-04-11')
|
|
expect(controller.setBulkDesc).toHaveBeenCalledWith('family trip')
|
|
expect(controller.setSel).toHaveBeenCalled()
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Apply to all videos' }))
|
|
fireEvent.click(screen.getByRole('button', { name: 'Create' }))
|
|
fireEvent.click(screen.getByRole('button', { name: 'Rename' }))
|
|
fireEvent.click(screen.getByLabelText('Go to library root'))
|
|
fireEvent.click(screen.getByLabelText('Delete subfolder'))
|
|
fireEvent.click(screen.getAllByRole('button', { name: 'Select' })[0])
|
|
fireEvent.click(screen.getByRole('button', { name: 'Open' }))
|
|
fireEvent.click(screen.getByLabelText('Delete clip.mp4'))
|
|
fireEvent.click(screen.getByRole('button', { name: /Upload \(3\)/ }))
|
|
|
|
expect(controller.applyDescToAllVideos).toHaveBeenCalled()
|
|
expect(controller.createSubfolder).toHaveBeenCalledWith('new-folder')
|
|
expect(controller.renameFolder).toHaveBeenCalledWith('videos')
|
|
expect(controller.refresh).toHaveBeenCalled()
|
|
expect(controller.deleteFolder).toHaveBeenCalledWith('videos')
|
|
expect(controller.deletePath).toHaveBeenCalledWith('clip.mp4', false)
|
|
expect(controller.doUpload).toHaveBeenCalled()
|
|
expect(screen.getByText('photo.jpg')).toBeTruthy()
|
|
expect(screen.getByText('clip.mp4')).toBeTruthy()
|
|
expect(screen.getByText('note.pdf')).toBeTruthy()
|
|
})
|
|
|
|
it('renders the empty-library state', () => {
|
|
controllerMock.useUploaderController.mockReturnValue(
|
|
makeController({
|
|
lib: '',
|
|
sub: '',
|
|
rootDirs: [],
|
|
sel: [],
|
|
sortedRows: [],
|
|
videosNeedingDesc: 0,
|
|
hasNameIssues: false,
|
|
destPath: '/(choose a library)',
|
|
})
|
|
)
|
|
|
|
render(<UploaderView />)
|
|
|
|
expect(screen.getByText('Select at least one file.')).toBeTruthy()
|
|
expect(screen.getByText('Select a library to create subfolders and view contents.')).toBeTruthy()
|
|
})
|
|
|
|
it('renders empty destination sections when the library has no children', () => {
|
|
controllerMock.useUploaderController.mockReturnValue(
|
|
makeController({
|
|
lib: 'alpha',
|
|
sub: '',
|
|
rootDirs: [],
|
|
sel: [],
|
|
sortedRows: [],
|
|
destPath: '/alpha',
|
|
videosNeedingDesc: 0,
|
|
})
|
|
)
|
|
|
|
render(<UploaderView />)
|
|
|
|
expect(screen.getByText(/No subfolders yet/)).toBeTruthy()
|
|
expect(screen.getByText('Empty.')).toBeTruthy()
|
|
})
|
|
})
|