1import { AndroidConfig } from '@expo/config-plugins'; 2import { ExpoConfig } from '@expo/config-types'; 3import * as fs from 'fs'; 4import { vol } from 'memfs'; 5import * as path from 'path'; 6 7import { 8 ADAPTIVE_ICON_XML_WITH_BACKGROUND_COLOR, 9 ADAPTIVE_ICON_XML_WITH_BACKGROUND_COLOR_AND_MONOCHROME, 10 ADAPTIVE_ICON_XML_WITH_BOTH, 11 ADAPTIVE_ICON_XML_WITH_BOTH_AND_MONOCHROME, 12 LIST_OF_ANDROID_ADAPTIVE_ICON_FILES_FINAL, 13 SAMPLE_COLORS_XML, 14} from '../../__tests__/fixtures/androidIcons'; 15import { 16 createAdaptiveIconXmlString, 17 getAdaptiveIcon, 18 getIcon, 19 setIconAsync, 20 setRoundIconManifest, 21} from '../withAndroidIcons'; 22import { getDirFromFS } from './utils/getDirFromFS'; 23const { getMainApplicationOrThrow, readAndroidManifestAsync } = AndroidConfig.Manifest; 24 25const sampleManifestPath = path.resolve( 26 __dirname, 27 '../../__tests__/fixtures', 28 'react-native-AndroidManifest.xml' 29); 30const fsReal = jest.requireActual('fs') as typeof fs; 31 32jest.mock('fs'); 33 34function setUpMipmapDirectories() { 35 vol.mkdirpSync('/app/android/app/src/main/res/mipmap-mdpi'); 36 vol.mkdirpSync('/app/android/app/src/main/res/mipmap-hdpi'); 37 vol.mkdirpSync('/app/android/app/src/main/res/mipmap-xhdpi'); 38 vol.mkdirpSync('/app/android/app/src/main/res/mipmap-xxhdpi'); 39 vol.mkdirpSync('/app/android/app/src/main/res/mipmap-xxxhdpi'); 40} 41 42describe(setRoundIconManifest, () => { 43 vol.fromJSON({ './AndroidManifest.xml': fsReal.readFileSync(sampleManifestPath, 'utf-8') }); 44 it(`adds the round icon property when an adaptive icon is present`, async () => { 45 const manifest = await readAndroidManifestAsync('./AndroidManifest.xml'); 46 const results = setRoundIconManifest({ android: { adaptiveIcon: {} } }, manifest); 47 48 const app = getMainApplicationOrThrow(results); 49 expect(app.$['android:roundIcon']).toBe('@mipmap/ic_launcher_round'); 50 }); 51 it(`removes the round icon property when an adaptive icon is missing`, async () => { 52 const manifest = await readAndroidManifestAsync('./AndroidManifest.xml'); 53 const results = setRoundIconManifest({ android: {} }, manifest); 54 55 const app = getMainApplicationOrThrow(results); 56 expect(app.$['android:roundIcon']).not.toBeDefined(); 57 }); 58}); 59 60describe('Android Icon', () => { 61 it(`returns null if no icon values provided`, () => { 62 expect(getIcon({} as ExpoConfig)).toBeNull(); 63 expect(getAdaptiveIcon({} as ExpoConfig)).toMatchObject({ 64 foregroundImage: null, 65 backgroundColor: null, 66 backgroundImage: null, 67 }); 68 }); 69 70 it(`returns adaptive icon over android icon`, () => { 71 const config = { 72 icon: 'icon', 73 android: { 74 icon: 'androidIcon', 75 adaptiveIcon: { 76 foregroundImage: 'adaptiveIcon', 77 backgroundImage: 'backgroundImage', 78 backgroundColor: '#000000', 79 }, 80 }, 81 }; 82 const { foregroundImage, backgroundColor, backgroundImage } = getAdaptiveIcon( 83 config as ExpoConfig 84 ); 85 const icon = foregroundImage || getIcon(config as ExpoConfig); 86 expect(icon).toMatch('adaptiveIcon'); 87 expect(backgroundColor).toMatch('#000000'); 88 expect(backgroundImage).toMatch('backgroundImage'); 89 }); 90 91 it(`creates the proper AdaptiveIconXmlString`, () => { 92 const withBackgroundImage = createAdaptiveIconXmlString('path/to/image', null); 93 const withBackgroundColor = createAdaptiveIconXmlString(null, null); 94 const withBackgroundColorAndMonochrome = createAdaptiveIconXmlString(null, 'path/to/image'); 95 const withBoth = createAdaptiveIconXmlString('path/to/image', null); 96 const withBothAndMonochrome = createAdaptiveIconXmlString('path/to/image', 'path/to/image'); 97 98 expect(withBackgroundColor).toBe(ADAPTIVE_ICON_XML_WITH_BACKGROUND_COLOR); 99 expect(withBackgroundColorAndMonochrome).toBe( 100 ADAPTIVE_ICON_XML_WITH_BACKGROUND_COLOR_AND_MONOCHROME 101 ); 102 expect(withBackgroundImage).toBe(ADAPTIVE_ICON_XML_WITH_BOTH); 103 expect(withBoth).toBe(ADAPTIVE_ICON_XML_WITH_BOTH); 104 expect(withBothAndMonochrome).toBe(ADAPTIVE_ICON_XML_WITH_BOTH_AND_MONOCHROME); 105 }); 106 107 it('returns null if no icon config provided', async () => { 108 expect( 109 await setIconAsync('./', { 110 icon: null, 111 backgroundImage: null, 112 backgroundColor: null, 113 isAdaptive: false, 114 monochromeImage: null, 115 }) 116 ).toBe(null); 117 }); 118}); 119 120describe('e2e: ONLY android legacy icon', () => { 121 const legacyIconPath = path.resolve(__dirname, '../../__tests__/fixtures/icon.png'); 122 const projectRoot = '/app'; 123 const icon = require('../../icons/withAndroidIcons'); 124 const spyOnConfigureAdaptiveIconAsync = jest.spyOn(icon, 'configureAdaptiveIconAsync'); 125 beforeAll(async () => { 126 const icon = fsReal.readFileSync(legacyIconPath); 127 vol.fromJSON( 128 { './android/app/src/main/res/values/colors.xml': SAMPLE_COLORS_XML }, 129 projectRoot 130 ); 131 setUpMipmapDirectories(); 132 vol.mkdirpSync('/app/assets'); 133 vol.writeFileSync('/app/assets/iconForeground.png', icon); 134 135 await setIconAsync(projectRoot, { 136 icon: '/app/assets/iconForeground.png', 137 backgroundColor: null, 138 backgroundImage: null, 139 isAdaptive: true, 140 monochromeImage: null, 141 }); 142 }); 143 144 afterAll(() => { 145 vol.reset(); 146 }); 147 148 it('writes all the image files expected', async () => { 149 const after = getDirFromFS(vol.toJSON(), projectRoot); 150 Object.keys(after).forEach((path) => { 151 expect(LIST_OF_ANDROID_ADAPTIVE_ICON_FILES_FINAL).toContain(path); 152 }); 153 }); 154 155 it('Does not set adaptive icon config', () => { 156 expect(spyOnConfigureAdaptiveIconAsync).toHaveBeenCalledTimes(0); 157 }); 158}); 159 160describe('e2e: android adaptive icon', () => { 161 const adaptiveIconForegroundPath = path.resolve(__dirname, '../../__tests__/fixtures/icon.png'); 162 const adaptiveIconBackgroundPath = path.resolve(__dirname, '../../__tests__/fixtures/icon.png'); 163 const adaptiveIconMonochromePath = path.resolve(__dirname, '../../__tests__/fixtures/icon.png'); 164 const projectRoot = '/app'; 165 166 beforeAll(async () => { 167 const adaptiveIconForeground = fsReal.readFileSync(adaptiveIconForegroundPath); 168 const adaptiveIconBackground = fsReal.readFileSync(adaptiveIconBackgroundPath); 169 const adaptiveIconMonochrome = fsReal.readFileSync(adaptiveIconMonochromePath); 170 171 vol.fromJSON( 172 { './android/app/src/main/res/values/colors.xml': SAMPLE_COLORS_XML }, 173 projectRoot 174 ); 175 setUpMipmapDirectories(); 176 vol.mkdirpSync('/app/assets'); 177 vol.writeFileSync('/app/assets/iconForeground.png', adaptiveIconForeground); 178 vol.writeFileSync('/app/assets/iconBackground.png', adaptiveIconBackground); 179 vol.writeFileSync('/app/assets/iconMonochrome.png', adaptiveIconMonochrome); 180 181 await setIconAsync(projectRoot, { 182 icon: '/app/assets/iconForeground.png', 183 backgroundImage: '/app/assets/iconBackground.png', 184 backgroundColor: '#123456', 185 isAdaptive: true, 186 monochromeImage: '/app/assets/iconMonochrome.png', 187 }); 188 }); 189 190 afterAll(() => { 191 vol.reset(); 192 }); 193 194 it('writes all the image files expected', () => { 195 const after = getDirFromFS(vol.toJSON(), projectRoot); 196 expect(Object.keys(after)).toEqual(LIST_OF_ANDROID_ADAPTIVE_ICON_FILES_FINAL); 197 }); 198}); 199