@neurosity/sdk 6.5.9 → 6.5.11
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/browser/neurosity.iife.js +2419 -1009
- package/dist/browser/neurosity.js +180 -148
- package/dist/browser/neurosity.js.map +1 -1
- package/dist/cjs/__tests__/Neurosity.test.d.ts +1 -0
- package/dist/cjs/__tests__/Neurosity.test.js +201 -0
- package/dist/cjs/__tests__/WebBluetoothTransport.test.d.ts +1 -0
- package/dist/cjs/__tests__/WebBluetoothTransport.test.js +201 -0
- package/dist/cjs/__tests__/accelerometer.test.d.ts +1 -0
- package/dist/cjs/__tests__/accelerometer.test.js +158 -0
- package/dist/cjs/__tests__/auth.test.d.ts +1 -0
- package/dist/cjs/__tests__/auth.test.js +239 -0
- package/dist/cjs/__tests__/brainwaves.test.d.ts +1 -0
- package/dist/cjs/__tests__/brainwaves.test.js +291 -0
- package/dist/cjs/__tests__/device.test.d.ts +1 -0
- package/dist/cjs/__tests__/device.test.js +281 -0
- package/dist/cjs/__tests__/haptics.test.d.ts +1 -0
- package/dist/cjs/__tests__/haptics.test.js +162 -0
- package/dist/cjs/__tests__/metrics.test.d.ts +1 -0
- package/dist/cjs/__tests__/metrics.test.js +178 -0
- package/dist/cjs/__tests__/oauth.test.d.ts +1 -0
- package/dist/cjs/__tests__/oauth.test.js +138 -0
- package/dist/cjs/__tests__/settings.test.d.ts +1 -0
- package/dist/cjs/__tests__/settings.test.js +130 -0
- package/dist/cjs/__tests__/setup/webBluetooth.setup.d.ts +11 -0
- package/dist/cjs/__tests__/setup/webBluetooth.setup.js +35 -0
- package/dist/cjs/__tests__/setup.d.ts +0 -0
- package/dist/cjs/__tests__/setup.js +7 -0
- package/dist/cjs/__tests__/streaming.test.d.ts +1 -0
- package/dist/cjs/__tests__/streaming.test.js +259 -0
- package/dist/cjs/__tests__/timesync.test.d.ts +1 -0
- package/dist/cjs/__tests__/timesync.test.js +54 -0
- package/dist/cjs/__tests__/utils.test.d.ts +1 -0
- package/dist/cjs/__tests__/utils.test.js +281 -0
- package/dist/cjs/api/bluetooth/BluetoothClient.d.ts +6 -6
- package/dist/cjs/api/bluetooth/BluetoothTransport.d.ts +1 -1
- package/dist/cjs/api/bluetooth/react-native/ReactNativeTransport.d.ts +4 -4
- package/dist/cjs/api/bluetooth/react-native/types/ReactNativeTypes.d.ts +2 -2
- package/dist/cjs/api/bluetooth/types/index.d.ts +2 -2
- package/dist/cjs/api/bluetooth/utils/decodeJSONChunks.d.ts +1 -1
- package/dist/cjs/api/bluetooth/utils/stitch.d.ts +1 -1
- package/dist/cjs/api/bluetooth/utils/textCodec.d.ts +1 -1
- package/dist/cjs/api/bluetooth/web/WebBluetoothTransport.d.ts +1 -1
- package/dist/cjs/api/firebase/FirebaseDevice.d.ts +1 -1
- package/dist/cjs/api/firebase/FirebaseUser.d.ts +1 -1
- package/dist/cjs/api/index.js +1 -1
- package/dist/cjs/timesync/Timesync.d.ts +1 -1
- package/dist/cjs/types/awareness.d.ts +1 -1
- package/dist/cjs/types/brainwaves.d.ts +25 -12
- package/dist/cjs/types/credentials.d.ts +4 -4
- package/dist/cjs/types/deviceInfo.d.ts +4 -4
- package/dist/cjs/types/epoch.d.ts +1 -1
- package/dist/cjs/types/experiment.d.ts +1 -1
- package/dist/cjs/types/hapticEffects.d.ts +1 -1
- package/dist/cjs/types/marker.d.ts +1 -1
- package/dist/cjs/types/metrics.d.ts +2 -2
- package/dist/cjs/types/oauth.d.ts +4 -4
- package/dist/cjs/types/sample.d.ts +2 -2
- package/dist/cjs/types/signalQuality.d.ts +1 -1
- package/dist/cjs/types/skill.d.ts +2 -2
- package/dist/cjs/types/user.d.ts +3 -3
- package/dist/cjs/utils/oauth.d.ts +1 -1
- package/dist/cjs/utils/transferDevice.d.ts +3 -3
- package/dist/cjs/utils/whileOnline.d.ts +1 -1
- package/dist/electron/index.js +1 -1
- package/dist/electron/index.js.map +1 -1
- package/dist/esm/__tests__/Neurosity.test.d.ts +1 -0
- package/dist/esm/__tests__/Neurosity.test.js +199 -0
- package/dist/esm/__tests__/WebBluetoothTransport.test.d.ts +1 -0
- package/dist/esm/__tests__/WebBluetoothTransport.test.js +199 -0
- package/dist/esm/__tests__/accelerometer.test.d.ts +1 -0
- package/dist/esm/__tests__/accelerometer.test.js +156 -0
- package/dist/esm/__tests__/auth.test.d.ts +1 -0
- package/dist/esm/__tests__/auth.test.js +237 -0
- package/dist/esm/__tests__/brainwaves.test.d.ts +1 -0
- package/dist/esm/__tests__/brainwaves.test.js +289 -0
- package/dist/esm/__tests__/device.test.d.ts +1 -0
- package/dist/esm/__tests__/device.test.js +279 -0
- package/dist/esm/__tests__/haptics.test.d.ts +1 -0
- package/dist/esm/__tests__/haptics.test.js +160 -0
- package/dist/esm/__tests__/metrics.test.d.ts +1 -0
- package/dist/esm/__tests__/metrics.test.js +176 -0
- package/dist/esm/__tests__/oauth.test.d.ts +1 -0
- package/dist/esm/__tests__/oauth.test.js +133 -0
- package/dist/esm/__tests__/settings.test.d.ts +1 -0
- package/dist/esm/__tests__/settings.test.js +128 -0
- package/dist/esm/__tests__/setup/webBluetooth.setup.d.ts +11 -0
- package/dist/esm/__tests__/setup/webBluetooth.setup.js +35 -0
- package/dist/esm/__tests__/setup.d.ts +0 -0
- package/dist/esm/__tests__/setup.js +7 -0
- package/dist/esm/__tests__/streaming.test.d.ts +1 -0
- package/dist/esm/__tests__/streaming.test.js +257 -0
- package/dist/esm/__tests__/timesync.test.d.ts +1 -0
- package/dist/esm/__tests__/timesync.test.js +52 -0
- package/dist/esm/__tests__/utils.test.d.ts +1 -0
- package/dist/esm/__tests__/utils.test.js +279 -0
- package/dist/esm/api/bluetooth/BluetoothClient.d.ts +6 -6
- package/dist/esm/api/bluetooth/BluetoothTransport.d.ts +1 -1
- package/dist/esm/api/bluetooth/react-native/ReactNativeTransport.d.ts +4 -4
- package/dist/esm/api/bluetooth/react-native/types/ReactNativeTypes.d.ts +2 -2
- package/dist/esm/api/bluetooth/types/index.d.ts +2 -2
- package/dist/esm/api/bluetooth/utils/decodeJSONChunks.d.ts +1 -1
- package/dist/esm/api/bluetooth/utils/stitch.d.ts +1 -1
- package/dist/esm/api/bluetooth/utils/textCodec.d.ts +1 -1
- package/dist/esm/api/bluetooth/web/WebBluetoothTransport.d.ts +1 -1
- package/dist/esm/api/firebase/FirebaseDevice.d.ts +1 -1
- package/dist/esm/api/firebase/FirebaseUser.d.ts +1 -1
- package/dist/esm/api/index.js +2 -2
- package/dist/esm/neurosity.mjs +2480 -1007
- package/dist/esm/timesync/Timesync.d.ts +1 -1
- package/dist/esm/types/awareness.d.ts +1 -1
- package/dist/esm/types/brainwaves.d.ts +25 -12
- package/dist/esm/types/credentials.d.ts +4 -4
- package/dist/esm/types/deviceInfo.d.ts +4 -4
- package/dist/esm/types/epoch.d.ts +1 -1
- package/dist/esm/types/experiment.d.ts +1 -1
- package/dist/esm/types/hapticEffects.d.ts +1 -1
- package/dist/esm/types/marker.d.ts +1 -1
- package/dist/esm/types/metrics.d.ts +2 -2
- package/dist/esm/types/oauth.d.ts +4 -4
- package/dist/esm/types/sample.d.ts +2 -2
- package/dist/esm/types/signalQuality.d.ts +1 -1
- package/dist/esm/types/skill.d.ts +2 -2
- package/dist/esm/types/user.d.ts +3 -3
- package/dist/esm/utils/oauth.d.ts +1 -1
- package/dist/esm/utils/transferDevice.d.ts +3 -3
- package/dist/esm/utils/whileOnline.d.ts +1 -1
- package/dist/examples/neurosity.iife.js +2419 -1009
- package/dist/examples/neurosity.js +180 -148
- package/dist/examples/neurosity.mjs +2480 -1007
- package/package.json +17 -5
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const Neurosity_1 = require("../Neurosity");
|
|
13
|
+
const rxjs_1 = require("rxjs");
|
|
14
|
+
const status_1 = require("../types/status");
|
|
15
|
+
// Mock CloudClient
|
|
16
|
+
jest.mock("../api", () => {
|
|
17
|
+
const originalModule = jest.requireActual("../api");
|
|
18
|
+
class MockCloudClient {
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.user = null;
|
|
21
|
+
this.userClaims = { scopes: ["brainwaves"] };
|
|
22
|
+
this.subscriptionManager = {
|
|
23
|
+
add: jest.fn(),
|
|
24
|
+
remove: jest.fn(),
|
|
25
|
+
removeAll: jest.fn()
|
|
26
|
+
};
|
|
27
|
+
this._deviceInfo = {
|
|
28
|
+
deviceId: "test-device-id",
|
|
29
|
+
deviceNickname: "Test Device",
|
|
30
|
+
channelNames: ["CH1", "CH2"],
|
|
31
|
+
channels: 2,
|
|
32
|
+
samplingRate: 250,
|
|
33
|
+
manufacturer: "Neurosity",
|
|
34
|
+
model: "Crown",
|
|
35
|
+
modelName: "Crown",
|
|
36
|
+
modelVersion: "v1",
|
|
37
|
+
apiVersion: "1.0.0",
|
|
38
|
+
osVersion: "1.0.0",
|
|
39
|
+
emulator: false
|
|
40
|
+
};
|
|
41
|
+
this._selectedDevice = new rxjs_1.BehaviorSubject(this._deviceInfo);
|
|
42
|
+
this._deviceStatus = new rxjs_1.BehaviorSubject({
|
|
43
|
+
state: status_1.STATUS.ONLINE,
|
|
44
|
+
charging: false,
|
|
45
|
+
battery: 100,
|
|
46
|
+
sleepMode: false,
|
|
47
|
+
sleepModeReason: null,
|
|
48
|
+
lastHeartbeat: Date.now(),
|
|
49
|
+
ssid: "test-network"
|
|
50
|
+
});
|
|
51
|
+
this._signalQuality = new rxjs_1.BehaviorSubject({
|
|
52
|
+
CH1: {
|
|
53
|
+
standardDeviation: 0.1,
|
|
54
|
+
status: "good"
|
|
55
|
+
},
|
|
56
|
+
CH2: {
|
|
57
|
+
standardDeviation: 0.1,
|
|
58
|
+
status: "good"
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
this.getInfo = jest.fn().mockResolvedValue(this._deviceInfo);
|
|
62
|
+
this.selectDevice = jest
|
|
63
|
+
.fn()
|
|
64
|
+
.mockImplementation((selector) => __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
try {
|
|
66
|
+
const selectedDevice = selector([this._deviceInfo]);
|
|
67
|
+
this._selectedDevice.next(selectedDevice);
|
|
68
|
+
return selectedDevice;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
this._selectedDevice.next(null);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}));
|
|
75
|
+
this.didSelectDevice = jest.fn().mockResolvedValue(true);
|
|
76
|
+
this.onDeviceChange = jest
|
|
77
|
+
.fn()
|
|
78
|
+
.mockReturnValue(this._selectedDevice.asObservable());
|
|
79
|
+
this.status = jest.fn().mockReturnValue(this._deviceStatus.asObservable());
|
|
80
|
+
this.signalQuality = jest
|
|
81
|
+
.fn()
|
|
82
|
+
.mockReturnValue(this._signalQuality.asObservable());
|
|
83
|
+
this.osVersion = jest.fn().mockReturnValue((0, rxjs_1.of)("1.0.0"));
|
|
84
|
+
this.metrics = {
|
|
85
|
+
subscribe: jest.fn().mockImplementation((subscription) => {
|
|
86
|
+
return Object.assign({ id: "test-id" }, subscription);
|
|
87
|
+
}),
|
|
88
|
+
on: jest.fn().mockImplementation((subscription, callback) => {
|
|
89
|
+
if (subscription.metric === "signalQuality") {
|
|
90
|
+
const sub = this._signalQuality.subscribe(callback);
|
|
91
|
+
return () => sub.unsubscribe();
|
|
92
|
+
}
|
|
93
|
+
return () => { };
|
|
94
|
+
})
|
|
95
|
+
};
|
|
96
|
+
this.addDevice = jest.fn().mockImplementation((deviceId) => __awaiter(this, void 0, void 0, function* () {
|
|
97
|
+
if (deviceId === "invalid-id") {
|
|
98
|
+
throw new Error("Invalid device ID");
|
|
99
|
+
}
|
|
100
|
+
return Promise.resolve();
|
|
101
|
+
}));
|
|
102
|
+
this.removeDevice = jest.fn().mockImplementation((deviceId) => __awaiter(this, void 0, void 0, function* () {
|
|
103
|
+
if (deviceId === "invalid-id") {
|
|
104
|
+
throw new Error("Invalid device ID");
|
|
105
|
+
}
|
|
106
|
+
return Promise.resolve();
|
|
107
|
+
}));
|
|
108
|
+
this.transferDevice = jest
|
|
109
|
+
.fn()
|
|
110
|
+
.mockImplementation((options) => __awaiter(this, void 0, void 0, function* () {
|
|
111
|
+
if (options.deviceId === "invalid-id" ||
|
|
112
|
+
options.recipientsUserId === "invalid-user") {
|
|
113
|
+
throw new Error("Invalid transfer parameters");
|
|
114
|
+
}
|
|
115
|
+
return Promise.resolve();
|
|
116
|
+
}));
|
|
117
|
+
this.onUserDevicesChange = jest.fn().mockReturnValue((0, rxjs_1.of)([this._deviceInfo]));
|
|
118
|
+
this.options = options;
|
|
119
|
+
}
|
|
120
|
+
// Helper methods for tests
|
|
121
|
+
_updateDeviceStatus(status) {
|
|
122
|
+
this._deviceStatus.next(Object.assign(Object.assign({}, this._deviceStatus.value), status));
|
|
123
|
+
}
|
|
124
|
+
_updateSignalQuality(quality) {
|
|
125
|
+
this._signalQuality.next(quality);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return Object.assign(Object.assign({}, originalModule), { CloudClient: jest
|
|
129
|
+
.fn()
|
|
130
|
+
.mockImplementation((options) => new MockCloudClient(options)) });
|
|
131
|
+
});
|
|
132
|
+
describe("Device Management", () => {
|
|
133
|
+
let neurosity;
|
|
134
|
+
let cloudClient;
|
|
135
|
+
const testDeviceId = "test-device-id";
|
|
136
|
+
beforeEach(() => {
|
|
137
|
+
neurosity = new Neurosity_1.Neurosity({
|
|
138
|
+
deviceId: testDeviceId,
|
|
139
|
+
emulator: true
|
|
140
|
+
});
|
|
141
|
+
cloudClient = neurosity.cloudClient;
|
|
142
|
+
});
|
|
143
|
+
describe("Device Information", () => {
|
|
144
|
+
it("should get device info", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
145
|
+
const info = yield neurosity.getInfo();
|
|
146
|
+
expect(info).toBeDefined();
|
|
147
|
+
expect(info.deviceId).toBe(testDeviceId);
|
|
148
|
+
expect(info.manufacturer).toBe("Neurosity");
|
|
149
|
+
expect(info.model).toBe("Crown");
|
|
150
|
+
}));
|
|
151
|
+
it("should get device status", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
152
|
+
const status = yield (0, rxjs_1.firstValueFrom)(neurosity.status());
|
|
153
|
+
expect(status).toBeDefined();
|
|
154
|
+
expect(status.state).toBe(status_1.STATUS.ONLINE);
|
|
155
|
+
expect(status.charging).toBe(false);
|
|
156
|
+
expect(status.battery).toBe(100);
|
|
157
|
+
expect(status.sleepMode).toBe(false);
|
|
158
|
+
expect(status.ssid).toBe("test-network");
|
|
159
|
+
}));
|
|
160
|
+
it("should get signal quality", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
161
|
+
const quality = yield (0, rxjs_1.firstValueFrom)(neurosity.signalQuality());
|
|
162
|
+
expect(quality).toBeDefined();
|
|
163
|
+
expect(quality.CH1.status).toBe("good");
|
|
164
|
+
expect(quality.CH1.standardDeviation).toBe(0.1);
|
|
165
|
+
}));
|
|
166
|
+
it("should get OS version", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
167
|
+
const version = yield (0, rxjs_1.firstValueFrom)(neurosity.osVersion());
|
|
168
|
+
expect(version).toBeDefined();
|
|
169
|
+
expect(version).toBe("1.0.0");
|
|
170
|
+
}));
|
|
171
|
+
it("should handle device status changes", (done) => {
|
|
172
|
+
const newStatus = {
|
|
173
|
+
state: status_1.STATUS.ONLINE,
|
|
174
|
+
charging: true,
|
|
175
|
+
battery: 80,
|
|
176
|
+
sleepMode: true,
|
|
177
|
+
sleepModeReason: status_1.SLEEP_MODE_REASON.CHARGING
|
|
178
|
+
};
|
|
179
|
+
neurosity.status().subscribe({
|
|
180
|
+
next: (status) => {
|
|
181
|
+
if (status.charging === true) {
|
|
182
|
+
expect(status.battery).toBe(80);
|
|
183
|
+
expect(status.sleepMode).toBe(true);
|
|
184
|
+
expect(status.sleepModeReason).toBe(status_1.SLEEP_MODE_REASON.CHARGING);
|
|
185
|
+
done();
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
error: done
|
|
189
|
+
});
|
|
190
|
+
cloudClient._updateDeviceStatus(newStatus);
|
|
191
|
+
});
|
|
192
|
+
it("should handle signal quality changes", (done) => {
|
|
193
|
+
const newQuality = {
|
|
194
|
+
CH1: {
|
|
195
|
+
standardDeviation: 0.5,
|
|
196
|
+
status: "bad"
|
|
197
|
+
},
|
|
198
|
+
CH2: {
|
|
199
|
+
standardDeviation: 0.5,
|
|
200
|
+
status: "bad"
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
cloudClient._updateSignalQuality(newQuality);
|
|
204
|
+
neurosity
|
|
205
|
+
.signalQuality()
|
|
206
|
+
.pipe((0, rxjs_1.take)(1))
|
|
207
|
+
.subscribe({
|
|
208
|
+
next: (quality) => {
|
|
209
|
+
expect(quality.CH1.standardDeviation).toBe(0.5);
|
|
210
|
+
expect(quality.CH2.status).toBe("bad");
|
|
211
|
+
done();
|
|
212
|
+
},
|
|
213
|
+
error: done
|
|
214
|
+
});
|
|
215
|
+
}, 10000);
|
|
216
|
+
});
|
|
217
|
+
describe("Device Selection", () => {
|
|
218
|
+
it("should select device", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
219
|
+
yield expect(neurosity.selectDevice((devices) => {
|
|
220
|
+
const device = devices.find((d) => d.deviceId === testDeviceId);
|
|
221
|
+
if (!device)
|
|
222
|
+
throw new Error("Device not found");
|
|
223
|
+
return device;
|
|
224
|
+
})).resolves.not.toThrow();
|
|
225
|
+
const deviceInfo = yield neurosity.getInfo();
|
|
226
|
+
expect(deviceInfo.deviceId).toBe(testDeviceId);
|
|
227
|
+
}));
|
|
228
|
+
it("should handle device selection failure", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
229
|
+
yield expect(neurosity.selectDevice(() => {
|
|
230
|
+
throw new Error("Device not found");
|
|
231
|
+
})).rejects.toThrow("Device not found");
|
|
232
|
+
}));
|
|
233
|
+
it("should monitor device changes", (done) => {
|
|
234
|
+
neurosity
|
|
235
|
+
.onDeviceChange()
|
|
236
|
+
.pipe((0, rxjs_1.take)(1))
|
|
237
|
+
.subscribe({
|
|
238
|
+
next: (deviceInfo) => {
|
|
239
|
+
expect(deviceInfo).toBeDefined();
|
|
240
|
+
expect(deviceInfo.deviceId).toBe(testDeviceId);
|
|
241
|
+
done();
|
|
242
|
+
},
|
|
243
|
+
error: done
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
describe("Device Management", () => {
|
|
248
|
+
it("should add device", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
249
|
+
yield expect(neurosity.addDevice(testDeviceId)).resolves.not.toThrow();
|
|
250
|
+
}));
|
|
251
|
+
it("should handle invalid device ID when adding", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
252
|
+
yield expect(neurosity.addDevice("invalid-id")).rejects.toThrow("Invalid device ID");
|
|
253
|
+
}));
|
|
254
|
+
it("should remove device", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
255
|
+
yield expect(neurosity.removeDevice(testDeviceId)).resolves.not.toThrow();
|
|
256
|
+
}));
|
|
257
|
+
it("should handle invalid device ID when removing", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
258
|
+
yield expect(neurosity.removeDevice("invalid-id")).rejects.toThrow("Invalid device ID");
|
|
259
|
+
}));
|
|
260
|
+
it("should transfer device", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
261
|
+
const transferOptions = {
|
|
262
|
+
deviceId: testDeviceId,
|
|
263
|
+
recipientsUserId: "target-user-id"
|
|
264
|
+
};
|
|
265
|
+
yield expect(neurosity.transferDevice(transferOptions)).resolves.not.toThrow();
|
|
266
|
+
}));
|
|
267
|
+
it("should handle invalid transfer parameters", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
268
|
+
const invalidOptions = {
|
|
269
|
+
deviceId: "invalid-id",
|
|
270
|
+
recipientsUserId: "invalid-user"
|
|
271
|
+
};
|
|
272
|
+
yield expect(neurosity.transferDevice(invalidOptions)).rejects.toThrow("Invalid transfer parameters");
|
|
273
|
+
}));
|
|
274
|
+
it("should monitor user devices changes", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
275
|
+
const devices = yield (0, rxjs_1.firstValueFrom)(neurosity.onUserDevicesChange());
|
|
276
|
+
expect(devices).toBeDefined();
|
|
277
|
+
expect(Array.isArray(devices)).toBe(true);
|
|
278
|
+
expect(devices[0].deviceId).toBe(testDeviceId);
|
|
279
|
+
}));
|
|
280
|
+
});
|
|
281
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const Neurosity_1 = require("../Neurosity");
|
|
13
|
+
const rxjs_1 = require("rxjs");
|
|
14
|
+
const status_1 = require("../types/status");
|
|
15
|
+
const hapticEffects_1 = require("../utils/hapticEffects");
|
|
16
|
+
const platform_1 = require("../utils/platform");
|
|
17
|
+
// Mock CloudClient
|
|
18
|
+
jest.mock("../api", () => {
|
|
19
|
+
const originalModule = jest.requireActual("../api");
|
|
20
|
+
class MockCloudClient {
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.user = null;
|
|
23
|
+
this.userClaims = { scopes: ["brainwaves"] };
|
|
24
|
+
this.subscriptionManager = {
|
|
25
|
+
add: jest.fn(),
|
|
26
|
+
remove: jest.fn(),
|
|
27
|
+
removeAll: jest.fn()
|
|
28
|
+
};
|
|
29
|
+
this._selectedDevice = new rxjs_1.ReplaySubject(1);
|
|
30
|
+
this._deviceInfo = {
|
|
31
|
+
deviceId: "test-device-id",
|
|
32
|
+
deviceNickname: "Test Device",
|
|
33
|
+
channelNames: ["CH1", "CH2"],
|
|
34
|
+
channels: 2,
|
|
35
|
+
samplingRate: 250,
|
|
36
|
+
manufacturer: "Neurosity",
|
|
37
|
+
model: "Crown",
|
|
38
|
+
modelName: "Crown",
|
|
39
|
+
modelVersion: platform_1.MODEL_VERSION_2,
|
|
40
|
+
apiVersion: "1.0.0",
|
|
41
|
+
osVersion: "1.0.0",
|
|
42
|
+
emulator: false
|
|
43
|
+
};
|
|
44
|
+
this.getInfo = jest.fn().mockResolvedValue(this._deviceInfo);
|
|
45
|
+
this.selectDevice = jest
|
|
46
|
+
.fn()
|
|
47
|
+
.mockImplementation((selector) => __awaiter(this, void 0, void 0, function* () {
|
|
48
|
+
const selectedDevice = selector([this._deviceInfo]);
|
|
49
|
+
this._selectedDevice.next(selectedDevice);
|
|
50
|
+
return selectedDevice;
|
|
51
|
+
}));
|
|
52
|
+
this.getSelectedDevice = jest.fn().mockResolvedValue(this._deviceInfo);
|
|
53
|
+
this.didSelectDevice = jest.fn().mockResolvedValue(true);
|
|
54
|
+
this.onDeviceChange = jest.fn().mockReturnValue((0, rxjs_1.of)({
|
|
55
|
+
state: status_1.STATUS.ONLINE,
|
|
56
|
+
charging: false,
|
|
57
|
+
battery: 100,
|
|
58
|
+
sleepMode: false,
|
|
59
|
+
updatingProgress: 0,
|
|
60
|
+
bluetoothEnabled: false,
|
|
61
|
+
sleepModeReason: null,
|
|
62
|
+
lastHeartbeat: Date.now(),
|
|
63
|
+
ssid: "test-network"
|
|
64
|
+
}));
|
|
65
|
+
this.osVersion = jest.fn().mockReturnValue((0, rxjs_1.of)("1.0.0"));
|
|
66
|
+
this.status = jest.fn().mockReturnValue((0, rxjs_1.of)({
|
|
67
|
+
state: status_1.STATUS.ONLINE,
|
|
68
|
+
charging: false,
|
|
69
|
+
battery: 100,
|
|
70
|
+
sleepMode: false,
|
|
71
|
+
updatingProgress: 0,
|
|
72
|
+
bluetoothEnabled: false,
|
|
73
|
+
sleepModeReason: null,
|
|
74
|
+
lastHeartbeat: Date.now(),
|
|
75
|
+
ssid: "test-network"
|
|
76
|
+
}));
|
|
77
|
+
this.dispatchAction = jest.fn().mockImplementation((payload) => __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
const { effects } = payload.message;
|
|
79
|
+
for (const location of Object.keys(effects)) {
|
|
80
|
+
if (![platform_1.HAPTIC_P7, platform_1.HAPTIC_P8].includes(location)) {
|
|
81
|
+
throw new Error(`Invalid haptic location: ${location}`);
|
|
82
|
+
}
|
|
83
|
+
const effectList = effects[location];
|
|
84
|
+
if (!Array.isArray(effectList)) {
|
|
85
|
+
throw new Error("Effects must be an array");
|
|
86
|
+
}
|
|
87
|
+
if (effectList.length > 7) {
|
|
88
|
+
throw new Error("Maximum items in array is 7");
|
|
89
|
+
}
|
|
90
|
+
for (const effect of effectList) {
|
|
91
|
+
const validEffects = [
|
|
92
|
+
hapticEffects_1.strongClick100,
|
|
93
|
+
hapticEffects_1.strongBuzz100,
|
|
94
|
+
hapticEffects_1.alert750ms,
|
|
95
|
+
hapticEffects_1.doubleClick100,
|
|
96
|
+
hapticEffects_1.sharpClick100
|
|
97
|
+
];
|
|
98
|
+
if (!validEffects.includes(effect)) {
|
|
99
|
+
throw new Error("Invalid haptic effect");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return Promise.resolve();
|
|
104
|
+
}));
|
|
105
|
+
this.options = options;
|
|
106
|
+
this._selectedDevice.next(this._deviceInfo);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return Object.assign(Object.assign({}, originalModule), { CloudClient: jest
|
|
110
|
+
.fn()
|
|
111
|
+
.mockImplementation((options) => new MockCloudClient(options)) });
|
|
112
|
+
});
|
|
113
|
+
describe("Haptics", () => {
|
|
114
|
+
let neurosity;
|
|
115
|
+
const testDeviceId = "test-device-id";
|
|
116
|
+
beforeEach(() => {
|
|
117
|
+
neurosity = new Neurosity_1.Neurosity({
|
|
118
|
+
deviceId: testDeviceId,
|
|
119
|
+
emulator: true
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe("Haptic Effects", () => {
|
|
123
|
+
it("should trigger strong click haptic effect on P7", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
124
|
+
yield expect(neurosity.haptics({
|
|
125
|
+
[platform_1.HAPTIC_P7]: [hapticEffects_1.strongClick100]
|
|
126
|
+
})).resolves.not.toThrow();
|
|
127
|
+
}));
|
|
128
|
+
it("should trigger strong buzz haptic effect on P8", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
129
|
+
yield expect(neurosity.haptics({
|
|
130
|
+
[platform_1.HAPTIC_P8]: [hapticEffects_1.strongBuzz100]
|
|
131
|
+
})).resolves.not.toThrow();
|
|
132
|
+
}));
|
|
133
|
+
it("should trigger effects on both motors", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
134
|
+
yield expect(neurosity.haptics({
|
|
135
|
+
[platform_1.HAPTIC_P7]: [hapticEffects_1.alert750ms],
|
|
136
|
+
[platform_1.HAPTIC_P8]: [hapticEffects_1.doubleClick100]
|
|
137
|
+
})).resolves.not.toThrow();
|
|
138
|
+
}));
|
|
139
|
+
it("should trigger multiple effects on one motor", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
140
|
+
yield expect(neurosity.haptics({
|
|
141
|
+
[platform_1.HAPTIC_P7]: [hapticEffects_1.sharpClick100, hapticEffects_1.strongClick100, hapticEffects_1.doubleClick100]
|
|
142
|
+
})).resolves.not.toThrow();
|
|
143
|
+
}));
|
|
144
|
+
});
|
|
145
|
+
describe("Error Handling", () => {
|
|
146
|
+
it("should handle invalid haptic location", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
147
|
+
yield expect(neurosity.haptics({
|
|
148
|
+
invalid: [hapticEffects_1.strongClick100]
|
|
149
|
+
})).rejects.toThrow(/location not supported/);
|
|
150
|
+
}));
|
|
151
|
+
it("should handle invalid haptic effect", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
152
|
+
yield expect(neurosity.haptics({
|
|
153
|
+
[platform_1.HAPTIC_P7]: ["invalid"]
|
|
154
|
+
})).rejects.toThrow("Invalid haptic effect");
|
|
155
|
+
}));
|
|
156
|
+
it("should handle too many effects", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
157
|
+
yield expect(neurosity.haptics({
|
|
158
|
+
[platform_1.HAPTIC_P7]: Array(8).fill(hapticEffects_1.strongClick100)
|
|
159
|
+
})).rejects.toThrow(/Maximum items in array is 7/);
|
|
160
|
+
}));
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const Neurosity_1 = require("../Neurosity");
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
const operators_1 = require("rxjs/operators");
|
|
6
|
+
const status_1 = require("../types/status");
|
|
7
|
+
// Mock CloudClient
|
|
8
|
+
jest.mock("../api/index", () => {
|
|
9
|
+
const mockSubscriptions = new Map();
|
|
10
|
+
const mockListeners = new Map();
|
|
11
|
+
let subscriptionId = 0;
|
|
12
|
+
const mockCloudClient = {
|
|
13
|
+
login: jest.fn(),
|
|
14
|
+
logout: jest.fn(),
|
|
15
|
+
onAuthStateChanged: jest.fn(),
|
|
16
|
+
onDeviceChange: jest.fn(),
|
|
17
|
+
status: jest.fn(),
|
|
18
|
+
metrics: {
|
|
19
|
+
on: jest.fn((subscription, callback) => {
|
|
20
|
+
const listener = (value) => callback(value);
|
|
21
|
+
const key = `${subscription.id}-${subscription.metric}-${subscription.labels.join(",")}`;
|
|
22
|
+
if (!mockListeners.has(key)) {
|
|
23
|
+
mockListeners.set(key, []);
|
|
24
|
+
}
|
|
25
|
+
mockListeners.get(key).push(listener);
|
|
26
|
+
return listener;
|
|
27
|
+
}),
|
|
28
|
+
subscribe: jest.fn((subscription) => {
|
|
29
|
+
const id = `subscription-${subscriptionId++}`;
|
|
30
|
+
const sub = Object.assign({ id }, subscription);
|
|
31
|
+
mockSubscriptions.set(id, sub);
|
|
32
|
+
return sub;
|
|
33
|
+
}),
|
|
34
|
+
unsubscribe: jest.fn((subscription, listener) => {
|
|
35
|
+
const key = `${subscription.id}-${subscription.metric}-${subscription.labels.join(",")}`;
|
|
36
|
+
const listeners = mockListeners.get(key) || [];
|
|
37
|
+
const index = listeners.indexOf(listener);
|
|
38
|
+
if (index > -1) {
|
|
39
|
+
listeners.splice(index, 1);
|
|
40
|
+
}
|
|
41
|
+
mockSubscriptions.delete(subscription.id);
|
|
42
|
+
})
|
|
43
|
+
},
|
|
44
|
+
osVersion: jest.fn(),
|
|
45
|
+
userClaims: {
|
|
46
|
+
scopes: ["focus", "kinesis"]
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
CloudClient: jest.fn().mockImplementation(() => mockCloudClient)
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
const testDeviceId = "mock-device-id";
|
|
54
|
+
describe("Metrics", () => {
|
|
55
|
+
let neurosity;
|
|
56
|
+
let mockFocusData;
|
|
57
|
+
let mockKinesisData;
|
|
58
|
+
let cloudClient;
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
neurosity = new Neurosity_1.Neurosity({
|
|
61
|
+
deviceId: testDeviceId,
|
|
62
|
+
emulator: true
|
|
63
|
+
});
|
|
64
|
+
cloudClient = neurosity.cloudClient;
|
|
65
|
+
// Mock device info
|
|
66
|
+
const mockDeviceInfo = {
|
|
67
|
+
deviceId: testDeviceId,
|
|
68
|
+
channelNames: ["CP3", "C3", "F5", "PO3", "PO4", "F6", "C4", "CP4"],
|
|
69
|
+
samplingRate: 256,
|
|
70
|
+
modelName: "crown",
|
|
71
|
+
modelVersion: "v3"
|
|
72
|
+
};
|
|
73
|
+
// Mock device status
|
|
74
|
+
const mockDeviceStatus = {
|
|
75
|
+
state: status_1.STATUS.ONLINE,
|
|
76
|
+
charging: false,
|
|
77
|
+
battery: 100,
|
|
78
|
+
sleepMode: false,
|
|
79
|
+
sleepModeReason: null,
|
|
80
|
+
lastHeartbeat: Date.now(),
|
|
81
|
+
ssid: "test-network"
|
|
82
|
+
};
|
|
83
|
+
// Setup mock data
|
|
84
|
+
mockFocusData = new rxjs_1.BehaviorSubject({
|
|
85
|
+
probability: 0.85,
|
|
86
|
+
label: "focus",
|
|
87
|
+
metric: "awareness",
|
|
88
|
+
timestamp: Date.now()
|
|
89
|
+
});
|
|
90
|
+
mockKinesisData = new rxjs_1.BehaviorSubject({
|
|
91
|
+
probability: 0.75,
|
|
92
|
+
label: "leftArm",
|
|
93
|
+
metric: "kinesis",
|
|
94
|
+
timestamp: Date.now()
|
|
95
|
+
});
|
|
96
|
+
// Mock cloud client methods
|
|
97
|
+
cloudClient.onDeviceChange.mockReturnValue((0, rxjs_1.of)(mockDeviceInfo));
|
|
98
|
+
cloudClient.status.mockReturnValue((0, rxjs_1.of)(mockDeviceStatus));
|
|
99
|
+
cloudClient.osVersion.mockReturnValue((0, rxjs_1.of)("16.0.0"));
|
|
100
|
+
// Setup metrics subscription behavior
|
|
101
|
+
cloudClient.metrics.subscribe.mockImplementation((subscription) => {
|
|
102
|
+
return Object.assign({ id: "test-id" }, subscription);
|
|
103
|
+
});
|
|
104
|
+
cloudClient.metrics.on.mockImplementation((subscription, callback) => {
|
|
105
|
+
if (subscription.metric === "awareness") {
|
|
106
|
+
const sub = mockFocusData.subscribe((value) => callback(value));
|
|
107
|
+
return () => sub.unsubscribe();
|
|
108
|
+
}
|
|
109
|
+
else if (subscription.metric === "kinesis") {
|
|
110
|
+
const sub = mockKinesisData.subscribe((value) => callback(value));
|
|
111
|
+
return () => sub.unsubscribe();
|
|
112
|
+
}
|
|
113
|
+
return () => { };
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe("Focus", () => {
|
|
117
|
+
it("should get focus metrics", (done) => {
|
|
118
|
+
neurosity
|
|
119
|
+
.focus()
|
|
120
|
+
.pipe((0, operators_1.take)(1))
|
|
121
|
+
.subscribe({
|
|
122
|
+
next: (focus) => {
|
|
123
|
+
expect(focus).toBeDefined();
|
|
124
|
+
expect(focus.probability).toBe(0.85);
|
|
125
|
+
expect(focus.label).toBe("focus");
|
|
126
|
+
expect(focus.metric).toBe("awareness");
|
|
127
|
+
expect(focus.timestamp).toBeDefined();
|
|
128
|
+
done();
|
|
129
|
+
},
|
|
130
|
+
error: done
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe("Kinesis", () => {
|
|
135
|
+
it("should get kinesis metrics", (done) => {
|
|
136
|
+
neurosity
|
|
137
|
+
.kinesis("leftArm")
|
|
138
|
+
.pipe((0, operators_1.take)(1))
|
|
139
|
+
.subscribe({
|
|
140
|
+
next: (kinesis) => {
|
|
141
|
+
expect(kinesis).toBeDefined();
|
|
142
|
+
expect(kinesis.probability).toBe(0.75);
|
|
143
|
+
expect(kinesis.label).toBe("leftArm");
|
|
144
|
+
expect(kinesis.metric).toBe("kinesis");
|
|
145
|
+
expect(kinesis.timestamp).toBeDefined();
|
|
146
|
+
done();
|
|
147
|
+
},
|
|
148
|
+
error: done
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
describe("Error Handling", () => {
|
|
153
|
+
it("should handle device offline state", (done) => {
|
|
154
|
+
// Mock device going offline
|
|
155
|
+
cloudClient.onDeviceChange.mockReturnValue((0, rxjs_1.of)({
|
|
156
|
+
deviceId: testDeviceId,
|
|
157
|
+
channelNames: ["CP3", "C3", "F5", "PO3", "PO4", "F6", "C4", "CP4"],
|
|
158
|
+
samplingRate: 256,
|
|
159
|
+
modelName: "crown",
|
|
160
|
+
modelVersion: "v3"
|
|
161
|
+
}));
|
|
162
|
+
cloudClient.status.mockReturnValue((0, rxjs_1.throwError)(() => new Error("Device is offline")));
|
|
163
|
+
neurosity
|
|
164
|
+
.focus()
|
|
165
|
+
.pipe((0, operators_1.take)(1))
|
|
166
|
+
.subscribe({
|
|
167
|
+
next: () => {
|
|
168
|
+
done(new Error("Should not emit when device is offline"));
|
|
169
|
+
},
|
|
170
|
+
error: (err) => {
|
|
171
|
+
expect(err).toBeDefined();
|
|
172
|
+
expect(err.message).toContain("offline");
|
|
173
|
+
done();
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|