1import nock from 'nock';
2
3import { getExpoApiBaseUrl } from '../../../api/endpoint';
4import * as ProjectDevices from '../../project/devices';
5import { DevelopmentSession } from '../DevelopmentSession';
6
7const asMock = (fn: any): jest.Mock => fn as jest.Mock;
8
9jest.mock('../../../api/settings', () => ({
10  APISettings: {
11    isOffline: false,
12  },
13}));
14jest.mock('../../project/devices', () => ({
15  getDevicesInfoAsync: jest.fn(),
16}));
17jest.mock('../../../api/user/user');
18
19describe(`startAsync`, () => {
20  it(`starts a dev session`, async () => {
21    const err = jest.fn();
22    const session = new DevelopmentSession('/', 'http://localhost:19001/', err);
23
24    asMock(ProjectDevices.getDevicesInfoAsync).mockResolvedValue({
25      devices: [{ installationId: '123' }, { installationId: '456' }],
26    });
27
28    const exp = {
29      name: 'my-app',
30      slug: 'my-app',
31      description: 'my-foo-bar',
32      primaryColor: '#4630eb',
33    };
34    const runtime = 'native';
35    const startScope = nock(getExpoApiBaseUrl())
36      .post('/v2/development-sessions/notify-alive?deviceId=123&deviceId=456')
37      .reply(200, '');
38    const closeScope = nock(getExpoApiBaseUrl())
39      .post('/v2/development-sessions/notify-close?deviceId=123&deviceId=456')
40      .reply(200, '');
41
42    await session.startAsync({
43      exp,
44      runtime,
45    });
46
47    await session.closeAsync();
48
49    expect(ProjectDevices.getDevicesInfoAsync).toHaveBeenCalledTimes(2);
50    expect(startScope.isDone()).toBe(true);
51    expect(closeScope.isDone()).toBe(true);
52    expect(err).not.toBeCalled();
53  });
54
55  it(`surfaces exceptions that would otherwise be uncaught`, async () => {
56    const err = jest.fn();
57    const session = new DevelopmentSession('/', 'http://localhost:19001/', err);
58
59    asMock(ProjectDevices.getDevicesInfoAsync).mockRejectedValueOnce(new Error('predefined error'));
60
61    const exp = {
62      name: 'my-app',
63      slug: 'my-app',
64      description: 'my-foo-bar',
65      primaryColor: '#4630eb',
66    };
67    const runtime = 'native';
68
69    // Does not throw directly
70    await session.startAsync({
71      exp,
72      runtime,
73    });
74
75    expect(err).toBeCalled();
76
77    // Did not repeat the cycle
78    expect(session['timeout']).toBe(null);
79  });
80
81  it(`gracefully handles server outages`, async () => {
82    const err = jest.fn();
83    const session = new DevelopmentSession('/', 'http://localhost:19001/', err);
84
85    asMock(ProjectDevices.getDevicesInfoAsync).mockResolvedValue({
86      devices: [{ installationId: '123' }, { installationId: '456' }],
87    });
88
89    const exp = {
90      name: 'my-app',
91      slug: 'my-app',
92      description: 'my-foo-bar',
93      primaryColor: '#4630eb',
94    };
95    const runtime = 'native';
96
97    // Server is down
98    nock(getExpoApiBaseUrl())
99      .post('/v2/development-sessions/notify-alive?deviceId=123&deviceId=456')
100      .reply(500, '');
101
102    // Does not throw directly
103    await session.startAsync({
104      exp,
105      runtime,
106    });
107
108    expect(err).toBeCalled();
109
110    // Did not repeat the cycle
111    expect(session['timeout']).toBe(null);
112  });
113});
114