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