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        '0.11.0'
53      )
54    ).toMatchObject({
55      EXUpdatesEnabled: false,
56      EXUpdatesCheckOnLaunch: 'ERROR_RECOVERY_ONLY',
57      EXUpdatesLaunchWaitMs: 2000,
58      EXUpdatesSDKVersion: '37.0.0',
59      EXUpdatesCodeSigningCertificate: fsReal.readFileSync(
60        sampleCodeSigningCertificatePath,
61        'utf-8'
62      ),
63      EXUpdatesCodeSigningMetadata: { alg: 'rsa-v1_5-sha256', keyid: 'test' },
64      EXUpdatesRequestHeaders: { 'expo-channel-name': 'test', testheader: 'test' },
65    });
66  });
67
68  describe(Updates.ensureBundleReactNativePhaseContainsConfigurationScript, () => {
69    beforeEach(() => {
70      vol.reset();
71      const resolveFrom = require('resolve-from');
72      resolveFrom.silent = silent;
73    });
74
75    it("adds create-manifest-ios.sh line to the 'Bundle React Native code and images' build phase ", () => {
76      vol.fromJSON(
77        {
78          'ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
79            path.join(__dirname, 'fixtures/project-without-create-manifest-ios.pbxproj'),
80            'utf-8'
81          ),
82          'node_modules/expo-updates/scripts/create-manifest-ios.sh': 'whatever',
83        },
84        '/app'
85      );
86
87      const xcodeProject = getPbxproj('/app');
88      Updates.ensureBundleReactNativePhaseContainsConfigurationScript('/app', xcodeProject);
89      const bundleReactNative = Updates.getBundleReactNativePhase(xcodeProject);
90      expect(bundleReactNative.shellScript).toMatchSnapshot();
91    });
92
93    it('fixes the path to create-manifest-ios.sh in case of a monorepo', () => {
94      // Pseudo node module resolution since actually mocking it could prove challenging.
95      // In a yarn workspace, resolve-from would be able to locate a module in any node_module folder if properly linked.
96      const resolveFrom = require('resolve-from');
97      resolveFrom.silent = (p, a) => {
98        return silent(path.join(p, '..'), a);
99      };
100
101      vol.fromJSON(
102        {
103          'workspace/ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
104            path.join(
105              __dirname,
106              'fixtures/project-with-incorrect-create-manifest-ios-path.pbxproj'
107            ),
108            'utf-8'
109          ),
110          'node_modules/expo-updates/scripts/create-manifest-ios.sh': 'whatever',
111        },
112        '/app'
113      );
114      const xcodeProject = getPbxproj('/app/workspace');
115      Updates.ensureBundleReactNativePhaseContainsConfigurationScript(
116        '/app/workspace',
117        xcodeProject
118      );
119      const bundleReactNative = Updates.getBundleReactNativePhase(xcodeProject);
120      expect(bundleReactNative.shellScript).toMatchSnapshot();
121    });
122  });
123});
124