]> source.dussan.org Git - nextcloud-server.git/commitdiff
feat: add sidebar action testing
authorJohn Molakvoæ <skjnldsv@protonmail.com>
Wed, 14 Jun 2023 08:50:08 +0000 (10:50 +0200)
committerJohn Molakvoæ <skjnldsv@protonmail.com>
Wed, 21 Jun 2023 07:08:04 +0000 (09:08 +0200)
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
apps/files/src/actions/sidebarAction.spec.ts [new file with mode: 0644]
apps/files/src/actions/sidebarAction.ts
apps/files/src/services/FileAction.ts
package-lock.json
package.json
tsconfig.json

diff --git a/apps/files/src/actions/sidebarAction.spec.ts b/apps/files/src/actions/sidebarAction.spec.ts
new file mode 100644 (file)
index 0000000..0381ab9
--- /dev/null
@@ -0,0 +1,144 @@
+/**
+ * @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
+ *
+ * @author John Molakvoæ <skjnldsv@protonmail.com>
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+import { action } from './sidebarAction'
+import { expect } from '@jest/globals'
+import { File } from '@nextcloud/files'
+import { FileAction } from '../services/FileAction'
+import type { Navigation } from '../services/Navigation'
+import logger from '../logger'
+
+const view = {
+       id: 'files',
+       name: 'Files',
+} as Navigation
+
+describe('Open sidebar action conditions tests', () => {
+       test('Default values', () => {
+               expect(action).toBeInstanceOf(FileAction)
+               expect(action.id).toBe('details')
+               expect(action.displayName([], view)).toBe('Details')
+               expect(action.iconSvgInline([], view)).toBe('SvgMock')
+               expect(action.default).toBe(true)
+               expect(action.order).toBe(-50)
+       })
+})
+
+describe('Open folder action enabled tests', () => {
+       test('Enabled for ressources within user root folder', () => {
+               window.OCA = { Files: { Sidebar: {} } }
+
+               const file = new File({
+                       id: 1,
+                       source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt',
+                       owner: 'admin',
+                       mime: 'text/plain',
+               })
+
+               expect(action.enabled).toBeDefined()
+               expect(action.enabled!([file], view)).toBe(true)
+       })
+
+       test('Disabled if more than one node', () => {
+               window.OCA = { Files: { Sidebar: {} } }
+
+               const file1 = new File({
+                       id: 1,
+                       source: 'https://cloud.domain.com/remote.php/dav/files/admin/foo.txt',
+                       owner: 'admin',
+                       mime: 'text/plain',
+               })
+               const file2 = new File({
+                       id: 1,
+                       source: 'https://cloud.domain.com/remote.php/dav/files/admin/bar.txt',
+                       owner: 'admin',
+                       mime: 'text/plain',
+               })
+
+               expect(action.enabled).toBeDefined()
+               expect(action.enabled!([file1, file2], view)).toBe(false)
+       })
+
+       test('Disabled if no Sidebar', () => {
+               window.OCA = {}
+
+               const file = new File({
+                       id: 1,
+                       source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt',
+                       owner: 'admin',
+                       mime: 'text/plain',
+               })
+
+               expect(action.enabled).toBeDefined()
+               expect(action.enabled!([file], view)).toBe(false)
+       })
+
+       test('Disabled for non-dav ressources', () => {
+               window.OCA = { Files: { Sidebar: {} } }
+
+               const file = new File({
+                       id: 1,
+                       source: 'https://domain.com/documents/admin/foobar.txt',
+                       owner: 'admin',
+                       mime: 'text/plain',
+               })
+
+               expect(action.enabled).toBeDefined()
+               expect(action.enabled!([file], view)).toBe(false)
+       })
+})
+
+describe('Open sidebar action exec tests', () => {
+       test('Open sidebar', async () => {
+               const openMock = jest.fn()
+               window.OCA = { Files: { Sidebar: { open: openMock } } }
+
+               const file = new File({
+                       id: 1,
+                       source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt',
+                       owner: 'admin',
+                       mime: 'text/plain',
+               })
+
+               const exec = await action.exec(file, view, '/')
+               // Silent action
+               expect(exec).toBe(null)
+               expect(openMock).toBeCalledWith('/foobar.txt')
+       })
+
+       test('Open sidebar fails', async () => {
+               const openMock = jest.fn(() => { throw new Error('Mock error') })
+               logger.error = jest.fn()
+               window.OCA = { Files: { Sidebar: { open: openMock } } }
+
+               const file = new File({
+                       id: 1,
+                       source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt',
+                       owner: 'admin',
+                       mime: 'text/plain',
+               })
+
+               const exec = await action.exec(file, view, '/')
+               expect(exec).toBe(false)
+               expect(openMock).toBeCalledTimes(1)
+               expect(logger.error).toBeCalledTimes(1)
+       })
+})
index f56d3a9475f42eebc0a485f02267642852db1b1c..d0baf611992a02d0be79823c0513a848a473181f 100644 (file)
@@ -23,19 +23,30 @@ import { translate as t } from '@nextcloud/l10n'
 import InformationSvg from '@mdi/svg/svg/information-variant.svg?raw'
 import type { Node } from '@nextcloud/files'
 
-import { registerFileAction, FileAction } from '../services/FileAction.ts'
+import { registerFileAction, FileAction } from '../services/FileAction'
 import logger from '../logger.js'
 
 export const ACTION_DETAILS = 'details'
 
-registerFileAction(new FileAction({
+export const action = new FileAction({
        id: ACTION_DETAILS,
        displayName: () => t('files', 'Details'),
        iconSvgInline: () => InformationSvg,
 
        // Sidebar currently supports user folder only, /files/USER
-       enabled: (files: Node[]) => !!window?.OCA?.Files?.Sidebar
-               && files.some(node => node.root?.startsWith('/files/')),
+       enabled: (nodes: Node[]) => {
+               // Only works on single node
+               if (nodes.length !== 1) {
+                       return false
+               }
+
+               // Only work if the sidebar is available
+               if (!window?.OCA?.Files?.Sidebar) {
+                       return false
+               }
+
+               return nodes[0].root?.startsWith('/files/') ?? false
+       },
 
        async exec(node: Node) {
                try {
@@ -51,4 +62,7 @@ registerFileAction(new FileAction({
 
        default: true,
        order: -50,
-}))
+})
+
+registerFileAction(action)
+
index 70d6405c8049af21505c85d9c3585b8fd4b2273c..6b2e3750a2425c807c5eaad6181fba8accc8f741 100644 (file)
@@ -39,11 +39,11 @@ interface FileActionData {
        /** Unique ID */
        id: string
        /** Translatable string displayed in the menu */
