@symbiote-native/sensors 0.2.0 → 0.3.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.
Files changed (100) hide show
  1. package/README.md +5 -5
  2. package/build/angular/services/accelerometer.service/index.js +7 -12
  3. package/build/angular/services/barometer.service/index.js +7 -12
  4. package/build/angular/services/device-motion.service/index.js +7 -12
  5. package/build/angular/services/gyroscope.service/index.js +7 -12
  6. package/build/angular/services/light-sensor.service/index.js +7 -12
  7. package/build/angular/services/magnetometer-uncalibrated.service/index.js +8 -12
  8. package/build/angular/services/magnetometer.service/index.js +7 -12
  9. package/build/angular/services/pedometer.service/index.js +6 -10
  10. package/build/core/device-motion.js +3 -4
  11. package/build/svelte/index.d.ts +16 -0
  12. package/build/svelte/index.js +14 -0
  13. package/build/svelte/runes/use-accelerometer.svelte.d.ts +4 -0
  14. package/build/svelte/runes/use-accelerometer.svelte.js +31 -0
  15. package/build/svelte/runes/use-barometer.svelte.d.ts +4 -0
  16. package/build/svelte/runes/use-barometer.svelte.js +22 -0
  17. package/build/svelte/runes/use-device-motion.svelte.d.ts +4 -0
  18. package/build/svelte/runes/use-device-motion.svelte.js +22 -0
  19. package/build/svelte/runes/use-gyroscope.svelte.d.ts +4 -0
  20. package/build/svelte/runes/use-gyroscope.svelte.js +22 -0
  21. package/build/svelte/runes/use-light-sensor.svelte.d.ts +4 -0
  22. package/build/svelte/runes/use-light-sensor.svelte.js +22 -0
  23. package/build/svelte/runes/use-magnetometer-uncalibrated.svelte.d.ts +4 -0
  24. package/build/svelte/runes/use-magnetometer-uncalibrated.svelte.js +22 -0
  25. package/build/svelte/runes/use-magnetometer.svelte.d.ts +4 -0
  26. package/build/svelte/runes/use-magnetometer.svelte.js +22 -0
  27. package/build/svelte/runes/use-pedometer.svelte.d.ts +4 -0
  28. package/build/svelte/runes/use-pedometer.svelte.js +20 -0
  29. package/build-ngc/angular/services/accelerometer.service/index.js +10 -15
  30. package/build-ngc/angular/services/accelerometer.service/index.js.map +1 -1
  31. package/build-ngc/angular/services/barometer.service/index.js +10 -15
  32. package/build-ngc/angular/services/barometer.service/index.js.map +1 -1
  33. package/build-ngc/angular/services/device-motion.service/index.js +10 -15
  34. package/build-ngc/angular/services/device-motion.service/index.js.map +1 -1
  35. package/build-ngc/angular/services/gyroscope.service/index.js +10 -15
  36. package/build-ngc/angular/services/gyroscope.service/index.js.map +1 -1
  37. package/build-ngc/angular/services/light-sensor.service/index.js +10 -15
  38. package/build-ngc/angular/services/light-sensor.service/index.js.map +1 -1
  39. package/build-ngc/angular/services/magnetometer-uncalibrated.service/index.js +11 -15
  40. package/build-ngc/angular/services/magnetometer-uncalibrated.service/index.js.map +1 -1
  41. package/build-ngc/angular/services/magnetometer.service/index.js +10 -15
  42. package/build-ngc/angular/services/magnetometer.service/index.js.map +1 -1
  43. package/build-ngc/angular/services/pedometer.service/index.js +9 -13
  44. package/build-ngc/angular/services/pedometer.service/index.js.map +1 -1
  45. package/build-ngc/core/device-motion.js +3 -4
  46. package/build-ngc/core/device-motion.js.map +1 -1
  47. package/native-link.json +52 -0
  48. package/package.json +31 -13
  49. package/src/angular/services/accelerometer.service/index.ts +7 -12
  50. package/src/angular/services/barometer.service/index.ts +7 -12
  51. package/src/angular/services/device-motion.service/index.ts +7 -12
  52. package/src/angular/services/gyroscope.service/index.ts +7 -12
  53. package/src/angular/services/light-sensor.service/index.ts +7 -12
  54. package/src/angular/services/magnetometer-uncalibrated.service/index.ts +8 -12
  55. package/src/angular/services/magnetometer.service/index.ts +7 -12
  56. package/src/angular/services/pedometer.service/index.ts +6 -10
  57. package/src/core/device-motion.ts +3 -4
  58. package/src/svelte/index.ts +29 -0
  59. package/src/svelte/runes/use-accelerometer.svelte.ts +36 -0
  60. package/src/svelte/runes/use-barometer.svelte.ts +27 -0
  61. package/src/svelte/runes/use-device-motion.svelte.ts +27 -0
  62. package/src/svelte/runes/use-gyroscope.svelte.ts +27 -0
  63. package/src/svelte/runes/use-light-sensor.svelte.ts +27 -0
  64. package/src/svelte/runes/use-magnetometer-uncalibrated.svelte.ts +31 -0
  65. package/src/svelte/runes/use-magnetometer.svelte.ts +27 -0
  66. package/src/svelte/runes/use-pedometer.svelte.ts +23 -0
  67. package/src/svelte/svelte-ambient.d.ts +6 -0
  68. package/src/angular/services/accelerometer.service/accelerometer.service.test.ts +0 -118
  69. package/src/angular/services/barometer.service/barometer.service.test.ts +0 -122
  70. package/src/angular/services/device-motion.service/device-motion.service.test.ts +0 -125
  71. package/src/angular/services/gyroscope.service/gyroscope.service.test.ts +0 -118
  72. package/src/angular/services/light-sensor.service/light-sensor.service.test.ts +0 -118
  73. package/src/angular/services/magnetometer-uncalibrated.service/magnetometer-uncalibrated.service.test.ts +0 -123
  74. package/src/angular/services/magnetometer.service/magnetometer.service.test.ts +0 -118
  75. package/src/angular/services/pedometer.service/pedometer.service.test.ts +0 -86
  76. package/src/core/accelerometer.test.ts +0 -49
  77. package/src/core/barometer.test.ts +0 -46
  78. package/src/core/device-motion.test.ts +0 -60
  79. package/src/core/device-sensor.test.ts +0 -76
  80. package/src/core/gyroscope.test.ts +0 -46
  81. package/src/core/light-sensor.test.ts +0 -49
  82. package/src/core/magnetometer-uncalibrated.test.ts +0 -49
  83. package/src/core/magnetometer.test.ts +0 -49
  84. package/src/core/pedometer.test.ts +0 -118
  85. package/src/react/hooks/use-accelerometer/use-accelerometer.test.tsx +0 -81
  86. package/src/react/hooks/use-barometer/use-barometer.test.tsx +0 -83
  87. package/src/react/hooks/use-device-motion/use-device-motion.test.tsx +0 -88
  88. package/src/react/hooks/use-gyroscope/use-gyroscope.test.tsx +0 -79
  89. package/src/react/hooks/use-light-sensor/use-light-sensor.test.tsx +0 -79
  90. package/src/react/hooks/use-magnetometer/use-magnetometer.test.tsx +0 -81
  91. package/src/react/hooks/use-magnetometer-uncalibrated/use-magnetometer-uncalibrated.test.tsx +0 -88
  92. package/src/react/hooks/use-pedometer/use-pedometer.test.tsx +0 -69
  93. package/src/vue/composables/use-accelerometer/use-accelerometer.test.ts +0 -96
  94. package/src/vue/composables/use-barometer/use-barometer.test.ts +0 -100
  95. package/src/vue/composables/use-device-motion/use-device-motion.test.ts +0 -104
  96. package/src/vue/composables/use-gyroscope/use-gyroscope.test.ts +0 -96
  97. package/src/vue/composables/use-light-sensor/use-light-sensor.test.ts +0 -96
  98. package/src/vue/composables/use-magnetometer/use-magnetometer.test.ts +0 -96
  99. package/src/vue/composables/use-magnetometer-uncalibrated/use-magnetometer-uncalibrated.test.ts +0 -103
  100. package/src/vue/composables/use-pedometer/use-pedometer.test.ts +0 -77
