@osimatic/helpers-js 1.4.24 → 1.4.26

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 (68) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/chartjs.js +1 -1
  3. package/date_time.js +25 -16
  4. package/draw.js +3 -2
  5. package/duration.js +176 -130
  6. package/event_bus.js +2 -2
  7. package/file.js +20 -5
  8. package/form_helper.js +1 -1
  9. package/google_charts.js +2 -1
  10. package/http_client.js +2 -0
  11. package/jwt.js +18 -6
  12. package/location.js +5 -1
  13. package/media.js +7 -7
  14. package/multi_files_input.js +3 -1
  15. package/number.js +2 -3
  16. package/package.json +4 -2
  17. package/paging.js +2 -2
  18. package/social_network.js +5 -0
  19. package/string.js +11 -2
  20. package/tests/__mocks__/socket.io-client.js +13 -0
  21. package/tests/chartjs.test.js +273 -0
  22. package/tests/count_down.test.js +580 -0
  23. package/tests/date_time/DatePeriod.test.js +179 -0
  24. package/tests/date_time/DateTime.test.js +492 -0
  25. package/tests/date_time/SqlDate.test.js +205 -0
  26. package/tests/date_time/SqlDateTime.test.js +326 -0
  27. package/tests/date_time/SqlTime.test.js +162 -0
  28. package/tests/date_time/TimestampUnix.test.js +262 -0
  29. package/tests/details_sub_array.test.js +367 -0
  30. package/tests/draw.test.js +271 -0
  31. package/tests/duration.test.js +365 -0
  32. package/tests/event_bus.test.js +268 -0
  33. package/tests/file.test.js +568 -0
  34. package/tests/flash_message.test.js +297 -0
  35. package/tests/form_date.test.js +1559 -0
  36. package/tests/form_helper.test.js +1065 -0
  37. package/tests/google_charts.test.js +768 -0
  38. package/tests/google_maps.test.js +655 -0
  39. package/tests/google_recaptcha.test.js +441 -0
  40. package/tests/http_client.test.js +570 -0
  41. package/tests/import_from_csv.test.js +797 -0
  42. package/tests/jwt.test.js +804 -0
  43. package/tests/list_box.test.js +255 -0
  44. package/tests/location.test.js +86 -0
  45. package/tests/media.test.js +473 -0
  46. package/tests/multi_files_input.test.js +1015 -0
  47. package/tests/multiple_action_in_table.test.js +477 -0
  48. package/tests/network.test.js +489 -0
  49. package/tests/number.test.js +448 -0
  50. package/tests/open_street_map.test.js +388 -0
  51. package/tests/paging.test.js +646 -0
  52. package/tests/select_all.test.js +360 -0
  53. package/tests/shopping_cart.test.js +355 -0
  54. package/tests/social_network.test.js +333 -0
  55. package/tests/sortable_list.test.js +602 -0
  56. package/tests/string.test.js +489 -0
  57. package/tests/user.test.js +204 -0
  58. package/tests/util.test.js +99 -0
  59. package/tests/visitor.test.js +508 -0
  60. package/tests/web_rtc.test.js +458 -0
  61. package/tests/web_socket.test.js +538 -0
  62. package/visitor.js +2 -2
  63. package/tmpclaude-0fa4-cwd +0 -1
  64. package/tmpclaude-104f-cwd +0 -1
  65. package/tmpclaude-1468-cwd +0 -1
  66. package/tmpclaude-324b-cwd +0 -1
  67. package/tmpclaude-35d3-cwd +0 -1
  68. package/tmpclaude-4aa8-cwd +0 -1
