Skip to content

Commit 7b78a64

Browse files
committed
test(files): Add tests for path handling
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent c7d22f9 commit 7b78a64

6 files changed

Lines changed: 173 additions & 7 deletions

File tree

apps/files/src/store/files.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6-
import type { FilesStore, RootsStore, RootOptions, Service, FilesState, FileSource } from '../types'
6+
import type { FilesStore, RootsStore, RootOptions, Service, FileSource } from '../types'
77
import type { FileStat, ResponseDataDetailed } from 'webdav'
88
import type { Folder, Node } from '@nextcloud/files'
99

@@ -27,9 +27,10 @@ const fetchNode = async (node: Node): Promise<Node> => {
2727

2828
export const useFilesStore = function(...args) {
2929
const store = defineStore('files', {
30-
state: (): FilesState => ({
30+
state: () => ({
3131
files: {} as FilesStore,
3232
roots: {} as RootsStore,
33+
_initialized: false,
3334
}),
3435

3536
getters: {
@@ -86,6 +87,7 @@ export const useFilesStore = function(...args) {
8687
}
8788

8889
// If we found a cache entry and the cache entry was already loaded (has children) then use it
90+
// @ts-expect-error The _children prop is undocumented - we need to make this official
8991
return (folder?._children ?? [])
9092
.map((source: string) => this.getNode(source))
9193
.filter(Boolean)

apps/files/src/store/paths.spec.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { emit } from '@nextcloud/event-bus'
7+
import { describe, expect, test } from '@jest/globals'
8+
import { File, Folder } from '@nextcloud/files'
9+
import { setActivePinia, createPinia } from 'pinia'
10+
import { usePathsStore } from './paths.ts'
11+
import { useFilesStore } from './files.ts'
12+
13+
describe('Path store', () => {
14+
15+
let store: ReturnType<typeof usePathsStore>
16+
let files: ReturnType<typeof useFilesStore>
17+
let root: Folder & { _children?: string[] }
18+
19+
beforeEach(() => {
20+
setActivePinia(createPinia())
21+
22+
root = new Folder({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/', id: 1 })
23+
files = useFilesStore()
24+
files.setRoot({ service: 'files', root })
25+
26+
store = usePathsStore()
27+
})
28+
29+
test('Folder is created', () => {
30+
// no defined paths
31+
expect(store.paths).toEqual({})
32+
33+
// create the folder
34+
const node = new Folder({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/folder', id: 2 })
35+
emit('files:node:created', node)
36+
37+
// see that the path is added
38+
expect(store.paths).toEqual({ files: { [node.path]: node.source } })
39+
40+
// see that the node is added
41+
expect(root._children).toEqual([node.source])
42+
})
43+
44+
test('File is created', () => {
45+
// no defined paths
46+
expect(store.paths).toEqual({})
47+
48+
// create the file
49+
const node = new File({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/file.txt', id: 2, mime: 'text/plain' })
50+
emit('files:node:created', node)
51+
52+
// see that there are still no paths
53+
expect(store.paths).toEqual({})
54+
55+
// see that the node is added
56+
expect(root._children).toEqual([node.source])
57+
})
58+
59+
test('Existing file is created', () => {
60+
// no defined paths
61+
expect(store.paths).toEqual({})
62+
63+
// create the file
64+
const node1 = new File({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/file.txt', id: 2, mime: 'text/plain' })
65+
emit('files:node:created', node1)
66+
67+
// see that there are still no paths
68+
expect(store.paths).toEqual({})
69+
70+
// see that the node is added
71+
expect(root._children).toEqual([node1.source])
72+
73+
// create the same named file again
74+
const node2 = new File({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/file.txt', id: 2, mime: 'text/plain' })
75+
emit('files:node:created', node2)
76+
77+
// see that there are still no paths and the children are not duplicated
78+
expect(store.paths).toEqual({})
79+
expect(root._children).toEqual([node1.source])
80+
81+
})
82+
83+
test('Existing folder is created', () => {
84+
// no defined paths
85+
expect(store.paths).toEqual({})
86+
87+
// create the file
88+
const node1 = new Folder({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/folder', id: 2 })
89+
emit('files:node:created', node1)
90+
91+
// see the path is added
92+
expect(store.paths).toEqual({ files: { [node1.path]: node1.source } })
93+
94+
// see that the node is added
95+
expect(root._children).toEqual([node1.source])
96+
97+
// create the same named file again
98+
const node2 = new Folder({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/folder', id: 2 })
99+
emit('files:node:created', node2)
100+
101+
// see that there is still only one paths and the children are not duplicated
102+
expect(store.paths).toEqual({ files: { [node1.path]: node1.source } })
103+
expect(root._children).toEqual([node1.source])
104+
})
105+
106+
test('Folder is deleted', () => {
107+
const node = new Folder({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/folder', id: 2 })
108+
emit('files:node:created', node)
109+
// see that the path is added and the children are set-up
110+
expect(store.paths).toEqual({ files: { [node.path]: node.source } })
111+
expect(root._children).toEqual([node.source])
112+
113+
emit('files:node:deleted', node)
114+
// See the path is removed
115+
expect(store.paths).toEqual({ files: {} })
116+
// See the child is removed
117+
expect(root._children).toEqual([])
118+
})
119+
120+
test('File is deleted', () => {
121+
const node = new File({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/file.txt', id: 2, mime: 'text/plain' })
122+
emit('files:node:created', node)
123+
// see that the children are set-up
124+
expect(root._children).toEqual([node.source])
125+
126+
emit('files:node:deleted', node)
127+
// See the child is removed
128+
expect(root._children).toEqual([])
129+
})
130+
})

apps/files/src/store/paths.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5-
import type { FileSource, PathsStore, PathOptions, ServicesState, Service } from '../types'
5+
import type { FileSource, PathOptions, ServicesState, Service } from '../types'
66
import { defineStore } from 'pinia'
77
import { FileType, Folder, Node, getNavigation } from '@nextcloud/files'
88
import { subscribe } from '@nextcloud/event-bus'
@@ -17,7 +17,8 @@ export const usePathsStore = function(...args) {
1717
const store = defineStore('paths', {
1818
state: () => ({
1919
paths: {} as ServicesState,
20-
} as PathsStore),
20+
_initialized: false,
21+
}),
2122

2223
getters: {
2324
getPath: (state) => {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { createFolder, getRowForFile, triggerActionForFile } from './FilesUtils.ts'
7+
8+
before(() => {
9+
cy.createRandomUser()
10+
.then((user) => {
11+
cy.mkdir(user, '/only once')
12+
cy.login(user)
13+
cy.visit('/apps/files')
14+
})
15+
})
16+
17+
/**
18+
* Regression test for https://github.com/nextcloud/server/issues/47904
19+
*/
20+
it('Ensure nodes are not duplicated in the file list', () => {
21+
// See the folder
22+
getRowForFile('only once').should('be.visible')
23+
// Delete the folder
24+
cy.intercept('DELETE', '**/remote.php/dav/**').as('deleteFolder')
25+
triggerActionForFile('only once', 'delete')
26+
cy.wait('@deleteFolder')
27+
getRowForFile('only once').should('not.exist')
28+
// Create the folder again
29+
createFolder('only once')
30+
// See folder exists only once
31+
getRowForFile('only once')
32+
.should('have.length', 1)
33+
})

dist/files-main.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/files-main.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)