@@ -2,10 +2,13 @@ import {bundleAndBuildExtensions} from './bundle.js'
22import { testApp , testFunctionExtension , testThemeExtensions , testUIExtension } from '../../models/app/app.test-data.js'
33import { AppInterface } from '../../models/app/app.js'
44import * as bundle from '../bundle.js'
5+ import * as functionBuild from '../function/build.js'
56import { describe , expect , test , vi } from 'vitest'
67import * as file from '@shopify/cli-kit/node/fs'
78import { joinPath } from '@shopify/cli-kit/node/path'
89
10+ vi . mock ( '../function/build.js' )
11+
912describe ( 'bundleAndBuildExtensions' , ( ) => {
1013 let app : AppInterface
1114
@@ -108,9 +111,11 @@ describe('bundleAndBuildExtensions', () => {
108111 const bundlePath = joinPath ( tmpDir , 'bundle.zip' )
109112
110113 const functionExtension = await testFunctionExtension ( )
114+ const extensionBuildMock = vi . fn ( )
111115 const extensionCopyIntoBundleMock = vi . fn ( ) . mockImplementation ( async ( options , bundleDirectory , identifiers ) => {
112116 file . writeFileSync ( joinPath ( bundleDirectory , 'index.wasm' ) , '' )
113117 } )
118+ functionExtension . buildForBundle = extensionBuildMock
114119 functionExtension . copyIntoBundle = extensionCopyIntoBundleMock
115120 const app = testApp ( { allExtensions : [ functionExtension ] , directory : tmpDir } )
116121
@@ -129,6 +134,166 @@ describe('bundleAndBuildExtensions', () => {
129134 await bundleAndBuildExtensions ( { app, identifiers, bundlePath, skipBuild : true } )
130135
131136 // Then
137+ expect ( extensionBuildMock ) . not . toHaveBeenCalled ( )
138+ expect ( extensionCopyIntoBundleMock ) . toHaveBeenCalledTimes ( 1 )
139+ await expect ( file . fileExists ( bundlePath ) ) . resolves . toBeTruthy ( )
140+ } )
141+ } )
142+
143+ test ( 'skips installing Javy for function extensions when skipBuild is true' , async ( ) => {
144+ await file . inTemporaryDirectory ( async ( tmpDir : string ) => {
145+ // Given
146+ const bundlePath = joinPath ( tmpDir , 'bundle.zip' )
147+ const mockInstallJavy = vi . mocked ( functionBuild . installJavy )
148+
149+ const functionExtension = await testFunctionExtension ( )
150+ const extensionCopyIntoBundleMock = vi . fn ( ) . mockImplementation ( async ( options , bundleDirectory , identifiers ) => {
151+ file . writeFileSync ( joinPath ( bundleDirectory , 'index.wasm' ) , '' )
152+ } )
153+ functionExtension . copyIntoBundle = extensionCopyIntoBundleMock
154+ const app = testApp ( { allExtensions : [ functionExtension ] , directory : tmpDir } )
155+
156+ const identifiers = {
157+ app : 'app-id' ,
158+ extensions : { [ functionExtension . localIdentifier ] : functionExtension . localIdentifier } ,
159+ extensionIds : { } ,
160+ extensionsNonUuidManaged : { } ,
161+ }
162+
163+ // When
164+ await bundleAndBuildExtensions ( { app, identifiers, bundlePath, skipBuild : true } )
165+
166+ // Then
167+ expect ( mockInstallJavy ) . not . toHaveBeenCalled ( )
168+ } )
169+ } )
170+
171+ test ( 'installs Javy for function extensions when skipBuild is false' , async ( ) => {
172+ await file . inTemporaryDirectory ( async ( tmpDir : string ) => {
173+ // Given
174+ const bundlePath = joinPath ( tmpDir , 'bundle.zip' )
175+ const mockInstallJavy = vi . mocked ( functionBuild . installJavy )
176+
177+ const functionExtension = await testFunctionExtension ( )
178+ const extensionBuildMock = vi . fn ( ) . mockImplementation ( async ( options , bundleDirectory , identifiers ) => {
179+ file . writeFileSync ( joinPath ( bundleDirectory , 'index.wasm' ) , '' )
180+ } )
181+ functionExtension . buildForBundle = extensionBuildMock
182+ const app = testApp ( { allExtensions : [ functionExtension ] , directory : tmpDir } )
183+
184+ const identifiers = {
185+ app : 'app-id' ,
186+ extensions : { [ functionExtension . localIdentifier ] : functionExtension . localIdentifier } ,
187+ extensionIds : { } ,
188+ extensionsNonUuidManaged : { } ,
189+ }
190+
191+ // When
192+ await bundleAndBuildExtensions ( { app, identifiers, bundlePath, skipBuild : false } )
193+
194+ // Then
195+ expect ( mockInstallJavy ) . toHaveBeenCalledWith ( app )
196+ } )
197+ } )
198+
199+ test ( 'handles theme extensions correctly with skipBuild' , async ( ) => {
200+ await file . inTemporaryDirectory ( async ( tmpDir : string ) => {
201+ // Given
202+ const bundlePath = joinPath ( tmpDir , 'bundle.zip' )
203+
204+ const themeExtension = await testThemeExtensions ( )
205+ const extensionBuildMock = vi . fn ( )
206+ const extensionCopyIntoBundleMock = vi . fn ( ) . mockImplementation ( async ( options , bundleDirectory , identifiers ) => {
207+ // Theme extensions would typically copy theme files here
208+ const themeDir = joinPath ( bundleDirectory , themeExtension . uid )
209+ await file . mkdir ( themeDir )
210+ file . writeFileSync ( joinPath ( themeDir , 'theme-file.liquid' ) , '<h1>Theme</h1>' )
211+ } )
212+ themeExtension . buildForBundle = extensionBuildMock
213+ themeExtension . copyIntoBundle = extensionCopyIntoBundleMock
214+
215+ const app = testApp ( { allExtensions : [ themeExtension ] , directory : tmpDir } )
216+
217+ const identifiers = {
218+ app : 'app-id' ,
219+ extensions : { [ themeExtension . localIdentifier ] : themeExtension . localIdentifier } ,
220+ extensionIds : { } ,
221+ extensionsNonUuidManaged : { } ,
222+ }
223+
224+ // When
225+ await bundleAndBuildExtensions ( { app, identifiers, bundlePath, skipBuild : true } )
226+
227+ // Then
228+ expect ( extensionBuildMock ) . not . toHaveBeenCalled ( )
229+ expect ( extensionCopyIntoBundleMock ) . toHaveBeenCalledTimes ( 1 )
230+ await expect ( file . fileExists ( bundlePath ) ) . resolves . toBeTruthy ( )
231+ } )
232+ } )
233+
234+ test ( 'handles multiple extension types together' , async ( ) => {
235+ await file . inTemporaryDirectory ( async ( tmpDir : string ) => {
236+ // Given
237+ const bundlePath = joinPath ( tmpDir , 'bundle.zip' )
238+
239+ // Create different extension types
240+ const functionExtension = await testFunctionExtension ( )
241+ const themeExtension = await testThemeExtensions ( )
242+ const uiExtension = await testUIExtension ( { type : 'checkout_ui_extension' } )
243+
244+ // Set up mocks for each extension
245+ const functionBuildMock = vi . fn ( )
246+ const functionCopyMock = vi . fn ( ) . mockImplementation ( async ( options , bundleDirectory ) => {
247+ file . writeFileSync ( joinPath ( bundleDirectory , 'index.wasm' ) , '' )
248+ } )
249+ functionExtension . buildForBundle = functionBuildMock
250+ functionExtension . copyIntoBundle = functionCopyMock
251+
252+ const themeBuildMock = vi . fn ( )
253+ const themeCopyMock = vi . fn ( ) . mockImplementation ( async ( options , bundleDirectory ) => {
254+ const themeDir = joinPath ( bundleDirectory , themeExtension . uid )
255+ await file . mkdir ( themeDir )
256+ file . writeFileSync ( joinPath ( themeDir , 'theme.liquid' ) , '' )
257+ } )
258+ themeExtension . buildForBundle = themeBuildMock
259+ themeExtension . copyIntoBundle = themeCopyMock
260+
261+ const uiBuildMock = vi . fn ( )
262+ const uiCopyMock = vi . fn ( ) . mockImplementation ( async ( options , bundleDirectory ) => {
263+ file . writeFileSync ( joinPath ( bundleDirectory , 'ui.js' ) , '' )
264+ } )
265+ uiExtension . buildForBundle = uiBuildMock
266+ uiExtension . copyIntoBundle = uiCopyMock
267+
268+ const app = testApp ( {
269+ allExtensions : [ functionExtension , themeExtension , uiExtension ] ,
270+ directory : tmpDir ,
271+ } )
272+
273+ const extensions : { [ key : string ] : string } = { }
274+ for ( const extension of app . allExtensions ) {
275+ extensions [ extension . localIdentifier ] = extension . localIdentifier
276+ }
277+ const identifiers = {
278+ app : 'app-id' ,
279+ extensions,
280+ extensionIds : { } ,
281+ extensionsNonUuidManaged : { } ,
282+ }
283+
284+ // When
285+ await bundleAndBuildExtensions ( { app, identifiers, bundlePath, skipBuild : true } )
286+
287+ // Then - verify none of the build methods were called
288+ expect ( functionBuildMock ) . not . toHaveBeenCalled ( )
289+ expect ( themeBuildMock ) . not . toHaveBeenCalled ( )
290+ expect ( uiBuildMock ) . not . toHaveBeenCalled ( )
291+
292+ // Verify all copy methods were called
293+ expect ( functionCopyMock ) . toHaveBeenCalledTimes ( 1 )
294+ expect ( themeCopyMock ) . toHaveBeenCalledTimes ( 1 )
295+ expect ( uiCopyMock ) . toHaveBeenCalledTimes ( 1 )
296+
132297 await expect ( file . fileExists ( bundlePath ) ) . resolves . toBeTruthy ( )
133298 } )
134299 } )
0 commit comments