@flighthq/haptics 0.3.1-next.975.01aea94 → 0.4.0-edge.1908.751021c
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/dist/haptics.d.ts +5 -0
- package/dist/haptics.d.ts.map +1 -1
- package/dist/haptics.js +86 -40
- package/dist/haptics.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/haptics.test.ts +81 -0
package/dist/haptics.d.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import type { BackendExplanation } from '@flighthq/types/contract';
|
|
1
2
|
import type { HapticImpactStyle, HapticNotificationType, HapticsBackend, HapticsCapabilities } from '@flighthq/types/contract';
|
|
2
3
|
export declare function cancelDeviceVibration(): boolean;
|
|
3
4
|
export declare function createWebHapticsBackend(): HapticsBackend;
|
|
5
|
+
export declare function explainHapticsBackend(): BackendExplanation;
|
|
4
6
|
export declare function getHapticsBackend(): HapticsBackend;
|
|
5
7
|
export declare function getHapticsCapabilities(out: HapticsCapabilities): HapticsCapabilities;
|
|
8
|
+
export declare function installHapticsHostBackend(backend: HapticsBackend): void;
|
|
6
9
|
export declare function isHapticsSupported(): boolean;
|
|
10
|
+
export declare function observeHapticsHostResult(operation: string, succeeded: boolean): void;
|
|
7
11
|
export declare function prepareHaptics(): void;
|
|
12
|
+
export declare function resetHapticsBackendForTest(): void;
|
|
8
13
|
export declare function setHapticsBackend(backend: HapticsBackend | null): void;
|
|
9
14
|
export declare function triggerHapticImpact(style: HapticImpactStyle, intensity?: number): boolean;
|
|
10
15
|
export declare function triggerHapticNotification(type: HapticNotificationType): boolean;
|
package/dist/haptics.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"haptics.d.ts","sourceRoot":"","sources":["../src/haptics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,mBAAmB,EACpB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"haptics.d.ts","sourceRoot":"","sources":["../src/haptics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EACV,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,mBAAmB,EACpB,MAAM,0BAA0B,CAAC;AAElC,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAKD,wBAAgB,uBAAuB,IAAI,cAAc,CA2CxD;AAED,wBAAgB,qBAAqB,IAAI,kBAAkB,CAa1D;AAED,wBAAgB,iBAAiB,IAAI,cAAc,CAElD;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,mBAAmB,GAAG,mBAAmB,CAEpF;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAMvE;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAKpF;AAED,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAKjD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI,CAEtE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzF;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAE/E;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAGzE;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,EAC3B,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,EAC9B,MAAM,SAAK,GACV,OAAO,CAOT"}
|
package/dist/haptics.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// Cancels any in-progress device vibration. Returns false when haptics are unavailable.
|
|
2
1
|
export function cancelDeviceVibration() {
|
|
3
2
|
return getHapticsBackend().cancel();
|
|
4
3
|
}
|
|
@@ -8,7 +7,7 @@ export function cancelDeviceVibration() {
|
|
|
8
7
|
export function createWebHapticsBackend() {
|
|
9
8
|
return {
|
|
10
9
|
cancel() {
|
|
11
|
-
return
|
|
10
|
+
return _webVibrate(0, 'cancel');
|
|
12
11
|
},
|
|
13
12
|
capabilities(out) {
|
|
14
13
|
const supported = typeof navigator !== 'undefined' && typeof navigator.vibrate === 'function';
|
|
@@ -22,93 +21,101 @@ export function createWebHapticsBackend() {
|
|
|
22
21
|
impact(style, intensity) {
|
|
23
22
|
const base = style === 'heavy' || style === 'rigid' ? 30 : style === 'medium' ? 20 : style === 'soft' ? 25 : 10;
|
|
24
23
|
const ms = intensity !== undefined ? Math.round(base * Math.max(0, Math.min(1, intensity))) : base;
|
|
25
|
-
return
|
|
24
|
+
return _webVibrate(ms, 'impact');
|
|
26
25
|
},
|
|
27
26
|
isSupported() {
|
|
28
27
|
return typeof navigator !== 'undefined' && typeof navigator.vibrate === 'function';
|
|
29
28
|
},
|
|
30
29
|
notification(type) {
|
|
31
30
|
const pattern = type === 'error' ? [20, 60, 20] : type === 'warning' ? [20, 60, 20, 60] : [15, 50, 15];
|
|
32
|
-
return
|
|
33
|
-
},
|
|
34
|
-
prepare() {
|
|
35
|
-
// No-op on web; native backends may pre-allocate feedback generators to reduce latency.
|
|
31
|
+
return _webVibrate(pattern, 'notification');
|
|
36
32
|
},
|
|
33
|
+
prepare() { },
|
|
37
34
|
selection() {
|
|
38
|
-
return
|
|
35
|
+
return _webVibrate(5, 'selection');
|
|
39
36
|
},
|
|
40
37
|
vibrate(durationMs) {
|
|
41
|
-
return
|
|
38
|
+
return _webVibrate(durationMs, 'vibrate');
|
|
42
39
|
},
|
|
43
40
|
vibratePattern(pattern) {
|
|
44
41
|
if (pattern.length === 0)
|
|
45
42
|
return false;
|
|
46
|
-
return
|
|
43
|
+
return _webVibrate(pattern, 'vibratePattern');
|
|
47
44
|
},
|
|
48
45
|
vibrateWaveform(timings, _amplitudes, repeat) {
|
|
49
|
-
// Web backend ignores amplitudes; falls back to vibratePattern. repeat is unsupported on web.
|
|
50
46
|
if (timings.length === 0)
|
|
51
47
|
return false;
|
|
52
48
|
void repeat;
|
|
53
|
-
return
|
|
49
|
+
return _webVibrate(timings, 'vibrateWaveform');
|
|
54
50
|
},
|
|
55
51
|
};
|
|
56
52
|
}
|
|
57
|
-
|
|
53
|
+
export function explainHapticsBackend() {
|
|
54
|
+
if (_custom !== null) {
|
|
55
|
+
return { conflict: _hostConflict, layer: 'custom', operation: null, viability: 'unobserved' };
|
|
56
|
+
}
|
|
57
|
+
if (_host !== null) {
|
|
58
|
+
return {
|
|
59
|
+
conflict: _hostConflict,
|
|
60
|
+
layer: 'host',
|
|
61
|
+
operation: _hostObservation !== null ? _hostObservation.operation : null,
|
|
62
|
+
viability: _hostObservation !== null ? _hostObservation.viability : 'unobserved',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return { conflict: false, layer: 'host-not-enabled', operation: null, viability: 'unobserved' };
|
|
66
|
+
}
|
|
58
67
|
export function getHapticsBackend() {
|
|
59
|
-
|
|
60
|
-
_backend = createWebHapticsBackend();
|
|
61
|
-
return _backend;
|
|
68
|
+
return _custom ?? _host ?? _sentinel;
|
|
62
69
|
}
|
|
63
|
-
// Fills out with the capabilities of the active HapticsBackend. Returns the same out object.
|
|
64
70
|
export function getHapticsCapabilities(out) {
|
|
65
71
|
return getHapticsBackend().capabilities(out);
|
|
66
72
|
}
|
|
67
|
-
|
|
73
|
+
export function installHapticsHostBackend(backend) {
|
|
74
|
+
if (_host !== null) {
|
|
75
|
+
if (_host !== backend)
|
|
76
|
+
_hostConflict = true;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
_host = backend;
|
|
80
|
+
}
|
|
68
81
|
export function isHapticsSupported() {
|
|
69
82
|
return getHapticsBackend().isSupported();
|
|
70
83
|
}
|
|
71
|
-
|
|
72
|
-
|
|
84
|
+
export function observeHapticsHostResult(operation, succeeded) {
|
|
85
|
+
_hostObservation = {
|
|
86
|
+
operation,
|
|
87
|
+
viability: succeeded ? 'available' : 'runtime-api-unavailable',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
73
90
|
export function prepareHaptics() {
|
|
74
91
|
getHapticsBackend().prepare?.();
|
|
75
92
|
}
|
|
76
|
-
|
|
93
|
+
export function resetHapticsBackendForTest() {
|
|
94
|
+
_custom = null;
|
|
95
|
+
_host = null;
|
|
96
|
+
_hostConflict = false;
|
|
97
|
+
_hostObservation = null;
|
|
98
|
+
}
|
|
77
99
|
export function setHapticsBackend(backend) {
|
|
78
|
-
|
|
100
|
+
_custom = backend;
|
|
79
101
|
}
|
|
80
|
-
// Triggers a physical impact at the given style, with optional continuous intensity (0..1).
|
|
81
|
-
// Intensity defaults to 1 when omitted. Returns false when haptics are unavailable.
|
|
82
102
|
export function triggerHapticImpact(style, intensity) {
|
|
83
|
-
// Resolve the documented default here so every backend receives a concrete intensity; a native
|
|
84
|
-
// backend must not have to reinterpret `undefined` as full strength on its own.
|
|
85
103
|
return getHapticsBackend().impact(style, intensity ?? 1);
|
|
86
104
|
}
|
|
87
|
-
// Triggers a semantic notification cue ('success' | 'warning' | 'error').
|
|
88
|
-
// Returns false when haptics are unavailable.
|
|
89
105
|
export function triggerHapticNotification(type) {
|
|
90
106
|
return getHapticsBackend().notification(type);
|
|
91
107
|
}
|
|
92
|
-
// Triggers a light selection tick. Returns false when haptics are unavailable.
|
|
93
108
|
export function triggerHapticSelection() {
|
|
94
109
|
return getHapticsBackend().selection();
|
|
95
110
|
}
|
|
96
|
-
// Vibrates the device for the given duration in milliseconds. Returns false when unavailable or denied.
|
|
97
111
|
export function vibrateDevice(durationMs) {
|
|
98
112
|
return getHapticsBackend().vibrate(durationMs);
|
|
99
113
|
}
|
|
100
|
-
// Vibrates the device using a pattern array [onMs, offMs, onMs, ...] matching the Web Vibration API.
|
|
101
|
-
// Returns false on empty pattern or when haptics are unavailable.
|
|
102
114
|
export function vibrateDevicePattern(pattern) {
|
|
103
115
|
if (pattern.length === 0)
|
|
104
116
|
return false;
|
|
105
117
|
return getHapticsBackend().vibratePattern(pattern);
|
|
106
118
|
}
|
|
107
|
-
// Vibrates using an amplitude-aware waveform (maps to Android VibrationEffect.createWaveform).
|
|
108
|
-
// timings and amplitudes must have equal length; amplitudes are 0..255 (per Android convention).
|
|
109
|
-
// repeat is the index into timings at which to loop, or -1 (default) for no repeat.
|
|
110
|
-
// Backends that lack native waveform support fall back to vibratePattern(timings).
|
|
111
|
-
// Returns false on empty timings or when haptics are unavailable.
|
|
112
119
|
export function vibrateDeviceWaveform(timings, amplitudes, repeat = -1) {
|
|
113
120
|
const backend = getHapticsBackend();
|
|
114
121
|
if (timings.length === 0)
|
|
@@ -118,15 +125,54 @@ export function vibrateDeviceWaveform(timings, amplitudes, repeat = -1) {
|
|
|
118
125
|
}
|
|
119
126
|
return backend.vibratePattern(timings);
|
|
120
127
|
}
|
|
121
|
-
let
|
|
122
|
-
|
|
128
|
+
let _custom = null;
|
|
129
|
+
let _host = null;
|
|
130
|
+
let _hostConflict = false;
|
|
131
|
+
let _hostObservation = null;
|
|
132
|
+
const _sentinel = {
|
|
133
|
+
cancel() {
|
|
134
|
+
return false;
|
|
135
|
+
},
|
|
136
|
+
capabilities(out) {
|
|
137
|
+
out.amplitudeControl = false;
|
|
138
|
+
out.customEvents = false;
|
|
139
|
+
out.intensity = false;
|
|
140
|
+
out.patterns = false;
|
|
141
|
+
out.supported = false;
|
|
142
|
+
return out;
|
|
143
|
+
},
|
|
144
|
+
impact() {
|
|
145
|
+
return false;
|
|
146
|
+
},
|
|
147
|
+
isSupported() {
|
|
148
|
+
return false;
|
|
149
|
+
},
|
|
150
|
+
notification() {
|
|
151
|
+
return false;
|
|
152
|
+
},
|
|
153
|
+
prepare() { },
|
|
154
|
+
selection() {
|
|
155
|
+
return false;
|
|
156
|
+
},
|
|
157
|
+
vibrate() {
|
|
158
|
+
return false;
|
|
159
|
+
},
|
|
160
|
+
vibratePattern() {
|
|
161
|
+
return false;
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
function _webVibrate(pattern, operation) {
|
|
123
165
|
if (typeof navigator === 'undefined' || !('vibrate' in navigator) || typeof navigator.vibrate !== 'function') {
|
|
166
|
+
observeHapticsHostResult(operation, false);
|
|
124
167
|
return false;
|
|
125
168
|
}
|
|
126
169
|
try {
|
|
127
|
-
|
|
170
|
+
const result = navigator.vibrate(pattern);
|
|
171
|
+
observeHapticsHostResult(operation, result);
|
|
172
|
+
return result;
|
|
128
173
|
}
|
|
129
174
|
catch {
|
|
175
|
+
observeHapticsHostResult(operation, false);
|
|
130
176
|
return false;
|
|
131
177
|
}
|
|
132
178
|
}
|
package/dist/haptics.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"haptics.js","sourceRoot":"","sources":["../src/haptics.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"haptics.js","sourceRoot":"","sources":["../src/haptics.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { cancelDeviceVibration, getHapticsCapabilities, isHapticsSupported, prepareHaptics, triggerHapticImpact, triggerHapticNotification, triggerHapticSelection, vibrateDevice, vibrateDevicePattern, vibrateDeviceWaveform, } from './contract';
|
|
1
|
+
export { explainHapticsBackend, cancelDeviceVibration, getHapticsCapabilities, isHapticsSupported, prepareHaptics, triggerHapticImpact, triggerHapticNotification, triggerHapticSelection, vibrateDevice, vibrateDevicePattern, vibrateDeviceWaveform, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,yBAAyB,EACzB,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,yBAAyB,EACzB,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { cancelDeviceVibration, getHapticsCapabilities, isHapticsSupported, prepareHaptics, triggerHapticImpact, triggerHapticNotification, triggerHapticSelection, vibrateDevice, vibrateDevicePattern, vibrateDeviceWaveform, } from './contract';
|
|
1
|
+
export { explainHapticsBackend, cancelDeviceVibration, getHapticsCapabilities, isHapticsSupported, prepareHaptics, triggerHapticImpact, triggerHapticNotification, triggerHapticSelection, vibrateDevice, vibrateDevicePattern, vibrateDeviceWaveform, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,yBAAyB,EACzB,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,yBAAyB,EACzB,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/haptics",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-edge.1908.751021c",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/types": "0.
|
|
41
|
+
"@flighthq/types": "0.4.0-edge.1908.751021c"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"typescript": "^5.3.0"
|
package/src/haptics.test.ts
CHANGED
|
@@ -19,6 +19,10 @@ import {
|
|
|
19
19
|
vibrateDevice,
|
|
20
20
|
vibrateDevicePattern,
|
|
21
21
|
vibrateDeviceWaveform,
|
|
22
|
+
explainHapticsBackend,
|
|
23
|
+
installHapticsHostBackend,
|
|
24
|
+
observeHapticsHostResult,
|
|
25
|
+
resetHapticsBackendForTest,
|
|
22
26
|
} from './haptics';
|
|
23
27
|
|
|
24
28
|
function makeCapabilities(overrides: Partial<HapticsCapabilities> = {}): HapticsCapabilities {
|
|
@@ -155,6 +159,34 @@ describe('createWebHapticsBackend', () => {
|
|
|
155
159
|
});
|
|
156
160
|
});
|
|
157
161
|
|
|
162
|
+
describe('explainHapticsBackend', () => {
|
|
163
|
+
afterEach(() => resetHapticsBackendForTest());
|
|
164
|
+
|
|
165
|
+
it('reports host-not-enabled when no backend is installed', () => {
|
|
166
|
+
resetHapticsBackendForTest();
|
|
167
|
+
const explanation = explainHapticsBackend();
|
|
168
|
+
expect(explanation.layer).toBe('host-not-enabled');
|
|
169
|
+
expect(explanation.conflict).toBe(false);
|
|
170
|
+
expect(explanation.viability).toBe('unobserved');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('reports custom layer when a custom backend is set', () => {
|
|
174
|
+
setHapticsBackend(fakeBackend());
|
|
175
|
+
expect(explainHapticsBackend().layer).toBe('custom');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('reports host layer when a host backend is installed', () => {
|
|
179
|
+
installHapticsHostBackend(fakeBackend());
|
|
180
|
+
expect(explainHapticsBackend().layer).toBe('host');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('reports conflict when two different host backends are installed', () => {
|
|
184
|
+
installHapticsHostBackend(fakeBackend());
|
|
185
|
+
installHapticsHostBackend(fakeBackend());
|
|
186
|
+
expect(explainHapticsBackend().conflict).toBe(true);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
158
190
|
describe('getHapticsBackend', () => {
|
|
159
191
|
it('falls back to a web backend', () => {
|
|
160
192
|
expect(getHapticsBackend()).not.toBeNull();
|
|
@@ -188,6 +220,25 @@ describe('getHapticsCapabilities', () => {
|
|
|
188
220
|
});
|
|
189
221
|
});
|
|
190
222
|
|
|
223
|
+
describe('installHapticsHostBackend', () => {
|
|
224
|
+
afterEach(() => resetHapticsBackendForTest());
|
|
225
|
+
|
|
226
|
+
it('installs a host backend that getHapticsBackend returns', () => {
|
|
227
|
+
const backend = fakeBackend();
|
|
228
|
+
installHapticsHostBackend(backend);
|
|
229
|
+
expect(getHapticsBackend()).toBe(backend);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('is first-host-wins: a second different backend sets conflict', () => {
|
|
233
|
+
const first = fakeBackend();
|
|
234
|
+
const second = fakeBackend();
|
|
235
|
+
installHapticsHostBackend(first);
|
|
236
|
+
installHapticsHostBackend(second);
|
|
237
|
+
expect(getHapticsBackend()).toBe(first);
|
|
238
|
+
expect(explainHapticsBackend().conflict).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
191
242
|
describe('isHapticsSupported', () => {
|
|
192
243
|
it('returns true from fake backend', () => {
|
|
193
244
|
setHapticsBackend(fakeBackend());
|
|
@@ -199,6 +250,24 @@ describe('isHapticsSupported', () => {
|
|
|
199
250
|
});
|
|
200
251
|
});
|
|
201
252
|
|
|
253
|
+
describe('observeHapticsHostResult', () => {
|
|
254
|
+
afterEach(() => resetHapticsBackendForTest());
|
|
255
|
+
|
|
256
|
+
it('records a successful observation', () => {
|
|
257
|
+
installHapticsHostBackend(fakeBackend());
|
|
258
|
+
observeHapticsHostResult('cancel', true);
|
|
259
|
+
const explanation = explainHapticsBackend();
|
|
260
|
+
expect(explanation.operation).toBe('cancel');
|
|
261
|
+
expect(explanation.viability).toBe('available');
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('records a failed observation', () => {
|
|
265
|
+
installHapticsHostBackend(fakeBackend());
|
|
266
|
+
observeHapticsHostResult('cancel', false);
|
|
267
|
+
expect(explainHapticsBackend().viability).toBe('runtime-api-unavailable');
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
202
271
|
describe('prepareHaptics', () => {
|
|
203
272
|
it('calls prepare on backends that provide it', () => {
|
|
204
273
|
const backend = fakeBackend();
|
|
@@ -216,6 +285,18 @@ describe('prepareHaptics', () => {
|
|
|
216
285
|
});
|
|
217
286
|
});
|
|
218
287
|
|
|
288
|
+
describe('resetHapticsBackendForTest', () => {
|
|
289
|
+
it('clears all backend slots', () => {
|
|
290
|
+
setHapticsBackend(fakeBackend());
|
|
291
|
+
installHapticsHostBackend(fakeBackend());
|
|
292
|
+
observeHapticsHostResult('cancel', true);
|
|
293
|
+
resetHapticsBackendForTest();
|
|
294
|
+
expect(explainHapticsBackend().layer).toBe('host-not-enabled');
|
|
295
|
+
expect(explainHapticsBackend().conflict).toBe(false);
|
|
296
|
+
expect(explainHapticsBackend().viability).toBe('unobserved');
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
219
300
|
describe('setHapticsBackend', () => {
|
|
220
301
|
it('clears back to the web fallback when passed null', () => {
|
|
221
302
|
setHapticsBackend(fakeBackend());
|