1import fs from 'fs'; 2import { vol } from 'memfs'; 3import path from 'path'; 4 5import { 6 getNativeVersion, 7 getRuntimeVersion, 8 getSDKVersion, 9 getUpdatesCheckOnLaunch, 10 getUpdatesCodeSigningCertificate, 11 getUpdatesCodeSigningMetadata, 12 getUpdatesCodeSigningMetadataStringified, 13 getUpdatesEnabled, 14 getUpdatesTimeout, 15 getUpdateUrl, 16} from '../Updates'; 17 18const fsReal = jest.requireActual('fs') as typeof fs; 19jest.mock('fs'); 20jest.mock('resolve-from'); 21 22const { silent } = require('resolve-from'); 23 24const fixturesPath = path.resolve(__dirname, 'fixtures'); 25const sampleCodeSigningCertificatePath = path.resolve(fixturesPath, 'codeSigningCertificate.pem'); 26 27console.warn = jest.fn(); 28 29describe('shared config getters', () => { 30 beforeEach(() => { 31 const resolveFrom = require('resolve-from'); 32 resolveFrom.silent = silent; 33 vol.reset(); 34 }); 35 36 it(`returns correct default values from all getters if no value provided`, () => { 37 expect(getSDKVersion({})).toBe(null); 38 expect(getUpdatesCheckOnLaunch({})).toBe('ALWAYS'); 39 expect(getUpdatesEnabled({})).toBe(true); 40 expect(getUpdatesTimeout({})).toBe(0); 41 expect(getUpdatesCodeSigningCertificate('/app', {})).toBe(undefined); 42 expect(getUpdatesCodeSigningMetadata({})).toBe(undefined); 43 }); 44 45 it(`returns correct value from all getters if value provided`, () => { 46 vol.fromJSON({ 47 '/app/hello': fsReal.readFileSync(sampleCodeSigningCertificatePath, 'utf-8'), 48 }); 49 50 expect(getSDKVersion({ sdkVersion: '37.0.0' })).toBe('37.0.0'); 51 expect(getUpdatesCheckOnLaunch({ updates: { checkAutomatically: 'ON_ERROR_RECOVERY' } })).toBe( 52 'NEVER' 53 ); 54 expect( 55 getUpdatesCheckOnLaunch({ updates: { checkAutomatically: 'ON_ERROR_RECOVERY' } }, '0.11.0') 56 ).toBe('ERROR_RECOVERY_ONLY'); 57 expect( 58 getUpdatesCheckOnLaunch({ updates: { checkAutomatically: 'ON_ERROR_RECOVERY' } }, '0.10.15') 59 ).toBe('NEVER'); 60 expect(getUpdatesCheckOnLaunch({ updates: { checkAutomatically: 'ON_LOAD' } })).toBe('ALWAYS'); 61 expect(getUpdatesEnabled({ updates: { enabled: false } })).toBe(false); 62 expect(getUpdatesTimeout({ updates: { fallbackToCacheTimeout: 2000 } })).toBe(2000); 63 expect( 64 getUpdatesCodeSigningCertificate('/app', { 65 updates: { 66 codeSigningCertificate: 'hello', 67 }, 68 }) 69 ).toBe(fsReal.readFileSync(sampleCodeSigningCertificatePath, 'utf-8')); 70 expect( 71 getUpdatesCodeSigningMetadataStringified({ 72 updates: { 73 codeSigningMetadata: { 74 alg: 'rsa-v1_5-sha256', 75 keyid: 'test', 76 }, 77 }, 78 }) 79 ).toBe( 80 JSON.stringify({ 81 alg: 'rsa-v1_5-sha256', 82 keyid: 'test', 83 }) 84 ); 85 expect( 86 getUpdatesCodeSigningMetadata({ 87 updates: { 88 codeSigningMetadata: { 89 alg: 'rsa-v1_5-sha256', 90 keyid: 'test', 91 }, 92 }, 93 }) 94 ).toMatchObject({ 95 alg: 'rsa-v1_5-sha256', 96 keyid: 'test', 97 }); 98 }); 99}); 100 101describe(getUpdateUrl, () => { 102 it(`returns correct default values from all getters if no value provided.`, () => { 103 const url = 'https://u.expo.dev/00000000-0000-0000-0000-000000000000'; 104 expect(getUpdateUrl({ updates: { url }, slug: 'foo' }, 'user')).toBe(url); 105 }); 106 107 it(`returns null if neither 'updates.url' or 'user' is supplied.`, () => { 108 expect(getUpdateUrl({ slug: 'foo' }, null)).toBe(null); 109 }); 110 111 it(`returns correct legacy urls if 'updates.url' is not provided, but 'slug' and ('username'|'owner') are provided.`, () => { 112 expect(getUpdateUrl({ slug: 'my-app' }, 'user')).toBe('https://exp.host/@user/my-app'); 113 expect(getUpdateUrl({ slug: 'my-app', owner: 'owner' }, 'user')).toBe( 114 'https://exp.host/@owner/my-app' 115 ); 116 expect(getUpdateUrl({ slug: 'my-app', owner: 'owner' }, null)).toBe( 117 'https://exp.host/@owner/my-app' 118 ); 119 }); 120}); 121 122describe(getNativeVersion, () => { 123 const version = '2.0.0'; 124 const versionCode = 42; 125 const buildNumber = '13'; 126 it('works for android', () => { 127 expect(getNativeVersion({ version, android: { versionCode } }, 'android')).toBe( 128 `${version}(${versionCode})` 129 ); 130 }); 131 it('works for ios', () => { 132 expect(getNativeVersion({ version, ios: { buildNumber } }, 'ios')).toBe( 133 `${version}(${buildNumber})` 134 ); 135 }); 136 it('throws an error if platform is not recognized', () => { 137 const fakePlatform = 'doesnotexist'; 138 expect(() => { 139 getNativeVersion({ version }, fakePlatform as any); 140 }).toThrow(`"${fakePlatform}" is not a supported platform. Choose either "ios" or "android".`); 141 }); 142 it('uses the default version if the version is missing', () => { 143 expect(getNativeVersion({}, 'ios')).toBe('1.0.0(1)'); 144 }); 145 it('uses the default buildNumber if the platform is ios and the buildNumber is missing', () => { 146 expect(getNativeVersion({ version }, 'ios')).toBe(`${version}(1)`); 147 }); 148 it('uses the default versionCode if the platform is android and the versionCode is missing', () => { 149 expect(getNativeVersion({ version }, 'android')).toBe(`${version}(1)`); 150 }); 151}); 152 153describe(getRuntimeVersion, () => { 154 it('works if the top level runtimeVersion is a string', () => { 155 const runtimeVersion = '42'; 156 expect(getRuntimeVersion({ runtimeVersion }, 'ios')).toBe(runtimeVersion); 157 }); 158 it('works if the platform specific runtimeVersion is a string', () => { 159 const runtimeVersion = '42'; 160 expect(getRuntimeVersion({ ios: { runtimeVersion } }, 'ios')).toBe(runtimeVersion); 161 }); 162 it('works if the runtimeVersion is a nativeVersion policy', () => { 163 const version = '1'; 164 const buildNumber = '2'; 165 expect( 166 getRuntimeVersion( 167 { version, runtimeVersion: { policy: 'nativeVersion' }, ios: { buildNumber } }, 168 'ios' 169 ) 170 ).toBe(`${version}(${buildNumber})`); 171 }); 172 it('works if the runtimeVersion is an appVersion policy', () => { 173 const version = '1'; 174 const buildNumber = '2'; 175 expect( 176 getRuntimeVersion( 177 { version, runtimeVersion: { policy: 'appVersion' }, ios: { buildNumber } }, 178 'ios' 179 ) 180 ).toBe(version); 181 }); 182 it('returns null if no runtime version is supplied', () => { 183 expect(getRuntimeVersion({}, 'ios')).toEqual(null); 184 }); 185 it('throws if runtime version is not parseable', () => { 186 expect(() => { 187 getRuntimeVersion({ runtimeVersion: 1 } as any, 'ios'); 188 }).toThrow( 189 `"1" is not a valid runtime version. getRuntimeVersion only supports a string, "sdkVersion", "appVersion", or "nativeVersion" policy.` 190 ); 191 expect(() => { 192 getRuntimeVersion({ runtimeVersion: { policy: 'unsupportedPlugin' } } as any, 'ios'); 193 }).toThrow( 194 `"{"policy":"unsupportedPlugin"}" is not a valid runtime version. getRuntimeVersion only supports a string, "sdkVersion", "appVersion", or "nativeVersion" policy.` 195 ); 196 }); 197}); 198