umap-project 2.1.2__py3-none-any.whl → 2.2.0__py3-none-any.whl

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.

Potentially problematic release.


This version of umap-project might be problematic. Click here for more details.

Files changed (211) hide show
  1. umap/__init__.py +1 -1
  2. umap/context_processors.py +1 -0
  3. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/en/LC_MESSAGES/django.po +32 -32
  5. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/ms/LC_MESSAGES/django.mo +0 -0
  8. umap/migrations/0020_alter_tilelayer_url_template.py +19 -0
  9. umap/migrations/0021_remove_map_description.py +16 -0
  10. umap/models.py +10 -6
  11. umap/settings/base.py +1 -0
  12. umap/static/umap/base.css +43 -156
  13. umap/static/umap/content.css +7 -25
  14. umap/static/umap/css/icon.css +112 -0
  15. umap/static/umap/css/panel.css +140 -0
  16. umap/static/umap/img/16-white.svg +5 -1
  17. umap/static/umap/img/16.svg +7 -4
  18. umap/static/umap/img/24-white.svg +3 -1
  19. umap/static/umap/img/24.svg +3 -4
  20. umap/static/umap/img/source/16-white.svg +176 -940
  21. umap/static/umap/img/source/16.svg +8 -5
  22. umap/static/umap/img/source/24-white.svg +5 -3
  23. umap/static/umap/img/source/24.svg +6 -7
  24. umap/static/umap/js/modules/browser.js +97 -73
  25. umap/static/umap/js/modules/dompurify.js +12 -0
  26. umap/static/umap/js/modules/facets.js +149 -0
  27. umap/static/umap/js/modules/global.js +9 -1
  28. umap/static/umap/js/modules/i18n.js +7 -0
  29. umap/static/umap/js/modules/orderable.js +84 -0
  30. umap/static/umap/js/modules/panel.js +76 -0
  31. umap/static/umap/js/modules/request.js +0 -1
  32. umap/static/umap/js/modules/schema.js +324 -223
  33. umap/static/umap/js/modules/urls.js +1 -16
  34. umap/static/umap/js/modules/utils.js +340 -0
  35. umap/static/umap/js/umap.autocomplete.js +40 -25
  36. umap/static/umap/js/umap.controls.js +248 -361
  37. umap/static/umap/js/umap.core.js +77 -366
  38. umap/static/umap/js/umap.datalayer.permissions.js +1 -1
  39. umap/static/umap/js/umap.features.js +65 -43
  40. umap/static/umap/js/umap.forms.js +128 -36
  41. umap/static/umap/js/umap.icon.js +11 -4
  42. umap/static/umap/js/umap.importer.js +78 -58
  43. umap/static/umap/js/umap.js +206 -192
  44. umap/static/umap/js/umap.layer.js +86 -46
  45. umap/static/umap/js/umap.permissions.js +13 -9
  46. umap/static/umap/js/umap.popup.js +26 -30
  47. umap/static/umap/js/umap.share.js +12 -9
  48. umap/static/umap/js/umap.tableeditor.js +4 -6
  49. umap/static/umap/js/umap.ui.js +10 -60
  50. umap/static/umap/locale/am_ET.js +243 -227
  51. umap/static/umap/locale/am_ET.json +21 -9
  52. umap/static/umap/locale/ar.js +243 -227
  53. umap/static/umap/locale/ar.json +21 -9
  54. umap/static/umap/locale/ast.js +243 -227
  55. umap/static/umap/locale/ast.json +21 -9
  56. umap/static/umap/locale/bg.js +243 -227
  57. umap/static/umap/locale/bg.json +21 -9
  58. umap/static/umap/locale/br.js +253 -237
  59. umap/static/umap/locale/br.json +25 -13
  60. umap/static/umap/locale/ca.js +243 -227
  61. umap/static/umap/locale/ca.json +21 -9
  62. umap/static/umap/locale/cs_CZ.js +243 -227
  63. umap/static/umap/locale/cs_CZ.json +21 -9
  64. umap/static/umap/locale/da.js +243 -227
  65. umap/static/umap/locale/da.json +21 -9
  66. umap/static/umap/locale/de.js +243 -227
  67. umap/static/umap/locale/de.json +21 -9
  68. umap/static/umap/locale/el.js +243 -227
  69. umap/static/umap/locale/el.json +21 -9
  70. umap/static/umap/locale/en.js +243 -234
  71. umap/static/umap/locale/en.json +22 -10
  72. umap/static/umap/locale/en_US.json +21 -9
  73. umap/static/umap/locale/es.js +243 -227
  74. umap/static/umap/locale/es.json +21 -9
  75. umap/static/umap/locale/et.js +243 -227
  76. umap/static/umap/locale/et.json +21 -9
  77. umap/static/umap/locale/eu.js +227 -199
  78. umap/static/umap/locale/eu.json +1 -1
  79. umap/static/umap/locale/fa_IR.js +243 -227
  80. umap/static/umap/locale/fa_IR.json +21 -9
  81. umap/static/umap/locale/fi.js +243 -227
  82. umap/static/umap/locale/fi.json +21 -9
  83. umap/static/umap/locale/fr.js +243 -234
  84. umap/static/umap/locale/fr.json +21 -9
  85. umap/static/umap/locale/gl.js +243 -227
  86. umap/static/umap/locale/gl.json +21 -9
  87. umap/static/umap/locale/he.js +243 -227
  88. umap/static/umap/locale/he.json +21 -9
  89. umap/static/umap/locale/hr.js +243 -227
  90. umap/static/umap/locale/hr.json +21 -9
  91. umap/static/umap/locale/hu.js +243 -234
  92. umap/static/umap/locale/hu.json +21 -9
  93. umap/static/umap/locale/id.js +243 -227
  94. umap/static/umap/locale/id.json +21 -9
  95. umap/static/umap/locale/is.js +243 -227
  96. umap/static/umap/locale/is.json +21 -9
  97. umap/static/umap/locale/it.js +243 -234
  98. umap/static/umap/locale/it.json +21 -9
  99. umap/static/umap/locale/ja.js +243 -227
  100. umap/static/umap/locale/ja.json +21 -9
  101. umap/static/umap/locale/ko.js +243 -227
  102. umap/static/umap/locale/ko.json +21 -9
  103. umap/static/umap/locale/lt.js +243 -227
  104. umap/static/umap/locale/lt.json +21 -9
  105. umap/static/umap/locale/ms.js +243 -234
  106. umap/static/umap/locale/ms.json +22 -10
  107. umap/static/umap/locale/nl.js +246 -230
  108. umap/static/umap/locale/nl.json +21 -9
  109. umap/static/umap/locale/no.js +243 -227
  110. umap/static/umap/locale/no.json +21 -9
  111. umap/static/umap/locale/pl.js +243 -227
  112. umap/static/umap/locale/pl.json +21 -9
  113. umap/static/umap/locale/pl_PL.json +21 -9
  114. umap/static/umap/locale/pt.js +243 -227
  115. umap/static/umap/locale/pt.json +21 -9
  116. umap/static/umap/locale/pt_BR.js +243 -227
  117. umap/static/umap/locale/pt_BR.json +21 -9
  118. umap/static/umap/locale/pt_PT.js +243 -227
  119. umap/static/umap/locale/pt_PT.json +21 -9
  120. umap/static/umap/locale/ro.js +243 -227
  121. umap/static/umap/locale/ro.json +21 -9
  122. umap/static/umap/locale/ru.js +243 -227
  123. umap/static/umap/locale/ru.json +21 -9
  124. umap/static/umap/locale/si.js +1 -1
  125. umap/static/umap/locale/si.json +1 -1
  126. umap/static/umap/locale/sk_SK.js +243 -227
  127. umap/static/umap/locale/sk_SK.json +21 -9
  128. umap/static/umap/locale/sl.js +243 -227
  129. umap/static/umap/locale/sl.json +21 -9
  130. umap/static/umap/locale/sr.js +243 -227
  131. umap/static/umap/locale/sr.json +21 -9
  132. umap/static/umap/locale/sv.js +243 -227
  133. umap/static/umap/locale/sv.json +21 -9
  134. umap/static/umap/locale/th_TH.js +243 -227
  135. umap/static/umap/locale/th_TH.json +21 -9
  136. umap/static/umap/locale/tr.js +243 -227
  137. umap/static/umap/locale/tr.json +21 -9
  138. umap/static/umap/locale/uk_UA.js +243 -227
  139. umap/static/umap/locale/uk_UA.json +21 -9
  140. umap/static/umap/locale/vi.js +243 -227
  141. umap/static/umap/locale/vi.json +21 -9
  142. umap/static/umap/locale/vi_VN.json +21 -9
  143. umap/static/umap/locale/zh.js +243 -227
  144. umap/static/umap/locale/zh.json +21 -9
  145. umap/static/umap/locale/zh_CN.json +21 -9
  146. umap/static/umap/locale/zh_TW.Big5.json +21 -9
  147. umap/static/umap/locale/zh_TW.js +243 -234
  148. umap/static/umap/locale/zh_TW.json +21 -9
  149. umap/static/umap/map.css +124 -264
  150. umap/static/umap/test/DataLayer.js +1 -1
  151. umap/static/umap/test/Feature.js +0 -226
  152. umap/static/umap/test/Map.js +0 -304
  153. umap/static/umap/test/Polygon.js +0 -256
  154. umap/static/umap/test/Polyline.js +0 -116
  155. umap/static/umap/test/TableEditor.js +10 -10
  156. umap/static/umap/test/Util.js +0 -521
  157. umap/static/umap/test/index.html +1 -5
  158. umap/static/umap/unittests/URLs.js +1 -1
  159. umap/static/umap/unittests/utils.js +610 -0
  160. umap/static/umap/vars.css +9 -0
  161. umap/static/umap/vendors/dompurify/purify.es.mjs +1525 -0
  162. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +1 -0
  163. umap/static/umap/vendors/iconlayers/iconLayers.js +1 -1
  164. umap/templates/umap/css.html +2 -0
  165. umap/templates/umap/js.html +0 -1
  166. umap/templates/umap/map_detail.html +4 -0
  167. umap/templates/umap/map_table.html +12 -10
  168. umap/templatetags/umap_tags.py +5 -0
  169. umap/tests/conftest.py +9 -0
  170. umap/tests/fixtures/test_upload_data.csv +2 -1
  171. umap/tests/fixtures/test_upload_data.umap +171 -0
  172. umap/tests/fixtures/test_upload_data_osm.json +33 -0
  173. umap/tests/integration/conftest.py +16 -0
  174. umap/tests/integration/test_anonymous_owned_map.py +30 -5
  175. umap/tests/integration/test_basics.py +21 -0
  176. umap/tests/integration/test_browser.py +16 -36
  177. umap/tests/integration/test_choropleth.py +89 -0
  178. umap/tests/integration/test_collaborative_editing.py +30 -1
  179. umap/tests/integration/test_dashboard.py +10 -0
  180. umap/tests/integration/test_datalayer.py +132 -0
  181. umap/tests/integration/test_draw_polygon.py +363 -0
  182. umap/tests/integration/test_draw_polyline.py +325 -0
  183. umap/tests/integration/test_edit_datalayer.py +145 -6
  184. umap/tests/integration/test_edit_map.py +202 -0
  185. umap/tests/integration/test_edit_marker.py +120 -0
  186. umap/tests/integration/test_edit_polygon.py +122 -0
  187. umap/tests/integration/test_facets_browser.py +132 -11
  188. umap/tests/integration/test_import.py +407 -10
  189. umap/tests/integration/test_map.py +36 -54
  190. umap/tests/integration/test_map_list.py +28 -0
  191. umap/tests/integration/test_owned_map.py +24 -6
  192. umap/tests/integration/test_picto.py +25 -38
  193. umap/tests/integration/test_querystring.py +9 -15
  194. umap/tests/integration/test_slideshow.py +0 -5
  195. umap/tests/integration/test_statics.py +3 -2
  196. umap/tests/integration/test_tableeditor.py +23 -0
  197. umap/tests/integration/test_tilelayer.py +10 -0
  198. umap/tests/integration/test_view_marker.py +64 -0
  199. umap/tests/integration/test_view_polygon.py +59 -0
  200. umap/tests/integration/test_view_polyline.py +51 -0
  201. umap/tests/test_map_views.py +13 -0
  202. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/METADATA +12 -12
  203. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/RECORD +206 -187
  204. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/WHEEL +1 -1
  205. umap/static/umap/test/Choropleth.js +0 -245
  206. umap/static/umap/test/Permissions.js +0 -74
  207. umap/static/umap/vendors/dompurify/purify.min.js +0 -3
  208. umap/static/umap/vendors/dompurify/purify.min.js.map +0 -1
  209. umap/tests/integration/test_drawing.py +0 -243
  210. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/entry_points.txt +0 -0
  211. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,89 @@
