1import { ExpoConfig } from '@expo/config-types';
2import { fs, vol } from 'memfs';
3import * as path from 'path';
4
5import {
6  getNotificationColor,
7  getNotificationIcon,
8  setNotificationIconAsync,
9  setNotificationSounds,
10} from '../withNotificationsAndroid';
11
12export function getDirFromFS(fsJSON: Record<string, string | null>, rootDir: string) {
13  return Object.entries(fsJSON)
14    .filter(([path, value]) => value !== null && path.startsWith(rootDir))
15    .reduce<Record<string, string>>(
16      (acc, [path, fileContent]) => ({
17        ...acc,
18        [path.substring(rootDir.length).startsWith('/')
19          ? path.substring(rootDir.length + 1)
20          : path.substring(rootDir.length)]: fileContent,
21      }),
22      {}
23    );
24}
25
26const SAMPLE_COLORS_XML = `<?xml version="1.0" encoding="utf-8"?>
27    <resources>
28      <!-- Below line is handled by '@expo/configure-splash-screen' command and it's discouraged to modify it manually -->
29      <color name="splashscreen_background">#FFFFFF</color>
30    </resources>
31    `;
32
33jest.mock('fs');
34
35const fsReal = jest.requireActual('fs') as typeof fs;
36
37const LIST_OF_GENERATED_NOTIFICATION_FILES = [
38  'android/app/src/main/res/drawable-mdpi/notification_icon.png',
39  'android/app/src/main/res/drawable-hdpi/notification_icon.png',
40  'android/app/src/main/res/drawable-xhdpi/notification_icon.png',
41  'android/app/src/main/res/drawable-xxhdpi/notification_icon.png',
42  'android/app/src/main/res/drawable-xxxhdpi/notification_icon.png',
43  'android/app/src/main/res/values/colors.xml',
44  'assets/notificationIcon.png',
45  'assets/notificationSound.wav',
46  'android/app/src/main/res/raw/notificationSound.wav',
47];
48
49const iconPath = path.resolve(__dirname, './fixtures/icon.png');
50const soundPath = path.resolve(__dirname, './fixtures/cat.wav');
51
52const projectRoot = '/app';
53
54describe('Android notifications configuration', () => {
55  beforeAll(async () => {
56    const icon = fsReal.readFileSync(iconPath);
57    const sound = fsReal.readFileSync(soundPath);
58    vol.fromJSON(
59      { './android/app/src/main/res/values/colors.xml': SAMPLE_COLORS_XML },
60      projectRoot
61    );
62    setUpDrawableDirectories();
63    vol.mkdirpSync('/app/assets');
64    vol.writeFileSync('/app/assets/notificationIcon.png', icon);
65    vol.writeFileSync('/app/assets/notificationSound.wav', sound);
66  });
67
68  afterAll(() => {
69    jest.unmock('@expo/image-utils');
70    jest.unmock('fs');
71    vol.reset();
72  });
73
74  it(`returns null if no config provided`, () => {
75    expect(getNotificationIcon({} as ExpoConfig)).toBeNull();
76    expect(getNotificationColor({} as ExpoConfig)).toBeNull();
77  });
78
79  it(`returns config if provided`, () => {
80    expect(getNotificationIcon({ notification: { icon: './myIcon.png' } } as ExpoConfig)).toMatch(
81      './myIcon.png'
82    );
83    expect(getNotificationColor({ notification: { color: '#123456' } } as ExpoConfig)).toMatch(
84      '#123456'
85    );
86  });
87
88  it('writes all the asset files (sounds and images) as expected', async () => {
89    await setNotificationIconAsync(projectRoot, '/app/assets/notificationIcon.png');
90    setNotificationSounds(projectRoot, ['/app/assets/notificationSound.wav']);
91
92    const after = getDirFromFS(vol.toJSON(), projectRoot);
93    expect(Object.keys(after).sort()).toEqual(LIST_OF_GENERATED_NOTIFICATION_FILES.sort());
94  });
95});
96
97function setUpDrawableDirectories() {
98  vol.mkdirpSync('/app/android/app/src/main/res/drawable-mdpi');
99  vol.mkdirpSync('/app/android/app/src/main/res/drawable-hdpi');
100  vol.mkdirpSync('/app/android/app/src/main/res/drawable-xhdpi');
101  vol.mkdirpSync('/app/android/app/src/main/res/drawable-xxhdpi');
102  vol.mkdirpSync('/app/android/app/src/main/res/drawable-xxxhdpi');
103}
104