@osimatic/helpers-js 1.4.24 → 1.4.25

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 (114) 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 +12 -15
  6. package/event_bus.js +2 -2
  7. package/file.js +1 -1
  8. package/form_helper.js +1 -1
  9. package/http_client.js +2 -0
  10. package/jwt.js +18 -6
  11. package/media.js +1 -1
  12. package/number.js +2 -3
  13. package/package.json +3 -2
  14. package/social_network.js +5 -0
  15. package/string.js +11 -2
  16. package/tests/chartjs.test.js +273 -0
  17. package/tests/date_time/DatePeriod.test.js +179 -0
  18. package/tests/date_time/DateTime.test.js +492 -0
  19. package/tests/date_time/SqlDate.test.js +205 -0
  20. package/tests/date_time/SqlDateTime.test.js +326 -0
  21. package/tests/date_time/SqlTime.test.js +162 -0
  22. package/tests/date_time/TimestampUnix.test.js +262 -0
  23. package/tests/draw.test.js +271 -0
  24. package/tests/duration.test.js +365 -0
  25. package/tests/event_bus.test.js +268 -0
  26. package/tests/file.test.js +358 -0
  27. package/tests/form_date.test.js +417 -0
  28. package/tests/form_helper.test.js +415 -0
  29. package/tests/http_client.test.js +570 -0
  30. package/tests/jwt.test.js +804 -0
  31. package/tests/media.test.js +458 -0
  32. package/tests/network.test.js +489 -0
  33. package/tests/number.test.js +448 -0
  34. package/tests/open_street_map.test.js +388 -0
  35. package/tests/shopping_cart.test.js +355 -0
  36. package/tests/social_network.test.js +333 -0
  37. package/tests/string.test.js +473 -0
  38. package/tests/user.test.js +204 -0
  39. package/tests/util.test.js +99 -0
  40. package/tests/visitor.test.js +508 -0
  41. package/tmpclaude-00a6-cwd +1 -0
  42. package/tmpclaude-0526-cwd +1 -0
  43. package/tmpclaude-0973-cwd +1 -0
  44. package/tmpclaude-0b61-cwd +1 -0
  45. package/tmpclaude-146f-cwd +1 -0
  46. package/tmpclaude-223d-cwd +1 -0
  47. package/tmpclaude-2330-cwd +1 -0
  48. package/tmpclaude-282a-cwd +1 -0
  49. package/tmpclaude-2846-cwd +1 -0
  50. package/tmpclaude-28a6-cwd +1 -0
  51. package/tmpclaude-2b5a-cwd +1 -0
  52. package/tmpclaude-2def-cwd +1 -0
  53. package/tmpclaude-3906-cwd +1 -0
  54. package/tmpclaude-3b32-cwd +1 -0
  55. package/tmpclaude-3da9-cwd +1 -0
  56. package/tmpclaude-3dc3-cwd +1 -0
  57. package/tmpclaude-3e3b-cwd +1 -0
  58. package/tmpclaude-43b6-cwd +1 -0
  59. package/tmpclaude-4495-cwd +1 -0
  60. package/tmpclaude-462f-cwd +1 -0
  61. package/tmpclaude-4b29-cwd +1 -0
  62. package/tmpclaude-4db5-cwd +1 -0
  63. package/tmpclaude-4e01-cwd +1 -0
  64. package/tmpclaude-5101-cwd +1 -0
  65. package/tmpclaude-524f-cwd +1 -0
  66. package/tmpclaude-5636-cwd +1 -0
  67. package/tmpclaude-5cdd-cwd +1 -0
  68. package/tmpclaude-5f1f-cwd +1 -0
  69. package/tmpclaude-6078-cwd +1 -0
  70. package/tmpclaude-622e-cwd +1 -0
  71. package/tmpclaude-6802-cwd +1 -0
  72. package/tmpclaude-6e36-cwd +1 -0
  73. package/tmpclaude-7793-cwd +1 -0
  74. package/tmpclaude-7f96-cwd +1 -0
  75. package/tmpclaude-8566-cwd +1 -0
  76. package/tmpclaude-8874-cwd +1 -0
  77. package/tmpclaude-8915-cwd +1 -0
  78. package/tmpclaude-8c8b-cwd +1 -0
  79. package/tmpclaude-94df-cwd +1 -0
  80. package/tmpclaude-9859-cwd +1 -0
  81. package/tmpclaude-9ac5-cwd +1 -0
  82. package/tmpclaude-9f18-cwd +1 -0
  83. package/tmpclaude-a202-cwd +1 -0
  84. package/tmpclaude-a741-cwd +1 -0
  85. package/tmpclaude-ab5f-cwd +1 -0
  86. package/tmpclaude-b008-cwd +1 -0
  87. package/tmpclaude-b0a1-cwd +1 -0
  88. package/tmpclaude-b63d-cwd +1 -0
  89. package/tmpclaude-b681-cwd +1 -0
  90. package/tmpclaude-b72d-cwd +1 -0
  91. package/tmpclaude-b92f-cwd +1 -0
  92. package/tmpclaude-bc49-cwd +1 -0
  93. package/tmpclaude-bc50-cwd +1 -0
  94. package/tmpclaude-bccf-cwd +1 -0
  95. package/tmpclaude-be55-cwd +1 -0
  96. package/tmpclaude-c228-cwd +1 -0
  97. package/tmpclaude-c717-cwd +1 -0
  98. package/tmpclaude-c7ce-cwd +1 -0
  99. package/tmpclaude-cf3e-cwd +1 -0
  100. package/tmpclaude-d142-cwd +1 -0
  101. package/tmpclaude-d5bc-cwd +1 -0
  102. package/tmpclaude-d6ae-cwd +1 -0
  103. package/tmpclaude-d77a-cwd +1 -0
  104. package/tmpclaude-d8da-cwd +1 -0
  105. package/tmpclaude-dbdb-cwd +1 -0
  106. package/tmpclaude-de61-cwd +1 -0
  107. package/tmpclaude-de81-cwd +1 -0
  108. package/tmpclaude-df9d-cwd +1 -0
  109. package/tmpclaude-e786-cwd +1 -0
  110. package/tmpclaude-f01d-cwd +1 -0
  111. package/tmpclaude-f2a9-cwd +1 -0
  112. package/tmpclaude-fc36-cwd +1 -0
  113. package/tmpclaude-ffef-cwd +1 -0
  114. package/visitor.js +2 -2
