@@ -149,6 +149,7 @@ describe('Preferences view', () => {
149149
150150 it ( 'renders signing order section without verbose summary labels' , async ( ) => {
151151 const wrapper = await createWrapper ( )
152+ await nextTick ( )
152153
153154 expect ( wrapper . text ( ) ) . toContain ( 'Signing order' )
154155 expect ( wrapper . text ( ) ) . not . toContain ( 'Effective value' )
@@ -337,6 +338,7 @@ describe('Preferences view', () => {
337338 } )
338339
339340 const wrapper = await createWrapper ( )
341+ await nextTick ( )
340342
341343 expect ( wrapper . text ( ) ) . toContain ( 'Signature footer' )
342344 expect ( wrapper . findComponent ( { name : 'SignatureFooterRuleEditor' } ) . exists ( ) ) . toBe ( true )
@@ -374,7 +376,7 @@ describe('Preferences view', () => {
374376
375377 expect ( saveUserPreferenceMock ) . toHaveBeenCalledWith (
376378 'add_footer' ,
377- '{"enabled":true,"writeQrcodeOnFooter":true,"validationSite":"","customizeFooterTemplate":true,"footerTemplate":"Changed template"}' ,
379+ '{"enabled":true,"writeQrcodeOnFooter":true,"validationSite":"","customizeFooterTemplate":true,"footerTemplate":"Changed template","previewWidth":595,"previewHeight":100,"previewZoom":100 }' ,
378380 )
379381 } )
380382
@@ -388,6 +390,75 @@ describe('Preferences view', () => {
388390 expect ( saveUserPreferenceMock ) . toHaveBeenCalledWith ( 'signature_flow' , 'ordered_numeric' )
389391 } )
390392
393+ it ( 'ignores no-op preference updates emitted during editor hydration' , async ( ) => {
394+ const wrapper = await createWrapper ( )
395+ await nextTick ( )
396+
397+ saveUserPreferenceMock . mockClear ( )
398+
399+ await wrapper . vm . onPreferenceChange ( 'signature_flow' , 'parallel' )
400+
401+ expect ( saveUserPreferenceMock ) . not . toHaveBeenCalled ( )
402+ } )
403+
404+ it ( 'does not autosave preference updates before initialization finishes' , async ( ) => {
405+ let resolveFetchEffectivePolicies : ( ( ) => void ) | null = null
406+ fetchEffectivePoliciesMock . mockImplementation ( ( ) => new Promise < void > ( ( resolve ) => {
407+ resolveFetchEffectivePolicies = resolve
408+ } ) )
409+
410+ const wrapper = await createWrapper ( )
411+
412+ expect ( wrapper . vm . preferencesReady ) . toBe ( false )
413+
414+ await wrapper . vm . onPreferenceChange ( 'signature_flow' , 'ordered_numeric' )
415+
416+ expect ( saveUserPreferenceMock ) . not . toHaveBeenCalled ( )
417+
418+ resolveFetchEffectivePolicies ?.( )
419+ await nextTick ( )
420+ await Promise . resolve ( )
421+
422+ expect ( wrapper . vm . preferencesReady ) . toBe ( true )
423+
424+ await wrapper . vm . onPreferenceChange ( 'signature_flow' , 'parallel' )
425+ await wrapper . vm . onPreferenceChange ( 'signature_flow' , 'ordered_numeric' )
426+
427+ expect ( saveUserPreferenceMock ) . toHaveBeenCalledWith ( 'signature_flow' , 'ordered_numeric' )
428+ } )
429+
430+ it ( 'ignores footer updates that only normalize to the current effective value' , async ( ) => {
431+ getPolicyMock . mockImplementation ( ( key : string ) => {
432+ if ( key === 'add_footer' ) {
433+ return {
434+ policyKey : 'add_footer' ,
435+ effectiveValue : '{"enabled":true,"writeQrcodeOnFooter":true,"validationSite":"","customizeFooterTemplate":true,"footerTemplate":"Inherited footer template"}' ,
436+ sourceScope : 'group' ,
437+ visible : true ,
438+ editableByCurrentActor : true ,
439+ allowedValues : [ ] ,
440+ blockedBy : null ,
441+ canSaveAsUserDefault : true ,
442+ canUseAsRequestOverride : true ,
443+ preferenceWasCleared : false ,
444+ groupCount : 0 ,
445+ userCount : 0 ,
446+ }
447+ }
448+
449+ return null
450+ } )
451+
452+ const wrapper = await createWrapper ( )
453+ await nextTick ( )
454+
455+ saveUserPreferenceMock . mockClear ( )
456+
457+ await wrapper . vm . onPreferenceChange ( 'add_footer' , '{"enabled":true,"writeQrcodeOnFooter":true,"validationSite":"","customizeFooterTemplate":true,"footerTemplate":"Inherited footer template","previewWidth":595,"previewHeight":100,"previewZoom":100}' )
458+
459+ expect ( saveUserPreferenceMock ) . not . toHaveBeenCalled ( )
460+ } )
461+
391462 it ( 'does not expose reset action after footer autosave when no persisted user default exists yet' , async ( ) => {
392463 getPolicyMock . mockImplementation ( ( key : string ) => {
393464 if ( key === 'add_footer' ) {
@@ -419,7 +490,7 @@ describe('Preferences view', () => {
419490
420491 expect ( saveUserPreferenceMock ) . toHaveBeenCalledWith (
421492 'add_footer' ,
422- '{"enabled":true,"writeQrcodeOnFooter":true,"validationSite":"","customizeFooterTemplate":true,"footerTemplate":"Changed template"}' ,
493+ '{"enabled":true,"writeQrcodeOnFooter":true,"validationSite":"","customizeFooterTemplate":true,"footerTemplate":"Changed template","previewWidth":595,"previewHeight":100,"previewZoom":100 }' ,
423494 )
424495 expect ( wrapper . vm . isAutoSaveSavedFor ( 'add_footer' ) ) . toBe ( true )
425496 expect ( wrapper . vm . canUndoAutoSaveFor ( 'add_footer' ) ) . toBe ( false )
@@ -470,6 +541,7 @@ describe('Preferences view', () => {
470541 } )
471542
472543 const wrapper = await createWrapper ( )
544+ await nextTick ( )
473545
474546 expect ( wrapper . text ( ) ) . toContain ( 'Signature footer' )
475547 expect ( wrapper . findComponent ( { name : 'SignatureFooterRuleEditor' } ) . exists ( ) ) . toBe ( false )
0 commit comments