@panoramax/web-viewer 4.0.1 → 4.0.2-develop-9d664bb8

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 (55) hide show
  1. package/CHANGELOG.md +31 -3
  2. package/build/index.css +1 -1
  3. package/build/index.css.map +1 -1
  4. package/build/index.js +150 -51
  5. package/build/index.js.map +1 -1
  6. package/config/jest/mocks.js +2 -1
  7. package/docs/03_URL_settings.md +1 -1
  8. package/docs/09_Develop.md +5 -1
  9. package/docs/reference/components/core/Viewer.md +1 -1
  10. package/docs/reference/components/ui/CopyButton.md +1 -0
  11. package/docs/reference/components/ui/Map.md +13 -0
  12. package/docs/reference/components/ui/MapMore.md +13 -0
  13. package/docs/reference/components/ui/Photo.md +1 -1
  14. package/docs/reference/components/ui/widgets/CopyCoordinates.md +32 -0
  15. package/docs/reference/components/ui/widgets/GeoSearch.md +5 -1
  16. package/docs/reference/utils/API.md +1 -1
  17. package/docs/reference.md +1 -0
  18. package/docs/tutorials/migrate_v4.md +1 -1
  19. package/docs/tutorials/synced_coverage.md +1 -1
  20. package/mkdocs.yml +1 -0
  21. package/package.json +1 -1
  22. package/src/components/core/CoverageMap.js +2 -2
  23. package/src/components/core/PhotoViewer.js +5 -1
  24. package/src/components/core/Viewer.js +10 -5
  25. package/src/components/menus/PictureLegend.js +7 -4
  26. package/src/components/menus/PictureMetadata.js +23 -2
  27. package/src/components/styles.js +61 -0
  28. package/src/components/ui/ButtonGroup.css +2 -0
  29. package/src/components/ui/CopyButton.js +3 -1
  30. package/src/components/ui/Map.js +35 -4
  31. package/src/components/ui/Photo.js +4 -2
  32. package/src/components/ui/TogglableGroup.js +1 -1
  33. package/src/components/ui/widgets/CopyCoordinates.js +75 -0
  34. package/src/components/ui/widgets/GeoSearch.js +13 -5
  35. package/src/components/ui/widgets/Legend.js +1 -1
  36. package/src/components/ui/widgets/OSMEditors.js +2 -2
  37. package/src/components/ui/widgets/PictureLegendActions.js +1 -1
  38. package/src/components/ui/widgets/Player.js +1 -0
  39. package/src/components/ui/widgets/index.js +1 -0
  40. package/src/translations/en.json +6 -2
  41. package/src/translations/fr.json +6 -2
  42. package/src/translations/it.json +3 -1
  43. package/src/translations/ti.json +9 -0
  44. package/src/utils/API.js +1 -1
  45. package/src/utils/InitParameters.js +2 -2
  46. package/src/utils/geocoder.js +155 -67
  47. package/src/utils/index.js +2 -1
  48. package/src/utils/picture.js +6 -1
  49. package/src/utils/services.js +57 -0
  50. package/src/utils/utils.js +18 -5
  51. package/tests/components/ui/Map.test.js +7 -3
  52. package/tests/utils/InitParameters.test.js +15 -15
  53. package/tests/utils/__snapshots__/geocoder.test.js.snap +0 -6
  54. package/tests/utils/geocoder.test.js +1 -1
  55. package/tests/utils/utils.test.js +136 -109
