@@ -15,6 +15,7 @@ const {
1515 findXcodeProjectDirectory,
1616 runIosPrebuild,
1717 runPodDeintegrate,
18+ setBuildFromSource,
1819} = require ( '../prepare-app-utils' ) ;
1920
2021// Mock child_process module
@@ -189,6 +190,237 @@ describe('findXcodeProjectDirectory', () => {
189190 } ) ;
190191} ) ;
191192
193+ describe ( 'setBuildFromSource' , ( ) => {
194+ let mockFs ;
195+ let mockPath ;
196+ let mockConsoleLog ;
197+ let mockConsoleWarn ;
198+
199+ beforeEach ( ( ) => {
200+ // Setup mocks
201+ mockFs = require ( 'fs' ) ;
202+ mockPath = require ( 'path' ) ;
203+ mockConsoleLog = console . log ;
204+ mockConsoleWarn = console . warn ;
205+
206+ // Clear and reset all mocks completely
207+ jest . clearAllMocks ( ) ;
208+ jest . resetAllMocks ( ) ;
209+
210+ // Set up fresh mock implementations
211+ mockFs . existsSync = jest . fn ( ) ;
212+ mockFs . readFileSync = jest . fn ( ) ;
213+ mockFs . writeFileSync = jest . fn ( ) ;
214+
215+ // Mock path.join to return realistic paths
216+ mockPath . join . mockImplementation ( ( ...args ) => args . join ( '/' ) ) ;
217+ } ) ;
218+
219+ it ( 'should update BUILD_FROM_SOURCE from false to true successfully' , async ( ) => {
220+ // Setup
221+ const reactNativePath = '/path/to/react-native' ;
222+ const mockPackageSwiftContent = `
223+ // Package.swift
224+ import PackageDescription
225+
226+ let BUILD_FROM_SOURCE = false
227+
228+ let package = Package(
229+ name: "ReactNative",
230+ platforms: [.iOS(.v13)],
231+ // rest of package
232+ )
233+ ` ;
234+
235+ const expectedUpdatedContent = `
236+ // Package.swift
237+ import PackageDescription
238+
239+ let BUILD_FROM_SOURCE = true
240+
241+ let package = Package(
242+ name: "ReactNative",
243+ platforms: [.iOS(.v13)],
244+ // rest of package
245+ )
246+ ` ;
247+
248+ mockFs . existsSync . mockReturnValue ( true ) ;
249+ mockFs . readFileSync . mockReturnValue ( mockPackageSwiftContent ) ;
250+ mockFs . writeFileSync . mockImplementation ( ( ) => { } ) ;
251+
252+ // Execute
253+ await setBuildFromSource ( reactNativePath ) ;
254+
255+ // Assert
256+ expect ( mockFs . existsSync ) . toHaveBeenCalledWith (
257+ '/path/to/react-native/Package.swift' ,
258+ ) ;
259+ expect ( mockFs . readFileSync ) . toHaveBeenCalledWith (
260+ '/path/to/react-native/Package.swift' ,
261+ 'utf8' ,
262+ ) ;
263+ expect ( mockFs . writeFileSync ) . toHaveBeenCalledWith (
264+ '/path/to/react-native/Package.swift' ,
265+ expectedUpdatedContent ,
266+ 'utf8' ,
267+ ) ;
268+ expect ( mockConsoleLog ) . toHaveBeenCalledWith (
269+ 'Updating BUILD_FROM_SOURCE in: /path/to/react-native/Package.swift' ,
270+ ) ;
271+ expect ( mockConsoleLog ) . toHaveBeenCalledWith (
272+ '✓ BUILD_FROM_SOURCE set to true in Package.swift' ,
273+ ) ;
274+ expect ( mockConsoleWarn ) . not . toHaveBeenCalled ( ) ;
275+ } ) ;
276+
277+ it ( 'should handle when BUILD_FROM_SOURCE is already true' , async ( ) => {
278+ // Setup
279+ const reactNativePath = '/path/to/react-native' ;
280+ const mockPackageSwiftContent = `
281+ // Package.swift
282+ import PackageDescription
283+
284+ let BUILD_FROM_SOURCE = true
285+
286+ let package = Package(
287+ name: "ReactNative",
288+ platforms: [.iOS(.v13)],
289+ // rest of package
290+ )
291+ ` ;
292+
293+ mockFs . existsSync . mockReturnValue ( true ) ;
294+ mockFs . readFileSync . mockReturnValue ( mockPackageSwiftContent ) ;
295+ mockFs . writeFileSync . mockImplementation ( ( ) => { } ) ;
296+
297+ // Execute
298+ await setBuildFromSource ( reactNativePath ) ;
299+
300+ // Assert
301+ expect ( mockFs . existsSync ) . toHaveBeenCalledWith (
302+ '/path/to/react-native/Package.swift' ,
303+ ) ;
304+ expect ( mockFs . readFileSync ) . toHaveBeenCalledWith (
305+ '/path/to/react-native/Package.swift' ,
306+ 'utf8' ,
307+ ) ;
308+ expect ( mockFs . writeFileSync ) . not . toHaveBeenCalled ( ) ; // Should not write when already true
309+ expect ( mockConsoleLog ) . toHaveBeenCalledWith (
310+ 'Updating BUILD_FROM_SOURCE in: /path/to/react-native/Package.swift' ,
311+ ) ;
312+ expect ( mockConsoleLog ) . toHaveBeenCalledWith (
313+ '✓ BUILD_FROM_SOURCE is already set to true in Package.swift' ,
314+ ) ;
315+ expect ( mockConsoleWarn ) . not . toHaveBeenCalled ( ) ;
316+ } ) ;
317+
318+ it ( 'should warn when BUILD_FROM_SOURCE declaration is not found' , async ( ) => {
319+ // Setup
320+ const reactNativePath = '/path/to/react-native' ;
321+ const mockPackageSwiftContent = `
322+ // Package.swift
323+ import PackageDescription
324+
325+ let package = Package(
326+ name: "ReactNative",
327+ platforms: [.iOS(.v13)],
328+ // rest of package without BUILD_FROM_SOURCE
329+ )
330+ ` ;
331+
332+ mockFs . existsSync . mockReturnValue ( true ) ;
333+ mockFs . readFileSync . mockReturnValue ( mockPackageSwiftContent ) ;
334+ mockFs . writeFileSync . mockImplementation ( ( ) => { } ) ;
335+
336+ // Execute
337+ await setBuildFromSource ( reactNativePath ) ;
338+
339+ // Assert
340+ expect ( mockFs . existsSync ) . toHaveBeenCalledWith (
341+ '/path/to/react-native/Package.swift' ,
342+ ) ;
343+ expect ( mockFs . readFileSync ) . toHaveBeenCalledWith (
344+ '/path/to/react-native/Package.swift' ,
345+ 'utf8' ,
346+ ) ;
347+ expect ( mockFs . writeFileSync ) . not . toHaveBeenCalled ( ) ; // Should not write when declaration not found
348+ expect ( mockConsoleLog ) . toHaveBeenCalledWith (
349+ 'Updating BUILD_FROM_SOURCE in: /path/to/react-native/Package.swift' ,
350+ ) ;
351+ expect ( mockConsoleWarn ) . toHaveBeenCalledWith (
352+ '⚠️ BUILD_FROM_SOURCE declaration not found in Package.swift' ,
353+ ) ;
354+ } ) ;
355+
356+ it ( 'should throw error when Package.swift does not exist' , async ( ) => {
357+ // Setup
358+ const reactNativePath = '/path/to/react-native' ;
359+
360+ mockFs . existsSync . mockReturnValue ( false ) ;
361+
362+ // Execute & Assert
363+ await expect ( setBuildFromSource ( reactNativePath ) ) . rejects . toThrow (
364+ 'Package.swift not found at: /path/to/react-native/Package.swift' ,
365+ ) ;
366+
367+ expect ( mockFs . existsSync ) . toHaveBeenCalledWith (
368+ '/path/to/react-native/Package.swift' ,
369+ ) ;
370+ expect ( mockFs . readFileSync ) . not . toHaveBeenCalled ( ) ;
371+ expect ( mockFs . writeFileSync ) . not . toHaveBeenCalled ( ) ;
372+ expect ( mockConsoleLog ) . not . toHaveBeenCalled ( ) ;
373+ } ) ;
374+
375+ it ( 'should handle multiple BUILD_FROM_SOURCE occurrences' , async ( ) => {
376+ // Setup
377+ const reactNativePath = '/path/to/react-native' ;
378+ const mockPackageSwiftContent = `
379+ // Package.swift
380+ import PackageDescription
381+
382+ let BUILD_FROM_SOURCE = false
383+ // Some comment about BUILD_FROM_SOURCE = false
384+ let anotherVar = "BUILD_FROM_SOURCE = false in string"
385+
386+ let package = Package(
387+ name: "ReactNative",
388+ platforms: [.iOS(.v13)],
389+ // Another BUILD_FROM_SOURCE = false comment
390+ )
391+ ` ;
392+
393+ const expectedUpdatedContent = `
394+ // Package.swift
395+ import PackageDescription
396+
397+ let BUILD_FROM_SOURCE = true
398+ // Some comment about BUILD_FROM_SOURCE = false
399+ let anotherVar = "BUILD_FROM_SOURCE = false in string"
400+
401+ let package = Package(
402+ name: "ReactNative",
403+ platforms: [.iOS(.v13)],
404+ // Another BUILD_FROM_SOURCE = false comment
405+ )
406+ ` ;
407+
408+ mockFs . existsSync . mockReturnValue ( true ) ;
409+ mockFs . readFileSync . mockReturnValue ( mockPackageSwiftContent ) ;
410+ mockFs . writeFileSync . mockImplementation ( ( ) => { } ) ;
411+
412+ // Execute
413+ await setBuildFromSource ( reactNativePath ) ;
414+
415+ // Assert - should replace only the declaration, not comments or strings
416+ expect ( mockFs . writeFileSync ) . toHaveBeenCalledWith (
417+ '/path/to/react-native/Package.swift' ,
418+ expectedUpdatedContent ,
419+ 'utf8' ,
420+ ) ;
421+ } ) ;
422+ } ) ;
423+
192424describe ( 'runIosPrebuild' , ( ) => {
193425 let mockExecSync ;
194426 let mockConsoleLog ;
0 commit comments