1import { vol } from 'memfs';
2import nock from 'nock';
3
4import { getExpoApiBaseUrl } from '../../api/endpoint';
5import { downloadAppAsync } from '../downloadAppAsync';
6import { downloadExpoGoAsync } 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(downloadExpoGoAsync, () => {
22  beforeEach(() => {
23    asMock(extractAsync).mockImplementationOnce(jest.fn());
24    asMock(downloadAppAsync).mockImplementationOnce(
25      jest.fn(jest.requireActual('../downloadAppAsync').downloadAppAsync)
26    );
27    vol.reset();
28  });
29  it('downloads the Expo Go app on iOS', async () => {
30    vol.fromJSON({ tmp: '' }, '/tmp');
31    const scope = nock(getExpoApiBaseUrl())
32      .get('/v2/versions/latest')
33      .reply(200, require('../../api/__tests__/fixtures/versions-latest.json'));
34    const scopeClient = nock('https://dpq5q02fu5f55.cloudfront.net')
35      .get('/Exponent-2.23.2.tar.gz')
36      .reply(200, '...');
37
38    await downloadExpoGoAsync('ios');
39
40    const generatedOutput = '/home/.expo/ios-simulator-app-cache/Exponent-2.23.2.tar.app';
41
42    // Endpoints were called
43    expect(scope.isDone()).toBe(true);
44    expect(scopeClient.isDone()).toBe(true);
45
46    // Platform options were parsed
47    expect(downloadAppAsync).toHaveBeenCalledWith({
48      cacheDirectory: 'expo-go',
49      extract: true,
50      onProgress: expect.anything(),
51      outputPath: generatedOutput,
52      url: 'https://dpq5q02fu5f55.cloudfront.net/Exponent-2.23.2.tar.gz',
53    });
54
55    // File won't be written because we mock the tar extraction.
56    // expect(vol.toJSON()[generatedOutput]).toBeDefined();
57
58    // Did extract tar
59    expect(extractAsync).toHaveBeenCalledWith('/tmp/Exponent-2.23.2.tar.app', generatedOutput);
60  });
61
62  it('downloads the Expo Go app on Android', async () => {
63    vol.fromJSON({}, '');
64
65    const scope = nock(getExpoApiBaseUrl())
66      .get('/v2/versions/latest')
67      .reply(200, require('../../api/__tests__/fixtures/versions-latest.json'));
68    const scopeClient = nock('https://d1ahtucjixef4r.cloudfront.net')
69      .get('/Exponent-2.23.2.apk')
70      .reply(200, '...');
71
72    await downloadExpoGoAsync('android');
73
74    const generatedOutput = '/home/.expo/android-apk-cache/Exponent-2.23.2.apk';
75
76    // Endpoints were called
77    expect(scope.isDone()).toBe(true);
78    expect(scopeClient.isDone()).toBe(true);
79
80    // Platform options were parsed
81    expect(downloadAppAsync).toHaveBeenCalledWith({
82      cacheDirectory: 'expo-go',
83      extract: false,
84      onProgress: expect.anything(),
85      outputPath: generatedOutput,
86      url: 'https://d1ahtucjixef4r.cloudfront.net/Exponent-2.23.2.apk',
87    });
88
89    expect(vol.toJSON()[generatedOutput]).toBeDefined();
90    // Did not extract Android APK
91    expect(extractAsync).toHaveBeenCalledTimes(0);
92  });
93
94  it('fails when the servers are down', async () => {
95    vol.fromJSON({}, '');
96
97    const scope = nock(getExpoApiBaseUrl())
98      .get('/v2/versions/latest')
99      .reply(200, require('../../api/__tests__/fixtures/versions-latest.json'));
100
101    // Mock a server failure when fetching from cloudfront.
102    const scopeClient = nock('https://d1ahtucjixef4r.cloudfront.net')
103      .get('/Exponent-2.23.2.apk')
104      .reply(500, 'something went wrong');
105
106    await expect(downloadExpoGoAsync('android')).rejects.toThrow(
107      /Unexpected response: Internal Server Error\. From url: https:\/\/d1ahtucjixef4r\.cloudfront\.net\/Exponent-2\.23\.2\.apk/
108    );
109    const generatedOutput = '/home/.expo/android-apk-cache/Exponent-2.23.2.apk';
110
111    // Endpoints were called
112    expect(scope.isDone()).toBe(true);
113    expect(scopeClient.isDone()).toBe(true);
114
115    // Platform options were parsed
116    expect(downloadAppAsync).toHaveBeenCalled();
117
118    // FS was not modified
119    expect(vol.toJSON()[generatedOutput]).not.toBeDefined();
120  });
121});
122