react-native-background-geolocation 5.3.0 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/RNBackgroundGeolocation.podspec +1 -1
- package/android/build.gradle +11 -2
- package/android/src/main/java/com/transistorsoft/rnbackgroundgeolocation/RNBackgroundGeolocationModule.java +1 -1
- package/android/src/test/java/com/transistorsoft/rnbackgroundgeolocation/RNBackgroundGeolocationModuleTest.java +70 -0
- package/mocks/react-native.js +32 -29
- package/package.json +2 -2
- package/tests/insert-location.test.js +51 -0
|
@@ -26,7 +26,7 @@ Pod::Spec.new do |s|
|
|
|
26
26
|
s.source_files = 'ios/RNBackgroundGeolocation/*.{h,m,mm}'
|
|
27
27
|
s.preserve_paths = 'docs', 'CHANGELOG.md', 'LICENSE', 'package.json', 'RNBackgroundGeolocation.ios.js'
|
|
28
28
|
|
|
29
|
-
tslm_version = ENV['TSLOCATIONMANAGER_VERSION'] || '~> 4.
|
|
29
|
+
tslm_version = ENV['TSLOCATIONMANAGER_VERSION'] || '~> 4.4.0'
|
|
30
30
|
s.dependency 'TSLocationManager', tslm_version
|
|
31
31
|
|
|
32
32
|
s.libraries = 'sqlite3', 'z', 'stdc++'
|
package/android/build.gradle
CHANGED
|
@@ -9,7 +9,7 @@ def DEFAULT_TARGET_SDK_VERSION = 35
|
|
|
9
9
|
|
|
10
10
|
// Plugin dependencies
|
|
11
11
|
def DEFAULT_PLAY_SERVICES_LOCATION_VERSION = "21.3.0"
|
|
12
|
-
def DEFAULT_TSLOCATIONMANAGER_VERSION = "4.
|
|
12
|
+
def DEFAULT_TSLOCATIONMANAGER_VERSION = "4.4.+"
|
|
13
13
|
def DEFAULT_OK_HTTP_VERSION = "4.12.0"
|
|
14
14
|
def DEFAULT_ANDROID_PERMISSIONS_VERSION = "2.1.6"
|
|
15
15
|
def DEFAULT_EVENTBUS_VERSION = "3.3.1"
|
|
@@ -83,6 +83,11 @@ android {
|
|
|
83
83
|
java.srcDir("$buildDir/generated/source/codegen/java")
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
|
+
testOptions {
|
|
87
|
+
unitTests {
|
|
88
|
+
includeAndroidResources = true
|
|
89
|
+
}
|
|
90
|
+
}
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
repositories{
|
|
@@ -103,6 +108,10 @@ dependencies {
|
|
|
103
108
|
api "com.transistorsoft:tslocationmanager-gms20:$tslocationmanagerVersion"
|
|
104
109
|
}
|
|
105
110
|
|
|
106
|
-
implementation "com.google.android.gms:play-services-location:$playServicesLocationVersion"
|
|
111
|
+
implementation "com.google.android.gms:play-services-location:$playServicesLocationVersion"
|
|
107
112
|
implementation 'org.greenrobot:eventbus:3.3.1'
|
|
113
|
+
|
|
114
|
+
// Bridge-translation unit tests (JVM/Robolectric) — not shipped to consumers.
|
|
115
|
+
testImplementation "junit:junit:4.13.2"
|
|
116
|
+
testImplementation "org.robolectric:robolectric:4.14.1"
|
|
108
117
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
package com.transistorsoft.rnbackgroundgeolocation;
|
|
2
|
+
|
|
3
|
+
import static org.junit.Assert.assertEquals;
|
|
4
|
+
import static org.junit.Assert.assertTrue;
|
|
5
|
+
|
|
6
|
+
import com.facebook.react.bridge.JavaOnlyMap;
|
|
7
|
+
|
|
8
|
+
import org.json.JSONObject;
|
|
9
|
+
import org.junit.Test;
|
|
10
|
+
import org.junit.runner.RunWith;
|
|
11
|
+
import org.robolectric.RobolectricTestRunner;
|
|
12
|
+
import org.robolectric.annotation.Config;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Bridge-translation tests for the React Native Android module — the JS→native marshaling seam.
|
|
16
|
+
*
|
|
17
|
+
* These run on the JVM (Robolectric only for a real {@code org.json} implementation) with no device
|
|
18
|
+
* and no SDK: they exercise the exact {@code ReadableMap → org.json.JSONObject} conversion the real
|
|
19
|
+
* {@code insertLocation} path hands to the native SDK. This is the layer where the bridge-audit
|
|
20
|
+
* findings live (a numeric timestamp must stay a number; nested {@code coords}/{@code extras} must
|
|
21
|
+
* survive), and it is un-catchable by either a JS-only Jest test (mocked native) or a native SDK
|
|
22
|
+
* test (no bridge).
|
|
23
|
+
*
|
|
24
|
+
* The complementary half — the native {@code onSuccess(uuid) → promise.resolve(uuid)} settlement —
|
|
25
|
+
* is trivial glue that needs a live React runtime (RN 0.81's {@code ReactApplicationContext} is
|
|
26
|
+
* abstract and the module constructor initialises the SDK), so it belongs to the on-device E2E
|
|
27
|
+
* layer rather than a JVM unit test.
|
|
28
|
+
*/
|
|
29
|
+
@RunWith(RobolectricTestRunner.class)
|
|
30
|
+
@Config(sdk = 34)
|
|
31
|
+
public class RNBackgroundGeolocationModuleTest {
|
|
32
|
+
|
|
33
|
+
/** A bridge-shaped insertLocation params map: {coords:{lat,lng}, timestamp, extras:{...}}. */
|
|
34
|
+
private JavaOnlyMap makeInsertParams(double timestampMillis) {
|
|
35
|
+
JavaOnlyMap coords = new JavaOnlyMap();
|
|
36
|
+
coords.putDouble("latitude", 45.5152);
|
|
37
|
+
coords.putDouble("longitude", -73.6104);
|
|
38
|
+
|
|
39
|
+
JavaOnlyMap extras = new JavaOnlyMap();
|
|
40
|
+
extras.putString("source", "import");
|
|
41
|
+
|
|
42
|
+
JavaOnlyMap params = new JavaOnlyMap();
|
|
43
|
+
params.putMap("coords", coords);
|
|
44
|
+
params.putDouble("timestamp", timestampMillis);
|
|
45
|
+
params.putMap("extras", extras);
|
|
46
|
+
return params;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Test
|
|
50
|
+
public void mapToJson_preservesNestedCoordsAndExtras() throws Exception {
|
|
51
|
+
JSONObject json = RNBackgroundGeolocationModule.mapToJson(makeInsertParams(1_705_314_600_000d));
|
|
52
|
+
|
|
53
|
+
assertTrue("coords must survive as a nested object", json.has("coords"));
|
|
54
|
+
assertEquals(45.5152, json.getJSONObject("coords").getDouble("latitude"), 0.0);
|
|
55
|
+
assertEquals(-73.6104, json.getJSONObject("coords").getDouble("longitude"), 0.0);
|
|
56
|
+
assertEquals("import", json.getJSONObject("extras").getString("source"));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@Test
|
|
60
|
+
public void mapToJson_keepsNumericTimestampAsNumber_notString() throws Exception {
|
|
61
|
+
// Audit finding: a numeric epoch timestamp must reach the SDK as a NUMBER, so the SDK's
|
|
62
|
+
// epoch branch (TSLocation.normalizeTimestamp `raw instanceof Number`) fires — never a String.
|
|
63
|
+
JSONObject json = RNBackgroundGeolocationModule.mapToJson(makeInsertParams(1_705_314_600_000d));
|
|
64
|
+
|
|
65
|
+
Object ts = json.get("timestamp");
|
|
66
|
+
assertTrue("timestamp must be a JSON number, not a String (got " + ts.getClass().getSimpleName() + ")",
|
|
67
|
+
ts instanceof Number);
|
|
68
|
+
assertEquals(1_705_314_600_000L, ((Number) ts).longValue());
|
|
69
|
+
}
|
|
70
|
+
}
|
package/mocks/react-native.js
CHANGED
|
@@ -9,38 +9,41 @@ class NativeEventEmitter {
|
|
|
9
9
|
removeAllListeners() { listeners.clear(); }
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
// The native module is a TurboModule: every data method returns a Promise (not the pre-migration
|
|
13
|
+
// callback convention). NativeModule.js returns these calls directly, so the mocks must resolve a
|
|
14
|
+
// Promise. `log`/`playSound` are fire-and-forget (void).
|
|
12
15
|
const RNBackgroundGeolocation = {
|
|
13
16
|
// Minimal surface your JS calls in tests:
|
|
14
|
-
ready: (
|
|
15
|
-
configure: (
|
|
16
|
-
setConfig: (
|
|
17
|
-
reset: (
|
|
18
|
-
getState: (
|
|
19
|
-
beginBackgroundTask: (
|
|
20
|
-
finish: (id
|
|
21
|
-
addGeofence: (
|
|
22
|
-
addGeofences: (
|
|
23
|
-
removeGeofence: (
|
|
24
|
-
removeGeofences: (
|
|
25
|
-
getGeofences: (
|
|
26
|
-
getGeofence: (
|
|
27
|
-
geofenceExists: (
|
|
28
|
-
changePace: (
|
|
29
|
-
getLog: (
|
|
30
|
-
destroyLog: (
|
|
31
|
-
emailLog: (
|
|
32
|
-
getOdometer: (
|
|
33
|
-
setOdometer: (
|
|
34
|
-
getLocations: (
|
|
35
|
-
getCount: (
|
|
36
|
-
destroyLocations: (
|
|
37
|
-
insertLocation: (
|
|
38
|
-
sync: (
|
|
39
|
-
getProviderState: (
|
|
40
|
-
requestPermission: (
|
|
41
|
-
requestTemporaryFullAccuracy: (
|
|
17
|
+
ready: () => Promise.resolve({ enabled: false }),
|
|
18
|
+
configure: () => Promise.resolve({}),
|
|
19
|
+
setConfig: () => Promise.resolve({}),
|
|
20
|
+
reset: () => Promise.resolve({ enabled: false }),
|
|
21
|
+
getState: () => Promise.resolve({ enabled: false }),
|
|
22
|
+
beginBackgroundTask: () => Promise.resolve(1),
|
|
23
|
+
finish: (id) => Promise.resolve(id),
|
|
24
|
+
addGeofence: () => Promise.resolve(),
|
|
25
|
+
addGeofences: () => Promise.resolve(),
|
|
26
|
+
removeGeofence: () => Promise.resolve(),
|
|
27
|
+
removeGeofences: () => Promise.resolve(),
|
|
28
|
+
getGeofences: () => Promise.resolve([]),
|
|
29
|
+
getGeofence: () => Promise.resolve(null),
|
|
30
|
+
geofenceExists: () => Promise.resolve(false),
|
|
31
|
+
changePace: () => Promise.resolve(),
|
|
32
|
+
getLog: () => Promise.resolve(''),
|
|
33
|
+
destroyLog: () => Promise.resolve(),
|
|
34
|
+
emailLog: () => Promise.resolve(),
|
|
35
|
+
getOdometer: () => Promise.resolve(0),
|
|
36
|
+
setOdometer: () => Promise.resolve({}),
|
|
37
|
+
getLocations: () => Promise.resolve([]),
|
|
38
|
+
getCount: () => Promise.resolve(0),
|
|
39
|
+
destroyLocations: () => Promise.resolve(),
|
|
40
|
+
insertLocation: () => Promise.resolve('00000000-0000-0000-0000-000000000000'),
|
|
41
|
+
sync: () => Promise.resolve({ success: true }),
|
|
42
|
+
getProviderState: () => Promise.resolve({ enabled: true }),
|
|
43
|
+
requestPermission: () => Promise.resolve(1),
|
|
44
|
+
requestTemporaryFullAccuracy: () => Promise.resolve(1),
|
|
42
45
|
log: () => {},
|
|
43
|
-
getDeviceInfo: (
|
|
46
|
+
getDeviceInfo: () => Promise.resolve({ model: 'mock' }),
|
|
44
47
|
playSound: () => {},
|
|
45
48
|
};
|
|
46
49
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
|
|
3
3
|
"name": "react-native-background-geolocation",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.4.0",
|
|
5
5
|
"description": "The most sophisticated cross-platform background location-tracking & geofencing module with battery-conscious motion-detection intelligence",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "yarn run build:expo",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@babel/runtime": "^7.26.0",
|
|
44
|
-
"@transistorsoft/background-geolocation-types": "^5.2.
|
|
44
|
+
"@transistorsoft/background-geolocation-types": "^5.2.2",
|
|
45
45
|
"tslib": "^2.6.3"
|
|
46
46
|
},
|
|
47
47
|
"author": "Chris Scott <chris@transistorsoft.com>",
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// JS-side marshaling test for insertLocation: does the JavaScript API layer forward the caller's
|
|
2
|
+
// location to the native bridge boundary *unchanged*, and pass the returned uuid back — with no
|
|
3
|
+
// real SDK and no device? The native module is a jest spy standing in for the bridge; we assert on
|
|
4
|
+
// what it receives (the args that crossed JS -> native) and what the caller ultimately gets back.
|
|
5
|
+
//
|
|
6
|
+
// This is the JS half of the two-halves bridge-marshaling strategy. The native half (ReadableMap ->
|
|
7
|
+
// JSONObject) is covered by the Android Robolectric test `RNBackgroundGeolocationModuleTest`.
|
|
8
|
+
|
|
9
|
+
const { NativeModules } = require('react-native'); // mapped to mocks/react-native.js via jest config
|
|
10
|
+
const BG = require('../src/index.js').default;
|
|
11
|
+
|
|
12
|
+
const native = NativeModules.RNBackgroundGeolocation;
|
|
13
|
+
|
|
14
|
+
describe('insertLocation — JS -> native bridge marshaling', () => {
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
jest.restoreAllMocks();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('forwards the location to the native module unchanged and resolves its uuid', async () => {
|
|
20
|
+
const insert = jest.spyOn(native, 'insertLocation').mockResolvedValue('uuid-abc');
|
|
21
|
+
|
|
22
|
+
const location = {
|
|
23
|
+
coords: { latitude: 45.5152, longitude: -73.6104 },
|
|
24
|
+
timestamp: 1705314600000, // numeric epoch
|
|
25
|
+
extras: { source: 'import' },
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const uuid = await BG.insertLocation(location);
|
|
29
|
+
|
|
30
|
+
// The exact object reaches the native boundary — no mangling by index.js / NativeModule.
|
|
31
|
+
expect(insert).toHaveBeenCalledTimes(1);
|
|
32
|
+
expect(insert).toHaveBeenCalledWith(location);
|
|
33
|
+
|
|
34
|
+
// Type/shape preserved across the JS layers: numeric timestamp stays a number, coords/extras nested.
|
|
35
|
+
const [passed] = insert.mock.calls[0];
|
|
36
|
+
expect(typeof passed.timestamp).toBe('number');
|
|
37
|
+
expect(passed.coords).toEqual({ latitude: 45.5152, longitude: -73.6104 });
|
|
38
|
+
expect(passed.extras).toEqual({ source: 'import' });
|
|
39
|
+
|
|
40
|
+
// The uuid string flows back out through NativeModule + index.js.
|
|
41
|
+
expect(uuid).toBe('uuid-abc');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('propagates a native rejection to the caller', async () => {
|
|
45
|
+
jest.spyOn(native, 'insertLocation').mockRejectedValue(new Error('insert_location_error'));
|
|
46
|
+
|
|
47
|
+
await expect(
|
|
48
|
+
BG.insertLocation({ coords: { latitude: 0, longitude: 0 }, timestamp: 0 })
|
|
49
|
+
).rejects.toThrow('insert_location_error');
|
|
50
|
+
});
|
|
51
|
+
});
|