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    asMock(extractAsync).mockClear();
66
67    const scope = nock(getExpoApiBaseUrl())
68      .get('/v2/versions/latest')
69      .reply(200, require('../../api/__tests__/fixtures/versions-latest.json'));
70    const scopeClient = nock('https://d1ahtucjixef4r.cloudfront.net')
71      .get('/Exponent-2.23.2.apk')
72      .reply(200, '...');
73
74    await downloadExpoGoAsync('android');
75
76    const generatedOutput = '/home/.expo/android-apk-cache/Exponent-2.23.2.apk';
77
78    // Endpoints were called
79    expect(scope.isDone()).toBe(true);
80    expect(scopeClient.isDone()).toBe(true);
81
82    // Platform options were parsed
83    expect(downloadAppAsync).toHaveBeenCalledWith({
84      cacheDirectory: 'expo-go',
85      extract: false,
86      onProgress: expect.anything(),
87      outputPath: generatedOutput,
88      url: 'https://d1ahtucjixef4r.cloudfront.net/Exponent-2.23.2.apk',
89    });
90
91    expect(vol.toJSON()[generatedOutput]).toBeDefined();
92    // Did not extract Android APK
93    expect(extractAsync).toHaveBeenCalledTimes(0);
94  });
95
96  it('fails when the servers are down', async () => {
97    vol.fromJSON({}, '');
98
99    const scope = nock(getExpoApiBaseUrl())
100      .get('/v2/versions/latest')
101      .reply(200, require('../../api/__tests__/fixtures/versions-latest.json'));
102
103    // Mock a server failure when fetching from cloudfront.
104    const scopeClient = nock('https://d1ahtucjixef4r.cloudfront.net')
105      .get('/Exponent-2.23.2.apk')
106      .reply(500, 'something went wrong');
107
108    await expect(downloadExpoGoAsync('android')).rejects.toThrow(
109      /Unexpected response: Internal Server Error\. From url: https:\/\/d1ahtucjixef4r\.cloudfront\.net\/Exponent-2\.23\.2\.apk/
110    );
111    const generatedOutput = '/home/.expo/android-apk-cache/Exponent-2.23.2.apk';
112
113    // Endpoints were called
114    expect(scope.isDone()).toBe(true);
115    expect(scopeClient.isDone()).toBe(true);
116
117    // Platform options were parsed
118    expect(downloadAppAsync).toHaveBeenCalled();
119
120    // FS was not modified
121    expect(vol.toJSON()[generatedOutput]).not.toBeDefined();
122  });
123});
124