1import { vol } from 'memfs';
2import * as path from 'path';
3
4import rnFixture from '../../plugins/__tests__/fixtures/react-native-project';
5import { UnexpectedError } from '../../utils/errors';
6import * as WarningAggregator from '../../utils/warnings';
7import {
8  findSchemeNames,
9  getAllInfoPlistPaths,
10  getAppDelegate,
11  getXcodeProjectPath,
12} from '../Paths';
13
14jest.mock('fs');
15jest.mock('../../utils/warnings');
16
17describe(findSchemeNames, () => {
18  afterEach(() => {
19    vol.reset();
20  });
21
22  it(`returns project path`, () => {
23    vol.fromJSON(
24      {
25        'ios/my-app.xcodeproj/xcshareddata/xcschemes/my-app.xcscheme': '',
26        'ios/my_app.xcodeproj/xcshareddata/xcschemes/my_app.xcscheme': '',
27        'ios/clientTests.xcodeproj/xcshareddata/xcschemes/client.beta.xcscheme': '',
28      },
29      '/'
30    );
31
32    expect(findSchemeNames('/')).toStrictEqual(['client.beta', 'my_app', 'my-app']);
33  });
34});
35
36describe(getXcodeProjectPath, () => {
37  afterEach(() => {
38    vol.reset();
39  });
40
41  it(`returns project path`, () => {
42    vol.fromJSON(
43      {
44        'ios/testproject.xcodeproj/project.pbxproj':
45          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
46        'ios/Podfile': 'content',
47        'ios/TestPod.podspec': 'noop',
48        'ios/testproject/AppDelegate.m': '',
49      },
50      '/app'
51    );
52    expect(getXcodeProjectPath('/app')).toBe('/app/ios/testproject.xcodeproj');
53  });
54
55  it(`throws when no paths are found`, () => {
56    expect(() => getXcodeProjectPath('/none')).toThrow(UnexpectedError);
57  });
58
59  it(`warns when multiple paths are found`, () => {
60    vol.fromJSON(
61      {
62        'ios/otherproject.xcodeproj/project.pbxproj':
63          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
64        'ios/testproject.xcodeproj/project.pbxproj':
65          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
66        'ios/testproject/AppDelegate.m': '',
67      },
68      '/app'
69    );
70
71    expect(getXcodeProjectPath('/app')).toBe('/app/ios/otherproject.xcodeproj');
72    expect(WarningAggregator.addWarningIOS).toHaveBeenLastCalledWith(
73      'paths-xcodeproj',
74      'Found multiple *.xcodeproj file paths, using "ios/otherproject.xcodeproj". Ignored paths: ["ios/testproject.xcodeproj"]'
75    );
76  });
77
78  it(`selects xcodeproj based on alphabetical order, but picks direct children of ios directory first`, () => {
79    vol.fromJSON(
80      {
81        'ios/otherproject.xcodeproj/project.pbxproj':
82          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
83        'ios/aa/otherproject.xcodeproj/project.pbxproj':
84          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
85        'ios/botherproject.xcodeproj/project.pbxproj':
86          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
87        'ios/testproject.xcodeproj/project.pbxproj':
88          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
89        'ios/testproject/AppDelegate.m': '',
90      },
91      '/app'
92    );
93
94    expect(getXcodeProjectPath('/app')).toBe('/app/ios/botherproject.xcodeproj');
95    expect(WarningAggregator.addWarningIOS).toHaveBeenLastCalledWith(
96      'paths-xcodeproj',
97      'Found multiple *.xcodeproj file paths, using "ios/botherproject.xcodeproj". Ignored paths: ["ios/otherproject.xcodeproj","ios/testproject.xcodeproj","ios/aa/otherproject.xcodeproj"]'
98    );
99  });
100
101  it(`warns when multiple paths are found`, () => {
102    vol.fromJSON(
103      {
104        'ios/otherproject.xcodeproj/project.pbxproj':
105          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
106        'ios/testproject.xcodeproj/project.pbxproj':
107          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
108        'ios/testproject/AppDelegate.m': '',
109      },
110      '/app'
111    );
112
113    expect(getXcodeProjectPath('/app')).toBe('/app/ios/otherproject.xcodeproj');
114    expect(WarningAggregator.addWarningIOS).toHaveBeenLastCalledWith(
115      'paths-xcodeproj',
116      'Found multiple *.xcodeproj file paths, using "ios/otherproject.xcodeproj". Ignored paths: ["ios/testproject.xcodeproj"]'
117    );
118  });
119  it(`ignores xcodeproj outside of the ios directory`, () => {
120    vol.fromJSON(
121      {
122        'lib/testproject.xcodeproj/project.pbxproj':
123          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
124      },
125      '/app'
126    );
127    expect(() => getXcodeProjectPath('/app')).toThrow(UnexpectedError);
128  });
129});
130
131describe(getAppDelegate, () => {
132  beforeEach(() => {
133    vol.reset();
134  });
135  afterAll(() => {
136    vol.reset();
137  });
138
139  it(`returns objc path`, () => {
140    vol.fromJSON(
141      {
142        'ios/testproject.xcodeproj/project.pbxproj':
143          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
144        'ios/Podfile': 'content',
145        'ios/TestPod.podspec': 'noop',
146        'ios/testproject/AppDelegate.m': '',
147        'ios/testproject/AppDelegate.h': '',
148      },
149      '/objc'
150    );
151
152    expect(getAppDelegate('/objc')).toStrictEqual({
153      contents: '',
154      path: '/objc/ios/testproject/AppDelegate.m',
155      language: 'objc',
156    });
157  });
158
159  it(`returns C++ (objcpp) path`, () => {
160    vol.fromJSON(rnFixture, '/');
161
162    expect(getAppDelegate('/')).toStrictEqual({
163      contents: expect.any(String),
164      path: '/ios/HelloWorld/AppDelegate.mm',
165      language: 'objcpp',
166    });
167  });
168
169  it(`returns swift path`, () => {
170    vol.fromJSON(
171      {
172        'ios/testproject.xcodeproj/project.pbxproj':
173          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
174        'ios/Podfile': 'content',
175        'ios/TestPod.podspec': 'noop',
176        'ios/testproject/AppDelegate.swift': '',
177      },
178      '/swift'
179    );
180
181    expect(getAppDelegate('/swift')).toStrictEqual({
182      contents: '',
183      path: '/swift/ios/testproject/AppDelegate.swift',
184      language: 'swift',
185    });
186  });
187
188  it(`throws on invalid project`, () => {
189    vol.fromJSON(
190      {
191        'ios/testproject.xcodeproj/project.pbxproj':
192          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
193        'ios/Podfile': 'content',
194        'ios/TestPod.podspec': 'noop',
195      },
196      '/invalid'
197    );
198
199    expect(() => getAppDelegate('/invalid')).toThrow(UnexpectedError);
200    expect(() => getAppDelegate('/invalid')).toThrow(/AppDelegate/);
201  });
202
203  it(`warns when multiple paths are found`, () => {
204    vol.fromJSON(
205      {
206        'ios/testproject.xcodeproj/project.pbxproj':
207          rnFixture['ios/HelloWorld.xcodeproj/project.pbxproj'],
208        'ios/Podfile': 'content',
209        'ios/TestPod.podspec': 'noop',
210        'ios/testproject/AppDelegate.swift': '',
211        'ios/testproject/AppDelegate.m': '',
212        'ios/testproject/AppDelegate.h': '',
213      },
214      '/confusing'
215    );
216
217    expect(getAppDelegate('/confusing')).toStrictEqual({
218      contents: '',
219      path: '/confusing/ios/testproject/AppDelegate.m',
220      language: 'objc',
221    });
222    expect(WarningAggregator.addWarningIOS).toHaveBeenLastCalledWith(
223      'paths-app-delegate',
224      'Found multiple AppDelegate file paths, using "ios/testproject/AppDelegate.m". Ignored paths: ["ios/testproject/AppDelegate.swift"]'
225    );
226  });
227});
228
229describe(getAllInfoPlistPaths, () => {
230  beforeAll(async () => {
231    const project = {
232      'ExampleE2E-tvOS/Info.plist': '',
233      'ExampleE2E/Info.plist': '',
234      'ExampleE2E-tvOSTests/Info.plist': '',
235      'ExampleE2ETests/Info.plist': '',
236    };
237    vol.fromJSON(project, path.join('/app', 'ios'));
238    vol.fromJSON(project, '/app');
239  });
240
241  afterAll(() => {
242    vol.reset();
243  });
244
245  it(`gets paths in order`, () => {
246    expect(getAllInfoPlistPaths('/app')).toStrictEqual([
247      '/app/ios/ExampleE2E/Info.plist',
248      '/app/ios/ExampleE2E-tvOS/Info.plist',
249      '/app/ios/ExampleE2ETests/Info.plist',
250      '/app/ios/ExampleE2E-tvOSTests/Info.plist',
251    ]);
252  });
253});
254