1import { ExpoConfig } from '@expo/config-types';
2import { vol } from 'memfs';
3
4import { addWarningIOS } from '../../utils/warnings';
5import { createInfoPlistPluginWithPropertyGuard } from '../ios-plugins';
6import { evalModsAsync } from '../mod-compiler';
7import { getIosModFileProviders, withIosBaseMods } from '../withIosBaseMods';
8import rnFixture from './fixtures/react-native-project';
9
10jest.mock('../../utils/warnings', () => ({
11  addWarningIOS: jest.fn(),
12}));
13
14jest.mock('fs');
15
16export const asMock = <T extends (...args: any[]) => any>(fn: T): jest.MockedFunction<T> =>
17  fn as jest.MockedFunction<T>;
18describe(createInfoPlistPluginWithPropertyGuard, () => {
19  const projectRoot = '/app';
20
21  beforeEach(async () => {
22    asMock(addWarningIOS).mockClear();
23    vol.fromJSON(rnFixture, projectRoot);
24  });
25
26  afterEach(() => {
27    vol.reset();
28  });
29
30  it(`respects info plist manual values`, async () => {
31    const setter = jest.fn();
32    const withPlugin = createInfoPlistPluginWithPropertyGuard(setter, {
33      infoPlistProperty: 'CFFakeValue',
34      // Supports nesting
35      expoConfigProperty: 'ios.appStoreUrl',
36    });
37
38    let config: ExpoConfig = {
39      name: 'hey',
40      slug: '',
41      ios: {
42        appStoreUrl: 'underlying',
43        infoPlist: {
44          CFFakeValue: false,
45        },
46      },
47    };
48
49    config = withPlugin(config);
50
51    config = withIosBaseMods(config, {
52      providers: {
53        infoPlist: getIosModFileProviders().infoPlist,
54      },
55    });
56
57    const results = await evalModsAsync(config, {
58      projectRoot,
59      platforms: ['ios'],
60      introspect: true,
61      assertMissingModProviders: true,
62    });
63
64    expect(results.ios.infoPlist.CFFakeValue).toEqual(false);
65
66    expect(setter).not.toBeCalled();
67    expect(addWarningIOS).toBeCalledWith(
68      'ios.appStoreUrl',
69      '"ios.infoPlist.CFFakeValue" is set in the config. Ignoring abstract property "ios.appStoreUrl": underlying'
70    );
71  });
72
73  it(`does not warn about info plist overrides if the abstract value is not defined`, async () => {
74    const setter = jest.fn();
75    const withPlugin = createInfoPlistPluginWithPropertyGuard(setter, {
76      infoPlistProperty: 'CFFakeValue',
77      expoConfigProperty: 'foobar',
78    });
79
80    let config: ExpoConfig = {
81      name: 'hey',
82      slug: '',
83      ios: {
84        infoPlist: {
85          CFFakeValue: false,
86        },
87      },
88    };
89
90    config = withPlugin(config);
91
92    config = withIosBaseMods(config, {
93      providers: {
94        infoPlist: getIosModFileProviders().infoPlist,
95      },
96    });
97
98    const results = await evalModsAsync(config, {
99      projectRoot,
100      platforms: ['ios'],
101      introspect: true,
102      assertMissingModProviders: true,
103    });
104
105    expect(results.ios.infoPlist.CFFakeValue).toEqual(false);
106
107    expect(setter).not.toBeCalled();
108    expect(addWarningIOS).not.toBeCalled();
109  });
110
111  it(`uses default behavior when not overwritten`, async () => {
112    const setter = jest.fn();
113    const withPlugin = createInfoPlistPluginWithPropertyGuard(setter, {
114      infoPlistProperty: 'CFFakeValue',
115      expoConfigProperty: 'name',
116    });
117
118    let config: ExpoConfig = {
119      name: 'hey',
120      slug: '',
121      ios: {
122        infoPlist: {},
123      },
124    };
125
126    config = withPlugin(config);
127
128    config = withIosBaseMods(config, {
129      providers: {
130        infoPlist: getIosModFileProviders().infoPlist,
131      },
132    });
133
134    await evalModsAsync(config, {
135      projectRoot,
136      platforms: ['ios'],
137      introspect: true,
138      assertMissingModProviders: true,
139    });
140
141    expect(setter).toBeCalled();
142    expect(addWarningIOS).not.toBeCalled();
143  });
144});
145