|
3 | 3 | * SPDX-License-Identifier: AGPL-3.0-or-later |
4 | 4 | */ |
5 | 5 |
|
6 | | -import { File } from '@nextcloud/files' |
| 6 | +import type { VueWrapper } from '@vue/test-utils' |
| 7 | +import type { ComponentProps } from 'vue-component-type-helpers' |
| 8 | + |
| 9 | +import { File, Folder, Permission } from '@nextcloud/files' |
7 | 10 | import { shallowMount } from '@vue/test-utils' |
8 | 11 | import { afterEach, describe, expect, it, vi } from 'vitest' |
9 | 12 | import { nextTick } from 'vue' |
10 | 13 | import FileListRow from './FileListRow.vue' |
11 | 14 |
|
12 | | -describe('FilePicker: FileListRow', () => { |
13 | | - const node = new File({ |
14 | | - owner: null, |
15 | | - mtime: new Date(), |
16 | | - mime: 'text/plain', |
17 | | - source: 'https://example.com/dav/a.txt', |
18 | | - root: '/', |
19 | | - attributes: { displayName: 'test' }, |
| 15 | +type SubmitAction = (wrapper: VueWrapper<any>) => Promise<void> |
| 16 | +type ElementEvent = { 'update:selected': boolean | undefined, enterDirectory: Folder | undefined } |
| 17 | + |
| 18 | +async function clickCheckboxAction(wrapper: VueWrapper<any>) { |
| 19 | + wrapper.find('input[type="checkbox"]').trigger('click') |
| 20 | +} |
| 21 | + |
| 22 | +async function clickElementAction(wrapper: VueWrapper<any>) { |
| 23 | + wrapper.find('[data-testid="row-name"]').trigger('click') |
| 24 | +} |
| 25 | + |
| 26 | +async function pressEnterAction(wrapper: VueWrapper<any>) { |
| 27 | + wrapper.element.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Enter' })) |
| 28 | + await nextTick() |
| 29 | +} |
| 30 | + |
| 31 | +function testSubmitNode(name: string, props: ComponentProps<typeof FileListRow>, eventPayload: ElementEvent, actionCallback: SubmitAction) { |
| 32 | + it(name, async () => { |
| 33 | + const wrapper = shallowMount(FileListRow, { |
| 34 | + props, |
| 35 | + global: { |
| 36 | + stubs: { |
| 37 | + NcCheckboxRadioSwitch: { |
| 38 | + template: '<label><input type="checkbox" @click="$emit(\'update:model-value\', true)" ></label>', |
| 39 | + }, |
| 40 | + }, |
| 41 | + }, |
| 42 | + }) |
| 43 | + |
| 44 | + await actionCallback(wrapper) |
| 45 | + |
| 46 | + for (const [event, payload] of Object.entries(eventPayload)) { |
| 47 | + if (payload === undefined) { |
| 48 | + expect(wrapper.emitted(event)).toBeUndefined() |
| 49 | + } else { |
| 50 | + expect(wrapper.emitted(event)).toEqual([[payload]]) |
| 51 | + } |
| 52 | + } |
20 | 53 | }) |
| 54 | +} |
| 55 | + |
| 56 | +const node = new File({ |
| 57 | + owner: 'alice', |
| 58 | + mtime: new Date(), |
| 59 | + mime: 'text/plain', |
| 60 | + source: 'https://example.com/remote.php/dav/alice/a.txt', |
| 61 | + root: '/', |
| 62 | + attributes: { displayName: 'test' }, |
| 63 | +}) |
21 | 64 |
|
| 65 | +const folder = new Folder({ |
| 66 | + owner: 'alice', |
| 67 | + mtime: new Date(), |
| 68 | + mime: 'httpd/unix-directory', |
| 69 | + source: 'https://example.com/remote.php/dav/alice/b', |
| 70 | + root: '/', |
| 71 | + permissions: Permission.ALL, |
| 72 | + attributes: { displayName: 'test folder' }, |
| 73 | +}) |
| 74 | + |
| 75 | +const folderNonReadable = new Folder({ |
| 76 | + owner: 'alice', |
| 77 | + mtime: new Date(), |
| 78 | + mime: 'httpd/unix-directory', |
| 79 | + source: 'https://example.com/remote.php/dav/alice/b', |
| 80 | + root: '/', |
| 81 | + permissions: Permission.ALL & ~Permission.READ, |
| 82 | + attributes: { displayName: 'test folder' }, |
| 83 | +}) |
| 84 | + |
| 85 | +const defaultOptions = { |
| 86 | + selected: false, |
| 87 | + cropImagePreviews: true, |
| 88 | + canPick: true, |
| 89 | + showCheckbox: true, |
| 90 | + allowPickDirectory: true, |
| 91 | +} |
| 92 | + |
| 93 | +const noEmits = { |
| 94 | + 'update:selected': undefined, |
| 95 | + enterDirectory: undefined, |
| 96 | +} |
| 97 | + |
| 98 | +const selectNode = { |
| 99 | + 'update:selected': true, |
| 100 | + enterDirectory: undefined, |
| 101 | +} |
| 102 | + |
| 103 | +const navigateToFolder = { |
| 104 | + 'update:selected': undefined, |
| 105 | + enterDirectory: folder, |
| 106 | +} |
| 107 | + |
| 108 | +describe('FilePicker: FileListRow', () => { |
22 | 109 | afterEach(() => { |
23 | 110 | vi.restoreAllMocks() |
24 | 111 | }) |
@@ -64,82 +151,73 @@ describe('FilePicker: FileListRow', () => { |
64 | 151 | expect(wrapper.find('[data-testid="row-checkbox"]').exists()).toBe(false) |
65 | 152 | }) |
66 | 153 |
|
67 | | - it('Click checkbox triggers select', async () => { |
68 | | - const wrapper = shallowMount(FileListRow, { |
69 | | - props: { |
70 | | - allowPickDirectory: false, |
71 | | - selected: false, |
72 | | - showCheckbox: true, |
73 | | - canPick: true, |
74 | | - node, |
75 | | - cropImagePreviews: true, |
76 | | - }, |
77 | | - global: { |
78 | | - stubs: { |
79 | | - NcCheckboxRadioSwitch: { |
80 | | - template: '<label><input type="checkbox" @click="$emit(\'update:model-value\', true)" ></label>', |
81 | | - }, |
82 | | - }, |
83 | | - }, |
| 154 | + describe('when node is a file', () => { |
| 155 | + const fileOptions = { |
| 156 | + ...defaultOptions, |
| 157 | + node, |
| 158 | + } |
| 159 | + |
| 160 | + testSubmitNode('Click checkbox triggers select', { ...fileOptions }, selectNode, clickCheckboxAction) |
| 161 | + testSubmitNode('Click element triggers select', { ...fileOptions }, selectNode, clickElementAction) |
| 162 | + testSubmitNode('Click element without checkbox triggers select', { ...fileOptions, showCheckbox: false }, selectNode, clickElementAction) |
| 163 | + testSubmitNode('Enter triggers select', { ...fileOptions, showCheckbox: false }, selectNode, pressEnterAction) |
| 164 | + |
| 165 | + describe('canPick: false', () => { |
| 166 | + const options = { |
| 167 | + ...fileOptions, |
| 168 | + canPick: false, |
| 169 | + } |
| 170 | + |
| 171 | + testSubmitNode('Click checkbox does not triggers select', options, noEmits, clickCheckboxAction) |
| 172 | + testSubmitNode('Click element does not triggers select', options, noEmits, clickElementAction) |
| 173 | + testSubmitNode('Click element without checkbox does not triggers select', { ...options, showCheckbox: false }, noEmits, clickElementAction) |
| 174 | + testSubmitNode('Enter does not triggers select', { ...options, showCheckbox: false }, noEmits, pressEnterAction) |
84 | 175 | }) |
85 | | - |
86 | | - await wrapper.find('input[type="checkbox"]').trigger('click') |
87 | | - |
88 | | - // one event with payload `true` is expected |
89 | | - expect(wrapper.emitted('update:selected')).toEqual([[true]]) |
90 | 176 | }) |
91 | 177 |
|
92 | | - it('Click element triggers select', async () => { |
93 | | - const wrapper = shallowMount(FileListRow, { |
94 | | - props: { |
95 | | - allowPickDirectory: false, |
96 | | - selected: false, |
97 | | - showCheckbox: true, |
98 | | - canPick: true, |
99 | | - node, |
100 | | - cropImagePreviews: true, |
101 | | - }, |
| 178 | + describe('when node is a folder', () => { |
| 179 | + const folderOptions = { |
| 180 | + ...defaultOptions, |
| 181 | + node: folder, |
| 182 | + } |
| 183 | + |
| 184 | + testSubmitNode('Click checkbox triggers select', folderOptions, selectNode, clickCheckboxAction) |
| 185 | + testSubmitNode('Click element navigates to it', folderOptions, navigateToFolder, clickElementAction) |
| 186 | + testSubmitNode('Click element without checkbox navigates to it', { ...folderOptions, showCheckbox: false }, navigateToFolder, clickElementAction) |
| 187 | + testSubmitNode('Enter navigates to it', { ...folderOptions, showCheckbox: false }, navigateToFolder, pressEnterAction) |
| 188 | + |
| 189 | + describe('canPick: false', () => { |
| 190 | + const options = { |
| 191 | + ...folderOptions, |
| 192 | + canPick: false, |
| 193 | + } |
| 194 | + |
| 195 | + testSubmitNode('Click checkbox does not triggers select', options, noEmits, clickCheckboxAction) |
| 196 | + testSubmitNode('Click element navigates to it', options, navigateToFolder, clickElementAction) |
| 197 | + testSubmitNode('Click element without checkbox navigates to it', { ...options, showCheckbox: false }, navigateToFolder, clickElementAction) |
| 198 | + testSubmitNode('Enter navigates to it', { ...options, showCheckbox: false }, navigateToFolder, pressEnterAction) |
102 | 199 | }) |
103 | 200 |
|
104 | | - await wrapper.find('[data-testid="row-name"]').trigger('click') |
| 201 | + describe('without READ permissions', () => { |
| 202 | + const options = { |
| 203 | + ...folderOptions, |
| 204 | + node: folderNonReadable, |
| 205 | + } |
105 | 206 |
|
106 | | - // one event with payload `true` is expected |
107 | | - expect(wrapper.emitted('update:selected')).toEqual([[true]]) |
108 | | - }) |
109 | | - |
110 | | - it('Click element without checkbox triggers select', async () => { |
111 | | - const wrapper = shallowMount(FileListRow, { |
112 | | - props: { |
113 | | - allowPickDirectory: false, |
114 | | - selected: false, |
115 | | - showCheckbox: false, |
116 | | - canPick: true, |
117 | | - node, |
118 | | - cropImagePreviews: true, |
119 | | - }, |
| 207 | + testSubmitNode('Click checkbox triggers select', options, selectNode, clickCheckboxAction) |
| 208 | + testSubmitNode('Click element does not navigates to it', options, noEmits, clickElementAction) |
| 209 | + testSubmitNode('Click element without checkbox does not navigates to it', { ...options, showCheckbox: false }, noEmits, clickElementAction) |
| 210 | + testSubmitNode('Enter does not navigates to it', { ...options, showCheckbox: false }, noEmits, pressEnterAction) |
120 | 211 | }) |
121 | 212 |
|
122 | | - await wrapper.find('[data-testid="row-name"]').trigger('click') |
123 | | - |
124 | | - // one event with payload `true` is expected |
125 | | - expect(wrapper.emitted('update:selected')).toEqual([[true]]) |
126 | | - }) |
127 | | - |
128 | | - it('Enter triggers select', async () => { |
129 | | - const wrapper = shallowMount(FileListRow, { |
130 | | - props: { |
| 213 | + describe('allowPickDirectory: false', () => { |
| 214 | + const options = { |
| 215 | + ...folderOptions, |
| 216 | + node: folderNonReadable, |
131 | 217 | allowPickDirectory: false, |
132 | | - selected: false, |
133 | | - showCheckbox: false, |
134 | | - canPick: true, |
135 | | - node, |
136 | | - cropImagePreviews: true, |
137 | | - }, |
138 | | - }) |
| 218 | + } |
139 | 219 |
|
140 | | - wrapper.element.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Enter' })) |
141 | | - await nextTick() |
142 | | - |
143 | | - expect(wrapper.emitted('update:selected')).toEqual([[true]]) |
| 220 | + testSubmitNode('Click checkbox does not triggers select', options, noEmits, clickCheckboxAction) |
| 221 | + }) |
144 | 222 | }) |
145 | 223 | }) |
0 commit comments