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,202 @@
1
+ import re
2
+
3
+ from playwright.sync_api import expect
4
+
5
+ from umap.models import DataLayer, Map
6
+
7
+ from ..base import DataLayerFactory
8
+
9
+
10
+ def test_can_edit_name(page, live_server, tilelayer):
11
+ page.goto(f"{live_server.url}/en/map/new/")
12
+
13
+ page.get_by_title("Edit map name and caption").click()
14
+ name_input = page.locator('.map-metadata input[name="name"]')
15
+ expect(name_input).to_be_visible()
16
+ name_input.click()
17
+ name_input.press("Control+a")
18
+ name_input.fill("New map name")
19
+ expect(page.locator(".umap-main-edit-toolbox .map-name").nth(0)).to_have_text(
20
+ "New map name"
21
+ )
22
+
23
+
24
+ def test_can_edit_name_on_click_on_toolbar(page, live_server, tilelayer):
25
+ page.goto(f"{live_server.url}/en/map/new/")
26
+ page.locator(".map-name").click()
27
+ name_input = page.locator('.map-metadata input[name="name"]')
28
+ expect(name_input).to_be_visible()
29
+
30
+
31
+ def test_map_name_impacts_ui(live_server, page, tilelayer):
32
+ page.goto(f"{live_server.url}/en/map/new/")
33
+
34
+ gear_icon = page.get_by_title("Edit map name and caption")
35
+ expect(gear_icon).to_be_visible()
36
+ gear_icon.click()
37
+
38
+ name_input = page.locator("form").locator('input[name="name"]').first
39
+ expect(name_input).to_be_visible()
40
+
41
+ name_input.fill("something else")
42
+
43
+ expect(page.get_by_role("button", name="something else").nth(1)).to_be_visible()
44
+
45
+
46
+ def test_zoomcontrol_impacts_ui(live_server, page, tilelayer):
47
+ page.goto(f"{live_server.url}/en/map/new/")
48
+
49
+ gear_icon = page.get_by_title("Map advanced properties")
50
+ expect(gear_icon).to_be_visible()
51
+ gear_icon.click()
52
+
53
+ # Should be visible by default
54
+ zoom_in = page.get_by_label("Zoom in")
55
+ zoom_out = page.get_by_label("Zoom out")
56
+
57
+ expect(zoom_in).to_be_visible()
58
+ expect(zoom_out).to_be_visible()
59
+
60
+ # Hide them
61
+ page.get_by_role("heading", name="User interface options").click()
62
+ hide_zoom_controls = (
63
+ page.locator("div")
64
+ .filter(has_text=re.compile(r"^Display the zoom control"))
65
+ .locator("label")
66
+ .nth(2)
67
+ )
68
+ hide_zoom_controls.click()
69
+
70
+ expect(zoom_in).to_be_hidden()
71
+ expect(zoom_out).to_be_hidden()
72
+
73
+
74
+ def test_map_color_impacts_data(live_server, page, tilelayer):
75
+ page.goto(f"{live_server.url}/en/map/new/")
76
+
77
+ gear_icon = page.get_by_title("Map advanced properties")
78
+ expect(gear_icon).to_be_visible()
79
+ gear_icon.click()
80
+
81
+ # Click on the Draw a marker button on a new map.
82
+ create_marker_p1 = page.get_by_title("Draw a marker")
83
+ expect(create_marker_p1).to_be_visible()
84
+ create_marker_p1.click()
85
+
86
+ # Add a new marker
87
+ marker_pane_p1 = page.locator(".leaflet-marker-pane > div")
88
+ map_el = page.locator("#map")
89
+ map_el.click(position={"x": 200, "y": 200})
90
+ expect(marker_pane_p1).to_have_count(1)
91
+
92
+ # Change the default color
93
+ page.get_by_role("heading", name="Shape properties").click()
94
+ page.locator("#umap-feature-shape-properties").get_by_text("define").first.click()
95
+ page.get_by_title("Lime", exact=True).click()
96
+
97
+ # Assert the new color was used
98
+ marker_style = page.locator(".leaflet-marker-icon .icon_container").get_attribute(
99
+ "style"
100
+ )
101
+ assert "lime" in marker_style
102
+
103
+
104
+ def test_limitbounds_impacts_ui(live_server, page, tilelayer):
105
+ page.goto(f"{live_server.url}/en/map/new/")
106
+
107
+ gear_icon = page.get_by_title("Map advanced properties")
108
+ expect(gear_icon).to_be_visible()
109
+ gear_icon.click()
110
+
111
+ page.get_by_role("heading", name="Limit bounds").click()
112
+ default_zoom_url = f"{live_server.url}/en/map/new/#5/51.110/7.053"
113
+ page.goto(default_zoom_url)
114
+ page.get_by_role("button", name="Use current bounds").click()
115
+
116
+ zoom_in = page.get_by_label("Zoom in")
117
+ zoom_out = page.get_by_label("Zoom out")
118
+
119
+ # It should be possible to zoom in
120
+ zoom_in.click()
121
+ page.wait_for_timeout(500)
122
+ assert page.url != default_zoom_url
123
+
124
+ # But not to zoom out of the window
125
+ zoom_out.click() # back to normal
126
+ page.wait_for_timeout(500)
127
+ assert "leaflet-disabled" in zoom_out.get_attribute("class")
128
+
129
+
130
+ def test_sortkey_impacts_datalayerindex(map, live_server, page):
131
+ # Create points with a "key" property.
132
+ # But we want them to sort by key (First, Second, Third)
133
+ DataLayerFactory(
134
+ map=map,
135
+ data={
136
+ "type": "FeatureCollection",
137
+ "features": [
138
+ {
139
+ "type": "Feature",
140
+ "geometry": {
141
+ "type": "Point",
142
+ "coordinates": [13.6, 48.5],
143
+ },
144
+ "properties": {"name": "Z First", "key": "1st Point"},
145
+ },
146
+ {
147
+ "type": "Feature",
148
+ "geometry": {
149
+ "type": "Point",
150
+ "coordinates": [13.7, 48.4],
151
+ },
152
+ "properties": {"name": "Y Second", "key": "2d Point"},
153
+ },
154
+ {
155
+ "type": "Feature",
156
+ "geometry": {
157
+ "type": "Point",
158
+ "coordinates": [13.5, 48.6],
159
+ },
160
+ "properties": {"name": "X Third", "key": "3rd Point"},
161
+ },
162
+ ],
163
+ },
164
+ )
165
+ map.edit_status = Map.ANONYMOUS
166
+ datalayer = map.datalayer_set.first()
167
+ datalayer.edit_status = DataLayer.ANONYMOUS
168
+ datalayer.save()
169
+ map.save()
170
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
171
+
172
+ # By default, features are sorted by name (Third, Second, First)
173
+ page.get_by_role("button", name="See layers").click()
174
+ page.get_by_role("heading", name="Show/hide layer").locator("i").click()
175
+
176
+ first_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(0)
177
+ second_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(1)
178
+ third_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(2)
179
+ assert "X Third" == first_listed_feature.text_content()
180
+ assert "Y Second" == second_listed_feature.text_content()
181
+ assert "Z First" == third_listed_feature.text_content()
182
+
183
+ # Change the default sortkey to be "key"
184
+ page.get_by_role("button", name="Edit").click()
185
+ page.get_by_role("link", name="Map advanced properties").click()
186
+ page.get_by_role("heading", name="Default properties").click()
187
+
188
+ # Click "define"
189
+ page.locator(".panel .umap-field-sortKey .define").click()
190
+ page.locator('input[name="sortKey"]').click()
191
+ page.locator('input[name="sortKey"]').fill("key")
192
+
193
+ # Click the checkmark to apply the changes
194
+ page.locator(".panel .umap-field-sortKey .blur-button").click()
195
+
196
+ # Features should be sorted by key (First, Second, Third)
197
+ first_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(0)
198
+ second_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(1)
199
+ third_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(2)
200
+ assert "Z First" == first_listed_feature.text_content()
201
+ assert "Y Second" == second_listed_feature.text_content()
202
+ assert "X Third" == third_listed_feature.text_content()
@@ -0,0 +1,120 @@
1
+ import platform
2
+ from copy import deepcopy
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
+ DATALAYER_DATA = {
12
+ "type": "FeatureCollection",
13
+ "features": [
14
+ {
15
+ "type": "Feature",
16
+ "properties": {
17
+ "name": "test marker",
18
+ "description": "Some description",
19
+ },
20
+ "id": "QwNjg",
21
+ "geometry": {
22
+ "type": "Point",
23
+ "coordinates": [14.6889, 48.5529],
24
+ },
25
+ },
26
+ ],
27
+ }
28
+
29
+
30
+ @pytest.fixture
31
+ def bootstrap(map, live_server):
32
+ DataLayerFactory(map=map, data=DATALAYER_DATA)
33
+
34
+
35
+ def test_can_edit_on_shift_click(live_server, openmap, page, datalayer):
36
+ modifier = "Meta" if platform.system() == "Darwin" else "Control"
37
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
38
+ page.locator(".leaflet-marker-icon").click(modifiers=[modifier, "Shift"])
39
+ expect(page.get_by_role("heading", name="Layer properties")).to_be_visible()
40
+
41
+
42
+ def test_marker_style_should_have_precedence(live_server, openmap, page, bootstrap):
43
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
44
+
45
+ # Change colour at layer level
46
+ page.get_by_role("link", name="Manage layers").click()
47
+ page.locator(".panel").get_by_title("Edit", exact=True).click()
48
+ page.get_by_role("heading", name="Shape properties").click()
49
+ page.locator(".umap-field-color .define").click()
50
+ expect(page.locator(".leaflet-marker-icon .icon_container")).to_have_css(
51
+ "background-color", "rgb(0, 0, 139)"
52
+ )
53
+ page.get_by_title("DarkRed").first.click()
54
+ expect(page.locator(".leaflet-marker-icon .icon_container")).to_have_css(
55
+ "background-color", "rgb(139, 0, 0)"
56
+ )
57
+
58
+ # Now change at marker level, it should take precedence
59
+ page.locator(".leaflet-marker-icon").click(modifiers=["Shift"])
60
+ page.get_by_role("heading", name="Shape properties").click()
61
+ page.locator("#umap-feature-shape-properties").get_by_text("define").first.click()
62
+ page.get_by_title("GoldenRod", exact=True).click()
63
+ expect(page.locator(".leaflet-marker-icon .icon_container")).to_have_css(
64
+ "background-color", "rgb(218, 165, 32)"
65
+ )
66
+
67
+ # Now change again at layer level again, it should not change the marker color
68
+ page.get_by_role("link", name="Manage layers").click()
69
+ page.locator(".panel").get_by_title("Edit", exact=True).click()
70
+ page.get_by_role("heading", name="Shape properties").click()
71
+ page.locator(".umap-field-color input").click()
72
+ page.get_by_title("DarkViolet").first.click()
73
+ expect(page.locator(".leaflet-marker-icon .icon_container")).to_have_css(
74
+ "background-color", "rgb(218, 165, 32)"
75
+ )
76
+
77
+
78
+ def test_should_open_an_edit_toolbar_on_click(live_server, openmap, page, bootstrap):
79
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
80
+ page.locator(".leaflet-marker-icon").click()
81
+ expect(page.get_by_role("link", name="Toggle edit mode")).to_be_visible()
82
+ expect(page.get_by_role("link", name="Delete this feature")).to_be_visible()
83
+
84
+
85
+ def test_should_update_open_popup_on_edit(live_server, openmap, page, bootstrap):
86
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}")
87
+ expect(page.locator(".umap-icon-active")).to_be_hidden()
88
+ page.locator(".leaflet-marker-icon").click()
89
+ expect(page.locator(".leaflet-popup-content-wrapper")).to_be_visible()
90
+ expect(page.get_by_role("heading", name="test marker")).to_be_visible()
91
+ expect(page.get_by_text("Some description")).to_be_visible()
92
+ page.get_by_role("button", name="Edit").click()
93
+ page.locator(".leaflet-marker-icon").click(modifiers=["Shift"])
94
+ page.locator('input[name="name"]').fill("test marker edited")
95
+ expect(page.get_by_role("heading", name="test marker edited")).to_be_visible()
96
+
97
+
98
+ def test_should_follow_datalayer_style_when_changing_datalayer(
99
+ live_server, openmap, page
100
+ ):
101
+ data = deepcopy(DATALAYER_DATA)
102
+ data["_umap_options"] = {"color": "DarkCyan"}
103
+ DataLayerFactory(map=openmap, data=data)
104
+ DataLayerFactory(
105
+ map=openmap,
106
+ name="other datalayer",
107
+ data={
108
+ "type": "FeatureCollection",
109
+ "features": [],
110
+ "_umap_options": {"color": "DarkViolet"},
111
+ },
112
+ )
113
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
114
+ marker = page.locator(".leaflet-marker-icon .icon_container")
115
+ expect(marker).to_have_css("background-color", "rgb(0, 139, 139)")
116
+ # Change datalayer
117
+ marker.click()
118
+ page.get_by_role("link", name="Toggle edit mode (⇧+Click)").click()
119
+ page.locator(".umap-field-datalayer select").select_option(label="other datalayer")
120
+ expect(marker).to_have_css("background-color", "rgb(148, 0, 211)")
@@ -0,0 +1,122 @@
1
+ import platform
2
+
3
+ import pytest
4
+ from playwright.sync_api import expect
5
+
6
+ from ..base import DataLayerFactory
7
+
8
+ pytestmark = pytest.mark.django_db
9
+
10
+ DATALAYER_DATA = {
11
+ "type": "FeatureCollection",
12
+ "features": [
13
+ {
14
+ "type": "Feature",
15
+ "properties": {
16
+ "name": "name poly",
17
+ },
18
+ "id": "gyNzM",
19
+ "geometry": {
20
+ "type": "Polygon",
21
+ "coordinates": [
22
+ [
23
+ [11.25, 53.585984],
24
+ [10.151367, 52.975108],
25
+ [12.689209, 52.167194],
26
+ [14.084473, 53.199452],
27
+ [12.634277, 53.618579],
28
+ [11.25, 53.585984],
29
+ [11.25, 53.585984],
30
+ ],
31
+ ],
32
+ },
33
+ },
34
+ ],
35
+ }
36
+
37
+
38
+ @pytest.fixture
39
+ def bootstrap(map, live_server):
40
+ map.settings["properties"]["zoom"] = 6
41
+ map.settings["geometry"] = {
42
+ "type": "Point",
43
+ "coordinates": [8.429, 53.239],
44
+ }
45
+ map.save()
46
+ DataLayerFactory(map=map, data=DATALAYER_DATA)
47
+
48
+
49
+ def test_can_edit_on_shift_click(live_server, openmap, page, datalayer):
50
+ modifier = "Meta" if platform.system() == "Darwin" else "Control"
51
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
52
+ page.locator(".leaflet-marker-icon").click(modifiers=[modifier, "Shift"])
53
+ expect(page.get_by_role("heading", name="Layer properties")).to_be_visible()
54
+
55
+
56
+ def test_marker_style_should_have_precedence(live_server, openmap, page, bootstrap):
57
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
58
+
59
+ # Change colour at layer level
60
+ page.get_by_role("link", name="Manage layers").click()
61
+ page.locator(".panel").get_by_title("Edit", exact=True).click()
62
+ page.get_by_role("heading", name="Shape properties").click()
63
+ page.locator(".umap-field-color .define").click()
64
+ expect(page.locator(".leaflet-overlay-pane path[fill='DarkBlue']")).to_have_count(1)
65
+ page.get_by_title("DarkRed").first.click()
66
+ expect(page.locator(".leaflet-overlay-pane path[fill='DarkRed']")).to_have_count(1)
67
+
68
+ # Now change at polygon level, it should take precedence
69
+ page.locator("path").click(modifiers=["Shift"])
70
+ page.get_by_role("heading", name="Shape properties").click()
71
+ page.locator("#umap-feature-shape-properties").get_by_text("define").first.click()
72
+ page.get_by_title("GoldenRod", exact=True).first.click()
73
+ expect(page.locator(".leaflet-overlay-pane path[fill='GoldenRod']")).to_have_count(
74
+ 1
75
+ )
76
+
77
+ # Now change again at layer level again, it should not change the marker color
78
+ page.get_by_role("link", name="Manage layers").click()
79
+ page.locator(".panel").get_by_title("Edit", exact=True).click()
80
+ page.get_by_role("heading", name="Shape properties").click()
81
+ page.locator(".umap-field-color input").click()
82
+ page.get_by_title("DarkViolet").first.click()
83
+ expect(page.locator(".leaflet-overlay-pane path[fill='GoldenRod']")).to_have_count(
84
+ 1
85
+ )
86
+
87
+
88
+ def test_should_open_an_edit_toolbar_on_click(live_server, openmap, page, bootstrap):
89
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
90
+ page.locator("path").click()
91
+ expect(page.get_by_role("link", name="Toggle edit mode")).to_be_visible()
92
+ expect(page.get_by_role("link", name="Delete this feature")).to_be_visible()
93
+
94
+
95
+ def test_can_remove_stroke(live_server, openmap, page, bootstrap):
96
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
97
+ expect(page.locator(".leaflet-overlay-pane path[stroke='DarkBlue']")).to_have_count(
98
+ 1
99
+ )
100
+ page.locator("path").click()
101
+ page.get_by_role("link", name="Toggle edit mode").click()
102
+ page.get_by_role("heading", name="Shape properties").click()
103
+ page.locator(".umap-field-stroke .define").first.click()
104
+ page.locator(".umap-field-stroke label").first.click()
105
+ expect(page.locator(".leaflet-overlay-pane path[stroke='DarkBlue']")).to_have_count(
106
+ 0
107
+ )
108
+ expect(page.locator(".leaflet-overlay-pane path[stroke='none']")).to_have_count(1)
109
+
110
+
111
+ def test_should_reset_style_on_cancel(live_server, openmap, page, bootstrap):
112
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
113
+ page.locator("path").click(modifiers=["Shift"])
114
+ page.get_by_role("heading", name="Shape properties").click()
115
+ page.locator("#umap-feature-shape-properties").get_by_text("define").first.click()
116
+ page.get_by_title("GoldenRod", exact=True).first.click()
117
+ expect(page.locator(".leaflet-overlay-pane path[fill='GoldenRod']")).to_have_count(
118
+ 1
119
+ )
120
+ page.once("dialog", lambda dialog: dialog.accept())
121
+ page.get_by_role("button", name="Cancel edits").click()
122
+ expect(page.locator(".leaflet-overlay-pane path[fill='DarkBlue']")).to_have_count(1)
@@ -1,3 +1,5 @@
1
+ import copy
2
+
1
3
  import pytest
