From c7643aa137e2260394e7c301d44913541bee8e87 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Thu, 16 Jul 2026 19:16:03 +0200 Subject: [PATCH] fix(filepicker): only add current folder if pickable If the current folder is not pickable do not set it as selected for the buttons callback. Signed-off-by: Ferdinand Thiessen --- lib/components/FilePicker/FilePicker.spec.ts | 99 ++++++++++++++++++++ lib/components/FilePicker/FilePicker.vue | 2 + 2 files changed, 101 insertions(+) create mode 100644 lib/components/FilePicker/FilePicker.spec.ts diff --git a/lib/components/FilePicker/FilePicker.spec.ts b/lib/components/FilePicker/FilePicker.spec.ts new file mode 100644 index 000000000..ed4a43bc6 --- /dev/null +++ b/lib/components/FilePicker/FilePicker.spec.ts @@ -0,0 +1,99 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { INode } from '@nextcloud/files' + +import { Folder } from '@nextcloud/files' +import { shallowMount } from '@vue/test-utils' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { ref, shallowRef } from 'vue' +import FilePicker from './FilePicker.vue' + +const axios = vi.hoisted(() => ({ + get: vi.fn(() => new Promise(() => {})), +})) +vi.mock('@nextcloud/axios', () => ({ default: axios })) + +// Shared state injected into the mocked `useDAVFiles` composable so we can +// control the current folder without hitting WebDAV. +const dav = vi.hoisted(() => ({ + files: undefined as never, + folder: undefined as never, + isLoading: undefined as never, +})) +vi.mock('../../composables/dav.ts', () => ({ + useDAVFiles: () => ({ + files: dav.files, + folder: dav.folder, + isLoading: dav.isLoading, + loadFiles: vi.fn(), + createDirectory: vi.fn(), + }), +})) + +const currentFolder = new Folder({ + owner: null, + source: 'http://example.com/dav/folder', + root: '/', +}) + +describe('FilePicker', () => { + beforeEach(() => { + dav.files = shallowRef([]) as never + dav.folder = shallowRef(currentFolder) as never + dav.isLoading = ref(false) as never + }) + + afterEach(() => { + vi.clearAllMocks() + }) + + /** + * Mount the FilePicker with a `buttons` factory that captures the nodes + * that would be passed to the button callback. + * + * @param props Additional props for the FilePicker + */ + function mountWithButtonSpy(props: Record = {}) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const buttons = vi.fn((_nodes: INode[]) => [{ + label: 'Pick', + callback: () => {}, + }]) + + shallowMount(FilePicker, { + propsData: { + name: 'Test picker', + buttons, + ...props, + }, + }) + + // The last call reflects the current selection state + return buttons.mock.calls.at(-1)![0] + } + + it('picks the current folder if directory picking is allowed and nothing is selected', () => { + const nodes = mountWithButtonSpy({ allowPickDirectory: true }) + expect(nodes).toEqual([currentFolder]) + }) + + it('picks the current folder if `canPickFn` allows it', () => { + const canPickFn = vi.fn(() => true) + const nodes = mountWithButtonSpy({ allowPickDirectory: true, canPickFn }) + + expect(canPickFn).toHaveBeenCalledWith(currentFolder) + expect(nodes).toEqual([currentFolder]) + }) + + it('does not pick the current folder if `canPickFn` rejects it', () => { + const canPickFn = vi.fn(() => false) + const nodes = mountWithButtonSpy({ allowPickDirectory: true, canPickFn }) + + expect(canPickFn).toHaveBeenCalledWith(currentFolder) + // The unpickable folder must not be passed as a selected node + expect(nodes).toEqual([]) + }) +}) diff --git a/lib/components/FilePicker/FilePicker.vue b/lib/components/FilePicker/FilePicker.vue index 390eda8c2..6414bfbb4 100644 --- a/lib/components/FilePicker/FilePicker.vue +++ b/lib/components/FilePicker/FilePicker.vue @@ -157,6 +157,8 @@ const dialogButtons = computed(() => { const nodes = selectedFiles.value.length === 0 && props.allowPickDirectory && currentFolder.value + // either no canPickFn is provided or the current folder is pickable + && (!props.canPickFn || props.canPickFn(currentFolder.value)) ? [currentFolder.value] : selectedFiles.value