1const spawnAsync = require('@expo/spawn-async');
2const fs = require('fs/promises');
3const glob = require('glob');
4const path = require('path');
5
6const projectRoot = path.resolve(__dirname, '..');
7const notifyStrings = [
8  'test-update-1',
9  'test-update-2',
10  'test-update-3',
11  'test-update-invalid-hash',
12  'test-update-older',
13  'test-assets-1',
14];
15
16createTestUpdateBundles(projectRoot, notifyStrings);
17
18///////////////////////////////////////////////////////////////
19
20/**
21 * Originally, the E2E tests would directly modify the text of the minified JS in update bundles
22 * when testing to make sure that the correct update was applied.
23 *
24 * Since Hermes bundles are bytecode and not readable JS, we instead pre-generate Hermes bundles
25 * corresponding to each test case, and save them in the `test-update-bundles` directory in the test app.
26 */
27async function createTestUpdateBundles(projectRoot, notifyStrings) {
28  // export update for test server to host
29  await createUpdateBundleAsync(projectRoot);
30
31  // move exported update to "updates" directory for EAS testing
32  await fs.rm(path.join(projectRoot, 'updates'), { recursive: true, force: true });
33  await fs.rename(path.join(projectRoot, 'dist'), path.join(projectRoot, 'updates'));
34
35  const testUpdateBundlesPath = path.join(projectRoot, 'test-update-bundles');
36  await fs.rm(testUpdateBundlesPath, { recursive: true, force: true });
37  await fs.mkdir(testUpdateBundlesPath);
38  const appJsPath = path.join(projectRoot, 'App.tsx');
39  const originalAppJs = await fs.readFile(appJsPath, 'utf-8');
40  const testUpdateJson = {};
41  for (const notifyString of ['test', ...notifyStrings]) {
42    console.log(`Creating bundle for string '${notifyString}'...`);
43    const modifiedAppJs = originalAppJs.replace(
44      /testID="updateString" value="test"/g,
45      `testID="updateString" value="${notifyString}"`
46    );
47    await fs.rm(appJsPath);
48    await fs.writeFile(appJsPath, modifiedAppJs, 'utf-8');
49    await createUpdateBundleAsync(projectRoot);
50    const manifestJsonString = await fs.readFile(
51      path.join(projectRoot, 'dist', 'metadata.json'),
52      'utf-8'
53    );
54    const manifest = JSON.parse(manifestJsonString);
55    const iosBundlePath = path.join(projectRoot, 'dist', manifest.fileMetadata.ios.bundle);
56    const androidBundlePath = path.join(projectRoot, 'dist', manifest.fileMetadata.android.bundle);
57    const iosBundleDestPath = path.join(testUpdateBundlesPath, path.basename(iosBundlePath));
58    const androidBundleDestPath = path.join(
59      testUpdateBundlesPath,
60      path.basename(androidBundlePath)
61    );
62    await fs.copyFile(iosBundlePath, iosBundleDestPath);
63    await fs.copyFile(androidBundlePath, androidBundleDestPath);
64    testUpdateJson[notifyString] = {
65      ios: path.basename(iosBundlePath),
66      android: path.basename(androidBundlePath),
67    };
68  }
69  const testUpdateBundlesJsonPath = path.join(testUpdateBundlesPath, 'test-updates.json');
70  await fs.writeFile(testUpdateBundlesJsonPath, JSON.stringify(testUpdateJson, null, 2), 'utf-8');
71  await fs.rm(appJsPath);
72  await fs.writeFile(appJsPath, originalAppJs, 'utf-8');
73  console.log('Done creating test bundles');
74}
75
76async function createUpdateBundleAsync(projectRoot, localCliBin) {
77  await fs.rm(path.join(projectRoot, 'dist'), { force: true, recursive: true });
78  await spawnAsync('npx', ['expo', 'export'], {
79    cwd: projectRoot,
80    stdio: 'inherit',
81  });
82}
83