1082815dcSEvan Baconimport * as fs from 'fs';
2082815dcSEvan Baconimport { vol } from 'memfs';
3082815dcSEvan Baconimport * as path from 'path';
4082815dcSEvan Bacon
5*97e1898cSEvan Baconimport { getConfig } from '../Config';
6082815dcSEvan Bacon
7082815dcSEvan Baconconst fsReal = jest.requireActual('fs') as typeof fs;
8082815dcSEvan Bacon
9082815dcSEvan Baconjest.mock('fs');
10082815dcSEvan Bacon
11082815dcSEvan Bacondescribe(getConfig, () => {
12*97e1898cSEvan Bacon  afterAll(() => {
13*97e1898cSEvan Bacon    vol.reset();
14*97e1898cSEvan Bacon  });
15*97e1898cSEvan Bacon
16*97e1898cSEvan Bacon  // Tests the following:
17*97e1898cSEvan Bacon  // - All supported languages are working
18*97e1898cSEvan Bacon  // - ensure `app.config` has higher priority to `app`
19*97e1898cSEvan Bacon  // - generated `.expo` object is created and the language hint is added
20*97e1898cSEvan Bacon  describe('language support', () => {
21*97e1898cSEvan Bacon    beforeEach(() => {
22*97e1898cSEvan Bacon      delete process.env.EXPO_DEBUG;
23*97e1898cSEvan Bacon      vol.reset();
24*97e1898cSEvan Bacon    });
25*97e1898cSEvan Bacon    it('parses a ts config', () => {
26*97e1898cSEvan Bacon      vol.fromJSON(
27*97e1898cSEvan Bacon        {
28*97e1898cSEvan Bacon          // Read this TS file so we can validate the types
29*97e1898cSEvan Bacon          'app.config.ts': fsReal.readFileSync(
30*97e1898cSEvan Bacon            path.join(__dirname, './fixtures/ts/app.config.ts'),
31*97e1898cSEvan Bacon            'utf8'
32*97e1898cSEvan Bacon          ),
33*97e1898cSEvan Bacon          'package.json': JSON.stringify({
34*97e1898cSEvan Bacon            name: 'ts-config-test',
35*97e1898cSEvan Bacon            version: '1.0.0',
36*97e1898cSEvan Bacon          }),
37*97e1898cSEvan Bacon        },
38*97e1898cSEvan Bacon        '/'
39*97e1898cSEvan Bacon      );
40*97e1898cSEvan Bacon
41*97e1898cSEvan Bacon      const { exp } = getConfig('/', {
42*97e1898cSEvan Bacon        skipSDKVersionRequirement: true,
43*97e1898cSEvan Bacon      });
44*97e1898cSEvan Bacon      // @ts-ignore: foo property is not defined
45*97e1898cSEvan Bacon      expect(exp.foo).toBe('bar+value');
46*97e1898cSEvan Bacon      expect(exp.name).toBe('rewrote+ts-config-test');
47*97e1898cSEvan Bacon      expect(exp._internal).toStrictEqual({
48*97e1898cSEvan Bacon        dynamicConfigPath: '/app.config.ts',
49*97e1898cSEvan Bacon        isDebug: false,
50*97e1898cSEvan Bacon        packageJsonPath: '/package.json',
51*97e1898cSEvan Bacon        projectRoot: '/',
52*97e1898cSEvan Bacon        staticConfigPath: null,
53*97e1898cSEvan Bacon      });
54*97e1898cSEvan Bacon    });
55*97e1898cSEvan Bacon    it('parses a js config', () => {
56082815dcSEvan Bacon      vol.fromJSON(
57082815dcSEvan Bacon        {
58082815dcSEvan Bacon          'app.json': JSON.stringify({
59082815dcSEvan Bacon            expo: {
60082815dcSEvan Bacon              foo: 'invalid',
61082815dcSEvan Bacon              slug: 'someslug',
62082815dcSEvan Bacon            },
63082815dcSEvan Bacon          }),
64082815dcSEvan Bacon          'package.json': JSON.stringify({
65082815dcSEvan Bacon            name: 'js-config-test',
66082815dcSEvan Bacon            version: '1.0.0',
67082815dcSEvan Bacon          }),
68082815dcSEvan Bacon          // Config exporting a function on the module.exports object
69082815dcSEvan Bacon          'app.config.js': `module.exports = function ({ config }) {
70082815dcSEvan Bacon            config.foo = 'bar';
71082815dcSEvan Bacon            if (config.name) config.name += '+config';
72082815dcSEvan Bacon            if (config.slug) config.slug += '+config';
73082815dcSEvan Bacon            return config;
74082815dcSEvan Bacon          };`,
75*97e1898cSEvan Bacon        },
76*97e1898cSEvan Bacon        '/'
77*97e1898cSEvan Bacon      );
78*97e1898cSEvan Bacon
79*97e1898cSEvan Bacon      // ensure config is composed (package.json values still exist)
80*97e1898cSEvan Bacon      const { exp, dynamicConfigPath, staticConfigPath } = getConfig('/', {
81*97e1898cSEvan Bacon        skipSDKVersionRequirement: true,
82*97e1898cSEvan Bacon      });
83*97e1898cSEvan Bacon      expect(dynamicConfigPath).toBe('/app.config.js');
84*97e1898cSEvan Bacon      expect(staticConfigPath).toBe('/app.json');
85*97e1898cSEvan Bacon
86*97e1898cSEvan Bacon      // @ts-ignore: foo property is not defined
87*97e1898cSEvan Bacon      expect(exp.foo).toBe('bar');
88*97e1898cSEvan Bacon      // Ensure the config is passed the package.json values
89*97e1898cSEvan Bacon      expect(exp.name).toBe('js-config-test+config');
90*97e1898cSEvan Bacon      // Ensures that the app.json is read and passed to the method
91*97e1898cSEvan Bacon      expect(exp.slug).toBe('someslug+config');
92*97e1898cSEvan Bacon      expect(exp._internal).toStrictEqual({
93*97e1898cSEvan Bacon        dynamicConfigPath: '/app.config.js',
94*97e1898cSEvan Bacon        isDebug: false,
95*97e1898cSEvan Bacon        packageJsonPath: '/package.json',
96*97e1898cSEvan Bacon        projectRoot: '/',
97*97e1898cSEvan Bacon        staticConfigPath: '/app.json',
98*97e1898cSEvan Bacon      });
99*97e1898cSEvan Bacon    });
100*97e1898cSEvan Bacon    it('parses a js config with export default', () => {
101*97e1898cSEvan Bacon      vol.fromJSON(
102*97e1898cSEvan Bacon        {
103*97e1898cSEvan Bacon          'package.json': JSON.stringify({
104*97e1898cSEvan Bacon            name: 'js-config-test',
105*97e1898cSEvan Bacon            version: '1.0.0',
106*97e1898cSEvan Bacon          }),
107082815dcSEvan Bacon          // Config exporting a function as default
108*97e1898cSEvan Bacon          'app.config.js': `export default function ({ config }) {
109082815dcSEvan Bacon            config.foo = 'bar';
110082815dcSEvan Bacon            if (config.name) config.name += '+config-default';
111082815dcSEvan Bacon            return config;
112082815dcSEvan Bacon          }`,
113*97e1898cSEvan Bacon        },
114*97e1898cSEvan Bacon        '/'
115*97e1898cSEvan Bacon      );
116*97e1898cSEvan Bacon      const { exp, staticConfigPath } = getConfig('/', {
117*97e1898cSEvan Bacon        skipSDKVersionRequirement: true,
118*97e1898cSEvan Bacon      });
119*97e1898cSEvan Bacon      // @ts-ignore: foo property is not defined
120*97e1898cSEvan Bacon      expect(exp.foo).toBe('bar');
121*97e1898cSEvan Bacon      expect(exp.name).toBe('js-config-test+config-default');
122*97e1898cSEvan Bacon      // Static is undefined when a custom path is a dynamic config.
123*97e1898cSEvan Bacon      expect(staticConfigPath).toBe(null);
124*97e1898cSEvan Bacon    });
125*97e1898cSEvan Bacon    it('parses a js config that exports json', () => {
126*97e1898cSEvan Bacon      vol.fromJSON(
127*97e1898cSEvan Bacon        {
128*97e1898cSEvan Bacon          'package.json': JSON.stringify({
129*97e1898cSEvan Bacon            name: 'js-config-test',
130*97e1898cSEvan Bacon            version: '1.0.0',
131*97e1898cSEvan Bacon          }),
132082815dcSEvan Bacon          // Config exporting an object (JSON)
133*97e1898cSEvan Bacon          'app.config.js': `module.exports = {
134082815dcSEvan Bacon            foo: 'bar',
135082815dcSEvan Bacon            name: 'cool+export-json_app.config',
136082815dcSEvan Bacon          };`,
137082815dcSEvan Bacon        },
138*97e1898cSEvan Bacon        '/'
139082815dcSEvan Bacon      );
140*97e1898cSEvan Bacon
141*97e1898cSEvan Bacon      const { exp } = getConfig('/', {
142*97e1898cSEvan Bacon        skipSDKVersionRequirement: true,
143*97e1898cSEvan Bacon      });
144*97e1898cSEvan Bacon      // @ts-ignore: foo property is not defined
145*97e1898cSEvan Bacon      expect(exp.foo).toBe('bar');
146*97e1898cSEvan Bacon      expect(exp.name).toBe('cool+export-json_app.config');
147*97e1898cSEvan Bacon    });
148*97e1898cSEvan Bacon  });
149*97e1898cSEvan Bacon
150*97e1898cSEvan Bacon  describe('behavior', () => {
151*97e1898cSEvan Bacon    beforeEach(() => {
152*97e1898cSEvan Bacon      delete process.env.EXPO_DEBUG;
153*97e1898cSEvan Bacon      vol.reset();
154*97e1898cSEvan Bacon    });
155*97e1898cSEvan Bacon
156*97e1898cSEvan Bacon    it(`skips plugin parsing`, () => {
157082815dcSEvan Bacon      vol.fromJSON(
158082815dcSEvan Bacon        {
159082815dcSEvan Bacon          'app.json': JSON.stringify({
160082815dcSEvan Bacon            expo: {
161082815dcSEvan Bacon              name: 'app-expo-name',
162082815dcSEvan Bacon              plugins: ['__missing-plugin'],
163082815dcSEvan Bacon            },
164082815dcSEvan Bacon          }),
165082815dcSEvan Bacon          'package.json': JSON.stringify({
166082815dcSEvan Bacon            version: '1.0.0',
167082815dcSEvan Bacon          }),
168082815dcSEvan Bacon        },
169*97e1898cSEvan Bacon        '/'
170082815dcSEvan Bacon      );
171*97e1898cSEvan Bacon      const { exp } = getConfig('/', {
172*97e1898cSEvan Bacon        skipSDKVersionRequirement: true,
173*97e1898cSEvan Bacon        skipPlugins: true,
174*97e1898cSEvan Bacon      });
175*97e1898cSEvan Bacon      expect(exp.plugins).toBeUndefined();
176*97e1898cSEvan Bacon    });
177*97e1898cSEvan Bacon    it(`skips JS plugin parsing`, () => {
178082815dcSEvan Bacon      vol.fromJSON(
179082815dcSEvan Bacon        {
180082815dcSEvan Bacon          'app.config.js': `module.exports = {
181082815dcSEvan Bacon            foo: 'bar',
182082815dcSEvan Bacon            name: 'cool+export-json_app.config',
183082815dcSEvan Bacon            plugins: [(config)=> { config.name ='custom'; return config; }]
184082815dcSEvan Bacon          };`,
185082815dcSEvan Bacon          'package.json': JSON.stringify({
186082815dcSEvan Bacon            version: '1.0.0',
187082815dcSEvan Bacon          }),
188082815dcSEvan Bacon        },
189*97e1898cSEvan Bacon        '/'
190082815dcSEvan Bacon      );
191*97e1898cSEvan Bacon      const { exp } = getConfig('/', {
192082815dcSEvan Bacon        skipSDKVersionRequirement: true,
193082815dcSEvan Bacon        skipPlugins: true,
194082815dcSEvan Bacon      });
195082815dcSEvan Bacon      expect(exp.name).toBe('cool+export-json_app.config');
196082815dcSEvan Bacon      expect(exp.plugins).toBeUndefined();
197082815dcSEvan Bacon    });
198082815dcSEvan Bacon    it(`applies JS plugins`, () => {
199*97e1898cSEvan Bacon      vol.fromJSON(
200*97e1898cSEvan Bacon        {
201*97e1898cSEvan Bacon          'app.config.js': `module.exports = {
202*97e1898cSEvan Bacon            foo: 'bar',
203*97e1898cSEvan Bacon            name: 'cool+export-json_app.config',
204*97e1898cSEvan Bacon            plugins: [(config)=> { config.name ='custom'; return config; }]
205*97e1898cSEvan Bacon          };`,
206*97e1898cSEvan Bacon          'package.json': JSON.stringify({
207*97e1898cSEvan Bacon            version: '1.0.0',
208*97e1898cSEvan Bacon          }),
209*97e1898cSEvan Bacon        },
210*97e1898cSEvan Bacon        '/'
211*97e1898cSEvan Bacon      );
212*97e1898cSEvan Bacon      const { exp } = getConfig('/', {
213082815dcSEvan Bacon        skipSDKVersionRequirement: true,
214082815dcSEvan Bacon        skipPlugins: false,
215082815dcSEvan Bacon      });
216082815dcSEvan Bacon      expect(exp.name).toBe('custom');
217082815dcSEvan Bacon      expect(exp.plugins).toBeDefined();
218082815dcSEvan Bacon    });
219082815dcSEvan Bacon    it(`throws when plugins are missing`, () => {
220*97e1898cSEvan Bacon      vol.fromJSON(
221*97e1898cSEvan Bacon        {
222*97e1898cSEvan Bacon          'app.json': JSON.stringify({
223*97e1898cSEvan Bacon            expo: {
224*97e1898cSEvan Bacon              name: 'app-expo-name',
225*97e1898cSEvan Bacon              plugins: ['__missing-plugin'],
226*97e1898cSEvan Bacon            },
227*97e1898cSEvan Bacon          }),
228*97e1898cSEvan Bacon          'package.json': JSON.stringify({
229*97e1898cSEvan Bacon            version: '1.0.0',
230*97e1898cSEvan Bacon          }),
231*97e1898cSEvan Bacon        },
232*97e1898cSEvan Bacon        '/'
233*97e1898cSEvan Bacon      );
234082815dcSEvan Bacon      expect(() =>
235*97e1898cSEvan Bacon        getConfig('/', {
236082815dcSEvan Bacon          skipSDKVersionRequirement: true,
237082815dcSEvan Bacon          skipPlugins: false,
238082815dcSEvan Bacon        })
239*97e1898cSEvan Bacon      ).toThrow(/Failed to resolve plugin for module "__missing-plugin" relative to "\/"/);
240082815dcSEvan Bacon    });
241082815dcSEvan Bacon  });
242082815dcSEvan Bacon});
243