@@ -0,0 +1,57 @@
1
+ /*
2
+ * Settings that may be useful to change for special cases (e.g. offline implementation).
3
+ * Most users will be fine with the defaults.
4
+ */
5
+
6
+
7
+ /**
8
+ * OpenStreetMap iD editor URL
9
+ * @returns {string} The editor URL
10
+ */
11
+ export function IdEditorURL() {
12
+ return "https://www.openstreetmap.org/edit?editor=id";
13
+ }
14
+
15
+
16
+ /* -----------------------------------------------------
17
+ * Internet speed tests
18
+ */
19
+
20
+ /**
21
+ * Get the threshold fast internet speed value.
22
+ *
23
+ * @returns {number} the minimum speed in MBps for internet to be considered "fast"
24
+ */
25
+ export function InternetFastThreshold() {
26
+ return 10;
27
+ }
28
+
29
+ /**
30
+ * Get the fast internet speed test file.
31
+ *
32
+ * @returns {string} URL for the file to use for internet speed testing.
33
+ */
34
+ export function InternetFastTestFile() {
35
+ return "https://panoramax.openstreetmap.fr/images/05/ca/2c/98/0111-4baf-b6f3-587bb8847d2e.jpg";
36
+ }
37
+
38
+
39
+ /* -----------------------------------------------------
40
+ * Geocoding-related settings
41
+ */
42
+
43
+ /**
44
+ * Get the Base Adresse Nationale URL for geocoding API.
45
+ * @returns {string} The Base Adresse Nationale URL (must support /search calls).
46
+ */
47
+ export function AdresseDataGouvBaseURL() {
48
+ return "https://data.geopf.fr/geocodage/search";
49
+ }
50
+
51
+ /**
52
+ * Get the Nominatim base URL for geocoding API.
53
+ * @returns {string} The Nominatim URL (must support /search & /reverse calls).
54
+ */
55
+ export function NominatimBaseUrl() {
56
+ return "https://nominatim.openstreetmap.org";
57
+ }
@@ -1,3 +1,5 @@
1
+ import { InternetFastTestFile, InternetFastThreshold } from "./services";
2
+
1
3
  export const BASE_PANORAMA_ID = "geovisio-fake-id-0";
2
4
 
3
5
  export const COLORS = {
@@ -209,6 +211,19 @@ export function xyzToPosition(x, y, z) {
209
211
  };
210
212
  }
211
213
 