-       displayName: (files: Node[], view) => string
+       displayName: (files: Node[], view: Navigation) => string
        /** Svg as inline string. <svg><path fill="..." /></svg> */
-       iconSvgInline: (files: Node[], view) => string
+       iconSvgInline: (files: Node[], view: Navigation) => string
        /** Condition wether this action is shown or not */
-       enabled?: (files: Node[], view) => boolean
+       enabled?: (files: Node[], view: Navigation) => boolean
        /**
         * Function executed on single file action
         * @returns true if the action was executed, false otherwise
@@ -64,12 +64,12 @@ interface FileActionData {
        /**
         * If true, the renderInline function will be called
         */
-       inline?: (file: Node, view) => boolean,
+       inline?: (file: Node, view: Navigation) => boolean,
        /**
         * If defined, the returned html element will be
         * appended before the actions menu.
         */
-       renderInline?: (file: Node, view) => HTMLElement,
+       renderInline?: (file: Node, view: Navigation) => HTMLElement,
 }
 
 export class FileAction {
index bfe848e1acd7f03761bf3868a8a4b8e9445369c1..4631d185b967bb8e91bc17d0e3ca4680cd33e8c0 100644 (file)
         "@testing-library/user-event": "^14.4.3",
         "@testing-library/vue": "^5.8.3",
         "@types/dockerode": "^3.3.17",
+        "@types/jest": "^29.5.2",
         "@typescript-eslint/eslint-plugin": "^5.59.2",
         "@typescript-eslint/parser": "^5.59.5",
         "@vue/test-utils": "^1.3.5",
       }
     },
     "node_modules/@types/jest": {
-      "version": "29.5.1",
+      "version": "29.5.2",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.2.tgz",
+      "integrity": "sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "expect": "^29.0.0",
         "pretty-format": "^29.0.0"
       }
     },
     "@types/jest": {
-      "version": "29.5.1",
+      "version": "29.5.2",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.2.tgz",
+      "integrity": "sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg==",
       "dev": true,
       "requires": {
         "expect": "^29.0.0",
index 5f729f0fddf34fd1039f9703a540946aee96d950..5e58a7f7ee3281895087c67637cd8485016ddc06 100644 (file)
     "@testing-library/user-event": "^14.4.3",
     "@testing-library/vue": "^5.8.3",
     "@types/dockerode": "^3.3.17",
+    "@types/jest": "^29.5.2",
     "@typescript-eslint/eslint-plugin": "^5.59.2",
     "@typescript-eslint/parser": "^5.59.5",
     "@vue/test-utils": "^1.3.5",
index d8f4257afe49784587b3ccf1325bc10170b124e5..b49d327b1a0fabe8c0e390e25a000a5f89a6aa31 100644 (file)
@@ -2,7 +2,7 @@
        "extends": "@vue/tsconfig/tsconfig.json",
        "include": ["./apps/**/*.ts", "./core/**/*.ts", "./*.d.ts"],
        "compilerOptions": {
-               "types": ["cypress", "node", "vue"],
+               "types": ["cypress", "jest", "node", "vue"],
                "outDir": "./dist/",
                "target": "ESNext",
                "module": "esnext",