Skip to content

Commit b0242d0

Browse files
test(FileListFilters): add component tests
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent a59fb23 commit b0242d0

1 file changed

Lines changed: 208 additions & 0 deletions

File tree

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { beforeEach, describe, expect, it, vi } from 'vitest'
7+
import { mount } from '@vue/test-utils'
8+
import { computed, ref } from '@vue/reactivity'
9+
import { setActivePinia } from 'pinia'
10+
import { createTestingPinia } from '@pinia/testing'
11+
12+
import FileListFilters from '../../../views/FilesList/FileListFilters.vue'
13+
import { useFiltersStore } from '../../../store/filters.js'
14+
15+
// Controlled ref to toggle isWide in tests
16+
const mockIsWide = ref(true)
17+
18+
vi.mock('../../../composables/useFileListWidth.js', () => ({
19+
useFileListWidth: () => ({
20+
isWide: computed(() => mockIsWide.value),
21+
isMedium: computed(() => false),
22+
isNarrow: computed(() => !mockIsWide.value),
23+
width: ref(0),
24+
}),
25+
}))
26+
27+
vi.mock('@nextcloud/l10n', () => ({
28+
t: vi.fn((_app: string, text: string) => text),
29+
}))
30+
31+
vi.mock('@nextcloud/logger', () => ({
32+
getLogger: vi.fn(() => ({
33+
error: vi.fn(),
34+
warn: vi.fn(),
35+
info: vi.fn(),
36+
debug: vi.fn(),
37+
})),
38+
getLoggerBuilder: vi.fn(() => ({
39+
setApp: vi.fn().mockReturnThis(),
40+
detectUser: vi.fn().mockReturnThis(),
41+
build: vi.fn(() => ({
42+
error: vi.fn(),
43+
warn: vi.fn(),
44+
info: vi.fn(),
45+
debug: vi.fn(),
46+
})),
47+
})),
48+
}))
49+
50+
vi.mock('@nextcloud/axios', () => ({
51+
default: {
52+
get: vi.fn(),
53+
post: vi.fn(),
54+
delete: vi.fn(),
55+
patch: vi.fn(),
56+
},
57+
}))
58+
59+
vi.mock('@nextcloud/router', () => ({
60+
generateOcsUrl: vi.fn((path: string) => `/ocs/v2.php${path}`),
61+
}))
62+
63+
vi.mock('@nextcloud/initial-state', () => ({
64+
loadState: vi.fn((_app: string, _key: string, defaultValue: unknown) => defaultValue),
65+
}))
66+
67+
vi.mock('@nextcloud/moment', () => ({
68+
default: vi.fn(() => ({
69+
format: () => 'date',
70+
fromNow: () => '2 days ago',
71+
})),
72+
}))
73+
74+
vi.mock('@nextcloud/vue/components/NcButton', () => ({
75+
default: {
76+
name: 'NcButton',
77+
props: ['pressed', 'variant', 'ariaLabel'],
78+
template: '<button class="nc-button-stub" :data-pressed="pressed" :data-variant="variant"><slot /><slot name="icon" /></button>',
79+
},
80+
}))
81+
82+
vi.mock('@nextcloud/vue/components/NcIconSvgWrapper', () => ({
83+
default: {
84+
name: 'NcIconSvgWrapper',
85+
props: ['path', 'svg', 'size'],
86+
template: '<i class="nc-icon" :data-path="path" />',
87+
},
88+
}))
89+
90+
vi.mock('@nextcloud/vue/components/NcPopover', () => ({
91+
default: {
92+
name: 'NcPopover',
93+
template: '<div class="nc-popover-stub"><slot name="trigger" /><slot /></div>',
94+
},
95+
}))
96+
97+
vi.mock('../../../views/FilesList/FileListFilter/FileListFilterModified.vue', () => ({
98+
default: {
99+
name: 'FileListFilterModified',
100+
template: '<div class="file-list-filter-modified-stub" />',
101+
},
102+
}))
103+
104+
vi.mock('../../../views/FilesList/FileListFilter/FileListFilterStatus.vue', () => ({
105+
default: {
106+
name: 'FileListFilterStatus',
107+
template: '<div class="file-list-filter-status-stub" />',
108+
},
109+
}))
110+
111+
describe('FileListFilters.vue', () => {
112+
beforeEach(() => {
113+
setActivePinia(createTestingPinia({ createSpy: vi.fn }))
114+
vi.clearAllMocks()
115+
mockIsWide.value = true
116+
})
117+
118+
function mountComponent() {
119+
return mount(FileListFilters)
120+
}
121+
122+
it('has data-test-id="files-list-filters"', () => {
123+
const wrapper = mountComponent()
124+
expect(wrapper.find('[data-test-id="files-list-filters"]').exists()).toBe(true)
125+
})
126+
127+
describe('wide layout (isWide = true)', () => {
128+
beforeEach(() => {
129+
mockIsWide.value = true
130+
})
131+
132+
it('renders FileListFilterModified directly in the header', () => {
133+
const wrapper = mountComponent()
134+
expect(wrapper.find('.file-list-filter-modified-stub').exists()).toBe(true)
135+
})
136+
137+
it('renders FileListFilterStatus directly in the header', () => {
138+
const wrapper = mountComponent()
139+
expect(wrapper.find('.file-list-filter-status-stub').exists()).toBe(true)
140+
})
141+
142+
it('does not render the collapsed filter button', () => {
143+
const wrapper = mountComponent()
144+
expect(wrapper.find('.nc-popover-stub').exists()).toBe(false)
145+
})
146+
})
147+
148+
describe('narrow layout (isWide = false)', () => {
149+
beforeEach(() => {
150+
mockIsWide.value = false
151+
})
152+
153+
it('renders NcPopover as the collapsed filter trigger', () => {
154+
const wrapper = mountComponent()
155+
expect(wrapper.find('.nc-popover-stub').exists()).toBe(true)
156+
})
157+
158+
it('renders the filter icon button inside the popover trigger', () => {
159+
const wrapper = mountComponent()
160+
const icon = wrapper.find('.nc-icon')
161+
expect(icon.exists()).toBe(true)
162+
})
163+
164+
it('the filter button shows mdiFilterVariant icon', () => {
165+
const wrapper = mountComponent()
166+
const icon = wrapper.find('.nc-icon')
167+
expect(icon.attributes('data-path')).toBe(wrapper.vm.mdiFilterVariant)
168+
})
169+
170+
it('renders individual filter components inside the popover content', () => {
171+
const wrapper = mountComponent()
172+
expect(wrapper.find('.file-list-filter-modified-stub').exists()).toBe(true)
173+
expect(wrapper.find('.file-list-filter-status-stub').exists()).toBe(true)
174+
})
175+
176+
it('does not render individual filters outside of the popover', () => {
177+
const wrapper = mountComponent()
178+
// All filter stubs should be inside the popover
179+
const popover = wrapper.find('.nc-popover-stub')
180+
expect(popover.find('.file-list-filter-modified-stub').exists()).toBe(true)
181+
expect(popover.find('.file-list-filter-status-stub').exists()).toBe(true)
182+
})
183+
})
184+
185+
describe('hasActiveFilters (button pressed state)', () => {
186+
beforeEach(() => {
187+
mockIsWide.value = false
188+
})
189+
190+
it('button is not pressed when there are no active chips', () => {
191+
const filtersStore = useFiltersStore()
192+
filtersStore.$patch({ chips: {} })
193+
194+
const wrapper = mountComponent()
195+
const button = wrapper.find('.nc-button-stub')
196+
expect(button.attributes('data-pressed')).toBe('false')
197+
})
198+
199+
it('button is pressed when there are active chips', async () => {
200+
const filtersStore = useFiltersStore()
201+
filtersStore.$patch({ chips: { test: [{ text: 'Modified', icon: '', onclick: () => {} }] } })
202+
203+
const wrapper = mountComponent()
204+
const button = wrapper.find('.nc-button-stub')
205+
expect(button.attributes('data-pressed')).toBe('true')
206+
})
207+
})
208+
})

0 commit comments

Comments
 (0)