1
+ import json
2
+ from pathlib import Path
3
+
4
+ import pytest
5
+ from playwright.sync_api import expect
6
+
7
+ from ..base import DataLayerFactory
8
+
9
+ pytestmark = pytest.mark.django_db
10
+
11
+
12
+ def test_basic_choropleth_map_with_default_color(map, live_server, page):
13
+ path = Path(__file__).parent.parent / "fixtures/choropleth_region_chomage.geojson"
14
+ data = json.loads(path.read_text())
15
+ DataLayerFactory(data=data, map=map)
16
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
17
+ # Hauts-de-France
18
+ expect(page.locator("path[fill='#08519c']")).to_have_count(1)
19
+ # Occitanie
20
+ expect(page.locator("path[fill='#3182bd']")).to_have_count(1)
21
+ # Grand-Est, PACA
22
+ expect(page.locator("path[fill='#6baed6']")).to_have_count(2)
23
+ # Bourgogne-Franche-Comté, Centre-Val-de-Loire, IdF, Normandie, Corse, Nouvelle-Aquitaine
24
+ expect(page.locator("path[fill='#bdd7e7']")).to_have_count(6)
25
+ # Bretagne, Pays de la Loire, AURA
26
+ expect(page.locator("path[fill='#eff3ff']")).to_have_count(3)
27
+
28
+
29
+ def test_basic_choropleth_map_with_custom_brewer(openmap, live_server, page):
30
+ path = Path(__file__).parent.parent / "fixtures/choropleth_region_chomage.geojson"
31
+ data = json.loads(path.read_text())
32
+
33
+ # Change brewer at load
34
+ data["_umap_options"]["choropleth"]["brewer"] = "Reds"
35
+ DataLayerFactory(data=data, map=openmap)
36
+
37
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}")
38
+ # Hauts-de-France
39
+ expect(page.locator("path[fill='#a50f15']")).to_have_count(1)
40
+ # Occitanie
41
+ expect(page.locator("path[fill='#de2d26']")).to_have_count(1)
42
+ # Grand-Est, PACA
43
+ expect(page.locator("path[fill='#fb6a4a']")).to_have_count(2)
44
+ # Bourgogne-Franche-Comté, Centre-Val-de-Loire, IdF, Normandie, Corse, Nouvelle-Aquitaine
45
+ expect(page.locator("path[fill='#fcae91']")).to_have_count(6)
46
+ # Bretagne, Pays de la Loire, AURA
47
+ expect(page.locator("path[fill='#fee5d9']")).to_have_count(3)
48
+
49
+ # Now change brewer from UI
50
+ page.get_by_role("button", name="Edit").click()
51
+ page.get_by_role("link", name="Manage layers").click()
52
+ page.locator(".panel").get_by_title("Edit", exact=True).click()
53
+ page.get_by_role("heading", name="Choropleth: settings").click()
54
+ page.locator('select[name="brewer"]').select_option("Greens")
55
+
56
+ # Hauts-de-France
57
+ expect(page.locator("path[fill='#006d2c']")).to_have_count(1)
58
+ # Occitanie
59
+ expect(page.locator("path[fill='#31a354']")).to_have_count(1)
60
+ # Grand-Est, PACA
61
+ expect(page.locator("path[fill='#74c476']")).to_have_count(2)
62
+ # Bourgogne-Franche-Comté, Centre-Val-de-Loire, IdF, Normandie, Corse, Nouvelle-Aquitaine
63
+ expect(page.locator("path[fill='#bae4b3']")).to_have_count(6)
64
+ # Bretagne, Pays de la Loire, AURA
65
+ expect(page.locator("path[fill='#edf8e9']")).to_have_count(3)
66
+
67
+
68
+ def test_basic_choropleth_map_with_custom_classes(openmap, live_server, page):
69
+ path = Path(__file__).parent.parent / "fixtures/choropleth_region_chomage.geojson"
70
+ data = json.loads(path.read_text())
71
+
72
+ # Change brewer at load
73
+ data["_umap_options"]["choropleth"]["classes"] = 6
74
+ DataLayerFactory(data=data, map=openmap)
75
+
76
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}")
77
+
78
+ # Hauts-de-France
79
+ expect(page.locator("path[fill='#08519c']")).to_have_count(1)
80
+ # Occitanie
81
+ expect(page.locator("path[fill='#3182bd']")).to_have_count(1)
82
+ # PACA
83
+ expect(page.locator("path[fill='#6baed6']")).to_have_count(1)
84
+ # Grand-Est
85
+ expect(page.locator("path[fill='#9ecae1']")).to_have_count(1)
86
+ # Bourgogne-Franche-Comté, Centre-Val-de-Loire, IdF, Normandie, Corse, Nouvelle-Aquitaine
87
+ expect(page.locator("path[fill='#c6dbef']")).to_have_count(6)
88
+ # Bretagne, Pays de la Loire, AURA
89
+ expect(page.locator("path[fill='#eff3ff']")).to_have_count(3)
@@ -1,9 +1,11 @@
1
+ import json
1
2
  import re
