You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nextcloud/apps/files/src/services/DropServiceUtils.spec.ts

143 lines
5.7 KiB
TypeScript

import { describe, it, expect } from '@jest/globals'
import { FileSystemDirectoryEntry, FileSystemFileEntry, fileSystemEntryToDataTransferItem, DataTransferItem as DataTransferItemMock } from '../../../../__tests__/FileSystemAPIUtils'
import { join } from 'node:path'
import { Directory, traverseTree } from './DropServiceUtils'
import { dataTransferToFileTree } from './DropService'
import logger from '../logger'
const dataTree = {
'file0.txt': ['Hello, world!', 1234567890],
dir1: {
'file1.txt': ['Hello, world!', 4567891230],
'file2.txt': ['Hello, world!', 7891234560],
},
dir2: {
'file3.txt': ['Hello, world!', 1234567890],
},
}
// This is mocking a file tree using the FileSystem API
const buildFileSystemDirectoryEntry = (path: string, tree: any): FileSystemDirectoryEntry => {
const entries = Object.entries(tree).map(([name, contents]) => {
const fullPath = join(path, name)
if (Array.isArray(contents)) {
return new FileSystemFileEntry(fullPath, contents[0], contents[1])
} else {
return buildFileSystemDirectoryEntry(fullPath, contents)
}
})
return new FileSystemDirectoryEntry(path, entries)
}
const buildDataTransferItemArray = (path: string, tree: any, isFileSystemAPIAvailable = true): DataTransferItemMock[] => {
return Object.entries(tree).map(([name, contents]) => {
const fullPath = join(path, name)
if (Array.isArray(contents)) {
const entry = new FileSystemFileEntry(fullPath, contents[0], contents[1])
return fileSystemEntryToDataTransferItem(entry, isFileSystemAPIAvailable)
}
const entry = buildFileSystemDirectoryEntry(fullPath, contents)
return fileSystemEntryToDataTransferItem(entry, isFileSystemAPIAvailable)
})
}
describe('Filesystem API traverseTree', () => {
it('Should traverse a file tree from root', async () => {
// Fake a FileSystemEntry tree
const root = buildFileSystemDirectoryEntry('root', dataTree)
const tree = await traverseTree(root as unknown as FileSystemEntry) as Directory
expect(tree.name).toBe('root')
expect(tree).toBeInstanceOf(Directory)
expect(tree.contents).toHaveLength(3)
expect(tree.size).toBe(13 * 4) // 13 bytes from 'Hello, world!'
})
it('Should traverse a file tree from a subdirectory', async () => {
// Fake a FileSystemEntry tree
const dir2 = buildFileSystemDirectoryEntry('dir2', dataTree.dir2)
const tree = await traverseTree(dir2 as unknown as FileSystemEntry) as Directory
expect(tree.name).toBe('dir2')
expect(tree).toBeInstanceOf(Directory)
expect(tree.contents).toHaveLength(1)
expect(tree.contents[0].name).toBe('file3.txt')
expect(tree.size).toBe(13) // 13 bytes from 'Hello, world!'
})
it('Should properly compute the last modified', async () => {
// Fake a FileSystemEntry tree
const root = buildFileSystemDirectoryEntry('root', dataTree)
const rootTree = await traverseTree(root as unknown as FileSystemEntry) as Directory
expect(rootTree.lastModified).toBe(7891234560)
// Fake a FileSystemEntry tree
const dir2 = buildFileSystemDirectoryEntry('root', dataTree.dir2)
const dir2Tree = await traverseTree(dir2 as unknown as FileSystemEntry) as Directory
expect(dir2Tree.lastModified).toBe(1234567890)
})
})
describe('DropService dataTransferToFileTree', () => {
beforeAll(() => {
// DataTransferItem doesn't exists in jsdom, let's mock
// a dumb one so we can check the instanceof
// @ts-expect-error jsdom doesn't have DataTransferItem
window.DataTransferItem = DataTransferItemMock
})
afterAll(() => {
// @ts-expect-error jsdom doesn't have DataTransferItem
delete window.DataTransferItem
})
it('Should return a RootDirectory with Filesystem API', async () => {
jest.spyOn(logger, 'error').mockImplementation(() => jest.fn())
jest.spyOn(logger, 'warn').mockImplementation(() => jest.fn())
const dataTransferItems = buildDataTransferItemArray('root', dataTree)
const fileTree = await dataTransferToFileTree(dataTransferItems as unknown as DataTransferItem[])
expect(fileTree.name).toBe('root')
expect(fileTree).toBeInstanceOf(Directory)
expect(fileTree.contents).toHaveLength(3)
// The file tree should be recursive when using the Filesystem API
expect(fileTree.contents[1]).toBeInstanceOf(Directory)
expect((fileTree.contents[1] as Directory).contents).toHaveLength(2)
expect(fileTree.contents[2]).toBeInstanceOf(Directory)
expect((fileTree.contents[2] as Directory).contents).toHaveLength(1)
expect(logger.error).not.toBeCalled()
expect(logger.warn).not.toBeCalled()
})
it('Should return a RootDirectory with legacy File API ignoring recursive directories', async () => {
jest.spyOn(logger, 'error').mockImplementation(() => jest.fn())
jest.spyOn(logger, 'warn').mockImplementation(() => jest.fn())
const dataTransferItems = buildDataTransferItemArray('root', dataTree, false)
const fileTree = await dataTransferToFileTree(dataTransferItems as unknown as DataTransferItem[])
expect(fileTree.name).toBe('root')
expect(fileTree).toBeInstanceOf(Directory)
expect(fileTree.contents).toHaveLength(1)
// The file tree should be recursive when using the Filesystem API
expect(fileTree.contents[0]).not.toBeInstanceOf(Directory)
expect((fileTree.contents[0].name)).toBe('file0.txt')
expect(logger.error).not.toBeCalled()
expect(logger.warn).toHaveBeenNthCalledWith(1, 'Could not get FilesystemEntry of item, falling back to file')
expect(logger.warn).toHaveBeenNthCalledWith(2, 'Could not get FilesystemEntry of item, falling back to file')
expect(logger.warn).toHaveBeenNthCalledWith(3, 'Browser does not support Filesystem API. Directories will not be uploaded')
expect(logger.warn).toHaveBeenNthCalledWith(4, 'Could not get FilesystemEntry of item, falling back to file')
expect(logger.warn).toHaveBeenCalledTimes(4)
})
})