@@ -49,6 +49,7 @@ import {
4949} from '@/ee/workspace-forking/components/fork-sync/dependent-value'
5050import {
5151 type ForkSyncController ,
52+ shouldReconfigureEntry ,
5253 useForkSync ,
5354} from '@/ee/workspace-forking/components/fork-sync/use-fork-sync'
5455
@@ -556,3 +557,71 @@ describe('useForkSync post-sync reset', () => {
556557 expect ( get ( ) . dirty ) . toBe ( true )
557558 } )
558559} )
560+
561+ describe ( 'shouldReconfigureEntry' , ( ) => {
562+ const entry = ( overrides : Partial < ForkMappingEntry > = { } ) : ForkMappingEntry =>
563+ ( {
564+ kind : 'credential' ,
565+ sourceId : 'cred-src' ,
566+ targetId : 'cred-tgt' ,
567+ suggested : false ,
568+ ...overrides ,
569+ } ) as ForkMappingEntry
570+
571+ it ( 'is false for a settled non-custom-block mapping' , ( ) => {
572+ // An unchanged credential mapping leaves its stored dependent picks valid, so its fields
573+ // stay out of the way until something actually changes.
574+ expect ( shouldReconfigureEntry ( entry ( ) , { } ) ) . toBe ( false )
575+ } )
576+
577+ it ( 'is true for a custom block mapped to a DIFFERENT block, even once saved' , ( ) => {
578+ // The regression: `parentChanged` drove the reconfigure UI off "was this edited in this
579+ // session", so a saved mapping read as settled and the modal showed "no changes required"
580+ // with no inputs. A custom block pointed elsewhere has sub-blocks keyed by the SOURCE
581+ // block's field ids — they describe nothing on the target and nothing carries over — so it
582+ // needs configuring for as long as the mapping stands, not just the session it was made in.
583+ const saved = entry ( {
584+ kind : 'custom-block' ,
585+ sourceId : 'custom_block_prod01' ,
586+ targetId : 'custom_block_uat0001' ,
587+ } )
588+
589+ expect ( shouldReconfigureEntry ( saved , { } ) ) . toBe ( true )
590+ } )
591+
592+ it ( 'is false for a custom block mapped to itself' , ( ) => {
593+ // "Keep the same block across environments": the type never changes, so the source's own
594+ // field ids still describe it and its values carry across untouched.
595+ const identity = entry ( {
596+ kind : 'custom-block' ,
597+ sourceId : 'custom_block_prod01' ,
598+ targetId : 'custom_block_prod01' ,
599+ } )
600+
601+ expect ( shouldReconfigureEntry ( identity , { } ) ) . toBe ( false )
602+ } )
603+
604+ it ( 'follows an in-session custom-block re-pick rather than the saved target' , ( ) => {
605+ const saved = entry ( {
606+ kind : 'custom-block' ,
607+ sourceId : 'custom_block_prod01' ,
608+ targetId : 'custom_block_uat0001' ,
609+ } )
610+ const key = `${ saved . kind } :${ saved . sourceId } `
611+
612+ // Re-pointed back at itself in-session: nothing to configure.
613+ expect ( shouldReconfigureEntry ( saved , { [ key ] : 'custom_block_prod01' } ) ) . toBe ( false )
614+ // Re-pointed at a third block: configure against that one.
615+ expect ( shouldReconfigureEntry ( saved , { [ key ] : 'custom_block_sbx0001' } ) ) . toBe ( true )
616+ } )
617+
618+ it ( 'is false for an unmapped custom block, which the promote blocks instead' , ( ) => {
619+ const unmapped = entry ( {
620+ kind : 'custom-block' ,
621+ sourceId : 'custom_block_prod01' ,
622+ targetId : null ,
623+ } )
624+
625+ expect ( shouldReconfigureEntry ( unmapped , { } ) ) . toBe ( false )
626+ } )
627+ } )
0 commit comments