1import plist from '@expo/plist';
2import * as fs from 'fs';
3import { vol } from 'memfs';
4import * as path from 'path';
5
6import {
7  ensureApplicationTargetEntitlementsFileConfigured,
8  getEntitlementsPath,
9} from '../Entitlements';
10
11const fsReal = jest.requireActual('fs') as typeof fs;
12
13jest.mock('fs');
14
15const exampleEntitlements = `<?xml version="0.0" encoding="UTF-8"?>
16<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
17<plist version="1.0">
18<dict>
19	<key>special</key>
20	<true/>
21</dict>
22</plist>`;
23
24describe(ensureApplicationTargetEntitlementsFileConfigured, () => {
25  const projectRoot = '/app';
26
27  afterEach(() => {
28    vol.reset();
29  });
30
31  it('creates a new entitlements file when none exists', async () => {
32    vol.fromJSON(
33      {
34        'ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
35          path.join(__dirname, 'fixtures/project.pbxproj'),
36          'utf-8'
37        ),
38        'ios/testproject/AppDelegate.m': '',
39      },
40      projectRoot
41    );
42    const entitlementsPathBefore = getEntitlementsPath(projectRoot);
43    ensureApplicationTargetEntitlementsFileConfigured(projectRoot);
44    const entitlementsPath = getEntitlementsPath(projectRoot);
45    expect(entitlementsPathBefore).toBeNull();
46    expect(entitlementsPath).toBe('/app/ios/testproject/testproject.entitlements');
47
48    // New file has the contents of the old entitlements file
49    const data = plist.parse(await fs.promises.readFile(entitlementsPath, 'utf8'));
50    expect(data).toStrictEqual({
51      // No entitlements enabled by default
52    });
53  });
54
55  it('creates a new entitlements file if file in XCBuildConfiguration does not exists', async () => {
56    vol.fromJSON(
57      {
58        'ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
59          path.join(__dirname, 'fixtures/project-with-entitlements.pbxproj'),
60          'utf-8'
61        ),
62        'ios/testproject/AppDelegate.m': '',
63      },
64      projectRoot
65    );
66    ensureApplicationTargetEntitlementsFileConfigured(projectRoot);
67    const entitlementsPath = getEntitlementsPath(projectRoot);
68    expect(entitlementsPath).toBe('/app/ios/testproject/testproject.entitlements');
69
70    // New file has the contents of the old entitlements file
71    const data = plist.parse(await fs.promises.readFile(entitlementsPath, 'utf8'));
72    expect(data).toStrictEqual({});
73  });
74
75  it('does not create any entitlements files if it already exists', async () => {
76    vol.fromJSON(
77      {
78        'ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
79          path.join(__dirname, 'fixtures/project-with-entitlements.pbxproj'),
80          'utf-8'
81        ),
82        'ios/testapp/example.entitlements': exampleEntitlements,
83        'ios/testproject/AppDelegate.m': '',
84      },
85      projectRoot
86    );
87    ensureApplicationTargetEntitlementsFileConfigured(projectRoot);
88    const entitlementsPath = getEntitlementsPath(projectRoot);
89    expect(entitlementsPath).toBe('/app/ios/testapp/example.entitlements');
90
91    // New file has the contents of the old entitlements file
92    const data = plist.parse(await fs.promises.readFile(entitlementsPath, 'utf8'));
93    expect(data).toStrictEqual({ special: true });
94
95    // entitlement file in default location does not exist
96    expect(fs.existsSync('/app/ios/testproject/testproject.entitlements')).toBe(false);
97  });
98});
99
100describe(getEntitlementsPath, () => {
101  const projectRoot = '/app';
102
103  afterEach(() => {
104    vol.reset();
105  });
106
107  it('returns null if CODE_SIGN_ENTITLEMENTS is not specified', async () => {
108    vol.fromJSON(
109      {
110        'ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
111          path.join(__dirname, 'fixtures/project.pbxproj'),
112          'utf-8'
113        ),
114        'ios/testproject/AppDelegate.m': '',
115      },
116      projectRoot
117    );
118
119    const entitlementsPath = getEntitlementsPath(projectRoot);
120    expect(entitlementsPath).toBeNull();
121  });
122  it('returns path if CODE_SIGN_ENTITLEMENTS is specified and file exists', async () => {
123    vol.fromJSON(
124      {
125        'ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
126          path.join(__dirname, 'fixtures/project-with-entitlements.pbxproj'),
127          'utf-8'
128        ),
129        'ios/testapp/example.entitlements': exampleEntitlements,
130        'ios/testproject/AppDelegate.m': '',
131      },
132      projectRoot
133    );
134
135    const entitlementsPath = getEntitlementsPath(projectRoot);
136    expect(entitlementsPath).toBe('/app/ios/testapp/example.entitlements');
137  });
138});
139