@@ -0,0 +1,458 @@
1
+ const { WebRTC } = require('../web_rtc');
2
+
3
+ describe('WebRTC', () => {
4
+ let mockPeerConnection;
5
+ let mockSignalingBus;
6
+ let mockStream;
7
+ let mockTrack;
8
+ let consoleErrorSpy;
9
+
10
+ beforeEach(() => {
11
+ // Mock crypto module
12
+ jest.mock('crypto', () => ({
13
+ createHmac: jest.fn(() => ({
14
+ setEncoding: jest.fn(),
15
+ write: jest.fn(),
16
+ end: jest.fn(),
17
+ read: jest.fn(() => 'mock-password-hash')
18
+ }))
19
+ }));
20
+
21
+ // Mock track
22
+ mockTrack = {
23
+ kind: 'video',
24
+ id: 'track-1'
25
+ };
26
+
27
+ // Mock stream
28
+ mockStream = {
29
+ getTracks: jest.fn(() => [mockTrack])
30
+ };
31
+
32
+ // Mock RTCPeerConnection
33
+ mockPeerConnection = {
34
+ localDescription: { type: 'offer', sdp: 'mock-sdp' },
35
+ connectionState: 'connected',
36
+ signalingState: 'stable',
37
+ onconnectionstatechange: null,
38
+ onicecandidate: null,
39
+ ontrack: null,
40
+ oniceconnectionstatechange: null,
41
+ onicecandidateerror: null,
42
+ addTrack: jest.fn(),
43
+ createOffer: jest.fn().mockResolvedValue({ type: 'offer', sdp: 'offer-sdp' }),
44
+ createAnswer: jest.fn().mockResolvedValue({ type: 'answer', sdp: 'answer-sdp' }),
45
+ setLocalDescription: jest.fn().mockResolvedValue(undefined),
46
+ setRemoteDescription: jest.fn().mockResolvedValue(undefined),
47
+ addIceCandidate: jest.fn().mockResolvedValue(undefined),
48
+ getSenders: jest.fn(() => [{ track: mockTrack }]),
49
+ removeTrack: jest.fn(),
50
+ close: jest.fn()
51
+ };
52
+
53
+ global.RTCPeerConnection = jest.fn(() => mockPeerConnection);
54
+ global.RTCSessionDescription = jest.fn((desc) => desc);
55
+ global.RTCIceCandidate = jest.fn((candidate) => candidate);
56
+
57
+ // Mock signaling bus
58
+ mockSignalingBus = {
59
+ subscribe: jest.fn(),
60
+ publish: jest.fn()
61
+ };
62
+
63
+ // Spy on console.error
64
+ consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
65
+
66
+ // Clear static properties
67
+ delete WebRTC.turnUrl;
68
+ delete WebRTC.stunUrl;
69
+ delete WebRTC.turnSecret;
70
+ delete WebRTC.signalingBus;
71
+ });
72
+
73
+ afterEach(() => {
74
+ jest.clearAllMocks();
75
+ consoleErrorSpy.mockRestore();
76
+ delete global.RTCPeerConnection;
77
+ delete global.RTCSessionDescription;
78
+ delete global.RTCIceCandidate;
79
+ });
80
+
81
+ describe('setIceServers', () => {
82
+ test('should set TURN and STUN URLs', () => {
83
+ const turnUrl = 'turn:turn.example.com:3478';
84
+ const stunUrl = 'stun:stun.example.com:3478';
85
+
86
+ WebRTC.setIceServers(turnUrl, stunUrl);
87
+
88
+ expect(WebRTC.turnUrl).toBe(turnUrl);
89
+ expect(WebRTC.stunUrl).toBe(stunUrl);
90
+ });
91
+ });
92
+
93
+ describe('setTurnSecret', () => {
94
+ test('should set TURN secret', () => {
95
+ const secret = 'my-secret-key';
96
+
97
+ WebRTC.setTurnSecret(secret);
98
+
99
+ expect(WebRTC.turnSecret).toBe(secret);
100
+ });
101
+ });
102
+
103
+ describe('setSignalingBus', () => {
104
+ test('should set signaling bus', () => {
105
+ WebRTC.setSignalingBus(mockSignalingBus);
106
+
107
+ expect(WebRTC.signalingBus).toBe(mockSignalingBus);
108
+ });
109
+ });
110
+
111
+ describe('offer', () => {
112
+ beforeEach(() => {
113
+ WebRTC.setIceServers('turn:turn.example.com', 'stun:stun.example.com');
114
+ WebRTC.setTurnSecret('secret');
115
+ WebRTC.setSignalingBus(mockSignalingBus);
116
+ });
117
+
118
+ test('should create RTCPeerConnection with correct configuration', async () => {
119
+ const iceCandidateCallback = jest.fn();
120
+ const connectionStateCallback = jest.fn();
121
+
122
+ await WebRTC.offer('broadcaster-1', mockStream, iceCandidateCallback, connectionStateCallback);
123
+
124
+ expect(global.RTCPeerConnection).toHaveBeenCalledWith({
125
+ iceServers: [
126
+ {
127
+ urls: ['turn:turn.example.com?transport=udp', 'turn:turn.example.com?transport=tcp'],
128
+ username: expect.any(String),
129
+ credential: expect.any(String)
130
+ },
131
+ {
132
+ urls: 'stun:stun.example.com'
133
+ }
134
+ ]
135
+ });
136
+ });
137
+
138
+ test('should add stream tracks to peer connection', async () => {
139
+ const iceCandidateCallback = jest.fn();
140
+ const connectionStateCallback = jest.fn();
141
+
142
+ await WebRTC.offer('broadcaster-1', mockStream, iceCandidateCallback, connectionStateCallback);
143
+
144
+ expect(mockStream.getTracks).toHaveBeenCalled();
145
+ expect(mockPeerConnection.addTrack).toHaveBeenCalledWith(mockTrack, mockStream);
146
+ });
147
+
148
+ test('should create and set local description', async () => {
149
+ const iceCandidateCallback = jest.fn();
150
+ const connectionStateCallback = jest.fn();
151
+
152
+ await WebRTC.offer('broadcaster-1', mockStream, iceCandidateCallback, connectionStateCallback);
153
+
154
+ expect(mockPeerConnection.createOffer).toHaveBeenCalled();
155
+ expect(mockPeerConnection.setLocalDescription).toHaveBeenCalled();
156
+ });
157
+
158
+ test('should set up connection state change callback', async () => {
159
+ const iceCandidateCallback = jest.fn();
160
+ const connectionStateCallback = jest.fn();
161
+
162
+ await WebRTC.offer('broadcaster-1', mockStream, iceCandidateCallback, connectionStateCallback);
163
+
164
+ expect(mockPeerConnection.onconnectionstatechange).toBeDefined();
165
+
166
+ // Trigger callback
167
+ const event = { type: 'connectionstatechange' };
168
+ mockPeerConnection.onconnectionstatechange(event);
169
+
170
+ expect(connectionStateCallback).toHaveBeenCalledWith(event, 'connected');
171
+ });
172
+
173
+ test('should set up ice candidate callback', async () => {
174
+ const iceCandidateCallback = jest.fn();
175
+ const connectionStateCallback = jest.fn();
176
+
177
+ await WebRTC.offer('broadcaster-1', mockStream, iceCandidateCallback, connectionStateCallback);
178
+
179
+ expect(mockPeerConnection.onicecandidate).toBeDefined();
180
+
181
+ // Trigger callback
182
+ const event = { candidate: { candidate: 'mock-candidate' } };
183
+ mockPeerConnection.onicecandidate(event);
184
+
185
+ expect(iceCandidateCallback).toHaveBeenCalledWith(event);
186
+ });
187
+
188
+ test('should subscribe to answer event', async () => {
189
+ const iceCandidateCallback = jest.fn();
190
+ const connectionStateCallback = jest.fn();
191
+
192
+ await WebRTC.offer('broadcaster-1', mockStream, iceCandidateCallback, connectionStateCallback);
193
+
194
+ expect(mockSignalingBus.subscribe).toHaveBeenCalledWith('answer', expect.any(Function));
195
+ });
196
+
197
+ test('should subscribe to candidate event', async () => {
198
+ const iceCandidateCallback = jest.fn();
199
+ const connectionStateCallback = jest.fn();
200
+
201
+ await WebRTC.offer('broadcaster-1', mockStream, iceCandidateCallback, connectionStateCallback);
202
+
203
+ expect(mockSignalingBus.subscribe).toHaveBeenCalledWith('candidate', expect.any(Function));
204
+ });
205
+
206
+ test('should publish offer', async () => {
207
+ const iceCandidateCallback = jest.fn();
208
+ const connectionStateCallback = jest.fn();
209
+
210
+ await WebRTC.offer('broadcaster-1', mockStream, iceCandidateCallback, connectionStateCallback);
211
+
212
+ expect(mockSignalingBus.publish).toHaveBeenCalledWith('offer', {
213
+ id: 'broadcaster-1',
214
+ description: mockPeerConnection.localDescription
215
+ });
216
+ });
217
+
218
+ test('should resolve with peer connection', async () => {
219
+ const iceCandidateCallback = jest.fn();
220
+ const connectionStateCallback = jest.fn();
221
+
222
+ const result = await WebRTC.offer('broadcaster-1', mockStream, iceCandidateCallback, connectionStateCallback);
223
+
224
+ expect(result).toBe(mockPeerConnection);
225
+ });
226
+
227
+ test('should handle errors and reject', async () => {
228
+ const error = new Error('Connection failed');
229
+ mockPeerConnection.createOffer.mockRejectedValue(error);
230
+
231
+ const iceCandidateCallback = jest.fn();
232
+ const connectionStateCallback = jest.fn();
233
+
234
+ await expect(
235
+ WebRTC.offer('broadcaster-1', mockStream, iceCandidateCallback, connectionStateCallback)
236
+ ).rejects.toThrow('Connection failed');
237
+
238
+ expect(consoleErrorSpy).toHaveBeenCalledWith(error);
239
+ });
240
+
241
+ test('should handle remote description through answer subscription', async () => {
242
+ const iceCandidateCallback = jest.fn();
243
+ const connectionStateCallback = jest.fn();
244
+
245
+ await WebRTC.offer('broadcaster-1', mockStream, iceCandidateCallback, connectionStateCallback);
246
+
247
+ // Get the answer callback
248
+ const answerCallback = mockSignalingBus.subscribe.mock.calls.find(call => call[0] === 'answer')[1];
249
+
250
+ // Trigger the callback
251
+ const answerPayload = { description: { type: 'answer', sdp: 'answer-sdp' } };
252
+ answerCallback(answerPayload);
253
+
254
+ expect(mockPeerConnection.setRemoteDescription).toHaveBeenCalledWith(answerPayload.description);
255
+ });
256
+
257
+ test('should handle ice candidate through candidate subscription', async () => {
258
+ const iceCandidateCallback = jest.fn();
259
+ const connectionStateCallback = jest.fn();
260
+
261
+ await WebRTC.offer('broadcaster-1', mockStream, iceCandidateCallback, connectionStateCallback);
262
+
263
+ // Get the candidate callback
264
+ const candidateCallback = mockSignalingBus.subscribe.mock.calls.find(call => call[0] === 'candidate')[1];
265
+
266
+ // Trigger the callback
267
+ const candidatePayload = { candidate: { candidate: 'ice-candidate' } };
268
+ candidateCallback(candidatePayload);
269
+
270
+ expect(mockPeerConnection.addIceCandidate).toHaveBeenCalled();
271
+ });
272
+ });
273
+
274
+ describe('answer', () => {
275
+ beforeEach(() => {
276
+ WebRTC.setIceServers('turn:turn.example.com', 'stun:stun.example.com');
277
+ WebRTC.setTurnSecret('secret');
278
+ WebRTC.setSignalingBus(mockSignalingBus);
279
+ });
280
+
281
+ test('should create RTCPeerConnection with correct configuration', async () => {
282
+ const remoteDescription = { type: 'offer', sdp: 'remote-sdp' };
283
+ const onTrackCallback = jest.fn();
284
+ const iceCandidateCallback = jest.fn();
285
+ const connectionStateCallback = jest.fn();
286
+
287
+ await WebRTC.answer('broadcaster-1', remoteDescription, onTrackCallback, iceCandidateCallback, connectionStateCallback);
288
+
289
+ expect(global.RTCPeerConnection).toHaveBeenCalled();
290
+ });
291
+
292
+ test('should set remote description', async () => {
293
+ const remoteDescription = { type: 'offer', sdp: 'remote-sdp' };
294
+ const onTrackCallback = jest.fn();
295
+ const iceCandidateCallback = jest.fn();
296
+ const connectionStateCallback = jest.fn();
297
+
298
+ await WebRTC.answer('broadcaster-1', remoteDescription, onTrackCallback, iceCandidateCallback, connectionStateCallback);
299
+
300
+ expect(mockPeerConnection.setRemoteDescription).toHaveBeenCalled();
301
+ });
302
+
303
+ test('should create and set local description', async () => {
304
+ const remoteDescription = { type: 'offer', sdp: 'remote-sdp' };
305
+ const onTrackCallback = jest.fn();
306
+ const iceCandidateCallback = jest.fn();
307
+ const connectionStateCallback = jest.fn();
308
+
309
+ await WebRTC.answer('broadcaster-1', remoteDescription, onTrackCallback, iceCandidateCallback, connectionStateCallback);
310
+
311
+ expect(mockPeerConnection.createAnswer).toHaveBeenCalled();
312
+ expect(mockPeerConnection.setLocalDescription).toHaveBeenCalled();
313
+ });
314
+
315
+ test('should set up ontrack callback', async () => {
316
+ const remoteDescription = { type: 'offer', sdp: 'remote-sdp' };
317
+ const onTrackCallback = jest.fn();
318
+ const iceCandidateCallback = jest.fn();
319
+ const connectionStateCallback = jest.fn();
320
+
321
+ await WebRTC.answer('broadcaster-1', remoteDescription, onTrackCallback, iceCandidateCallback, connectionStateCallback);
322
+
323
+ expect(mockPeerConnection.ontrack).toBeDefined();
324
+
325
+ // Trigger callback
326
+ const event = { streams: [mockStream] };
327
+ mockPeerConnection.ontrack(event);
328
+
329
+ expect(onTrackCallback).toHaveBeenCalledWith([mockStream]);
330
+ });
331
+
332
+ test('should subscribe to candidate event', async () => {
333
+ const remoteDescription = { type: 'offer', sdp: 'remote-sdp' };
334
+ const onTrackCallback = jest.fn();
335
+ const iceCandidateCallback = jest.fn();
336
+ const connectionStateCallback = jest.fn();
337
+
338
+ await WebRTC.answer('broadcaster-1', remoteDescription, onTrackCallback, iceCandidateCallback, connectionStateCallback);
339
+
340
+ expect(mockSignalingBus.subscribe).toHaveBeenCalledWith('candidate', expect.any(Function));
341
+ });
342
+
343
+ test('should publish answer', async () => {
344
+ const remoteDescription = { type: 'offer', sdp: 'remote-sdp' };
345
+ const onTrackCallback = jest.fn();
346
+ const iceCandidateCallback = jest.fn();
347
+ const connectionStateCallback = jest.fn();
348
+
349
+ await WebRTC.answer('broadcaster-1', remoteDescription, onTrackCallback, iceCandidateCallback, connectionStateCallback);
350
+
351
+ expect(mockSignalingBus.publish).toHaveBeenCalledWith('answer', {
352
+ id: 'broadcaster-1',
353
+ description: mockPeerConnection.localDescription
354
+ });
355
+ });
356
+
357
+ test('should resolve with peer connection', async () => {
358
+ const remoteDescription = { type: 'offer', sdp: 'remote-sdp' };
359
+ const onTrackCallback = jest.fn();
360
+ const iceCandidateCallback = jest.fn();
361
+ const connectionStateCallback = jest.fn();
362
+
363
+ const result = await WebRTC.answer('broadcaster-1', remoteDescription, onTrackCallback, iceCandidateCallback, connectionStateCallback);
364
+
365
+ expect(result).toBe(mockPeerConnection);
366
+ });
367
+
368
+ test('should handle errors and reject', async () => {
369
+ const error = new Error('Answer failed');
370
+ mockPeerConnection.createAnswer.mockRejectedValue(error);
371
+
372
+ const remoteDescription = { type: 'offer', sdp: 'remote-sdp' };
373
+ const onTrackCallback = jest.fn();
374
+ const iceCandidateCallback = jest.fn();
375
+ const connectionStateCallback = jest.fn();
376
+
377
+ await expect(
378
+ WebRTC.answer('broadcaster-1', remoteDescription, onTrackCallback, iceCandidateCallback, connectionStateCallback)
379
+ ).rejects.toThrow('Answer failed');
380
+
381
+ expect(consoleErrorSpy).toHaveBeenCalledWith(error);
382
+ });
383
+ });
384
+
385
+ describe('disconnectPeer', () => {
386
+ test('should clear event handlers', () => {
387
+ const result = WebRTC.disconnectPeer(mockPeerConnection);
388
+
389
+ expect(mockPeerConnection.oniceconnectionstatechange).toBeNull();
390
+ expect(mockPeerConnection.onicecandidateerror).toBeNull();
391
+ expect(mockPeerConnection.onicecandidate).toBeNull();
392
+ expect(mockPeerConnection.ontrack).toBeNull();
393
+ });
394
+
395
+ test('should remove tracks when signaling state is not closed', () => {
396
+ mockPeerConnection.signalingState = 'stable';
397
+
398
+ WebRTC.disconnectPeer(mockPeerConnection);
399
+
400
+ expect(mockPeerConnection.getSenders).toHaveBeenCalled();
401
+ expect(mockPeerConnection.removeTrack).toHaveBeenCalled();
402
+ });
403
+
404
+ test('should close peer connection when signaling state is not closed', () => {
405
+ mockPeerConnection.signalingState = 'stable';
406
+
407
+ WebRTC.disconnectPeer(mockPeerConnection);
408
+
409
+ expect(mockPeerConnection.close).toHaveBeenCalled();
410
+ });
411
+
412
+ test('should not close peer connection when signaling state is closed', () => {
413
+ mockPeerConnection.signalingState = 'closed';
414
+
415
+ WebRTC.disconnectPeer(mockPeerConnection);
416
+
417
+ expect(mockPeerConnection.close).not.toHaveBeenCalled();
418
+ });
419
+
420
+ test('should return null', () => {
421
+ const result = WebRTC.disconnectPeer(mockPeerConnection);
422
+
423
+ expect(result).toBeNull();
424
+ });
425
+
426
+ test('should handle null peer connection', () => {
427
+ const result = WebRTC.disconnectPeer(null);
428
+
429
+ expect(result).toBeNull();
430
+ });
431
+
432
+ test('should handle undefined peer connection', () => {
433
+ const result = WebRTC.disconnectPeer(undefined);
434
+
435
+ expect(result).toBeUndefined();
436
+ });
437
+ });
438
+
439
+ describe('getTurnCredentials', () => {
440
+ test('should generate username based on timestamp', () => {
441
+ WebRTC.setTurnSecret('test-secret');
442
+
443
+ const credentials = WebRTC.getTurnCredentials();
444
+
445
+ expect(credentials.username).toMatch(/^\d+$/);
446
+ expect(parseInt(credentials.username)).toBeGreaterThan(0);
447
+ });
448
+
449
+ test('should generate password using HMAC', () => {
450
+ WebRTC.setTurnSecret('test-secret');
451
+
452
+ const credentials = WebRTC.getTurnCredentials();
453
+
454
+ expect(credentials.password).toBeDefined();
455
+ expect(typeof credentials.password).toBe('string');
456
+ });
457
+ });
458
+ });