@@ -1,123 +0,0 @@
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
- });
@@ -1,118 +0,0 @@
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
- });
@@ -1,86 +0,0 @@
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
- });
@@ -1,49 +0,0 @@
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
- });
@@ -1,46 +0,0 @@
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
- });
@@ -1,60 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
-
3
- const FAKE_GRAVITY = 9.80665;
4
-
5
- const FAKE_NATIVE_DEVICE_MOTION = {
6
- addListener: vi.fn(() => ({ remove: vi.fn() })),
7
- listenerCount: vi.fn(() => 0),
8
- removeAllListeners: vi.fn(),
9
- setUpdateInterval: vi.fn(),
10
- Gravity: FAKE_GRAVITY,
11
- };
12
-
13
- // The real ExponentDeviceMotion native module only exists on device — resolving it via
14
- // requireNativeModule() at import time would throw in this headless test run, so the
15
- // module-lookup file is faked in place of `expo-modules-core`'s runtime resolution.
16
- vi.mock('./native/exponent-device-motion', () => ({
17
- exponentDeviceMotion: FAKE_NATIVE_DEVICE_MOTION,
18
- }));
19
-
20
- // device-sensor.ts (imported transitively through ./device-motion) pulls Platform/PermissionStatus
21
- // from expo-modules-core, whose real entry drags in the Flow-typed 'react-native' source that
22
- // Vitest's Oxc transform can't parse — same fake as device-sensor.test.ts.
23
- vi.mock('expo-modules-core', () => ({
24
- Platform: { OS: 'ios' },
25
- PermissionStatus: { GRANTED: 'granted', UNDETERMINED: 'undetermined', DENIED: 'denied' },
26
- }));
27
-
28
- const { DeviceMotion, gravity } = await import('./device-motion');
29
-
30
- afterEach(() => {
31
- DeviceMotion.removeAllListeners();
32
- vi.clearAllMocks();
33
- });
34
-
35
- describe('DeviceMotion', () => {
36
- it('sets the update interval', () => {
37
- DeviceMotion.setUpdateInterval(1234);
38
-
39
- expect(FAKE_NATIVE_DEVICE_MOTION.setUpdateInterval).toHaveBeenCalledTimes(1);
40
- expect(FAKE_NATIVE_DEVICE_MOTION.setUpdateInterval).toHaveBeenCalledWith(1234);
41
- });
42
-
43
- it('subscribes through the shared deviceMotionDidUpdate event name', () => {
44
- const listener = vi.fn();
45
- DeviceMotion.addListener(listener);
46
-
47
- expect(FAKE_NATIVE_DEVICE_MOTION.addListener).toHaveBeenCalledWith(
48
- 'deviceMotionDidUpdate',
49
- listener,
50
- );
51
- });
52
-
53
- it('exposes the native Gravity constant as a standalone export', () => {
54
- expect(gravity).toBe(FAKE_GRAVITY);
55
- });
56
-
57
- it('exposes the native Gravity constant as an instance property', () => {
58
- expect(DeviceMotion.gravity).toBe(FAKE_GRAVITY);
59
- });
60
- });
@@ -1,76 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import type { INativeSensorModule } from './device-sensor';
3
-
4
- // expo-modules-core's real entry transitively imports the 'react-native' package for
5
- // Platform/TurboModuleRegistry, whose Flow-typed source Vitest's Oxc transform cannot parse
6
- // (Metro strips Flow on-device, Vitest has no such pipeline) — faked here the same way
7
- // react-native-bootsplash is faked in the splash-screen package's own composable tests.
8
- vi.mock('expo-modules-core', () => ({
9
- Platform: { OS: 'ios' },
10
- PermissionStatus: { GRANTED: 'granted', UNDETERMINED: 'undetermined', DENIED: 'denied' },
11
- }));
12
-
13
- const { DeviceSensor } = await import('./device-sensor');
14
-
15
- // DeviceSensor only ever calls addListener/listenerCount/removeAllListeners on its native
16
- // module — a plain fake satisfying INativeSensorModule is enough, no need to extend
17
- // expo-modules-core's real NativeModule (which requires a live native JSI runtime to construct).
18
- function createFakeNativeModule(): INativeSensorModule<unknown> {
19
- const listeners = new Set<(measurement: unknown) => void>();
20
- return {
21
- addListener: vi.fn((_eventName: string, listener: (measurement: unknown) => void) => {
22
- listeners.add(listener);
23
- return { remove: () => listeners.delete(listener) };
24
- }),
25
- listenerCount: vi.fn(() => listeners.size),
26
- removeAllListeners: vi.fn(() => listeners.clear()),
27
- setUpdateInterval: vi.fn(),
28
- };
29
- }
30
-
31
- describe('DeviceSensor', () => {
32
- it('counts the number of listeners', () => {
33
- const nativeModule = createFakeNativeModule();
34
- const sensor = new DeviceSensor(nativeModule, 'mockDidUpdate');
35
-
36
- const subscription1 = sensor.addListener(() => {});
37
- expect(sensor.hasListeners()).toBe(true);
38
- expect(sensor.getListenerCount()).toBe(1);
39
-
40
- const subscription2 = sensor.addListener(() => {});
41
- sensor.addListener(() => {});
42
- expect(sensor.getListenerCount()).toBe(3);
43
-
44
- subscription2.remove();
45
- expect(sensor.getListenerCount()).toBe(2);
46
-
47
- sensor.removeSubscription(subscription1);
48
- expect(sensor.getListenerCount()).toBe(1);
49
-
50
- sensor.removeAllListeners();
51
- expect(sensor.getListenerCount()).toBe(0);
52
- });
53
-
54
- it('forwards setUpdateInterval to the native module', () => {
55
- const nativeModule = createFakeNativeModule();
56
- const sensor = new DeviceSensor(nativeModule, 'mockDidUpdate');
57
-
58
- sensor.setUpdateInterval(1234);
59
-
60
- expect(nativeModule.setUpdateInterval).toHaveBeenCalledTimes(1);
61
- expect(nativeModule.setUpdateInterval).toHaveBeenCalledWith(1234);
62
- });
63
-
64
- it('falls back to a granted default permission response when the native module has none', async () => {
65
- const nativeModule = createFakeNativeModule();
66
- const sensor = new DeviceSensor(nativeModule, 'mockDidUpdate');
67
-
68
- await expect(sensor.getPermissionsAsync()).resolves.toEqual({
69
- granted: true,
70
- expires: 'never',
71
- canAskAgain: true,
72
- status: 'granted',
73
- });
74
- await expect(sensor.isAvailableAsync()).resolves.toBe(false);
75
- });
76
- });
@@ -1,46 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
-
3
- const FAKE_NATIVE_GYROSCOPE = {
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 ExponentGyroscope 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-gyroscope', () => ({
14
- exponentGyroscope: FAKE_NATIVE_GYROSCOPE,
15
- }));
16
-
17
- // device-sensor.ts (imported transitively through ./gyroscope) 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 { Gyroscope } = await import('./gyroscope');
26
-
27
- afterEach(() => {
28
- Gyroscope.removeAllListeners();
29
- vi.clearAllMocks();
30
- });
31
-
32
- describe('Gyroscope', () => {
33
- it('sets the update interval', () => {
34
- Gyroscope.setUpdateInterval(1234);
35
-
36
- expect(FAKE_NATIVE_GYROSCOPE.setUpdateInterval).toHaveBeenCalledTimes(1);
37
- expect(FAKE_NATIVE_GYROSCOPE.setUpdateInterval).toHaveBeenCalledWith(1234);
38
- });
39
-
40
- it('subscribes through the shared gyroscopeDidUpdate event name', () => {
41
- const listener = vi.fn();
42
- Gyroscope.addListener(listener);
43
-
44
- expect(FAKE_NATIVE_GYROSCOPE.addListener).toHaveBeenCalledWith('gyroscopeDidUpdate', listener);
45
- });
46
- });
@@ -1,49 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
-
3
- const FAKE_NATIVE_LIGHT_SENSOR = {
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 ExpoLightSensor 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-light-sensor', () => ({
14
- exponentLightSensor: FAKE_NATIVE_LIGHT_SENSOR,
15
- }));
16
-
17
- // device-sensor.ts (imported transitively through ./light-sensor) 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 { LightSensor } = await import('./light-sensor');
26
-
27
- afterEach(() => {
28
- LightSensor.removeAllListeners();
29
- vi.clearAllMocks();
30
- });
31
-
32
- describe('LightSensor', () => {
33
- it('sets the update interval', () => {
34
- LightSensor.setUpdateInterval(1234);
35
-
36
- expect(FAKE_NATIVE_LIGHT_SENSOR.setUpdateInterval).toHaveBeenCalledTimes(1);
37
- expect(FAKE_NATIVE_LIGHT_SENSOR.setUpdateInterval).toHaveBeenCalledWith(1234);
38
- });
39
-
40
- it('subscribes through the shared lightSensorDidUpdate event name', () => {
41
- const listener = vi.fn();
42
- LightSensor.addListener(listener);
43
-
44
- expect(FAKE_NATIVE_LIGHT_SENSOR.addListener).toHaveBeenCalledWith(
45
- 'lightSensorDidUpdate',
46
- listener,
47
- );
48
- });
49
- });