1*ed3bd27bSEvan Baconimport rnFixture from '../../plugins/__tests__/fixtures/react-native-project';
2082815dcSEvan Baconimport { format } from '../../utils/XML';
3*ed3bd27bSEvan Baconimport * as XML from '../../utils/XML';
4*ed3bd27bSEvan Baconimport { AndroidManifest } from '../Manifest';
5082815dcSEvan Baconimport {
6082815dcSEvan Bacon  addBlockedPermissions,
7082815dcSEvan Bacon  ensurePermission,
8082815dcSEvan Bacon  ensurePermissions,
9082815dcSEvan Bacon  getAndroidPermissions,
10082815dcSEvan Bacon  getPermissions,
11082815dcSEvan Bacon  removePermissions,
12082815dcSEvan Bacon  setAndroidPermissions,
13082815dcSEvan Bacon  withInternalBlockedPermissions,
14082815dcSEvan Bacon} from '../Permissions';
15082815dcSEvan Bacon
16*ed3bd27bSEvan Baconasync function getFixtureManifestAsync() {
17*ed3bd27bSEvan Bacon  const manifest = (await XML.parseXMLAsync(
18*ed3bd27bSEvan Bacon    rnFixture['android/app/src/main/AndroidManifest.xml']
19*ed3bd27bSEvan Bacon  )) as AndroidManifest;
20082815dcSEvan Bacon
21*ed3bd27bSEvan Bacon  removePermissions(manifest, [
22*ed3bd27bSEvan Bacon    'android.permission.SYSTEM_ALERT_WINDOW',
23*ed3bd27bSEvan Bacon    'android.permission.VIBRATE',
24*ed3bd27bSEvan Bacon    'android.permission.READ_EXTERNAL_STORAGE',
25*ed3bd27bSEvan Bacon    'android.permission.WRITE_EXTERNAL_STORAGE',
26*ed3bd27bSEvan Bacon  ]);
27*ed3bd27bSEvan Bacon  return manifest;
28082815dcSEvan Bacon}
29082815dcSEvan Bacon
30082815dcSEvan Bacondescribe(withInternalBlockedPermissions, () => {
31082815dcSEvan Bacon  it(`adds blocked permissions`, async () => {
32082815dcSEvan Bacon    const config = withInternalBlockedPermissions({
33082815dcSEvan Bacon      slug: '',
34082815dcSEvan Bacon      name: '',
35082815dcSEvan Bacon      android: {
36082815dcSEvan Bacon        blockedPermissions: ['android.permission.ACCESS_FINE_LOCATION', 'OTHER'],
37082815dcSEvan Bacon      },
38082815dcSEvan Bacon    });
39082815dcSEvan Bacon
40082815dcSEvan Bacon    const { modResults } = await (config as any).mods.android.manifest({
41082815dcSEvan Bacon      modRequest: {},
42*ed3bd27bSEvan Bacon      modResults: await getFixtureManifestAsync(),
43082815dcSEvan Bacon    });
44082815dcSEvan Bacon
45082815dcSEvan Bacon    expect(modResults).toEqual({
46082815dcSEvan Bacon      manifest: {
47082815dcSEvan Bacon        $: {
48082815dcSEvan Bacon          'xmlns:android': expect.any(String),
49082815dcSEvan Bacon          // Added tools
50082815dcSEvan Bacon          'xmlns:tools': 'http://schemas.android.com/tools',
51082815dcSEvan Bacon        },
52082815dcSEvan Bacon        'uses-permission': [
53082815dcSEvan Bacon          expect.anything(),
54082815dcSEvan Bacon          // Added two blocked permissions
55082815dcSEvan Bacon          {
56082815dcSEvan Bacon            $: {
57082815dcSEvan Bacon              'android:name': 'android.permission.ACCESS_FINE_LOCATION',
58082815dcSEvan Bacon              'tools:node': 'remove',
59082815dcSEvan Bacon            },
60082815dcSEvan Bacon          },
61082815dcSEvan Bacon          {
62082815dcSEvan Bacon            $: {
638828a4a3SCedric van Putten              'android:name': 'android.permission.OTHER',
64082815dcSEvan Bacon              'tools:node': 'remove',
65082815dcSEvan Bacon            },
66082815dcSEvan Bacon          },
67082815dcSEvan Bacon        ],
68082815dcSEvan Bacon        queries: expect.anything(),
69082815dcSEvan Bacon        application: expect.anything(),
70082815dcSEvan Bacon      },
71082815dcSEvan Bacon    });
72082815dcSEvan Bacon  });
73082815dcSEvan Bacon
74082815dcSEvan Bacon  it(`does not add tools if there are no blocked permissions`, async () => {
75082815dcSEvan Bacon    const config = withInternalBlockedPermissions({
76082815dcSEvan Bacon      slug: '',
77082815dcSEvan Bacon      name: '',
78082815dcSEvan Bacon      android: {
79082815dcSEvan Bacon        blockedPermissions: [],
80082815dcSEvan Bacon      },
81082815dcSEvan Bacon    });
82082815dcSEvan Bacon
83082815dcSEvan Bacon    // Doesn't even add the mod
84082815dcSEvan Bacon    expect((config as any).mods).not.toBeDefined();
85082815dcSEvan Bacon  });
868828a4a3SCedric van Putten
878828a4a3SCedric van Putten  it(`adds blocked permission when using short notation`, async () => {
888828a4a3SCedric van Putten    const config = withInternalBlockedPermissions({
898828a4a3SCedric van Putten      slug: '',
908828a4a3SCedric van Putten      name: '',
918828a4a3SCedric van Putten      android: {
928828a4a3SCedric van Putten        permissions: ['android.permission.ACCESS_FINE_LOCATION'],
938828a4a3SCedric van Putten        blockedPermissions: ['ACCESS_FINE_LOCATION'],
948828a4a3SCedric van Putten      },
958828a4a3SCedric van Putten    });
968828a4a3SCedric van Putten
978828a4a3SCedric van Putten    const { modResults } = await (config as any).mods.android.manifest({
988828a4a3SCedric van Putten      modRequest: {},
99*ed3bd27bSEvan Bacon      modResults: await getFixtureManifestAsync(),
1008828a4a3SCedric van Putten    });
1018828a4a3SCedric van Putten
1028828a4a3SCedric van Putten    expect(modResults).toEqual({
1038828a4a3SCedric van Putten      manifest: {
1048828a4a3SCedric van Putten        $: {
1058828a4a3SCedric van Putten          'xmlns:android': expect.any(String),
1068828a4a3SCedric van Putten          // Added tools
1078828a4a3SCedric van Putten          'xmlns:tools': 'http://schemas.android.com/tools',
1088828a4a3SCedric van Putten        },
1098828a4a3SCedric van Putten        'uses-permission': [
1108828a4a3SCedric van Putten          expect.anything(),
111*ed3bd27bSEvan Bacon
1128828a4a3SCedric van Putten          // Added two blocked permissions
1138828a4a3SCedric van Putten          {
1148828a4a3SCedric van Putten            $: {
1158828a4a3SCedric van Putten              'android:name': 'android.permission.ACCESS_FINE_LOCATION',
1168828a4a3SCedric van Putten              'tools:node': 'remove',
1178828a4a3SCedric van Putten            },
1188828a4a3SCedric van Putten          },
1198828a4a3SCedric van Putten        ],
1208828a4a3SCedric van Putten        queries: expect.anything(),
1218828a4a3SCedric van Putten        application: expect.anything(),
1228828a4a3SCedric van Putten      },
1238828a4a3SCedric van Putten    });
1248828a4a3SCedric van Putten  });
1258828a4a3SCedric van Putten
1268828a4a3SCedric van Putten  it(`adds blocked permission when using long notation`, async () => {
1278828a4a3SCedric van Putten    const config = withInternalBlockedPermissions({
1288828a4a3SCedric van Putten      slug: '',
1298828a4a3SCedric van Putten      name: '',
1308828a4a3SCedric van Putten      android: {
1318828a4a3SCedric van Putten        permissions: ['ACCESS_FINE_LOCATION'],
1328828a4a3SCedric van Putten        blockedPermissions: ['android.permission.ACCESS_FINE_LOCATION'],
1338828a4a3SCedric van Putten      },
1348828a4a3SCedric van Putten    });
1358828a4a3SCedric van Putten
1368828a4a3SCedric van Putten    const { modResults } = await (config as any).mods.android.manifest({
1378828a4a3SCedric van Putten      modRequest: {},
138*ed3bd27bSEvan Bacon      modResults: await getFixtureManifestAsync(),
1398828a4a3SCedric van Putten    });
1408828a4a3SCedric van Putten
1418828a4a3SCedric van Putten    expect(modResults).toEqual({
1428828a4a3SCedric van Putten      manifest: {
1438828a4a3SCedric van Putten        $: {
1448828a4a3SCedric van Putten          'xmlns:android': expect.any(String),
1458828a4a3SCedric van Putten          // Added tools
1468828a4a3SCedric van Putten          'xmlns:tools': 'http://schemas.android.com/tools',
1478828a4a3SCedric van Putten        },
1488828a4a3SCedric van Putten        'uses-permission': [
1498828a4a3SCedric van Putten          expect.anything(),
1508828a4a3SCedric van Putten          // Added two blocked permissions
1518828a4a3SCedric van Putten          {
1528828a4a3SCedric van Putten            $: {
1538828a4a3SCedric van Putten              'android:name': 'android.permission.ACCESS_FINE_LOCATION',
1548828a4a3SCedric van Putten              'tools:node': 'remove',
1558828a4a3SCedric van Putten            },
1568828a4a3SCedric van Putten          },
1578828a4a3SCedric van Putten        ],
1588828a4a3SCedric van Putten        queries: expect.anything(),
1598828a4a3SCedric van Putten        application: expect.anything(),
1608828a4a3SCedric van Putten      },
1618828a4a3SCedric van Putten    });
1628828a4a3SCedric van Putten  });
163082815dcSEvan Bacon});
164082815dcSEvan Bacon
165082815dcSEvan Bacondescribe(addBlockedPermissions, () => {
166082815dcSEvan Bacon  it(`restricts an existing permission`, () => {
167082815dcSEvan Bacon    expect(
168082815dcSEvan Bacon      addBlockedPermissions(
169082815dcSEvan Bacon        {
170082815dcSEvan Bacon          manifest: {
171082815dcSEvan Bacon            $: {
172082815dcSEvan Bacon              'xmlns:android': '...',
173082815dcSEvan Bacon            },
174082815dcSEvan Bacon            'uses-permission': [
175082815dcSEvan Bacon              {
1768828a4a3SCedric van Putten                $: { 'android:name': 'dev.expo.foobar' },
177082815dcSEvan Bacon              },
178082815dcSEvan Bacon              {
1798828a4a3SCedric van Putten                $: { 'android:name': 'dev.expo.foobar-2' },
180082815dcSEvan Bacon              },
181082815dcSEvan Bacon            ],
182082815dcSEvan Bacon          },
183082815dcSEvan Bacon        },
1848828a4a3SCedric van Putten        ['dev.expo.foobar']
185082815dcSEvan Bacon      ).manifest['uses-permission']
186082815dcSEvan Bacon    ).toStrictEqual([
187082815dcSEvan Bacon      {
1888828a4a3SCedric van Putten        $: { 'android:name': 'dev.expo.foobar-2' },
189082815dcSEvan Bacon      },
190082815dcSEvan Bacon      {
1918828a4a3SCedric van Putten        $: { 'android:name': 'dev.expo.foobar', 'tools:node': 'remove' },
192082815dcSEvan Bacon      },
193082815dcSEvan Bacon    ]);
194082815dcSEvan Bacon  });
195082815dcSEvan Bacon
196082815dcSEvan Bacon  it(`restricts a new permission`, () => {
197082815dcSEvan Bacon    expect(
198082815dcSEvan Bacon      addBlockedPermissions(
199082815dcSEvan Bacon        {
200082815dcSEvan Bacon          manifest: {
201082815dcSEvan Bacon            $: {
202082815dcSEvan Bacon              'xmlns:android': '...',
203082815dcSEvan Bacon            },
204082815dcSEvan Bacon            'uses-permission': [],
205082815dcSEvan Bacon          },
206082815dcSEvan Bacon        },
2078828a4a3SCedric van Putten        ['dev.expo.foobar']
208082815dcSEvan Bacon      ).manifest['uses-permission']
209082815dcSEvan Bacon    ).toStrictEqual([
210082815dcSEvan Bacon      {
2118828a4a3SCedric van Putten        $: { 'android:name': 'dev.expo.foobar', 'tools:node': 'remove' },
212082815dcSEvan Bacon      },
213082815dcSEvan Bacon    ]);
214082815dcSEvan Bacon  });
215082815dcSEvan Bacon});
216082815dcSEvan Bacon
217082815dcSEvan Bacondescribe('Android permissions', () => {
218082815dcSEvan Bacon  it(`returns empty array if no android permissions key is provided`, () => {
219082815dcSEvan Bacon    expect(getAndroidPermissions({})).toMatchObject([]);
220082815dcSEvan Bacon  });
221082815dcSEvan Bacon
222082815dcSEvan Bacon  it(`returns android permissions if array is provided`, () => {
223082815dcSEvan Bacon    expect(
224082815dcSEvan Bacon      getAndroidPermissions({ android: { permissions: ['CAMERA', 'RECORD_AUDIO'] } })
225082815dcSEvan Bacon    ).toMatchObject(['CAMERA', 'RECORD_AUDIO']);
226082815dcSEvan Bacon  });
227082815dcSEvan Bacon
228082815dcSEvan Bacon  it('adds permissions if not present, does not duplicate permissions', async () => {
229082815dcSEvan Bacon    const givenPermissions = [
230082815dcSEvan Bacon      'android.permission.READ_CONTACTS',
231082815dcSEvan Bacon      'com.android.launcher.permission.INSTALL_SHORTCUT',
232082815dcSEvan Bacon      'com.android.launcher.permission.INSTALL_SHORTCUT',
233082815dcSEvan Bacon    ];
234*ed3bd27bSEvan Bacon    let androidManifestJson = await getFixtureManifestAsync();
235082815dcSEvan Bacon    androidManifestJson = await setAndroidPermissions(
236082815dcSEvan Bacon      { android: { permissions: givenPermissions } },
237082815dcSEvan Bacon      androidManifestJson
238082815dcSEvan Bacon    );
239082815dcSEvan Bacon
240082815dcSEvan Bacon    const manifestPermissionsJSON = androidManifestJson.manifest['uses-permission'];
241*ed3bd27bSEvan Bacon    const manifestPermissions = manifestPermissionsJSON!.map((e) => e.$['android:name']);
242082815dcSEvan Bacon
243082815dcSEvan Bacon    // Account for INTERNET permission in fixture
244082815dcSEvan Bacon    // No duplicates
245082815dcSEvan Bacon    expect(manifestPermissions).toStrictEqual([
246082815dcSEvan Bacon      'android.permission.INTERNET',
247*ed3bd27bSEvan Bacon
248082815dcSEvan Bacon      'android.permission.READ_CONTACTS',
249082815dcSEvan Bacon      'com.android.launcher.permission.INSTALL_SHORTCUT',
250082815dcSEvan Bacon    ]);
251082815dcSEvan Bacon    expect(
252082815dcSEvan Bacon      manifestPermissions.filter((e) => e === 'com.android.launcher.permission.INSTALL_SHORTCUT')
253082815dcSEvan Bacon    ).toHaveLength(1);
254082815dcSEvan Bacon  });
255082815dcSEvan Bacon});
256082815dcSEvan Bacon
257082815dcSEvan Bacondescribe('Permissions', () => {
258082815dcSEvan Bacon  it(`adds a new permission`, async () => {
259*ed3bd27bSEvan Bacon    const manifest = await getFixtureManifestAsync();
260082815dcSEvan Bacon    const didAdd = ensurePermission(manifest, 'EXPO_TEST_PERMISSION');
261082815dcSEvan Bacon    const permissions = getPermissions(manifest);
262082815dcSEvan Bacon    expect(didAdd).toBe(true);
263082815dcSEvan Bacon    expect(permissions).toContain('android.permission.EXPO_TEST_PERMISSION');
264082815dcSEvan Bacon    expect(permissions.length).toBe(2);
265082815dcSEvan Bacon  });
266082815dcSEvan Bacon
267082815dcSEvan Bacon  it(`prevents adding a duplicate permission`, async () => {
268*ed3bd27bSEvan Bacon    const manifest = await getFixtureManifestAsync();
269082815dcSEvan Bacon    const didAdd = ensurePermission(manifest, 'INTERNET');
270082815dcSEvan Bacon    const permissions = getPermissions(manifest);
271082815dcSEvan Bacon    expect(didAdd).toBe(false);
272082815dcSEvan Bacon    expect(permissions).toContain('android.permission.INTERNET');
273082815dcSEvan Bacon    expect(permissions.length).toBe(1);
274082815dcSEvan Bacon  });
275082815dcSEvan Bacon
276082815dcSEvan Bacon  it(`ensures multiple permissions`, async () => {
277*ed3bd27bSEvan Bacon    const manifest = await getFixtureManifestAsync();
278082815dcSEvan Bacon    const permissionsToAdd = ['VALUE_1', 'VALUE_2'];
279082815dcSEvan Bacon    const results = ensurePermissions(manifest, permissionsToAdd);
280082815dcSEvan Bacon    expect(results).toMatchSnapshot();
281082815dcSEvan Bacon    expect(Object.values(results)).toStrictEqual([true, true]);
282082815dcSEvan Bacon
283082815dcSEvan Bacon    expect(getPermissions(manifest).length).toBe(3);
284082815dcSEvan Bacon  });
285082815dcSEvan Bacon
286082815dcSEvan Bacon  it(`removes permissions by name`, async () => {
287*ed3bd27bSEvan Bacon    const manifest = await getFixtureManifestAsync();
288082815dcSEvan Bacon    expect(ensurePermission(manifest, 'VALUE_TO_REMOVE_1')).toBe(true);
289082815dcSEvan Bacon    expect(ensurePermission(manifest, 'VALUE_TO_REMOVE_2')).toBe(true);
290082815dcSEvan Bacon    expect(getPermissions(manifest).length).toBe(3);
291082815dcSEvan Bacon
292082815dcSEvan Bacon    removePermissions(manifest, ['VALUE_TO_REMOVE_1', 'VALUE_TO_REMOVE_2']);
293082815dcSEvan Bacon    expect(getPermissions(manifest).length).toBe(1);
294082815dcSEvan Bacon  });
295082815dcSEvan Bacon
296082815dcSEvan Bacon  it(`removes all permissions`, async () => {
297*ed3bd27bSEvan Bacon    const manifest = await getFixtureManifestAsync();
298082815dcSEvan Bacon    expect(ensurePermission(manifest, 'VALUE_TO_REMOVE_1')).toBe(true);
299082815dcSEvan Bacon    expect(ensurePermission(manifest, 'VALUE_TO_REMOVE_2')).toBe(true);
300082815dcSEvan Bacon    expect(getPermissions(manifest).length).toBe(3);
301082815dcSEvan Bacon
302082815dcSEvan Bacon    removePermissions(manifest);
303082815dcSEvan Bacon
304082815dcSEvan Bacon    expect(getPermissions(manifest).length).toBe(0);
305082815dcSEvan Bacon  });
306082815dcSEvan Bacon
307082815dcSEvan Bacon  it(`can write with a pretty format`, async () => {
308*ed3bd27bSEvan Bacon    const manifest = await getFixtureManifestAsync();
309082815dcSEvan Bacon    expect(ensurePermission(manifest, 'NEW_PERMISSION_1')).toBe(true);
310082815dcSEvan Bacon    expect(ensurePermission(manifest, 'NEW_PERMISSION_2')).toBe(true);
311082815dcSEvan Bacon    expect(getPermissions(manifest).length).toBe(3);
312082815dcSEvan Bacon
313082815dcSEvan Bacon    expect(format(manifest)).toMatchSnapshot();
314082815dcSEvan Bacon  });
315082815dcSEvan Bacon});
316