214
+ /**
215
+ * Transforms decimal degrees into degrees/minutes/seconds format.
216
+ * @param {number} degrees The decimal degrees value
217
+ * @returns {object} Coordinate as {d,m,s} object
218
+ */
219
+ export function degToDms(degrees) {
220
+ const d = degrees < 0 ? Math.ceil(degrees) : Math.floor(degrees);
221
+ const rm = Math.abs(degrees - d) * 60;
222
+ const m = Math.floor(rm);
223
+ const s = parseFloat(((rm - m) * 60).toFixed(3));
224
+ return { d, m, s };
225
+ }
226
+
212
227
  /**
213
228
  * Get the query string for JOSM to load current picture area
214
229
  * @returns {string} The query string, or null if not available
@@ -245,9 +260,7 @@ export function isInIframe() {
245
260
  }
246
261
 
247
262
 
248
- const INTERNET_FAST_THRESHOLD = 10; // MBit/s
249
263
  const INTERNET_FAST_STORAGE = "pnx-internet-fast";
250
- const INTERNET_FAST_TESTFILE = "https://panoramax.openstreetmap.fr/images/05/ca/2c/98/0111-4baf-b6f3-587bb8847d2e.jpg";
251
264
 
252
265
  /**
253
266
  * Check if Internet connection is high-speed or not.
@@ -258,7 +271,7 @@ export function isInternetFast() {
258
271
  // Check if downlink property is available
259
272
  try {
260
273
  const speed = navigator.connection.downlink; // MBit/s
261
- return Promise.resolve(speed >= INTERNET_FAST_THRESHOLD);
274
+ return Promise.resolve(speed >= InternetFastThreshold());
262
275
  }
263
276
  // Fallback for other browsers
264
277
  catch(e) {
@@ -271,14 +284,14 @@ export function isInternetFast() {
271
284
 
272
285
  // Run download testing
273
286
  const startTime = (new Date()).getTime();
274
- return fetch(INTERNET_FAST_TESTFILE+"?nocache="+startTime)
287
+ return fetch(`${InternetFastTestFile()}?nocache=${startTime}`)
275
288
  .then(async res => [res, await res.blob()])
276
289
  .then(([res, blob]) => {
277
290
  const size = parseInt(res.headers.get("Content-Length") || blob.size); // Bytes
278
291
  const endTime = (new Date()).getTime();
279
292
  const duration = (endTime - startTime) / 1000; // Transfer time in seconds
280
293
  const speed = (size * 8 / 1024 / 1024) / duration; // MBits/s
281
- const isFast = speed >= INTERNET_FAST_THRESHOLD;
294
+ const isFast = speed >= InternetFastThreshold();
282
295
  sessionStorage.setItem(INTERNET_FAST_STORAGE, isFast ? "true" : "false");
283
296
  return isFast;
284
297
  })
@@ -81,6 +81,7 @@ describe("getBackground", () => {
81
81
  const p = createParent();
82
82
  const c = document.createElement("div");
83
83
  const m = new Map(p, c);
84
+ m.getLayer = () => undefined;
84
85
  expect(m.getBackground()).toBe("streets");
85
86
  });
86
87
  });
@@ -99,7 +100,7 @@ describe("setBackground", () => {
99
100
  resolve();
100
101
  });
101
102
  m.setBackground("aerial");
102
- })
103
+ });
103
104
  });
104
105
 
105
106
  it("skips if setting streets and no raster available", () => {
@@ -107,6 +108,7 @@ describe("setBackground", () => {
107
108
  const c = document.createElement("div");
108
109
  const m = new Map(p, c);
109
110
  m.setLayoutProperty = jest.fn();
111
+ m.getLayer = () => undefined;
110
112
  m.setBackground("streets");
111
113
  expect(m.setLayoutProperty.mock.calls.length).toBe(0);
112
114
  });
@@ -115,6 +117,7 @@ describe("setBackground", () => {
115
117
  const p = createParent();
116
118
  const c = document.createElement("div");
117
119
  const m = new Map(p, c);
120
+ m.getLayer = () => undefined;
118
121
  expect(() => m.setBackground("aerial")).toThrowError("No aerial imagery available");
119
122
  });
120
123
  });
@@ -138,9 +141,10 @@ describe("setVisibleUsers", () => {
138
141
  const c = document.createElement("div");
139
142
  const m = new Map(p, c);
140
143
  m._createPicturesTilesLayer = (url, id) => m._userLayers.add(id);
141
- m.setLayoutProperty = jest.fn();
144
+ m._addedLayers = [];
145
+ m.addLayer = jest.fn();
142
146
  await m.setVisibleUsers(["blabla"]);
143
- expect(m.setLayoutProperty.mock.calls).toMatchSnapshot();
147
+ expect(m.addLayer.mock.calls).toMatchSnapshot();
144
148
  expect(p.dispatchEvent.mock.calls).toMatchSnapshot();
145
149
  });
146
150
 
@@ -372,47 +372,47 @@ describe("alterMapState", () => {
372
372
 
373
373
  afterEach(() => jest.clearAllMocks());
374
374
 
375
- it("should jump to map position when map param is provided", () => {
375
+ it("should jump to map position when map param is provided", async () => {
376
376
  const params = { map: "10/20/30" };
377
377
  const mapOpts = { center: [30,20], zoom: 10, pitch: 0 };
378
378
 
379
- alterMapState(map, params);
379
+ await alterMapState(map, params);
380
380
  expect(map.jumpTo).toHaveBeenCalledWith(mapOpts);
381
381
  });
382
382
 
383
- it("should set visible users when users param is provided", () => {
383
+ it("should set visible users when users param is provided", async () => {
384
384
  const params = { users: "user1,user2" };
385
- alterMapState(map, params);
385
+ await alterMapState(map, params);
386
386
  expect(map.setVisibleUsers).toHaveBeenCalledWith(["user1", "user2"]);
387
387
  });
388
388
 
389
- it("should set default visible user when users param is empty", () => {
389
+ it("should set default visible user when users param is empty", async () => {
390
390
  const params = { users: "" };
391
- alterMapState(map, params);
391
+ await alterMapState(map, params);
392
392
  expect(map.setVisibleUsers).toHaveBeenCalledWith(["geovisio"]);
393
393
  });
394
394
 
395
- it("should set map filters when params are provided", () => {
395
+ it("should set map filters when params are provided", async () => {
396
396
  const params = { "date_from": "2024-01-01", "pic_score": "ABC" };
397
397
  const filters = { "minDate": "2024-01-01", "qualityscore": [5,4,3] };
398
-
399
- alterMapState(map, params);
398
+ await alterMapState(map, params);
400
399
  expect(map.setFilters).toHaveBeenCalledWith(filters);
401
400
  });
402
401
 
403
- it("should set map background when background param is valid", () => {
402
+ it("should set map background when background param is valid", async () => {
404
403
  const params = { background: "aerial" };
405
- alterMapState(map, params);
404
+ map.setVisibleUsers = () => Promise.resolve();
405
+ await alterMapState(map, params);
406
406
  expect(map.setBackground).toHaveBeenCalledWith("aerial");
407
407
  });
408
408
 
409
- it("should not set map background when background param is invalid", () => {
409
+ it("should not set map background when background param is invalid", async () => {
410
410
  const params = { background: "invalid" };
411
- alterMapState(map, params);
411
+ await alterMapState(map, params);
412
412
  expect(map.setBackground).not.toHaveBeenCalled();
413
413
  });
414
414
 
415
- it("should handle multiple params correctly", () => {
415
+ it("should handle multiple params correctly", async () => {
416
416
  const params = {
417
417
  map: "15/7/6",
418
418
  users: "user1,user2",
@@ -422,7 +422,7 @@ describe("alterMapState", () => {
422
422
  const mapOpts = { center: [6, 7], zoom: 15, pitch: 0 };
423
423
  const filters = { camera: "value1" };
424
424
 
425
- alterMapState(map, params);
425
+ await alterMapState(map, params);
426
426
  expect(map.jumpTo).toHaveBeenCalledWith(mapOpts);
427
427
  expect(map.setVisibleUsers).toHaveBeenCalledWith(["user1", "user2"]);
428
428
  expect(map.setFilters).toHaveBeenCalledWith(filters);
@@ -2,7 +2,6 @@
2
2
 
3
3
  exports[`forwardGeocodingBAN works 1`] = `
4
4
  Object {
5
- "attribution": "BAN",
6
5
  "features": Array [
7
6
  Object {
8
7
  "center": Object {
@@ -16,11 +15,6 @@ Object {
16
15
  "zoom": 20,
17
16
  },
18
17
  ],
19
- "licence": "ODbL 1.0",
20
- "limit": 1,
21
- "query": "8 bd du port",
22
- "type": "FeatureCollection",
23
- "version": "draft",
24
18
  }
25
19
  `;
26
20
 
@@ -30,7 +30,7 @@ describe("forwardGeocodingBAN", () => {
30
30
  const cfg = { query: "bla", limit: 5, proximity: "17.7,-45.2" };
31
31
 
32
32
  return geocoder.forwardGeocodingBAN(cfg).then(res => {
33
- expect(global.fetch.mock.calls).toEqual([["https://api-adresse.data.gouv.fr/search/?q=bla&limit=5&lat=17.7&lon=-45.2"]]);
33
+ expect(global.fetch.mock.calls).toEqual([["https://data.geopf.fr/geocodage/search/?q=bla&limit=5&lat=17.7&lon=-45.2"]]);
34
34
  expect(res).toMatchSnapshot();
35
35
  });
36
36
  });
@@ -8,18 +8,18 @@ jest.mock("../../src/utils/map", () => ({
8
8
 
9
9
  describe("getGrade", () => {
10
10
  it("works with null-like", () => {
11
- expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, null)).toBeNull();
12
- expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, undefined)).toBeNull();
13
- expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, "")).toBeNull();
14
- });
11
+ expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, null)).toBeNull();
12
+ expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, undefined)).toBeNull();
13
+ expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, "")).toBeNull();
14
+ });
15
15
 
16
16
  it("works with grade values", () => {
17
- expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, 0)).toBe(1);
18
- expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, 5)).toBe(1);
19
- expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, 12)).toBe(2);
20
- expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, 25)).toBe(3);
21
- expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, 40)).toBe(4);
22
- });
17
+ expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, 0)).toBe(1);
18
+ expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, 5)).toBe(1);
19
+ expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, 12)).toBe(2);
20
+ expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, 25)).toBe(3);
21
+ expect(utils.getGrade(utils.QUALITYSCORE_RES_FLAT_VALUES, 40)).toBe(4);
22
+ });
23
23
  });
24
24
 
25
25
  describe("getDistance", () => {
@@ -32,25 +32,25 @@ describe("getDistance", () => {
32
32
  });
33
33
 
34
34
  describe("svgToPSVLink", () => {
35
- it("works", () => {
36
- const base64Svg = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxjaXJjbGUgY3g9JzUnIGN5PSc1JyByPSc1JyBmaWxsPScjMDAwJy8+PC9zdmc+";
37
- const fillColor = "red";
38
- const result = utils.svgToPSVLink(base64Svg, fillColor);
39
-
40
- expect(result).toBeInstanceOf(HTMLButtonElement);
41
- expect(result.classList.contains("pnx-psv-tour-arrows")).toBe(true);
42
- expect(result.style.color).toBe(fillColor);
43
- expect(result.querySelector("svg")).not.toBeNull();
44
- });
45
-
46
- it("works with invalid input", () => {
47
- const invalidBase64Svg = "http://test.net/invalid_string";
48
- const result = utils.svgToPSVLink(invalidBase64Svg, "blue");
49
-
50
- expect(result).toBeInstanceOf(HTMLImageElement);
51
- expect(result.src).toBe(invalidBase64Svg);
52
- expect(result.alt).toBe("");
53
- });
35
+ it("works", () => {
36
+ const base64Svg = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxjaXJjbGUgY3g9JzUnIGN5PSc1JyByPSc1JyBmaWxsPScjMDAwJy8+PC9zdmc+";
37
+ const fillColor = "red";
38
+ const result = utils.svgToPSVLink(base64Svg, fillColor);
39
+
40
+ expect(result).toBeInstanceOf(HTMLButtonElement);
41
+ expect(result.classList.contains("pnx-psv-tour-arrows")).toBe(true);
42
+ expect(result.style.color).toBe(fillColor);
43
+ expect(result.querySelector("svg")).not.toBeNull();
44
+ });
45
+
46
+ it("works with invalid input", () => {
47
+ const invalidBase64Svg = "http://test.net/invalid_string";
48
+ const result = utils.svgToPSVLink(invalidBase64Svg, "blue");
49
+
50
+ expect(result).toBeInstanceOf(HTMLImageElement);
51
+ expect(result.src).toBe(invalidBase64Svg);
52
+ expect(result.alt).toBe("");
53
+ });
54
54
  });
55
55
 
56
56
  describe("getAzimuth", () => {
@@ -197,92 +197,119 @@ describe("xyzToPosition", () => {
197
197
  });
198
198
  });
199
199
 
200
+ describe("degToDms", () => {
201
+ it("converts positive decimal degrees to DMS correctly", () => {
202
+ const result = utils.degToDms(45.7896541);
203
+ expect(result).toEqual({ d: 45, m: 47, s: 22.755 });
204
+ });
205
+
206
+ it("converts negative decimal degrees to DMS correctly 1", () => {
207
+ const result = utils.degToDms(-12.751234);
208
+ expect(result).toEqual({ d: -12, m: 45, s: 4.442 });
209
+ });
210
+
211
+ it("converts negative decimal degrees to DMS correctly 2", () => {
212
+ const result = utils.degToDms(-21.007598);
213
+ expect(result).toEqual({ d: -21, m: 0, s: 27.353 });
214
+ });
215
+
216
+ it("converts zero degrees to DMS correctly", () => {
217
+ const result = utils.degToDms(0);
218
+ expect(result).toEqual({ d: 0, m: 0, s: 0 });
219
+ });
220
+
221
+ it("handles integer degrees correctly", () => {
222
+ const result = utils.degToDms(90);
223
+ expect(result).toEqual({ d: 90, m: 0, s: 0 });
224
+ });
225
+ });
226
+
200
227
  describe("josmBboxParameters", () => {
201
228
  it("works with null-like", () => {
202
- expect(utils.josmBboxParameters(null)).toBeNull();
203
- expect(utils.josmBboxParameters(undefined)).toBeNull();
204
- });
205
-
206
- it("works without azimuth", () => {
207
- const meta = { gps: [2.3522, 48.8566] };
208
- const result = utils.josmBboxParameters(meta);
209
- expect(result).toBe("left=2.3522&right=2.3522&top=48.8566&bottom=48.8566&changeset_source=Panoramax");
210
- });
211
-
212
- it("works with azimuth = 0", () => {
213
- const meta = { gps: [2.3522, 48.8566], properties: { "view:azimuth": 0 } };
214
- const result = utils.josmBboxParameters(meta);
215
- expect(result).toBe("left=2.3522&right=2.3524&top=48.8568&bottom=48.8566&changeset_source=Panoramax");
216
- });
217
-
218
- it("works with azimuth = 180", () => {
219
- const meta = { gps: [2.3522, 48.8566], properties: { "view:azimuth": 180 } };
220
- const result = utils.josmBboxParameters(meta);
221
- expect(result).toBe("left=2.352&right=2.3524&top=48.8566&bottom=48.8564&changeset_source=Panoramax");
222
- });
223
-
224
- it("works with azimuth = 90", () => {
225
- const meta = { gps: [2.3522, 48.8566], properties: { "view:azimuth": 90 } };
226
- const result = utils.josmBboxParameters(meta);
227
- expect(result).toBe("left=2.3522&right=2.3524&top=48.8568&bottom=48.8564&changeset_source=Panoramax");
228
- });
229
-
230
- it("works with azimuth = 270", () => {
231
- const meta = { gps: [2.3522, 48.8566], properties: { "view:azimuth": 270 } };
232
- const result = utils.josmBboxParameters(meta);
233
- expect(result).toBe("left=2.352&right=2.3522&top=48.8568&bottom=48.8564&changeset_source=Panoramax");
234
- });
229
+ expect(utils.josmBboxParameters(null)).toBeNull();
230
+ expect(utils.josmBboxParameters(undefined)).toBeNull();
231
+ });
232
+
233
+ it("works without azimuth", () => {
234
+ const meta = { gps: [2.3522, 48.8566] };
235
+ const result = utils.josmBboxParameters(meta);
236
+ expect(result).toBe("left=2.3522&right=2.3522&top=48.8566&bottom=48.8566&changeset_source=Panoramax");
237
+ });
238
+
239
+ it("works with azimuth = 0", () => {
240
+ const meta = { gps: [2.3522, 48.8566], properties: { "view:azimuth": 0 } };
241
+ const result = utils.josmBboxParameters(meta);
242
+ expect(result).toBe("left=2.3522&right=2.3524&top=48.8568&bottom=48.8566&changeset_source=Panoramax");
243
+ });
244
+
245
+ it("works with azimuth = 180", () => {
246
+ const meta = { gps: [2.3522, 48.8566], properties: { "view:azimuth": 180 } };
247
+ const result = utils.josmBboxParameters(meta);
248
+ expect(result).toBe("left=2.352&right=2.3524&top=48.8566&bottom=48.8564&changeset_source=Panoramax");
249
+ });
250
+
251
+ it("works with azimuth = 90", () => {
252
+ const meta = { gps: [2.3522, 48.8566], properties: { "view:azimuth": 90 } };
253
+ const result = utils.josmBboxParameters(meta);
254
+ expect(result).toBe("left=2.3522&right=2.3524&top=48.8568&bottom=48.8564&changeset_source=Panoramax");
255
+ });
256
+
257
+ it("works with azimuth = 270", () => {
258
+ const meta = { gps: [2.3522, 48.8566], properties: { "view:azimuth": 270 } };
259
+ const result = utils.josmBboxParameters(meta);
260
+ expect(result).toBe("left=2.352&right=2.3522&top=48.8568&bottom=48.8564&changeset_source=Panoramax");
261
+ });
235
262
  });
236
263
 
237
264
  describe("getCookie", () => {
238
- it("should return the value of the specified cookie", () => {
239
- jest.spyOn(document, "cookie", "get").mockReturnValueOnce("session=abc123");
240
- expect(utils.getCookie("session")).toBe("abc123");
241
- });
242
-
243
- it("should return null if the cookie is not found", () => {
244
- jest.spyOn(document, "cookie", "get").mockReturnValueOnce("session=abc123");
245
- expect(utils.getCookie("user_id")).toBeUndefined();
246
- });
247
-
248
- it("should return the correct value when multiple cookies are set", () => {
249
- jest.spyOn(document, "cookie", "get").mockReturnValueOnce("session=abc123; user_id=789; user_name=John");
250
- expect(utils.getCookie("user_id")).toBe("789");
251
- });
252
-
253
- it("should return null if cookie with the specified name has no value", () => {
254
- jest.spyOn(document, "cookie", "get").mockReturnValueOnce("session=; user_id=789");
255
- expect(utils.getCookie("session")).toBe("");
256
- });
257
-
258
- it("should return the correct value when the cookie contains =", () => {
259
- jest.spyOn(document, "cookie", "get").mockReturnValueOnce("custom_cookie=abc=123");
260
- expect(utils.getCookie("custom_cookie")).toBe("abc=123");
261
- });
265
+ it("should return the value of the specified cookie", () => {
266
+ jest.spyOn(document, "cookie", "get").mockReturnValueOnce("session=abc123");
267
+ expect(utils.getCookie("session")).toBe("abc123");
268
+ });
269
+
270
+ it("should return null if the cookie is not found", () => {
271
+ jest.spyOn(document, "cookie", "get").mockReturnValueOnce("session=abc123");
272
+ expect(utils.getCookie("user_id")).toBeUndefined();
273
+ });
274
+
275
+ it("should return the correct value when multiple cookies are set", () => {
276
+ jest.spyOn(document, "cookie", "get").mockReturnValueOnce("session=abc123; user_id=789; user_name=John");
277
+ expect(utils.getCookie("user_id")).toBe("789");
278
+ });
279
+
280
+ it("should return null if cookie with the specified name has no value", () => {
281
+ jest.spyOn(document, "cookie", "get").mockReturnValueOnce("session=; user_id=789");
282
+ expect(utils.getCookie("session")).toBe("");
283
+ });
284
+
285
+ it("should return the correct value when the cookie contains =", () => {
286
+ jest.spyOn(document, "cookie", "get").mockReturnValueOnce("custom_cookie=abc=123");
287
+ expect(utils.getCookie("custom_cookie")).toBe("abc=123");
288
+ });
262
289
  });
263
290
 
264
291
  describe("getUserAccount", () => {
265
- it("should return an object with user id and name when all cookies are present", () => {
266
- jest.spyOn(document, "cookie", "get").mockReturnValue("session=abc123; user_id=789; user_name=John");
267
- expect(utils.getUserAccount()).toEqual({ id: "789", name: "John" });
268
- });
269
-
270
- it("should return null if session cookie is missing", () => {
271
- jest.spyOn(document, "cookie", "get").mockReturnValue("user_id=789; user_name=John");
272
- expect(utils.getUserAccount()).toBeNull();
273
- });
274
-
275
- it("should return null if user_id cookie is missing", () => {
276
- jest.spyOn(document, "cookie", "get").mockReturnValue("session=abc123; user_name=John");
277
- expect(utils.getUserAccount()).toBeNull();
278
- });
279
-
280
- it("should return null if user_name cookie is missing", () => {
281
- jest.spyOn(document, "cookie", "get").mockReturnValue("session=abc123; user_id=789");
282
- expect(utils.getUserAccount()).toBeNull();
283
- });
284
-
285
- it("should return null if all cookies are missing", () => {
286
- expect(utils.getUserAccount()).toBeNull();
287
- });
292
+ it("should return an object with user id and name when all cookies are present", () => {
293
+ jest.spyOn(document, "cookie", "get").mockReturnValue("session=abc123; user_id=789; user_name=John");
294
+ expect(utils.getUserAccount()).toEqual({ id: "789", name: "John" });
295
+ });
296
+
297
+ it("should return null if session cookie is missing", () => {
298
+ jest.spyOn(document, "cookie", "get").mockReturnValue("user_id=789; user_name=John");
299
+ expect(utils.getUserAccount()).toBeNull();
300
+ });
301
+
302
+ it("should return null if user_id cookie is missing", () => {
303
+ jest.spyOn(document, "cookie", "get").mockReturnValue("session=abc123; user_name=John");
304
+ expect(utils.getUserAccount()).toBeNull();
305
+ });
306
+
307
+ it("should return null if user_name cookie is missing", () => {
308
+ jest.spyOn(document, "cookie", "get").mockReturnValue("session=abc123; user_id=789");
309
+ expect(utils.getUserAccount()).toBeNull();
310
+ });
311
+
312
+ it("should return null if all cookies are missing", () => {
313
+ expect(utils.getUserAccount()).toBeNull();
314
+ });
288
315
  });