1import fs from 'fs';
2import { vol } from 'memfs';
3import path from 'path';
4
5import * as Updates from '../Updates';
6import { getPbxproj } from '../utils/Xcodeproj';
7
8const fsReal = jest.requireActual('fs') as typeof fs;
9jest.mock('fs');
10jest.mock('resolve-from');
11
12const { silent } = require('resolve-from');
13
14const fixturesPath = path.resolve(__dirname, 'fixtures');
15const sampleCodeSigningCertificatePath = path.resolve(fixturesPath, 'codeSigningCertificate.pem');
16
17describe('iOS Updates config', () => {
18  beforeEach(() => {
19    const resolveFrom = require('resolve-from');
20    resolveFrom.silent = silent;
21    vol.reset();
22  });
23
24  it('sets the correct values in Expo.plist', () => {
25    vol.fromJSON({
26      '/app/hello': fsReal.readFileSync(sampleCodeSigningCertificatePath, 'utf-8'),
27    });
28
29    expect(
30      Updates.setUpdatesConfig(
31        '/app',
32        {
33          sdkVersion: '37.0.0',
34          slug: 'my-app',
35          owner: 'owner',
36          updates: {
37            enabled: false,
38            fallbackToCacheTimeout: 2000,
39            checkAutomatically: 'ON_ERROR_RECOVERY',
40            codeSigningCertificate: 'hello',
41            codeSigningMetadata: {
42              alg: 'rsa-v1_5-sha256',
43              keyid: 'test',
44            },
45            requestHeaders: {
46              'expo-channel-name': 'test',
47              testheader: 'test',
48            },
49          },
50        },
51        {} as any,
52        'user',
53        '0.11.0'
54      )
55    ).toMatchObject({
56      EXUpdatesEnabled: false,
57      EXUpdatesURL: 'https://exp.host/@owner/my-app',
58      EXUpdatesCheckOnLaunch: 'ERROR_RECOVERY_ONLY',
59      EXUpdatesLaunchWaitMs: 2000,
60      EXUpdatesSDKVersion: '37.0.0',
61      EXUpdatesCodeSigningCertificate: fsReal.readFileSync(
62        sampleCodeSigningCertificatePath,
63        'utf-8'
64      ),
65      EXUpdatesCodeSigningMetadata: { alg: 'rsa-v1_5-sha256', keyid: 'test' },
66      EXUpdatesRequestHeaders: { 'expo-channel-name': 'test', testheader: 'test' },
67    });
68  });
69
70  describe(Updates.ensureBundleReactNativePhaseContainsConfigurationScript, () => {
71    beforeEach(() => {
72      vol.reset();
73      const resolveFrom = require('resolve-from');
74      resolveFrom.silent = silent;
75    });
76
77    it("adds create-manifest-ios.sh line to the 'Bundle React Native code and images' build phase ", () => {
78      vol.fromJSON(
79        {
80          'ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
81            path.join(__dirname, 'fixtures/project-without-create-manifest-ios.pbxproj'),
82            'utf-8'
83          ),
84          'node_modules/expo-updates/scripts/create-manifest-ios.sh': 'whatever',
85        },
86        '/app'
87      );
88
89      const xcodeProject = getPbxproj('/app');
90      Updates.ensureBundleReactNativePhaseContainsConfigurationScript('/app', xcodeProject);
91      const bundleReactNative = Updates.getBundleReactNativePhase(xcodeProject);
92      expect(bundleReactNative.shellScript).toMatchSnapshot();
93    });
94
95    it('fixes the path to create-manifest-ios.sh in case of a monorepo', () => {
96      // Pseudo node module resolution since actually mocking it could prove challenging.
97      // In a yarn workspace, resolve-from would be able to locate a module in any node_module folder if properly linked.
98      const resolveFrom = require('resolve-from');
99      resolveFrom.silent = (p, a) => {
100        return silent(path.join(p, '..'), a);
101      };
102
103      vol.fromJSON(
104        {
105          'workspace/ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
106            path.join(
107              __dirname,
108              'fixtures/project-with-incorrect-create-manifest-ios-path.pbxproj'
109            ),
110            'utf-8'
111          ),
112          'node_modules/expo-updates/scripts/create-manifest-ios.sh': 'whatever',
113        },
114        '/app'
115      );
116      const xcodeProject = getPbxproj('/app/workspace');
117      Updates.ensureBundleReactNativePhaseContainsConfigurationScript(
118        '/app/workspace',
119        xcodeProject
120      );
121      const bundleReactNative = Updates.getBundleReactNativePhase(xcodeProject);
122      expect(bundleReactNative.shellScript).toMatchSnapshot();
123    });
124  });
125});
126