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+
9+ import FileStatusList from '../../components/FileStatusList.vue'
10+
11+ const axiosGetMock = vi . fn ( )
12+
13+ vi . mock ( '@nextcloud/l10n' , ( ) => ( {
14+ t : vi . fn ( ( _app : string , text : string ) => text ) ,
15+ n : vi . fn ( ( _app : string , singular : string , plural : string , count : number ) => ( count === 1 ? singular : plural ) ) ,
16+ } ) )
17+
18+ vi . mock ( '@nextcloud/files' , ( ) => ( {
19+ formatFileSize : vi . fn ( ( size : number ) => `${ size } B` ) ,
20+ } ) )
21+
22+ vi . mock ( '@nextcloud/moment' , ( ) => ( {
23+ default : vi . fn ( ( value : string ) => ( {
24+ calendar : vi . fn ( ( ) => `calendar:${ value } ` ) ,
25+ } ) ) ,
26+ } ) )
27+
28+ vi . mock ( '@nextcloud/axios' , ( ) => ( {
29+ default : {
30+ get : vi . fn ( ( ...args : unknown [ ] ) => axiosGetMock ( ...args ) ) ,
31+ } ,
32+ } ) )
33+
34+ vi . mock ( '@nextcloud/router' , ( ) => ( {
35+ generateOcsUrl : vi . fn ( ( path : string ) => `/ocs/v2.php${ path } ` ) ,
36+ } ) )
37+
38+ vi . mock ( '../../utils/fileStatus.js' , ( ) => ( {
39+ getStatusLabel : vi . fn ( ( status : number ) => `status:${ status } ` ) ,
40+ getStatusIcon : vi . fn ( ( status : number ) => `icon:${ status } ` ) ,
41+ } ) )
42+
43+ vi . mock ( '../../constants.js' , ( ) => ( {
44+ FILE_STATUS : {
45+ NOT_LIBRESIGN_FILE : 0 ,
46+ DRAFT : 0 ,
47+ ABLE_TO_SIGN : 1 ,
48+ PARTIAL_SIGNED : 2 ,
49+ SIGNED : 3 ,
50+ DELETED : 4 ,
51+ SIGNING_IN_PROGRESS : 5 ,
52+ } ,
53+ } ) )
54+
55+ vi . mock ( '@nextcloud/vue/components/NcEmptyContent' , ( ) => ( {
56+ default : {
57+ name : 'NcEmptyContent' ,
58+ props : [ 'name' ] ,
59+ template : '<div class="nc-empty-content-stub">{{ name }}</div>' ,
60+ } ,
61+ } ) )
62+
63+ vi . mock ( '@nextcloud/vue/components/NcIconSvgWrapper' , ( ) => ( {
64+ default : {
65+ name : 'NcIconSvgWrapper' ,
66+ template : '<i class="nc-icon-svg-wrapper-stub" />' ,
67+ } ,
68+ } ) )
69+
70+ describe ( 'FileStatusList.vue' , ( ) => {
71+ const createWrapper = ( props : { fileIds ?: number [ ] ; updateInterval ?: number } = { } ) => mount ( FileStatusList , {
72+ props : {
73+ fileIds : [ ] ,
74+ updateInterval : 2000 ,
75+ ...props ,
76+ } ,
77+ } )
78+
79+ beforeEach ( ( ) => {
80+ vi . useRealTimers ( )
81+ axiosGetMock . mockReset ( )
82+ axiosGetMock . mockResolvedValue ( {
83+ data : {
84+ ocs : {
85+ data : {
86+ data : [ ] ,
87+ } ,
88+ } ,
89+ } ,
90+ } )
91+ } )
92+
93+ it ( 'renders the empty state when there are no files to show' , ( ) => {
94+ const wrapper = createWrapper ( )
95+
96+ expect ( wrapper . find ( '.empty-state' ) . exists ( ) ) . toBe ( true )
97+ expect ( wrapper . find ( '.nc-empty-content-stub' ) . text ( ) ) . toBe ( 'No files to sign' )
98+ } )
99+
100+ it ( 'loads the requested files and emits files-updated' , async ( ) => {
101+ axiosGetMock . mockResolvedValueOnce ( {
102+ data : {
103+ ocs : {
104+ data : {
105+ data : [
106+ { id : 1 , uuid : 'a' , name : 'contract-a.pdf' , size : 100 , status : 1 } ,
107+ { id : 2 , uuid : 'b' , name : 'contract-b.pdf' , size : 250 , status : 3 } ,
108+ ] ,
109+ } ,
110+ } ,
111+ } ,
112+ } )
113+
114+ const wrapper = createWrapper ( { fileIds : [ 2 , 1 ] } )
115+ await wrapper . vm . loadFiles ( )
116+
117+ expect ( axiosGetMock ) . toHaveBeenCalledWith ( '/ocs/v2.php/apps/libresign/api/v1/file/list' , { timeout : 10000 } )
118+ expect ( wrapper . vm . files . map ( ( file : { id : number } ) => file . id ) ) . toEqual ( [ 2 , 1 ] )
119+ expect ( wrapper . emitted ( 'files-updated' ) ?. at ( - 1 ) ?. [ 0 ] ) . toEqual ( wrapper . vm . files )
120+ } )
121+
122+ it ( 'emits file-signed only for newly signed files' , async ( ) => {
123+ axiosGetMock . mockResolvedValue ( {
124+ data : {
125+ ocs : {
126+ data : {
127+ data : [
128+ { id : 1 , uuid : 'a' , name : 'contract-a.pdf' , size : 100 , status : 3 } ,
129+ ] ,
130+ } ,
131+ } ,
132+ } ,
133+ } )
134+
135+ const wrapper = createWrapper ( { fileIds : [ 1 ] } )
136+ await wrapper . vm . loadFiles ( )
137+ await wrapper . vm . loadFiles ( )
138+
139+ expect ( wrapper . emitted ( 'file-signed' ) ) . toHaveLength ( 1 )
140+ expect ( wrapper . emitted ( 'file-signed' ) ?. [ 0 ] ?. [ 0 ] ) . toMatchObject ( { id : 1 , status : 3 } )
141+ } )
142+
143+ it ( 'starts and stops polling with the configured interval' , async ( ) => {
144+ vi . useFakeTimers ( )
145+ axiosGetMock . mockResolvedValue ( {
146+ data : {
147+ ocs : {
148+ data : {
149+ data : [ { id : 1 , uuid : 'a' , name : 'contract-a.pdf' , size : 100 , status : 0 } ] ,
150+ } ,
151+ } ,
152+ } ,
153+ } )
154+
155+ const wrapper = createWrapper ( { fileIds : [ 1 ] , updateInterval : 1500 } )
156+ const loadFilesSpy = vi . spyOn ( wrapper . vm , 'loadFiles' )
157+
158+ wrapper . vm . startUpdatePolling ( )
159+ vi . advanceTimersByTime ( 1500 )
160+
161+ expect ( loadFilesSpy ) . toHaveBeenCalled ( )
162+ expect ( wrapper . vm . updatePollingInterval ) . toBeTruthy ( )
163+
164+ wrapper . vm . stopUpdatePolling ( )
165+
166+ expect ( wrapper . vm . updatePollingInterval ) . toBeNull ( )
167+ } )
168+ } )
0 commit comments