3
+ from pathlib import Path
2
4
  from time import sleep
3
5
 
4
6
  from playwright.sync_api import expect
5
7
 
6
- from umap.models import DataLayer
8
+ from umap.models import DataLayer, Map
7
9
 
8
10
  from ..base import DataLayerFactory, MapFactory
9
11
 
@@ -265,3 +267,30 @@ def test_same_second_edit_doesnt_conflict(context, live_server, tilelayer):
265
267
  "id": str(datalayer.pk),
266
268
  "permissions": {"edit_status": 1},
267
269
  }
270
+
271
+
272
+ def test_should_display_alert_on_conflict(context, live_server, datalayer, openmap):
273
+ # Open the map on two pages.
274
+ page_one = context.new_page()
275
+ page_one.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
276
+ page_two = context.new_page()
277
+ page_two.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
278
+
279
+ page_one.locator(".leaflet-marker-icon").click(modifiers=["Shift"])
280
+ page_one.locator('input[name="name"]').fill("new name")
281
+ with page_one.expect_response(re.compile(r".*/datalayer/update/.*")):
282
+ page_one.get_by_role("button", name="Save").click()
283
+
284
+ page_two.locator(".leaflet-marker-icon").click(modifiers=["Shift"])
285
+ page_two.locator('input[name="name"]').fill("custom name")
286
+ with page_two.expect_response(re.compile(r".*/datalayer/update/.*")):
287
+ page_two.get_by_role("button", name="Save").click()
288
+ saved = DataLayer.objects.last()
289
+ data = json.loads(Path(saved.geojson.path).read_text())
290
+ assert data["features"][0]["properties"]["name"] == "new name"
291
+ expect(page_two.get_by_text("Woops! Someone else seems to")).to_be_visible()
292
+ with page_two.expect_response(re.compile(r".*/datalayer/update/.*")):
293
+ page_two.get_by_role("button", name="Save anyway").click()
294
+ saved = DataLayer.objects.last()
295
+ data = json.loads(Path(saved.geojson.path).read_text())
296
+ assert data["features"][0]["properties"]["name"] == "custom name"
@@ -36,3 +36,13 @@ def test_dashboard_map_preview(map, live_server, datalayer, login):
36
36
  expect(dialog).to_be_visible()
