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(
24      {
25        ...rnFixture,
26      },
27      projectRoot
28    );
29  });
30
31  afterEach(() => {
32    vol.reset();
33  });
34
35  it(`respects info plist manual values`, async () => {
36    const setter = jest.fn();
37    const withPlugin = createInfoPlistPluginWithPropertyGuard(setter, {
38      infoPlistProperty: 'CFFakeValue',
39      // Supports nesting
40      expoConfigProperty: 'ios.appStoreUrl',
41    });
42
43    let config: ExpoConfig = {
44      name: 'hey',
45      slug: '',
46      ios: {
47        appStoreUrl: 'underlying',
48        infoPlist: {
49          CFFakeValue: false,
50        },
51      },
52    };
53
54    config = withPlugin(config);
55
56    config = withIosBaseMods(config, {
57      providers: {
58        infoPlist: getIosModFileProviders().infoPlist,
59      },
60    });
61
62    const results = await evalModsAsync(config, {
63      projectRoot,
64      platforms: ['ios'],
65      introspect: true,
66      assertMissingModProviders: true,
67    });
68
69    expect(results.ios.infoPlist.CFFakeValue).toEqual(false);
70
71    expect(setter).not.toBeCalled();
72    expect(addWarningIOS).toBeCalledWith(
73      'ios.appStoreUrl',
74      '"ios.infoPlist.CFFakeValue" is set in the config. Ignoring abstract property "ios.appStoreUrl": underlying'
75    );
76  });
77
78  it(`does not warn about info plist overrides if the abstract value is not defined`, async () => {
79    const setter = jest.fn();
80    const withPlugin = createInfoPlistPluginWithPropertyGuard(setter, {
81      infoPlistProperty: 'CFFakeValue',
82      expoConfigProperty: 'foobar',
83    });
84
85    let config: ExpoConfig = {
86      name: 'hey',
87      slug: '',
88      ios: {
89        infoPlist: {
90          CFFakeValue: false,
91        },
92      },
93    };
94
95    config = withPlugin(config);
96
97    config = withIosBaseMods(config, {
98      providers: {
99        infoPlist: getIosModFileProviders().infoPlist,
100      },
101    });
102
103    const results = await evalModsAsync(config, {
104      projectRoot,
105      platforms: ['ios'],
106      introspect: true,
107      assertMissingModProviders: true,
108    });
109
110    expect(results.ios.infoPlist.CFFakeValue).toEqual(false);
111
112    expect(setter).not.toBeCalled();
113    expect(addWarningIOS).not.toBeCalled();
114  });
115
116  it(`uses default behavior when not overwritten`, async () => {
117    const setter = jest.fn();
118    const withPlugin = createInfoPlistPluginWithPropertyGuard(setter, {
119      infoPlistProperty: 'CFFakeValue',
120      expoConfigProperty: 'name',
121    });
122
123    let config: ExpoConfig = {
124      name: 'hey',
125      slug: '',
126      ios: {
127        infoPlist: {},
128      },
129    };
130
131    config = withPlugin(config);
132
133    config = withIosBaseMods(config, {
134      providers: {
135        infoPlist: getIosModFileProviders().infoPlist,
136      },
137    });
138
139    await evalModsAsync(config, {
140      projectRoot,
141      platforms: ['ios'],
142      introspect: true,
143      assertMissingModProviders: true,
144    });
145
146    expect(setter).toBeCalled();
147    expect(addWarningIOS).not.toBeCalled();
148  });
149});
150