@@ -77,6 +77,64 @@ describe('ui_extension', async () => {
7777 } )
7878 }
7979
80+ async function setupToolsExtension (
81+ tmpDir : string ,
82+ options : { tools ?: string ; instructions ?: string ; createFiles ?: boolean } = { } ,
83+ ) {
84+ await mkdir ( joinPath ( tmpDir , 'src' ) )
85+ await touchFile ( joinPath ( tmpDir , 'src' , 'ExtensionPointA.js' ) )
86+
87+ if ( options . createFiles ) {
88+ if ( options . tools ) {
89+ await writeFile ( joinPath ( tmpDir , options . tools ) , '{"tools": []}' )
90+ }
91+ if ( options . instructions ) {
92+ await writeFile ( joinPath ( tmpDir , options . instructions ) , '# Instructions' )
93+ }
94+ }
95+
96+ const allSpecs = await loadLocalExtensionsSpecifications ( )
97+ const specification = allSpecs . find ( ( spec ) => spec . identifier === 'ui_extension' ) !
98+
99+ const targetConfig : any = {
100+ target : 'EXTENSION::POINT::A' ,
101+ module : './src/ExtensionPointA.js' ,
102+ }
103+
104+ if ( options . tools ) targetConfig . tools = options . tools
105+ if ( options . instructions ) targetConfig . instructions = options . instructions
106+
107+ const configuration = {
108+ targeting : [ targetConfig ] ,
109+ api_version : '2023-01' as const ,
110+ name : 'UI Extension' ,
111+ description : 'This is an ordinary test extension' ,
112+ type : 'ui_extension' ,
113+ handle : 'test-ui-extension' ,
114+ capabilities : {
115+ block_progress : false ,
116+ network_access : false ,
117+ api_access : false ,
118+ collect_buyer_consent : {
119+ customer_privacy : true ,
120+ sms_marketing : false ,
121+ } ,
122+ iframe : {
123+ sources : [ ] ,
124+ } ,
125+ } ,
126+ settings : { } ,
127+ }
128+
129+ const parsed = specification . parseConfigurationObject ( configuration )
130+ if ( parsed . state !== 'ok' ) {
131+ throw new Error ( "Couldn't parse configuration" )
132+ }
133+
134+ const result = await specification . validate ?.( parsed . data , joinPath ( tmpDir , 'shopify.extension.toml' ) , tmpDir )
135+ return { result, tmpDir}
136+ }
137+
80138 describe ( 'validate()' , ( ) => {
81139 test ( 'returns ok({}) if there are no errors' , async ( ) => {
82140 await inTemporaryDirectory ( async ( tmpDir ) => {
@@ -487,6 +545,138 @@ describe('ui_extension', async () => {
487545 ] )
488546 } )
489547
548+ test ( 'build_manifest includes tools asset when tools is present' , async ( ) => {
549+ const allSpecs = await loadLocalExtensionsSpecifications ( )
550+ const specification = allSpecs . find ( ( spec ) => spec . identifier === 'ui_extension' ) !
551+ const configuration = {
552+ targeting : [
553+ {
554+ target : 'EXTENSION::POINT::A' ,
555+ module : './src/ExtensionPointA.js' ,
556+ tools : './tools.json' ,
557+ } ,
558+ ] ,
559+ api_version : '2023-01' as const ,
560+ name : 'UI Extension' ,
561+ description : 'This is an ordinary test extension' ,
562+ type : 'ui_extension' ,
563+ handle : 'test-ui-extension' ,
564+ capabilities : {
565+ block_progress : false ,
566+ network_access : false ,
567+ api_access : false ,
568+ collect_buyer_consent : {
569+ customer_privacy : true ,
570+ sms_marketing : false ,
571+ } ,
572+ iframe : {
573+ sources : [ ] ,
574+ } ,
575+ } ,
576+ settings : { } ,
577+ }
578+
579+ // When
580+ const parsed = specification . parseConfigurationObject ( configuration )
581+ if ( parsed . state !== 'ok' ) {
582+ throw new Error ( "Couldn't parse configuration" )
583+ }
584+
585+ const got = parsed . data
586+
587+ // Then
588+ expect ( got . extension_points ) . toStrictEqual ( [
589+ {
590+ target : 'EXTENSION::POINT::A' ,
591+ module : './src/ExtensionPointA.js' ,
592+ metafields : [ ] ,
593+ default_placement_reference : undefined ,
594+ capabilities : undefined ,
595+ preloads : { } ,
596+ build_manifest : {
597+ assets : {
598+ main : {
599+ filepath : 'test-ui-extension.js' ,
600+ module : './src/ExtensionPointA.js' ,
601+ } ,
602+ tools : {
603+ filepath : 'test-ui-extension-tools-tools.json' ,
604+ module : './tools.json' ,
605+ static : true ,
606+ } ,
607+ } ,
608+ } ,
609+ urls : { } ,
610+ } ,
611+ ] )
612+ } )
613+
614+ test ( 'build_manifest includes instructions asset when instructions is present' , async ( ) => {
615+ const allSpecs = await loadLocalExtensionsSpecifications ( )
616+ const specification = allSpecs . find ( ( spec ) => spec . identifier === 'ui_extension' ) !
617+ const configuration = {
618+ targeting : [
619+ {
620+ target : 'EXTENSION::POINT::A' ,
621+ module : './src/ExtensionPointA.js' ,
622+ instructions : './instructions.md' ,
623+ } ,
624+ ] ,
625+ api_version : '2023-01' as const ,
626+ name : 'UI Extension' ,
627+ description : 'This is an ordinary test extension' ,
628+ type : 'ui_extension' ,
629+ handle : 'test-ui-extension' ,
630+ capabilities : {
631+ block_progress : false ,
632+ network_access : false ,
633+ api_access : false ,
634+ collect_buyer_consent : {
635+ customer_privacy : true ,
636+ sms_marketing : false ,
637+ } ,
638+ iframe : {
639+ sources : [ ] ,
640+ } ,
641+ } ,
642+ settings : { } ,
643+ }
644+
645+ // When
646+ const parsed = specification . parseConfigurationObject ( configuration )
647+ if ( parsed . state !== 'ok' ) {
648+ throw new Error ( "Couldn't parse configuration" )
649+ }
650+
651+ const got = parsed . data
652+
653+ // Then
654+ expect ( got . extension_points ) . toStrictEqual ( [
655+ {
656+ target : 'EXTENSION::POINT::A' ,
657+ module : './src/ExtensionPointA.js' ,
658+ metafields : [ ] ,
659+ default_placement_reference : undefined ,
660+ capabilities : undefined ,
661+ preloads : { } ,
662+ build_manifest : {
663+ assets : {
664+ main : {
665+ filepath : 'test-ui-extension.js' ,
666+ module : './src/ExtensionPointA.js' ,
667+ } ,
668+ instructions : {
669+ filepath : 'test-ui-extension-instructions-instructions.md' ,
670+ module : './instructions.md' ,
671+ static : true ,
672+ } ,
673+ } ,
674+ } ,
675+ urls : { } ,
676+ } ,
677+ ] )
678+ } )
679+
490680 test ( 'returns error if there is no targeting or extension_points' , async ( ) => {
491681 // Given
492682 const allSpecs = await loadLocalExtensionsSpecifications ( )
@@ -545,7 +735,7 @@ describe('ui_extension', async () => {
545735
546736 expect ( result ) . toEqual (
547737 err ( `Couldn't find ${ notFoundPath }
548- Please check the module path for EXTENSION::POINT::A
738+ Please check the module path for EXTENSION::POINT::A
549739
550740Please check the configuration in ${ uiExtension . configurationPath } ` ) ,
551741 )
@@ -584,6 +774,139 @@ Please check the configuration in ${uiExtension.configurationPath}`),
584774 )
585775 } )
586776 } )
777+
778+ test ( 'shows an error when the tools file is missing' , async ( ) => {
779+ await inTemporaryDirectory ( async ( tmpDir ) => {
780+ const { result} = await setupToolsExtension ( tmpDir , { tools : './tools.json' } )
781+
782+ const notFoundPath = joinPath ( tmpDir , './tools.json' )
783+ expect ( result ) . toEqual (
784+ err ( `Couldn't find ${ notFoundPath }
785+ Please check the tools path for EXTENSION::POINT::A
786+
787+ Please check the configuration in ${ joinPath ( tmpDir , 'shopify.extension.toml' ) } ` ) ,
788+ )
789+ } )
790+ } )
791+
792+ test ( 'shows an error when the instructions file is missing' , async ( ) => {
793+ await inTemporaryDirectory ( async ( tmpDir ) => {
794+ const { result} = await setupToolsExtension ( tmpDir , { instructions : './instructions.md' } )
795+
796+ const notFoundPath = joinPath ( tmpDir , './instructions.md' )
797+ expect ( result ) . toEqual (
798+ err ( `Couldn't find ${ notFoundPath }
799+ Please check the instructions path for EXTENSION::POINT::A
800+
801+ Please check the configuration in ${ joinPath ( tmpDir , 'shopify.extension.toml' ) } ` ) ,
802+ )
803+ } )
804+ } )
805+
806+ test ( 'shows multiple errors when both tools and instructions files are missing' , async ( ) => {
807+ await inTemporaryDirectory ( async ( tmpDir ) => {
808+ const { result} = await setupToolsExtension ( tmpDir , {
809+ tools : './tools.json' ,
810+ instructions : './instructions.md' ,
811+ } )
812+
813+ const toolsNotFoundPath = joinPath ( tmpDir , './tools.json' )
814+ const instructionsNotFoundPath = joinPath ( tmpDir , './instructions.md' )
815+ expect ( result ) . toEqual (
816+ err ( `Couldn't find ${ toolsNotFoundPath }
817+ Please check the tools path for EXTENSION::POINT::A
818+
819+ Couldn't find ${ instructionsNotFoundPath }
820+ Please check the instructions path for EXTENSION::POINT::A
821+
822+ Please check the configuration in ${ joinPath ( tmpDir , 'shopify.extension.toml' ) } ` ) ,
823+ )
824+ } )
825+ } )
826+
827+ test ( 'succeeds when both tools and instructions files exist' , async ( ) => {
828+ await inTemporaryDirectory ( async ( tmpDir ) => {
829+ const { result} = await setupToolsExtension ( tmpDir , {
830+ tools : './tools.json' ,
831+ instructions : './instructions.md' ,
832+ createFiles : true ,
833+ } )
834+
835+ expect ( result ) . toStrictEqual ( ok ( { } ) )
836+ } )
837+ } )
838+
839+ test ( 'build_manifest includes both tools and instructions when both are present' , async ( ) => {
840+ const allSpecs = await loadLocalExtensionsSpecifications ( )
841+ const specification = allSpecs . find ( ( spec ) => spec . identifier === 'ui_extension' ) !
842+ const configuration = {
843+ targeting : [
844+ {
845+ target : 'EXTENSION::POINT::A' ,
846+ module : './src/ExtensionPointA.js' ,
847+ tools : './tools.json' ,
848+ instructions : './instructions.md' ,
849+ } ,
850+ ] ,
851+ api_version : '2023-01' as const ,
852+ name : 'UI Extension' ,
853+ description : 'This is an ordinary test extension' ,
854+ type : 'ui_extension' ,
855+ handle : 'test-ui-extension' ,
856+ capabilities : {
857+ block_progress : false ,
858+ network_access : false ,
859+ api_access : false ,
860+ collect_buyer_consent : {
861+ customer_privacy : true ,
862+ sms_marketing : false ,
863+ } ,
864+ iframe : {
865+ sources : [ ] ,
866+ } ,
867+ } ,
868+ settings : { } ,
869+ }
870+
871+ // When
872+ const parsed = specification . parseConfigurationObject ( configuration )
873+ if ( parsed . state !== 'ok' ) {
874+ throw new Error ( "Couldn't parse configuration" )
875+ }
876+
877+ const got = parsed . data
878+
879+ // Then
880+ expect ( got . extension_points ) . toStrictEqual ( [
881+ {
882+ target : 'EXTENSION::POINT::A' ,
883+ module : './src/ExtensionPointA.js' ,
884+ metafields : [ ] ,
885+ default_placement_reference : undefined ,
886+ capabilities : undefined ,
887+ preloads : { } ,
888+ build_manifest : {
889+ assets : {
890+ main : {
891+ filepath : 'test-ui-extension.js' ,
892+ module : './src/ExtensionPointA.js' ,
893+ } ,
894+ tools : {
895+ filepath : 'test-ui-extension-tools-tools.json' ,
896+ module : './tools.json' ,
897+ static : true ,
898+ } ,
899+ instructions : {
900+ filepath : 'test-ui-extension-instructions-instructions.md' ,
901+ module : './instructions.md' ,
902+ static : true ,
903+ } ,
904+ } ,
905+ } ,
906+ urls : { } ,
907+ } ,
908+ ] )
909+ } )
587910 } )
588911
589912 describe ( 'deployConfig()' , ( ) => {
0 commit comments