Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a1f19dc

Browse files
committedFeb 9, 2019
Add jest unit tests for the JavaScript code
1 parent 48a8a93 commit a1f19dc

12 files changed

+505
-12
lines changed
 

‎.circleci/config.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ aliases:
7373
name: Flow Checks
7474
command: yarn test:flow
7575

76-
- &javascript
77-
name: Javascript Tests
78-
command: yarn test:js
76+
- &jest
77+
name: Jest Unit Tests
78+
command: yarn test:jest
7979

8080
# -------------------------
8181
# JOBS
@@ -110,12 +110,12 @@ jobs:
110110
at: ~/react-native-netinfo
111111
- run: *flow
112112

113-
javascript:
113+
jest:
114114
<<: *linux_defaults
115115
steps:
116116
- attach_workspace:
117117
at: ~/react-native-netinfo
118-
- run: *javascript
118+
- run: *jest
119119

120120
android-compile:
121121
<<: *android_defaults
@@ -172,7 +172,7 @@ workflows:
172172
- flow:
173173
requires:
174174
- linux-checkout
175-
- javascript:
175+
- jest:
176176
requires:
177177
- linux-checkout
178178
- android-compile:

‎.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,6 @@ buck-out/
4646

4747
# Editor config
4848
.vscode
49+
50+
# Outputs
51+
coverage

