@@ -77,3 +77,101 @@ describe('Configures iOS native project correctly', () => {
7777 expect ( warnOnce ) . toHaveBeenCalled ( ) ;
7878 } ) ;
7979} ) ;
80+
81+ describe ( 'Upload Debug Symbols to Sentry build phase' , ( ) => {
82+ let mockXcodeProject : any ;
83+ let addBuildPhaseSpy : jest . Mock ;
84+ const expectedShellScript =
85+ "/bin/sh `${NODE_BINARY:-node} --print \"require('path').dirname(require.resolve('@sentry/react-native/package.json')) + '/scripts/sentry-xcode-debug-files.sh'\"`" ;
86+
87+ const getOptions = ( ) => {
88+ const callArgs = addBuildPhaseSpy . mock . calls [ 0 ] ;
89+ return callArgs [ 4 ] ;
90+ } ;
91+
92+ beforeEach ( ( ) => {
93+ addBuildPhaseSpy = jest . fn ( ) ;
94+ mockXcodeProject = {
95+ pbxItemByComment : jest . fn ( ) . mockReturnValue ( null ) ,
96+ addBuildPhase : addBuildPhaseSpy ,
97+ } ;
98+ } ) ;
99+
100+ afterEach ( ( ) => {
101+ jest . clearAllMocks ( ) ;
102+ } ) ;
103+
104+ it ( 'creates Upload Debug Symbols build phase with correct shell script' , ( ) => {
105+ mockXcodeProject . addBuildPhase ( [ ] , 'PBXShellScriptBuildPhase' , 'Upload Debug Symbols to Sentry' , null , {
106+ shellPath : '/bin/sh' ,
107+ shellScript : expectedShellScript ,
108+ } ) ;
109+
110+ expect ( addBuildPhaseSpy ) . toHaveBeenCalledWith (
111+ [ ] ,
112+ 'PBXShellScriptBuildPhase' ,
113+ 'Upload Debug Symbols to Sentry' ,
114+ null ,
115+ {
116+ shellPath : '/bin/sh' ,
117+ shellScript : expectedShellScript ,
118+ } ,
119+ ) ;
120+ } ) ;
121+
122+ it ( 'does not include inputPaths in options before fix' , ( ) => {
123+ mockXcodeProject . addBuildPhase ( [ ] , 'PBXShellScriptBuildPhase' , 'Upload Debug Symbols to Sentry' , null , {
124+ shellPath : '/bin/sh' ,
125+ shellScript : expectedShellScript ,
126+ } ) ;
127+
128+ const options = getOptions ( ) ;
129+
130+ expect ( options . inputPaths ) . toBeUndefined ( ) ;
131+ } ) ;
132+
133+ it ( 'skips creating build phase if it already exists' , ( ) => {
134+ mockXcodeProject . pbxItemByComment = jest . fn ( ) . mockReturnValue ( {
135+ shellScript : 'existing' ,
136+ } ) ;
137+
138+ expect ( addBuildPhaseSpy ) . not . toHaveBeenCalled ( ) ;
139+ } ) ;
140+
141+ it ( 'includes inputPaths with escaped quotes to avoid pbxproj serialization issues' , ( ) => {
142+ mockXcodeProject . addBuildPhase ( [ ] , 'PBXShellScriptBuildPhase' , 'Upload Debug Symbols to Sentry' , null , {
143+ shellPath : '/bin/sh' ,
144+ shellScript : expectedShellScript ,
145+ inputPaths : [
146+ '"$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Resources/DWARF/$(PRODUCT_NAME)"' ,
147+ '"$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)"' ,
148+ ] ,
149+ } ) ;
150+
151+ const options = getOptions ( ) ;
152+
153+ expect ( options . inputPaths ) . toBeDefined ( ) ;
154+ expect ( options . inputPaths ) . toHaveLength ( 2 ) ;
155+ } ) ;
156+
157+ it ( 'inputPaths values are wrapped in escaped quotes' , ( ) => {
158+ mockXcodeProject . addBuildPhase ( [ ] , 'PBXShellScriptBuildPhase' , 'Upload Debug Symbols to Sentry' , null , {
159+ shellPath : '/bin/sh' ,
160+ shellScript : expectedShellScript ,
161+ inputPaths : [
162+ '"$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Resources/DWARF/$(PRODUCT_NAME)"' ,
163+ '"$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)"' ,
164+ ] ,
165+ } ) ;
166+
167+ const options = getOptions ( ) ;
168+
169+ // Verify paths are wrapped in quotes to prevent pbxproj corruption
170+ expect ( options . inputPaths [ 0 ] ) . toMatch ( / ^ " .* " $ / ) ;
171+ expect ( options . inputPaths [ 1 ] ) . toMatch ( / ^ " .* " $ / ) ;
172+ expect ( options . inputPaths [ 0 ] ) . toBe (
173+ '"$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Resources/DWARF/$(PRODUCT_NAME)"' ,
174+ ) ;
175+ expect ( options . inputPaths [ 1 ] ) . toBe ( '"$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)"' ) ;
176+ } ) ;
177+ } ) ;
0 commit comments