1import { vol } from 'memfs'; 2import nock from 'nock'; 3 4import { getExpoApiBaseUrl } from '../../api/endpoint'; 5import { downloadAppAsync } from '../downloadAppAsync'; 6import { downloadExpoGoAsync, getExpoGoVersionEntryAsync } from '../downloadExpoGoAsync'; 7import { extractAsync } from '../tar'; 8 9const asMock = (fn: any): jest.Mock => fn; 10 11jest.mock('../../log'); 12 13jest.mock(`../downloadAppAsync`, () => ({ 14 downloadAppAsync: jest.fn(), 15})); 16 17jest.mock(`../tar`, () => ({ 18 extractAsync: jest.fn(), 19})); 20 21describe(getExpoGoVersionEntryAsync, () => { 22 beforeEach(() => { 23 vol.fromJSON({ tmp: '' }, '/tmp'); 24 }); 25 afterEach(() => { 26 vol.reset(); 27 }); 28 29 it(`returns the entry for a version`, async () => { 30 const scope = nock(getExpoApiBaseUrl()) 31 .get('/v2/versions/latest') 32 .reply(200, require('../../api/__tests__/fixtures/versions-latest.json')); 33 34 expect(await getExpoGoVersionEntryAsync('42.0.0')).toEqual( 35 expect.objectContaining({ 36 expoReactNativeTag: 'sdk-42.0.0', 37 }) 38 ); 39 40 expect(scope.isDone()).toBe(true); 41 }); 42 43 it(`returns the entry for an UNVERSIONED project`, async () => { 44 const scope = nock(getExpoApiBaseUrl()) 45 .get('/v2/versions/latest') 46 .reply(200, require('../../api/__tests__/fixtures/versions-latest.json')); 47 48 expect(await getExpoGoVersionEntryAsync('UNVERSIONED')).toEqual( 49 // Latest in the fixture 50 expect.objectContaining({ 51 expoReactNativeTag: 'sdk-44.0.0', 52 }) 53 ); 54 55 expect(scope.isDone()).toBe(true); 56 }); 57}); 58 59describe(downloadExpoGoAsync, () => { 60 beforeEach(() => { 61 asMock(extractAsync).mockImplementationOnce(jest.fn()); 62 asMock(downloadAppAsync).mockImplementationOnce( 63 jest.fn(jest.requireActual('../downloadAppAsync').downloadAppAsync) 64 ); 65 vol.reset(); 66 }); 67 it('downloads the Expo Go app on iOS', async () => { 68 vol.fromJSON({ tmp: '' }, '/tmp'); 69 const scope = nock(getExpoApiBaseUrl()) 70 .get('/v2/versions/latest') 71 .reply(200, require('../../api/__tests__/fixtures/versions-latest.json')); 72 const scopeClient = nock('https://dpq5q02fu5f55.cloudfront.net') 73 .get('/Exponent-2.23.2.tar.gz') 74 .reply(200, '...'); 75 76 await downloadExpoGoAsync('ios', { sdkVersion: '44.0.0' }); 77 78 const generatedOutput = '/home/.expo/ios-simulator-app-cache/Exponent-2.23.2.tar.app'; 79 80 // Endpoints were called 81 expect(scope.isDone()).toBe(true); 82 expect(scopeClient.isDone()).toBe(true); 83 84 // Platform options were parsed 85 expect(downloadAppAsync).toHaveBeenCalledWith({ 86 cacheDirectory: 'expo-go', 87 extract: true, 88 onProgress: expect.anything(), 89 outputPath: generatedOutput, 90 url: 'https://dpq5q02fu5f55.cloudfront.net/Exponent-2.23.2.tar.gz', 91 }); 92 93 // File won't be written because we mock the tar extraction. 94 // expect(vol.toJSON()[generatedOutput]).toBeDefined(); 95 96 // Did extract tar 97 expect(extractAsync).toHaveBeenCalledWith('/tmp/Exponent-2.23.2.tar.app', generatedOutput); 98 }); 99 100 it('downloads the Expo Go app on Android', async () => { 101 vol.fromJSON({}, ''); 102 103 const scope = nock(getExpoApiBaseUrl()) 104 .get('/v2/versions/latest') 105 .reply(200, require('../../api/__tests__/fixtures/versions-latest.json')); 106 const scopeClient = nock('https://d1ahtucjixef4r.cloudfront.net') 107 .get('/Exponent-2.23.2.apk') 108 .reply(200, '...'); 109 110 await downloadExpoGoAsync('android', { sdkVersion: '44.0.0' }); 111 112 const generatedOutput = '/home/.expo/android-apk-cache/Exponent-2.23.2.apk'; 113 114 // Endpoints were called 115 expect(scope.isDone()).toBe(true); 116 expect(scopeClient.isDone()).toBe(true); 117 118 // Platform options were parsed 119 expect(downloadAppAsync).toHaveBeenCalledWith({ 120 cacheDirectory: 'expo-go', 121 extract: false, 122 onProgress: expect.anything(), 123 outputPath: generatedOutput, 124 url: 'https://d1ahtucjixef4r.cloudfront.net/Exponent-2.23.2.apk', 125 }); 126 127 expect(vol.toJSON()[generatedOutput]).toBeDefined(); 128 // Did not extract Android APK 129 expect(extractAsync).toHaveBeenCalledTimes(0); 130 }); 131 132 it('fails when the servers are down', async () => { 133 vol.fromJSON({}, ''); 134 135 const scope = nock(getExpoApiBaseUrl()) 136 .get('/v2/versions/latest') 137 .reply(200, require('../../api/__tests__/fixtures/versions-latest.json')); 138 139 // Mock a server failure when fetching from cloudfront. 140 const scopeClient = nock('https://d1ahtucjixef4r.cloudfront.net') 141 .get('/Exponent-2.23.2.apk') 142 .reply(500, 'something went wrong'); 143 144 await expect(downloadExpoGoAsync('android', { sdkVersion: '44.0.0' })).rejects.toThrow( 145 /Unexpected response: Internal Server Error\. From url: https:\/\/d1ahtucjixef4r\.cloudfront\.net\/Exponent-2\.23\.2\.apk/ 146 ); 147 const generatedOutput = '/home/.expo/android-apk-cache/Exponent-2.23.2.apk'; 148 149 // Endpoints were called 150 expect(scope.isDone()).toBe(true); 151 expect(scopeClient.isDone()).toBe(true); 152 153 // Platform options were parsed 154 expect(downloadAppAsync).toHaveBeenCalled(); 155 156 // FS was not modified 157 expect(vol.toJSON()[generatedOutput]).not.toBeDefined(); 158 }); 159}); 160