1082815dcSEvan Baconimport fs from 'fs';
2082815dcSEvan Baconimport { vol } from 'memfs';
3082815dcSEvan Baconimport path from 'path';
4082815dcSEvan Bacon
5082815dcSEvan Baconimport {
6082815dcSEvan Bacon  getNativeVersion,
7*f0d67e12SMateus Craveiro  getRuntimeVersionAsync,
8082815dcSEvan Bacon  getSDKVersion,
9082815dcSEvan Bacon  getUpdatesCheckOnLaunch,
10082815dcSEvan Bacon  getUpdatesCodeSigningCertificate,
11082815dcSEvan Bacon  getUpdatesCodeSigningMetadata,
12082815dcSEvan Bacon  getUpdatesCodeSigningMetadataStringified,
1309bd1012SUmberto Ghio  getUpdatesRequestHeaders,
1409bd1012SUmberto Ghio  getUpdatesRequestHeadersStringified,
15082815dcSEvan Bacon  getUpdatesEnabled,
16082815dcSEvan Bacon  getUpdatesTimeout,
17082815dcSEvan Bacon  getUpdateUrl,
18082815dcSEvan Bacon} from '../Updates';
19082815dcSEvan Bacon
20082815dcSEvan Baconconst fsReal = jest.requireActual('fs') as typeof fs;
21082815dcSEvan Baconjest.mock('fs');
22082815dcSEvan Baconjest.mock('resolve-from');
23082815dcSEvan Bacon
24082815dcSEvan Baconconst { silent } = require('resolve-from');
25082815dcSEvan Bacon
26082815dcSEvan Baconconst fixturesPath = path.resolve(__dirname, 'fixtures');
27082815dcSEvan Baconconst sampleCodeSigningCertificatePath = path.resolve(fixturesPath, 'codeSigningCertificate.pem');
28082815dcSEvan Bacon
29082815dcSEvan Baconconsole.warn = jest.fn();
30082815dcSEvan Bacon
31082815dcSEvan Bacondescribe('shared config getters', () => {
32082815dcSEvan Bacon  beforeEach(() => {
33082815dcSEvan Bacon    const resolveFrom = require('resolve-from');
34082815dcSEvan Bacon    resolveFrom.silent = silent;
35082815dcSEvan Bacon    vol.reset();
36082815dcSEvan Bacon  });
37082815dcSEvan Bacon
38082815dcSEvan Bacon  it(`returns correct default values from all getters if no value provided`, () => {
39082815dcSEvan Bacon    expect(getSDKVersion({})).toBe(null);
40082815dcSEvan Bacon    expect(getUpdatesCheckOnLaunch({})).toBe('ALWAYS');
41082815dcSEvan Bacon    expect(getUpdatesTimeout({})).toBe(0);
42082815dcSEvan Bacon    expect(getUpdatesCodeSigningCertificate('/app', {})).toBe(undefined);
43082815dcSEvan Bacon    expect(getUpdatesCodeSigningMetadata({})).toBe(undefined);
4409bd1012SUmberto Ghio    expect(getUpdatesRequestHeaders({})).toBe(undefined);
452fae8288SWill Schurman
4682ade864SWill Schurman    expect(getUpdatesEnabled({})).toBe(false);
4782ade864SWill Schurman    expect(getUpdatesEnabled({ updates: {} })).toBe(false);
48082815dcSEvan Bacon  });
49082815dcSEvan Bacon
50082815dcSEvan Bacon  it(`returns correct value from all getters if value provided`, () => {
51082815dcSEvan Bacon    vol.fromJSON({
52082815dcSEvan Bacon      '/app/hello': fsReal.readFileSync(sampleCodeSigningCertificatePath, 'utf-8'),
53082815dcSEvan Bacon    });
54082815dcSEvan Bacon
55082815dcSEvan Bacon    expect(getSDKVersion({ sdkVersion: '37.0.0' })).toBe('37.0.0');
56082815dcSEvan Bacon    expect(getUpdatesCheckOnLaunch({ updates: { checkAutomatically: 'ON_ERROR_RECOVERY' } })).toBe(
57082815dcSEvan Bacon      'NEVER'
58082815dcSEvan Bacon    );
59082815dcSEvan Bacon    expect(
60082815dcSEvan Bacon      getUpdatesCheckOnLaunch({ updates: { checkAutomatically: 'ON_ERROR_RECOVERY' } }, '0.11.0')
61082815dcSEvan Bacon    ).toBe('ERROR_RECOVERY_ONLY');
62082815dcSEvan Bacon    expect(
63082815dcSEvan Bacon      getUpdatesCheckOnLaunch({ updates: { checkAutomatically: 'ON_ERROR_RECOVERY' } }, '0.10.15')
64082815dcSEvan Bacon    ).toBe('NEVER');
65082815dcSEvan Bacon    expect(getUpdatesCheckOnLaunch({ updates: { checkAutomatically: 'ON_LOAD' } })).toBe('ALWAYS');
66b8b0e64dSDouglas Lowder    expect(getUpdatesCheckOnLaunch({ updates: { checkAutomatically: 'WIFI_ONLY' } })).toBe(
67b8b0e64dSDouglas Lowder      'WIFI_ONLY'
68b8b0e64dSDouglas Lowder    );
69b8b0e64dSDouglas Lowder    expect(getUpdatesCheckOnLaunch({ updates: { checkAutomatically: 'NEVER' } })).toBe('NEVER');
70b8b0e64dSDouglas Lowder    expect(getUpdatesCheckOnLaunch({ updates: {} })).toBe('ALWAYS');
7182ade864SWill Schurman    expect(getUpdatesEnabled({ updates: { enabled: false } })).toBe(false);
72082815dcSEvan Bacon    expect(getUpdatesTimeout({ updates: { fallbackToCacheTimeout: 2000 } })).toBe(2000);
73082815dcSEvan Bacon    expect(
74082815dcSEvan Bacon      getUpdatesCodeSigningCertificate('/app', {
75082815dcSEvan Bacon        updates: {
76082815dcSEvan Bacon          codeSigningCertificate: 'hello',
77082815dcSEvan Bacon        },
78082815dcSEvan Bacon      })
79082815dcSEvan Bacon    ).toBe(fsReal.readFileSync(sampleCodeSigningCertificatePath, 'utf-8'));
80082815dcSEvan Bacon    expect(
81082815dcSEvan Bacon      getUpdatesCodeSigningMetadataStringified({
82082815dcSEvan Bacon        updates: {
83082815dcSEvan Bacon          codeSigningMetadata: {
84082815dcSEvan Bacon            alg: 'rsa-v1_5-sha256',
85082815dcSEvan Bacon            keyid: 'test',
86082815dcSEvan Bacon          },
87082815dcSEvan Bacon        },
88082815dcSEvan Bacon      })
89082815dcSEvan Bacon    ).toBe(
90082815dcSEvan Bacon      JSON.stringify({
91082815dcSEvan Bacon        alg: 'rsa-v1_5-sha256',
92082815dcSEvan Bacon        keyid: 'test',
93082815dcSEvan Bacon      })
94082815dcSEvan Bacon    );
95082815dcSEvan Bacon    expect(
96082815dcSEvan Bacon      getUpdatesCodeSigningMetadata({
97082815dcSEvan Bacon        updates: {
98082815dcSEvan Bacon          codeSigningMetadata: {
99082815dcSEvan Bacon            alg: 'rsa-v1_5-sha256',
100082815dcSEvan Bacon            keyid: 'test',
101082815dcSEvan Bacon          },
102082815dcSEvan Bacon        },
103082815dcSEvan Bacon      })
104082815dcSEvan Bacon    ).toMatchObject({
105082815dcSEvan Bacon      alg: 'rsa-v1_5-sha256',
106082815dcSEvan Bacon      keyid: 'test',
107082815dcSEvan Bacon    });
10809bd1012SUmberto Ghio    expect(
10909bd1012SUmberto Ghio      getUpdatesRequestHeadersStringified({
11009bd1012SUmberto Ghio        updates: {
11109bd1012SUmberto Ghio          requestHeaders: {
11209bd1012SUmberto Ghio            'expo-channel-name': 'test',
11309bd1012SUmberto Ghio            testheader: 'test',
11409bd1012SUmberto Ghio          },
11509bd1012SUmberto Ghio        },
11609bd1012SUmberto Ghio      })
11709bd1012SUmberto Ghio    ).toBe(
11809bd1012SUmberto Ghio      JSON.stringify({
11909bd1012SUmberto Ghio        'expo-channel-name': 'test',
12009bd1012SUmberto Ghio        testheader: 'test',
12109bd1012SUmberto Ghio      })
12209bd1012SUmberto Ghio    );
12309bd1012SUmberto Ghio    expect(
12409bd1012SUmberto Ghio      getUpdatesRequestHeaders({
12509bd1012SUmberto Ghio        updates: {
12609bd1012SUmberto Ghio          requestHeaders: {
12709bd1012SUmberto Ghio            'expo-channel-name': 'test',
12809bd1012SUmberto Ghio            testheader: 'test',
12909bd1012SUmberto Ghio          },
13009bd1012SUmberto Ghio        },
13109bd1012SUmberto Ghio      })
13209bd1012SUmberto Ghio    ).toMatchObject({
13309bd1012SUmberto Ghio      'expo-channel-name': 'test',
13409bd1012SUmberto Ghio      testheader: 'test',
13509bd1012SUmberto Ghio    });
136082815dcSEvan Bacon  });
137082815dcSEvan Bacon});
138082815dcSEvan Bacon
139082815dcSEvan Bacondescribe(getUpdateUrl, () => {
140082815dcSEvan Bacon  it(`returns correct default values from all getters if no value provided.`, () => {
141082815dcSEvan Bacon    const url = 'https://u.expo.dev/00000000-0000-0000-0000-000000000000';
14282ade864SWill Schurman    expect(getUpdateUrl({ updates: { url } })).toBe(url);
1432fae8288SWill Schurman  });
1442fae8288SWill Schurman
1452fae8288SWill Schurman  it(`returns correct legacy urls if 'updates.url' is not provided, but 'slug' and ('username'|'owner') are provided and useClassicUpdates is false.`, () => {
14682ade864SWill Schurman    expect(getUpdateUrl({})).toBe(null);
147082815dcSEvan Bacon  });
148082815dcSEvan Bacon});
149082815dcSEvan Bacon
150082815dcSEvan Bacondescribe(getNativeVersion, () => {
151082815dcSEvan Bacon  const version = '2.0.0';
152082815dcSEvan Bacon  const versionCode = 42;
153082815dcSEvan Bacon  const buildNumber = '13';
154082815dcSEvan Bacon  it('works for android', () => {
155082815dcSEvan Bacon    expect(getNativeVersion({ version, android: { versionCode } }, 'android')).toBe(
156082815dcSEvan Bacon      `${version}(${versionCode})`
157082815dcSEvan Bacon    );
158082815dcSEvan Bacon  });
159082815dcSEvan Bacon  it('works for ios', () => {
160082815dcSEvan Bacon    expect(getNativeVersion({ version, ios: { buildNumber } }, 'ios')).toBe(
161082815dcSEvan Bacon      `${version}(${buildNumber})`
162082815dcSEvan Bacon    );
163082815dcSEvan Bacon  });
164082815dcSEvan Bacon  it('throws an error if platform is not recognized', () => {
165082815dcSEvan Bacon    const fakePlatform = 'doesnotexist';
166082815dcSEvan Bacon    expect(() => {
167082815dcSEvan Bacon      getNativeVersion({ version }, fakePlatform as any);
168082815dcSEvan Bacon    }).toThrow(`"${fakePlatform}" is not a supported platform. Choose either "ios" or "android".`);
169082815dcSEvan Bacon  });
170082815dcSEvan Bacon  it('uses the default version if the version is missing', () => {
171082815dcSEvan Bacon    expect(getNativeVersion({}, 'ios')).toBe('1.0.0(1)');
172082815dcSEvan Bacon  });
173082815dcSEvan Bacon  it('uses the default buildNumber if the platform is ios and the buildNumber is missing', () => {
174082815dcSEvan Bacon    expect(getNativeVersion({ version }, 'ios')).toBe(`${version}(1)`);
175082815dcSEvan Bacon  });
176082815dcSEvan Bacon  it('uses the default versionCode if the platform is android and the versionCode is missing', () => {
177082815dcSEvan Bacon    expect(getNativeVersion({ version }, 'android')).toBe(`${version}(1)`);
178082815dcSEvan Bacon  });
179082815dcSEvan Bacon});
180082815dcSEvan Bacon
181*f0d67e12SMateus Craveirodescribe(getRuntimeVersionAsync, () => {
182*f0d67e12SMateus Craveiro  it('works if the top level runtimeVersion is a string', async () => {
183082815dcSEvan Bacon    const runtimeVersion = '42';
184*f0d67e12SMateus Craveiro    expect(await getRuntimeVersionAsync('', { runtimeVersion }, 'ios')).toBe(runtimeVersion);
185082815dcSEvan Bacon  });
186*f0d67e12SMateus Craveiro  it('works if the platform specific runtimeVersion is a string', async () => {
187082815dcSEvan Bacon    const runtimeVersion = '42';
188*f0d67e12SMateus Craveiro    expect(await getRuntimeVersionAsync('', { ios: { runtimeVersion } }, 'ios')).toBe(
189*f0d67e12SMateus Craveiro      runtimeVersion
190*f0d67e12SMateus Craveiro    );
191082815dcSEvan Bacon  });
192*f0d67e12SMateus Craveiro  it('works if the runtimeVersion is a nativeVersion policy', async () => {
193082815dcSEvan Bacon    const version = '1';
194082815dcSEvan Bacon    const buildNumber = '2';
195082815dcSEvan Bacon    expect(
196*f0d67e12SMateus Craveiro      await getRuntimeVersionAsync(
197*f0d67e12SMateus Craveiro        '',
198082815dcSEvan Bacon        { version, runtimeVersion: { policy: 'nativeVersion' }, ios: { buildNumber } },
199082815dcSEvan Bacon        'ios'
200082815dcSEvan Bacon      )
201082815dcSEvan Bacon    ).toBe(`${version}(${buildNumber})`);
202082815dcSEvan Bacon  });
203*f0d67e12SMateus Craveiro  it('works if the runtimeVersion is an appVersion policy', async () => {
204053187fcSWojciech Kozyra    const version = '1';
205053187fcSWojciech Kozyra    const buildNumber = '2';
206053187fcSWojciech Kozyra    expect(
207*f0d67e12SMateus Craveiro      await getRuntimeVersionAsync(
208*f0d67e12SMateus Craveiro        '',
209053187fcSWojciech Kozyra        { version, runtimeVersion: { policy: 'appVersion' }, ios: { buildNumber } },
210053187fcSWojciech Kozyra        'ios'
211053187fcSWojciech Kozyra      )
212053187fcSWojciech Kozyra    ).toBe(version);
213053187fcSWojciech Kozyra  });
214*f0d67e12SMateus Craveiro  it('returns null if no runtime version is supplied', async () => {
215*f0d67e12SMateus Craveiro    expect(await getRuntimeVersionAsync('', {}, 'ios')).toEqual(null);
216082815dcSEvan Bacon  });
217*f0d67e12SMateus Craveiro  it('throws if runtime version is not parseable', async () => {
218*f0d67e12SMateus Craveiro    await expect(getRuntimeVersionAsync('', { runtimeVersion: 1 } as any, 'ios')).rejects.toThrow(
219*f0d67e12SMateus Craveiro      `"1" is not a valid runtime version. getRuntimeVersionAsync only supports a string, "sdkVersion", "appVersion", "nativeVersion" or "fingerprintExperimental" policy.`
220082815dcSEvan Bacon    );
221*f0d67e12SMateus Craveiro    await expect(
222*f0d67e12SMateus Craveiro      getRuntimeVersionAsync('', { runtimeVersion: { policy: 'unsupportedPlugin' } } as any, 'ios')
223*f0d67e12SMateus Craveiro    ).rejects.toThrow(
224*f0d67e12SMateus Craveiro      `"{"policy":"unsupportedPlugin"}" is not a valid runtime version. getRuntimeVersionAsync only supports a string, "sdkVersion", "appVersion", "nativeVersion" or "fingerprintExperimental" policy.`
225082815dcSEvan Bacon    );
226082815dcSEvan Bacon  });
227082815dcSEvan Bacon});
228