@osimatic/helpers-js 1.4.25 → 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 (110) hide show
  1. package/.claude/settings.local.json +1 -1
  2. package/duration.js +174 -125
  3. package/file.js +19 -4
  4. package/google_charts.js +2 -1
  5. package/location.js +5 -1
  6. package/media.js +6 -6
  7. package/multi_files_input.js +3 -1
  8. package/package.json +2 -1
  9. package/paging.js +2 -2
  10. package/tests/__mocks__/socket.io-client.js +13 -0
  11. package/tests/count_down.test.js +580 -0
  12. package/tests/details_sub_array.test.js +367 -0
  13. package/tests/file.test.js +210 -0
  14. package/tests/flash_message.test.js +297 -0
  15. package/tests/form_date.test.js +1142 -0
  16. package/tests/form_helper.test.js +780 -130
  17. package/tests/google_charts.test.js +768 -0
  18. package/tests/google_maps.test.js +655 -0
  19. package/tests/google_recaptcha.test.js +441 -0
  20. package/tests/import_from_csv.test.js +797 -0
  21. package/tests/list_box.test.js +255 -0
  22. package/tests/location.test.js +86 -0
  23. package/tests/media.test.js +15 -0
  24. package/tests/multi_files_input.test.js +1015 -0
  25. package/tests/multiple_action_in_table.test.js +477 -0
  26. package/tests/paging.test.js +646 -0
  27. package/tests/select_all.test.js +360 -0
  28. package/tests/sortable_list.test.js +602 -0
  29. package/tests/string.test.js +16 -0
  30. package/tests/web_rtc.test.js +458 -0
  31. package/tests/web_socket.test.js +538 -0
  32. package/tmpclaude-00a6-cwd +0 -1
  33. package/tmpclaude-0526-cwd +0 -1
  34. package/tmpclaude-0973-cwd +0 -1
  35. package/tmpclaude-0b61-cwd +0 -1
  36. package/tmpclaude-0fa4-cwd +0 -1
  37. package/tmpclaude-104f-cwd +0 -1
  38. package/tmpclaude-1468-cwd +0 -1
  39. package/tmpclaude-146f-cwd +0 -1
  40. package/tmpclaude-223d-cwd +0 -1
  41. package/tmpclaude-2330-cwd +0 -1
  42. package/tmpclaude-282a-cwd +0 -1
  43. package/tmpclaude-2846-cwd +0 -1
  44. package/tmpclaude-28a6-cwd +0 -1
  45. package/tmpclaude-2b5a-cwd +0 -1
  46. package/tmpclaude-2def-cwd +0 -1
  47. package/tmpclaude-324b-cwd +0 -1
  48. package/tmpclaude-35d3-cwd +0 -1
  49. package/tmpclaude-3906-cwd +0 -1
  50. package/tmpclaude-3b32-cwd +0 -1
  51. package/tmpclaude-3da9-cwd +0 -1
  52. package/tmpclaude-3dc3-cwd +0 -1
  53. package/tmpclaude-3e3b-cwd +0 -1
  54. package/tmpclaude-43b6-cwd +0 -1
  55. package/tmpclaude-4495-cwd +0 -1
  56. package/tmpclaude-462f-cwd +0 -1
  57. package/tmpclaude-4aa8-cwd +0 -1
  58. package/tmpclaude-4b29-cwd +0 -1
  59. package/tmpclaude-4db5-cwd +0 -1
  60. package/tmpclaude-4e01-cwd +0 -1
  61. package/tmpclaude-5101-cwd +0 -1
  62. package/tmpclaude-524f-cwd +0 -1
  63. package/tmpclaude-5636-cwd +0 -1
  64. package/tmpclaude-5cdd-cwd +0 -1
  65. package/tmpclaude-5f1f-cwd +0 -1
  66. package/tmpclaude-6078-cwd +0 -1
  67. package/tmpclaude-622e-cwd +0 -1
  68. package/tmpclaude-6802-cwd +0 -1
  69. package/tmpclaude-6e36-cwd +0 -1
  70. package/tmpclaude-7793-cwd +0 -1
  71. package/tmpclaude-7f96-cwd +0 -1
  72. package/tmpclaude-8566-cwd +0 -1
  73. package/tmpclaude-8874-cwd +0 -1
  74. package/tmpclaude-8915-cwd +0 -1
  75. package/tmpclaude-8c8b-cwd +0 -1
  76. package/tmpclaude-94df-cwd +0 -1
  77. package/tmpclaude-9859-cwd +0 -1
  78. package/tmpclaude-9ac5-cwd +0 -1
  79. package/tmpclaude-9f18-cwd +0 -1
  80. package/tmpclaude-a202-cwd +0 -1
  81. package/tmpclaude-a741-cwd +0 -1
  82. package/tmpclaude-ab5f-cwd +0 -1
  83. package/tmpclaude-b008-cwd +0 -1
  84. package/tmpclaude-b0a1-cwd +0 -1
  85. package/tmpclaude-b63d-cwd +0 -1
  86. package/tmpclaude-b681-cwd +0 -1
  87. package/tmpclaude-b72d-cwd +0 -1
  88. package/tmpclaude-b92f-cwd +0 -1
  89. package/tmpclaude-bc49-cwd +0 -1
  90. package/tmpclaude-bc50-cwd +0 -1
  91. package/tmpclaude-bccf-cwd +0 -1
  92. package/tmpclaude-be55-cwd +0 -1
  93. package/tmpclaude-c228-cwd +0 -1
  94. package/tmpclaude-c717-cwd +0 -1
  95. package/tmpclaude-c7ce-cwd +0 -1
  96. package/tmpclaude-cf3e-cwd +0 -1
  97. package/tmpclaude-d142-cwd +0 -1
  98. package/tmpclaude-d5bc-cwd +0 -1
  99. package/tmpclaude-d6ae-cwd +0 -1
  100. package/tmpclaude-d77a-cwd +0 -1
  101. package/tmpclaude-d8da-cwd +0 -1
  102. package/tmpclaude-dbdb-cwd +0 -1
  103. package/tmpclaude-de61-cwd +0 -1
  104. package/tmpclaude-de81-cwd +0 -1
  105. package/tmpclaude-df9d-cwd +0 -1
  106. package/tmpclaude-e786-cwd +0 -1
  107. package/tmpclaude-f01d-cwd +0 -1
  108. package/tmpclaude-f2a9-cwd +0 -1
  109. package/tmpclaude-fc36-cwd +0 -1
  110. package/tmpclaude-ffef-cwd +0 -1
