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