1082815dcSEvan Baconimport { ExpoConfig } from '@expo/config-types';
2*42d6b312SCedric van Puttenimport glob from 'glob';
3082815dcSEvan Baconimport { vol } from 'memfs';
4*42d6b312SCedric van Puttenimport path from 'path';
5082815dcSEvan Bacon
6082815dcSEvan Baconimport { withEntitlementsPlist, withInfoPlist } from '../ios-plugins';
7082815dcSEvan Baconimport { evalModsAsync } from '../mod-compiler';
8*42d6b312SCedric van Puttenimport { getIosModFileProviders, withIosBaseMods } from '../withIosBaseMods';
9082815dcSEvan Bacon
10082815dcSEvan Baconjest.mock('fs');
11*42d6b312SCedric van Puttenjest.mock('glob');
12082815dcSEvan Bacon
13082815dcSEvan Bacondescribe('entitlements', () => {
14082815dcSEvan Bacon  afterEach(() => {
15082815dcSEvan Bacon    vol.reset();
16082815dcSEvan Bacon  });
17082815dcSEvan Bacon
18082815dcSEvan Bacon  it(`evaluates in dry run mode`, async () => {
19082815dcSEvan Bacon    // Ensure this test runs in a blank file system
20082815dcSEvan Bacon    vol.fromJSON({});
21082815dcSEvan Bacon    let config: ExpoConfig = { name: 'bacon', slug: 'bacon' };
22082815dcSEvan Bacon    config = withEntitlementsPlist(config, (config) => {
23082815dcSEvan Bacon      config.modResults['haha'] = 'bet';
24082815dcSEvan Bacon      return config;
25082815dcSEvan Bacon    });
26082815dcSEvan Bacon
27082815dcSEvan Bacon    // base mods must be added last
28082815dcSEvan Bacon    config = withIosBaseMods(config, {
29082815dcSEvan Bacon      saveToInternal: true,
30082815dcSEvan Bacon      providers: {
31082815dcSEvan Bacon        entitlements: {
32082815dcSEvan Bacon          getFilePath() {
33082815dcSEvan Bacon            return '';
34082815dcSEvan Bacon          },
35082815dcSEvan Bacon          async read() {
36082815dcSEvan Bacon            return {};
37082815dcSEvan Bacon          },
38082815dcSEvan Bacon          async write() {},
39082815dcSEvan Bacon        },
40082815dcSEvan Bacon      },
41082815dcSEvan Bacon    });
42082815dcSEvan Bacon    config = await evalModsAsync(config, { projectRoot: '/', platforms: ['ios'] });
43082815dcSEvan Bacon
44082815dcSEvan Bacon    expect(config.ios?.entitlements).toStrictEqual({
45082815dcSEvan Bacon      haha: 'bet',
46082815dcSEvan Bacon    });
47082815dcSEvan Bacon    // @ts-ignore: mods are untyped
48082815dcSEvan Bacon    expect(config.mods.ios.entitlements).toBeDefined();
49082815dcSEvan Bacon
50082815dcSEvan Bacon    expect(config._internal.modResults.ios.entitlements).toBeDefined();
51082815dcSEvan Bacon
52082815dcSEvan Bacon    // Ensure no files were written
53082815dcSEvan Bacon    expect(vol.toJSON()).toStrictEqual({});
54082815dcSEvan Bacon  });
55*42d6b312SCedric van Putten
56*42d6b312SCedric van Putten  it('uses local entitlement files by default', async () => {
57*42d6b312SCedric van Putten    // Create a fake project that can load entitlements
58*42d6b312SCedric van Putten    vol.fromJSON({
59*42d6b312SCedric van Putten      '/ios/HelloWorld/AppDelegate.mm': 'Fake AppDelegate.mm',
60*42d6b312SCedric van Putten      '/ios/HelloWorld.xcodeproj/project.pbxproj': jest
61*42d6b312SCedric van Putten        .requireActual<typeof import('fs')>('fs')
62*42d6b312SCedric van Putten        .readFileSync(
63*42d6b312SCedric van Putten          path.resolve(__dirname, './fixtures/project-files/ios/project.pbxproj'),
64*42d6b312SCedric van Putten          'utf-8'
65*42d6b312SCedric van Putten        ),
66*42d6b312SCedric van Putten      '/ios/HelloWorld/HelloWorld.entitlements': jest
67*42d6b312SCedric van Putten        .requireActual<typeof import('fs')>('fs')
68*42d6b312SCedric van Putten        .readFileSync(
69*42d6b312SCedric van Putten          path.resolve(__dirname, './fixtures/project-files/ios/project.entitlements'),
70*42d6b312SCedric van Putten          'utf-8'
71*42d6b312SCedric van Putten        ),
72*42d6b312SCedric van Putten    });
73*42d6b312SCedric van Putten
74*42d6b312SCedric van Putten    // Mock glob response to "find" the memfs files
75*42d6b312SCedric van Putten    jest.mocked(glob.sync).mockImplementation((pattern) => {
76*42d6b312SCedric van Putten      if (pattern === 'ios/**/*.xcodeproj') return ['/ios/HelloWorld.xcodeproj'];
77*42d6b312SCedric van Putten      if (pattern === 'ios/*/AppDelegate.@(m|mm|swift)') return ['/ios/HelloWorld/AppDelegate.mm'];
78*42d6b312SCedric van Putten      throw new Error('Unexpected glob pattern used in test');
79*42d6b312SCedric van Putten    });
80*42d6b312SCedric van Putten
81*42d6b312SCedric van Putten    // Create simple project config and config plugin chain
82*42d6b312SCedric van Putten    let config: ExpoConfig = { name: 'bacon', slug: 'bacon' };
83*42d6b312SCedric van Putten    config = withEntitlementsPlist(config, (config) => {
84*42d6b312SCedric van Putten      config.modResults['haha'] = 'yes';
85*42d6b312SCedric van Putten      return config;
86*42d6b312SCedric van Putten    });
87*42d6b312SCedric van Putten
88*42d6b312SCedric van Putten    // Base mod must be added last
89*42d6b312SCedric van Putten    config = withIosBaseMods(config, {
90*42d6b312SCedric van Putten      saveToInternal: true,
91*42d6b312SCedric van Putten      providers: {
92*42d6b312SCedric van Putten        // Use the default mod provider, that's the one we need to test
93*42d6b312SCedric van Putten        entitlements: getIosModFileProviders().entitlements,
94*42d6b312SCedric van Putten      },
95*42d6b312SCedric van Putten    });
96*42d6b312SCedric van Putten    config = await evalModsAsync(config, {
97*42d6b312SCedric van Putten      projectRoot: '/',
98*42d6b312SCedric van Putten      platforms: ['ios'],
99*42d6b312SCedric van Putten    });
100*42d6b312SCedric van Putten
101*42d6b312SCedric van Putten    // Check if the generated entitlements are merged with local entitlements
102*42d6b312SCedric van Putten    expect(config.ios?.entitlements).toMatchInlineSnapshot(`
103*42d6b312SCedric van Putten      {
104*42d6b312SCedric van Putten        "aps-environment": "development",
105*42d6b312SCedric van Putten        "com.apple.developer.applesignin": [
106*42d6b312SCedric van Putten          "Default",
107*42d6b312SCedric van Putten        ],
108*42d6b312SCedric van Putten        "com.apple.developer.associated-domains": [
109*42d6b312SCedric van Putten          "applinks:acme.com",
110*42d6b312SCedric van Putten        ],
111*42d6b312SCedric van Putten        "com.apple.developer.icloud-container-identifiers": [
112*42d6b312SCedric van Putten          "iCloud.$(CFBundleIdentifier)",
113*42d6b312SCedric van Putten        ],
114*42d6b312SCedric van Putten        "com.apple.developer.icloud-services": [
115*42d6b312SCedric van Putten          "CloudDocuments",
116*42d6b312SCedric van Putten        ],
117*42d6b312SCedric van Putten        "com.apple.developer.ubiquity-container-identifiers": [
118*42d6b312SCedric van Putten          "iCloud.$(CFBundleIdentifier)",
119*42d6b312SCedric van Putten        ],
120*42d6b312SCedric van Putten        "com.apple.developer.ubiquity-kvstore-identifier": "$(TeamIdentifierPrefix)$(CFBundleIdentifier)",
121*42d6b312SCedric van Putten        "haha": "yes",
122*42d6b312SCedric van Putten      }
123*42d6b312SCedric van Putten    `);
124*42d6b312SCedric van Putten  });
125*42d6b312SCedric van Putten
126*42d6b312SCedric van Putten  it('skips local entitlements files when ignoring existing native files', async () => {
127*42d6b312SCedric van Putten    // Create a fake project that can load entitlements
128*42d6b312SCedric van Putten    vol.fromJSON({
129*42d6b312SCedric van Putten      '/ios/HelloWorld/AppDelegate.mm': 'Fake AppDelegate.mm',
130*42d6b312SCedric van Putten      '/ios/HelloWorld.xcodeproj/project.pbxproj': jest
131*42d6b312SCedric van Putten        .requireActual<typeof import('fs')>('fs')
132*42d6b312SCedric van Putten        .readFileSync(
133*42d6b312SCedric van Putten          path.resolve(__dirname, './fixtures/project-files/ios/project.pbxproj'),
134*42d6b312SCedric van Putten          'utf-8'
135*42d6b312SCedric van Putten        ),
136*42d6b312SCedric van Putten      '/ios/HelloWorld/HelloWorld.entitlements': jest
137*42d6b312SCedric van Putten        .requireActual<typeof import('fs')>('fs')
138*42d6b312SCedric van Putten        .readFileSync(
139*42d6b312SCedric van Putten          path.resolve(__dirname, './fixtures/project-files/ios/project.entitlements'),
140*42d6b312SCedric van Putten          'utf-8'
141*42d6b312SCedric van Putten        ),
142*42d6b312SCedric van Putten    });
143*42d6b312SCedric van Putten
144*42d6b312SCedric van Putten    // Mock glob response to "find" the memfs files
145*42d6b312SCedric van Putten    jest.mocked(glob.sync).mockImplementation((pattern) => {
146*42d6b312SCedric van Putten      if (pattern === 'ios/**/*.xcodeproj') return ['/ios/HelloWorld.xcodeproj'];
147*42d6b312SCedric van Putten      if (pattern === 'ios/*/AppDelegate.@(m|mm|swift)') return ['/ios/HelloWorld/AppDelegate.mm'];
148*42d6b312SCedric van Putten      throw new Error('Unexpected glob pattern used in test');
149*42d6b312SCedric van Putten    });
150*42d6b312SCedric van Putten
151*42d6b312SCedric van Putten    // Create simple project config and config plugin chain
152*42d6b312SCedric van Putten    let config: ExpoConfig = { name: 'bacon', slug: 'bacon' };
153*42d6b312SCedric van Putten    config = withEntitlementsPlist(config, (config) => {
154*42d6b312SCedric van Putten      config.modResults['haha'] = 'yes';
155*42d6b312SCedric van Putten      return config;
156*42d6b312SCedric van Putten    });
157*42d6b312SCedric van Putten
158*42d6b312SCedric van Putten    // Base mod must be added last
159*42d6b312SCedric van Putten    config = withIosBaseMods(config, {
160*42d6b312SCedric van Putten      saveToInternal: true,
161*42d6b312SCedric van Putten      providers: {
162*42d6b312SCedric van Putten        // Use the default mod provider, that's the one we need to test
163*42d6b312SCedric van Putten        entitlements: getIosModFileProviders().entitlements,
164*42d6b312SCedric van Putten      },
165*42d6b312SCedric van Putten    });
166*42d6b312SCedric van Putten    config = await evalModsAsync(config, {
167*42d6b312SCedric van Putten      projectRoot: '/',
168*42d6b312SCedric van Putten      platforms: ['ios'],
169*42d6b312SCedric van Putten      ignoreExistingNativeFiles: true,
170*42d6b312SCedric van Putten    });
171*42d6b312SCedric van Putten
172*42d6b312SCedric van Putten    // Check if the generated entitlements are NOT merged with local entitlements
173*42d6b312SCedric van Putten    expect(config.ios?.entitlements).toMatchInlineSnapshot(`
174*42d6b312SCedric van Putten      {
175*42d6b312SCedric van Putten        "haha": "yes",
176*42d6b312SCedric van Putten      }
177*42d6b312SCedric van Putten    `);
178*42d6b312SCedric van Putten  });
179082815dcSEvan Bacon});
180082815dcSEvan Bacon
181082815dcSEvan Bacondescribe('infoPlist', () => {
182082815dcSEvan Bacon  afterEach(() => {
183082815dcSEvan Bacon    vol.reset();
184082815dcSEvan Bacon  });
185082815dcSEvan Bacon
186082815dcSEvan Bacon  it(`evaluates in dry run mode`, async () => {
187082815dcSEvan Bacon    // Ensure this test runs in a blank file system
188082815dcSEvan Bacon    vol.fromJSON({});
189082815dcSEvan Bacon    let config: ExpoConfig = { name: 'bacon', slug: 'bacon' };
190082815dcSEvan Bacon    config = withInfoPlist(config, (config) => {
191082815dcSEvan Bacon      config.modResults['haha'] = 'bet';
192082815dcSEvan Bacon      return config;
193082815dcSEvan Bacon    });
194082815dcSEvan Bacon
195082815dcSEvan Bacon    // base mods must be added last
196082815dcSEvan Bacon    config = withIosBaseMods(config, {
197082815dcSEvan Bacon      saveToInternal: true,
198082815dcSEvan Bacon      providers: {
199082815dcSEvan Bacon        infoPlist: {
200082815dcSEvan Bacon          getFilePath() {
201082815dcSEvan Bacon            return '';
202082815dcSEvan Bacon          },
203082815dcSEvan Bacon          async read() {
204082815dcSEvan Bacon            return {};
205082815dcSEvan Bacon          },
206082815dcSEvan Bacon          async write() {},
207082815dcSEvan Bacon        },
208082815dcSEvan Bacon      },
209082815dcSEvan Bacon    });
210082815dcSEvan Bacon    config = await evalModsAsync(config, { projectRoot: '/', platforms: ['ios'] });
211082815dcSEvan Bacon
212082815dcSEvan Bacon    expect(config.ios?.infoPlist).toStrictEqual({
213082815dcSEvan Bacon      haha: 'bet',
214082815dcSEvan Bacon    });
215082815dcSEvan Bacon    // @ts-ignore: mods are untyped
216082815dcSEvan Bacon    expect(config.mods.ios.infoPlist).toBeDefined();
217082815dcSEvan Bacon
218082815dcSEvan Bacon    expect(config._internal.modResults.ios.infoPlist).toBeDefined();
219082815dcSEvan Bacon
220082815dcSEvan Bacon    // Ensure no files were written
221082815dcSEvan Bacon    expect(vol.toJSON()).toStrictEqual({});
222082815dcSEvan Bacon  });
223082815dcSEvan Bacon});
224