From 714ef088ade452c0a7017b20846f4c885271b1ef Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Wed, 11 Sep 2024 21:51:54 +0200 Subject: [PATCH] test(files): Add tests for path handling Signed-off-by: Ferdinand Thiessen --- apps/files/src/store/paths.spec.ts | 130 ++++++++++++++++++ cypress/e2e/files/FilesUtils.ts | 15 ++ .../files/duplicated-node-regression.cy.ts | 33 +++++ 3 files changed, 178 insertions(+) create mode 100644 apps/files/src/store/paths.spec.ts create mode 100644 cypress/e2e/files/duplicated-node-regression.cy.ts diff --git a/apps/files/src/store/paths.spec.ts b/apps/files/src/store/paths.spec.ts new file mode 100644 index 00000000000..26a41bb2a4a --- /dev/null +++ b/apps/files/src/store/paths.spec.ts @@ -0,0 +1,130 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { describe, beforeEach, test, expect } from '@jest/globals' +import { setActivePinia, createPinia } from 'pinia' +import { usePathsStore } from './paths.ts' +import { emit } from '@nextcloud/event-bus' +import { File, Folder } from '@nextcloud/files' +import { useFilesStore } from './files.ts' + +describe('Path store', () => { + + let store: ReturnType + let files: ReturnType + let root: Folder & { _children?: string[] } + + beforeEach(() => { + setActivePinia(createPinia()) + + root = new Folder({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/', id: 1 }) + files = useFilesStore() + files.setRoot({ service: 'files', root }) + + store = usePathsStore() + }) + + test('Folder is created', () => { + // no defined paths + expect(store.paths).toEqual({}) + + // create the folder + const node = new Folder({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/folder', id: 2 }) + emit('files:node:created', node) + + // see that the path is added + expect(store.paths).toEqual({ files: { [node.path]: node.source } }) + + // see that the node is added + expect(root._children).toEqual([node.source]) + }) + + test('File is created', () => { + // no defined paths + expect(store.paths).toEqual({}) + + // create the file + const node = new File({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/file.txt', id: 2, mime: 'text/plain' }) + emit('files:node:created', node) + + // see that there are still no paths + expect(store.paths).toEqual({}) + + // see that the node is added + expect(root._children).toEqual([node.source]) + }) + + test('Existing file is created', () => { + // no defined paths + expect(store.paths).toEqual({}) + + // create the file + const node1 = new File({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/file.txt', id: 2, mime: 'text/plain' }) + emit('files:node:created', node1) + + // see that there are still no paths + expect(store.paths).toEqual({}) + + // see that the node is added + expect(root._children).toEqual([node1.source]) + + // create the same named file again + const node2 = new File({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/file.txt', id: 2, mime: 'text/plain' }) + emit('files:node:created', node2) + + // see that there are still no paths and the children are not duplicated + expect(store.paths).toEqual({}) + expect(root._children).toEqual([node1.source]) + + }) + + test('Existing folder is created', () => { + // no defined paths + expect(store.paths).toEqual({}) + + // create the file + const node1 = new Folder({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/folder', id: 2 }) + emit('files:node:created', node1) + + // see the path is added + expect(store.paths).toEqual({ files: { [node1.path]: node1.source } }) + + // see that the node is added + expect(root._children).toEqual([node1.source]) + + // create the same named file again + const node2 = new Folder({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/folder', id: 2 }) + emit('files:node:created', node2) + + // see that there is still only one paths and the children are not duplicated + expect(store.paths).toEqual({ files: { [node1.path]: node1.source } }) + expect(root._children).toEqual([node1.source]) + }) + + test('Folder is deleted', () => { + const node = new Folder({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/folder', id: 2 }) + emit('files:node:created', node) + // see that the path is added and the children are set-up + expect(store.paths).toEqual({ files: { [node.path]: node.source } }) + expect(root._children).toEqual([node.source]) + + emit('files:node:deleted', node) + // See the path is removed + expect(store.paths).toEqual({ files: {} }) + // See the child is removed + expect(root._children).toEqual([]) + }) + + test('File is deleted', () => { + const node = new File({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/file.txt', id: 2, mime: 'text/plain' }) + emit('files:node:created', node) + // see that the children are set-up + expect(root._children).toEqual([node.source]) + + emit('files:node:deleted', node) + // See the child is removed + expect(root._children).toEqual([]) + }) +}) diff --git a/cypress/e2e/files/FilesUtils.ts b/cypress/e2e/files/FilesUtils.ts index 1cb84f3f657..79607989d50 100644 --- a/cypress/e2e/files/FilesUtils.ts +++ b/cypress/e2e/files/FilesUtils.ts @@ -45,6 +45,21 @@ export const triggerInlineActionForFile = (filename: string, actionId: string) = getActionsForFile(filename).get(`button[data-cy-files-list-row-action="${CSS.escape(actionId)}"]`).should('exist').click() } +export const createFolder = (folderName: string) => { + cy.intercept('MKCOL', /\/remote.php\/dav\/files\//).as('createFolder') + + // TODO: replace by proper data-cy selectors + cy.get('[data-cy-upload-picker] .action-item__menutoggle').first().click() + cy.contains('.upload-picker__menu-entry button', 'New folder').click() + cy.get('[data-cy-files-new-node-dialog]').should('be.visible') + cy.get('[data-cy-files-new-node-dialog-input]').type(`{selectall}${folderName}`) + cy.get('[data-cy-files-new-node-dialog-submit]').click() + + cy.wait('@createFolder') + + getRowForFile(folderName).should('be.visible') +} + export const moveFile = (fileName: string, dirPath: string) => { getRowForFile(fileName).should('be.visible') triggerActionForFile(fileName, 'move-copy') diff --git a/cypress/e2e/files/duplicated-node-regression.cy.ts b/cypress/e2e/files/duplicated-node-regression.cy.ts new file mode 100644 index 00000000000..14355a62b9d --- /dev/null +++ b/cypress/e2e/files/duplicated-node-regression.cy.ts @@ -0,0 +1,33 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { createFolder, getRowForFile, triggerActionForFile } from './FilesUtils.ts' + +before(() => { + cy.createRandomUser() + .then((user) => { + cy.mkdir(user, '/only once') + cy.login(user) + cy.visit('/apps/files') + }) +}) + +/** + * Regression test for https://github.com/nextcloud/server/issues/47904 + */ +it('Ensure nodes are not duplicated in the file list', () => { + // See the folder + getRowForFile('only once').should('be.visible') + // Delete the folder + cy.intercept('DELETE', '**/remote.php/dav/**').as('deleteFolder') + triggerActionForFile('only once', 'delete') + cy.wait('@deleteFolder') + getRowForFile('only once').should('not.exist') + // Create the folder again + createFolder('only once') + // See folder exists only once + getRowForFile('only once') + .should('have.length', 1) +})