umap-project 3.4.0b1__py3-none-any.whl → 3.4.2__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.
Files changed (202) hide show
  1. umap/__init__.py +1 -1
  2. umap/context_processors.py +1 -1
  3. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/da/LC_MESSAGES/django.po +20 -16
  5. umap/locale/en/LC_MESSAGES/django.po +18 -14
  6. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/es/LC_MESSAGES/django.po +20 -16
  8. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  9. umap/locale/fr/LC_MESSAGES/django.po +18 -14
  10. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  11. umap/locale/pl/LC_MESSAGES/django.po +72 -71
  12. umap/migrations/0018_datalayer_uuid.py +1 -1
  13. umap/models.py +7 -3
  14. umap/settings/local.py.sample +1 -1
  15. umap/static/umap/content.css +0 -3
  16. umap/static/umap/css/bar.css +9 -6
  17. umap/static/umap/css/form.css +27 -11
  18. umap/static/umap/css/popup.css +1 -0
  19. umap/static/umap/js/components/base.js +1 -1
  20. umap/static/umap/js/components/copiable.js +47 -0
  21. umap/static/umap/js/modules/autocomplete.js +31 -58
  22. umap/static/umap/js/modules/browser.js +8 -8
  23. umap/static/umap/js/modules/data/features.js +33 -36
  24. umap/static/umap/js/modules/data/fields.js +446 -0
  25. umap/static/umap/js/modules/data/layer.js +76 -93
  26. umap/static/umap/js/modules/domutils.js +24 -4
  27. umap/static/umap/js/modules/filters.js +20 -47
  28. umap/static/umap/js/modules/form/fields.js +4 -4
  29. umap/static/umap/js/modules/formatter.js +9 -1
  30. umap/static/umap/js/modules/help.js +13 -14
  31. umap/static/umap/js/modules/i18n.js +1 -1
  32. umap/static/umap/js/modules/importer.js +18 -27
  33. umap/static/umap/js/modules/importers/banfr.js +0 -1
  34. umap/static/umap/js/modules/importers/cadastrefr.js +19 -19
  35. umap/static/umap/js/modules/importers/communesfr.js +7 -8
  36. umap/static/umap/js/modules/importers/datasets.js +14 -14
  37. umap/static/umap/js/modules/importers/geodatamine.js +20 -22
  38. umap/static/umap/js/modules/importers/opendata.js +10 -0
  39. umap/static/umap/js/modules/importers/overpass.js +19 -18
  40. umap/static/umap/js/modules/managers.js +1 -265
  41. umap/static/umap/js/modules/permissions.js +5 -3
  42. umap/static/umap/js/modules/rendering/controls.js +6 -4
  43. umap/static/umap/js/modules/rendering/icon.js +5 -9
  44. umap/static/umap/js/modules/rendering/layers/base.js +1 -1
  45. umap/static/umap/js/modules/rendering/layers/classified.js +16 -11
  46. umap/static/umap/js/modules/rendering/layers/heat.js +27 -21
  47. umap/static/umap/js/modules/rendering/map.js +22 -22
  48. umap/static/umap/js/modules/rendering/popup.js +6 -3
  49. umap/static/umap/js/modules/rendering/template.js +31 -37
  50. umap/static/umap/js/modules/rendering/ui.js +1 -2
  51. umap/static/umap/js/modules/rules.js +34 -41
  52. umap/static/umap/js/modules/schema.js +0 -7
  53. umap/static/umap/js/modules/share.js +36 -69
  54. umap/static/umap/js/modules/slideshow.js +3 -3
  55. umap/static/umap/js/modules/tableeditor.js +0 -1
  56. umap/static/umap/js/modules/ui/bar.js +51 -32
  57. umap/static/umap/js/modules/ui/dialog.js +10 -1
  58. umap/static/umap/js/modules/ui/panel.js +28 -23
  59. umap/static/umap/js/modules/ui/tooltip.js +1 -1
  60. umap/static/umap/js/modules/umap.js +84 -84
  61. umap/static/umap/js/modules/utils.js +13 -4
  62. umap/static/umap/js/umap.controls.js +33 -14
  63. umap/static/umap/locale/am_ET.js +19 -8
  64. umap/static/umap/locale/am_ET.json +19 -8
  65. umap/static/umap/locale/ar.js +19 -8
  66. umap/static/umap/locale/ar.json +19 -8
  67. umap/static/umap/locale/ast.js +19 -8
  68. umap/static/umap/locale/ast.json +19 -8
  69. umap/static/umap/locale/bg.js +19 -8
  70. umap/static/umap/locale/bg.json +19 -8
  71. umap/static/umap/locale/br.js +20 -9
  72. umap/static/umap/locale/br.json +20 -9
  73. umap/static/umap/locale/ca.js +19 -8
  74. umap/static/umap/locale/ca.json +19 -8
  75. umap/static/umap/locale/cs_CZ.js +20 -9
  76. umap/static/umap/locale/cs_CZ.json +20 -9
  77. umap/static/umap/locale/da.js +54 -43
  78. umap/static/umap/locale/da.json +54 -43
  79. umap/static/umap/locale/de.js +44 -33
  80. umap/static/umap/locale/de.json +44 -33
  81. umap/static/umap/locale/el.js +20 -9
  82. umap/static/umap/locale/el.json +20 -9
  83. umap/static/umap/locale/en.js +20 -9
  84. umap/static/umap/locale/en.json +20 -9
  85. umap/static/umap/locale/en_US.json +19 -8
  86. umap/static/umap/locale/es.js +55 -44
  87. umap/static/umap/locale/es.json +55 -44
  88. umap/static/umap/locale/et.js +20 -9
  89. umap/static/umap/locale/et.json +20 -9
  90. umap/static/umap/locale/eu.js +25 -14
  91. umap/static/umap/locale/eu.json +25 -14
  92. umap/static/umap/locale/fa_IR.js +20 -9
  93. umap/static/umap/locale/fa_IR.json +20 -9
  94. umap/static/umap/locale/fi.js +19 -8
  95. umap/static/umap/locale/fi.json +19 -8
  96. umap/static/umap/locale/fr.js +21 -10
  97. umap/static/umap/locale/fr.json +21 -10
  98. umap/static/umap/locale/gl.js +147 -136
  99. umap/static/umap/locale/gl.json +147 -136
  100. umap/static/umap/locale/he.js +19 -8
  101. umap/static/umap/locale/he.json +19 -8
  102. umap/static/umap/locale/hr.js +19 -8
  103. umap/static/umap/locale/hr.json +19 -8
  104. umap/static/umap/locale/hu.js +62 -51
  105. umap/static/umap/locale/hu.json +62 -51
  106. umap/static/umap/locale/id.js +19 -8
  107. umap/static/umap/locale/id.json +19 -8
  108. umap/static/umap/locale/is.js +20 -9
  109. umap/static/umap/locale/is.json +20 -9
  110. umap/static/umap/locale/it.js +20 -9
  111. umap/static/umap/locale/it.json +20 -9
  112. umap/static/umap/locale/ja.js +19 -8
  113. umap/static/umap/locale/ja.json +19 -8
  114. umap/static/umap/locale/ko.js +19 -8
  115. umap/static/umap/locale/ko.json +19 -8
  116. umap/static/umap/locale/lt.js +19 -8
  117. umap/static/umap/locale/lt.json +19 -8
  118. umap/static/umap/locale/ms.js +20 -9
  119. umap/static/umap/locale/ms.json +20 -9
  120. umap/static/umap/locale/nl.js +20 -9
  121. umap/static/umap/locale/nl.json +20 -9
  122. umap/static/umap/locale/no.js +19 -8
  123. umap/static/umap/locale/no.json +19 -8
  124. umap/static/umap/locale/pl.js +56 -45
  125. umap/static/umap/locale/pl.json +56 -45
  126. umap/static/umap/locale/pl_PL.json +19 -8
  127. umap/static/umap/locale/pt.js +20 -9
  128. umap/static/umap/locale/pt.json +20 -9
  129. umap/static/umap/locale/pt_BR.js +19 -8
  130. umap/static/umap/locale/pt_BR.json +19 -8
  131. umap/static/umap/locale/pt_PT.js +19 -8
  132. umap/static/umap/locale/pt_PT.json +19 -8
  133. umap/static/umap/locale/ro.js +19 -8
  134. umap/static/umap/locale/ro.json +19 -8
  135. umap/static/umap/locale/ru.js +19 -8
  136. umap/static/umap/locale/ru.json +19 -8
  137. umap/static/umap/locale/si.js +1 -1
  138. umap/static/umap/locale/si.json +1 -1
  139. umap/static/umap/locale/sk_SK.js +19 -8
  140. umap/static/umap/locale/sk_SK.json +19 -8
  141. umap/static/umap/locale/sl.js +19 -8
  142. umap/static/umap/locale/sl.json +19 -8
  143. umap/static/umap/locale/sr.js +19 -8
  144. umap/static/umap/locale/sr.json +19 -8
  145. umap/static/umap/locale/sv.js +19 -8
  146. umap/static/umap/locale/sv.json +19 -8
  147. umap/static/umap/locale/th_TH.js +19 -8
  148. umap/static/umap/locale/th_TH.json +19 -8
  149. umap/static/umap/locale/tr.js +19 -8
  150. umap/static/umap/locale/tr.json +19 -8
  151. umap/static/umap/locale/uk_UA.js +19 -8
  152. umap/static/umap/locale/uk_UA.json +19 -8
  153. umap/static/umap/locale/vi.js +19 -8
  154. umap/static/umap/locale/vi.json +19 -8
  155. umap/static/umap/locale/vi_VN.json +19 -8
  156. umap/static/umap/locale/zh.js +19 -8
  157. umap/static/umap/locale/zh.json +19 -8
  158. umap/static/umap/locale/zh_CN.json +19 -8
  159. umap/static/umap/locale/zh_TW.Big5.json +19 -8
  160. umap/static/umap/locale/zh_TW.js +53 -42
  161. umap/static/umap/locale/zh_TW.json +53 -42
  162. umap/static/umap/map.css +8 -7
  163. umap/static/umap/unittests/utils.js +7 -7
  164. umap/templates/umap/content_footer.html +1 -0
  165. umap/templates/umap/css.html +0 -2
  166. umap/templates/umap/js.html +1 -3
  167. umap/templates/umap/login_popup_end.html +2 -2
  168. umap/tests/integration/conftest.py +11 -2
  169. umap/tests/integration/test_anonymous_owned_map.py +2 -2
  170. umap/tests/integration/test_conditional_rules.py +107 -52
  171. umap/tests/integration/test_draw_polygon.py +4 -0
  172. umap/tests/integration/test_draw_polyline.py +11 -0
  173. umap/tests/integration/test_edit_datalayer.py +1 -1
  174. umap/tests/integration/test_fields.py +19 -0
  175. umap/tests/integration/test_filters.py +6 -7
  176. umap/tests/integration/test_iframe.py +1 -1
  177. umap/tests/integration/test_import.py +23 -0
  178. umap/tests/integration/test_map.py +2 -2
  179. umap/tests/integration/test_map_preview.py +1 -1
  180. umap/tests/integration/test_owned_map.py +2 -2
  181. umap/tests/integration/test_picto.py +1 -1
  182. umap/tests/integration/test_popup.py +31 -0
  183. umap/tests/integration/test_remote_data.py +4 -4
  184. umap/tests/integration/test_save.py +1 -1
  185. umap/tests/integration/test_search.py +41 -0
  186. umap/tests/integration/test_share.py +2 -2
  187. umap/tests/integration/test_team.py +1 -1
  188. umap/tests/integration/test_websocket_sync.py +69 -20
  189. umap/tests/test_dashboard.py +1 -1
  190. umap/tests/test_statics.py +2 -2
  191. umap/tests/test_utils.py +4 -1
  192. umap/tests/test_views.py +1 -1
  193. umap/utils.py +3 -2
  194. {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/METADATA +17 -17
  195. {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/RECORD +198 -199
  196. umap/static/umap/js/umap.core.js +0 -93
  197. umap/static/umap/vendors/editinosm/Leaflet.EditInOSM.css +0 -46
  198. umap/static/umap/vendors/editinosm/Leaflet.EditInOSM.js +0 -240
  199. umap/static/umap/vendors/editinosm/edit-in-osm.png +0 -0
  200. {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/WHEEL +0 -0
  201. {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/entry_points.txt +0 -0
  202. {umap_project-3.4.0b1.dist-info → umap_project-3.4.2.dist-info}/licenses/LICENSE +0 -0
@@ -147,14 +147,14 @@ def test_default_view_latest_with_polygon(map, live_server, page):
147
147
  expect(layers).to_have_count(1)
148
148
 
149
149
 
150
- def test_default_view_locate(browser, live_server, map):
150
+ def test_default_view_locate(browser, live_server, map, new_page):
151
151
  context = browser.new_context(
152
152
  geolocation={"longitude": 8.52967, "latitude": 39.16267},
153
153
  permissions=["geolocation"],
154
154
  )
155
155
  map.settings["properties"]["defaultView"] = "locate"
156
156
  map.save()
157
- page = context.new_page()
157
+ page = new_page(custom_context=context)
158
158
  page.goto(f"{live_server.url}{map.get_absolute_url()}")
159
159
  expect(page).to_have_url(re.compile(r".*#18/39\.16267/8\.52967"))
160
160
 
@@ -58,7 +58,7 @@ def test_map_preview_can_load_remote_geojson(page, live_server, tilelayer):
58
58
  expect(markers).to_have_count(1)
59
59
 
60
60
 
61
- def test_map_preview_can_load_mutiple_remote_geojson(page, live_server, tilelayer):
61
+ def test_map_preview_can_load_multiple_remote_geojson(page, live_server, tilelayer):
62
62
  def handle(route):
63
63
  if "2" in route.request.url:
64
64
  route.fulfill(json=GEOJSON2)
@@ -183,7 +183,7 @@ def test_can_change_perms_after_create(tilelayer, live_server, login, user):
183
183
  page.goto(f"{live_server.url}/en/map/new")
184
184
  # Create a layer
185
185
  page.get_by_title("Manage layers").click()
186
- page.get_by_title("Add a layer").click()
186
+ page.get_by_role("button", name="Add a layer").click()
187
187
  page.locator("input[name=name]").fill("Layer 1")
188
188
  expect(
189
189
  page.get_by_role("button", name="Visibility: Draft (private)")
@@ -217,7 +217,7 @@ def test_can_change_owner(map, live_server, login, user):
217
217
  page.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
218
218
  edit_permissions = page.get_by_title("Update permissions and editors")
219
219
  edit_permissions.click()
220
- close = page.locator(".umap-field-owner .close")
220
+ close = page.locator(".umap-field-owner .icon-close")
221
221
  close.click()
222
222
  input = page.locator("input.edit-owner")
223
223
  with page.expect_response(re.compile(r".*/agnocomplete/.*")):
@@ -136,7 +136,7 @@ def test_can_change_picto_at_marker_level(openmap, live_server, page, pictos):
136
136
  expect(define).to_be_visible()
137
137
  expect(undefine).to_be_hidden()
138
138
  define.click()
139
- # Map has an icon defined, so it shuold open on Recent tab
139
+ # Map has an icon defined, so it should open on Recent tab
140
140
  symbols = page.locator(".umap-pictogram-body .umap-pictogram-choice")
141
141
  expect(page.get_by_text("Recent")).to_be_visible()
142
142
  expect(symbols).to_have_count(1)
@@ -42,3 +42,34 @@ def test_openstreetmap_popup(live_server, map, page):
42
42
  "src",
43
43
  "https://api.panoramax.xyz/api/pictures/d811b398-d930-4cf8-95a2-0c29c34d9fca/sd.jpg",
44
44
  )
45
+
46
+
47
+ def test_table_popup(live_server, map, page):
48
+ data = {
49
+ "type": "FeatureCollection",
50
+ "features": [
51
+ {
52
+ "type": "Feature",
53
+ "geometry": {"type": "Point", "coordinates": [2.49, 48.79]},
54
+ "properties": {
55
+ "url": "https://restaurant.org",
56
+ "formatted": "with **bold**",
57
+ },
58
+ "id": "AzMjk",
59
+ },
60
+ ],
61
+ "_umap_options": {
62
+ "popupTemplate": "Table",
63
+ "fields": [
64
+ {"key": "url", "type": "String"},
65
+ {"key": "formatted", "type": "Text"},
66
+ ],
67
+ },
68
+ }
69
+ DataLayerFactory(map=map, data=data)
70
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#18/48.79/2.49")
71
+ page.locator(".leaflet-marker-icon").click()
72
+ expect(page.get_by_role("link", name="https://restaurant.org")).to_be_visible()
73
+ expect(page.get_by_role("cell", name="with bold").locator("strong")).to_have_text(
74
+ "bold"
75
+ )
@@ -16,7 +16,7 @@ def intercept_remote_data(page):
16
16
  "features": [
17
17
  {
18
18
  "type": "Feature",
19
- "properties": {"name": "Point 2"},
19
+ "properties": {"name": "Point 2", "foobar": "bla"},
20
20
  "geometry": {
21
21
  "type": "Point",
22
22
  "coordinates": [4.3375, 11.2707],
@@ -29,7 +29,7 @@ def intercept_remote_data(page):
29
29
  "features": [
30
30
  {
31
31
  "type": "Feature",
32
- "properties": {"name": "Point 1"},
32
+ "properties": {"name": "Point 1", "foobar": "baz"},
33
33
  "geometry": {
34
34
  "type": "Point",
35
35
  "coordinates": [4.3375, 12.2707],
@@ -117,8 +117,8 @@ def test_create_remote_data_layer(page, live_server, tilelayer, settings):
117
117
  "type": "String",
118
118
  },
119
119
  {
120
- "key": "description",
121
- "type": "Text",
120
+ "key": "foobar",
121
+ "type": "String",
122
122
  },
123
123
  ],
124
124
  "id": str(datalayer.pk),
@@ -1,7 +1,7 @@
1
1
  import re
2
2
 
3
3
 
4
- def test_reseting_map_would_remove_from_save_queue(
4
+ def test_resetting_map_would_remove_from_save_queue(
5
5
  live_server, openmap, page, datalayer
6
6
  ):
7
7
  page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
@@ -0,0 +1,41 @@
1
+ from playwright.sync_api import expect
2
+
3
+
4
+ def test_reverse_search(live_server, page, tilelayer):
5
+ photon_response = {
6
+ "type": "FeatureCollection",
7
+ "features": [
8
+ {
9
+ "type": "Feature",
10
+ "properties": {
11
+ "osm_type": "N",
12
+ "osm_id": 5055853416,
13
+ "osm_key": "place",
14
+ "osm_value": "locality",
15
+ "type": "locality",
16
+ "postcode": "10200",
17
+ "countrycode": "FR",
18
+ "name": "Le Haut Sentier",
19
+ "country": "France",
20
+ "city": "Thors",
21
+ "state": "Grand Est",
22
+ "county": "Aube",
23
+ },
24
+ "geometry": {"type": "Point", "coordinates": [4.7995256, 48.2985251]},
25
+ }
26
+ ],
27
+ }
28
+ page.goto(f"{live_server.url}/en/map/new")
29
+
30
+ def handle_search(route):
31
+ route.fulfill(json=photon_response)
32
+
33
+ # Intercept the route
34
+ page.route(
35
+ "https://photon.komoot.io/reverse/?limit=1&lat=48.3&lon=4.8",
36
+ handle_search,
37
+ )
38
+ page.get_by_role("button", name="Search location").click()
39
+ page.get_by_role("searchbox", name="Type a place name or").fill("48.3 4.8")
40
+ expect(page.get_by_text("48.3 4.8")).to_be_visible()
41
+ expect(page.get_by_text("Le Haut Sentier")).to_be_visible()
@@ -8,7 +8,7 @@ pytestmark = pytest.mark.django_db
8
8
 
9
9
  def test_iframe_code_can_contain_datalayers(map, live_server, datalayer, page):
10
10
  page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
11
- textarea = page.locator(".umap-share-iframe")
11
+ textarea = page.get_by_label("Iframe")
12
12
  expect(textarea).to_be_visible()
13
13
  expect(textarea).to_have_text(re.compile('src="'))
14
14
  expect(textarea).to_have_text(re.compile('href="'))
@@ -28,7 +28,7 @@ def test_iframe_code_can_contain_datalayers(map, live_server, datalayer, page):
28
28
  def test_iframe_code_can_contain_feature(map, live_server, datalayer, page):
29
29
  page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
30
30
  page.locator(".icon-container").click()
31
- textarea = page.locator(".umap-share-iframe")
31
+ textarea = page.get_by_label("Iframe")
32
32
  expect(textarea).to_be_visible()
33
33
  expect(textarea).not_to_have_text(re.compile("feature=Here"))
34
34
  # Open options
@@ -39,7 +39,7 @@ def test_can_remove_user_from_team(live_server, map, user, user2, team, login):
39
39
  page.get_by_role("link", name="My teams").click()
40
40
  with page.expect_navigation():
41
41
  page.get_by_role("link", name="Edit").click()
42
- page.locator("li").filter(has_text="Averell").locator(".close").click()
42
+ page.locator("li").filter(has_text="Averell").locator(".icon-close").click()
43
43
  page.get_by_role("button", name="Save").click()
44
44
  assert Team.objects.count() == 1
45
45
  modified = Team.objects.first()
@@ -25,7 +25,9 @@ def setup_function():
25
25
 
26
26
 
27
27
  @pytest.mark.xdist_group(name="websockets")
28
- def test_websocket_connection_can_sync_markers(new_page, asgi_live_server, tilelayer):
28
+ def test_websocket_connection_can_sync_markers(
29
+ new_page, asgi_live_server, tilelayer, wait_for_loaded
30
+ ):
29
31
  map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
30
32
  map.settings["properties"]["syncEnabled"] = True
31
33
  map.save()
@@ -34,8 +36,10 @@ def test_websocket_connection_can_sync_markers(new_page, asgi_live_server, tilel
34
36
  # Create two tabs
35
37
  peerA = new_page("Page A")
36
38
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
39
+ wait_for_loaded(peerA)
37
40
  peerB = new_page("Page B")
38
41
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
42
+ wait_for_loaded(peerB)
39
43
 
40
44
  a_marker_pane = peerA.locator(".leaflet-marker-pane > div")
41
45
  b_marker_pane = peerB.locator(".leaflet-marker-pane > div")
@@ -49,6 +53,7 @@ def test_websocket_connection_can_sync_markers(new_page, asgi_live_server, tilel
49
53
 
50
54
  a_map_el = peerA.locator("#map")
51
55
  a_map_el.click(position={"x": 220, "y": 220})
56
+ peerA.wait_for_timeout(300) # Time for the panel animation to finish
52
57
  expect(a_marker_pane).to_have_count(1)
53
58
  expect(b_marker_pane).to_have_count(1)
54
59
  # Peer B should not be in state dirty
@@ -91,17 +96,21 @@ def test_websocket_connection_can_sync_markers(new_page, asgi_live_server, tilel
91
96
 
92
97
 
93
98
  @pytest.mark.xdist_group(name="websockets")
94
- def test_websocket_connection_can_sync_polygons(context, asgi_live_server, tilelayer):
99
+ def test_websocket_connection_can_sync_polygons(
100
+ new_page, asgi_live_server, tilelayer, wait_for_loaded
101
+ ):
95
102
  map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
96
103
  map.settings["properties"]["syncEnabled"] = True
97
104
  map.save()
98
105
  DataLayerFactory(map=map, data={})
99
106
 
100
107
  # Create two tabs
101
- peerA = context.new_page()
108
+ peerA = new_page("Page A")
102
109
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
103
- peerB = context.new_page()
110
+ wait_for_loaded(peerA)
111
+ peerB = new_page("Page B")
104
112
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
113
+ wait_for_loaded(peerB)
105
114
 
106
115
  b_map_el = peerB.locator("#map")
107
116
 
@@ -171,7 +180,7 @@ def test_websocket_connection_can_sync_polygons(context, asgi_live_server, tilel
171
180
 
172
181
  @pytest.mark.xdist_group(name="websockets")
173
182
  def test_websocket_connection_can_sync_map_properties(
174
- new_page, asgi_live_server, tilelayer
183
+ new_page, asgi_live_server, tilelayer, wait_for_loaded
175
184
  ):
176
185
  map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
177
186
  map.settings["properties"]["syncEnabled"] = True
@@ -181,8 +190,10 @@ def test_websocket_connection_can_sync_map_properties(
181
190
  # Create two tabs
182
191
  peerA = new_page()
183
192
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
193
+ wait_for_loaded(peerA)
184
194
  peerB = new_page()
185
195
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
196
+ wait_for_loaded(peerB)
186
197
 
187
198
  # Name change is synced
188
199
  peerA.get_by_role("button", name="Edit map name and caption").click()
@@ -205,7 +216,7 @@ def test_websocket_connection_can_sync_map_properties(
205
216
 
206
217
  @pytest.mark.xdist_group(name="websockets")
207
218
  def test_websocket_connection_can_sync_datalayer_properties(
208
- new_page, asgi_live_server, tilelayer
219
+ new_page, asgi_live_server, tilelayer, wait_for_loaded
209
220
  ):
210
221
  map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
211
222
  map.settings["properties"]["syncEnabled"] = True
@@ -215,8 +226,10 @@ def test_websocket_connection_can_sync_datalayer_properties(
215
226
  # Create two tabs
216
227
  peerA = new_page()
217
228
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
229
+ wait_for_loaded(peerA)
218
230
  peerB = new_page()
219
231
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
232
+ wait_for_loaded(peerB)
220
233
 
221
234
  # Layer addition, name and type are synced
222
235
  peerA.get_by_role("button", name="Manage layers").click()
@@ -236,7 +249,7 @@ def test_websocket_connection_can_sync_datalayer_properties(
236
249
 
237
250
  @pytest.mark.xdist_group(name="websockets")
238
251
  def test_websocket_connection_can_sync_cloned_polygons(
239
- context, asgi_live_server, tilelayer
252
+ new_page, asgi_live_server, tilelayer, wait_for_loaded
240
253
  ):
241
254
  map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
242
255
  map.settings["properties"]["syncEnabled"] = True
@@ -244,10 +257,12 @@ def test_websocket_connection_can_sync_cloned_polygons(
244
257
  DataLayerFactory(map=map, data={})
245
258
 
246
259
  # Create two tabs
247
- peerA = context.new_page()
260
+ peerA = new_page("Page A")
248
261
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
249
- peerB = context.new_page()
262
+ wait_for_loaded(peerA)
263
+ peerB = new_page("Page B")
250
264
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
265
+ wait_for_loaded(peerB)
251
266
 
252
267
  b_map_el = peerB.locator("#map")
253
268
 
@@ -297,7 +312,7 @@ def test_websocket_connection_can_sync_cloned_polygons(
297
312
 
298
313
  @pytest.mark.xdist_group(name="websockets")
299
314
  def test_websocket_connection_can_sync_late_joining_peer(
300
- new_page, asgi_live_server, tilelayer
315
+ new_page, asgi_live_server, tilelayer, wait_for_loaded
301
316
  ):
302
317
  map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
303
318
  map.settings["properties"]["syncEnabled"] = True
@@ -307,6 +322,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
307
322
  # Create first peer (A) and have it join immediately
308
323
  peerA = new_page("Page A")
309
324
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
325
+ wait_for_loaded(peerA)
310
326
 
311
327
  # Add a marker from peer A
312
328
  a_create_marker = peerA.get_by_title("Draw a marker")
@@ -315,6 +331,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
315
331
 
316
332
  a_map_el = peerA.locator("#map")
317
333
  a_map_el.click(position={"x": 220, "y": 220})
334
+ peerA.wait_for_timeout(300) # Time for the panel animation to finish
318
335
  peerA.locator("body").type("First marker")
319
336
  peerA.locator("body").press("Escape")
320
337
  peerA.wait_for_timeout(300)
@@ -333,6 +350,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
333
350
  # Now create peer B and have it join
334
351
  peerB = new_page("Page B")
335
352
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
353
+ wait_for_loaded(peerB)
336
354
 
337
355
  # Check if peer B has received all the updates
338
356
  b_marker_pane = peerB.locator(".leaflet-marker-pane > div")
@@ -357,7 +375,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
357
375
 
358
376
 
359
377
  @pytest.mark.xdist_group(name="websockets")
360
- def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
378
+ def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer, wait_for_loaded):
361
379
  map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
362
380
  map.settings["properties"]["syncEnabled"] = True
363
381
  map.save()
@@ -367,8 +385,10 @@ def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
367
385
  # Create two tabs
368
386
  peerA = new_page("Page A")
369
387
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
388
+ wait_for_loaded(peerA)
370
389
  peerB = new_page("Page B")
371
390
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
391
+ wait_for_loaded(peerB)
372
392
 
373
393
  # Create a new layer from peerA
374
394
  peerA.get_by_role("button", name="Manage layers").click()
@@ -398,7 +418,7 @@ def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
398
418
  peerA.locator("#map").click()
399
419
 
400
420
  # Make sure this new marker is in Layer 2 for peerB
401
- # Show features for this layer in the brower.
421
+ # Show features for this layer in the browser.
402
422
  peerB.locator("summary").filter(has_text="Layer 2").click()
403
423
  expect(peerB.locator("li").filter(has_text="Layer 2")).to_be_visible()
404
424
  peerB.locator(".panel.left").get_by_role("button", name="Show/hide layer").nth(
@@ -433,7 +453,9 @@ def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
433
453
 
434
454
 
435
455
  @pytest.mark.xdist_group(name="websockets")
436
- def test_should_sync_datalayers_delete(new_page, asgi_live_server, tilelayer):
456
+ def test_should_sync_datalayers_delete(
457
+ new_page, asgi_live_server, tilelayer, wait_for_loaded
458
+ ):
437
459
  map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
438
460
  map.settings["properties"]["syncEnabled"] = True
439
461
  map.save()
@@ -473,8 +495,10 @@ def test_should_sync_datalayers_delete(new_page, asgi_live_server, tilelayer):
473
495
  # Create two tabs
474
496
  peerA = new_page("Page A")
475
497
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
498
+ wait_for_loaded(peerA)
476
499
  peerB = new_page("Page B")
477
500
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
501
+ wait_for_loaded(peerB)
478
502
 
479
503
  peerA.get_by_role("button", name="Open browser").click()
480
504
  expect(peerA.locator(".panel").get_by_text("datalayer 1")).to_be_visible()
@@ -496,10 +520,13 @@ def test_should_sync_datalayers_delete(new_page, asgi_live_server, tilelayer):
496
520
 
497
521
 
498
522
  @pytest.mark.xdist_group(name="websockets")
499
- def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user):
523
+ def test_create_and_sync_map(
524
+ new_page, asgi_live_server, tilelayer, login, user, wait_for_loaded
525
+ ):
500
526
  # Create a syncable map with peerA
501
527
  peerA = login(user, prefix="Page A")
502
528
  peerA.goto(f"{asgi_live_server.url}/en/map/new/")
529
+ wait_for_loaded(peerA)
503
530
  peerA.get_by_role("button", name="Map advanced properties").click()
504
531
  expect(peerA.get_by_text("Real-time collaboration", exact=True)).to_be_hidden()
505
532
  with peerA.expect_response(re.compile("./map/create/.*")):
@@ -519,6 +546,7 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
519
546
  # Open map and go to edit mode with peer B
520
547
  peerB = new_page("Page B")
521
548
  peerB.goto(peerA.url)
549
+ wait_for_loaded(peerB)
522
550
  peerB.get_by_role("button", name="Edit").click()
523
551
 
524
552
  # Create a marker from peerA
@@ -529,8 +557,10 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
529
557
 
530
558
  # Add a marker from peer A
531
559
  peerA.get_by_role("button", name="Edit").click()
560
+ peerA.wait_for_timeout(300) # Time for the animation to finish
532
561
  peerA.get_by_title("Draw a marker").click()
533
562
  peerA.locator("#map").click(position={"x": 220, "y": 220})
563
+ peerA.wait_for_timeout(300) # Time for the panel animation to finish
534
564
  expect(markersA).to_have_count(1)
535
565
  expect(markersB).to_have_count(1)
536
566
 
@@ -556,6 +586,7 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
556
586
  # Add a marker from peer B
557
587
  peerB.get_by_title("Draw a marker").click()
558
588
  peerB.locator("#map").click(position={"x": 200, "y": 200})
589
+ peerA.wait_for_timeout(300) # Time for the panel animation to finish
559
590
  expect(markersB).to_have_count(2)
560
591
  expect(markersA).to_have_count(1)
561
592
  with peerB.expect_response(re.compile("./datalayer/update/.*")):
@@ -568,7 +599,9 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
568
599
 
569
600
 
570
601
  @pytest.mark.xdist_group(name="websockets")
571
- def test_saved_datalayer_are_not_duplicated(new_page, asgi_live_server, tilelayer):
602
+ def test_saved_datalayer_are_not_duplicated(
603
+ new_page, asgi_live_server, tilelayer, wait_for_loaded
604
+ ):
572
605
  map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
573
606
  map.settings["properties"]["syncEnabled"] = True
574
607
  map.save()
@@ -576,9 +609,10 @@ def test_saved_datalayer_are_not_duplicated(new_page, asgi_live_server, tilelaye
576
609
  # Create one tab
577
610
  peerA = new_page("Page A")
578
611
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
612
+ wait_for_loaded(peerA)
579
613
  # Create a new datalayer
580
614
  peerA.get_by_title("Manage layers").click()
581
- peerA.get_by_title("Add a layer").click()
615
+ peerA.get_by_role("button", name="Add a layer").click()
582
616
  peerA.locator("#map").click(position={"x": 220, "y": 220})
583
617
  # Save layer to the server, so now the datalayer exist on the server AND
584
618
  # is still in the live operations of peer A
@@ -588,6 +622,7 @@ def test_saved_datalayer_are_not_duplicated(new_page, asgi_live_server, tilelaye
588
622
  # Now load the map from another tab
589
623
  peerB = new_page("Page B")
590
624
  peerB.goto(peerA.url)
625
+ wait_for_loaded(peerB)
591
626
  peerB.get_by_role("button", name="Open browser").click()
592
627
  expect(peerB.get_by_text("Layer 1")).to_be_visible()
593
628
  peerB.get_by_role("button", name="Edit").click()
@@ -596,7 +631,9 @@ def test_saved_datalayer_are_not_duplicated(new_page, asgi_live_server, tilelaye
596
631
 
597
632
 
598
633
  @pytest.mark.xdist_group(name="websockets")
599
- def test_should_sync_saved_status(new_page, asgi_live_server, tilelayer):
634
+ def test_should_sync_saved_status(
635
+ new_page, asgi_live_server, tilelayer, wait_for_loaded
636
+ ):
600
637
  map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
601
638
  map.settings["properties"]["syncEnabled"] = True
602
639
  map.save()
@@ -604,8 +641,10 @@ def test_should_sync_saved_status(new_page, asgi_live_server, tilelayer):
604
641
  # Create two tabs
605
642
  peerA = new_page("Page A")
606
643
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
644
+ wait_for_loaded(peerA)
607
645
  peerB = new_page("Page B")
608
646
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
647
+ wait_for_loaded(peerB)
609
648
 
610
649
  # Create a new marker from peerA
611
650
  peerA.get_by_title("Draw a marker").click()
@@ -639,7 +678,9 @@ def test_should_sync_saved_status(new_page, asgi_live_server, tilelayer):
639
678
 
640
679
 
641
680
  @pytest.mark.xdist_group(name="websockets")
642
- def test_should_sync_line_on_escape(new_page, asgi_live_server, tilelayer):
681
+ def test_should_sync_line_on_escape(
682
+ new_page, asgi_live_server, tilelayer, wait_for_loaded
683
+ ):
643
684
  map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
644
685
  map.settings["properties"]["syncEnabled"] = True
645
686
  map.save()
@@ -647,8 +688,10 @@ def test_should_sync_line_on_escape(new_page, asgi_live_server, tilelayer):
647
688
  # Create two tabs
648
689
  peerA = new_page("Page A")
649
690
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
691
+ wait_for_loaded(peerA)
650
692
  peerB = new_page("Page B")
651
693
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
694
+ wait_for_loaded(peerB)
652
695
 
653
696
  # Create a new marker from peerA
654
697
  peerA.get_by_title("Draw a polyline").click()
@@ -662,7 +705,7 @@ def test_should_sync_line_on_escape(new_page, asgi_live_server, tilelayer):
662
705
 
663
706
  @pytest.mark.xdist_group(name="websockets")
664
707
  def test_should_sync_datalayer_clear(
665
- new_page, asgi_live_server, tilelayer, map, datalayer
708
+ new_page, asgi_live_server, tilelayer, map, datalayer, wait_for_loaded
666
709
  ):
667
710
  map.settings["properties"]["syncEnabled"] = True
668
711
  map.edit_status = Map.ANONYMOUS
@@ -671,8 +714,10 @@ def test_should_sync_datalayer_clear(
671
714
  # Create two tabs
672
715
  peerA = new_page("Page A")
673
716
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
717
+ wait_for_loaded(peerA)
674
718
  peerB = new_page("Page B")
675
719
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
720
+ wait_for_loaded(peerB)
676
721
  expect(peerA.locator(".leaflet-marker-icon")).to_have_count(1)
677
722
  expect(peerB.locator(".leaflet-marker-icon")).to_have_count(1)
678
723
 
@@ -691,7 +736,9 @@ def test_should_sync_datalayer_clear(
691
736
 
692
737
 
693
738
  @pytest.mark.xdist_group(name="websockets")
694
- def test_should_save_remote_dirty_datalayers(new_page, asgi_live_server, tilelayer):
739
+ def test_should_save_remote_dirty_datalayers(
740
+ new_page, asgi_live_server, tilelayer, wait_for_loaded
741
+ ):
695
742
  map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
696
743
  map.settings["properties"]["syncEnabled"] = True
697
744
  map.save()
@@ -701,8 +748,10 @@ def test_should_save_remote_dirty_datalayers(new_page, asgi_live_server, tilelay
701
748
  # Create two tabs
702
749
  peerA = new_page("Page A")
703
750
  peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
751
+ wait_for_loaded(peerA)
704
752
  peerB = new_page("Page B")
705
753
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
754
+ wait_for_loaded(peerB)
706
755
 
707
756
  # Create a new layer from peerA
708
757
  peerA.get_by_role("button", name="Manage layers").click()
@@ -68,7 +68,7 @@ def test_user_dashboard_display_user_team_maps(client, map, team, user, share_st
68
68
 
69
69
  def test_user_dashboard_display_user_maps_distinct(client, map):
70
70
  # cf https://github.com/umap-project/umap/issues/1325
71
- anonymap = MapFactory(name="Map witout owner should not appear")
71
+ anonymap = MapFactory(name="Map without owner should not appear")
72
72
  user1 = UserFactory(username="user1")
73
73
  user2 = UserFactory(username="user2")
74
74
  map.editors.add(user1)
@@ -32,9 +32,9 @@ def test_collectstatic_ran_successfully_with_hashes(settings, staticfiles):
32
32
  assert "hash" in json_manifest.keys()
33
33
  assert "umap/base.css" in json_manifest["paths"]
34
34
  # Hash + the dot ("umap/base.<hash>.css").
35
- md5_hash_lenght = 12 + 1
35
+ md5_hash_length = 12 + 1
36
36
  # The value of the manifest must contain the hash (length).
37
37
  assert (
38
38
  len(json_manifest["paths"]["umap/base.css"])
39
- == len("umap/base.css") + md5_hash_lenght
39
+ == len("umap/base.css") + md5_hash_length
40
40
  )
umap/tests/test_utils.py CHANGED
@@ -1,3 +1,4 @@
1
+ import stat
1
2
  from pathlib import Path
2
3
 
3
4
  import pytest
@@ -5,7 +6,8 @@ import pytest
5
6
  from umap.utils import gzip_file, normalize_string
6
7
 
7
8
 
8
- def test_gzip_file():
9
+ def test_gzip_file(settings):
10
+ settings.FILE_UPLOAD_PERMISSIONS = 0o666
9
11
  # Let's use any old file so we can check that the date of the gzip file is set.
10
12
  src = Path(__file__).parent / "settings.py"
11
13
  dest = Path("/tmp/test_settings.py.gz")
@@ -14,6 +16,7 @@ def test_gzip_file():
14
16
  dest_stat = dest.stat()
15
17
  dest.unlink()
16
18
  assert src_stat.st_mtime == dest_stat.st_mtime
19
+ assert stat.filemode(dest_stat.st_mode) == "-rw-rw-rw-"
17
20
 
18
21
 
19
22
  @pytest.mark.parametrize(
umap/tests/test_views.py CHANGED
@@ -73,7 +73,7 @@ def test_POST_raises():
73
73
  validate_url(request)
74
74
 
75
75
 
76
- def test_unkown_domain_raises():
76
+ def test_unknown_domain_raises():
77
77
  request = get("http://xlkjdkjsdlkjfd.com")
78
78
  with pytest.raises(AssertionError):
79
79
  validate_url(request)
umap/utils.py CHANGED
@@ -80,7 +80,7 @@ def get_uri_template(urlname, args=None, prefix="", module=None):
80
80
  result, params = possibility[0]
81
81
  return _convert(result, params)
82
82
  else:
83
- # If there are optionnal arguments passed, use them to try to find
83
+ # If there are optional arguments passed, use them to try to find
84
84
  # the correct pattern.
85
85
  # First, we need to build a list with all the arguments
86
86
  seen_params = []
@@ -150,6 +150,7 @@ def gzip_file(from_path, to_path):
150
150
  with gzip.open(to_path, "wb") as f_out:
151
151
  f_out.writelines(f_in)
152
152
  os.utime(to_path, ns=(stat.st_mtime_ns, stat.st_mtime_ns))
153
+ os.chmod(to_path, settings.FILE_UPLOAD_PERMISSIONS)
153
154
 
154
155
 
155
156
  def is_ajax(request):
@@ -226,5 +227,5 @@ def collect_pictograms():
226
227
 
227
228
 
228
229
  def normalize_string(s):
229
- n = unicodedata.normalize("NFKD", s)
230
+ n = unicodedata.normalize("NFKD", str(s))
230
231
  return "".join([c for c in n if not unicodedata.combining(c)]).lower()