From a8c985fbb0085ca93aa07bc233b05284daf216cf Mon Sep 17 00:00:00 2001 From: ColumbusLabs <287001685+ColumbusLabs@users.noreply.github.com> Date: Wed, 22 Jul 2026 15:25:43 -0400 Subject: [PATCH 1/2] test: migrate FilesList header specs to testing Pinia Signed-off-by: ColumbusLabs <287001685+ColumbusLabs@users.noreply.github.com> --- .../FilesList/FilesListTableHeader.spec.ts | 42 ++++------ .../FilesListTableHeaderButton.spec.ts | 79 +++++++++++-------- 2 files changed, 65 insertions(+), 56 deletions(-) diff --git a/src/tests/views/FilesList/FilesListTableHeader.spec.ts b/src/tests/views/FilesList/FilesListTableHeader.spec.ts index 49a4cc9501..49c6c25204 100644 --- a/src/tests/views/FilesList/FilesListTableHeader.spec.ts +++ b/src/tests/views/FilesList/FilesListTableHeader.spec.ts @@ -3,13 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { beforeEach, describe, expect, it, vi } from 'vitest' +import { createTestingPinia } from '@pinia/testing' import { mount } from '@vue/test-utils' -import { createPinia, setActivePinia } from 'pinia' +import { setActivePinia } from 'pinia' +import { beforeEach, describe, expect, it, vi } from 'vitest' import FilesListTableHeader from '../../../views/FilesList/FilesListTableHeader.vue' -import { useFilesStore } from '../../../store/files.js' -import { useFilesSortingStore } from '../../../store/filesSorting.js' + import { useSelectionStore } from '../../../store/selection.js' type Column = { @@ -103,6 +103,7 @@ function createWrapper(filesCount = 2) { nodes: Array.from({ length: filesCount }, (_, index) => ({ id: index + 1, basename: `file${index + 1}.pdf` })), }, global: { + plugins: [pinia], stubs: { NcCheckboxRadioSwitch: NcCheckboxRadioSwitchStub, FilesListTableHeaderButton: { @@ -116,7 +117,6 @@ function createWrapper(filesCount = 2) { describe('FilesListTableHeader.vue', () => { beforeEach(() => { - setActivePinia(createPinia()) vi.clearAllMocks() }) @@ -230,20 +230,14 @@ describe('FilesListTableHeader.vue', () => { }) it('sets modelValue to true when all files are selected', async () => { - const wrapper = createWrapper(2) - const selectionStore = useSelectionStore() - - selectionStore.set([1, 2]) + const wrapper = createWrapper(2, { selected: [1, 2] }) await wrapper.vm.$nextTick() expect(wrapper.findComponent(NcCheckboxRadioSwitchStub).props('modelValue')).toBe(true) }) it('sets indeterminate when only some files are selected', async () => { - const wrapper = createWrapper(2) - const selectionStore = useSelectionStore() - - selectionStore.set([1]) + const wrapper = createWrapper(2, { selected: [1] }) await wrapper.vm.$nextTick() const stub = wrapper.findComponent(NcCheckboxRadioSwitchStub) @@ -268,24 +262,22 @@ describe('FilesListTableHeader.vue', () => { expect(vm.ariaSortForMode('actions', false)).toBeNull() }) - it('returns descending when the active mode is descending', async () => { - const wrapper = createWrapper() + it('returns descending when the active mode is descending', () => { + const wrapper = createWrapper(2, { + sortingMode: 'created_at', + sortingDirection: 'desc', + }) const vm = wrapper.vm as FilesListTableHeaderVm - const sortingStore = useFilesSortingStore() - sortingStore.sortingMode = 'created_at' - sortingStore.sortingDirection = 'desc' - await wrapper.vm.$nextTick() expect(vm.ariaSortForMode('created_at')).toBe('descending') }) - it('returns ascending when the active mode is ascending', async () => { - const wrapper = createWrapper() + it('returns ascending when the active mode is ascending', () => { + const wrapper = createWrapper(2, { + sortingMode: 'status', + sortingDirection: 'asc', + }) const vm = wrapper.vm as FilesListTableHeaderVm - const sortingStore = useFilesSortingStore() - sortingStore.sortingMode = 'status' - sortingStore.sortingDirection = 'asc' - await wrapper.vm.$nextTick() expect(vm.ariaSortForMode('status')).toBe('ascending') }) diff --git a/src/tests/views/FilesList/FilesListTableHeaderButton.spec.ts b/src/tests/views/FilesList/FilesListTableHeaderButton.spec.ts index b9c82973c4..bef2f55146 100644 --- a/src/tests/views/FilesList/FilesListTableHeaderButton.spec.ts +++ b/src/tests/views/FilesList/FilesListTableHeaderButton.spec.ts @@ -3,11 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { beforeEach, describe, expect, it, vi } from 'vitest' +import { createTestingPinia } from '@pinia/testing' import { mount } from '@vue/test-utils' -import { createPinia, setActivePinia } from 'pinia' +import { setActivePinia } from 'pinia' +import { beforeEach, describe, expect, it, vi } from 'vitest' import FilesListTableHeaderButton from '../../../views/FilesList/FilesListTableHeaderButton.vue' + import { useFilesSortingStore } from '../../../store/filesSorting.js' vi.mock('@nextcloud/event-bus', () => ({ @@ -57,10 +59,31 @@ const NcButtonStub = { template: '', } -function createWrapper(mode = 'size', name = 'Size') { +type FilesSortingState = { + sortingMode?: string + sortingDirection?: string +} + +/** + * Mount the header button with isolated sorting-store state. + * + * @param mode Sort key represented by the button. + * @param name Visible label for the button. + * @param sortingState Initial state for the sorting store. + */ +function createWrapper(mode = 'size', name = 'Size', sortingState: FilesSortingState = {}) { + const pinia = createTestingPinia({ + createSpy: vi.fn, + initialState: { + filesSorting: sortingState, + }, + }) + setActivePinia(pinia) + return mount(FilesListTableHeaderButton, { props: { name, mode }, global: { + plugins: [pinia], stubs: { NcButton: NcButtonStub, NcIconSvgWrapper: true, @@ -71,7 +94,6 @@ function createWrapper(mode = 'size', name = 'Size') { describe('FilesListTableHeaderButton.vue', () => { beforeEach(() => { - setActivePinia(createPinia()) vi.clearAllMocks() }) @@ -82,11 +104,10 @@ describe('FilesListTableHeaderButton.vue', () => { }) it('computes descending state when the same column is sorted descending', () => { - const sortingStore = useFilesSortingStore() - sortingStore.sortingMode = 'size' - sortingStore.sortingDirection = 'desc' - - const wrapper = createWrapper('size') + const wrapper = createWrapper('size', 'Size', { + sortingMode: 'size', + sortingDirection: 'desc', + }) expect(wrapper.vm.isAscending).toBe(false) }) @@ -97,31 +118,29 @@ describe('FilesListTableHeaderButton.vue', () => { }) it('triggers the store sort toggle for the current mode', async () => { - const sortingStore = useFilesSortingStore() - const spy = vi.spyOn(sortingStore, 'toggleSortBy') const wrapper = createWrapper('size') + const sortingStore = useFilesSortingStore() await wrapper.find('button').trigger('click') - expect(spy).toHaveBeenCalledWith('size') + expect(sortingStore.toggleSortBy).toHaveBeenCalledWith('size') }) describe('Vue 3 sorting interactions', () => { it('is true when the column is active and direction is asc', () => { - const sortingStore = useFilesSortingStore() - sortingStore.sortingMode = 'created_at' - sortingStore.sortingDirection = 'asc' - - const wrapper = createWrapper('created_at', 'Created at') + const wrapper = createWrapper('created_at', 'Created at', { + sortingMode: 'created_at', + sortingDirection: 'asc', + }) expect(wrapper.vm.isAscending).toBe(true) }) it('reacts when direction changes from asc to desc', async () => { + const wrapper = createWrapper('created_at', 'Created at', { + sortingMode: 'created_at', + sortingDirection: 'asc', + }) const sortingStore = useFilesSortingStore() - sortingStore.sortingMode = 'created_at' - sortingStore.sortingDirection = 'asc' - - const wrapper = createWrapper('created_at', 'Created at') expect(wrapper.vm.isAscending).toBe(true) sortingStore.sortingDirection = 'desc' @@ -131,10 +150,9 @@ describe('FilesListTableHeaderButton.vue', () => { }) it('adds the active class when the column is the active sort mode', () => { - const sortingStore = useFilesSortingStore() - sortingStore.sortingMode = 'created_at' - - const wrapper = createWrapper('created_at', 'Created at') + const wrapper = createWrapper('created_at', 'Created at', { + sortingMode: 'created_at', + }) expect(wrapper.find('button').classes()).toContain('files-list__column-sort-button--active') }) @@ -145,10 +163,10 @@ describe('FilesListTableHeaderButton.vue', () => { }) it('removes the active class when the active mode changes', async () => { + const wrapper = createWrapper('created_at', 'Created at', { + sortingMode: 'created_at', + }) const sortingStore = useFilesSortingStore() - sortingStore.sortingMode = 'created_at' - - const wrapper = createWrapper('created_at', 'Created at') expect(wrapper.find('button').classes()).toContain('files-list__column-sort-button--active') sortingStore.sortingMode = 'status' @@ -158,13 +176,12 @@ describe('FilesListTableHeaderButton.vue', () => { }) it('delegates click to toggleSortBy for the provided mode', async () => { - const sortingStore = useFilesSortingStore() - const spy = vi.spyOn(sortingStore, 'toggleSortBy') const wrapper = createWrapper('created_at', 'Created at') + const sortingStore = useFilesSortingStore() await wrapper.find('button').trigger('click') - expect(spy).toHaveBeenCalledWith('created_at') + expect(sortingStore.toggleSortBy).toHaveBeenCalledWith('created_at') }) }) }) From 59b4f1bf6fca34270b5aaea271e68cc541016b64 Mon Sep 17 00:00:00 2001 From: ColumbusLabs <287001685+ColumbusLabs@users.noreply.github.com> Date: Thu, 23 Jul 2026 15:10:06 -0400 Subject: [PATCH 2/2] test: narrow FilesList Pinia action stubbing Signed-off-by: ColumbusLabs <287001685+ColumbusLabs@users.noreply.github.com> Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../FilesList/FilesListTableHeader.spec.ts | 46 +++++++++++++------ .../FilesListTableHeaderButton.spec.ts | 16 +------ 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/tests/views/FilesList/FilesListTableHeader.spec.ts b/src/tests/views/FilesList/FilesListTableHeader.spec.ts index 49c6c25204..f679aaf9c2 100644 --- a/src/tests/views/FilesList/FilesListTableHeader.spec.ts +++ b/src/tests/views/FilesList/FilesListTableHeader.spec.ts @@ -5,11 +5,11 @@ import { createTestingPinia } from '@pinia/testing' import { mount } from '@vue/test-utils' -import { setActivePinia } from 'pinia' import { beforeEach, describe, expect, it, vi } from 'vitest' import FilesListTableHeader from '../../../views/FilesList/FilesListTableHeader.vue' +import { useFilesSortingStore } from '../../../store/filesSorting.js' import { useSelectionStore } from '../../../store/selection.js' type Column = { @@ -95,8 +95,16 @@ const NcCheckboxRadioSwitchStub = { } function createWrapper(filesCount = 2) { - const filesStore = useFilesStore() - filesStore.ordered = Array.from({ length: filesCount }, (_, index) => index + 1) as typeof filesStore.ordered + const ordered = Array.from({ length: filesCount }, (_, index) => index + 1) + const pinia = createTestingPinia({ + createSpy: vi.fn, + stubActions: (action, store) => !(store.$id === 'selection' && ['set', 'reset'].includes(action)), + initialState: { + files: { + ordered, + }, + }, + }) return mount(FilesListTableHeader, { props: { @@ -230,14 +238,20 @@ describe('FilesListTableHeader.vue', () => { }) it('sets modelValue to true when all files are selected', async () => { - const wrapper = createWrapper(2, { selected: [1, 2] }) + const wrapper = createWrapper(2) + const selectionStore = useSelectionStore() + + selectionStore.set([1, 2]) await wrapper.vm.$nextTick() expect(wrapper.findComponent(NcCheckboxRadioSwitchStub).props('modelValue')).toBe(true) }) it('sets indeterminate when only some files are selected', async () => { - const wrapper = createWrapper(2, { selected: [1] }) + const wrapper = createWrapper(2) + const selectionStore = useSelectionStore() + + selectionStore.set([1]) await wrapper.vm.$nextTick() const stub = wrapper.findComponent(NcCheckboxRadioSwitchStub) @@ -262,22 +276,24 @@ describe('FilesListTableHeader.vue', () => { expect(vm.ariaSortForMode('actions', false)).toBeNull() }) - it('returns descending when the active mode is descending', () => { - const wrapper = createWrapper(2, { - sortingMode: 'created_at', - sortingDirection: 'desc', - }) + it('returns descending when the active mode is descending', async () => { + const wrapper = createWrapper() const vm = wrapper.vm as FilesListTableHeaderVm + const sortingStore = useFilesSortingStore() + sortingStore.sortingMode = 'created_at' + sortingStore.sortingDirection = 'desc' + await wrapper.vm.$nextTick() expect(vm.ariaSortForMode('created_at')).toBe('descending') }) - it('returns ascending when the active mode is ascending', () => { - const wrapper = createWrapper(2, { - sortingMode: 'status', - sortingDirection: 'asc', - }) + it('returns ascending when the active mode is ascending', async () => { + const wrapper = createWrapper() const vm = wrapper.vm as FilesListTableHeaderVm + const sortingStore = useFilesSortingStore() + sortingStore.sortingMode = 'status' + sortingStore.sortingDirection = 'asc' + await wrapper.vm.$nextTick() expect(vm.ariaSortForMode('status')).toBe('ascending') }) diff --git a/src/tests/views/FilesList/FilesListTableHeaderButton.spec.ts b/src/tests/views/FilesList/FilesListTableHeaderButton.spec.ts index bef2f55146..c9e6479f9d 100644 --- a/src/tests/views/FilesList/FilesListTableHeaderButton.spec.ts +++ b/src/tests/views/FilesList/FilesListTableHeaderButton.spec.ts @@ -5,7 +5,6 @@ import { createTestingPinia } from '@pinia/testing' import { mount } from '@vue/test-utils' -import { setActivePinia } from 'pinia' import { beforeEach, describe, expect, it, vi } from 'vitest' import FilesListTableHeaderButton from '../../../views/FilesList/FilesListTableHeaderButton.vue' @@ -59,26 +58,13 @@ const NcButtonStub = { template: '', } -type FilesSortingState = { - sortingMode?: string - sortingDirection?: string -} - -/** - * Mount the header button with isolated sorting-store state. - * - * @param mode Sort key represented by the button. - * @param name Visible label for the button. - * @param sortingState Initial state for the sorting store. - */ -function createWrapper(mode = 'size', name = 'Size', sortingState: FilesSortingState = {}) { +function createWrapper(mode = 'size', name = 'Size', sortingState: { sortingMode?: string, sortingDirection?: string } = {}) { const pinia = createTestingPinia({ createSpy: vi.fn, initialState: { filesSorting: sortingState, }, }) - setActivePinia(pinia) return mount(FilesListTableHeaderButton, { props: { name, mode },