1/* eslint-env jest */
2import execa from 'execa';
3import { constants as fsConstants } from 'fs';
4import fs from 'fs-extra';
5import klawSync from 'klaw-sync';
6import path from 'path';
7
8import { execute, projectRoot, getLoadedModulesAsync, setupTestProjectAsync, bin } from './utils';
9
10const originalForceColor = process.env.FORCE_COLOR;
11const originalCI = process.env.CI;
12const originalUseTypedRoutes = process.env._EXPO_E2E_USE_TYPED_ROUTES;
13
14const generatedFiles = ['tsconfig.json', 'expo-env.d.ts', '.expo/types/router.d.ts', '.gitignore'];
15
16beforeAll(async () => {
17  await fs.mkdir(projectRoot, { recursive: true });
18  process.env.FORCE_COLOR = '0';
19  process.env.CI = '1';
20  process.env._EXPO_E2E_USE_TYPED_ROUTES = '1';
21});
22
23afterAll(async () => {
24  process.env.FORCE_COLOR = originalForceColor;
25  process.env.CI = originalCI;
26  process.env._EXPO_E2E_USE_TYPED_ROUTES = originalUseTypedRoutes;
27
28  // Remove the generated files
29  await Promise.all(
30    generatedFiles.map((file) =>
31      fs.promises.rm(path.join(projectRoot, file), { recursive: true, force: true })
32    )
33  );
34});
35
36it('loads expected modules by default', async () => {
37  const modules = await getLoadedModulesAsync(`require('../../build/src/customize').expoCustomize`);
38  expect(modules).toStrictEqual([
39    '../node_modules/ansi-styles/index.js',
40    '../node_modules/arg/index.js',
41    '../node_modules/chalk/source/index.js',
42    '../node_modules/chalk/source/util.js',
43    '../node_modules/has-flag/index.js',
44    '../node_modules/supports-color/index.js',
45    '@expo/cli/build/src/customize/index.js',
46    '@expo/cli/build/src/log.js',
47    '@expo/cli/build/src/utils/args.js',
48  ]);
49});
50
51it('runs `npx expo customize --help`', async () => {
52  const results = await execute('customize', '--help');
53  expect(results.stdout).toMatchInlineSnapshot(`
54    "
55      Info
56        Generate static project files
57
58      Usage
59        $ npx expo customize [files...] -- [options]
60
61      Options
62        [files...]  List of files to generate
63        [options]   Options to pass to the install command
64        -h, --help  Usage info
65    "
66  `);
67});
68
69it(
70  'runs `npx expo customize`',
71  async () => {
72    const projectRoot = await setupTestProjectAsync('basic-customize', 'with-blank');
73    // `npx expo customize index.html serve.json babel.config.js`
74    await execa('node', [bin, 'customize', 'web/index.html', 'web/serve.json', 'babel.config.js'], {
75      cwd: projectRoot,
76    });
77
78    const files = klawSync(projectRoot)
79      .map((entry) => {
80        if (entry.path.includes('node_modules') || !entry.stats.isFile()) {
81          return null;
82        }
83        return path.posix.relative(projectRoot, entry.path);
84      })
85      .filter(Boolean);
86
87    expect(files).toEqual([
88      'App.js',
89      'app.json',
90      'babel.config.js',
91      'package.json',
92      'web/index.html',
93      'web/serve.json',
94      'yarn.lock',
95    ]);
96  },
97  // Could take 45s depending on how fast npm installs
98  120 * 1000
99);
100
101it(
102  'runs `npx expo customize tsconfig.json`',
103  async () => {
104    const projectRoot = await setupTestProjectAsync('expo-typescript', 'with-router', '48.0.0');
105
106    // `npx expo typescript
107    await execa('node', [bin, 'customize', 'tsconfig.json'], {
108      cwd: projectRoot,
109      // env: { NODE_OPTIONS: '--inspect-brk' },
110    });
111
112    // Expect them to exist with correct access controls
113    for (const file of generatedFiles) {
114      await expect(
115        fs.promises.access(path.join(projectRoot, file), fsConstants.F_OK)
116      ).resolves.toBeUndefined();
117    }
118  },
119  // Could take 45s depending on how fast npm installs
120  120 * 1000
121);
122
123it(
124  'runs `npx expo customize tsconfig.json` on a partially setup project',
125  async () => {
126    const projectRoot = await setupTestProjectAsync('expo-typescript', 'with-router', '48.0.0');
127
128    const existingTsConfig = {
129      extends: 'custom-package',
130      compilerOptions: {
131        strict: true,
132      },
133      customOption: true,
134      include: ['custom'],
135    };
136
137    // Write a tsconfig with partial data
138    await fs.promises.writeFile(
139      path.join(projectRoot, 'tsconfig.json'),
140      JSON.stringify(existingTsConfig)
141    );
142
143    // `npx expo typescript
144    const a = await execa('node', [bin, 'customize', 'tsconfig.json'], {
145      cwd: projectRoot,
146    });
147
148    const newTsconfig = await fs.promises.readFile(
149      path.join(projectRoot, 'tsconfig.json'),
150      'utf-8'
151    );
152
153    expect(JSON.parse(newTsconfig)).toEqual({
154      ...existingTsConfig,
155      include: ['custom', '.expo/types/**/*.ts', 'expo-env.d.ts'],
156    });
157  },
158  // Could take 45s depending on how fast npm installs
159  120 * 1000
160);
161