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 { markRaw } from 'vue'
9+
10+ import VirtualList from '../../../views/FilesList/VirtualList.vue'
11+
12+ const filesStoreMock = {
13+ loading : false ,
14+ getAllFiles : vi . fn ( ) ,
15+ filesSorted : vi . fn ( ( ) => [ ] as Array < { id : number , name : string } > ) ,
16+ }
17+
18+ const userConfigStoreMock = {
19+ files_list_grid_view : false ,
20+ }
21+
22+ const subscribeMock = vi . fn ( )
23+ const unsubscribeMock = vi . fn ( )
24+
25+ class IntersectionObserverMock {
26+ observe = vi . fn ( )
27+ disconnect = vi . fn ( )
28+
29+ constructor ( public callback : IntersectionObserverCallback ) { }
30+ }
31+
32+ vi . mock ( '@nextcloud/l10n' , ( ) => ( {
33+ t : vi . fn ( ( _app : string , text : string ) => text ) ,
34+ translate : vi . fn ( ( _app : string , text : string ) => text ) ,
35+ translatePlural : vi . fn ( ( _app : string , singular : string , plural : string , count : number ) => ( count === 1 ? singular : plural ) ) ,
36+ n : vi . fn ( ( _app : string , singular : string , plural : string , count : number ) => ( count === 1 ? singular : plural ) ) ,
37+ getLanguage : vi . fn ( ( ) => 'en' ) ,
38+ getLocale : vi . fn ( ( ) => 'en' ) ,
39+ isRTL : vi . fn ( ( ) => false ) ,
40+ } ) )
41+
42+ vi . mock ( 'debounce' , ( ) => ( {
43+ default : vi . fn ( ( fn : ( ...args : unknown [ ] ) => unknown ) => fn ) ,
44+ } ) )
45+
46+ vi . mock ( '@nextcloud/event-bus' , ( ) => ( {
47+ subscribe : vi . fn ( ( ...args : unknown [ ] ) => subscribeMock ( ...args ) ) ,
48+ unsubscribe : vi . fn ( ( ...args : unknown [ ] ) => unsubscribeMock ( ...args ) ) ,
49+ } ) )
50+
51+ vi . mock ( '../../../store/files.js' , ( ) => ( {
52+ useFilesStore : vi . fn ( ( ) => filesStoreMock ) ,
53+ } ) )
54+
55+ vi . mock ( '../../../store/userconfig.js' , ( ) => ( {
56+ useUserConfigStore : vi . fn ( ( ) => userConfigStoreMock ) ,
57+ } ) )
58+
59+ describe ( 'VirtualList.vue' , ( ) => {
60+ beforeEach ( ( ) => {
61+ vi . clearAllMocks ( )
62+ filesStoreMock . loading = false
63+ filesStoreMock . getAllFiles . mockReset ( )
64+ filesStoreMock . filesSorted . mockReturnValue ( [ ] )
65+ userConfigStoreMock . files_list_grid_view = false
66+ globalThis . IntersectionObserver = IntersectionObserverMock as unknown as typeof IntersectionObserver
67+ } )
68+
69+ function createWrapper ( ) {
70+ return mount ( VirtualList , {
71+ props : {
72+ dataComponent : markRaw ( {
73+ name : 'RowComponent' ,
74+ props : [ 'source' , 'loading' ] ,
75+ template : '<tr class="row-component"><td>{{ source.name }}</td></tr>' ,
76+ } ) ,
77+ loading : false ,
78+ caption : 'Files table' ,
79+ } ,
80+ global : {
81+ stubs : {
82+ transition : false ,
83+ } ,
84+ } ,
85+ slots : {
86+ empty : '<div class="empty-slot">Nothing here</div>' ,
87+ header : '<tr class="header-slot"><th>Name</th></tr>' ,
88+ footer : '<tr class="footer-slot"><td>Footer</td></tr>' ,
89+ } ,
90+ } )
91+ }
92+
93+ it ( 'renders the empty slot when there are no files' , ( ) => {
94+ const wrapper = createWrapper ( )
95+
96+ expect ( wrapper . find ( '.files-list__empty' ) . exists ( ) ) . toBe ( true )
97+ expect ( wrapper . find ( '.empty-slot' ) . text ( ) ) . toBe ( 'Nothing here' )
98+ expect ( wrapper . find ( '.files-list__table' ) . classes ( ) ) . toContain ( 'files-list__table--hidden' )
99+ } )
100+
101+ it ( 'renders rows and grid body class when files exist in grid mode' , ( ) => {
102+ filesStoreMock . filesSorted . mockReturnValue ( [
103+ { id : 1 , name : 'first.pdf' } ,
104+ { id : 2 , name : 'second.pdf' } ,
105+ ] )
106+ userConfigStoreMock . files_list_grid_view = true
107+ const wrapper = createWrapper ( )
108+
109+ expect ( wrapper . findAll ( '.row-component' ) ) . toHaveLength ( 2 )
110+ expect ( wrapper . find ( '.files-list' ) . classes ( ) ) . toContain ( 'files-list--grid' )
111+ expect ( wrapper . find ( '.files-list__tbody' ) . classes ( ) ) . toContain ( 'files-list__tbody--grid' )
112+ } )
113+
114+ it ( 'loads more files immediately when not currently loading' , ( ) => {
115+ const wrapper = createWrapper ( )
116+
117+ wrapper . vm . getFilesIfNotLoading ( )
118+
119+ expect ( filesStoreMock . getAllFiles ) . toHaveBeenCalledTimes ( 1 )
120+ } )
121+ } )
0 commit comments