@symbiote-native/sensors 0.1.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/LICENSE +21 -0
- package/README.md +138 -0
- package/build/angular/index.d.ts +16 -0
- package/build/angular/index.js +12 -0
- package/build/angular/services/accelerometer.service/index.d.ts +6 -0
- package/build/angular/services/accelerometer.service/index.js +81 -0
- package/build/angular/services/barometer.service/index.d.ts +6 -0
- package/build/angular/services/barometer.service/index.js +81 -0
- package/build/angular/services/device-motion.service/index.d.ts +6 -0
- package/build/angular/services/device-motion.service/index.js +81 -0
- package/build/angular/services/gyroscope.service/index.d.ts +6 -0
- package/build/angular/services/gyroscope.service/index.js +81 -0
- package/build/angular/services/light-sensor.service/index.d.ts +6 -0
- package/build/angular/services/light-sensor.service/index.js +81 -0
- package/build/angular/services/magnetometer-uncalibrated.service/index.d.ts +6 -0
- package/build/angular/services/magnetometer-uncalibrated.service/index.js +81 -0
- package/build/angular/services/magnetometer.service/index.d.ts +6 -0
- package/build/angular/services/magnetometer.service/index.js +81 -0
- package/build/angular/services/pedometer.service/index.d.ts +6 -0
- package/build/angular/services/pedometer.service/index.js +76 -0
- package/build/core/accelerometer.d.ts +10 -0
- package/build/core/accelerometer.js +6 -0
- package/build/core/barometer.d.ts +9 -0
- package/build/core/barometer.js +6 -0
- package/build/core/device-motion.d.ts +40 -0
- package/build/core/device-motion.js +21 -0
- package/build/core/device-sensor.d.ts +27 -0
- package/build/core/device-sensor.js +61 -0
- package/build/core/gyroscope.d.ts +10 -0
- package/build/core/gyroscope.js +6 -0
- package/build/core/index.d.ts +11 -0
- package/build/core/index.js +10 -0
- package/build/core/light-sensor.d.ts +8 -0
- package/build/core/light-sensor.js +6 -0
- package/build/core/magnetometer-uncalibrated.d.ts +10 -0
- package/build/core/magnetometer-uncalibrated.js +6 -0
- package/build/core/magnetometer.d.ts +10 -0
- package/build/core/magnetometer.js +6 -0
- package/build/core/native/exponent-accelerometer.d.ts +3 -0
- package/build/core/native/exponent-accelerometer.js +3 -0
- package/build/core/native/exponent-barometer.d.ts +3 -0
- package/build/core/native/exponent-barometer.js +3 -0
- package/build/core/native/exponent-device-motion.d.ts +7 -0
- package/build/core/native/exponent-device-motion.js +3 -0
- package/build/core/native/exponent-gyroscope.d.ts +3 -0
- package/build/core/native/exponent-gyroscope.js +3 -0
- package/build/core/native/exponent-light-sensor/index.android.d.ts +3 -0
- package/build/core/native/exponent-light-sensor/index.android.js +3 -0
- package/build/core/native/exponent-light-sensor/index.d.ts +1 -0
- package/build/core/native/exponent-light-sensor/index.ios.d.ts +3 -0
- package/build/core/native/exponent-light-sensor/index.ios.js +6 -0
- package/build/core/native/exponent-light-sensor/index.js +5 -0
- package/build/core/native/exponent-magnetometer-uncalibrated.d.ts +3 -0
- package/build/core/native/exponent-magnetometer-uncalibrated.js +3 -0
- package/build/core/native/exponent-magnetometer.d.ts +3 -0
- package/build/core/native/exponent-magnetometer.js +3 -0
- package/build/core/native/exponent-pedometer.d.ts +10 -0
- package/build/core/native/exponent-pedometer.js +3 -0
- package/build/core/pedometer.d.ts +10 -0
- package/build/core/pedometer.js +44 -0
- package/build/react/hooks/use-accelerometer/index.d.ts +2 -0
- package/build/react/hooks/use-accelerometer/index.js +17 -0
- package/build/react/hooks/use-barometer/index.d.ts +2 -0
- package/build/react/hooks/use-barometer/index.js +17 -0
- package/build/react/hooks/use-device-motion/index.d.ts +2 -0
- package/build/react/hooks/use-device-motion/index.js +17 -0
- package/build/react/hooks/use-gyroscope/index.d.ts +2 -0
- package/build/react/hooks/use-gyroscope/index.js +17 -0
- package/build/react/hooks/use-light-sensor/index.d.ts +2 -0
- package/build/react/hooks/use-light-sensor/index.js +17 -0
- package/build/react/hooks/use-magnetometer/index.d.ts +2 -0
- package/build/react/hooks/use-magnetometer/index.js +17 -0
- package/build/react/hooks/use-magnetometer-uncalibrated/index.d.ts +2 -0
- package/build/react/hooks/use-magnetometer-uncalibrated/index.js +17 -0
- package/build/react/hooks/use-pedometer/index.d.ts +2 -0
- package/build/react/hooks/use-pedometer/index.js +14 -0
- package/build/react/index.d.ts +16 -0
- package/build/react/index.js +14 -0
- package/build/vue/composables/use-accelerometer/index.d.ts +3 -0
- package/build/vue/composables/use-accelerometer/index.js +25 -0
- package/build/vue/composables/use-barometer/index.d.ts +3 -0
- package/build/vue/composables/use-barometer/index.js +25 -0
- package/build/vue/composables/use-device-motion/index.d.ts +3 -0
- package/build/vue/composables/use-device-motion/index.js +25 -0
- package/build/vue/composables/use-gyroscope/index.d.ts +3 -0
- package/build/vue/composables/use-gyroscope/index.js +25 -0
- package/build/vue/composables/use-light-sensor/index.d.ts +3 -0
- package/build/vue/composables/use-light-sensor/index.js +25 -0
- package/build/vue/composables/use-magnetometer/index.d.ts +3 -0
- package/build/vue/composables/use-magnetometer/index.js +25 -0
- package/build/vue/composables/use-magnetometer-uncalibrated/index.d.ts +3 -0
- package/build/vue/composables/use-magnetometer-uncalibrated/index.js +25 -0
- package/build/vue/composables/use-pedometer/index.d.ts +3 -0
- package/build/vue/composables/use-pedometer/index.js +20 -0
- package/build/vue/index.d.ts +16 -0
- package/build/vue/index.js +14 -0
- package/build-ngc/angular/index.d.ts +16 -0
- package/build-ngc/angular/index.js +13 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/angular/services/accelerometer.service/index.d.ts +9 -0
- package/build-ngc/angular/services/accelerometer.service/index.js +40 -0
- package/build-ngc/angular/services/accelerometer.service/index.js.map +1 -0
- package/build-ngc/angular/services/barometer.service/index.d.ts +9 -0
- package/build-ngc/angular/services/barometer.service/index.js +40 -0
- package/build-ngc/angular/services/barometer.service/index.js.map +1 -0
- package/build-ngc/angular/services/device-motion.service/index.d.ts +9 -0
- package/build-ngc/angular/services/device-motion.service/index.js +40 -0
- package/build-ngc/angular/services/device-motion.service/index.js.map +1 -0
- package/build-ngc/angular/services/gyroscope.service/index.d.ts +9 -0
- package/build-ngc/angular/services/gyroscope.service/index.js +40 -0
- package/build-ngc/angular/services/gyroscope.service/index.js.map +1 -0
- package/build-ngc/angular/services/light-sensor.service/index.d.ts +9 -0
- package/build-ngc/angular/services/light-sensor.service/index.js +40 -0
- package/build-ngc/angular/services/light-sensor.service/index.js.map +1 -0
- package/build-ngc/angular/services/magnetometer-uncalibrated.service/index.d.ts +9 -0
- package/build-ngc/angular/services/magnetometer-uncalibrated.service/index.js +40 -0
- package/build-ngc/angular/services/magnetometer-uncalibrated.service/index.js.map +1 -0
- package/build-ngc/angular/services/magnetometer.service/index.d.ts +9 -0
- package/build-ngc/angular/services/magnetometer.service/index.js +40 -0
- package/build-ngc/angular/services/magnetometer.service/index.js.map +1 -0
- package/build-ngc/angular/services/pedometer.service/index.d.ts +9 -0
- package/build-ngc/angular/services/pedometer.service/index.js +35 -0
- package/build-ngc/angular/services/pedometer.service/index.js.map +1 -0
- package/build-ngc/core/accelerometer.d.ts +10 -0
- package/build-ngc/core/accelerometer.js +7 -0
- package/build-ngc/core/accelerometer.js.map +1 -0
- package/build-ngc/core/barometer.d.ts +9 -0
- package/build-ngc/core/barometer.js +7 -0
- package/build-ngc/core/barometer.js.map +1 -0
- package/build-ngc/core/device-motion.d.ts +40 -0
- package/build-ngc/core/device-motion.js +22 -0
- package/build-ngc/core/device-motion.js.map +1 -0
- package/build-ngc/core/device-sensor.d.ts +27 -0
- package/build-ngc/core/device-sensor.js +62 -0
- package/build-ngc/core/device-sensor.js.map +1 -0
- package/build-ngc/core/gyroscope.d.ts +10 -0
- package/build-ngc/core/gyroscope.js +7 -0
- package/build-ngc/core/gyroscope.js.map +1 -0
- package/build-ngc/core/index.d.ts +11 -0
- package/build-ngc/core/index.js +11 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/light-sensor.d.ts +8 -0
- package/build-ngc/core/light-sensor.js +7 -0
- package/build-ngc/core/light-sensor.js.map +1 -0
- package/build-ngc/core/magnetometer-uncalibrated.d.ts +10 -0
- package/build-ngc/core/magnetometer-uncalibrated.js +7 -0
- package/build-ngc/core/magnetometer-uncalibrated.js.map +1 -0
- package/build-ngc/core/magnetometer.d.ts +10 -0
- package/build-ngc/core/magnetometer.js +7 -0
- package/build-ngc/core/magnetometer.js.map +1 -0
- package/build-ngc/core/native/exponent-accelerometer.d.ts +3 -0
- package/build-ngc/core/native/exponent-accelerometer.js +4 -0
- package/build-ngc/core/native/exponent-accelerometer.js.map +1 -0
- package/build-ngc/core/native/exponent-barometer.d.ts +3 -0
- package/build-ngc/core/native/exponent-barometer.js +4 -0
- package/build-ngc/core/native/exponent-barometer.js.map +1 -0
- package/build-ngc/core/native/exponent-device-motion.d.ts +7 -0
- package/build-ngc/core/native/exponent-device-motion.js +4 -0
- package/build-ngc/core/native/exponent-device-motion.js.map +1 -0
- package/build-ngc/core/native/exponent-gyroscope.d.ts +3 -0
- package/build-ngc/core/native/exponent-gyroscope.js +4 -0
- package/build-ngc/core/native/exponent-gyroscope.js.map +1 -0
- package/build-ngc/core/native/exponent-light-sensor/index.android.d.ts +3 -0
- package/build-ngc/core/native/exponent-light-sensor/index.android.js +4 -0
- package/build-ngc/core/native/exponent-light-sensor/index.android.js.map +1 -0
- package/build-ngc/core/native/exponent-light-sensor/index.d.ts +1 -0
- package/build-ngc/core/native/exponent-light-sensor/index.ios.d.ts +3 -0
- package/build-ngc/core/native/exponent-light-sensor/index.ios.js +7 -0
- package/build-ngc/core/native/exponent-light-sensor/index.ios.js.map +1 -0
- package/build-ngc/core/native/exponent-light-sensor/index.js +6 -0
- package/build-ngc/core/native/exponent-light-sensor/index.js.map +1 -0
- package/build-ngc/core/native/exponent-magnetometer-uncalibrated.d.ts +3 -0
- package/build-ngc/core/native/exponent-magnetometer-uncalibrated.js +4 -0
- package/build-ngc/core/native/exponent-magnetometer-uncalibrated.js.map +1 -0
- package/build-ngc/core/native/exponent-magnetometer.d.ts +3 -0
- package/build-ngc/core/native/exponent-magnetometer.js +4 -0
- package/build-ngc/core/native/exponent-magnetometer.js.map +1 -0
- package/build-ngc/core/native/exponent-pedometer.d.ts +10 -0
- package/build-ngc/core/native/exponent-pedometer.js +4 -0
- package/build-ngc/core/native/exponent-pedometer.js.map +1 -0
- package/build-ngc/core/pedometer.d.ts +10 -0
- package/build-ngc/core/pedometer.js +45 -0
- package/build-ngc/core/pedometer.js.map +1 -0
- package/package.json +106 -0
- package/src/angular/index.ts +26 -0
- package/src/angular/services/accelerometer.service/accelerometer.service.test.ts +118 -0
- package/src/angular/services/accelerometer.service/index.ts +39 -0
- package/src/angular/services/barometer.service/barometer.service.test.ts +122 -0
- package/src/angular/services/barometer.service/index.ts +39 -0
- package/src/angular/services/device-motion.service/device-motion.service.test.ts +125 -0
- package/src/angular/services/device-motion.service/index.ts +39 -0
- package/src/angular/services/gyroscope.service/gyroscope.service.test.ts +118 -0
- package/src/angular/services/gyroscope.service/index.ts +39 -0
- package/src/angular/services/light-sensor.service/index.ts +39 -0
- package/src/angular/services/light-sensor.service/light-sensor.service.test.ts +118 -0
- package/src/angular/services/magnetometer-uncalibrated.service/index.ts +39 -0
- package/src/angular/services/magnetometer-uncalibrated.service/magnetometer-uncalibrated.service.test.ts +123 -0
- package/src/angular/services/magnetometer.service/index.ts +39 -0
- package/src/angular/services/magnetometer.service/magnetometer.service.test.ts +118 -0
- package/src/angular/services/pedometer.service/index.ts +34 -0
- package/src/angular/services/pedometer.service/pedometer.service.test.ts +86 -0
- package/src/core/accelerometer.test.ts +49 -0
- package/src/core/accelerometer.ts +19 -0
- package/src/core/barometer.test.ts +46 -0
- package/src/core/barometer.ts +15 -0
- package/src/core/device-motion.test.ts +60 -0
- package/src/core/device-motion.ts +41 -0
- package/src/core/device-sensor.test.ts +76 -0
- package/src/core/device-sensor.ts +95 -0
- package/src/core/gyroscope.test.ts +46 -0
- package/src/core/gyroscope.ts +16 -0
- package/src/core/index.ts +37 -0
- package/src/core/light-sensor.test.ts +49 -0
- package/src/core/light-sensor.ts +18 -0
- package/src/core/magnetometer-uncalibrated.test.ts +49 -0
- package/src/core/magnetometer-uncalibrated.ts +20 -0
- package/src/core/magnetometer.test.ts +49 -0
- package/src/core/magnetometer.ts +19 -0
- package/src/core/native/exponent-accelerometer.ts +9 -0
- package/src/core/native/exponent-barometer.ts +9 -0
- package/src/core/native/exponent-device-motion.ts +15 -0
- package/src/core/native/exponent-gyroscope.ts +9 -0
- package/src/core/native/exponent-light-sensor/index.android.ts +9 -0
- package/src/core/native/exponent-light-sensor/index.ios.ts +12 -0
- package/src/core/native/exponent-light-sensor/index.ts +5 -0
- package/src/core/native/exponent-magnetometer-uncalibrated.ts +9 -0
- package/src/core/native/exponent-magnetometer.ts +9 -0
- package/src/core/native/exponent-pedometer.ts +17 -0
- package/src/core/pedometer.test.ts +118 -0
- package/src/core/pedometer.ts +62 -0
- package/src/react/hooks/use-accelerometer/index.ts +21 -0
- package/src/react/hooks/use-accelerometer/use-accelerometer.test.tsx +81 -0
- package/src/react/hooks/use-barometer/index.ts +21 -0
- package/src/react/hooks/use-barometer/use-barometer.test.tsx +83 -0
- package/src/react/hooks/use-device-motion/index.ts +21 -0
- package/src/react/hooks/use-device-motion/use-device-motion.test.tsx +88 -0
- package/src/react/hooks/use-gyroscope/index.ts +21 -0
- package/src/react/hooks/use-gyroscope/use-gyroscope.test.tsx +79 -0
- package/src/react/hooks/use-light-sensor/index.ts +21 -0
- package/src/react/hooks/use-light-sensor/use-light-sensor.test.tsx +79 -0
- package/src/react/hooks/use-magnetometer/index.ts +21 -0
- package/src/react/hooks/use-magnetometer/use-magnetometer.test.tsx +81 -0
- package/src/react/hooks/use-magnetometer-uncalibrated/index.ts +23 -0
- package/src/react/hooks/use-magnetometer-uncalibrated/use-magnetometer-uncalibrated.test.tsx +88 -0
- package/src/react/hooks/use-pedometer/index.ts +17 -0
- package/src/react/hooks/use-pedometer/use-pedometer.test.tsx +69 -0
- package/src/react/index.ts +29 -0
- package/src/vue/composables/use-accelerometer/index.ts +34 -0
- package/src/vue/composables/use-accelerometer/use-accelerometer.test.ts +96 -0
- package/src/vue/composables/use-barometer/index.ts +30 -0
- package/src/vue/composables/use-barometer/use-barometer.test.ts +100 -0
- package/src/vue/composables/use-device-motion/index.ts +30 -0
- package/src/vue/composables/use-device-motion/use-device-motion.test.ts +104 -0
- package/src/vue/composables/use-gyroscope/index.ts +30 -0
- package/src/vue/composables/use-gyroscope/use-gyroscope.test.ts +96 -0
- package/src/vue/composables/use-light-sensor/index.ts +30 -0
- package/src/vue/composables/use-light-sensor/use-light-sensor.test.ts +96 -0
- package/src/vue/composables/use-magnetometer/index.ts +30 -0
- package/src/vue/composables/use-magnetometer/use-magnetometer.test.ts +96 -0
- package/src/vue/composables/use-magnetometer-uncalibrated/index.ts +36 -0
- package/src/vue/composables/use-magnetometer-uncalibrated/use-magnetometer-uncalibrated.test.ts +103 -0
- package/src/vue/composables/use-pedometer/index.ts +25 -0
- package/src/vue/composables/use-pedometer/use-pedometer.test.ts +77 -0
- package/src/vue/index.ts +29 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Co-located Angular-driven test (ADR 0025) for LightSensorService. Mounts a real host
|
|
2
|
+
// component through @symbiote-native/angular so `connect()` runs the same way an app would call
|
|
3
|
+
// it — inside the component's own injection context — and drives the returned signal through a
|
|
4
|
+
// full mount/unmount lifecycle, because `effect()`'s injector-scoped cleanup only fires
|
|
5
|
+
// correctly when torn down through a real Angular injection context.
|
|
6
|
+
|
|
7
|
+
import '@angular/compiler';
|
|
8
|
+
import { Component, inject, type Signal } from '@angular/core';
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
10
|
+
import { mount, unmount } from '@symbiote-native/angular';
|
|
11
|
+
import { installFabric } from '@symbiote-native/test-utils';
|
|
12
|
+
import type { ILightSensorMeasurement } from '../../../core';
|
|
13
|
+
import { LightSensorService } from './index';
|
|
14
|
+
|
|
15
|
+
const addListenerMock = vi.fn();
|
|
16
|
+
const removeMock = vi.fn();
|
|
17
|
+
const setUpdateIntervalMock = vi.fn();
|
|
18
|
+
|
|
19
|
+
vi.mock('../../../core', () => ({
|
|
20
|
+
LightSensor: {
|
|
21
|
+
addListener: (listener: (measurement: ILightSensorMeasurement) => void) =>
|
|
22
|
+
addListenerMock(listener),
|
|
23
|
+
setUpdateInterval: (intervalMs: number) => setUpdateIntervalMock(intervalMs),
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
const ROOT_TAG = 942;
|
|
28
|
+
const fabric = installFabric();
|
|
29
|
+
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
30
|
+
|
|
31
|
+
const MEASUREMENT: ILightSensorMeasurement = { illuminance: 42, timestamp: 123 };
|
|
32
|
+
|
|
33
|
+
let capturedResult: Signal<ILightSensorMeasurement | null> | undefined;
|
|
34
|
+
let capturedListener: ((measurement: ILightSensorMeasurement) => void) | undefined;
|
|
35
|
+
|
|
36
|
+
@Component({
|
|
37
|
+
selector: 'symbiote-light-sensor-host',
|
|
38
|
+
standalone: true,
|
|
39
|
+
template: '',
|
|
40
|
+
})
|
|
41
|
+
class LightSensorHost {
|
|
42
|
+
readonly measurement = inject(LightSensorService).connect();
|
|
43
|
+
|
|
44
|
+
constructor() {
|
|
45
|
+
capturedResult = this.measurement;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Component({
|
|
50
|
+
selector: 'symbiote-light-sensor-interval-host',
|
|
51
|
+
standalone: true,
|
|
52
|
+
template: '',
|
|
53
|
+
})
|
|
54
|
+
class LightSensorIntervalHost {
|
|
55
|
+
readonly measurement = inject(LightSensorService).connect(500);
|
|
56
|
+
|
|
57
|
+
constructor() {
|
|
58
|
+
capturedResult = this.measurement;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
beforeEach(() => {
|
|
63
|
+
capturedResult = undefined;
|
|
64
|
+
capturedListener = undefined;
|
|
65
|
+
addListenerMock.mockImplementation(listener => {
|
|
66
|
+
capturedListener = listener;
|
|
67
|
+
return { remove: removeMock };
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
afterEach(() => {
|
|
72
|
+
unmount(ROOT_TAG);
|
|
73
|
+
fabric.reset();
|
|
74
|
+
vi.clearAllMocks();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('LightSensorService.connect', () => {
|
|
78
|
+
it('reports null before any measurement event fires', async () => {
|
|
79
|
+
mount(ROOT_TAG, LightSensorHost);
|
|
80
|
+
await tick();
|
|
81
|
+
|
|
82
|
+
expect(capturedResult?.()).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('updates the signal when the registered listener fires with a measurement', async () => {
|
|
86
|
+
mount(ROOT_TAG, LightSensorHost);
|
|
87
|
+
await tick();
|
|
88
|
+
|
|
89
|
+
if (capturedListener === undefined) throw new Error('addListener callback was not captured');
|
|
90
|
+
capturedListener(MEASUREMENT);
|
|
91
|
+
|
|
92
|
+
expect(capturedResult?.()).toEqual(MEASUREMENT);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('removes the subscription when the host component is unmounted', async () => {
|
|
96
|
+
mount(ROOT_TAG, LightSensorHost);
|
|
97
|
+
await tick();
|
|
98
|
+
|
|
99
|
+
unmount(ROOT_TAG);
|
|
100
|
+
await tick();
|
|
101
|
+
|
|
102
|
+
expect(removeMock).toHaveBeenCalledOnce();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('calls setUpdateInterval with the given interval when one is passed', async () => {
|
|
106
|
+
mount(ROOT_TAG, LightSensorIntervalHost);
|
|
107
|
+
await tick();
|
|
108
|
+
|
|
109
|
+
expect(setUpdateIntervalMock).toHaveBeenCalledWith(500);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('does not call setUpdateInterval when no interval is passed', async () => {
|
|
113
|
+
mount(ROOT_TAG, LightSensorHost);
|
|
114
|
+
await tick();
|
|
115
|
+
|
|
116
|
+
expect(setUpdateIntervalMock).not.toHaveBeenCalled();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
|
|
2
|
+
import { MagnetometerUncalibrated, type IMagnetometerUncalibratedMeasurement } from '../../../core';
|
|
3
|
+
|
|
4
|
+
// Angular twin of React's `useMagnetometerUncalibrated` hook and Vue's
|
|
5
|
+
// `useMagnetometerUncalibrated` composable. Angular has no per-instance hook — state and
|
|
6
|
+
// lifecycle live in DI instead, so `connect()` stands in for the hook's role: call it ONCE
|
|
7
|
+
// (typically from a component's field initializer, inside an injection context).
|
|
8
|
+
//
|
|
9
|
+
// readonly measurement = inject(MagnetometerUncalibratedService).connect();
|
|
10
|
+
// // template: {{ measurement()?.x }}
|
|
11
|
+
//
|
|
12
|
+
// Unlike HideAnimationService.connect(), there is no per-render config to re-sync — the
|
|
13
|
+
// subscription doesn't depend on anything the caller's own signals could change between
|
|
14
|
+
// renders — so a single effect() that subscribes once and cleans up once is enough; no
|
|
15
|
+
// `updateConfig`-style effect that re-runs on every read is needed here.
|
|
16
|
+
@Injectable({ providedIn: 'root' })
|
|
17
|
+
export class MagnetometerUncalibratedService {
|
|
18
|
+
// Captured in the constructor (itself always run inside an injection context by Angular's
|
|
19
|
+
// own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
|
|
20
|
+
// code that is not, on its own, an active injection context — mirrors HideAnimationService.
|
|
21
|
+
private readonly injector = inject(Injector);
|
|
22
|
+
|
|
23
|
+
connect(updateIntervalMs?: number): Signal<IMagnetometerUncalibratedMeasurement | null> {
|
|
24
|
+
const measurement = signal<IMagnetometerUncalibratedMeasurement | null>(null);
|
|
25
|
+
|
|
26
|
+
effect(
|
|
27
|
+
onCleanup => {
|
|
28
|
+
if (updateIntervalMs !== undefined) {
|
|
29
|
+
MagnetometerUncalibrated.setUpdateInterval(updateIntervalMs);
|
|
30
|
+
}
|
|
31
|
+
const subscription = MagnetometerUncalibrated.addListener(next => measurement.set(next));
|
|
32
|
+
onCleanup(() => subscription.remove());
|
|
33
|
+
},
|
|
34
|
+
{ injector: this.injector },
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return measurement.asReadonly();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// Co-located Angular-driven test (ADR 0025) for MagnetometerUncalibratedService. Mounts a real
|
|
2
|
+
// host component through @symbiote-native/angular so `connect()` runs the same way an app would
|
|
3
|
+
// call it — inside the component's own injection context — and drives the returned signal
|
|
4
|
+
// through a full mount/unmount lifecycle, because `effect()`'s injector-scoped cleanup only
|
|
5
|
+
// fires correctly when torn down through a real Angular injection context.
|
|
6
|
+
|
|
7
|
+
import '@angular/compiler';
|
|
8
|
+
import { Component, inject, type Signal } from '@angular/core';
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
10
|
+
import { mount, unmount } from '@symbiote-native/angular';
|
|
11
|
+
import { installFabric } from '@symbiote-native/test-utils';
|
|
12
|
+
import type { IMagnetometerUncalibratedMeasurement } from '../../../core';
|
|
13
|
+
import { MagnetometerUncalibratedService } from './index';
|
|
14
|
+
|
|
15
|
+
const addListenerMock = vi.fn();
|
|
16
|
+
const removeMock = vi.fn();
|
|
17
|
+
const setUpdateIntervalMock = vi.fn();
|
|
18
|
+
|
|
19
|
+
vi.mock('../../../core', () => ({
|
|
20
|
+
MagnetometerUncalibrated: {
|
|
21
|
+
addListener: (listener: (measurement: IMagnetometerUncalibratedMeasurement) => void) =>
|
|
22
|
+
addListenerMock(listener),
|
|
23
|
+
setUpdateInterval: (intervalMs: number) => setUpdateIntervalMock(intervalMs),
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
const ROOT_TAG = 942;
|
|
28
|
+
const fabric = installFabric();
|
|
29
|
+
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
30
|
+
|
|
31
|
+
const MEASUREMENT: IMagnetometerUncalibratedMeasurement = {
|
|
32
|
+
x: 0.1,
|
|
33
|
+
y: 0.2,
|
|
34
|
+
z: 0.3,
|
|
35
|
+
timestamp: 123,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
let capturedResult: Signal<IMagnetometerUncalibratedMeasurement | null> | undefined;
|
|
39
|
+
let capturedListener: ((measurement: IMagnetometerUncalibratedMeasurement) => void) | undefined;
|
|
40
|
+
|
|
41
|
+
@Component({
|
|
42
|
+
selector: 'symbiote-magnetometer-uncalibrated-host',
|
|
43
|
+
standalone: true,
|
|
44
|
+
template: '',
|
|
45
|
+
})
|
|
46
|
+
class MagnetometerUncalibratedHost {
|
|
47
|
+
readonly measurement = inject(MagnetometerUncalibratedService).connect();
|
|
48
|
+
|
|
49
|
+
constructor() {
|
|
50
|
+
capturedResult = this.measurement;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Component({
|
|
55
|
+
selector: 'symbiote-magnetometer-uncalibrated-interval-host',
|
|
56
|
+
standalone: true,
|
|
57
|
+
template: '',
|
|
58
|
+
})
|
|
59
|
+
class MagnetometerUncalibratedIntervalHost {
|
|
60
|
+
readonly measurement = inject(MagnetometerUncalibratedService).connect(500);
|
|
61
|
+
|
|
62
|
+
constructor() {
|
|
63
|
+
capturedResult = this.measurement;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
capturedResult = undefined;
|
|
69
|
+
capturedListener = undefined;
|
|
70
|
+
addListenerMock.mockImplementation(listener => {
|
|
71
|
+
capturedListener = listener;
|
|
72
|
+
return { remove: removeMock };
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
afterEach(() => {
|
|
77
|
+
unmount(ROOT_TAG);
|
|
78
|
+
fabric.reset();
|
|
79
|
+
vi.clearAllMocks();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('MagnetometerUncalibratedService.connect', () => {
|
|
83
|
+
it('reports null before any measurement event fires', async () => {
|
|
84
|
+
mount(ROOT_TAG, MagnetometerUncalibratedHost);
|
|
85
|
+
await tick();
|
|
86
|
+
|
|
87
|
+
expect(capturedResult?.()).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('updates the signal when the registered listener fires with a measurement', async () => {
|
|
91
|
+
mount(ROOT_TAG, MagnetometerUncalibratedHost);
|
|
92
|
+
await tick();
|
|
93
|
+
|
|
94
|
+
if (capturedListener === undefined) throw new Error('addListener callback was not captured');
|
|
95
|
+
capturedListener(MEASUREMENT);
|
|
96
|
+
|
|
97
|
+
expect(capturedResult?.()).toEqual(MEASUREMENT);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('removes the subscription when the host component is unmounted', async () => {
|
|
101
|
+
mount(ROOT_TAG, MagnetometerUncalibratedHost);
|
|
102
|
+
await tick();
|
|
103
|
+
|
|
104
|
+
unmount(ROOT_TAG);
|
|
105
|
+
await tick();
|
|
106
|
+
|
|
107
|
+
expect(removeMock).toHaveBeenCalledOnce();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('calls setUpdateInterval with the given interval when one is passed', async () => {
|
|
111
|
+
mount(ROOT_TAG, MagnetometerUncalibratedIntervalHost);
|
|
112
|
+
await tick();
|
|
113
|
+
|
|
114
|
+
expect(setUpdateIntervalMock).toHaveBeenCalledWith(500);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('does not call setUpdateInterval when no interval is passed', async () => {
|
|
118
|
+
mount(ROOT_TAG, MagnetometerUncalibratedHost);
|
|
119
|
+
await tick();
|
|
120
|
+
|
|
121
|
+
expect(setUpdateIntervalMock).not.toHaveBeenCalled();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
|
|
2
|
+
import { Magnetometer, type IMagnetometerMeasurement } from '../../../core';
|
|
3
|
+
|
|
4
|
+
// Angular twin of React's `useMagnetometer` hook and Vue's `useMagnetometer` composable.
|
|
5
|
+
// Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
|
|
6
|
+
// stands in for the hook's role: call it ONCE (typically from a component's field initializer,
|
|
7
|
+
// inside an injection context).
|
|
8
|
+
//
|
|
9
|
+
// readonly measurement = inject(MagnetometerService).connect();
|
|
10
|
+
// // template: {{ measurement()?.x }}
|
|
11
|
+
//
|
|
12
|
+
// Unlike HideAnimationService.connect(), there is no per-render config to re-sync — the
|
|
13
|
+
// subscription doesn't depend on anything the caller's own signals could change between
|
|
14
|
+
// renders — so a single effect() that subscribes once and cleans up once is enough; no
|
|
15
|
+
// `updateConfig`-style effect that re-runs on every read is needed here.
|
|
16
|
+
@Injectable({ providedIn: 'root' })
|
|
17
|
+
export class MagnetometerService {
|
|
18
|
+
// Captured in the constructor (itself always run inside an injection context by Angular's
|
|
19
|
+
// own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
|
|
20
|
+
// code that is not, on its own, an active injection context — mirrors HideAnimationService.
|
|
21
|
+
private readonly injector = inject(Injector);
|
|
22
|
+
|
|
23
|
+
connect(updateIntervalMs?: number): Signal<IMagnetometerMeasurement | null> {
|
|
24
|
+
const measurement = signal<IMagnetometerMeasurement | null>(null);
|
|
25
|
+
|
|
26
|
+
effect(
|
|
27
|
+
onCleanup => {
|
|
28
|
+
if (updateIntervalMs !== undefined) {
|
|
29
|
+
Magnetometer.setUpdateInterval(updateIntervalMs);
|
|
30
|
+
}
|
|
31
|
+
const subscription = Magnetometer.addListener(next => measurement.set(next));
|
|
32
|
+
onCleanup(() => subscription.remove());
|
|
33
|
+
},
|
|
34
|
+
{ injector: this.injector },
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return measurement.asReadonly();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Co-located Angular-driven test (ADR 0025) for MagnetometerService. Mounts a real host
|
|
2
|
+
// component through @symbiote-native/angular so `connect()` runs the same way an app would call
|
|
3
|
+
// it — inside the component's own injection context — and drives the returned signal through a
|
|
4
|
+
// full mount/unmount lifecycle, because `effect()`'s injector-scoped cleanup only fires
|
|
5
|
+
// correctly when torn down through a real Angular injection context.
|
|
6
|
+
|
|
7
|
+
import '@angular/compiler';
|
|
8
|
+
import { Component, inject, type Signal } from '@angular/core';
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
10
|
+
import { mount, unmount } from '@symbiote-native/angular';
|
|
11
|
+
import { installFabric } from '@symbiote-native/test-utils';
|
|
12
|
+
import type { IMagnetometerMeasurement } from '../../../core';
|
|
13
|
+
import { MagnetometerService } from './index';
|
|
14
|
+
|
|
15
|
+
const addListenerMock = vi.fn();
|
|
16
|
+
const removeMock = vi.fn();
|
|
17
|
+
const setUpdateIntervalMock = vi.fn();
|
|
18
|
+
|
|
19
|
+
vi.mock('../../../core', () => ({
|
|
20
|
+
Magnetometer: {
|
|
21
|
+
addListener: (listener: (measurement: IMagnetometerMeasurement) => void) =>
|
|
22
|
+
addListenerMock(listener),
|
|
23
|
+
setUpdateInterval: (intervalMs: number) => setUpdateIntervalMock(intervalMs),
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
const ROOT_TAG = 942;
|
|
28
|
+
const fabric = installFabric();
|
|
29
|
+
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
30
|
+
|
|
31
|
+
const MEASUREMENT: IMagnetometerMeasurement = { x: 0.1, y: 0.2, z: 0.3, timestamp: 123 };
|
|
32
|
+
|
|
33
|
+
let capturedResult: Signal<IMagnetometerMeasurement | null> | undefined;
|
|
34
|
+
let capturedListener: ((measurement: IMagnetometerMeasurement) => void) | undefined;
|
|
35
|
+
|
|
36
|
+
@Component({
|
|
37
|
+
selector: 'symbiote-magnetometer-host',
|
|
38
|
+
standalone: true,
|
|
39
|
+
template: '',
|
|
40
|
+
})
|
|
41
|
+
class MagnetometerHost {
|
|
42
|
+
readonly measurement = inject(MagnetometerService).connect();
|
|
43
|
+
|
|
44
|
+
constructor() {
|
|
45
|
+
capturedResult = this.measurement;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Component({
|
|
50
|
+
selector: 'symbiote-magnetometer-interval-host',
|
|
51
|
+
standalone: true,
|
|
52
|
+
template: '',
|
|
53
|
+
})
|
|
54
|
+
class MagnetometerIntervalHost {
|
|
55
|
+
readonly measurement = inject(MagnetometerService).connect(500);
|
|
56
|
+
|
|
57
|
+
constructor() {
|
|
58
|
+
capturedResult = this.measurement;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
beforeEach(() => {
|
|
63
|
+
capturedResult = undefined;
|
|
64
|
+
capturedListener = undefined;
|
|
65
|
+
addListenerMock.mockImplementation(listener => {
|
|
66
|
+
capturedListener = listener;
|
|
67
|
+
return { remove: removeMock };
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
afterEach(() => {
|
|
72
|
+
unmount(ROOT_TAG);
|
|
73
|
+
fabric.reset();
|
|
74
|
+
vi.clearAllMocks();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('MagnetometerService.connect', () => {
|
|
78
|
+
it('reports null before any measurement event fires', async () => {
|
|
79
|
+
mount(ROOT_TAG, MagnetometerHost);
|
|
80
|
+
await tick();
|
|
81
|
+
|
|
82
|
+
expect(capturedResult?.()).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('updates the signal when the registered listener fires with a measurement', async () => {
|
|
86
|
+
mount(ROOT_TAG, MagnetometerHost);
|
|
87
|
+
await tick();
|
|
88
|
+
|
|
89
|
+
if (capturedListener === undefined) throw new Error('addListener callback was not captured');
|
|
90
|
+
capturedListener(MEASUREMENT);
|
|
91
|
+
|
|
92
|
+
expect(capturedResult?.()).toEqual(MEASUREMENT);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('removes the subscription when the host component is unmounted', async () => {
|
|
96
|
+
mount(ROOT_TAG, MagnetometerHost);
|
|
97
|
+
await tick();
|
|
98
|
+
|
|
99
|
+
unmount(ROOT_TAG);
|
|
100
|
+
await tick();
|
|
101
|
+
|
|
102
|
+
expect(removeMock).toHaveBeenCalledOnce();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('calls setUpdateInterval with the given interval when one is passed', async () => {
|
|
106
|
+
mount(ROOT_TAG, MagnetometerIntervalHost);
|
|
107
|
+
await tick();
|
|
108
|
+
|
|
109
|
+
expect(setUpdateIntervalMock).toHaveBeenCalledWith(500);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('does not call setUpdateInterval when no interval is passed', async () => {
|
|
113
|
+
mount(ROOT_TAG, MagnetometerHost);
|
|
114
|
+
await tick();
|
|
115
|
+
|
|
116
|
+
expect(setUpdateIntervalMock).not.toHaveBeenCalled();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
|
|
2
|
+
import { watchStepCount, type IPedometerResult } from '../../../core';
|
|
3
|
+
|
|
4
|
+
// Angular twin of React's `usePedometer` hook and Vue's `usePedometer` composable. Angular has
|
|
5
|
+
// no per-instance hook — state and lifecycle live in DI instead, so `connect()` stands in for
|
|
6
|
+
// the hook's role: call it ONCE (typically from a component's field initializer, inside an
|
|
7
|
+
// injection context).
|
|
8
|
+
//
|
|
9
|
+
// readonly result = inject(PedometerService).connect();
|
|
10
|
+
// // template: {{ result()?.steps }}
|
|
11
|
+
//
|
|
12
|
+
// Unlike AccelerometerService.connect(), there is no updateIntervalMs to apply — Pedometer has
|
|
13
|
+
// no setUpdateInterval — so a single effect() that subscribes once and cleans up once is enough.
|
|
14
|
+
@Injectable({ providedIn: 'root' })
|
|
15
|
+
export class PedometerService {
|
|
16
|
+
// Captured in the constructor (itself always run inside an injection context by Angular's own
|
|
17
|
+
// DI) so `connect()` can create an `effect()` even when called from plain field-initializer
|
|
18
|
+
// code that is not, on its own, an active injection context — mirrors AccelerometerService.
|
|
19
|
+
private readonly injector = inject(Injector);
|
|
20
|
+
|
|
21
|
+
connect(): Signal<IPedometerResult | null> {
|
|
22
|
+
const result = signal<IPedometerResult | null>(null);
|
|
23
|
+
|
|
24
|
+
effect(
|
|
25
|
+
onCleanup => {
|
|
26
|
+
const subscription = watchStepCount(next => result.set(next));
|
|
27
|
+
onCleanup(() => subscription.remove());
|
|
28
|
+
},
|
|
29
|
+
{ injector: this.injector },
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return result.asReadonly();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Co-located Angular-driven test (ADR 0025) for PedometerService. Mounts a real host component
|
|
2
|
+
// through @symbiote-native/angular so `connect()` runs the same way an app would call it —
|
|
3
|
+
// inside the component's own injection context — and drives the returned signal through a full
|
|
4
|
+
// mount/unmount lifecycle, because `effect()`'s injector-scoped cleanup only fires correctly
|
|
5
|
+
// when torn down through a real Angular injection context.
|
|
6
|
+
|
|
7
|
+
import '@angular/compiler';
|
|
8
|
+
import { Component, inject, type Signal } from '@angular/core';
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
10
|
+
import { mount, unmount } from '@symbiote-native/angular';
|
|
11
|
+
import { installFabric } from '@symbiote-native/test-utils';
|
|
12
|
+
import type { IPedometerResult } from '../../../core';
|
|
13
|
+
import { PedometerService } from './index';
|
|
14
|
+
|
|
15
|
+
const watchStepCountMock = vi.fn();
|
|
16
|
+
const removeMock = vi.fn();
|
|
17
|
+
|
|
18
|
+
vi.mock('../../../core', () => ({
|
|
19
|
+
watchStepCount: (listener: (result: IPedometerResult) => void) => watchStepCountMock(listener),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
const ROOT_TAG = 942;
|
|
23
|
+
const fabric = installFabric();
|
|
24
|
+
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
25
|
+
|
|
26
|
+
const RESULT: IPedometerResult = { steps: 789 };
|
|
27
|
+
|
|
28
|
+
let capturedResult: Signal<IPedometerResult | null> | undefined;
|
|
29
|
+
let capturedListener: ((result: IPedometerResult) => void) | undefined;
|
|
30
|
+
|
|
31
|
+
@Component({
|
|
32
|
+
selector: 'symbiote-pedometer-host',
|
|
33
|
+
standalone: true,
|
|
34
|
+
template: '',
|
|
35
|
+
})
|
|
36
|
+
class PedometerHost {
|
|
37
|
+
readonly result = inject(PedometerService).connect();
|
|
38
|
+
|
|
39
|
+
constructor() {
|
|
40
|
+
capturedResult = this.result;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
capturedResult = undefined;
|
|
46
|
+
capturedListener = undefined;
|
|
47
|
+
watchStepCountMock.mockImplementation(listener => {
|
|
48
|
+
capturedListener = listener;
|
|
49
|
+
return { remove: removeMock };
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
unmount(ROOT_TAG);
|
|
55
|
+
fabric.reset();
|
|
56
|
+
vi.clearAllMocks();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('PedometerService.connect', () => {
|
|
60
|
+
it('reports null before any step count event fires', async () => {
|
|
61
|
+
mount(ROOT_TAG, PedometerHost);
|
|
62
|
+
await tick();
|
|
63
|
+
|
|
64
|
+
expect(capturedResult?.()).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('updates the signal when the registered listener fires with a result', async () => {
|
|
68
|
+
mount(ROOT_TAG, PedometerHost);
|
|
69
|
+
await tick();
|
|
70
|
+
|
|
71
|
+
if (capturedListener === undefined) throw new Error('watchStepCount callback was not captured');
|
|
72
|
+
capturedListener(RESULT);
|
|
73
|
+
|
|
74
|
+
expect(capturedResult?.()).toEqual(RESULT);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('removes the subscription when the host component is unmounted', async () => {
|
|
78
|
+
mount(ROOT_TAG, PedometerHost);
|
|
79
|
+
await tick();
|
|
80
|
+
|
|
81
|
+
unmount(ROOT_TAG);
|
|
82
|
+
await tick();
|
|
83
|
+
|
|
84
|
+
expect(removeMock).toHaveBeenCalledOnce();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const FAKE_NATIVE_ACCELEROMETER = {
|
|
4
|
+
addListener: vi.fn(() => ({ remove: vi.fn() })),
|
|
5
|
+
listenerCount: vi.fn(() => 0),
|
|
6
|
+
removeAllListeners: vi.fn(),
|
|
7
|
+
setUpdateInterval: vi.fn(),
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// The real ExponentAccelerometer native module only exists on device — resolving it via
|
|
11
|
+
// requireNativeModule() at import time would throw in this headless test run, so the
|
|
12
|
+
// module-lookup file is faked in place of `expo-modules-core`'s runtime resolution.
|
|
13
|
+
vi.mock('./native/exponent-accelerometer', () => ({
|
|
14
|
+
exponentAccelerometer: FAKE_NATIVE_ACCELEROMETER,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
// device-sensor.ts (imported transitively through ./accelerometer) pulls Platform/PermissionStatus
|
|
18
|
+
// from expo-modules-core, whose real entry drags in the Flow-typed 'react-native' source that
|
|
19
|
+
// Vitest's Oxc transform can't parse — same fake as device-sensor.test.ts.
|
|
20
|
+
vi.mock('expo-modules-core', () => ({
|
|
21
|
+
Platform: { OS: 'ios' },
|
|
22
|
+
PermissionStatus: { GRANTED: 'granted', UNDETERMINED: 'undetermined', DENIED: 'denied' },
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
const { Accelerometer } = await import('./accelerometer');
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
Accelerometer.removeAllListeners();
|
|
29
|
+
vi.clearAllMocks();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('Accelerometer', () => {
|
|
33
|
+
it('sets the update interval', () => {
|
|
34
|
+
Accelerometer.setUpdateInterval(1234);
|
|
35
|
+
|
|
36
|
+
expect(FAKE_NATIVE_ACCELEROMETER.setUpdateInterval).toHaveBeenCalledTimes(1);
|
|
37
|
+
expect(FAKE_NATIVE_ACCELEROMETER.setUpdateInterval).toHaveBeenCalledWith(1234);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('subscribes through the shared accelerometerDidUpdate event name', () => {
|
|
41
|
+
const listener = vi.fn();
|
|
42
|
+
Accelerometer.addListener(listener);
|
|
43
|
+
|
|
44
|
+
expect(FAKE_NATIVE_ACCELEROMETER.addListener).toHaveBeenCalledWith(
|
|
45
|
+
'accelerometerDidUpdate',
|
|
46
|
+
listener,
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DeviceSensor } from './device-sensor';
|
|
2
|
+
import { exponentAccelerometer } from './native/exponent-accelerometer';
|
|
3
|
+
|
|
4
|
+
// Values are g-force (a `g` = 9.81 m/s^2) reported along each axis; timestamp is in seconds.
|
|
5
|
+
export type IAccelerometerMeasurement = {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
z: number;
|
|
9
|
+
timestamp: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export class AccelerometerSensor extends DeviceSensor<IAccelerometerMeasurement> {}
|
|
13
|
+
|
|
14
|
+
const ACCELEROMETER_DID_UPDATE_EVENT_NAME = 'accelerometerDidUpdate';
|
|
15
|
+
|
|
16
|
+
export const Accelerometer = new AccelerometerSensor(
|
|
17
|
+
exponentAccelerometer,
|
|
18
|
+
ACCELEROMETER_DID_UPDATE_EVENT_NAME,
|
|
19
|
+
);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const FAKE_NATIVE_BAROMETER = {
|
|
4
|
+
addListener: vi.fn(() => ({ remove: vi.fn() })),
|
|
5
|
+
listenerCount: vi.fn(() => 0),
|
|
6
|
+
removeAllListeners: vi.fn(),
|
|
7
|
+
setUpdateInterval: vi.fn(),
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// The real ExpoBarometer native module only exists on device — resolving it via
|
|
11
|
+
// requireNativeModule() at import time would throw in this headless test run, so the
|
|
12
|
+
// module-lookup file is faked in place of `expo-modules-core`'s runtime resolution.
|
|
13
|
+
vi.mock('./native/exponent-barometer', () => ({
|
|
14
|
+
exponentBarometer: FAKE_NATIVE_BAROMETER,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
// device-sensor.ts (imported transitively through ./barometer) pulls Platform/PermissionStatus
|
|
18
|
+
// from expo-modules-core, whose real entry drags in the Flow-typed 'react-native' source that
|
|
19
|
+
// Vitest's Oxc transform can't parse — same fake as device-sensor.test.ts.
|
|
20
|
+
vi.mock('expo-modules-core', () => ({
|
|
21
|
+
Platform: { OS: 'ios' },
|
|
22
|
+
PermissionStatus: { GRANTED: 'granted', UNDETERMINED: 'undetermined', DENIED: 'denied' },
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
const { Barometer } = await import('./barometer');
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
Barometer.removeAllListeners();
|
|
29
|
+
vi.clearAllMocks();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('Barometer', () => {
|
|
33
|
+
it('sets the update interval', () => {
|
|
34
|
+
Barometer.setUpdateInterval(1234);
|
|
35
|
+
|
|
36
|
+
expect(FAKE_NATIVE_BAROMETER.setUpdateInterval).toHaveBeenCalledTimes(1);
|
|
37
|
+
expect(FAKE_NATIVE_BAROMETER.setUpdateInterval).toHaveBeenCalledWith(1234);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('subscribes through the shared barometerDidUpdate event name', () => {
|
|
41
|
+
const listener = vi.fn();
|
|
42
|
+
Barometer.addListener(listener);
|
|
43
|
+
|
|
44
|
+
expect(FAKE_NATIVE_BAROMETER.addListener).toHaveBeenCalledWith('barometerDidUpdate', listener);
|
|
45
|
+
});
|
|
46
|
+
});
|