37
37
  # Let's check we have a marker on it, so we can guess the map loaded correctly
38
38
  expect(dialog.locator(".leaflet-marker-icon")).to_be_visible()
39
+
40
+
41
+ def test_no_delete_button_for_editors(map, live_server, datalayer, login, user):
42
+ map.name = "Map I cannot delete"
43
+ map.editors.add(user)
44
+ map.save()
45
+ page = login(user)
46
+ page.goto(f"{live_server.url}/en/me")
47
+ expect(page.get_by_text("Map I cannot delete")).to_be_visible()
48
+ expect(page.get_by_title("Delete")).to_be_hidden()
@@ -0,0 +1,132 @@
1
+ import json
2
+
3
+ import pytest
4
+ from django.core.files.base import ContentFile
5
+ from playwright.sync_api import expect
6
+
7
+ from ..base import DataLayerFactory
8
+
9
+ pytestmark = pytest.mark.django_db
10
+
11
+
12
+ def set_options(datalayer, **options):
13
+ # For now we need to change both the DB and the FS…
14
+ datalayer.settings.update(options)
15
+ data = json.load(datalayer.geojson.file)
16
+ data["_umap_options"].update(**options)
17
+ datalayer.geojson = ContentFile(json.dumps(data), "foo.json")
18
+ datalayer.save()
19
+
20
+
21
+ def test_honour_displayOnLoad_false(map, live_server, datalayer, page):
22
+ set_options(datalayer, displayOnLoad=False)
23
+ page.goto(f"{live_server.url}{map.get_absolute_url()}?onLoadPanel=datalayers")
24
+ expect(page.locator(".leaflet-marker-icon")).to_be_hidden()
25
+ layers = page.locator(".umap-browser .datalayer")
26
+ markers = page.locator(".leaflet-marker-icon")
27
+ layers_off = page.locator(".umap-browser .datalayer.off")
28
+ expect(layers).to_have_count(1)
29
+ expect(layers_off).to_have_count(1)
30
+ page.get_by_role("button", name="See layers").click()
31
+ page.get_by_label("Zoom in").click()
32
+ expect(markers).to_be_hidden()
33
+ page.get_by_title("Show/hide layer").click()
34
+ expect(layers_off).to_have_count(0)
35
+ expect(markers).to_be_visible()
36
+
37
+
38
+ def test_should_honour_fromZoom(live_server, map, datalayer, page):
39
+ set_options(datalayer, displayOnLoad=True, fromZoom=6)
40
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#5/48.55/14.68")
41
+ markers = page.locator(".leaflet-marker-icon")
42
+ expect(markers).to_be_hidden()
43
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.55/14.68")
44
+ markers = page.locator(".leaflet-marker-icon")
45
+ expect(markers).to_be_visible()
46
+ page.get_by_label("Zoom out").click()
47
+ expect(markers).to_be_hidden()
48
+ page.get_by_label("Zoom in").click()
49
+ expect(markers).to_be_visible()
50
+ page.get_by_label("Zoom in").click()
51
+ expect(markers).to_be_visible()
52
+
53
+
54
+ def test_should_honour_toZoom(live_server, map, datalayer, page):
55
+ set_options(datalayer, displayOnLoad=True, toZoom=6)
56
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#7/48.55/14.68")
57
+ markers = page.locator(".leaflet-marker-icon")
58
+ expect(markers).to_be_hidden()
59
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.55/14.68")
60
+ markers = page.locator(".leaflet-marker-icon")
61
+ expect(markers).to_be_visible()
62
+ page.get_by_label("Zoom out").click()
63
+ expect(markers).to_be_visible()
64
+ page.get_by_label("Zoom in").click()
65
+ expect(markers).to_be_visible()
66
+ page.get_by_label("Zoom in").click()
67
+ # FIXME does not work (but works when using PWDEBUG=1), not sure why
68
+ # may be a race condition related to css transition
69
+ # expect(markers).to_be_hidden()
70
+
71
+
72
+ def test_should_honour_color_variable(live_server, map, page):
73
+ data = {
74
+ "type": "FeatureCollection",
75
+ "features": [
76
+ {
77
+ "type": "Feature",
78
+ "properties": {"mycolor": "aliceblue", "name": "Point 4"},
79
+ "geometry": {"type": "Point", "coordinates": [0.856934, 45.290347]},
80
+ },
81
+ {
82
+ "type": "Feature",
83
+ "properties": {"name": "a polygon", "mycolor": "tomato"},
84
+ "geometry": {
85
+ "type": "Polygon",
86
+ "coordinates": [
87
+ [
88
+ [2.12, 49.57],
89
+ [1.08, 49.02],
90
+ [2.51, 47.55],
91
+ [3.19, 48.77],
92
+ [2.12, 49.57],
93
+ ]
94
+ ],
95
+ },
96
+ },
97
+ ],
98
+ "_umap_options": {
99
+ "name": "Calque 2",
100
+ "color": "{mycolor}",
101
+ "fillColor": "{mycolor}",
102
+ },
103
+ }
104
+ DataLayerFactory(map=map, data=data)
105
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
106
+ expect(page.locator(".leaflet-overlay-pane path[fill='tomato']"))
107
+ markers = page.locator(".leaflet-marker-icon .icon_container")
108
+ expect(markers).to_have_css("background-color", "rgb(240, 248, 255)")
109
+
110
+
111
+ def test_datalayers_in_query_string(live_server, datalayer, map, page):
112
+ map.settings["properties"]["onLoadPanel"] = "datalayers"
113
+ map.save()
114
+ with_old_id = DataLayerFactory(old_id=134, map=map, name="with old id")
115
+ set_options(with_old_id, name="with old id")
116
+ visible = page.locator(".umap-browser .datalayer:not(.off) .datalayer-name")
117
+ hidden = page.locator(".umap-browser .datalayer.off .datalayer-name")
118
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
119
+ expect(visible).to_have_count(2)
120
+ expect(hidden).to_have_count(0)
121
+ page.goto(f"{live_server.url}{map.get_absolute_url()}?datalayers={datalayer.pk}")
122
+ expect(visible).to_have_count(1)
123
+ expect(visible).to_have_text(datalayer.name)
124
+ expect(hidden).to_have_count(1)
125
+ expect(hidden).to_have_text(with_old_id.name)
126
+ page.goto(
127
+ f"{live_server.url}{map.get_absolute_url()}?datalayers={with_old_id.old_id}"
128
+ )
129
+ expect(visible).to_have_count(1)
130
+ expect(visible).to_have_text(with_old_id.name)
131
+ expect(hidden).to_have_count(1)
132
+ expect(hidden).to_have_text(datalayer.name)
@@ -0,0 +1,363 @@
1
+ import json
2
+ import re
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+ from playwright.sync_api import expect
7
+
8
+ from umap.models import DataLayer
9
+
10
+ pytestmark = pytest.mark.django_db
11
+
12
+
13
+ def save_and_get_json(page):
14
+ with page.expect_response(re.compile(r".*/datalayer/create/.*")):
15
+ page.get_by_role("button", name="Save").click()
16
+ datalayer = DataLayer.objects.last()
17
+ return json.loads(Path(datalayer.geojson.path).read_text())
18
+
19
+
20
+ def test_draw_polygon(page, live_server, tilelayer):
21
+ page.goto(f"{live_server.url}/en/map/new/")
22
+
23
+ # Click on the Draw a polygon button on a new map.
24
+ create_line = page.locator(".leaflet-control-toolbar ").get_by_title(
25
+ "Draw a polygon"
26
+ )
27
+ create_line.click()
28
+
29
+ # Check no polygon is present by default.
30
+ # We target with the color, because there is also the drawing line guide (dash-array)
31
+ # around
32
+ lines = page.locator(".leaflet-overlay-pane path[stroke='DarkBlue']")
33
+ guide = page.locator(".leaflet-overlay-pane > svg > g > path")
34
+ expect(lines).to_have_count(0)
35
+ expect(guide).to_have_count(0)
36
+
37
+ # Click on the map, it will create a polygon.
38
+ map = page.locator("#map")
39
+ map.click(position={"x": 200, "y": 200})
40
+ expect(lines).to_have_count(1)
41
+ expect(guide).to_have_count(1)
42
+ map.click(position={"x": 100, "y": 200})
43
+ expect(lines).to_have_count(1)
44
+ expect(guide).to_have_count(2)
45
+ map.click(position={"x": 100, "y": 100})
46
+ expect(lines).to_have_count(1)
47
+ expect(guide).to_have_count(2)
48
+ # Click again to finish
49
+ map.click(position={"x": 100, "y": 100})
50
+ expect(lines).to_have_count(1)
51
+ expect(guide).to_have_count(0)
52
+
53
+
54
+ def test_clicking_esc_should_finish_polygon(page, live_server, tilelayer):
55
+ page.goto(f"{live_server.url}/en/map/new/")
56
+
57
+ # Click on the Draw a polygon button on a new map.
58
+ create_line = page.locator(".leaflet-control-toolbar ").get_by_title(
59
+ "Draw a polygon"
60
+ )
61
+ create_line.click()
62
+
63
+ # Check no polygon is present by default.
64
+ # We target with the color, because there is also the drawing line guide (dash-array)
65
+ # around
66
+ lines = page.locator(".leaflet-overlay-pane path[stroke='DarkBlue']")
67
+ guide = page.locator(".leaflet-overlay-pane > svg > g > path")
68
+ expect(lines).to_have_count(0)
69
+ expect(guide).to_have_count(0)
70
+
71
+ # Click on the map, it will create a polygon.
72
+ map = page.locator("#map")
73
+ map.click(position={"x": 200, "y": 200})
74
+ expect(lines).to_have_count(1)
75
+ expect(guide).to_have_count(1)
76
+ map.click(position={"x": 100, "y": 200})
77
+ expect(lines).to_have_count(1)
78
+ expect(guide).to_have_count(2)
79
+ map.click(position={"x": 100, "y": 100})
80
+ expect(lines).to_have_count(1)
81
+ expect(guide).to_have_count(2)
82
+ # Click ESC to finish
83
+ page.keyboard.press("Escape")
84
+ expect(lines).to_have_count(1)
85
+ expect(guide).to_have_count(0)
86
+
87
+
88
+ def test_clicking_esc_should_delete_polygon_if_empty(page, live_server, tilelayer):
89
+ page.goto(f"{live_server.url}/en/map/new/")
90
+
91
+ # Click on the Draw a polygon button on a new map.
92
+ create_line = page.locator(".leaflet-control-toolbar ").get_by_title(
93
+ "Draw a polygon"
94
+ )
95
+ create_line.click()
96
+
97
+ # Check no polygon is present by default.
98
+ # We target with the color, because there is also the drawing line guide (dash-array)
99
+ # around
100
+ lines = page.locator(".leaflet-overlay-pane path[stroke='DarkBlue']")
101
+ guide = page.locator(".leaflet-overlay-pane > svg > g > path")
102
+ expect(lines).to_have_count(0)
103
+ expect(guide).to_have_count(0)
104
+
105
+ # Click ESC to finish, no polygon should have been created
106
+ page.keyboard.press("Escape")
107
+ expect(lines).to_have_count(0)
108
+ expect(guide).to_have_count(0)
109
+
110
+
111
+ def test_clicking_esc_should_delete_polygon_if_invalid(page, live_server, tilelayer):
112
+ page.goto(f"{live_server.url}/en/map/new/")
113
+
114
+ # Click on the Draw a polygon button on a new map.
115
+ create_line = page.locator(".leaflet-control-toolbar ").get_by_title(
116
+ "Draw a polygon"
117
+ )
118
+ create_line.click()
119
+
120
+ # Check no polygon is present by default.
121
+ # We target with the color, because there is also the drawing line guide (dash-array)
122
+ # around
123
+ lines = page.locator(".leaflet-overlay-pane path[stroke='DarkBlue']")
124
+ guide = page.locator(".leaflet-overlay-pane > svg > g > path")
125
+ expect(lines).to_have_count(0)
126
+ expect(guide).to_have_count(0)
127
+
128
+ # Click on the map twice, it will start a polygon.
129
+ map = page.locator("#map")
130
+ map.click(position={"x": 200, "y": 200})
131
+ expect(lines).to_have_count(1)
132
+ expect(guide).to_have_count(1)
133
+ map.click(position={"x": 100, "y": 200})
134
+ expect(lines).to_have_count(1)
135
+ expect(guide).to_have_count(2)
136
+ # Click ESC to finish, the polygon is invalid, it should not be persisted
137
+ page.keyboard.press("Escape")
138
+ expect(lines).to_have_count(0)
139
+ expect(guide).to_have_count(0)
140
+
141
+
142
+ def test_can_draw_multi(live_server, page, tilelayer):
143
+ page.goto(f"{live_server.url}/en/map/new/")
144
+ polygons = page.locator(".leaflet-overlay-pane path")
145
+ expect(polygons).to_have_count(0)
146
+ multi_button = page.get_by_title("Add a polygon to the current multi")
147
+ expect(multi_button).to_be_hidden()
148
+ page.get_by_title("Draw a polygon").click()
149
+ map = page.locator("#map")
150
+ map.click(position={"x": 150, "y": 100})
151
+ map.click(position={"x": 150, "y": 150})
152
+ map.click(position={"x": 100, "y": 150})
153
+ map.click(position={"x": 100, "y": 100})
154
+ # Click again to finish
155
+ map.click(position={"x": 100, "y": 100})
156
+ expect(multi_button).to_be_visible()
157
+ expect(polygons).to_have_count(1)
158
+ multi_button.click()
159
+ map.click(position={"x": 250, "y": 200})
160
+ map.click(position={"x": 250, "y": 250})
161
+ map.click(position={"x": 200, "y": 250})
162
+ map.click(position={"x": 200, "y": 200})
163
+ # Click again to finish
164
+ map.click(position={"x": 200, "y": 200})
165
+ expect(polygons).to_have_count(1)
166
+ page.keyboard.press("Escape")
167
+ expect(multi_button).to_be_hidden()
168
+ polygons.first.click(button="right", position={"x": 10, "y": 10})
169
+ expect(page.get_by_role("link", name="Transform to lines")).to_be_hidden()
170
+ expect(page.get_by_role("link", name="Remove shape from the multi")).to_be_visible()
171
+
172
+
173
+ def test_can_draw_hole(page, live_server, tilelayer):
174
+ page.goto(f"{live_server.url}/en/map/new/")
175
+
176
+ page.get_by_title("Draw a polygon").click()
177
+
178
+ polygons = page.locator(".leaflet-overlay-pane path")
179
+ vertices = page.locator(".leaflet-vertex-icon")
180
+
181
+ # Click on the map, it will create a polygon.
182
+ map = page.locator("#map")
183
+ map.click(position={"x": 200, "y": 100})
184
+ map.click(position={"x": 200, "y": 200})
185
+ map.click(position={"x": 100, "y": 200})
186
+ map.click(position={"x": 100, "y": 100})
187
+ # Click again to finish
188
+ map.click(position={"x": 100, "y": 100})
189
+ expect(polygons).to_have_count(1)
190
+ expect(vertices).to_have_count(4)
191
+
192
+ # First vertex of the hole will be created here
193
+ map.click(position={"x": 180, "y": 120})
194
+ page.get_by_role("link", name="Start a hole here").click()
195
+ map.click(position={"x": 180, "y": 180})
196
+ map.click(position={"x": 120, "y": 180})
197
+ map.click(position={"x": 120, "y": 120})
198
+ # Click again to finish
199
+ map.click(position={"x": 120, "y": 120})
200
+ expect(polygons).to_have_count(1)
201
+ expect(vertices).to_have_count(8)
202
+ # Click on the polygon but not in the hole
203
+ polygons.first.click(button="right", position={"x": 10, "y": 10})
204
+ expect(page.get_by_role("link", name="Transform to lines")).to_be_hidden()
205
+
206
+
207
+ def test_can_transfer_shape_from_simple_polygon(live_server, page, tilelayer):
208
+ page.goto(f"{live_server.url}/en/map/new/")
209
+ polygons = page.locator(".leaflet-overlay-pane path")
210
+ expect(polygons).to_have_count(0)
211
+ page.get_by_title("Draw a polygon").click()
212
+ map = page.locator("#map")
213
+
214
+ # Draw a first polygon
215
+ map.click(position={"x": 150, "y": 100})
216
+ map.click(position={"x": 150, "y": 150})
217
+ map.click(position={"x": 100, "y": 150})
218
+ map.click(position={"x": 100, "y": 100})
219
+ # Click again to finish
220
+ map.click(position={"x": 100, "y": 100})
221
+ expect(polygons).to_have_count(1)
222
+
223
+ # Draw another polygon
224
+ page.get_by_title("Draw a polygon").click()
225
+ map.click(position={"x": 250, "y": 200})
226
+ map.click(position={"x": 250, "y": 250})
227
+ map.click(position={"x": 200, "y": 250})
228
+ map.click(position={"x": 200, "y": 200})
229
+ # Click again to finish
230
+ map.click(position={"x": 200, "y": 200})
231
+ expect(polygons).to_have_count(2)
232
+
233
+ # Now that polygon 2 is selected, right click on first one
234
+ # and transfer shape
235
+ polygons.first.click(position={"x": 20, "y": 20}, button="right")
236
+ page.get_by_role("link", name="Transfer shape to edited feature").click()
237
+ expect(polygons).to_have_count(1)
238
+
239
+
240
+ def test_can_extract_shape(live_server, page, tilelayer):
241
+ page.goto(f"{live_server.url}/en/map/new/")
242
+ polygons = page.locator(".leaflet-overlay-pane path")
243
+ expect(polygons).to_have_count(0)
244
+ page.get_by_title("Draw a polygon").click()
245
+ map = page.locator("#map")
246
+ map.click(position={"x": 150, "y": 100})
247
+ map.click(position={"x": 150, "y": 150})
248
+ map.click(position={"x": 100, "y": 150})
249
+ map.click(position={"x": 100, "y": 100})
250
+ # Click again to finish
251
+ map.click(position={"x": 100, "y": 100})
252
+ expect(polygons).to_have_count(1)
253
+ extract_button = page.get_by_role("link", name="Extract shape to separate feature")
254
+ expect(extract_button).to_be_hidden()
255
+ page.get_by_title("Add a polygon to the current multi").click()
256
+ map.click(position={"x": 250, "y": 200})
257
+ map.click(position={"x": 250, "y": 250})
258
+ map.click(position={"x": 200, "y": 250})
259
+ map.click(position={"x": 200, "y": 200})
260
+ # Click again to finish
261
+ map.click(position={"x": 200, "y": 200})
262
+ expect(polygons).to_have_count(1)
263
+ polygons.first.click(position={"x": 20, "y": 20}, button="right")
264
+ extract_button.click()
265
+ expect(polygons).to_have_count(2)
266
+
267
+
268
+ def test_cannot_transfer_shape_to_line(live_server, page, tilelayer):
269
+ page.goto(f"{live_server.url}/en/map/new/")
270
+ polygons = page.locator(".leaflet-overlay-pane path")
271
+ expect(polygons).to_have_count(0)
272
+ page.get_by_title("Draw a polygon").click()
273
+ map = page.locator("#map")
274
+ map.click(position={"x": 150, "y": 100})
275
+ map.click(position={"x": 150, "y": 150})
276
+ map.click(position={"x": 100, "y": 150})
277
+ map.click(position={"x": 100, "y": 100})
278
+ # Click again to finish
279
+ map.click(position={"x": 100, "y": 100})
280
+ expect(polygons).to_have_count(1)
281
+ extract_button = page.get_by_role("link", name="Extract shape to separate feature")
282
+ polygons.first.click(position={"x": 20, "y": 20}, button="right")
283
+ expect(extract_button).to_be_hidden()
284
+ page.get_by_title("Draw a polyline").click()
285
+ map.click(position={"x": 200, "y": 250})
286
+ map.click(position={"x": 200, "y": 200})
287
+ # Click again to finish
288
+ map.click(position={"x": 200, "y": 200})
289
+ expect(polygons).to_have_count(2)
290
+ polygons.first.click(position={"x": 20, "y": 20}, button="right")
291
+ expect(extract_button).to_be_hidden()
292
+
293
+
294
+ def test_cannot_transfer_shape_to_marker(live_server, page, tilelayer):
295
+ page.goto(f"{live_server.url}/en/map/new/")
296
+ polygons = page.locator(".leaflet-overlay-pane path")
297
+ expect(polygons).to_have_count(0)
298
+ page.get_by_title("Draw a polygon").click()
299
+ map = page.locator("#map")
300
+ map.click(position={"x": 150, "y": 100})
301
+ map.click(position={"x": 150, "y": 150})
302
+ map.click(position={"x": 100, "y": 150})
303
+ map.click(position={"x": 100, "y": 100})
304
+ # Click again to finish
305
+ map.click(position={"x": 100, "y": 100})
306
+ expect(polygons).to_have_count(1)
307
+ extract_button = page.get_by_role("link", name="Extract shape to separate feature")
308
+ polygons.first.click(position={"x": 20, "y": 20}, button="right")
309
+ expect(extract_button).to_be_hidden()
310
+ page.get_by_title("Draw a marker").click()
311
+ map.click(position={"x": 250, "y": 200})
312
+ expect(polygons).to_have_count(1)
313
+ polygons.first.click(position={"x": 20, "y": 20}, button="right")
314
+ expect(extract_button).to_be_hidden()
315
+
316
+
317
+ def test_can_clone_polygon(live_server, page, tilelayer, settings):
318
+ settings.UMAP_ALLOW_ANONYMOUS = True
319
+ page.goto(f"{live_server.url}/en/map/new/")
320
+ polygons = page.locator(".leaflet-overlay-pane path")
321
+ expect(polygons).to_have_count(0)
322
+ page.get_by_title("Draw a polygon").click()
323
+ map = page.locator("#map")
324
+ map.click(position={"x": 200, "y": 100})
325
+ map.click(position={"x": 200, "y": 200})
326
+ map.click(position={"x": 100, "y": 200})
327
+ map.click(position={"x": 100, "y": 100})
328
+ # Click again to finish
329
+ map.click(position={"x": 100, "y": 100})
330
+ expect(polygons).to_have_count(1)
331
+ polygons.first.click(button="right")
332
+ page.get_by_role("link", name="Clone this feature").click()
333
+ expect(polygons).to_have_count(2)
334
+ data = save_and_get_json(page)
335
+ assert len(data["features"]) == 2
336
+ assert data["features"][0]["geometry"]["type"] == "Polygon"
337
+ assert data["features"][0]["geometry"] == data["features"][1]["geometry"]
338
+
339
+
340
+ def test_can_transform_polygon_to_line(live_server, page, tilelayer, settings):
341
+ settings.UMAP_ALLOW_ANONYMOUS = True
342
+ page.goto(f"{live_server.url}/en/map/new/")
343
+ paths = page.locator(".leaflet-overlay-pane path")
344
+ polygons = page.locator(".leaflet-overlay-pane path[fill='DarkBlue']")
345
+ expect(polygons).to_have_count(0)
346
+ page.get_by_title("Draw a polygon").click()
347
+ map = page.locator("#map")
348
+ map.click(position={"x": 200, "y": 100})
349
+ map.click(position={"x": 200, "y": 200})
350
+ map.click(position={"x": 100, "y": 200})
351
+ map.click(position={"x": 100, "y": 100})
352
+ # Click again to finish
353
+ map.click(position={"x": 100, "y": 100})
354
+ expect(polygons).to_have_count(1)
355
+ expect(paths).to_have_count(1)
356
+ polygons.first.click(button="right")
357
+ page.get_by_role("link", name="Transform to lines").click()
358
+ # No more polygons (will fill), but one path, it must be a line
359
+ expect(polygons).to_have_count(0)
360
+ expect(paths).to_have_count(1)
361
+ data = save_and_get_json(page)
362
+ assert len(data["features"]) == 1
363
+ assert data["features"][0]["geometry"]["type"] == "LineString"