1+ /*
2+ * SPDX-FileCopyrightText: 2026 LibreSign contributors
3+ * SPDX-License-Identifier: AGPL-3.0-or-later
4+ */
5+
6+ import { beforeEach , describe , expect , it , vi } from 'vitest'
7+ import { flushPromises , mount } from '@vue/test-utils'
8+
9+ import Validation from '../../../views/Settings/Validation.vue'
10+
11+ const axiosGetMock = vi . fn ( )
12+ const loadStateMock = vi . fn ( )
13+
14+ vi . mock ( '@nextcloud/axios' , ( ) => ( {
15+ default : {
16+ get : ( ...args : unknown [ ] ) => axiosGetMock ( ...args ) ,
17+ } ,
18+ } ) )
19+
20+ vi . mock ( '@nextcloud/initial-state' , ( ) => ( {
21+ loadState : ( ...args : unknown [ ] ) => loadStateMock ( ...args ) ,
22+ } ) )
23+
24+ vi . mock ( '@nextcloud/router' , ( ) => ( {
25+ generateOcsUrl : vi . fn ( ( path : string ) => path ) ,
26+ } ) )
27+
28+ vi . mock ( '@nextcloud/l10n' , ( ) => ( {
29+ t : vi . fn ( ( _app : string , text : string ) => text ) ,
30+ translate : vi . fn ( ( _app : string , text : string ) => text ) ,
31+ translatePlural : vi . fn ( ( _app : string , singular : string , plural : string , count : number ) => ( count === 1 ? singular : plural ) ) ,
32+ n : vi . fn ( ( _app : string , singular : string , plural : string , count : number ) => ( count === 1 ? singular : plural ) ) ,
33+ getLanguage : vi . fn ( ( ) => 'en' ) ,
34+ getLocale : vi . fn ( ( ) => 'en' ) ,
35+ isRTL : vi . fn ( ( ) => false ) ,
36+ } ) )
37+
38+ const appConfigSetValueMock = vi . fn ( )
39+
40+ const FooterTemplateEditorStub = {
41+ name : 'FooterTemplateEditor' ,
42+ props : {
43+ initialIsDefault : {
44+ type : Boolean ,
45+ default : true ,
46+ } ,
47+ } ,
48+ template : '<div class="footer-template-editor-stub" />' ,
49+ methods : {
50+ resetFooterTemplate ( ) { } ,
51+ } ,
52+ }
53+
54+ describe ( 'Settings/Validation.vue' , ( ) => {
55+ beforeEach ( ( ) => {
56+ vi . clearAllMocks ( )
57+ loadStateMock . mockImplementation ( ( _app : string , _key : string , fallback : unknown ) => fallback )
58+ vi . stubGlobal ( 'OCP' , {
59+ AppConfig : {
60+ setValue : appConfigSetValueMock ,
61+ } ,
62+ } )
63+ } )
64+
65+ function createWrapper ( ) {
66+ return mount ( Validation , {
67+ global : {
68+ stubs : {
69+ NcSettingsSection : { template : '<section><slot /></section>' } ,
70+ NcCheckboxRadioSwitch : { template : '<div><slot /></div>' } ,
71+ FooterTemplateEditor : FooterTemplateEditorStub ,
72+ } ,
73+ } ,
74+ } )
75+ }
76+
77+ it ( 'loads validation settings on mount' , async ( ) => {
78+ axiosGetMock
79+ . mockResolvedValueOnce ( { data : { ocs : { data : { data : '1' } } } } )
80+ . mockResolvedValueOnce ( { data : { ocs : { data : { data : '0' } } } } )
81+ . mockResolvedValueOnce ( { data : { ocs : { data : { data : '1' } } } } )
82+ . mockResolvedValueOnce ( { data : { ocs : { data : { data : 'https://example.test/validation/' } } } } )
83+ . mockResolvedValueOnce ( { data : { ocs : { data : { data : '0' } } } } )
84+
85+ const wrapper = createWrapper ( )
86+ await flushPromises ( )
87+
88+ expect ( axiosGetMock ) . toHaveBeenCalledTimes ( 5 )
89+ expect ( wrapper . vm . makeValidationUrlPrivate ) . toBe ( true )
90+ expect ( wrapper . vm . addFooter ) . toBe ( false )
91+ expect ( wrapper . vm . writeQrcodeOnFooter ) . toBe ( true )
92+ expect ( wrapper . vm . url ) . toBe ( 'https://example.test/validation/' )
93+ expect ( wrapper . vm . isDefaultFooterTemplate ) . toBe ( false )
94+ expect ( wrapper . vm . customizeFooter ) . toBe ( true )
95+ } )
96+
97+ it ( 'falls back to the default validation URL placeholder' , async ( ) => {
98+ axiosGetMock . mockResolvedValue ( { data : { ocs : { data : { data : '' } } } } )
99+
100+ const wrapper = createWrapper ( )
101+ await flushPromises ( )
102+
103+ expect ( wrapper . vm . url ) . toBe ( wrapper . vm . paternValidadeUrl )
104+ } )
105+
106+ it ( 'trims and saves the typed validation URL' , async ( ) => {
107+ axiosGetMock
108+ . mockResolvedValueOnce ( { data : { ocs : { data : { data : '1' } } } } )
109+ . mockResolvedValueOnce ( { data : { ocs : { data : { data : '1' } } } } )
110+ . mockResolvedValueOnce ( { data : { ocs : { data : { data : '1' } } } } )
111+ . mockResolvedValueOnce ( { data : { ocs : { data : { data : 'https://example.test/' } } } } )
112+ . mockResolvedValueOnce ( { data : { ocs : { data : { data : '1' } } } } )
113+
114+ const wrapper = createWrapper ( )
115+ await flushPromises ( )
116+
117+ const input = wrapper . get ( '#validation_site' )
118+ ; ( input . element as HTMLInputElement ) . value = ' https://custom.test/validation '
119+ await input . trigger ( 'input' )
120+
121+ expect ( appConfigSetValueMock ) . toHaveBeenCalledWith ( 'libresign' , 'validation_site' , 'https://custom.test/validation' )
122+ } )
123+
124+ it ( 'resets the footer template when customization is disabled' , async ( ) => {
125+ axiosGetMock . mockResolvedValue ( { data : { ocs : { data : { data : '1' } } } } )
126+
127+ const wrapper = createWrapper ( )
128+ await flushPromises ( )
129+ wrapper . vm . addFooter = true
130+ wrapper . vm . customizeFooter = true
131+ await wrapper . vm . $nextTick ( )
132+
133+ const footerEditor = wrapper . findComponent ( FooterTemplateEditorStub )
134+ const resetFooterTemplateMock = vi . spyOn ( footerEditor . vm , 'resetFooterTemplate' )
135+
136+ await wrapper . vm . onCustomizeFooterChange ( false )
137+
138+ expect ( appConfigSetValueMock ) . toHaveBeenCalledWith ( 'libresign' , 'footer_template_is_default' , '1' )
139+ expect ( wrapper . vm . isDefaultFooterTemplate ) . toBe ( true )
140+ expect ( resetFooterTemplateMock ) . toHaveBeenCalledTimes ( 1 )
141+ } )
142+
143+ it ( 'persists the private validation URL toggle through the shared setter' , async ( ) => {
144+ axiosGetMock . mockResolvedValue ( { data : { ocs : { data : { data : '1' } } } } )
145+
146+ const wrapper = createWrapper ( )
147+ await flushPromises ( )
148+
149+ await wrapper . vm . onMakeValidationUrlPrivateChange ( true )
150+
151+ expect ( appConfigSetValueMock ) . toHaveBeenCalledWith ( 'libresign' , 'make_validation_url_private' , '1' )
152+ } )
153+ } )
0 commit comments