Skip to content

Commit 3b21b4f

Browse files
test(files-list): add unit tests for FilesListTableHeaderButton
Cover isAscending computed property, --active CSS class presence, and toggleSortBy delegation for active/inactive column states Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent 1b47290 commit 3b21b4f

1 file changed

Lines changed: 194 additions & 0 deletions

File tree

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
/**
7+
* Unit tests for FilesListTableHeaderButton:
8+
* - isAscending computed: determines which arrow icon is shown
9+
* - --active CSS class: applied only to the column currently being sorted
10+
* - click: delegates to filesSortingStore.toggleSortBy(mode)
11+
*/
12+
13+
import { beforeEach, describe, expect, it, vi } from 'vitest'
14+
import { mount } from '@vue/test-utils'
15+
import { setActivePinia, createPinia } from 'pinia'
16+
import FilesListTableHeaderButton from './FilesListTableHeaderButton.vue'
17+
import { useFilesSortingStore } from '../../store/filesSorting.js'
18+
19+
// ---------------------------------------------------------------------------
20+
// Mocks (same set required by filesSorting store)
21+
// ---------------------------------------------------------------------------
22+
23+
vi.mock('@nextcloud/event-bus', () => ({
24+
emit: vi.fn(),
25+
subscribe: vi.fn(),
26+
}))
27+
28+
vi.mock('@nextcloud/logger', () => ({
29+
getLogger: vi.fn(() => ({
30+
error: vi.fn(),
31+
warn: vi.fn(),
32+
info: vi.fn(),
33+
debug: vi.fn(),
34+
})),
35+
getLoggerBuilder: vi.fn(() => ({
36+
setApp: vi.fn().mockReturnThis(),
37+
detectUser: vi.fn().mockReturnThis(),
38+
build: vi.fn(() => ({
39+
error: vi.fn(),
40+
warn: vi.fn(),
41+
info: vi.fn(),
42+
debug: vi.fn(),
43+
})),
44+
})),
45+
}))
46+
47+
vi.mock('@nextcloud/auth', () => ({
48+
getCurrentUser: vi.fn(() => ({ uid: 'testuser', displayName: 'Test User' })),
49+
}))
50+
51+
vi.mock('@nextcloud/axios', () => ({
52+
default: { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn() },
53+
}))
54+
55+
vi.mock('@nextcloud/router', () => ({
56+
generateOcsUrl: vi.fn((path) => `/ocs/v2.php${path}`),
57+
}))
58+
59+
// loadState returns the default value — store initialises with
60+
// sortingMode = 'created_at' and sortingDirection = 'desc'.
61+
vi.mock('@nextcloud/initial-state', () => ({
62+
loadState: vi.fn((_app, _key, defaultValue) => defaultValue),
63+
}))
64+
65+
// ---------------------------------------------------------------------------
66+
// NcButton stub — forwards $attrs so @click reaches the root button element
67+
// ---------------------------------------------------------------------------
68+
const NcButtonStub = {
69+
name: 'NcButton',
70+
template: '<button v-bind="$attrs"><slot name="icon" /><slot /></button>',
71+
}
72+
73+
// ---------------------------------------------------------------------------
74+
// Helper
75+
// ---------------------------------------------------------------------------
76+
const createWrapper = (mode = 'created_at') =>
77+
mount(FilesListTableHeaderButton, {
78+
props: { name: 'Created at', mode },
79+
global: {
80+
stubs: {
81+
NcButton: NcButtonStub,
82+
NcIconSvgWrapper: { template: '<span />' },
83+
},
84+
},
85+
})
86+
87+
// ---------------------------------------------------------------------------
88+
// Tests
89+
// ---------------------------------------------------------------------------
90+
91+
describe('FilesListTableHeaderButton', () => {
92+
beforeEach(() => {
93+
setActivePinia(createPinia())
94+
vi.clearAllMocks()
95+
})
96+
97+
describe('RULE: isAscending reflects store state', () => {
98+
it('is true when the column is not the active sort mode', () => {
99+
const wrapper = createWrapper('status') // store defaults to 'created_at'
100+
const vm = wrapper.vm as InstanceType<typeof FilesListTableHeaderButton> & { isAscending: boolean }
101+
102+
expect(vm.isAscending).toBe(true)
103+
})
104+
105+
it('is true when the column is active and direction is asc', () => {
106+
const sortingStore = useFilesSortingStore()
107+
sortingStore.sortingMode = 'created_at'
108+
sortingStore.sortingDirection = 'asc'
109+
110+
const wrapper = createWrapper('created_at')
111+
const vm = wrapper.vm as InstanceType<typeof FilesListTableHeaderButton> & { isAscending: boolean }
112+
113+
expect(vm.isAscending).toBe(true)
114+
})
115+
116+
it('is false when the column is active and direction is desc', () => {
117+
const sortingStore = useFilesSortingStore()
118+
sortingStore.sortingMode = 'created_at'
119+
sortingStore.sortingDirection = 'desc'
120+
121+
const wrapper = createWrapper('created_at')
122+
const vm = wrapper.vm as InstanceType<typeof FilesListTableHeaderButton> & { isAscending: boolean }
123+
124+
expect(vm.isAscending).toBe(false)
125+
})
126+
127+
it('becomes false reactively when direction changes to desc', async () => {
128+
const sortingStore = useFilesSortingStore()
129+
sortingStore.sortingMode = 'created_at'
130+
sortingStore.sortingDirection = 'asc'
131+
132+
const wrapper = createWrapper('created_at')
133+
const vm = wrapper.vm as InstanceType<typeof FilesListTableHeaderButton> & { isAscending: boolean }
134+
expect(vm.isAscending).toBe(true)
135+
136+
sortingStore.sortingDirection = 'desc'
137+
await wrapper.vm.$nextTick()
138+
139+
expect(vm.isAscending).toBe(false)
140+
})
141+
})
142+
143+
describe('RULE: --active class applied only to the active column', () => {
144+
it('adds --active class when the column is the active sort mode', () => {
145+
const sortingStore = useFilesSortingStore()
146+
sortingStore.sortingMode = 'created_at'
147+
148+
const wrapper = createWrapper('created_at')
149+
expect(wrapper.find('button').classes()).toContain('files-list__column-sort-button--active')
150+
})
151+
152+
it('does not add --active class when the column is not the active sort mode', () => {
153+
// Store defaults to 'created_at'; mount with mode='status'
154+
const wrapper = createWrapper('status')
155+
expect(wrapper.find('button').classes()).not.toContain('files-list__column-sort-button--active')
156+
})
157+
158+
it('removes --active class reactively when another column becomes active', async () => {
159+
const sortingStore = useFilesSortingStore()
160+
sortingStore.sortingMode = 'created_at'
161+
162+
const wrapper = createWrapper('created_at')
163+
expect(wrapper.find('button').classes()).toContain('files-list__column-sort-button--active')
164+
165+
sortingStore.sortingMode = 'status'
166+
await wrapper.vm.$nextTick()
167+
168+
expect(wrapper.find('button').classes()).not.toContain('files-list__column-sort-button--active')
169+
})
170+
})
171+
172+
describe('RULE: click delegates to filesSortingStore.toggleSortBy(mode)', () => {
173+
it('calls toggleSortBy with the column mode on click', async () => {
174+
const sortingStore = useFilesSortingStore()
175+
const spy = vi.spyOn(sortingStore, 'toggleSortBy')
176+
177+
const wrapper = createWrapper('created_at')
178+
await wrapper.find('button').trigger('click')
179+
180+
expect(spy).toHaveBeenCalledOnce()
181+
expect(spy).toHaveBeenCalledWith('created_at')
182+
})
183+
184+
it('calls toggleSortBy with the correct mode for each column', async () => {
185+
const sortingStore = useFilesSortingStore()
186+
const spy = vi.spyOn(sortingStore, 'toggleSortBy')
187+
188+
const wrapper = createWrapper('status')
189+
await wrapper.find('button').trigger('click')
190+
191+
expect(spy).toHaveBeenCalledWith('status')
192+
})
193+
})
194+
})

0 commit comments

Comments
 (0)