2
4
  from playwright.sync_api import expect
3
5
 
@@ -11,12 +13,22 @@ DATALAYER_DATA1 = {
11
13
  "features": [
12
14
  {
13
15
  "type": "Feature",
14
- "properties": {"mytype": "even", "name": "Point 2"},
16
+ "properties": {
17
+ "mytype": "even",
18
+ "name": "Point 2",
19
+ "mynumber": 10,
20
+ "mydate": "2024/04/14 12:19:17",
21
+ },
15
22
  "geometry": {"type": "Point", "coordinates": [0.065918, 48.385442]},
16
23
  },
17
24
  {
18
25
  "type": "Feature",
19
- "properties": {"mytype": "odd", "name": "Point 1"},
26
+ "properties": {
27
+ "mytype": "odd",
28
+ "name": "Point 1",
29
+ "mynumber": 12,
30
+ "mydate": "2024/03/13 12:20:20",
31
+ },
20
32
  "geometry": {"type": "Point", "coordinates": [3.55957, 49.767074]},
21
33
  },
22
34
  ],
@@ -31,12 +43,22 @@ DATALAYER_DATA2 = {
31
43
  "features": [
32
44
  {
33
45
  "type": "Feature",
34
- "properties": {"mytype": "even", "name": "Point 4"},
46
+ "properties": {
47
+ "mytype": "even",
48
+ "name": "Point 4",
49
+ "mynumber": 10,
50
+ "mydate": "2024/08/18 13:14:15",
51
+ },
35
52
  "geometry": {"type": "Point", "coordinates": [0.856934, 45.290347]},
36
53
  },
37
54
  {
38
55
  "type": "Feature",
39
- "properties": {"mytype": "odd", "name": "Point 3"},
56
+ "properties": {
57
+ "mytype": "odd",
58
+ "name": "Point 3",
59
+ "mynumber": 14,
60
+ "mydate": "2024-04-14T10:19:17.000Z",
61
+ },
40
62
  "geometry": {"type": "Point", "coordinates": [4.372559, 47.945786]},
41
63
  },
42
64
  ],
@@ -46,19 +68,43 @@ DATALAYER_DATA2 = {
46
68
  }
47
69
 
48
70
 
49
- @pytest.fixture
50
- def bootstrap(map, live_server):
71
+ DATALAYER_DATA3 = {
72
+ "type": "FeatureCollection",
73
+ "features": [
74
+ {
75
+ "type": "Feature",
76
+ "properties": {"name": "a polygon"},
77
+ "geometry": {
78
+ "type": "Polygon",
79
+ "coordinates": [
80
+ [
81
+ [2.12, 49.57],
82
+ [1.08, 49.02],
83
+ [2.51, 47.55],
84
+ [3.19, 48.77],
85
+ [2.12, 49.57],
86
+ ]
87
+ ],
88
+ },
89
+ },
90
+ ],
91
+ "_umap_options": {"name": "Calque 2", "browsable": False},
92
+ }
93
+
94
+
95
+ def test_simple_facet_search(live_server, page, map):
51
96
  map.settings["properties"]["onLoadPanel"] = "facet"
52
- map.settings["properties"]["facetKey"] = "mytype|My type"
97
+ map.settings["properties"]["facetKey"] = "mytype|My type,mynumber|My Number|number"
53
98
  map.settings["properties"]["showLabel"] = True
54
99
  map.save()
55
100
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
56
101
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
57
-
58
-
59
- def test_simple_facet_search(live_server, page, bootstrap, map):
60
- page.goto(f"{live_server.url}{map.get_absolute_url()}")
102
+ DataLayerFactory(map=map, data=DATALAYER_DATA3)
103
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
61
104
  panel = page.locator(".umap-facet-search")
105
+ # From a non browsable datalayer, should not be impacted
106
+ paths = page.locator(".leaflet-overlay-pane path")
107
+ expect(paths).to_be_visible()
62
108
  expect(panel).to_be_visible()
63
109
  # Facet name
64
110
  expect(page.get_by_text("My type")).to_be_visible()
@@ -67,6 +113,7 @@ def test_simple_facet_search(live_server, page, bootstrap, map):
67
113
  odd = page.get_by_text("odd")
68
114
  expect(oven).to_be_visible()
69
115
  expect(odd).to_be_visible()
116
+ expect(paths).to_be_visible()
70
117
  markers = page.locator(".leaflet-marker-icon")
71
118
  expect(markers).to_have_count(4)
72
119
  # Tooltips
@@ -81,6 +128,80 @@ def test_simple_facet_search(live_server, page, bootstrap, map):
81
128
  expect(page.get_by_text("Point 4")).to_be_hidden()
82
129
  expect(page.get_by_text("Point 1")).to_be_visible()
83
130
  expect(page.get_by_text("Point 3")).to_be_visible()
131
+ expect(paths).to_be_visible
84
132
  # Now let's filter
85
133
  odd.click()
86
134
  expect(markers).to_have_count(4)
135
+ expect(paths).to_be_visible()
136
+
137
+ # Let's filter using the number facet
138
+ expect(page.get_by_text("My Number")).to_be_visible()
139
+ expect(page.get_by_label("Min")).to_have_value("10")
140
+ expect(page.get_by_label("Max")).to_have_value("14")
141
+ page.get_by_label("Min").fill("11")
142
+ page.keyboard.press("Tab") # Move out of the input, so the "change" event is sent
143
+ expect(markers).to_have_count(2)
144
+ expect(paths).to_be_visible()
145
+ page.get_by_label("Max").fill("13")
146
+ page.keyboard.press("Tab")
147
+ expect(markers).to_have_count(1)
148
+
149
+ # Now let's combine
150
+ page.get_by_label("Min").fill("10")
151
+ page.keyboard.press("Tab")
152
+ expect(markers).to_have_count(3)
153
+ odd.click()
154
+ expect(markers).to_have_count(1)
155
+ expect(paths).to_be_visible()
156
+
157
+
158
+ def test_date_facet_search(live_server, page, map):
159
+ map.settings["properties"]["onLoadPanel"] = "facet"
160
+ map.settings["properties"]["facetKey"] = "mydate|Date filter|date"
161
+ map.save()
162
+ DataLayerFactory(map=map, data=DATALAYER_DATA1)
163
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
164
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
165
+ markers = page.locator(".leaflet-marker-icon")
166
+ expect(markers).to_have_count(4)
167
+ expect(page.get_by_text("Date Filter")).to_be_visible()
168
+ expect(page.get_by_label("From")).to_have_value("2024-03-13")
169
+ expect(page.get_by_label("Until")).to_have_value("2024-08-18")
170
+ page.get_by_label("From").fill("2024-03-14")
171
+ expect(markers).to_have_count(3)
172
+ page.get_by_label("Until").fill("2024-08-17")
173
+ expect(markers).to_have_count(2)
174
+
175
+
176
+ def test_choice_with_empty_value(live_server, page, map):
177
+ map.settings["properties"]["onLoadPanel"] = "facet"
178
+ map.settings["properties"]["facetKey"] = "mytype|My type"
179
+ map.save()
180
+ data = copy.deepcopy(DATALAYER_DATA1)
181
+ data["features"][0]["properties"]["mytype"] = ""
182
+ del data["features"][1]["properties"]["mytype"]
183
+ DataLayerFactory(map=map, data=data)
184
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
185
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
186
+ expect(page.get_by_text("<empty value>")).to_be_visible()
187
+ markers = page.locator(".leaflet-marker-icon")
188
+ expect(markers).to_have_count(4)
189
+ page.get_by_text("<empty value>").click()
190
+ expect(markers).to_have_count(2)
191
+
192
+
193
+ def test_number_with_zero_value(live_server, page, map):
194
+ map.settings["properties"]["onLoadPanel"] = "facet"
195
+ map.settings["properties"]["facetKey"] = "mynumber|Filter|number"
196
+ map.save()
197
+ data = copy.deepcopy(DATALAYER_DATA1)
198
+ data["features"][0]["properties"]["mynumber"] = 0
199
+ DataLayerFactory(map=map, data=data)
200
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
201
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
202
+ expect(page.get_by_label("Min")).to_have_value("0")
203
+ expect(page.get_by_label("Max")).to_have_value("14")
204
+ page.get_by_label("Min").fill("1")
205
+ page.keyboard.press("Tab") # Move out of the input, so the "change" event is sent
206
+ markers = page.locator(".leaflet-marker-icon")
207
+ expect(markers).to_have_count(3)