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      EXUpdatesCheckOnLaunch: 'ERROR_RECOVERY_ONLY',
58      EXUpdatesLaunchWaitMs: 2000,
59      EXUpdatesSDKVersion: '37.0.0',
60      EXUpdatesCodeSigningCertificate: fsReal.readFileSync(
61        sampleCodeSigningCertificatePath,
62        'utf-8'
63      ),
64      EXUpdatesCodeSigningMetadata: { alg: 'rsa-v1_5-sha256', keyid: 'test' },
65      EXUpdatesRequestHeaders: { 'expo-channel-name': 'test', testheader: 'test' },
66    });
67  });
68
69  it('sets the correct values in Expo.plist for useClassicUpdates', () => {
70    vol.fromJSON({
71      '/app/hello': fsReal.readFileSync(sampleCodeSigningCertificatePath, 'utf-8'),
72    });
73
74    expect(
75      Updates.setUpdatesConfig(
76        '/app',
77        {
78          sdkVersion: '37.0.0',
79          slug: 'my-app',
80          owner: 'owner',
81          updates: {
82            useClassicUpdates: true,
83          },
84        },
85        {} as any,
86        'user',
87        '0.11.0'
88      )
89    ).toMatchObject({
90      EXUpdatesEnabled: true,
91      EXUpdatesURL: 'https://exp.host/@owner/my-app',
92    });
93  });
94
95  describe(Updates.ensureBundleReactNativePhaseContainsConfigurationScript, () => {
96    beforeEach(() => {
97      vol.reset();
98      const resolveFrom = require('resolve-from');
99      resolveFrom.silent = silent;
100    });
101
102    it("adds create-manifest-ios.sh line to the 'Bundle React Native code and images' build phase ", () => {
103      vol.fromJSON(
104        {
105          'ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
106            path.join(__dirname, 'fixtures/project-without-create-manifest-ios.pbxproj'),
107            'utf-8'
108          ),
109          'node_modules/expo-updates/scripts/create-manifest-ios.sh': 'whatever',
110        },
111        '/app'
112      );
113
114      const xcodeProject = getPbxproj('/app');
115      Updates.ensureBundleReactNativePhaseContainsConfigurationScript('/app', xcodeProject);
116      const bundleReactNative = Updates.getBundleReactNativePhase(xcodeProject);
117      expect(bundleReactNative.shellScript).toMatchSnapshot();
118    });
119
120    it('fixes the path to create-manifest-ios.sh in case of a monorepo', () => {
121      // Pseudo node module resolution since actually mocking it could prove challenging.
122      // In a yarn workspace, resolve-from would be able to locate a module in any node_module folder if properly linked.
123      const resolveFrom = require('resolve-from');
124      resolveFrom.silent = (p, a) => {
125        return silent(path.join(p, '..'), a);
126      };
127
128      vol.fromJSON(
129        {
130          'workspace/ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
131            path.join(
132              __dirname,
133              'fixtures/project-with-incorrect-create-manifest-ios-path.pbxproj'
134            ),
135            'utf-8'
136          ),
137          'node_modules/expo-updates/scripts/create-manifest-ios.sh': 'whatever',
138        },
139        '/app'
140      );
141      const xcodeProject = getPbxproj('/app/workspace');
142      Updates.ensureBundleReactNativePhaseContainsConfigurationScript(
143        '/app/workspace',
144        xcodeProject
145      );
146      const bundleReactNative = Updates.getBundleReactNativePhase(xcodeProject);
147      expect(bundleReactNative.shellScript).toMatchSnapshot();
148    });
149  });
150});
151