@@ -77,19 +77,39 @@ describe('ActionSchema', () => {
7777 } ) ;
7878
7979 describe ( 'Action Types' , ( ) => {
80- it ( 'should accept all action types' , ( ) => {
80+ it ( 'should accept all action types with target ' , ( ) => {
8181 const types = [ 'script' , 'url' , 'modal' , 'flow' , 'api' ] as const ;
8282
8383 types . forEach ( type => {
8484 const action : ActionType = {
8585 name : 'test_action' ,
8686 label : 'Test' ,
8787 type,
88+ target : 'test_handler' ,
8889 } ;
8990 expect ( ( ) => ActionSchema . parse ( action ) ) . not . toThrow ( ) ;
9091 } ) ;
9192 } ) ;
9293
94+ it ( 'should accept script type without target' , ( ) => {
95+ expect ( ( ) => ActionSchema . parse ( {
96+ name : 'test_action' ,
97+ label : 'Test' ,
98+ type : 'script' ,
99+ } ) ) . not . toThrow ( ) ;
100+ } ) ;
101+
102+ it ( 'should reject url/flow/modal/api types without target' , ( ) => {
103+ const targetRequiredTypes = [ 'url' , 'flow' , 'modal' , 'api' ] as const ;
104+ targetRequiredTypes . forEach ( type => {
105+ expect ( ( ) => ActionSchema . parse ( {
106+ name : 'test_action' ,
107+ label : 'Test' ,
108+ type,
109+ } ) ) . toThrow ( / t a r g e t / ) ;
110+ } ) ;
111+ } ) ;
112+
93113 it ( 'should default to script type' , ( ) => {
94114 const action = {
95115 name : 'custom_action' ,
@@ -294,6 +314,7 @@ describe('ActionSchema', () => {
294314 label : 'Transfer Case' ,
295315 icon : 'arrow-right' ,
296316 type : 'modal' ,
317+ target : 'transfer_case_modal' ,
297318 locations : [ 'record_more' ] ,
298319 params : [
299320 {
@@ -561,3 +582,89 @@ describe('ActionSchema - variant', () => {
561582 expect ( result . confirmText ) . toBe ( 'Are you sure?' ) ;
562583 } ) ;
563584} ) ;
585+
586+ // ============================================================================
587+ // Protocol Improvement Tests: execute → target migration & target validation
588+ // ============================================================================
589+
590+ describe ( 'ActionSchema - execute → target migration' , ( ) => {
591+ it ( 'should auto-migrate execute to target when target is not set' , ( ) => {
592+ const result = ActionSchema . parse ( {
593+ name : 'legacy_action' ,
594+ label : 'Legacy' ,
595+ type : 'script' ,
596+ execute : 'legacyHandler' ,
597+ } ) ;
598+ expect ( result . target ) . toBe ( 'legacyHandler' ) ;
599+ } ) ;
600+
601+ it ( 'should preserve target over execute when both are set' , ( ) => {
602+ const result = ActionSchema . parse ( {
603+ name : 'both_fields' ,
604+ label : 'Both' ,
605+ type : 'script' ,
606+ target : 'preferredHandler' ,
607+ execute : 'legacyHandler' ,
608+ } ) ;
609+ expect ( result . target ) . toBe ( 'preferredHandler' ) ;
610+ } ) ;
611+
612+ it ( 'should allow script type without target or execute' , ( ) => {
613+ expect ( ( ) => ActionSchema . parse ( {
614+ name : 'inline_script' ,
615+ label : 'Inline' ,
616+ type : 'script' ,
617+ } ) ) . not . toThrow ( ) ;
618+ } ) ;
619+ } ) ;
620+
621+ describe ( 'ActionSchema - target required for non-script types' , ( ) => {
622+ it ( 'should require target for url type' , ( ) => {
623+ expect ( ( ) => ActionSchema . parse ( {
624+ name : 'url_action' ,
625+ label : 'Open URL' ,
626+ type : 'url' ,
627+ } ) ) . toThrow ( / t a r g e t / ) ;
628+ } ) ;
629+
630+ it ( 'should require target for flow type' , ( ) => {
631+ expect ( ( ) => ActionSchema . parse ( {
632+ name : 'flow_action' ,
633+ label : 'Run Flow' ,
634+ type : 'flow' ,
635+ } ) ) . toThrow ( / t a r g e t / ) ;
636+ } ) ;
637+
638+ it ( 'should require target for modal type' , ( ) => {
639+ expect ( ( ) => ActionSchema . parse ( {
640+ name : 'modal_action' ,
641+ label : 'Open Modal' ,
642+ type : 'modal' ,
643+ } ) ) . toThrow ( / t a r g e t / ) ;
644+ } ) ;
645+
646+ it ( 'should require target for api type' , ( ) => {
647+ expect ( ( ) => ActionSchema . parse ( {
648+ name : 'api_action' ,
649+ label : 'Call API' ,
650+ type : 'api' ,
651+ } ) ) . toThrow ( / t a r g e t / ) ;
652+ } ) ;
653+
654+ it ( 'should accept non-script types when target is provided' , ( ) => {
655+ expect ( ( ) => ActionSchema . parse ( { name : 'url_ok' , label : 'URL' , type : 'url' , target : 'https://example.com' } ) ) . not . toThrow ( ) ;
656+ expect ( ( ) => ActionSchema . parse ( { name : 'flow_ok' , label : 'Flow' , type : 'flow' , target : 'my_flow' } ) ) . not . toThrow ( ) ;
657+ expect ( ( ) => ActionSchema . parse ( { name : 'modal_ok' , label : 'Modal' , type : 'modal' , target : 'my_modal' } ) ) . not . toThrow ( ) ;
658+ expect ( ( ) => ActionSchema . parse ( { name : 'api_ok' , label : 'API' , type : 'api' , target : '/api/endpoint' } ) ) . not . toThrow ( ) ;
659+ } ) ;
660+
661+ it ( 'should accept non-script types when execute is provided (auto-migrated)' , ( ) => {
662+ const result = ActionSchema . parse ( {
663+ name : 'flow_legacy' ,
664+ label : 'Flow Legacy' ,
665+ type : 'flow' ,
666+ execute : 'my_flow' ,
667+ } ) ;
668+ expect ( result . target ) . toBe ( 'my_flow' ) ;
669+ } ) ;
670+ } ) ;
0 commit comments