@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.
Files changed (130) hide show
  1. package/dist/browser/neurosity.iife.js +2419 -1009
  2. package/dist/browser/neurosity.js +180 -148
  3. package/dist/browser/neurosity.js.map +1 -1
  4. package/dist/cjs/__tests__/Neurosity.test.d.ts +1 -0
  5. package/dist/cjs/__tests__/Neurosity.test.js +201 -0
  6. package/dist/cjs/__tests__/WebBluetoothTransport.test.d.ts +1 -0
  7. package/dist/cjs/__tests__/WebBluetoothTransport.test.js +201 -0
  8. package/dist/cjs/__tests__/accelerometer.test.d.ts +1 -0
  9. package/dist/cjs/__tests__/accelerometer.test.js +158 -0
  10. package/dist/cjs/__tests__/auth.test.d.ts +1 -0
  11. package/dist/cjs/__tests__/auth.test.js +239 -0
  12. package/dist/cjs/__tests__/brainwaves.test.d.ts +1 -0
  13. package/dist/cjs/__tests__/brainwaves.test.js +291 -0
  14. package/dist/cjs/__tests__/device.test.d.ts +1 -0
  15. package/dist/cjs/__tests__/device.test.js +281 -0
  16. package/dist/cjs/__tests__/haptics.test.d.ts +1 -0
  17. package/dist/cjs/__tests__/haptics.test.js +162 -0
  18. package/dist/cjs/__tests__/metrics.test.d.ts +1 -0
  19. package/dist/cjs/__tests__/metrics.test.js +178 -0
  20. package/dist/cjs/__tests__/oauth.test.d.ts +1 -0
  21. package/dist/cjs/__tests__/oauth.test.js +138 -0
  22. package/dist/cjs/__tests__/settings.test.d.ts +1 -0
  23. package/dist/cjs/__tests__/settings.test.js +130 -0
  24. package/dist/cjs/__tests__/setup/webBluetooth.setup.d.ts +11 -0
  25. package/dist/cjs/__tests__/setup/webBluetooth.setup.js +35 -0
  26. package/dist/cjs/__tests__/setup.d.ts +0 -0
  27. package/dist/cjs/__tests__/setup.js +7 -0
  28. package/dist/cjs/__tests__/streaming.test.d.ts +1 -0
  29. package/dist/cjs/__tests__/streaming.test.js +259 -0
  30. package/dist/cjs/__tests__/timesync.test.d.ts +1 -0
  31. package/dist/cjs/__tests__/timesync.test.js +54 -0
  32. package/dist/cjs/__tests__/utils.test.d.ts +1 -0
  33. package/dist/cjs/__tests__/utils.test.js +281 -0
  34. package/dist/cjs/api/bluetooth/BluetoothClient.d.ts +6 -6
  35. package/dist/cjs/api/bluetooth/BluetoothTransport.d.ts +1 -1
  36. package/dist/cjs/api/bluetooth/react-native/ReactNativeTransport.d.ts +4 -4
  37. package/dist/cjs/api/bluetooth/react-native/types/ReactNativeTypes.d.ts +2 -2
  38. package/dist/cjs/api/bluetooth/types/index.d.ts +2 -2
  39. package/dist/cjs/api/bluetooth/utils/decodeJSONChunks.d.ts +1 -1
  40. package/dist/cjs/api/bluetooth/utils/stitch.d.ts +1 -1
  41. package/dist/cjs/api/bluetooth/utils/textCodec.d.ts +1 -1
  42. package/dist/cjs/api/bluetooth/web/WebBluetoothTransport.d.ts +1 -1
  43. package/dist/cjs/api/firebase/FirebaseDevice.d.ts +1 -1
  44. package/dist/cjs/api/firebase/FirebaseUser.d.ts +1 -1
  45. package/dist/cjs/api/index.js +1 -1
  46. package/dist/cjs/timesync/Timesync.d.ts +1 -1
  47. package/dist/cjs/types/awareness.d.ts +1 -1
  48. package/dist/cjs/types/brainwaves.d.ts +25 -12
  49. package/dist/cjs/types/credentials.d.ts +4 -4
  50. package/dist/cjs/types/deviceInfo.d.ts +4 -4
  51. package/dist/cjs/types/epoch.d.ts +1 -1
  52. package/dist/cjs/types/experiment.d.ts +1 -1
  53. package/dist/cjs/types/hapticEffects.d.ts +1 -1
  54. package/dist/cjs/types/marker.d.ts +1 -1
  55. package/dist/cjs/types/metrics.d.ts +2 -2
  56. package/dist/cjs/types/oauth.d.ts +4 -4
  57. package/dist/cjs/types/sample.d.ts +2 -2
  58. package/dist/cjs/types/signalQuality.d.ts +1 -1
  59. package/dist/cjs/types/skill.d.ts +2 -2
  60. package/dist/cjs/types/user.d.ts +3 -3
  61. package/dist/cjs/utils/oauth.d.ts +1 -1
  62. package/dist/cjs/utils/transferDevice.d.ts +3 -3
  63. package/dist/cjs/utils/whileOnline.d.ts +1 -1
  64. package/dist/electron/index.js +1 -1
  65. package/dist/electron/index.js.map +1 -1
  66. package/dist/esm/__tests__/Neurosity.test.d.ts +1 -0
  67. package/dist/esm/__tests__/Neurosity.test.js +199 -0
  68. package/dist/esm/__tests__/WebBluetoothTransport.test.d.ts +1 -0
  69. package/dist/esm/__tests__/WebBluetoothTransport.test.js +199 -0
  70. package/dist/esm/__tests__/accelerometer.test.d.ts +1 -0
  71. package/dist/esm/__tests__/accelerometer.test.js +156 -0
  72. package/dist/esm/__tests__/auth.test.d.ts +1 -0
  73. package/dist/esm/__tests__/auth.test.js +237 -0
  74. package/dist/esm/__tests__/brainwaves.test.d.ts +1 -0
  75. package/dist/esm/__tests__/brainwaves.test.js +289 -0
  76. package/dist/esm/__tests__/device.test.d.ts +1 -0
  77. package/dist/esm/__tests__/device.test.js +279 -0
  78. package/dist/esm/__tests__/haptics.test.d.ts +1 -0
  79. package/dist/esm/__tests__/haptics.test.js +160 -0
  80. package/dist/esm/__tests__/metrics.test.d.ts +1 -0
  81. package/dist/esm/__tests__/metrics.test.js +176 -0
  82. package/dist/esm/__tests__/oauth.test.d.ts +1 -0
  83. package/dist/esm/__tests__/oauth.test.js +133 -0
  84. package/dist/esm/__tests__/settings.test.d.ts +1 -0
  85. package/dist/esm/__tests__/settings.test.js +128 -0
  86. package/dist/esm/__tests__/setup/webBluetooth.setup.d.ts +11 -0
  87. package/dist/esm/__tests__/setup/webBluetooth.setup.js +35 -0
  88. package/dist/esm/__tests__/setup.d.ts +0 -0
  89. package/dist/esm/__tests__/setup.js +7 -0
  90. package/dist/esm/__tests__/streaming.test.d.ts +1 -0
  91. package/dist/esm/__tests__/streaming.test.js +257 -0
  92. package/dist/esm/__tests__/timesync.test.d.ts +1 -0
  93. package/dist/esm/__tests__/timesync.test.js +52 -0
  94. package/dist/esm/__tests__/utils.test.d.ts +1 -0
  95. package/dist/esm/__tests__/utils.test.js +279 -0
  96. package/dist/esm/api/bluetooth/BluetoothClient.d.ts +6 -6
  97. package/dist/esm/api/bluetooth/BluetoothTransport.d.ts +1 -1
  98. package/dist/esm/api/bluetooth/react-native/ReactNativeTransport.d.ts +4 -4
  99. package/dist/esm/api/bluetooth/react-native/types/ReactNativeTypes.d.ts +2 -2
  100. package/dist/esm/api/bluetooth/types/index.d.ts +2 -2
  101. package/dist/esm/api/bluetooth/utils/decodeJSONChunks.d.ts +1 -1
  102. package/dist/esm/api/bluetooth/utils/stitch.d.ts +1 -1
  103. package/dist/esm/api/bluetooth/utils/textCodec.d.ts +1 -1
  104. package/dist/esm/api/bluetooth/web/WebBluetoothTransport.d.ts +1 -1
  105. package/dist/esm/api/firebase/FirebaseDevice.d.ts +1 -1
  106. package/dist/esm/api/firebase/FirebaseUser.d.ts +1 -1
  107. package/dist/esm/api/index.js +2 -2
  108. package/dist/esm/neurosity.mjs +2480 -1007
  109. package/dist/esm/timesync/Timesync.d.ts +1 -1
  110. package/dist/esm/types/awareness.d.ts +1 -1
  111. package/dist/esm/types/brainwaves.d.ts +25 -12
  112. package/dist/esm/types/credentials.d.ts +4 -4
  113. package/dist/esm/types/deviceInfo.d.ts +4 -4
  114. package/dist/esm/types/epoch.d.ts +1 -1
  115. package/dist/esm/types/experiment.d.ts +1 -1
  116. package/dist/esm/types/hapticEffects.d.ts +1 -1
  117. package/dist/esm/types/marker.d.ts +1 -1
  118. package/dist/esm/types/metrics.d.ts +2 -2
  119. package/dist/esm/types/oauth.d.ts +4 -4
  120. package/dist/esm/types/sample.d.ts +2 -2
  121. package/dist/esm/types/signalQuality.d.ts +1 -1
  122. package/dist/esm/types/skill.d.ts +2 -2
  123. package/dist/esm/types/user.d.ts +3 -3
  124. package/dist/esm/utils/oauth.d.ts +1 -1
  125. package/dist/esm/utils/transferDevice.d.ts +3 -3
  126. package/dist/esm/utils/whileOnline.d.ts +1 -1
  127. package/dist/examples/neurosity.iife.js +2419 -1009
  128. package/dist/examples/neurosity.js +180 -148
  129. package/dist/examples/neurosity.mjs +2480 -1007
  130. package/package.json +17 -5
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,199 @@
1
+ /// <reference types="node" />
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
+ import { describe } from "node:test";
12
+ import { Neurosity } from "../Neurosity";
13
+ import { firstValueFrom, take, of, ReplaySubject } from "rxjs";
14
+ import { STATUS } from "../types/status";
15
+ // Mock Firebase modules
16
+ jest.mock("../api/firebase", () => {
17
+ const mockFirebaseApp = jest.fn().mockImplementation(() => ({
18
+ disconnect: jest.fn(),
19
+ useEmulator: jest.fn()
20
+ }));
21
+ mockFirebaseApp.prototype.constructor = mockFirebaseApp;
22
+ const mockFirebaseUser = jest.fn().mockImplementation(() => ({
23
+ login: jest.fn().mockResolvedValue({}),
24
+ logout: jest.fn().mockResolvedValue({}),
25
+ onAuthStateChanged: jest.fn().mockReturnValue(of(null)),
26
+ onUserClaimsChange: jest.fn().mockReturnValue(of({}))
27
+ }));
28
+ mockFirebaseUser.prototype.constructor = mockFirebaseUser;
29
+ const mockFirebaseDevice = jest.fn().mockImplementation(() => ({
30
+ disconnect: jest.fn(),
31
+ getInfo: jest.fn().mockResolvedValue({}),
32
+ selectDevice: jest.fn().mockResolvedValue({}),
33
+ dispatchAction: jest.fn()
34
+ }));
35
+ mockFirebaseDevice.prototype.constructor = mockFirebaseDevice;
36
+ return {
37
+ FirebaseApp: mockFirebaseApp,
38
+ FirebaseUser: mockFirebaseUser,
39
+ FirebaseDevice: mockFirebaseDevice
40
+ };
41
+ });
42
+ // Mock CloudClient
43
+ jest.mock("../api", () => {
44
+ const originalModule = jest.requireActual("../api");
45
+ class MockCloudClient {
46
+ constructor(options) {
47
+ this.user = null;
48
+ this.userClaims = { scopes: ["brainwaves"] };
49
+ this.subscriptionManager = {
50
+ add: jest.fn(),
51
+ remove: jest.fn(),
52
+ removeAll: jest.fn()
53
+ };
54
+ this._selectedDevice = new ReplaySubject(1);
55
+ this.login = jest.fn().mockResolvedValue({});
56
+ this.logout = jest.fn().mockResolvedValue({});
57
+ this.getInfo = jest.fn().mockResolvedValue({});
58
+ this.selectDevice = jest.fn().mockResolvedValue({});
59
+ this.didSelectDevice = jest.fn().mockResolvedValue(true);
60
+ this.onDeviceChange = jest.fn().mockReturnValue(of({
61
+ deviceId: "test-device-id",
62
+ status: STATUS.ONLINE
63
+ }));
64
+ this.osVersion = jest.fn().mockReturnValue(of("1.0.0"));
65
+ this.status = jest.fn().mockReturnValue(of({ state: STATUS.ONLINE }));
66
+ this.metrics = {
67
+ subscribe: jest.fn().mockReturnValue(of({})),
68
+ on: jest.fn().mockImplementation((subscription, callback) => {
69
+ callback({});
70
+ return jest.fn();
71
+ }),
72
+ unsubscribe: jest.fn()
73
+ };
74
+ this.options = options;
75
+ this._selectedDevice.next(undefined);
76
+ }
77
+ }
78
+ return Object.assign(Object.assign({}, originalModule), { CloudClient: jest
79
+ .fn()
80
+ .mockImplementation((options) => new MockCloudClient(options)) });
81
+ });
82
+ describe("Neurosity", () => {
83
+ let neurosity;
84
+ const testDeviceId = "test-device-id";
85
+ const testEmail = "test@example.com";
86
+ const testPassword = "test-password";
87
+ beforeEach(() => {
88
+ jest.clearAllMocks();
89
+ neurosity = new Neurosity({
90
+ deviceId: testDeviceId,
91
+ emulator: true
92
+ });
93
+ // Mock _osHasBluetoothSupport to return false to avoid Bluetooth-related code paths
94
+ neurosity["_osHasBluetoothSupport"] = jest.fn().mockReturnValue(of(false));
95
+ });
96
+ describe("Initialization", () => {
97
+ test("should initialize with default options when no options provided", () => {
98
+ const instance = new Neurosity();
99
+ expect(instance).toBeDefined();
100
+ });
101
+ test("should initialize with provided deviceId", () => {
102
+ const instance = new Neurosity({ deviceId: testDeviceId });
103
+ expect(instance).toBeDefined();
104
+ });
105
+ test("should initialize with custom options", () => {
106
+ const instance = new Neurosity({
107
+ deviceId: testDeviceId,
108
+ emulator: true,
109
+ timesync: false
110
+ });
111
+ expect(instance).toBeDefined();
112
+ });
113
+ });
114
+ describe("Authentication", () => {
115
+ test("should handle login with email and password", () => __awaiter(void 0, void 0, void 0, function* () {
116
+ yield expect(neurosity.login({ email: testEmail, password: testPassword })).resolves.not.toThrow();
117
+ }));
118
+ test("should handle logout", () => __awaiter(void 0, void 0, void 0, function* () {
119
+ yield expect(neurosity.logout()).resolves.not.toThrow();
120
+ }));
121
+ });
122
+ describe("Device Management", () => {
123
+ test("should get device info", () => __awaiter(void 0, void 0, void 0, function* () {
124
+ const mockInfo = {
125
+ deviceId: testDeviceId,
126
+ deviceNickname: "Test Device",
127
+ channelNames: ["CH1", "CH2"],
128
+ channels: 2,
129
+ samplingRate: 250,
130
+ manufacturer: "Neurosity",
131
+ model: "Crown",
132
+ modelName: "Crown",
133
+ modelVersion: "v1",
134
+ apiVersion: "1.0.0",
135
+ osVersion: "1.0.0",
136
+ emulator: false
137
+ };
138
+ neurosity["cloudClient"].getInfo.mockResolvedValueOnce(mockInfo);
139
+ const info = yield neurosity.getInfo();
140
+ expect(info).toEqual(mockInfo);
141
+ }));
142
+ test("should handle device selection", () => __awaiter(void 0, void 0, void 0, function* () {
143
+ const mockDevice = {
144
+ deviceId: testDeviceId,
145
+ deviceNickname: "Test Device",
146
+ channelNames: ["CH1", "CH2"],
147
+ channels: 2,
148
+ samplingRate: 250,
149
+ manufacturer: "Neurosity",
150
+ model: "Crown",
151
+ modelName: "Crown",
152
+ modelVersion: "v1",
153
+ apiVersion: "1.0.0",
154
+ osVersion: "1.0.0",
155
+ emulator: false
156
+ };
157
+ neurosity["cloudClient"].selectDevice.mockResolvedValueOnce(mockDevice);
158
+ yield expect(neurosity.selectDevice((devices) => {
159
+ const device = devices.find((d) => d.deviceId === testDeviceId);
160
+ if (!device) {
161
+ throw new Error("Device not found");
162
+ }
163
+ return device;
164
+ })).resolves.not.toThrow();
165
+ }));
166
+ });
167
+ describe("Streaming", () => {
168
+ test("should stream brainwaves", () => __awaiter(void 0, void 0, void 0, function* () {
169
+ const mockRawData = {
170
+ data: [
171
+ [1, 2, 3, 4, 5, 6, 7, 8],
172
+ [9, 10, 11, 12, 13, 14, 15, 16]
173
+ ],
174
+ info: {
175
+ samplingRate: 256,
176
+ startTime: Date.now()
177
+ }
178
+ };
179
+ // Mock the getCloudMetric function to return our mock data
180
+ neurosity["_getCloudMetricDependencies"] = jest.fn().mockReturnValue({
181
+ options: {
182
+ deviceId: testDeviceId,
183
+ emulator: true
184
+ },
185
+ cloudClient: neurosity["cloudClient"],
186
+ onDeviceChange: neurosity["cloudClient"].onDeviceChange,
187
+ status: neurosity["cloudClient"].status,
188
+ getCloudMetric: jest.fn().mockReturnValue(of(mockRawData))
189
+ });
190
+ const brainwaves = yield firstValueFrom(neurosity.brainwaves("raw").pipe(take(1)));
191
+ expect(brainwaves).toBeDefined();
192
+ if ("data" in brainwaves) {
193
+ expect(Array.isArray(brainwaves.data)).toBe(true);
194
+ expect(brainwaves.data.length).toBe(2);
195
+ expect(brainwaves.data[0].length).toBe(8);
196
+ }
197
+ }));
198
+ });
199
+ });
@@ -0,0 +1 @@
1
+ import "./setup/webBluetooth.setup";
@@ -0,0 +1,199 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import "./setup/webBluetooth.setup";
11
+ import { BLUETOOTH_PRIMARY_SERVICE_UUID_HEX } from "@neurosity/ipk";
12
+ import { BLUETOOTH_DEVICE_NAME_PREFIXES } from "@neurosity/ipk";
13
+ import { BLUETOOTH_COMPANY_IDENTIFIER_HEX } from "@neurosity/ipk";
14
+ import { NEVER } from "rxjs";
15
+ import { WebBluetoothTransport } from "../api/bluetooth/web/WebBluetoothTransport";
16
+ import { BLUETOOTH_CONNECTION, TRANSPORT_TYPE } from "../api/bluetooth/types";
17
+ // Get the mock function for isWebBluetoothSupported
18
+ const mockIsWebBluetoothSupported = jest.requireMock("../api/bluetooth/web/isWebBluetoothSupported").isWebBluetoothSupported;
19
+ describe("WebBluetoothTransport", () => {
20
+ let transport;
21
+ let mockDevice;
22
+ let mockServer;
23
+ let mockService;
24
+ let mockCharacteristic;
25
+ beforeEach(() => {
26
+ // Mock device and GATT objects
27
+ mockCharacteristic = {
28
+ uuid: "characteristic-uuid",
29
+ properties: {
30
+ write: true,
31
+ notify: true
32
+ },
33
+ startNotifications: jest.fn().mockResolvedValue(undefined),
34
+ addEventListener: jest.fn(),
35
+ removeEventListener: jest.fn(),
36
+ writeValue: jest.fn().mockResolvedValue(undefined)
37
+ };
38
+ mockService = {
39
+ uuid: BLUETOOTH_PRIMARY_SERVICE_UUID_HEX,
40
+ getCharacteristics: jest.fn().mockResolvedValue([mockCharacteristic])
41
+ };
42
+ mockServer = {
43
+ connected: true,
44
+ getPrimaryService: jest.fn().mockResolvedValue(mockService),
45
+ connect: jest.fn().mockResolvedValue(undefined),
46
+ disconnect: jest.fn()
47
+ };
48
+ mockDevice = {
49
+ id: "test-device",
50
+ name: "Test Device",
51
+ gatt: mockServer,
52
+ watchAdvertisements: jest.fn().mockResolvedValue(undefined),
53
+ addEventListener: jest.fn(),
54
+ removeEventListener: jest.fn()
55
+ };
56
+ // Mock navigator.bluetooth methods
57
+ window.navigator.bluetooth.getDevices.mockResolvedValue([]);
58
+ window.navigator.bluetooth.requestDevice.mockResolvedValue(mockDevice);
59
+ transport = new WebBluetoothTransport();
60
+ });
61
+ afterEach(() => {
62
+ jest.clearAllMocks();
63
+ });
64
+ it("should initialize with correct type and default options", () => {
65
+ expect(transport.type).toBe(TRANSPORT_TYPE.WEB);
66
+ expect(transport.options.autoConnect).toBe(true);
67
+ });
68
+ it("should throw error if Web Bluetooth is not supported", () => {
69
+ mockIsWebBluetoothSupported.mockReturnValueOnce(false);
70
+ expect(() => new WebBluetoothTransport()).toThrow("Web Bluetooth is not supported");
71
+ });
72
+ it("should enable/disable auto connect", (done) => {
73
+ transport.enableAutoConnect(false);
74
+ transport.connection().subscribe((connection) => {
75
+ expect(connection).toBe(BLUETOOTH_CONNECTION.DISCONNECTED);
76
+ done();
77
+ });
78
+ });
79
+ it("should handle connection state changes", (done) => {
80
+ const states = [];
81
+ transport.connection().subscribe((state) => {
82
+ states.push(state);
83
+ if (states.length === 2) {
84
+ expect(states).toEqual([
85
+ BLUETOOTH_CONNECTION.DISCONNECTED,
86
+ BLUETOOTH_CONNECTION.CONNECTED
87
+ ]);
88
+ done();
89
+ }
90
+ });
91
+ transport.connection$.next(BLUETOOTH_CONNECTION.CONNECTED);
92
+ });
93
+ it("should request device with correct options", () => __awaiter(void 0, void 0, void 0, function* () {
94
+ const deviceNickname = "Test Device";
95
+ yield transport.requestDevice(deviceNickname);
96
+ expect(window.navigator.bluetooth.requestDevice).toHaveBeenCalledWith({
97
+ filters: [
98
+ {
99
+ name: deviceNickname
100
+ },
101
+ {
102
+ manufacturerData: [
103
+ {
104
+ companyIdentifier: BLUETOOTH_COMPANY_IDENTIFIER_HEX
105
+ }
106
+ ]
107
+ }
108
+ ],
109
+ optionalServices: [BLUETOOTH_PRIMARY_SERVICE_UUID_HEX]
110
+ });
111
+ }));
112
+ it("should request device with prefixes when no nickname provided", () => __awaiter(void 0, void 0, void 0, function* () {
113
+ yield transport.requestDevice();
114
+ expect(window.navigator.bluetooth.requestDevice).toHaveBeenCalledWith({
115
+ filters: [
116
+ ...BLUETOOTH_DEVICE_NAME_PREFIXES.map((namePrefix) => ({
117
+ namePrefix
118
+ })),
119
+ {
120
+ manufacturerData: [
121
+ {
122
+ companyIdentifier: BLUETOOTH_COMPANY_IDENTIFIER_HEX
123
+ }
124
+ ]
125
+ }
126
+ ],
127
+ optionalServices: [BLUETOOTH_PRIMARY_SERVICE_UUID_HEX]
128
+ });
129
+ }));
130
+ it("should connect to device and setup GATT server", () => __awaiter(void 0, void 0, void 0, function* () {
131
+ mockServer.connect.mockResolvedValueOnce(mockServer);
132
+ yield transport.getServerServiceAndCharacteristics(mockDevice);
133
+ expect(mockServer.connect).toHaveBeenCalled();
134
+ expect(mockServer.getPrimaryService).toHaveBeenCalledWith(BLUETOOTH_PRIMARY_SERVICE_UUID_HEX);
135
+ expect(mockService.getCharacteristics).toHaveBeenCalled();
136
+ expect(transport.connection$.getValue()).toBe(BLUETOOTH_CONNECTION.CONNECTED);
137
+ }));
138
+ it("should handle disconnection", (done) => {
139
+ mockServer.connect.mockResolvedValueOnce(mockServer);
140
+ transport.device = mockDevice;
141
+ transport.server = mockServer;
142
+ transport.connection$.next(BLUETOOTH_CONNECTION.CONNECTED);
143
+ transport.connection().subscribe((connection) => {
144
+ if (connection === BLUETOOTH_CONNECTION.DISCONNECTED) {
145
+ done();
146
+ }
147
+ });
148
+ // Simulate disconnection
149
+ const disconnectCallback = mockDevice.addEventListener.mock.calls.find(([eventName]) => eventName === "gattserverdisconnected")[1];
150
+ disconnectCallback();
151
+ });
152
+ it("should auto connect when enabled", () => __awaiter(void 0, void 0, void 0, function* () {
153
+ const selectedDevice$ = NEVER;
154
+ const autoConnect$ = transport._autoConnect(selectedDevice$);
155
+ // Subscribe to auto connect observable
156
+ autoConnect$.subscribe();
157
+ // Verify auto connect is enabled
158
+ expect(transport.options.autoConnect).toBe(true);
159
+ }));
160
+ it("should get paired devices", () => __awaiter(void 0, void 0, void 0, function* () {
161
+ const devices = [mockDevice];
162
+ window.navigator.bluetooth.getDevices.mockResolvedValueOnce(devices);
163
+ const result = yield transport._getPairedDevices();
164
+ expect(result).toEqual(devices);
165
+ }));
166
+ it("should check connection status", () => {
167
+ transport.connection$.next(BLUETOOTH_CONNECTION.CONNECTED);
168
+ expect(transport.isConnected()).toBe(true);
169
+ transport.connection$.next(BLUETOOTH_CONNECTION.DISCONNECTED);
170
+ expect(transport.isConnected()).toBe(false);
171
+ });
172
+ it("should add logs", (done) => {
173
+ const testLog = "Test log message";
174
+ transport.logs$.subscribe((log) => {
175
+ expect(log).toBe(testLog);
176
+ done();
177
+ });
178
+ transport.addLog(testLog);
179
+ });
180
+ it("should handle connection errors", () => __awaiter(void 0, void 0, void 0, function* () {
181
+ const error = new Error("Connection failed");
182
+ mockServer.connect.mockRejectedValueOnce(error);
183
+ yield expect(transport.getServerServiceAndCharacteristics(mockDevice)).rejects.toThrow("Connection failed");
184
+ expect(transport.connection$.getValue()).not.toBe(BLUETOOTH_CONNECTION.CONNECTED);
185
+ }));
186
+ it("should handle service discovery errors", () => __awaiter(void 0, void 0, void 0, function* () {
187
+ mockServer.connect.mockResolvedValueOnce(mockServer);
188
+ mockServer.getPrimaryService.mockRejectedValueOnce(new Error("Service not found"));
189
+ yield expect(transport.getServerServiceAndCharacteristics(mockDevice)).rejects.toThrow("Service not found");
190
+ expect(transport.connection$.getValue()).not.toBe(BLUETOOTH_CONNECTION.CONNECTED);
191
+ }));
192
+ it("should handle characteristic discovery errors", () => __awaiter(void 0, void 0, void 0, function* () {
193
+ mockServer.connect.mockResolvedValueOnce(mockServer);
194
+ mockServer.getPrimaryService.mockResolvedValueOnce(mockService);
195
+ mockService.getCharacteristics.mockRejectedValueOnce(new Error("Characteristics not found"));
196
+ yield expect(transport.getServerServiceAndCharacteristics(mockDevice)).rejects.toThrow("Characteristics not found");
197
+ expect(transport.connection$.getValue()).not.toBe(BLUETOOTH_CONNECTION.CONNECTED);
198
+ }));
199
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,156 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Neurosity } from "../Neurosity";
11
+ import { BehaviorSubject, Subscription } from "rxjs";
12
+ import { take } from "rxjs/operators";
13
+ import { STATUS } from "../types/status";
14
+ // Mock CloudClient
15
+ jest.mock("../api", () => {
16
+ const originalModule = jest.requireActual("../api");
17
+ class MockCloudClient {
18
+ constructor(options) {
19
+ this.user = null;
20
+ this.userClaims = { scopes: ["brainwaves"] };
21
+ this.subscriptionManager = {
22
+ add: jest.fn(),
23
+ remove: jest.fn(),
24
+ removeAll: jest.fn()
25
+ };
26
+ this._accelerometerData = new BehaviorSubject({
27
+ x: 0.1,
28
+ y: -0.2,
29
+ z: 9.8,
30
+ timestamp: Date.now(),
31
+ acceleration: 9.8,
32
+ inclination: 0,
33
+ orientation: 0,
34
+ pitch: 0,
35
+ roll: 0
36
+ });
37
+ this.bluetoothClient = {
38
+ accelerometer: jest.fn().mockReturnValue(this._accelerometerData),
39
+ connection: jest.fn().mockReturnValue(new BehaviorSubject("connected"))
40
+ };
41
+ this._selectedDevice = new BehaviorSubject({
42
+ deviceId: "test-device-id",
43
+ deviceNickname: "Test Device",
44
+ channelNames: ["CH1", "CH2"],
45
+ channels: 2,
46
+ samplingRate: 250,
47
+ manufacturer: "Neurosity",
48
+ model: "Crown",
49
+ modelName: "Crown",
50
+ modelVersion: "3",
51
+ apiVersion: "1.0.0",
52
+ osVersion: "16.0.0",
53
+ emulator: false
54
+ });
55
+ this.getInfo = jest.fn().mockResolvedValue(this._selectedDevice.value);
56
+ this.selectDevice = jest
57
+ .fn()
58
+ .mockImplementation(() => __awaiter(this, void 0, void 0, function* () { return this._selectedDevice.value; }));
59
+ this.didSelectDevice = jest.fn().mockResolvedValue(true);
60
+ this.onDeviceChange = jest.fn().mockReturnValue(this._selectedDevice);
61
+ this.status = jest.fn().mockReturnValue(new BehaviorSubject({
62
+ state: STATUS.ONLINE,
63
+ charging: false,
64
+ battery: 100,
65
+ sleepMode: false,
66
+ sleepModeReason: null,
67
+ lastHeartbeat: Date.now(),
68
+ ssid: "test-network"
69
+ }));
70
+ this.osVersion = jest.fn().mockReturnValue(new BehaviorSubject("16.0.0"));
71
+ this.metrics = {
72
+ subscribe: jest.fn().mockImplementation(() => {
73
+ const subscription = new Subscription();
74
+ subscription.add(this._accelerometerData.subscribe());
75
+ return subscription;
76
+ }),
77
+ on: jest.fn().mockImplementation((subscription, callback) => {
78
+ const sub = this._accelerometerData.subscribe(callback);
79
+ return () => sub.unsubscribe();
80
+ })
81
+ };
82
+ this.options = options;
83
+ }
84
+ }
85
+ return Object.assign(Object.assign({}, originalModule), { CloudClient: jest
86
+ .fn()
87
+ .mockImplementation((options) => new MockCloudClient(options)) });
88
+ });
89
+ describe("Accelerometer", () => {
90
+ let neurosity;
91
+ const testDeviceId = "test-device-id";
92
+ beforeEach(() => {
93
+ neurosity = new Neurosity({
94
+ deviceId: testDeviceId,
95
+ emulator: true
96
+ });
97
+ });
98
+ describe("Accelerometer Data", () => {
99
+ it("should get accelerometer readings", (done) => {
100
+ neurosity
101
+ .accelerometer()
102
+ .pipe(take(1))
103
+ .subscribe({
104
+ next: (accel) => {
105
+ expect(accel).toBeDefined();
106
+ expect(typeof accel.x).toBe("number");
107
+ expect(typeof accel.y).toBe("number");
108
+ expect(typeof accel.z).toBe("number");
109
+ expect(accel.timestamp).toBeDefined();
110
+ // Check for reasonable values
111
+ expect(accel.x).toBe(0.1);
112
+ expect(accel.y).toBe(-0.2);
113
+ expect(accel.z).toBe(9.8); // Approximately Earth's gravity
114
+ done();
115
+ },
116
+ error: done
117
+ });
118
+ });
119
+ it("should provide continuous accelerometer updates", (done) => {
120
+ neurosity
121
+ .accelerometer()
122
+ .pipe(take(1))
123
+ .subscribe({
124
+ next: (reading) => {
125
+ expect(reading.x).toBeDefined();
126
+ expect(reading.y).toBeDefined();
127
+ expect(reading.z).toBeDefined();
128
+ expect(reading.timestamp).toBeDefined();
129
+ done();
130
+ },
131
+ error: done
132
+ });
133
+ });
134
+ });
135
+ describe("Error Handling", () => {
136
+ it("should handle device offline state", (done) => {
137
+ // Mock device going offline
138
+ const cloudClient = neurosity.cloudClient;
139
+ cloudClient.status.mockReturnValueOnce(new BehaviorSubject({
140
+ state: STATUS.OFFLINE,
141
+ charging: false,
142
+ battery: 100,
143
+ sleepMode: false,
144
+ sleepModeReason: null,
145
+ lastHeartbeat: Date.now(),
146
+ ssid: "test-network"
147
+ }));
148
+ neurosity.accelerometer().subscribe({
149
+ error: (err) => {
150
+ expect(err).toBeDefined();
151
+ done();
152
+ }
153
+ });
154
+ });
155
+ });
156
+ });
@@ -0,0 +1 @@
1
+ export {};