summaryrefslogtreecommitdiffstats
path: root/apps/files/src/services/DropServiceUtils.spec.ts
blob: 1502d83d9ce8ddc8821d81e14368f902f18975aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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)
	})
})