‎jest.setup.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @flow
9+
*/
10+
/* eslint-env jest */
11+
12+
import {NativeModules} from 'react-native';
13+
14+
// Mock the RNCNetInfo native module to allow us to unit test the JavaScript code
15+
NativeModules.RNCNetInfo = {
16+
getCurrentConnectivity: jest.fn(),
17+
isConnectionMetered: jest.fn(),
18+
addListener: jest.fn(),
19+
removeListeners: jest.fn(),
20+
};
21+
22+
// Reset the mocks before each test
23+
global.beforeEach(() => {
24+
jest.resetAllMocks();
25+
});
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @flow
9+
*/
10+
/* eslint-env jest */
11+
12+
import {NativeModules} from 'react-native';
13+
import NetInfo from '../index';
14+
import {NetInfoEventEmitter} from '../nativeInterface';
15+
16+
describe('react-native-netinfo', () => {
17+
describe('Event listener callbacks', () => {
18+
it('should call the listener when the native event is emmitted', () => {
19+
const listener = jest.fn();
20+
NetInfo.addEventListener('connectionChange', listener);
21+
22+
const expectedConnectionType = 'cellular';
23+
const expectedEffectiveConnectionType = '3g';
24+
25+
NetInfoEventEmitter.emit(NetInfo.Events.NetworkStatusDidChange, {
26+
connectionType: expectedConnectionType,
27+
effectiveConnectionType: expectedEffectiveConnectionType,
28+
});
29+
30+
expect(listener).toBeCalledWith({
31+
type: expectedConnectionType,
32+
effectiveType: expectedEffectiveConnectionType,
33+
});
34+
});
35+
36+
it('should call the listener multiple times when multiple native events are emmitted', () => {
37+
const listener = jest.fn();
38+
NetInfo.addEventListener('connectionChange', listener);
39+
40+
NetInfoEventEmitter.emit(NetInfo.Events.NetworkStatusDidChange, {
41+
connectionType: 'cellular',
42+
effectiveConnectionType: '3g',
43+
});
44+
NetInfoEventEmitter.emit(NetInfo.Events.NetworkStatusDidChange, {
45+
connectionType: 'wifi',
46+
effectiveConnectionType: 'unknown',
47+
});
48+
49+
expect(listener).toBeCalledTimes(2);
50+
});
51+
52+
it('should call all listeners when the native event is emmitted', () => {
53+
const listener1 = jest.fn();
54+
const listener2 = jest.fn();
55+
NetInfo.addEventListener('connectionChange', listener1);
56+
NetInfo.addEventListener('connectionChange', listener2);
57+
58+
const expectedConnectionType = 'cellular';
59+
const expectedEffectiveConnectionType = '3g';
60+
61+
NetInfoEventEmitter.emit(NetInfo.Events.NetworkStatusDidChange, {
62+
connectionType: expectedConnectionType,
63+
effectiveConnectionType: expectedEffectiveConnectionType,
64+
});
65+
66+
const expectedResults = {
67+
type: expectedConnectionType,
68+
effectiveType: expectedEffectiveConnectionType,
69+
};
70+
71+
expect(listener1).toBeCalledWith(expectedResults);
72+
expect(listener2).toBeCalledWith(expectedResults);
73+
});
74+
75+
it('should not call the listener after being removed', () => {
76+
const listener = jest.fn();
77+
NetInfo.addEventListener('connectionChange', listener);
78+
NetInfo.removeEventListener('connectionChange', listener);
79+
80+
NetInfoEventEmitter.emit(NetInfo.Events.NetworkStatusDidChange, {
81+
connectionType: 'cellular',
82+
effectiveConnectionType: '3g',
83+
});
84+
85+
expect(listener).not.toBeCalled();
86+
});
87+
88+
it('should call the remaining listeners when one has been removed', () => {
89+
const listener1 = jest.fn();
90+
const listener2 = jest.fn();
91+
NetInfo.addEventListener('connectionChange', listener1);
92+
NetInfo.addEventListener('connectionChange', listener2);
93+
94+
NetInfo.removeEventListener('connectionChange', listener1);
95+
96+
const expectedConnectionType = 'cellular';
97+
const expectedEffectiveConnectionType = '3g';
98+
99+
NetInfoEventEmitter.emit(NetInfo.Events.NetworkStatusDidChange, {
100+
connectionType: expectedConnectionType,
101+
effectiveConnectionType: expectedEffectiveConnectionType,
102+
});
103+
104+
expect(listener1).not.toBeCalled();
105+
expect(listener2).toBeCalledWith({
106+
type: expectedConnectionType,
107+
effectiveType: expectedEffectiveConnectionType,
108+
});
109+
});
110+
});
111+
});
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @flow
9+
*/
10+
/* eslint-env jest */
11+
12+
import {NativeModules} from 'react-native';
13+
import NetInfo from '../index';
14+
import {NetInfoEventEmitter} from '../nativeInterface';
15+
16+
describe('react-native-netinfo', () => {
17+
describe('Event listener management', () => {
18+
it('should add the listener to the native module when passing the correct event name', () => {
19+
NetInfo.addEventListener('connectionChange', jest.fn());
20+
expect(NativeModules.RNCNetInfo.addListener).toBeCalledWith(
21+
NetInfo.Events.NetworkStatusDidChange,
22+
);
23+
});
24+
25+
it('should do nothing when passing the wrong event name', () => {
26+
// $FlowExpectedError We are testing passing in the wrong name
27+
NetInfo.addEventListener('WRONGNAME', jest.fn());
28+
expect(NativeModules.RNCNetInfo.addListener).not.toBeCalled();
29+
});
30+
31+
it('should not error when calling remove on an invalid subscription', () => {
32+
// $FlowExpectedError We are testing passing in the wrong name
33+
const subscription = NetInfo.addEventListener('WRONGNAME', jest.fn());
34+
subscription.remove();
35+
expect(NativeModules.RNCNetInfo.addListener).not.toBeCalled();
36+
});
37+
38+
it('should remove the listener from the native module when calling removeEventListener', () => {
39+
const listener = jest.fn();
40+
NetInfo.addEventListener('connectionChange', listener);
41+
NetInfo.removeEventListener('connectionChange', listener);
42+
expect(NativeModules.RNCNetInfo.removeListeners).toBeCalled();
43+
});
44+
45+
it('should not call the native module if asked to remove a listener which was never added', () => {
46+
NetInfo.removeEventListener('connectionChange', jest.fn());
47+
expect(NativeModules.RNCNetInfo.removeListeners).not.toBeCalled();
48+
});
49+
50+
it('should remove the listener from the native module when calling remove on the returned subscription', () => {
51+
const listener = jest.fn();
52+
const subscription = NetInfo.addEventListener(
53+
'connectionChange',
54+
listener,
55+
);
56+
subscription.remove();
57+
expect(NativeModules.RNCNetInfo.removeListeners).toBeCalled();
58+
});
59+
});
60+
});