@@ -0,0 +1,655 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+ const { GoogleMap } = require('../google_maps');
5
+
6
+ describe('GoogleMap', () => {
7
+ let mockMap;
8
+ let mockMarker;
9
+ let mockInfoWindow;
10
+ let mockLatLng;
11
+ let mockLatLngBounds;
12
+ let mockPolyline;
13
+ let capturedMarkerClickCallback;
14
+
15
+ beforeEach(() => {
16
+ // Mock LatLng
17
+ mockLatLng = jest.fn(function(lat, lng) {
18
+ this.lat = () => lat;
19
+ this.lng = () => lng;
20
+ this._lat = lat;
21
+ this._lng = lng;
22
+ });
23
+
24
+ // Mock LatLngBounds
25
+ mockLatLngBounds = jest.fn(function() {
26
+ this.points = [];
27
+ this.extend = jest.fn(function(latLng) {
28
+ this.points.push(latLng);
29
+ });
30
+ this.getCenter = jest.fn(function() {
31
+ if (this.points.length === 0) {
32
+ return new mockLatLng(0, 0);
33
+ }
34
+ const avgLat = this.points.reduce((sum, p) => sum + p._lat, 0) / this.points.length;
35
+ const avgLng = this.points.reduce((sum, p) => sum + p._lng, 0) / this.points.length;
36
+ return new mockLatLng(avgLat, avgLng);
37
+ });
38
+ });
39
+
40
+ // Mock Marker
41
+ mockMarker = jest.fn(function(options) {
42
+ this.options = options;
43
+ this.position = options.position;
44
+ this.map = options.map;
45
+ this.title = options.title;
46
+ this.icon = options.icon;
47
+ this.listeners = {};
48
+ this.setMap = jest.fn(function(map) {
49
+ this.map = map;
50
+ });
51
+ this.addListener = jest.fn(function(event, callback) {
52
+ this.listeners[event] = callback;
53
+ capturedMarkerClickCallback = callback;
54
+ });
55
+ });
56
+
57
+ // Mock InfoWindow
58
+ mockInfoWindow = jest.fn(function(options) {
59
+ this.content = options.content;
60
+ this.open = jest.fn();
61
+ });
62
+
63
+ // Mock Polyline
64
+ mockPolyline = jest.fn(function(options) {
65
+ this.path = options.path;
66
+ this.setMap = jest.fn();
67
+ });
68
+
69
+ // Mock Map
70
+ mockMap = {
71
+ setCenter: jest.fn(),
72
+ setZoom: jest.fn(),
73
+ fitBounds: jest.fn()
74
+ };
75
+
76
+ // Mock google.maps
77
+ global.google = {
78
+ maps: {
79
+ Map: jest.fn(() => mockMap),
80
+ Marker: mockMarker,
81
+ LatLng: mockLatLng,
82
+ LatLngBounds: mockLatLngBounds,
83
+ InfoWindow: mockInfoWindow,
84
+ Polyline: mockPolyline
85
+ }
86
+ };
87
+
88
+ // Mock jQuery
89
+ global.$ = jest.fn((selector) => {
90
+ if (typeof selector === 'string') {
91
+ if (selector.startsWith('#map')) {
92
+ return { length: 1 };
93
+ }
94
+ return { length: 0 };
95
+ }
96
+ return { length: 0 };
97
+ });
98
+
99
+ // Mock document.getElementById
100
+ document.getElementById = jest.fn((id) => {
101
+ return { id: id };
102
+ });
103
+
104
+ // Clear captured callbacks
105
+ capturedMarkerClickCallback = null;
106
+ });
107
+
108
+ afterEach(() => {
109
+ jest.clearAllMocks();
110
+ delete global.google;
111
+ delete global.$;
112
+ });
113
+
114
+ describe('constructor', () => {
115
+ test('should return early when element does not exist', () => {
116
+ global.$ = jest.fn(() => ({ length: 0 }));
117
+
118
+ const googleMap = new GoogleMap('nonexistent');
119
+
120
+ expect(googleMap.mapId).toBe('nonexistent');
121
+ expect(googleMap.map).toBeUndefined();
122
+ });
123
+
124
+ test('should initialize map with correct properties', () => {
125
+ const googleMap = new GoogleMap('map-id');
126
+
127
+ expect(googleMap.markers).toEqual([]);
128
+ expect(googleMap.locations).toEqual([]);
129
+ expect(googleMap.mapId).toBe('map-id');
130
+ });
131
+
132
+ test('should create Google Map instance', () => {
133
+ const googleMap = new GoogleMap('map-id');
134
+
135
+ expect(global.google.maps.Map).toHaveBeenCalledWith(
136
+ { id: 'map-id' },
137
+ { maxZoom: 18 }
138
+ );
139
+ expect(googleMap.map).toBe(mockMap);
140
+ });
141
+
142
+ test('should center on France after initialization', () => {
143
+ const googleMap = new GoogleMap('map-id');
144
+
145
+ expect(mockMap.setCenter).toHaveBeenCalledWith({
146
+ lat: 46.52863469527167,
147
+ lng: 2.43896484375
148
+ });
149
+ expect(mockMap.setZoom).toHaveBeenCalledWith(6);
150
+ });
151
+
152
+ test('should check if element exists with jQuery', () => {
153
+ const googleMap = new GoogleMap('my-map');
154
+
155
+ expect(global.$).toHaveBeenCalledWith('#my-map');
156
+ });
157
+ });
158
+
159
+ describe('static getUrl', () => {
160
+ test('should return correct Google Maps URL', () => {
161
+ const url = GoogleMap.getUrl(48.8566, 2.3522);
162
+
163
+ expect(url).toBe('https://maps.google.com/?q=48.8566,2.3522');
164
+ });
165
+
166
+ test('should handle negative coordinates', () => {
167
+ const url = GoogleMap.getUrl(-33.8688, 151.2093);
168
+
169
+ expect(url).toBe('https://maps.google.com/?q=-33.8688,151.2093');
170
+ });
171
+
172
+ test('should handle zero coordinates', () => {
173
+ const url = GoogleMap.getUrl(0, 0);
174
+
175
+ expect(url).toBe('https://maps.google.com/?q=0,0');
176
+ });
177
+ });
178
+
179
+ describe('static getUrlFromCoordinates', () => {
180
+ test('should parse coordinates string and return URL', () => {
181
+ const url = GoogleMap.getUrlFromCoordinates('48.8566,2.3522');
182
+
183
+ expect(url).toBe('https://maps.google.com/?q=48.8566,2.3522');
184
+ });
185
+
186
+ test('should handle coordinates with spaces', () => {
187
+ const url = GoogleMap.getUrlFromCoordinates('48.8566, 2.3522');
188
+
189
+ expect(url).toBe('https://maps.google.com/?q=48.8566,2.3522');
190
+ });
191
+
192
+ test('should handle negative coordinates in string', () => {
193
+ const url = GoogleMap.getUrlFromCoordinates('-33.8688,151.2093');
194
+
195
+ expect(url).toBe('https://maps.google.com/?q=-33.8688,151.2093');
196
+ });
197
+ });
198
+
199
+ describe('deleteMarkers', () => {
200
+ test('should clear locations array', () => {
201
+ const googleMap = new GoogleMap('map-id');
202
+ googleMap.locations = ['48.8566,2.3522', '45.764,4.8357'];
203
+
204
+ googleMap.deleteMarkers();
205
+
206
+ expect(googleMap.locations).toEqual([]);
207
+ });
208
+
209
+ test('should remove all markers from map', () => {
210
+ const googleMap = new GoogleMap('map-id');
211
+ const marker1 = new mockMarker({ map: mockMap });
212
+ const marker2 = new mockMarker({ map: mockMap });
213
+ googleMap.markers = [marker1, marker2];
214
+
215
+ googleMap.deleteMarkers();
216
+
217
+ expect(marker1.setMap).toHaveBeenCalledWith(null);
218
+ expect(marker2.setMap).toHaveBeenCalledWith(null);
219
+ });
220
+
221
+ test('should clear markers array', () => {
222
+ const googleMap = new GoogleMap('map-id');
223
+ googleMap.markers = [new mockMarker({}), new mockMarker({})];
224
+
225
+ googleMap.deleteMarkers();
226
+
227
+ expect(googleMap.markers).toEqual([]);
228
+ expect(googleMap.markers.length).toBe(0);
229
+ });
230
+
231
+ test('should handle empty markers array', () => {
232
+ const googleMap = new GoogleMap('map-id');
233
+
234
+ expect(() => {
235
+ googleMap.deleteMarkers();
236
+ }).not.toThrow();
237
+ });
238
+ });
239
+
240
+ describe('addMarkers', () => {
241
+ test('should return early when list is empty', () => {
242
+ const googleMap = new GoogleMap('map-id');
243
+
244
+ googleMap.addMarkers([], null);
245
+
246
+ expect(googleMap.markers.length).toBe(0);
247
+ });
248
+
249
+ test('should add multiple markers', () => {
250
+ const googleMap = new GoogleMap('map-id');
251
+ const locations = ['48.8566,2.3522', '45.764,4.8357', '43.2965,5.3698'];
252
+
253
+ googleMap.addMarkers(locations, 'icon.png');
254
+
255
+ expect(googleMap.markers.length).toBe(3);
256
+ expect(googleMap.locations.length).toBe(3);
257
+ });
258
+
259
+ test('should pass icon to each marker', () => {
260
+ const googleMap = new GoogleMap('map-id');
261
+ const locations = ['48.8566,2.3522', '45.764,4.8357'];
262
+ const icon = 'custom-icon.png';
263
+
264
+ googleMap.addMarkers(locations, icon);
265
+
266
+ expect(mockMarker).toHaveBeenCalledTimes(2);
267
+ googleMap.markers.forEach(marker => {
268
+ expect(marker.icon).toBe(icon);
269
+ });
270
+ });
271
+ });
272
+
273
+ describe('addMarker', () => {
274
+ test('should create marker with correct position', () => {
275
+ const googleMap = new GoogleMap('map-id');
276
+ const coordinates = '48.8566,2.3522';
277
+
278
+ googleMap.addMarker(coordinates);
279
+
280
+ expect(mockLatLng).toHaveBeenCalledWith(48.8566, 2.3522);
281
+ expect(mockMarker).toHaveBeenCalled();
282
+ });
283
+
284
+ test('should add marker to markers array', () => {
285
+ const googleMap = new GoogleMap('map-id');
286
+
287
+ googleMap.addMarker('48.8566,2.3522');
288
+
289
+ expect(googleMap.markers.length).toBe(1);
290
+ });
291
+
292
+ test('should add location to locations array', () => {
293
+ const googleMap = new GoogleMap('map-id');
294
+ const coordinates = '48.8566,2.3522';
295
+
296
+ googleMap.addMarker(coordinates);
297
+
298
+ expect(googleMap.locations).toContain(coordinates);
299
+ });
300
+
301
+ test('should create marker with icon', () => {
302
+ const googleMap = new GoogleMap('map-id');
303
+ const icon = 'marker-icon.png';
304
+
305
+ googleMap.addMarker('48.8566,2.3522', icon);
306
+
307
+ const markerOptions = mockMarker.mock.calls[0][0];
308
+ expect(markerOptions.icon).toBe(icon);
309
+ });
310
+
311
+ test('should create marker with default title when no infoWindow', () => {
312
+ const googleMap = new GoogleMap('map-id');
313
+
314
+ googleMap.addMarker('48.8566,2.3522');
315
+
316
+ const markerOptions = mockMarker.mock.calls[0][0];
317
+ expect(markerOptions.title).toBe('Marker');
318
+ });
319
+
320
+ test('should create marker with custom title from infoWindow', () => {
321
+ const googleMap = new GoogleMap('map-id');
322
+ const infoWindowContent = {
323
+ title: 'Custom Title',
324
+ content: '<div>Content</div>'
325
+ };
326
+
327
+ googleMap.addMarker('48.8566,2.3522', null, infoWindowContent);
328
+
329
+ const markerOptions = mockMarker.mock.calls[0][0];
330
+ expect(markerOptions.title).toBe('Custom Title');
331
+ });
332
+
333
+ test('should create InfoWindow when content provided', () => {
334
+ const googleMap = new GoogleMap('map-id');
335
+ const infoWindowContent = {
336
+ title: 'Title',
337
+ content: '<div>Info content</div>'
338
+ };
339
+
340
+ googleMap.addMarker('48.8566,2.3522', null, infoWindowContent);
341
+
342
+ expect(mockInfoWindow).toHaveBeenCalledWith({
343
+ content: '<div>Info content</div>'
344
+ });
345
+ });
346
+
347
+ test('should add click listener when infoWindow provided', () => {
348
+ const googleMap = new GoogleMap('map-id');
349
+ const infoWindowContent = {
350
+ title: 'Title',
351
+ content: '<div>Content</div>'
352
+ };
353
+
354
+ googleMap.addMarker('48.8566,2.3522', null, infoWindowContent);
355
+
356
+ const marker = googleMap.markers[0];
357
+ expect(marker.addListener).toHaveBeenCalledWith('click', expect.any(Function));
358
+ });
359
+
360
+ test('should open InfoWindow on marker click', () => {
361
+ const googleMap = new GoogleMap('map-id');
362
+ const infoWindowContent = {
363
+ title: 'Title',
364
+ content: '<div>Content</div>'
365
+ };
366
+
367
+ googleMap.addMarker('48.8566,2.3522', null, infoWindowContent);
368
+
369
+ // Trigger the click callback with the map context
370
+ capturedMarkerClickCallback.call({ map: googleMap.map });
371
+
372
+ const infoWindow = mockInfoWindow.mock.instances[0];
373
+ expect(infoWindow.open).toHaveBeenCalled();
374
+ });
375
+
376
+ test('should call markerOnClickCallback when provided', () => {
377
+ const googleMap = new GoogleMap('map-id');
378
+ const infoWindowContent = {
379
+ title: 'Title',
380
+ content: '<div>Content</div>'
381
+ };
382
+ const callback = jest.fn();
383
+
384
+ googleMap.addMarker('48.8566,2.3522', null, infoWindowContent, callback);
385
+
386
+ // Trigger the click callback with the map context
387
+ capturedMarkerClickCallback.call({ map: googleMap.map });
388
+
389
+ expect(callback).toHaveBeenCalled();
390
+ });
391
+
392
+ test('should not call markerOnClickCallback if not a function', () => {
393
+ const googleMap = new GoogleMap('map-id');
394
+ const infoWindowContent = {
395
+ title: 'Title',
396
+ content: '<div>Content</div>'
397
+ };
398
+
399
+ expect(() => {
400
+ googleMap.addMarker('48.8566,2.3522', null, infoWindowContent, 'not-a-function');
401
+ capturedMarkerClickCallback.call({ map: googleMap.map });
402
+ }).not.toThrow();
403
+ });
404
+
405
+ test('should return LatLng object', () => {
406
+ const googleMap = new GoogleMap('map-id');
407
+
408
+ const result = googleMap.addMarker('48.8566,2.3522');
409
+
410
+ expect(result).toBeInstanceOf(mockLatLng);
411
+ expect(result.lat()).toBe(48.8566);
412
+ expect(result.lng()).toBe(2.3522);
413
+ });
414
+
415
+ test('should not create InfoWindow when content is undefined', () => {
416
+ const googleMap = new GoogleMap('map-id');
417
+
418
+ googleMap.addMarker('48.8566,2.3522', null, undefined);
419
+
420
+ expect(mockInfoWindow).not.toHaveBeenCalled();
421
+ });
422
+
423
+ test('should not create InfoWindow when content is null', () => {
424
+ const googleMap = new GoogleMap('map-id');
425
+
426
+ googleMap.addMarker('48.8566,2.3522', null, null);
427
+
428
+ expect(mockInfoWindow).not.toHaveBeenCalled();
429
+ });
430
+ });
431
+
432
+ describe('centerOnFrance', () => {
433
+ test('should set center to France coordinates', () => {
434
+ const googleMap = new GoogleMap('map-id');
435
+ jest.clearAllMocks();
436
+
437
+ googleMap.centerOnFrance();
438
+
439
+ expect(mockMap.setCenter).toHaveBeenCalledWith({
440
+ lat: 46.52863469527167,
441
+ lng: 2.43896484375
442
+ });
443
+ });
444
+
445
+ test('should set zoom to 6', () => {
446
+ const googleMap = new GoogleMap('map-id');
447
+ jest.clearAllMocks();
448
+
449
+ googleMap.centerOnFrance();
450
+
451
+ expect(mockMap.setZoom).toHaveBeenCalledWith(6);
452
+ });
453
+ });
454
+
455
+ describe('centerOnMarkers', () => {
456
+ test('should center on France when no locations', () => {
457
+ const googleMap = new GoogleMap('map-id');
458
+ jest.clearAllMocks();
459
+
460
+ googleMap.centerOnMarkers();
461
+
462
+ expect(mockMap.setCenter).toHaveBeenCalledWith({
463
+ lat: 46.52863469527167,
464
+ lng: 2.43896484375
465
+ });
466
+ expect(mockMap.setZoom).toHaveBeenCalledWith(6);
467
+ });
468
+
469
+ test('should return early when no locations and no map', () => {
470
+ const googleMap = new GoogleMap('map-id');
471
+ googleMap.map = null;
472
+
473
+ expect(() => {
474
+ googleMap.centerOnMarkers();
475
+ }).not.toThrow();
476
+ });
477
+
478
+ test('should create LatLngBounds when locations exist', () => {
479
+ const googleMap = new GoogleMap('map-id');
480
+ googleMap.locations = ['48.8566,2.3522', '45.764,4.8357'];
481
+ jest.clearAllMocks();
482
+
483
+ googleMap.centerOnMarkers();
484
+
485
+ expect(mockLatLngBounds).toHaveBeenCalled();
486
+ });
487
+
488
+ test('should extend bounds for each location', () => {
489
+ const googleMap = new GoogleMap('map-id');
490
+ googleMap.locations = ['48.8566,2.3522', '45.764,4.8357', '43.2965,5.3698'];
491
+ jest.clearAllMocks();
492
+
493
+ googleMap.centerOnMarkers();
494
+
495
+ const bounds = mockLatLngBounds.mock.instances[0];
496
+ expect(bounds.extend).toHaveBeenCalledTimes(3);
497
+ });
498
+
499
+ test('should set map center to bounds center', () => {
500
+ const googleMap = new GoogleMap('map-id');
501
+ googleMap.locations = ['48.8566,2.3522', '45.764,4.8357'];
502
+ jest.clearAllMocks();
503
+
504
+ googleMap.centerOnMarkers();
505
+
506
+ expect(mockMap.setCenter).toHaveBeenCalled();
507
+ });
508
+
509
+ test('should fit bounds on map', () => {
510
+ const googleMap = new GoogleMap('map-id');
511
+ googleMap.locations = ['48.8566,2.3522', '45.764,4.8357'];
512
+ jest.clearAllMocks();
513
+
514
+ googleMap.centerOnMarkers();
515
+
516
+ expect(mockMap.fitBounds).toHaveBeenCalled();
517
+ });
518
+
519
+ test('should handle single location', () => {
520
+ const googleMap = new GoogleMap('map-id');
521
+ googleMap.locations = ['48.8566,2.3522'];
522
+ jest.clearAllMocks();
523
+
524
+ googleMap.centerOnMarkers();
525
+
526
+ const bounds = mockLatLngBounds.mock.instances[0];
527
+ expect(bounds.extend).toHaveBeenCalledTimes(1);
528
+ expect(mockMap.fitBounds).toHaveBeenCalled();
529
+ });
530
+ });
531
+
532
+ describe('connectMarkers', () => {
533
+ test('should not create polylines when no locations', () => {
534
+ const googleMap = new GoogleMap('map-id');
535
+
536
+ googleMap.connectMarkers();
537
+
538
+ expect(mockPolyline).not.toHaveBeenCalled();
539
+ });
540
+
541
+ test('should not create polylines with single location', () => {
542
+ const googleMap = new GoogleMap('map-id');
543
+ googleMap.locations = ['48.8566,2.3522'];
544
+
545
+ googleMap.connectMarkers();
546
+
547
+ expect(mockPolyline).not.toHaveBeenCalled();
548
+ });
549
+
550
+ test('should create one polyline for two locations', () => {
551
+ const googleMap = new GoogleMap('map-id');
552
+ googleMap.locations = ['48.8566,2.3522', '45.764,4.8357'];
553
+
554
+ googleMap.connectMarkers();
555
+
556
+ expect(mockPolyline).toHaveBeenCalledTimes(1);
557
+ });
558
+
559
+ test('should create polylines between consecutive locations', () => {
560
+ const googleMap = new GoogleMap('map-id');
561
+ googleMap.locations = ['48.8566,2.3522', '45.764,4.8357', '43.2965,5.3698'];
562
+
563
+ googleMap.connectMarkers();
564
+
565
+ expect(mockPolyline).toHaveBeenCalledTimes(2);
566
+ });
567
+
568
+ test('should create polyline with correct options', () => {
569
+ const googleMap = new GoogleMap('map-id');
570
+ googleMap.locations = ['48.8566,2.3522', '45.764,4.8357'];
571
+
572
+ googleMap.connectMarkers();
573
+
574
+ const polylineOptions = mockPolyline.mock.calls[0][0];
575
+ expect(polylineOptions.geodesic).toBe(true);
576
+ expect(polylineOptions.strokeColor).toBe('#FF0000');
577
+ expect(polylineOptions.strokeOpacity).toBe(1.0);
578
+ expect(polylineOptions.strokeWeight).toBe(2);
579
+ });
580
+
581
+ test('should set polyline on map', () => {
582
+ const googleMap = new GoogleMap('map-id');
583
+ googleMap.locations = ['48.8566,2.3522', '45.764,4.8357'];
584
+
585
+ googleMap.connectMarkers();
586
+
587
+ const polyline = mockPolyline.mock.instances[0];
588
+ expect(polyline.setMap).toHaveBeenCalledWith(mockMap);
589
+ });
590
+
591
+ test('should create path with correct coordinates', () => {
592
+ const googleMap = new GoogleMap('map-id');
593
+ googleMap.locations = ['48.8566,2.3522', '45.764,4.8357'];
594
+
595
+ googleMap.connectMarkers();
596
+
597
+ const polylineOptions = mockPolyline.mock.calls[0][0];
598
+ expect(polylineOptions.path).toHaveLength(2);
599
+ expect(polylineOptions.path[0]).toBeInstanceOf(mockLatLng);
600
+ expect(polylineOptions.path[1]).toBeInstanceOf(mockLatLng);
601
+ });
602
+
603
+ test('should handle multiple polylines correctly', () => {
604
+ const googleMap = new GoogleMap('map-id');
605
+ googleMap.locations = ['48.8566,2.3522', '45.764,4.8357', '43.2965,5.3698', '50.6292,3.0573'];
606
+
607
+ googleMap.connectMarkers();
608
+
609
+ expect(mockPolyline).toHaveBeenCalledTimes(3);
610
+ mockPolyline.mock.instances.forEach(instance => {
611
+ expect(instance.setMap).toHaveBeenCalledWith(mockMap);
612
+ });
613
+ });
614
+ });
615
+
616
+ describe('integration scenarios', () => {
617
+ test('should add markers and center on them', () => {
618
+ const googleMap = new GoogleMap('map-id');
619
+ const locations = ['48.8566,2.3522', '45.764,4.8357'];
620
+ jest.clearAllMocks();
621
+
622
+ googleMap.addMarkers(locations);
623
+ googleMap.centerOnMarkers();
624
+
625
+ expect(googleMap.markers.length).toBe(2);
626
+ expect(mockMap.fitBounds).toHaveBeenCalled();
627
+ });
628
+
629
+ test('should add markers, connect them, and center', () => {
630
+ const googleMap = new GoogleMap('map-id');
631
+ const locations = ['48.8566,2.3522', '45.764,4.8357', '43.2965,5.3698'];
632
+
633
+ googleMap.addMarkers(locations);
634
+ googleMap.connectMarkers();
635
+ googleMap.centerOnMarkers();
636
+
637
+ expect(googleMap.markers.length).toBe(3);
638
+ expect(mockPolyline).toHaveBeenCalledTimes(2);
639
+ expect(mockMap.fitBounds).toHaveBeenCalled();
640
+ });
641
+
642
+ test('should delete markers and re-add new ones', () => {
643
+ const googleMap = new GoogleMap('map-id');
644
+
645
+ googleMap.addMarkers(['48.8566,2.3522', '45.764,4.8357']);
646
+ expect(googleMap.markers.length).toBe(2);
647
+
648
+ googleMap.deleteMarkers();
649
+ expect(googleMap.markers.length).toBe(0);
650
+
651
+ googleMap.addMarkers(['43.2965,5.3698']);
652
+ expect(googleMap.markers.length).toBe(1);
653
+ });
654
+ });
655
+ });