1import { resolve } from 'path'; 2 3import { format } from '../../utils/XML'; 4import { readAndroidManifestAsync } from '../Manifest'; 5import { 6 addBlockedPermissions, 7 ensurePermission, 8 ensurePermissions, 9 getAndroidPermissions, 10 getPermissions, 11 removePermissions, 12 setAndroidPermissions, 13 withInternalBlockedPermissions, 14} from '../Permissions'; 15 16const fixturesPath = resolve(__dirname, 'fixtures'); 17const sampleManifestPath = resolve(fixturesPath, 'react-native-AndroidManifest.xml'); 18 19function getMockAndroidManifest() { 20 return readAndroidManifestAsync(sampleManifestPath); 21} 22 23describe(withInternalBlockedPermissions, () => { 24 it(`adds blocked permissions`, async () => { 25 const config = withInternalBlockedPermissions({ 26 slug: '', 27 name: '', 28 android: { 29 blockedPermissions: ['android.permission.ACCESS_FINE_LOCATION', 'OTHER'], 30 }, 31 }); 32 33 const { modResults } = await (config as any).mods.android.manifest({ 34 modRequest: {}, 35 modResults: await getMockAndroidManifest(), 36 }); 37 38 expect(modResults).toEqual({ 39 manifest: { 40 $: { 41 'xmlns:android': expect.any(String), 42 package: expect.any(String), 43 // Added tools 44 'xmlns:tools': 'http://schemas.android.com/tools', 45 }, 46 'uses-permission': [ 47 expect.anything(), 48 // Added two blocked permissions 49 { 50 $: { 51 'android:name': 'android.permission.ACCESS_FINE_LOCATION', 52 'tools:node': 'remove', 53 }, 54 }, 55 { 56 $: { 57 'android:name': 'OTHER', 58 'tools:node': 'remove', 59 }, 60 }, 61 ], 62 queries: expect.anything(), 63 application: expect.anything(), 64 }, 65 }); 66 }); 67 68 it(`does not add tools if there are no blocked permissions`, async () => { 69 const config = withInternalBlockedPermissions({ 70 slug: '', 71 name: '', 72 android: { 73 blockedPermissions: [], 74 }, 75 }); 76 77 // Doesn't even add the mod 78 expect((config as any).mods).not.toBeDefined(); 79 }); 80}); 81 82describe(addBlockedPermissions, () => { 83 it(`restricts an existing permission`, () => { 84 expect( 85 addBlockedPermissions( 86 { 87 manifest: { 88 $: { 89 'xmlns:android': '...', 90 }, 91 'uses-permission': [ 92 { 93 $: { 'android:name': 'foobar' }, 94 }, 95 { 96 $: { 'android:name': 'foobar-2' }, 97 }, 98 ], 99 }, 100 }, 101 ['foobar'] 102 ).manifest['uses-permission'] 103 ).toStrictEqual([ 104 { 105 $: { 'android:name': 'foobar-2' }, 106 }, 107 { 108 $: { 'android:name': 'foobar', 'tools:node': 'remove' }, 109 }, 110 ]); 111 }); 112 113 it(`restricts a new permission`, () => { 114 expect( 115 addBlockedPermissions( 116 { 117 manifest: { 118 $: { 119 'xmlns:android': '...', 120 }, 121 'uses-permission': [], 122 }, 123 }, 124 ['foobar'] 125 ).manifest['uses-permission'] 126 ).toStrictEqual([ 127 { 128 $: { 'android:name': 'foobar', 'tools:node': 'remove' }, 129 }, 130 ]); 131 }); 132}); 133 134describe('Android permissions', () => { 135 it(`returns empty array if no android permissions key is provided`, () => { 136 expect(getAndroidPermissions({})).toMatchObject([]); 137 }); 138 139 it(`returns android permissions if array is provided`, () => { 140 expect( 141 getAndroidPermissions({ android: { permissions: ['CAMERA', 'RECORD_AUDIO'] } }) 142 ).toMatchObject(['CAMERA', 'RECORD_AUDIO']); 143 }); 144 145 it('adds permissions if not present, does not duplicate permissions', async () => { 146 const givenPermissions = [ 147 'android.permission.READ_CONTACTS', 148 'com.android.launcher.permission.INSTALL_SHORTCUT', 149 'com.android.launcher.permission.INSTALL_SHORTCUT', 150 ]; 151 let androidManifestJson = await readAndroidManifestAsync(sampleManifestPath); 152 androidManifestJson = await setAndroidPermissions( 153 { android: { permissions: givenPermissions } }, 154 androidManifestJson 155 ); 156 157 const manifestPermissionsJSON = androidManifestJson.manifest['uses-permission']; 158 const manifestPermissions = manifestPermissionsJSON.map((e) => e.$['android:name']); 159 160 // Account for INTERNET permission in fixture 161 // No duplicates 162 expect(manifestPermissions).toStrictEqual([ 163 'android.permission.INTERNET', 164 'android.permission.READ_CONTACTS', 165 'com.android.launcher.permission.INSTALL_SHORTCUT', 166 ]); 167 expect( 168 manifestPermissions.filter((e) => e === 'com.android.launcher.permission.INSTALL_SHORTCUT') 169 ).toHaveLength(1); 170 }); 171}); 172 173describe('Permissions', () => { 174 it(`adds a new permission`, async () => { 175 const manifest = await readAndroidManifestAsync(sampleManifestPath); 176 const didAdd = ensurePermission(manifest, 'EXPO_TEST_PERMISSION'); 177 const permissions = getPermissions(manifest); 178 expect(didAdd).toBe(true); 179 expect(permissions).toContain('android.permission.EXPO_TEST_PERMISSION'); 180 expect(permissions.length).toBe(2); 181 }); 182 183 it(`prevents adding a duplicate permission`, async () => { 184 const manifest = await readAndroidManifestAsync(sampleManifestPath); 185 const didAdd = ensurePermission(manifest, 'INTERNET'); 186 const permissions = getPermissions(manifest); 187 expect(didAdd).toBe(false); 188 expect(permissions).toContain('android.permission.INTERNET'); 189 expect(permissions.length).toBe(1); 190 }); 191 192 it(`ensures multiple permissions`, async () => { 193 const manifest = await readAndroidManifestAsync(sampleManifestPath); 194 const permissionsToAdd = ['VALUE_1', 'VALUE_2']; 195 const results = ensurePermissions(manifest, permissionsToAdd); 196 expect(results).toMatchSnapshot(); 197 expect(Object.values(results)).toStrictEqual([true, true]); 198 199 expect(getPermissions(manifest).length).toBe(3); 200 }); 201 202 it(`removes permissions by name`, async () => { 203 const manifest = await readAndroidManifestAsync(sampleManifestPath); 204 expect(ensurePermission(manifest, 'VALUE_TO_REMOVE_1')).toBe(true); 205 expect(ensurePermission(manifest, 'VALUE_TO_REMOVE_2')).toBe(true); 206 expect(getPermissions(manifest).length).toBe(3); 207 208 removePermissions(manifest, ['VALUE_TO_REMOVE_1', 'VALUE_TO_REMOVE_2']); 209 expect(getPermissions(manifest).length).toBe(1); 210 }); 211 212 it(`removes all permissions`, async () => { 213 const manifest = await readAndroidManifestAsync(sampleManifestPath); 214 expect(ensurePermission(manifest, 'VALUE_TO_REMOVE_1')).toBe(true); 215 expect(ensurePermission(manifest, 'VALUE_TO_REMOVE_2')).toBe(true); 216 expect(getPermissions(manifest).length).toBe(3); 217 218 removePermissions(manifest); 219 220 expect(getPermissions(manifest).length).toBe(0); 221 }); 222 223 it(`can write with a pretty format`, async () => { 224 const manifest = await readAndroidManifestAsync(sampleManifestPath); 225 expect(ensurePermission(manifest, 'NEW_PERMISSION_1')).toBe(true); 226 expect(ensurePermission(manifest, 'NEW_PERMISSION_2')).toBe(true); 227 expect(getPermissions(manifest).length).toBe(3); 228 229 expect(format(manifest)).toMatchSnapshot(); 230 }); 231}); 232