diff --git a/README.md b/README.md index 040c5dc0..57b665f6 100644 --- a/README.md +++ b/README.md @@ -301,13 +301,14 @@ Describes the current generation of the `cellular` connection. It is an enum wit #### `NetInfoConfiguration` The configuration options for the library. -| Property | Type | Description | -| ---------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `reachabilityUrl` | `string` | The URL to call to test if the internet is reachable. Only used on platforms which do not supply internet reachability natively. | -| `reachabilityTest` | `(response: Response) => boolean` | A function which is passed the `Response` from calling the reachability URL. It should return `true` if the response indicates that the internet is reachable. Only used on platforms which do not supply internet reachability natively. | -| `reachabilityShortTimeout` | `number` | The number of milliseconds between internet reachability checks when the internet was not previously detected. Only used on platforms which do not supply internet reachability natively. | -| `reachabilityLongTimeout` | `number` | The number of milliseconds between internet reachability checks when the internet was previously detected. Only used on platforms which do not supply internet reachability natively. | -| `reachabilityRequestTimeout` | `number` | The number of milliseconds that a reachability check is allowed to take before failing. Only used on platforms which do not supply internet reachability natively. | +| Property | Type | Default | Description +| ---------------------------- | --------------------------------- | ----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `reachabilityUrl` | `string` | `https://clients3.google.com/generate_204` | The URL to call to test if the internet is reachable. Only used on platforms which do not supply internet reachability natively. | +| `reachabilityTest` | `(response: Response) => boolean` | `Promise.resolve(response.status === 204)` | A function which is passed the `Response` from calling the reachability URL. It should return `true` if the response indicates that the internet is reachable. Only used on platforms which do not supply internet reachability natively. | +| `reachabilityShortTimeout` | `number` | 5 seconds | The number of milliseconds between internet reachability checks when the internet was not previously detected. Only used on platforms which do not supply internet reachability natively. | +| `reachabilityLongTimeout` | `number` | 60 seconds | The number of milliseconds between internet reachability checks when the internet was previously detected. Only used on platforms which do not supply internet reachability natively. | +| `reachabilityRequestTimeout` | `number` | 15 seconds | The number of milliseconds that a reachability check is allowed to take before failing. Only used on platforms which do not supply internet reachability natively. | +| `reachabilityShouldRun` | `() => boolean` | `() => true` | A function which returns a boolean to determine if checkInternetReachability should be run. ### Methods @@ -325,6 +326,7 @@ NetInfo.configure({ reachabilityLongTimeout: 60 * 1000, // 60s reachabilityShortTimeout: 5 * 1000, // 5s reachabilityRequestTimeout: 15 * 1000, // 15s + reachabilityShouldRun: () => true, }); ``` @@ -378,6 +380,7 @@ const YourComponent = () => { reachabilityLongTimeout: 60 * 1000, // 60s reachabilityShortTimeout: 5 * 1000, // 5s reachabilityRequestTimeout: 15 * 1000, // 15s + reachabilityShouldRun: () => true, }); // ... diff --git a/src/__tests__/eventListenerCallbacks.spec.ts b/src/__tests__/eventListenerCallbacks.spec.ts index 1fd1aac2..4ba58e9b 100644 --- a/src/__tests__/eventListenerCallbacks.spec.ts +++ b/src/__tests__/eventListenerCallbacks.spec.ts @@ -7,30 +7,30 @@ * @format */ +import fetchMock from 'jest-fetch-mock'; import NetInfo from '../index'; import NativeInterface from '../internal/nativeInterface'; import {DEVICE_CONNECTIVITY_EVENT} from '../internal/privateTypes'; import {NetInfoStateType, NetInfoCellularGeneration} from '../internal/types'; // Mock modules -// eslint-disable-next-line @typescript-eslint/no-var-requires -require('jest-fetch-mock').enableMocks(); +fetchMock.enableMocks(); jest.mock('../internal/nativeModule'); const mockNativeModule = jest.requireMock('../internal/nativeModule').default; -describe('react-native-community/netinfo', () => { - beforeEach(() => { - mockNativeModule.getCurrentState.mockResolvedValue({ - type: NetInfoStateType.cellular, - isConnected: true, - isInternetReachable: true, - details: { - isConnectionExpensive: true, - cellularGeneration: NetInfoCellularGeneration['4g'], - }, - }); +beforeAll(() => { + mockNativeModule.getCurrentState.mockResolvedValue({ + type: NetInfoStateType.cellular, + isConnected: true, + isInternetReachable: true, + details: { + isConnectionExpensive: true, + cellularGeneration: NetInfoCellularGeneration['4g'], + }, }); +}); +describe('@react-native-community/netinfo listener', () => { describe('Event listener callbacks', () => { it('should call the listener on listening', done => { const listener = jest.fn(); @@ -254,5 +254,79 @@ describe('react-native-community/netinfo', () => { expect(listener1).not.toBeCalled(); expect(listener2).toBeCalledWith(expectedConnectionInfo); }); + + describe('with configuration options', () => { + beforeAll(() => { + // Prevent `_checkInternetReachability` from rescheduling. + jest.useFakeTimers(); + }); + + beforeEach(() => { + fetchMock.resetMocks(); + }); + + describe('reachabilityShouldRun', () => { + function dataProvider() { + return [ + { + description: 'reachabilityShouldRun returns true', + configuration: { + reachabilityShouldRun: () => true, + }, + expectedConnectionInfo: { + type: 'wifi', + isConnected: true, + details: { + isConnectionExpensive: true, + cellularGeneration: 'unknown', + }, + }, + expectedIsInternetReachable: null, + expectFetchToBeCalled: true, + }, + { + description: 'reachabilityShouldRun returns false', + configuration: { + reachabilityShouldRun: () => false, + }, + expectedConnectionInfo: { + type: 'wifi', + isConnected: true, + details: { + isConnectionExpensive: true, + cellularGeneration: 'unknown', + }, + }, + expectedIsInternetReachable: false, + expectFetchToBeCalled: false, + }, + ]; + } + + dataProvider().forEach(testCase => { + it(testCase.description, () => { + NetInfo.configure(testCase.configuration); + + const listener = jest.fn(); + NetInfo.addEventListener(listener); + + NativeInterface.eventEmitter.emit( + DEVICE_CONNECTIVITY_EVENT, + testCase.expectedConnectionInfo, + ); + + expect(listener).toBeCalledWith({ + ...testCase.expectedConnectionInfo, + isInternetReachable: testCase.expectedIsInternetReachable, + }); + expect(listener).toBeCalledTimes(1); + + testCase.expectFetchToBeCalled + ? expect(fetchMock).toHaveBeenCalled() + : expect(fetchMock).not.toHaveBeenCalled(); + }); + }); + }); + }); }); }); diff --git a/src/__tests__/fetch.spec.ts b/src/__tests__/fetch.spec.ts index 35fe0f6a..af596600 100644 --- a/src/__tests__/fetch.spec.ts +++ b/src/__tests__/fetch.spec.ts @@ -7,17 +7,21 @@ * @format */ +import fetchMock from 'jest-fetch-mock'; import NetInfo from '../index'; import NativeInterface from '../internal/nativeInterface'; import {NetInfoStateType, NetInfoCellularGeneration} from '../internal/types'; import {DEVICE_CONNECTIVITY_EVENT} from '../internal/privateTypes'; // Mock modules -// eslint-disable-next-line @typescript-eslint/no-var-requires -require('jest-fetch-mock').enableMocks(); +fetchMock.enableMocks(); jest.mock('../internal/nativeModule'); const mockNativeModule = jest.requireMock('../internal/nativeModule').default; +beforeEach(() => { + mockNativeModule.getCurrentState.mockReset(); +}); + describe('@react-native-community/netinfo fetch', () => { describe('with cellular data types', () => { describe('4g cellular generation', () => { @@ -316,7 +320,20 @@ describe('@react-native-community/netinfo fetch', () => { it('will properly reject a promise if the connection request cannot be resolved', async () => { const rejectionMessage = 'nope, no connection info for you'; - mockNativeModule.getCurrentState.mockRejectedValue(rejectionMessage); + // Mock `_fetchCurrentState` in the State constructor. + mockNativeModule.getCurrentState.mockResolvedValueOnce({ + type: NetInfoStateType.cellular, + isConnected: true, + isInternetReachable: true, + details: { + isConnectionExpensive: true, + cellularGeneration: NetInfoCellularGeneration['4g'], + }, + }); + + mockNativeModule.getCurrentState.mockRejectedValue( + new Error(rejectionMessage), + ); try { await NetInfo.fetch(); @@ -327,4 +344,74 @@ describe('@react-native-community/netinfo fetch', () => { } }); }); + + describe('with configuration options', () => { + beforeAll(() => { + // Prevent `_checkInternetReachability` from rescheduling. + jest.useFakeTimers(); + }); + + beforeEach(() => { + fetchMock.resetMocks(); + }); + + describe('reachabilityShouldRun', () => { + function dataProvider() { + return [ + { + description: 'reachabilityShouldRun returns true', + configuration: { + reachabilityShouldRun: () => true, + }, + expectedConnectionInfo: { + type: 'wifi', + isConnected: true, + details: { + isConnectionExpensive: true, + cellularGeneration: 'unknown', + }, + }, + expectedIsInternetReachable: null, + expectFetchToBeCalled: true, + }, + { + description: 'reachabilityShouldRun returns false', + configuration: { + reachabilityShouldRun: () => false, + }, + expectedConnectionInfo: { + type: 'wifi', + isConnected: true, + details: { + isConnectionExpensive: true, + cellularGeneration: 'unknown', + }, + }, + expectedIsInternetReachable: false, + expectFetchToBeCalled: false, + }, + ]; + } + + dataProvider().forEach(testCase => { + it(testCase.description, async () => { + mockNativeModule.getCurrentState.mockResolvedValue( + testCase.expectedConnectionInfo, + ); + + // Configure NetInfo. + NetInfo.configure(testCase.configuration); + + await expect(NetInfo.fetch()).resolves.toEqual({ + ...testCase.expectedConnectionInfo, + isInternetReachable: testCase.expectedIsInternetReachable, + }); + + testCase.expectFetchToBeCalled + ? expect(fetchMock).toHaveBeenCalled() + : expect(fetchMock).not.toHaveBeenCalled(); + }); + }); + }); + }); }); diff --git a/src/internal/defaultConfiguration.ts b/src/internal/defaultConfiguration.ts index 6600aaf1..869fb3ba 100644 --- a/src/internal/defaultConfiguration.ts +++ b/src/internal/defaultConfiguration.ts @@ -5,4 +5,5 @@ export default { reachabilityShortTimeout: 5 * 1000, // 5s reachabilityLongTimeout: 60 * 1000, // 60s reachabilityRequestTimeout: 15 * 1000, // 15s + reachabilityShouldRun: (): boolean => true, }; diff --git a/src/internal/defaultConfiguration.web.ts b/src/internal/defaultConfiguration.web.ts index a2d3b5d0..19076d9f 100644 --- a/src/internal/defaultConfiguration.web.ts +++ b/src/internal/defaultConfiguration.web.ts @@ -5,4 +5,5 @@ export default { reachabilityShortTimeout: 5 * 1000, // 5s reachabilityLongTimeout: 60 * 1000, // 60s reachabilityRequestTimeout: 15 * 1000, // 15s + reachabilityShouldRun: (): boolean => true, }; diff --git a/src/internal/internetReachability.ts b/src/internal/internetReachability.ts index ed49fb7d..82bf6bec 100644 --- a/src/internal/internetReachability.ts +++ b/src/internal/internetReachability.ts @@ -53,7 +53,7 @@ export default class InternetReachability { this._currentTimeoutHandle = null; } - if (expectsConnection) { + if (expectsConnection && this._configuration.reachabilityShouldRun()) { // If we expect a connection, start the process for finding if we have one // Set the state to "null" if it was previously false if (!this._isInternetReachable) { @@ -62,7 +62,7 @@ export default class InternetReachability { // Start a network request to check for internet this._currentInternetReachabilityCheckHandler = this._checkInternetReachability(); } else { - // If we don't expect a connection, just change the state to "false" + // If we don't expect a connection or don't run reachability check, just change the state to "false" this._setIsInternetReachable(false); } }; diff --git a/src/internal/types.ts b/src/internal/types.ts index 68a0e206..f1398c01 100644 --- a/src/internal/types.ts +++ b/src/internal/types.ts @@ -112,4 +112,5 @@ export interface NetInfoConfiguration { reachabilityLongTimeout: number; reachabilityShortTimeout: number; reachabilityRequestTimeout: number; + reachabilityShouldRun: () => boolean; }