‎js/__tests__/getConnectionInfo.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @flow
9+
*/
10+
/* eslint-env jest */
11+
12+
import NetInfo from '../index';
13+
import {RNCNetInfo} from '../nativeInterface';
14+
15+
describe('react-native-netinfo', () => {
16+
describe('getConnectionInfo', () => {
17+
it('should pass on the responses when the library promise returns', () => {
18+
const expectedConnectionType = 'cellular';
19+
const expectedEffectiveConnectionType = '3g';
20+
21+
RNCNetInfo.getCurrentConnectivity.mockResolvedValue({
22+
connectionType: expectedConnectionType,
23+
effectiveConnectionType: expectedEffectiveConnectionType,
24+
});
25+
26+
return expect(NetInfo.getConnectionInfo()).resolves.toEqual({
27+
type: expectedConnectionType,
28+
effectiveType: expectedEffectiveConnectionType,
29+
});
30+
});
31+
32+
it('should pass on errors through the promise chain', () => {
33+
const expectedError = new Error('A test error');
34+
35+
RNCNetInfo.getCurrentConnectivity.mockRejectedValue(expectedError);
36+
37+
return expect(NetInfo.getConnectionInfo()).rejects.toBe(expectedError);
38+
});
39+
});
40+
});

‎js/__tests__/isConnected.js

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @flow
9+
*/
10+
/* eslint-env jest */
11+
12+
import {NativeModules} from 'react-native';
13+
import NetInfo from '../index';
14+
import {NetInfoEventEmitter} from '../nativeInterface';
15+
16+
const CONNECTED_STATES = [
17+
{type: 'cellular', connected: true},
18+
{type: 'wifi', connected: true},
19+
{type: 'bluetooth', connected: true},
20+
{type: 'ethernet', connected: true},
21+
{type: 'wimax', connected: true},
22+
{type: 'none', connected: false},
23+
{type: 'unknown', connected: false},
24+
];
25+
26+
describe('react-native-netinfo', () => {
27+
describe('isConnected', () => {
28+
describe('fetch', () => {
29+
CONNECTED_STATES.map(({type, connected}) => {
30+
it(`should resolve to ${connected.toString()} when the native module returns a ${type} state`, () => {
31+
NativeModules.RNCNetInfo.getCurrentConnectivity.mockResolvedValue({
32+
connectionType: type,
33+
effectiveConnectionType: 'unknown',
34+
});
35+
36+
return expect(NetInfo.isConnected.fetch()).resolves.toBe(connected);
37+
});
38+
});
39+
40+
it('should pass on errors through the promise chain', () => {
41+
const expectedError = new Error('A test error');
42+
43+
NativeModules.RNCNetInfo.getCurrentConnectivity.mockRejectedValue(
44+
expectedError,
45+
);
46+
47+
return expect(NetInfo.getConnectionInfo()).rejects.toBe(expectedError);
48+
});
49+
});
50+
51+
describe('Event listener management', () => {
52+
it('should add the listener to the native module when passing the correct event name', () => {
53+
NetInfo.isConnected.addEventListener('connectionChange', jest.fn());
54+
expect(NativeModules.RNCNetInfo.addListener).toBeCalledWith(
55+
NetInfo.Events.NetworkStatusDidChange,
56+
);
57+
});
58+
59+
it('should do nothing when passing the wrong event name', () => {
60+
// $FlowExpectedError We are testing passing in the wrong name
61+
NetInfo.isConnected.addEventListener('WRONGNAME', jest.fn());
62+
expect(NativeModules.RNCNetInfo.addListener).not.toBeCalled();
63+
});
64+
65+
it('should remove the listener from the native module when calling removeEventListener', () => {
66+
const listener = jest.fn();
67+
NetInfo.isConnected.addEventListener('connectionChange', listener);
68+
NetInfo.isConnected.removeEventListener('connectionChange', listener);
69+
expect(NativeModules.RNCNetInfo.removeListeners).toBeCalled();
70+
});
71+
72+
it('should remove the listener from the native module when calling remove on the returned subscription', () => {
73+
const listener = jest.fn();
74+
const subscription = NetInfo.isConnected.addEventListener(
75+
'connectionChange',
76+
listener,
77+
);
78+
subscription.remove();
79+
expect(NativeModules.RNCNetInfo.removeListeners).toBeCalled();
80+
});
81+
});
82+
83+
describe('Event listener callbacks', () => {
84+
it('should call the listener when the native event is emmitted', () => {
85+
const listener = jest.fn();
86+
NetInfo.isConnected.addEventListener('connectionChange', listener);
87+
88+
NetInfoEventEmitter.emit(NetInfo.Events.NetworkStatusDidChange, {
89+
connectionType: 'cellular',
90+
effectiveConnectionType: '4g',
91+
});
92+
93+
expect(listener).toBeCalledWith(true);
94+
});
95+
96+
it('should call the listener multiple times when multiple native events are emmitted', () => {
97+
const listener = jest.fn();
98+
NetInfo.isConnected.addEventListener('connectionChange', listener);
99+
100+
NetInfoEventEmitter.emit(NetInfo.Events.NetworkStatusDidChange, {
101+
connectionType: 'cellular',
102+
effectiveConnectionType: '3g',
103+
});
104+
NetInfoEventEmitter.emit(NetInfo.Events.NetworkStatusDidChange, {
105+
connectionType: 'wifi',
106+
effectiveConnectionType: 'unknown',
107+
});
108+
109+
expect(listener).toBeCalledTimes(2);
110+
});
111+
112+
it('should call all listeners when the native event is emmitted', () => {
113+
const listener1 = jest.fn();
114+
const listener2 = jest.fn();
115+
NetInfo.isConnected.addEventListener('connectionChange', listener1);
116+
NetInfo.isConnected.addEventListener('connectionChange', listener2);
117+
118+
NetInfoEventEmitter.emit(NetInfo.Events.NetworkStatusDidChange, {
119+
connectionType: 'cellular',
120+
effectiveConnectionType: '2g',
121+
});
122+
123+
expect(listener1).toBeCalledWith(true);
124+
expect(listener2).toBeCalledWith(true);
125+
});
126+
127+
it('should not call the listener after being removed', () => {
128+
const listener = jest.fn();
129+
NetInfo.isConnected.addEventListener('connectionChange', listener);
130+
NetInfo.isConnected.removeEventListener('connectionChange', listener);
131+
132+
NetInfoEventEmitter.emit(NetInfo.Events.NetworkStatusDidChange, {
133+
connectionType: 'cellular',
134+
effectiveConnectionType: '3g',
135+
});
136+
137+
expect(listener).not.toBeCalled();
138+
});
139+
140+
it('should call the remaining listeners when one has been removed', () => {
141+
const listener1 = jest.fn();
142+
const listener2 = jest.fn();
143+
NetInfo.isConnected.addEventListener('connectionChange', listener1);
144+
NetInfo.isConnected.addEventListener('connectionChange', listener2);
145+
146+
NetInfo.isConnected.removeEventListener('connectionChange', listener1);
147+
148+
NetInfoEventEmitter.emit(NetInfo.Events.NetworkStatusDidChange, {
149+
connectionType: 'unknown',
150+
effectiveConnectionType: 'unknown',
151+
});
152+
153+
expect(listener1).not.toBeCalled();
154+
expect(listener2).toBeCalledWith(false);
155+
});
156+
});
157+
});
158+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @flow
9+
*/
10+
/* eslint-env jest */
11+
12+
jest.mock('Platform', () => {
13+
const Platform = jest.requireActual('Platform');
14+
Platform.OS = 'android';
15+
return Platform;
16+
});
17+
18+
import NetInfo from '../index';
19+
import {RNCNetInfo} from '../nativeInterface';
20+
21+
describe('react-native-netinfo', () => {
22+
describe('isConnectionExpensive', () => {
23+
describe('Android', () => {
24+
it('should pass on errors through the promise chain', () => {
25+
const expectedError = new Error('A test error');
26+
RNCNetInfo.isConnectionMetered.mockRejectedValue(expectedError);
27+
return expect(NetInfo.isConnectionExpensive()).rejects.toBe(
28+
expectedError,
29+
);
30+
});
31+
});
32+
});
33+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @flow
9+
*/
10+
/* eslint-env jest */
11+
12+
jest.mock('Platform', () => {
13+
const Platform = jest.requireActual('Platform');
14+
Platform.OS = 'ios';
15+
return Platform;
16+
});
17+
18+
import NetInfo from '../index';
19+
import {RNCNetInfo} from '../nativeInterface';
20+
21+
describe('react-native-netinfo', () => {
22+
describe('isConnectionExpensive', () => {
23+
describe('iOS', () => {
24+
it('should reject with an error when called', () => {
25+
return expect(NetInfo.isConnectionExpensive()).rejects.toThrowError();
26+
});
27+
});
28+
});
29+
});

‎js/index.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@
1010

1111
'use strict';
1212

13-
import {NativeEventEmitter, NativeModules, Platform} from 'react-native';
14-
15-
const {RNCNetInfo} = NativeModules;
16-
const NetInfoEventEmitter = new NativeEventEmitter(RNCNetInfo);
13+
import {Platform} from 'react-native';
14+
import {RNCNetInfo, NetInfoEventEmitter} from './nativeInterface';
1715

1816
const DEVICE_CONNECTIVITY_EVENT = 'networkStatusDidChange';
1917

@@ -61,6 +59,10 @@ const _isConnectedSubscriptions = new Map();
6159
* See https://facebook.github.io/react-native/docs/netinfo.html
6260
*/
6361
const NetInfo = {
62+
Events: {
63+
NetworkStatusDidChange: DEVICE_CONNECTIVITY_EVENT,
64+
},
65+
6466
/**
6567
* Adds an event handler.
6668
*

‎js/nativeInterface.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @flow
9+
*/
10+
11+
import {NativeEventEmitter, NativeModules} from 'react-native';
12+
13+
const {RNCNetInfo} = NativeModules;
14+
15+
/**
16+
* We export the native interface in this way to give easy shared access to it between the
17+
* JavaScript code and the tests
18+
*/
19+
let nativeEventEmitter = null;
20+
module.exports = {
21+
RNCNetInfo,
22+
get NetInfoEventEmitter() {
23+
if (!nativeEventEmitter) {
24+
nativeEventEmitter = new NativeEventEmitter(RNCNetInfo);
25+
}
26+
return nativeEventEmitter;
27+
},
28+
};

‎package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
"license": "MIT",
1010
"scripts": {
1111
"start": "node node_modules/react-native/local-cli/cli.js start",
12-
"test": "yarn test:eslint && yarn test:flow && yarn test:js",
12+
"test": "yarn test:eslint && yarn test:flow && yarn test:jest",
1313
"test:eslint": "eslint 'js/**/*.js' 'example/**/*.js'",
1414
"test:flow": "flow check",
15-
"test:js": "echo 0"
15+
"test:jest": "jest js/"
1616
},
1717
"keywords": [
1818
"react-native",
@@ -49,6 +49,10 @@
4949
"react-native": "0.58.4",
5050
"react-test-renderer": "16.6.3"
5151
},
52+
"jest": {
53+
"preset": "react-native",
54+
"setupFilesAfterEnv": ["<rootDir>/jest.setup.js"]
55+
},
5256
"repository": {
5357
"type": "git",
5458
"url": "https://github.com/react-native-community/react-native-netinfo.git"

0 commit comments

Comments
 (0)
Please sign in to comment.