1/* eslint-env jest */
2import JsonFile from '@expo/json-file';
3import execa from 'execa';
4import fs from 'fs-extra';
5import klawSync from 'klaw-sync';
6import path from 'path';
7
8import {
9  execute,
10  projectRoot,
11  getLoadedModulesAsync,
12  setupTestProjectAsync,
13  bin,
14  ensurePortFreeAsync,
15} from './utils';
16
17const originalForceColor = process.env.FORCE_COLOR;
18const originalCI = process.env.CI;
19
20beforeAll(async () => {
21  await fs.mkdir(projectRoot, { recursive: true });
22  process.env.FORCE_COLOR = '0';
23  process.env.CI = '1';
24  process.env._EXPO_E2E_USE_PATH_ALIASES = '1';
25  delete process.env.EXPO_WEB_OUTPUT_MODE;
26});
27
28afterAll(() => {
29  process.env.FORCE_COLOR = originalForceColor;
30  process.env.CI = originalCI;
31  delete process.env._EXPO_E2E_USE_PATH_ALIASES;
32});
33
34it('loads expected modules by default', async () => {
35  const modules = await getLoadedModulesAsync(`require('../../build/src/export').expoExport`);
36  expect(modules).toStrictEqual([
37    '../node_modules/ansi-styles/index.js',
38    '../node_modules/arg/index.js',
39    '../node_modules/chalk/source/index.js',
40    '../node_modules/chalk/source/util.js',
41    '../node_modules/has-flag/index.js',
42    '../node_modules/supports-color/index.js',
43    '@expo/cli/build/src/export/index.js',
44    '@expo/cli/build/src/log.js',
45    '@expo/cli/build/src/utils/args.js',
46    '@expo/cli/build/src/utils/errors.js',
47  ]);
48});
49
50it('runs `npx expo export --help`', async () => {
51  const results = await execute('export', '--help');
52  expect(results.stdout).toMatchInlineSnapshot(`
53    "
54      Info
55        Export the static files of the app for hosting it on a web server
56
57      Usage
58        $ npx expo export <dir>
59
60      Options
61        <dir>                      Directory of the Expo project. Default: Current working directory
62        --dev                      Configure static files for developing locally using a non-https server
63        --output-dir <dir>         The directory to export the static files to. Default: dist
64        --max-workers <number>     Maximum number of tasks to allow the bundler to spawn
65        --dump-assetmap            Dump the asset map for further processing
66        --dump-sourcemap           Dump the source map for debugging the JS bundle
67        -p, --platform <platform>  Options: android, ios, web, all. Default: all
68        --no-minify                Prevent minifying source
69        -c, --clear                Clear the bundler cache
70        -h, --help                 Usage info
71    "
72  `);
73});
74
75describe('server', () => {
76  beforeEach(() => ensurePortFreeAsync(8081));
77  it(
78    'runs `npx expo export`',
79    async () => {
80      const projectRoot = await setupTestProjectAsync('basic-export', 'with-assets');
81      // `npx expo export`
82      await execa('node', [bin, 'export', '--dump-sourcemap', '--dump-assetmap'], {
83        cwd: projectRoot,
84      });
85
86      const outputDir = path.join(projectRoot, 'dist');
87      // List output files with sizes for snapshotting.
88      // This is to make sure that any changes to the output are intentional.
89      // Posix path formatting is used to make paths the same across OSes.
90      const files = klawSync(outputDir)
91        .map((entry) => {
92          if (entry.path.includes('node_modules') || !entry.stats.isFile()) {
93            return null;
94          }
95          return path.posix.relative(outputDir, entry.path);
96        })
97        .filter(Boolean);
98
99      const metadata = await JsonFile.readAsync(path.resolve(outputDir, 'metadata.json'));
100
101      expect(metadata).toEqual({
102        bundler: 'metro',
103        fileMetadata: {
104          android: {
105            assets: [
106              {
107                ext: 'png',
108                path: 'assets/fb960eb5e4eb49ec8786c7f6c4a57ce2',
109              },
110              {
111                ext: 'png',
112                path: 'assets/9ce7db807e4147e00df372d053c154c2',
113              },
114              {
115                ext: 'ttf',
116                path: 'assets/3858f62230ac3c915f300c664312c63f',
117              },
118            ],
119            bundle: expect.stringMatching(/bundles\/android-.*\.hbc/),
120          },
121          ios: {
122            assets: [
123              {
124                ext: 'png',
125                path: 'assets/fb960eb5e4eb49ec8786c7f6c4a57ce2',
126              },
127              {
128                ext: 'png',
129                path: 'assets/9ce7db807e4147e00df372d053c154c2',
130              },
131              {
132                ext: 'ttf',
133                path: 'assets/2f334f6c7ca5b2a504bdf8acdee104f3',
134              },
135            ],
136            bundle: expect.stringMatching(/bundles\/ios-.*\.hbc/),
137          },
138          web: {
139            assets: [
140              {
141                ext: 'png',
142                path: 'assets/fb960eb5e4eb49ec8786c7f6c4a57ce2',
143              },
144              {
145                ext: 'png',
146                path: 'assets/9ce7db807e4147e00df372d053c154c2',
147              },
148              {
149                ext: 'ttf',
150                path: 'assets/3858f62230ac3c915f300c664312c63f',
151              },
152            ],
153            bundle: expect.stringMatching(/bundles\/web-.*\.js/),
154          },
155        },
156        version: 0,
157      });
158
159      const assetmap = await JsonFile.readAsync(path.resolve(outputDir, 'assetmap.json'));
160      expect(assetmap).toEqual({
161        '2f334f6c7ca5b2a504bdf8acdee104f3': {
162          __packager_asset: true,
163          fileHashes: ['2f334f6c7ca5b2a504bdf8acdee104f3'],
164          fileSystemLocation: expect.stringMatching(/\/.*\/basic-export\/assets/),
165          files: [expect.stringMatching(/\/.*\/basic-export\/assets\/font\.ios\.ttf/)],
166          hash: '2f334f6c7ca5b2a504bdf8acdee104f3',
167          httpServerLocation: '/assets/assets',
168          name: 'font',
169          scales: [1],
170          type: 'ttf',
171        },
172
173        '3858f62230ac3c915f300c664312c63f': {
174          __packager_asset: true,
175          fileHashes: ['3858f62230ac3c915f300c664312c63f'],
176          fileSystemLocation: expect.stringMatching(/\/.*\/basic-export\/assets/),
177          files: [expect.stringMatching(/\/.*\/basic-export\/assets\/font\.ttf/)],
178          hash: '3858f62230ac3c915f300c664312c63f',
179          httpServerLocation: '/assets/assets',
180          name: 'font',
181          scales: [1],
182          type: 'ttf',
183        },
184        d48d481475a80809fcf9253a765193d1: {
185          __packager_asset: true,
186          fileHashes: ['fb960eb5e4eb49ec8786c7f6c4a57ce2', '9ce7db807e4147e00df372d053c154c2'],
187          fileSystemLocation: expect.stringMatching(/\/.*\/basic-export\/assets/),
188          files: [
189            expect.stringMatching(/\/.*\/basic-export\/assets\/icon\.png/),
190            expect.stringMatching(/\/.*\/basic-export\/assets\/icon@2x\.png/),
191          ],
192          hash: 'd48d481475a80809fcf9253a765193d1',
193          height: 1,
194          httpServerLocation: '/assets/assets',
195          name: 'icon',
196          scales: [1, 2],
197          type: 'png',
198          width: 1,
199        },
200      });
201
202      // If this changes then everything else probably changed as well.
203      expect(files).toEqual([
204        'assetmap.json',
205        'assets/2f334f6c7ca5b2a504bdf8acdee104f3',
206        'assets/3858f62230ac3c915f300c664312c63f',
207        'assets/9ce7db807e4147e00df372d053c154c2',
208        'assets/assets/font.ttf',
209        'assets/assets/icon.png',
210        'assets/assets/icon@2x.png',
211
212        'assets/fb960eb5e4eb49ec8786c7f6c4a57ce2',
213        expect.stringMatching(/bundles\/android-[\w\d]+\.hbc/),
214        expect.stringMatching(/bundles\/android-[\w\d]+\.map/),
215        expect.stringMatching(/bundles\/ios-[\w\d]+\.hbc/),
216        expect.stringMatching(/bundles\/ios-[\w\d]+\.map/),
217        expect.stringMatching(/bundles\/web-[\w\d]+\.js/),
218        expect.stringMatching(/bundles\/web-[\w\d]+\.map/),
219        'debug.html',
220        'drawable-mdpi/assets_icon.png',
221        'drawable-xhdpi/assets_icon.png',
222        'favicon.ico',
223        'index.html',
224        'metadata.json',
225        'raw/assets_font.ttf',
226      ]);
227    },
228    // Could take 45s depending on how fast npm installs
229    120 * 1000
230  );
231});
232