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