@@ -0,0 +1,570 @@
1
+ const { HTTPClient } = require('../http_client');
2
+
3
+ // Mock JwtSession
4
+ jest.mock('../jwt', () => ({
5
+ JwtSession: {
6
+ getToken: jest.fn(() => 'mock_jwt_token')
7
+ }
8
+ }));
9
+
10
+ // Mock UrlAndQueryString
11
+ jest.mock('../network', () => ({
12
+ UrlAndQueryString: {
13
+ buildQuery: jest.fn((data) => {
14
+ return Object.entries(data)
15
+ .map(([key, value]) => `${key}=${value}`)
16
+ .join('&');
17
+ })
18
+ }
19
+ }));
20
+
21
+ describe('HTTPClient', () => {
22
+ beforeEach(() => {
23
+ // Reset HTTPClient state
24
+ HTTPClient.authorizationToken = undefined;
25
+ HTTPClient.headers = undefined;
26
+ HTTPClient.refreshTokenUrl = undefined;
27
+ HTTPClient.refreshTokenCallback = undefined;
28
+ HTTPClient.getRefreshTokenCallback = undefined;
29
+ HTTPClient.onSuccessRefreshTokenCallback = undefined;
30
+ HTTPClient.onInvalidRefreshTokenCallback = undefined;
31
+ HTTPClient.onInvalidRefreshTokenRedirectUrl = undefined;
32
+ HTTPClient.onInvalidTokenRedirectUrl = undefined;
33
+ HTTPClient.onInvalidTokenCallback = undefined;
34
+
35
+ // Mock global Headers
36
+ global.Headers = jest.fn().mockImplementation(function() {
37
+ this.headers = {};
38
+ this.append = jest.fn((key, value) => {
39
+ this.headers[key] = value;
40
+ });
41
+ this.get = jest.fn((key) => this.headers[key]);
42
+ });
43
+
44
+ // Mock global FormData
45
+ global.FormData = jest.fn().mockImplementation(function() {
46
+ this.data = {};
47
+ this.append = jest.fn((key, value) => {
48
+ this.data[key] = value;
49
+ });
50
+ this.forEach = jest.fn(function(callback) {
51
+ Object.entries(this.data).forEach(([key, value]) => {
52
+ callback(value, key);
53
+ });
54
+ }.bind(this));
55
+ });
56
+
57
+ // Mock global File
58
+ global.File = class File {
59
+ constructor(parts, filename, options) {
60
+ this.parts = parts;
61
+ this.filename = filename;
62
+ this.options = options;
63
+ }
64
+ };
65
+ });
66
+
67
+ afterEach(() => {
68
+ jest.clearAllMocks();
69
+ });
70
+
71
+ describe('Configuration setters', () => {
72
+ test('setAuthorizationToken should set authorization token', () => {
73
+ HTTPClient.setAuthorizationToken('custom_token');
74
+
75
+ expect(HTTPClient.authorizationToken).toBe('custom_token');
76
+ });
77
+
78
+ test('setRefreshTokenUrl should set refresh token URL', () => {
79
+ HTTPClient.setRefreshTokenUrl('https://api.example.com/refresh');
80
+
81
+ expect(HTTPClient.refreshTokenUrl).toBe('https://api.example.com/refresh');
82
+ });
83
+
84
+ test('setRefreshTokenCallback should set refresh token callback', () => {
85
+ const callback = jest.fn();
86
+ HTTPClient.setRefreshTokenCallback(callback);
87
+
88
+ expect(HTTPClient.refreshTokenCallback).toBe(callback);
89
+ });
90
+
91
+ test('setGetRefreshTokenCallback should set get refresh token callback', () => {
92
+ const callback = jest.fn();
93
+ HTTPClient.setGetRefreshTokenCallback(callback);
94
+
95
+ expect(HTTPClient.getRefreshTokenCallback).toBe(callback);
96
+ });
97
+
98
+ test('setOnSuccessRefreshTokenCallback should set success callback', () => {
99
+ const callback = jest.fn();
100
+ HTTPClient.setOnSuccessRefreshTokenCallback(callback);
101
+
102
+ expect(HTTPClient.onSuccessRefreshTokenCallback).toBe(callback);
103
+ });
104
+
105
+ test('setOnInvalidRefreshTokenCallback should set invalid refresh callback', () => {
106
+ const callback = jest.fn();
107
+ HTTPClient.setOnInvalidRefreshTokenCallback(callback);
108
+
109
+ expect(HTTPClient.onInvalidRefreshTokenCallback).toBe(callback);
110
+ });
111
+
112
+ test('setOnInvalidRefreshTokenRedirectUrl should set redirect URL', () => {
113
+ HTTPClient.setOnInvalidRefreshTokenRedirectUrl('https://example.com/login');
114
+
115
+ expect(HTTPClient.onInvalidRefreshTokenRedirectUrl).toBe('https://example.com/login');
116
+ });
117
+
118
+ test('setOnInvalidTokenRedirectUrl should set redirect URL', () => {
119
+ HTTPClient.setOnInvalidTokenRedirectUrl('https://example.com/expired');
120
+
121
+ expect(HTTPClient.onInvalidTokenRedirectUrl).toBe('https://example.com/expired');
122
+ });
123
+
124
+ test('setOnInvalidTokenCallback should set invalid token callback', () => {
125
+ const callback = jest.fn();
126
+ HTTPClient.setOnInvalidTokenCallback(callback);
127
+
128
+ expect(HTTPClient.onInvalidTokenCallback).toBe(callback);
129
+ });
130
+ });
131
+
132
+ describe('getHeaders', () => {
133
+ test('should return Headers object by default', () => {
134
+ const headers = HTTPClient.getHeaders();
135
+
136
+ expect(headers).toBeInstanceOf(Headers);
137
+ });
138
+
139
+ test('should return plain object when asObject=true', () => {
140
+ const headers = HTTPClient.getHeaders(true);
141
+
142
+ expect(typeof headers).toBe('object');
143
+ expect(headers).not.toBeInstanceOf(Headers);
144
+ });
145
+
146
+ test('should include Authorization header with JWT token', () => {
147
+ const headers = HTTPClient.getHeaders(true);
148
+
149
+ expect(headers.Authorization).toBe('Bearer mock_jwt_token');
150
+ });
151
+
152
+ test('should use custom authorization token if set', () => {
153
+ HTTPClient.setAuthorizationToken('custom_token');
154
+
155
+ const headers = HTTPClient.getHeaders(true);
156
+
157
+ expect(headers.Authorization).toBe('Bearer custom_token');
158
+ });
159
+
160
+ test('should not include Authorization header when addAuthorizationHeader=false', () => {
161
+ const headers = HTTPClient.getHeaders(true, {}, false);
162
+
163
+ expect(headers.Authorization).toBeUndefined();
164
+ });
165
+
166
+ test('should include additional headers', () => {
167
+ const headers = HTTPClient.getHeaders(true, { 'Content-Type': 'application/json' });
168
+
169
+ expect(headers['Content-Type']).toBe('application/json');
170
+ });
171
+
172
+ test('should merge existing HTTPClient.headers', () => {
173
+ HTTPClient.setHeader('X-Custom-Header', 'custom-value');
174
+
175
+ const headers = HTTPClient.getHeaders(true);
176
+
177
+ expect(headers['X-Custom-Header']).toBe('custom-value');
178
+ });
179
+
180
+ test('should handle null authorization token', () => {
181
+ HTTPClient.authorizationToken = null;
182
+ const { JwtSession } = require('../jwt');
183
+ JwtSession.getToken.mockReturnValue(null);
184
+
185
+ const headers = HTTPClient.getHeaders(true);
186
+
187
+ expect(headers.Authorization).toBeUndefined();
188
+ });
189
+
190
+ test('should handle empty authorization token', () => {
191
+ HTTPClient.authorizationToken = '';
192
+ const { JwtSession } = require('../jwt');
193
+ JwtSession.getToken.mockReturnValue('');
194
+
195
+ const headers = HTTPClient.getHeaders(true);
196
+
197
+ expect(headers.Authorization).toBeUndefined();
198
+ });
199
+ });
200
+
201
+ describe('getHeader / setHeader', () => {
202
+ test('setHeader should set a header value', () => {
203
+ HTTPClient.setHeader('X-Custom', 'value');
204
+
205
+ expect(HTTPClient.headers['X-Custom']).toBe('value');
206
+ });
207
+
208
+ test('getHeader should retrieve a header value', () => {
209
+ HTTPClient.setHeader('X-Custom', 'value');
210
+
211
+ const value = HTTPClient.getHeader('X-Custom');
212
+
213
+ expect(value).toBe('value');
214
+ });
215
+
216
+ test('getHeader should return undefined for non-existent header', () => {
217
+ const value = HTTPClient.getHeader('Non-Existent');
218
+
219
+ expect(value).toBeUndefined();
220
+ });
221
+
222
+ test('should initialize headers object if undefined', () => {
223
+ HTTPClient.headers = undefined;
224
+
225
+ HTTPClient.setHeader('X-Test', 'test');
226
+
227
+ expect(HTTPClient.headers).toBeDefined();
228
+ expect(HTTPClient.headers['X-Test']).toBe('test');
229
+ });
230
+ });
231
+
232
+ describe('convertObjectToFormData', () => {
233
+ test('should convert simple object to FormData', () => {
234
+ const obj = { name: 'John', age: 30 };
235
+
236
+ const formData = HTTPClient.convertObjectToFormData(obj);
237
+
238
+ expect(formData).toBeInstanceOf(FormData);
239
+ expect(formData.data.name).toBe('John');
240
+ expect(formData.data.age).toBe(30);
241
+ });
242
+
243
+ test('should handle arrays in object', () => {
244
+ const obj = { tags: ['tag1', 'tag2', 'tag3'] };
245
+
246
+ const formData = HTTPClient.convertObjectToFormData(obj);
247
+
248
+ expect(formData.data['tags[0]']).toBe('tag1');
249
+ expect(formData.data['tags[1]']).toBe('tag2');
250
+ expect(formData.data['tags[2]']).toBe('tag3');
251
+ });
252
+
253
+ test('should handle nested objects', () => {
254
+ const obj = { user: { name: 'John', email: 'john@example.com' } };
255
+
256
+ const formData = HTTPClient.convertObjectToFormData(obj);
257
+
258
+ expect(formData.data['user.name']).toBe('John');
259
+ expect(formData.data['user.email']).toBe('john@example.com');
260
+ });
261
+
262
+ test('should handle File objects', () => {
263
+ const file = new File(['content'], 'test.txt', { type: 'text/plain' });
264
+ const obj = { document: file };
265
+
266
+ const formData = HTTPClient.convertObjectToFormData(obj);
267
+
268
+ expect(formData.data.document).toBe(file);
269
+ });
270
+
271
+ test('should skip null values', () => {
272
+ const obj = { name: 'John', middleName: null, age: 30 };
273
+
274
+ const formData = HTTPClient.convertObjectToFormData(obj);
275
+
276
+ expect(formData.data.name).toBe('John');
277
+ expect(formData.data.age).toBe(30);
278
+ expect(formData.data.middleName).toBeUndefined();
279
+ });
280
+
281
+ test('should skip undefined values', () => {
282
+ const obj = { name: 'John', middleName: undefined, age: 30 };
283
+
284
+ const formData = HTTPClient.convertObjectToFormData(obj);
285
+
286
+ expect(formData.data.name).toBe('John');
287
+ expect(formData.data.age).toBe(30);
288
+ expect(formData.data.middleName).toBeUndefined();
289
+ });
290
+
291
+ test('should handle mixed types', () => {
292
+ const obj = {
293
+ string: 'text',
294
+ number: 42,
295
+ boolean: true,
296
+ array: [1, 2],
297
+ object: { key: 'value' }
298
+ };
299
+
300
+ const formData = HTTPClient.convertObjectToFormData(obj);
301
+
302
+ expect(formData.data.string).toBe('text');
303
+ expect(formData.data.number).toBe(42);
304
+ expect(formData.data.boolean).toBe(true);
305
+ expect(formData.data['array[0]']).toBe(1);
306
+ expect(formData.data['array[1]']).toBe(2);
307
+ expect(formData.data['object.key']).toBe('value');
308
+ });
309
+ });
310
+
311
+ describe('formatQueryString', () => {
312
+ test('should return empty string for null data', () => {
313
+ const result = HTTPClient.formatQueryString(null);
314
+
315
+ expect(result).toBe('');
316
+ });
317
+
318
+ test('should convert object to query string', () => {
319
+ const data = { name: 'John', age: 30 };
320
+
321
+ const result = HTTPClient.formatQueryString(data);
322
+
323
+ expect(result).toBe('&name=John&age=30');
324
+ });
325
+
326
+ test('should prepend & to string without &', () => {
327
+ const result = HTTPClient.formatQueryString('name=John&age=30');
328
+
329
+ expect(result).toBe('&name=John&age=30');
330
+ });
331
+
332
+ test('should not prepend & to string starting with &', () => {
333
+ const result = HTTPClient.formatQueryString('&name=John&age=30');
334
+
335
+ expect(result).toBe('&name=John&age=30');
336
+ });
337
+
338
+ test('should handle empty string', () => {
339
+ const result = HTTPClient.formatQueryString('');
340
+
341
+ expect(result).toBe('');
342
+ });
343
+ });
344
+
345
+ describe('formatFormData', () => {
346
+ test('should return FormData instance unchanged', () => {
347
+ const formData = new FormData();
348
+ formData.append('key', 'value');
349
+
350
+ const result = HTTPClient.formatFormData(formData);
351
+
352
+ expect(result).toBe(formData);
353
+ });
354
+
355
+ test('should convert object to FormData', () => {
356
+ const obj = { name: 'John', age: 30 };
357
+
358
+ const result = HTTPClient.formatFormData(obj);
359
+
360
+ expect(result).toBeInstanceOf(FormData);
361
+ expect(result.data.name).toBe('John');
362
+ expect(result.data.age).toBe(30);
363
+ });
364
+ });
365
+
366
+ describe('formatJsonData', () => {
367
+ test('should return string unchanged', () => {
368
+ const jsonString = '{"name":"John"}';
369
+
370
+ const result = HTTPClient.formatJsonData(jsonString);
371
+
372
+ expect(result).toBe(jsonString);
373
+ });
374
+
375
+ test('should convert object to JSON string', () => {
376
+ const obj = { name: 'John', age: 30 };
377
+
378
+ const result = HTTPClient.formatJsonData(obj);
379
+
380
+ expect(result).toBe('{"name":"John","age":30}');
381
+ });
382
+
383
+ test('should handle nested objects', () => {
384
+ const obj = { user: { name: 'John', email: 'john@example.com' } };
385
+
386
+ const result = HTTPClient.formatJsonData(obj);
387
+
388
+ const parsed = JSON.parse(result);
389
+ expect(parsed['user.name']).toBe('John');
390
+ expect(parsed['user.email']).toBe('john@example.com');
391
+ });
392
+ });
393
+
394
+ describe('isExpiredToken', () => {
395
+ test('should return true for expired JWT token status text', () => {
396
+ const response = { status: 401, statusText: 'Expired JWT Token' };
397
+ const json = {};
398
+
399
+ expect(HTTPClient.isExpiredToken(response, json)).toBe(true);
400
+ });
401
+
402
+ test('should return true for expired token in message', () => {
403
+ const response = { status: 401, statusText: 'Unauthorized' };
404
+ const json = { message: 'Expired JWT Token' };
405
+
406
+ expect(HTTPClient.isExpiredToken(response, json)).toBe(true);
407
+ });
408
+
409
+ test('should return true for expired_token error', () => {
410
+ const response = { status: 401, statusText: 'Unauthorized' };
411
+ const json = { error: 'expired_token' };
412
+
413
+ expect(HTTPClient.isExpiredToken(response, json)).toBe(true);
414
+ });
415
+
416
+ test('should return true for expired_token string', () => {
417
+ const response = { status: 401, statusText: 'Unauthorized' };
418
+ const json = 'expired_token';
419
+
420
+ expect(HTTPClient.isExpiredToken(response, json)).toBe(true);
421
+ });
422
+
423
+ test('should return false for non-401 status', () => {
424
+ const response = { status: 200, statusText: 'OK' };
425
+ const json = { message: 'Expired JWT Token' };
426
+
427
+ expect(HTTPClient.isExpiredToken(response, json)).toBe(false);
428
+ });
429
+
430
+ test('should return false for invalid token message', () => {
431
+ const response = { status: 401, statusText: 'Unauthorized' };
432
+ const json = { message: 'Invalid JWT Token' };
433
+
434
+ expect(HTTPClient.isExpiredToken(response, json)).toBe(false);
435
+ });
436
+ });
437
+
438
+ describe('isInvalidToken', () => {
439
+ test('should return true for invalid JWT token status text', () => {
440
+ const response = { status: 401, statusText: 'Invalid JWT Token' };
441
+ const json = {};
442
+
443
+ expect(HTTPClient.isInvalidToken(response, json)).toBe(true);
444
+ });
445
+
446
+ test('should return true for invalid token in message', () => {
447
+ const response = { status: 401, statusText: 'Unauthorized' };
448
+ const json = { message: 'Invalid JWT Token' };
449
+
450
+ expect(HTTPClient.isInvalidToken(response, json)).toBe(true);
451
+ });
452
+
453
+ test('should return true for invalid_token error', () => {
454
+ const response = { status: 401, statusText: 'Unauthorized' };
455
+ const json = { error: 'invalid_token' };
456
+
457
+ expect(HTTPClient.isInvalidToken(response, json)).toBe(true);
458
+ });
459
+
460
+ test('should return true for authentification_failure error', () => {
461
+ const response = { status: 401, statusText: 'Unauthorized' };
462
+ const json = { error: 'authentification_failure' };
463
+
464
+ expect(HTTPClient.isInvalidToken(response, json)).toBe(true);
465
+ });
466
+
467
+ test('should return true for invalid_token string', () => {
468
+ const response = { status: 401, statusText: 'Unauthorized' };
469
+ const json = 'invalid_token';
470
+
471
+ expect(HTTPClient.isInvalidToken(response, json)).toBe(true);
472
+ });
473
+
474
+ test('should return true for authentification_failure string', () => {
475
+ const response = { status: 401, statusText: 'Unauthorized' };
476
+ const json = 'authentification_failure';
477
+
478
+ expect(HTTPClient.isInvalidToken(response, json)).toBe(true);
479
+ });
480
+
481
+ test('should return false for non-401 status', () => {
482
+ const response = { status: 200, statusText: 'OK' };
483
+ const json = { message: 'Invalid JWT Token' };
484
+
485
+ expect(HTTPClient.isInvalidToken(response, json)).toBe(false);
486
+ });
487
+
488
+ test('should return false for expired token message', () => {
489
+ const response = { status: 401, statusText: 'Unauthorized' };
490
+ const json = { message: 'Expired JWT Token' };
491
+
492
+ expect(HTTPClient.isInvalidToken(response, json)).toBe(false);
493
+ });
494
+ });
495
+
496
+ describe('logRequestFailure', () => {
497
+ let consoleErrorSpy;
498
+
499
+ beforeEach(() => {
500
+ consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
501
+ });
502
+
503
+ afterEach(() => {
504
+ consoleErrorSpy.mockRestore();
505
+ });
506
+
507
+ test('should log network error when response is null', () => {
508
+ HTTPClient.logRequestFailure(null, {});
509
+
510
+ expect(consoleErrorSpy).toHaveBeenCalledWith('Request failure : network error.');
511
+ });
512
+
513
+ test('should log response status and HTTP code', () => {
514
+ const response = { status: 404, statusText: 'Not Found' };
515
+ const json = { error: 'Resource not found' };
516
+
517
+ HTTPClient.logRequestFailure(response, json);
518
+
519
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
520
+ 'Request failure. Status: Not Found ; HTTP Code : 404',
521
+ json
522
+ );
523
+ });
524
+ });
525
+
526
+ describe('logJqueryRequestFailure', () => {
527
+ let consoleErrorSpy;
528
+
529
+ beforeEach(() => {
530
+ consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
531
+ });
532
+
533
+ afterEach(() => {
534
+ consoleErrorSpy.mockRestore();
535
+ });
536
+
537
+ test('should log jQuery request failure', () => {
538
+ const jqxhr = { status: 500, responseJSON: { error: 'Server error' } };
539
+
540
+ HTTPClient.logJqueryRequestFailure(jqxhr, 'error', 'Internal Server Error');
541
+
542
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
543
+ 'Request failure. Status: error ; HTTP Code: 500 ; Error message: Internal Server Error',
544
+ { error: 'Server error' }
545
+ );
546
+ });
547
+
548
+ test('should handle empty error thrown', () => {
549
+ const jqxhr = { status: 400, responseJSON: {} };
550
+
551
+ HTTPClient.logJqueryRequestFailure(jqxhr, 'error', '');
552
+
553
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
554
+ 'Request failure. Status: error ; HTTP Code: 400',
555
+ {}
556
+ );
557
+ });
558
+
559
+ test('should handle null error thrown', () => {
560
+ const jqxhr = { status: 400, responseJSON: {} };
561
+
562
+ HTTPClient.logJqueryRequestFailure(jqxhr, 'error', null);
563
+
564
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
565
+ 'Request failure. Status: error ; HTTP Code: 400',
566
+ {}
567
+ );
568
+ });
569
+ });
570
+ });