umap-project 2.8.1__py3-none-any.whl → 2.9.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.
- umap/__init__.py +1 -1
- umap/admin.py +15 -2
- umap/asgi.py +12 -7
- umap/context_processors.py +1 -0
- umap/locale/br/LC_MESSAGES/django.mo +0 -0
- umap/locale/br/LC_MESSAGES/django.po +111 -67
- umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- umap/locale/cs_CZ/LC_MESSAGES/django.po +110 -66
- umap/locale/el/LC_MESSAGES/django.mo +0 -0
- umap/locale/el/LC_MESSAGES/django.po +129 -85
- umap/locale/en/LC_MESSAGES/django.po +103 -60
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +114 -69
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +105 -61
- umap/locale/gl/LC_MESSAGES/django.mo +0 -0
- umap/locale/gl/LC_MESSAGES/django.po +216 -171
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +10 -10
- umap/locale/it/LC_MESSAGES/django.mo +0 -0
- umap/locale/it/LC_MESSAGES/django.po +142 -98
- umap/locale/nl/LC_MESSAGES/django.mo +0 -0
- umap/locale/nl/LC_MESSAGES/django.po +196 -151
- umap/locale/pt/LC_MESSAGES/django.mo +0 -0
- umap/locale/pt/LC_MESSAGES/django.po +115 -71
- umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
- umap/locale/zh_TW/LC_MESSAGES/django.po +109 -65
- umap/management/commands/empty_trash.py +12 -1
- umap/migrations/0026_datalayer_modified_at_datalayer_share_status.py +26 -0
- umap/models.py +43 -13
- umap/settings/base.py +5 -2
- umap/static/umap/base.css +5 -2
- umap/static/umap/content.css +2 -22
- umap/static/umap/css/bar.css +39 -10
- umap/static/umap/css/contextmenu.css +14 -2
- umap/static/umap/css/form.css +33 -39
- umap/static/umap/css/icon.css +47 -5
- umap/static/umap/css/panel.css +20 -2
- umap/static/umap/css/popup.css +0 -1
- umap/static/umap/css/tooltip.css +33 -31
- umap/static/umap/img/16-white.svg +5 -3
- umap/static/umap/img/16.svg +1 -1
- umap/static/umap/img/24-white.svg +17 -16
- umap/static/umap/img/24.svg +29 -18
- umap/static/umap/img/providers/bitbucket.png +0 -0
- umap/static/umap/img/providers/github.png +0 -0
- umap/static/umap/img/providers/keycloak.png +0 -0
- umap/static/umap/img/providers/openstreetmap-oauth2.png +0 -0
- umap/static/umap/img/providers/twitter-oauth2.png +0 -0
- umap/static/umap/img/source/16-white.svg +6 -4
- umap/static/umap/img/source/16.svg +1 -1
- umap/static/umap/img/source/24-white.svg +20 -18
- umap/static/umap/img/source/24.svg +30 -19
- umap/static/umap/js/components/alerts/alert.js +4 -1
- umap/static/umap/js/modules/browser.js +8 -8
- umap/static/umap/js/modules/caption.js +30 -7
- umap/static/umap/js/modules/data/features.js +101 -56
- umap/static/umap/js/modules/data/layer.js +108 -83
- umap/static/umap/js/modules/form/builder.js +242 -0
- umap/static/umap/js/modules/form/fields.js +1346 -0
- umap/static/umap/js/modules/formatter.js +9 -8
- umap/static/umap/js/modules/help.js +20 -24
- umap/static/umap/js/modules/importer.js +6 -3
- umap/static/umap/js/modules/permissions.js +11 -6
- umap/static/umap/js/modules/rendering/icon.js +5 -1
- umap/static/umap/js/modules/rendering/layers/classified.js +12 -8
- umap/static/umap/js/modules/rendering/layers/cluster.js +11 -1
- umap/static/umap/js/modules/rendering/map.js +1 -23
- umap/static/umap/js/modules/rendering/ui.js +20 -38
- umap/static/umap/js/modules/rules.js +3 -2
- umap/static/umap/js/modules/saving.js +5 -0
- umap/static/umap/js/modules/schema.js +8 -6
- umap/static/umap/js/modules/share.js +3 -3
- umap/static/umap/js/modules/sync/engine.js +56 -26
- umap/static/umap/js/modules/sync/updaters.js +15 -6
- umap/static/umap/js/modules/sync/websocket.js +50 -37
- umap/static/umap/js/modules/tableeditor.js +3 -2
- umap/static/umap/js/modules/ui/bar.js +101 -9
- umap/static/umap/js/modules/ui/base.js +7 -24
- umap/static/umap/js/modules/ui/contextmenu.js +9 -2
- umap/static/umap/js/modules/ui/panel.js +5 -1
- umap/static/umap/js/modules/ui/tooltip.js +19 -11
- umap/static/umap/js/modules/umap.js +124 -71
- umap/static/umap/js/modules/utils.js +196 -12
- umap/static/umap/js/umap.controls.js +12 -354
- umap/static/umap/locale/am_ET.js +17 -5
- umap/static/umap/locale/am_ET.json +17 -5
- umap/static/umap/locale/ar.js +17 -5
- umap/static/umap/locale/ar.json +17 -5
- umap/static/umap/locale/ast.js +17 -5
- umap/static/umap/locale/ast.json +17 -5
- umap/static/umap/locale/bg.js +17 -5
- umap/static/umap/locale/bg.json +17 -5
- umap/static/umap/locale/br.js +33 -20
- umap/static/umap/locale/br.json +33 -20
- umap/static/umap/locale/ca.js +17 -5
- umap/static/umap/locale/ca.json +17 -5
- umap/static/umap/locale/cs_CZ.js +15 -5
- umap/static/umap/locale/cs_CZ.json +15 -5
- umap/static/umap/locale/da.js +17 -5
- umap/static/umap/locale/da.json +17 -5
- umap/static/umap/locale/de.js +17 -5
- umap/static/umap/locale/de.json +17 -5
- umap/static/umap/locale/el.js +63 -51
- umap/static/umap/locale/el.json +63 -51
- umap/static/umap/locale/en.js +15 -5
- umap/static/umap/locale/en.json +15 -5
- umap/static/umap/locale/en_US.json +17 -5
- umap/static/umap/locale/es.js +25 -13
- umap/static/umap/locale/es.json +25 -13
- umap/static/umap/locale/et.js +17 -5
- umap/static/umap/locale/et.json +17 -5
- umap/static/umap/locale/eu.js +17 -5
- umap/static/umap/locale/eu.json +17 -5
- umap/static/umap/locale/fa_IR.js +17 -5
- umap/static/umap/locale/fa_IR.json +17 -5
- umap/static/umap/locale/fi.js +17 -5
- umap/static/umap/locale/fi.json +17 -5
- umap/static/umap/locale/fr.js +16 -6
- umap/static/umap/locale/fr.json +16 -6
- umap/static/umap/locale/gl.js +357 -345
- umap/static/umap/locale/gl.json +357 -345
- umap/static/umap/locale/he.js +17 -5
- umap/static/umap/locale/he.json +17 -5
- umap/static/umap/locale/hr.js +17 -5
- umap/static/umap/locale/hr.json +17 -5
- umap/static/umap/locale/hu.js +39 -27
- umap/static/umap/locale/hu.json +39 -27
- umap/static/umap/locale/id.js +17 -5
- umap/static/umap/locale/id.json +17 -5
- umap/static/umap/locale/is.js +17 -5
- umap/static/umap/locale/is.json +17 -5
- umap/static/umap/locale/it.js +125 -113
- umap/static/umap/locale/it.json +125 -113
- umap/static/umap/locale/ja.js +17 -5
- umap/static/umap/locale/ja.json +17 -5
- umap/static/umap/locale/ko.js +17 -5
- umap/static/umap/locale/ko.json +17 -5
- umap/static/umap/locale/lt.js +17 -5
- umap/static/umap/locale/lt.json +17 -5
- umap/static/umap/locale/ms.js +17 -5
- umap/static/umap/locale/ms.json +17 -5
- umap/static/umap/locale/nl.js +132 -119
- umap/static/umap/locale/nl.json +132 -119
- umap/static/umap/locale/no.js +17 -5
- umap/static/umap/locale/no.json +17 -5
- umap/static/umap/locale/pl.js +17 -5
- umap/static/umap/locale/pl.json +17 -5
- umap/static/umap/locale/pl_PL.json +17 -5
- umap/static/umap/locale/pt.js +38 -25
- umap/static/umap/locale/pt.json +38 -25
- umap/static/umap/locale/pt_BR.js +17 -5
- umap/static/umap/locale/pt_BR.json +17 -5
- umap/static/umap/locale/pt_PT.js +17 -5
- umap/static/umap/locale/pt_PT.json +17 -5
- umap/static/umap/locale/ro.js +17 -5
- umap/static/umap/locale/ro.json +17 -5
- umap/static/umap/locale/ru.js +17 -5
- umap/static/umap/locale/ru.json +17 -5
- umap/static/umap/locale/sk_SK.js +17 -5
- umap/static/umap/locale/sk_SK.json +17 -5
- umap/static/umap/locale/sl.js +17 -5
- umap/static/umap/locale/sl.json +17 -5
- umap/static/umap/locale/sr.js +17 -5
- umap/static/umap/locale/sr.json +17 -5
- umap/static/umap/locale/sv.js +17 -5
- umap/static/umap/locale/sv.json +17 -5
- umap/static/umap/locale/th_TH.js +17 -5
- umap/static/umap/locale/th_TH.json +17 -5
- umap/static/umap/locale/tr.js +17 -5
- umap/static/umap/locale/tr.json +17 -5
- umap/static/umap/locale/uk_UA.js +17 -5
- umap/static/umap/locale/uk_UA.json +17 -5
- umap/static/umap/locale/vi.js +17 -5
- umap/static/umap/locale/vi.json +17 -5
- umap/static/umap/locale/vi_VN.json +17 -5
- umap/static/umap/locale/zh.js +17 -5
- umap/static/umap/locale/zh.json +17 -5
- umap/static/umap/locale/zh_CN.json +17 -5
- umap/static/umap/locale/zh_TW.Big5.json +17 -5
- umap/static/umap/locale/zh_TW.js +15 -5
- umap/static/umap/locale/zh_TW.json +15 -5
- umap/static/umap/map.css +29 -76
- umap/static/umap/nav.css +6 -3
- umap/static/umap/unittests/utils.js +14 -0
- umap/static/umap/vars.css +3 -0
- umap/static/umap/vendors/dompurify/purify.es.js +138 -354
- umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
- umap/static/umap/vendors/editable/Leaflet.Editable.js +1 -0
- umap/sync/__init__.py +0 -0
- umap/sync/app.py +187 -0
- umap/sync/payloads.py +56 -0
- umap/templates/auth/user_detail.html +4 -0
- umap/templates/auth/user_form.html +9 -6
- umap/templates/auth/user_stars.html +4 -0
- umap/templates/base.html +1 -1
- umap/templates/registration/login.html +2 -5
- umap/templates/umap/about.html +5 -0
- umap/templates/umap/about_summary.html +2 -2
- umap/templates/umap/components/provider.html +8 -0
- umap/templates/umap/content_footer.html +1 -1
- umap/templates/umap/css.html +0 -2
- umap/templates/umap/js.html +0 -4
- umap/templates/umap/map_detail.html +1 -1
- umap/templates/umap/password_change.html +4 -0
- umap/templates/umap/password_change_done.html +4 -0
- umap/templates/umap/search.html +4 -0
- umap/templates/umap/search_bar.html +1 -0
- umap/templates/umap/team_confirm_delete.html +4 -0
- umap/templates/umap/team_detail.html +4 -0
- umap/templates/umap/team_form.html +4 -0
- umap/templates/umap/user_dashboard.html +1 -1
- umap/templates/umap/user_teams.html +4 -0
- umap/tests/base.py +3 -1
- umap/tests/integration/conftest.py +16 -23
- umap/tests/integration/test_anonymous_owned_map.py +2 -2
- umap/tests/integration/test_basics.py +4 -7
- umap/tests/integration/test_caption.py +1 -0
- umap/tests/integration/test_categorized_layer.py +4 -8
- umap/tests/integration/test_choropleth.py +1 -1
- umap/tests/integration/test_conditional_rules.py +3 -3
- umap/tests/integration/test_draw_polygon.py +14 -22
- umap/tests/integration/test_draw_polyline.py +6 -14
- umap/tests/integration/test_edit_datalayer.py +11 -11
- umap/tests/integration/test_edit_map.py +30 -4
- umap/tests/integration/test_edit_marker.py +5 -5
- umap/tests/integration/test_edit_polygon.py +6 -6
- umap/tests/integration/test_features_id_generation.py +2 -6
- umap/tests/integration/test_import.py +115 -29
- umap/tests/integration/test_optimistic_merge.py +1 -0
- umap/tests/integration/test_owned_map.py +1 -1
- umap/tests/integration/test_picto.py +8 -8
- umap/tests/integration/test_save.py +3 -2
- umap/tests/integration/test_star.py +13 -9
- umap/tests/integration/test_tableeditor.py +8 -7
- umap/tests/integration/test_view_marker.py +10 -0
- umap/tests/integration/test_websocket_sync.py +239 -64
- umap/tests/settings.py +2 -0
- umap/tests/test_datalayer.py +2 -3
- umap/tests/test_datalayer_views.py +20 -1
- umap/tests/test_empty_trash.py +10 -3
- umap/tests/test_map_views.py +11 -0
- umap/utils.py +27 -11
- umap/views.py +37 -6
- {umap_project-2.8.1.dist-info → umap_project-2.9.0.dist-info}/METADATA +22 -22
- {umap_project-2.8.1.dist-info → umap_project-2.9.0.dist-info}/RECORD +249 -250
- {umap_project-2.8.1.dist-info → umap_project-2.9.0.dist-info}/WHEEL +1 -1
- umap/management/commands/run_websocket_server.py +0 -23
- umap/settings/local_s3.py +0 -45
- umap/static/umap/bitbucket.png +0 -0
- umap/static/umap/github.png +0 -0
- umap/static/umap/js/umap.forms.js +0 -1242
- umap/static/umap/keycloak.png +0 -0
- umap/static/umap/openstreetmap.png +0 -0
- umap/static/umap/twitter.png +0 -0
- umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +0 -468
- umap/static/umap/vendors/toolbar/leaflet.toolbar.css +0 -1
- umap/static/umap/vendors/toolbar/leaflet.toolbar.js +0 -1
- umap/tests/test_websocket_server.py +0 -22
- umap/websocket_server.py +0 -202
- {umap_project-2.8.1.dist-info → umap_project-2.9.0.dist-info}/entry_points.txt +0 -0
- {umap_project-2.8.1.dist-info → umap_project-2.9.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import re
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
+
import redis
|
|
5
|
+
from django.conf import settings
|
|
4
6
|
from playwright.sync_api import expect
|
|
5
7
|
|
|
6
8
|
from umap.models import DataLayer, Map
|
|
@@ -9,11 +11,21 @@ from ..base import DataLayerFactory, MapFactory
|
|
|
9
11
|
|
|
10
12
|
DATALAYER_UPDATE = re.compile(r".*/datalayer/update/.*")
|
|
11
13
|
|
|
14
|
+
pytestmark = pytest.mark.django_db
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def setup_function():
|
|
18
|
+
# Sync client to prevent headache with pytest / pytest-asyncio and async
|
|
19
|
+
client = redis.from_url(settings.REDIS_URL)
|
|
20
|
+
# Make sure there are no dead peers in the Redis hash, otherwise asking for
|
|
21
|
+
# operations from another peer may never be answered
|
|
22
|
+
# FIXME this should not happen in an ideal world
|
|
23
|
+
assert client.connection_pool.connection_kwargs["db"] == 15
|
|
24
|
+
client.flushdb()
|
|
25
|
+
|
|
12
26
|
|
|
13
27
|
@pytest.mark.xdist_group(name="websockets")
|
|
14
|
-
def test_websocket_connection_can_sync_markers(
|
|
15
|
-
new_page, live_server, websocket_server, tilelayer
|
|
16
|
-
):
|
|
28
|
+
def test_websocket_connection_can_sync_markers(new_page, asgi_live_server, tilelayer):
|
|
17
29
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
18
30
|
map.settings["properties"]["syncEnabled"] = True
|
|
19
31
|
map.save()
|
|
@@ -21,9 +33,9 @@ def test_websocket_connection_can_sync_markers(
|
|
|
21
33
|
|
|
22
34
|
# Create two tabs
|
|
23
35
|
peerA = new_page("Page A")
|
|
24
|
-
peerA.goto(f"{
|
|
36
|
+
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
25
37
|
peerB = new_page("Page B")
|
|
26
|
-
peerB.goto(f"{
|
|
38
|
+
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
27
39
|
|
|
28
40
|
a_marker_pane = peerA.locator(".leaflet-marker-pane > div")
|
|
29
41
|
b_marker_pane = peerB.locator(".leaflet-marker-pane > div")
|
|
@@ -44,9 +56,10 @@ def test_websocket_connection_can_sync_markers(
|
|
|
44
56
|
expect(peerB.get_by_role("button", name="Cancel edits")).to_be_hidden()
|
|
45
57
|
peerA.locator("body").type("Synced name")
|
|
46
58
|
peerA.locator("body").press("Escape")
|
|
59
|
+
peerA.wait_for_timeout(300)
|
|
47
60
|
|
|
48
61
|
peerB.locator(".leaflet-marker-icon").first.click()
|
|
49
|
-
peerB.get_by_role("
|
|
62
|
+
peerB.get_by_role("button", name="Toggle edit mode (⇧+Click)").click()
|
|
50
63
|
expect(peerB.locator('input[name="name"]')).to_have_value("Synced name")
|
|
51
64
|
|
|
52
65
|
a_first_marker = peerA.locator("div:nth-child(4) > div:nth-child(2)").first
|
|
@@ -79,9 +92,7 @@ def test_websocket_connection_can_sync_markers(
|
|
|
79
92
|
|
|
80
93
|
|
|
81
94
|
@pytest.mark.xdist_group(name="websockets")
|
|
82
|
-
def test_websocket_connection_can_sync_polygons(
|
|
83
|
-
context, live_server, websocket_server, tilelayer
|
|
84
|
-
):
|
|
95
|
+
def test_websocket_connection_can_sync_polygons(context, asgi_live_server, tilelayer):
|
|
85
96
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
86
97
|
map.settings["properties"]["syncEnabled"] = True
|
|
87
98
|
map.save()
|
|
@@ -89,16 +100,14 @@ def test_websocket_connection_can_sync_polygons(
|
|
|
89
100
|
|
|
90
101
|
# Create two tabs
|
|
91
102
|
peerA = context.new_page()
|
|
92
|
-
peerA.goto(f"{
|
|
103
|
+
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
93
104
|
peerB = context.new_page()
|
|
94
|
-
peerB.goto(f"{
|
|
105
|
+
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
95
106
|
|
|
96
107
|
b_map_el = peerB.locator("#map")
|
|
97
108
|
|
|
98
109
|
# Click on the Draw a polygon button on a new map.
|
|
99
|
-
create_line = peerA.locator(".
|
|
100
|
-
"Draw a polygon"
|
|
101
|
-
)
|
|
110
|
+
create_line = peerA.locator(".umap-edit-bar ").get_by_title("Draw a polygon")
|
|
102
111
|
create_line.click()
|
|
103
112
|
|
|
104
113
|
a_polygons = peerA.locator(".leaflet-overlay-pane path[fill='DarkBlue']")
|
|
@@ -113,11 +122,11 @@ def test_websocket_connection_can_sync_polygons(
|
|
|
113
122
|
map.click(position={"x": 100, "y": 100})
|
|
114
123
|
map.click(position={"x": 100, "y": 100})
|
|
115
124
|
|
|
116
|
-
# It is created on peerA,
|
|
125
|
+
# It is created on peerA, and should be on peerB
|
|
117
126
|
expect(a_polygons).to_have_count(1)
|
|
118
|
-
expect(b_polygons).to_have_count(
|
|
127
|
+
expect(b_polygons).to_have_count(1)
|
|
119
128
|
|
|
120
|
-
# Escaping the edition
|
|
129
|
+
# Escaping the edition should not duplicate
|
|
121
130
|
peerA.keyboard.press("Escape")
|
|
122
131
|
expect(a_polygons).to_have_count(1)
|
|
123
132
|
expect(b_polygons).to_have_count(1)
|
|
@@ -130,7 +139,7 @@ def test_websocket_connection_can_sync_polygons(
|
|
|
130
139
|
assert b_polygon_bbox_t1 == a_polygon_bbox_t1
|
|
131
140
|
|
|
132
141
|
b_polygon.click()
|
|
133
|
-
peerB.get_by_role("
|
|
142
|
+
peerB.get_by_role("button", name="Toggle edit mode (⇧+Click)").click()
|
|
134
143
|
|
|
135
144
|
edited_vertex = peerB.locator("div:nth-child(6)").first
|
|
136
145
|
edited_vertex.drag_to(b_map_el, target_position={"x": 233, "y": 126})
|
|
@@ -144,7 +153,7 @@ def test_websocket_connection_can_sync_polygons(
|
|
|
144
153
|
|
|
145
154
|
# Move the polygon on peer B and check it moved also on peer A
|
|
146
155
|
b_polygon.click()
|
|
147
|
-
peerB.get_by_role("
|
|
156
|
+
peerB.get_by_role("button", name="Toggle edit mode (⇧+Click)").click()
|
|
148
157
|
|
|
149
158
|
b_polygon.drag_to(b_map_el, target_position={"x": 400, "y": 400})
|
|
150
159
|
peerB.keyboard.press("Escape")
|
|
@@ -164,7 +173,7 @@ def test_websocket_connection_can_sync_polygons(
|
|
|
164
173
|
|
|
165
174
|
@pytest.mark.xdist_group(name="websockets")
|
|
166
175
|
def test_websocket_connection_can_sync_map_properties(
|
|
167
|
-
new_page,
|
|
176
|
+
new_page, asgi_live_server, tilelayer
|
|
168
177
|
):
|
|
169
178
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
170
179
|
map.settings["properties"]["syncEnabled"] = True
|
|
@@ -173,30 +182,32 @@ def test_websocket_connection_can_sync_map_properties(
|
|
|
173
182
|
|
|
174
183
|
# Create two tabs
|
|
175
184
|
peerA = new_page()
|
|
176
|
-
peerA.goto(f"{
|
|
185
|
+
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
177
186
|
peerB = new_page()
|
|
178
|
-
peerB.goto(f"{
|
|
187
|
+
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
179
188
|
|
|
180
189
|
# Name change is synced
|
|
181
|
-
peerA.get_by_role("
|
|
190
|
+
peerA.get_by_role("button", name="Edit map name and caption").click()
|
|
182
191
|
peerA.locator('input[name="name"]').click()
|
|
183
192
|
peerA.locator('input[name="name"]').fill("it syncs!")
|
|
184
193
|
|
|
185
194
|
expect(peerB.locator(".map-name").last).to_have_text("it syncs!")
|
|
186
195
|
|
|
187
196
|
# Zoom control is synced
|
|
188
|
-
peerB.get_by_role("
|
|
197
|
+
peerB.get_by_role("button", name="Map advanced properties").click()
|
|
189
198
|
peerB.locator("summary").filter(has_text="User interface options").click()
|
|
190
|
-
peerB.locator("div").filter(
|
|
191
|
-
has_text=re.compile(
|
|
192
|
-
)
|
|
199
|
+
switch = peerB.locator("div.formbox").filter(
|
|
200
|
+
has_text=re.compile("Display the zoom control")
|
|
201
|
+
)
|
|
202
|
+
expect(switch).to_be_visible()
|
|
203
|
+
switch.get_by_text("Never").click()
|
|
193
204
|
|
|
194
205
|
expect(peerA.locator(".leaflet-control-zoom")).to_be_hidden()
|
|
195
206
|
|
|
196
207
|
|
|
197
208
|
@pytest.mark.xdist_group(name="websockets")
|
|
198
209
|
def test_websocket_connection_can_sync_datalayer_properties(
|
|
199
|
-
new_page,
|
|
210
|
+
new_page, asgi_live_server, tilelayer
|
|
200
211
|
):
|
|
201
212
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
202
213
|
map.settings["properties"]["syncEnabled"] = True
|
|
@@ -205,19 +216,19 @@ def test_websocket_connection_can_sync_datalayer_properties(
|
|
|
205
216
|
|
|
206
217
|
# Create two tabs
|
|
207
218
|
peerA = new_page()
|
|
208
|
-
peerA.goto(f"{
|
|
219
|
+
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
209
220
|
peerB = new_page()
|
|
210
|
-
peerB.goto(f"{
|
|
221
|
+
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
211
222
|
|
|
212
223
|
# Layer addition, name and type are synced
|
|
213
|
-
peerA.get_by_role("
|
|
224
|
+
peerA.get_by_role("button", name="Manage layers").click()
|
|
214
225
|
peerA.get_by_role("button", name="Add a layer").click()
|
|
215
226
|
peerA.locator('input[name="name"]').click()
|
|
216
227
|
peerA.locator('input[name="name"]').fill("synced layer!")
|
|
217
228
|
peerA.get_by_role("combobox").select_option("Choropleth")
|
|
218
229
|
peerA.locator("body").press("Escape")
|
|
219
230
|
|
|
220
|
-
peerB.get_by_role("
|
|
231
|
+
peerB.get_by_role("button", name="Manage layers").click()
|
|
221
232
|
peerB.locator(".panel.right").get_by_role("button", name="Edit").first.click()
|
|
222
233
|
expect(peerB.locator('input[name="name"]')).to_have_value("synced layer!")
|
|
223
234
|
expect(peerB.get_by_role("combobox")).to_have_value("Choropleth")
|
|
@@ -225,7 +236,7 @@ def test_websocket_connection_can_sync_datalayer_properties(
|
|
|
225
236
|
|
|
226
237
|
@pytest.mark.xdist_group(name="websockets")
|
|
227
238
|
def test_websocket_connection_can_sync_cloned_polygons(
|
|
228
|
-
context,
|
|
239
|
+
context, asgi_live_server, tilelayer
|
|
229
240
|
):
|
|
230
241
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
231
242
|
map.settings["properties"]["syncEnabled"] = True
|
|
@@ -234,16 +245,14 @@ def test_websocket_connection_can_sync_cloned_polygons(
|
|
|
234
245
|
|
|
235
246
|
# Create two tabs
|
|
236
247
|
peerA = context.new_page()
|
|
237
|
-
peerA.goto(f"{
|
|
248
|
+
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
238
249
|
peerB = context.new_page()
|
|
239
|
-
peerB.goto(f"{
|
|
250
|
+
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
240
251
|
|
|
241
252
|
b_map_el = peerB.locator("#map")
|
|
242
253
|
|
|
243
254
|
# Click on the Draw a polygon button on a new map.
|
|
244
|
-
create_line = peerA.locator(".
|
|
245
|
-
"Draw a polygon"
|
|
246
|
-
)
|
|
255
|
+
create_line = peerA.locator(".umap-edit-bar ").get_by_title("Draw a polygon")
|
|
247
256
|
create_line.click()
|
|
248
257
|
|
|
249
258
|
a_polygons = peerA.locator(".leaflet-overlay-pane path[fill='DarkBlue']")
|
|
@@ -278,7 +287,7 @@ def test_websocket_connection_can_sync_cloned_polygons(
|
|
|
278
287
|
peerB.locator("path").nth(1).drag_to(b_map_el, target_position={"x": 400, "y": 400})
|
|
279
288
|
peerB.locator("path").nth(1).click()
|
|
280
289
|
peerB.locator("summary").filter(has_text="Shape properties").click()
|
|
281
|
-
peerB.locator(".
|
|
290
|
+
peerB.locator(".umap-field-color button.define").first.click()
|
|
282
291
|
peerB.get_by_title("Orchid", exact=True).first.click()
|
|
283
292
|
peerB.locator("#map").press("Escape")
|
|
284
293
|
peerB.get_by_role("button", name="Save").click()
|
|
@@ -288,7 +297,7 @@ def test_websocket_connection_can_sync_cloned_polygons(
|
|
|
288
297
|
|
|
289
298
|
@pytest.mark.xdist_group(name="websockets")
|
|
290
299
|
def test_websocket_connection_can_sync_late_joining_peer(
|
|
291
|
-
new_page,
|
|
300
|
+
new_page, asgi_live_server, tilelayer
|
|
292
301
|
):
|
|
293
302
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
294
303
|
map.settings["properties"]["syncEnabled"] = True
|
|
@@ -297,7 +306,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
|
|
|
297
306
|
|
|
298
307
|
# Create first peer (A) and have it join immediately
|
|
299
308
|
peerA = new_page("Page A")
|
|
300
|
-
peerA.goto(f"{
|
|
309
|
+
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
301
310
|
|
|
302
311
|
# Add a marker from peer A
|
|
303
312
|
a_create_marker = peerA.get_by_title("Draw a marker")
|
|
@@ -308,11 +317,10 @@ def test_websocket_connection_can_sync_late_joining_peer(
|
|
|
308
317
|
a_map_el.click(position={"x": 220, "y": 220})
|
|
309
318
|
peerA.locator("body").type("First marker")
|
|
310
319
|
peerA.locator("body").press("Escape")
|
|
320
|
+
peerA.wait_for_timeout(300)
|
|
311
321
|
|
|
312
322
|
# Add a polygon from peer A
|
|
313
|
-
create_polygon = peerA.locator(".
|
|
314
|
-
"Draw a polygon"
|
|
315
|
-
)
|
|
323
|
+
create_polygon = peerA.locator(".umap-edit-bar ").get_by_title("Draw a polygon")
|
|
316
324
|
create_polygon.click()
|
|
317
325
|
|
|
318
326
|
a_map_el.click(position={"x": 200, "y": 200})
|
|
@@ -324,7 +332,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
|
|
|
324
332
|
|
|
325
333
|
# Now create peer B and have it join
|
|
326
334
|
peerB = new_page("Page B")
|
|
327
|
-
peerB.goto(f"{
|
|
335
|
+
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
328
336
|
|
|
329
337
|
# Check if peer B has received all the updates
|
|
330
338
|
b_marker_pane = peerB.locator(".leaflet-marker-pane > div")
|
|
@@ -335,7 +343,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
|
|
|
335
343
|
|
|
336
344
|
# Verify marker properties
|
|
337
345
|
peerB.locator(".leaflet-marker-icon").first.click()
|
|
338
|
-
peerB.get_by_role("
|
|
346
|
+
peerB.get_by_role("button", name="Toggle edit mode (⇧+Click)").click()
|
|
339
347
|
expect(peerB.locator('input[name="name"]')).to_have_value("First marker")
|
|
340
348
|
|
|
341
349
|
# Verify polygon exists (we've already checked the count)
|
|
@@ -349,7 +357,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
|
|
|
349
357
|
|
|
350
358
|
|
|
351
359
|
@pytest.mark.xdist_group(name="websockets")
|
|
352
|
-
def test_should_sync_datalayers(new_page,
|
|
360
|
+
def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
|
|
353
361
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
354
362
|
map.settings["properties"]["syncEnabled"] = True
|
|
355
363
|
map.save()
|
|
@@ -358,12 +366,12 @@ def test_should_sync_datalayers(new_page, live_server, websocket_server, tilelay
|
|
|
358
366
|
|
|
359
367
|
# Create two tabs
|
|
360
368
|
peerA = new_page("Page A")
|
|
361
|
-
peerA.goto(f"{
|
|
369
|
+
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
362
370
|
peerB = new_page("Page B")
|
|
363
|
-
peerB.goto(f"{
|
|
371
|
+
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
364
372
|
|
|
365
373
|
# Create a new layer from peerA
|
|
366
|
-
peerA.get_by_role("
|
|
374
|
+
peerA.get_by_role("button", name="Manage layers").click()
|
|
367
375
|
peerA.get_by_role("button", name="Add a layer").click()
|
|
368
376
|
|
|
369
377
|
# Check layer has been sync to peerB
|
|
@@ -371,7 +379,7 @@ def test_should_sync_datalayers(new_page, live_server, websocket_server, tilelay
|
|
|
371
379
|
expect(peerB.get_by_text("Layer 1")).to_be_visible()
|
|
372
380
|
|
|
373
381
|
# Draw a marker in layer 1 from peerA
|
|
374
|
-
peerA.get_by_role("
|
|
382
|
+
peerA.get_by_role("button", name="Draw a marker (Ctrl+M)").click()
|
|
375
383
|
peerA.locator("#map").click()
|
|
376
384
|
|
|
377
385
|
# Check marker is visible from peerB
|
|
@@ -384,20 +392,22 @@ def test_should_sync_datalayers(new_page, live_server, websocket_server, tilelay
|
|
|
384
392
|
assert DataLayer.objects.count() == 1
|
|
385
393
|
|
|
386
394
|
# Create another layer from peerA and draw a marker on it (without saving to server)
|
|
387
|
-
peerA.get_by_role("
|
|
395
|
+
peerA.get_by_role("button", name="Manage layers").click()
|
|
388
396
|
peerA.get_by_role("button", name="Add a layer").click()
|
|
389
|
-
peerA.get_by_role("
|
|
397
|
+
peerA.get_by_role("button", name="Draw a marker (Ctrl+M)").click()
|
|
390
398
|
peerA.locator("#map").click()
|
|
391
399
|
|
|
392
400
|
# Make sure this new marker is in Layer 2 for peerB
|
|
393
|
-
|
|
401
|
+
# Show features for this layer in the brower.
|
|
402
|
+
peerB.get_by_role("heading", name="Layer 2").locator(".datalayer-name").click()
|
|
403
|
+
expect(peerB.locator("li").filter(has_text="Layer 2")).to_be_visible()
|
|
394
404
|
peerB.locator(".panel.left").get_by_role("button", name="Show/hide layer").nth(
|
|
395
405
|
1
|
|
396
406
|
).click()
|
|
397
407
|
expect(peerB.locator(".leaflet-marker-icon")).to_be_visible()
|
|
398
408
|
|
|
399
409
|
# Now draw a marker from peerB
|
|
400
|
-
peerB.get_by_role("
|
|
410
|
+
peerB.get_by_role("button", name="Draw a marker (Ctrl+M)").click()
|
|
401
411
|
peerB.locator("#map").click()
|
|
402
412
|
peerB.locator('input[name="name"]').fill("marker from peerB")
|
|
403
413
|
|
|
@@ -413,26 +423,93 @@ def test_should_sync_datalayers(new_page, live_server, websocket_server, tilelay
|
|
|
413
423
|
1
|
|
414
424
|
).click()
|
|
415
425
|
|
|
416
|
-
#
|
|
417
|
-
|
|
418
|
-
|
|
426
|
+
# Peer A should not be in dirty state
|
|
427
|
+
expect(peerA.locator("body")).not_to_have_class(re.compile(".*umap-is-dirty.*"))
|
|
428
|
+
|
|
429
|
+
# Peer A should only have two markers
|
|
430
|
+
expect(peerA.locator(".leaflet-marker-icon")).to_have_count(2)
|
|
419
431
|
|
|
420
432
|
assert DataLayer.objects.count() == 2
|
|
421
433
|
|
|
422
434
|
|
|
423
435
|
@pytest.mark.xdist_group(name="websockets")
|
|
424
|
-
def
|
|
425
|
-
|
|
426
|
-
|
|
436
|
+
def test_should_sync_datalayers_delete(new_page, asgi_live_server, tilelayer):
|
|
437
|
+
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
438
|
+
map.settings["properties"]["syncEnabled"] = True
|
|
439
|
+
map.save()
|
|
440
|
+
data1 = {
|
|
441
|
+
"type": "FeatureCollection",
|
|
442
|
+
"features": [
|
|
443
|
+
{
|
|
444
|
+
"type": "Feature",
|
|
445
|
+
"properties": {
|
|
446
|
+
"name": "Point 1",
|
|
447
|
+
},
|
|
448
|
+
"geometry": {"type": "Point", "coordinates": [0.065918, 48.385442]},
|
|
449
|
+
},
|
|
450
|
+
],
|
|
451
|
+
"_umap_options": {
|
|
452
|
+
"name": "datalayer 1",
|
|
453
|
+
},
|
|
454
|
+
}
|
|
455
|
+
data2 = {
|
|
456
|
+
"type": "FeatureCollection",
|
|
457
|
+
"features": [
|
|
458
|
+
{
|
|
459
|
+
"type": "Feature",
|
|
460
|
+
"properties": {
|
|
461
|
+
"name": "Point 2",
|
|
462
|
+
},
|
|
463
|
+
"geometry": {"type": "Point", "coordinates": [3.55957, 49.767074]},
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
"_umap_options": {
|
|
467
|
+
"name": "datalayer 2",
|
|
468
|
+
},
|
|
469
|
+
}
|
|
470
|
+
DataLayerFactory(map=map, data=data1)
|
|
471
|
+
DataLayerFactory(map=map, data=data2)
|
|
472
|
+
|
|
473
|
+
# Create two tabs
|
|
474
|
+
peerA = new_page("Page A")
|
|
475
|
+
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
476
|
+
peerB = new_page("Page B")
|
|
477
|
+
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
478
|
+
|
|
479
|
+
peerA.get_by_role("button", name="Open browser").click()
|
|
480
|
+
expect(peerA.get_by_text("datalayer 1")).to_be_visible()
|
|
481
|
+
expect(peerA.get_by_text("datalayer 2")).to_be_visible()
|
|
482
|
+
peerB.get_by_role("button", name="Open browser").click()
|
|
483
|
+
expect(peerB.get_by_text("datalayer 1")).to_be_visible()
|
|
484
|
+
expect(peerB.get_by_text("datalayer 2")).to_be_visible()
|
|
485
|
+
|
|
486
|
+
# Delete "datalayer 2" in peerA
|
|
487
|
+
peerA.locator(".datalayer").get_by_role("button", name="Delete layer").first.click()
|
|
488
|
+
peerA.get_by_role("button", name="OK").click()
|
|
489
|
+
expect(peerA.get_by_text("datalayer 2")).to_be_hidden()
|
|
490
|
+
expect(peerB.get_by_text("datalayer 2")).to_be_hidden()
|
|
491
|
+
|
|
492
|
+
# Save delete to the server
|
|
493
|
+
with peerA.expect_response(re.compile(".*/datalayer/delete/.*")):
|
|
494
|
+
peerA.get_by_role("button", name="Save").click()
|
|
495
|
+
expect(peerA.get_by_text("datalayer 2")).to_be_hidden()
|
|
496
|
+
expect(peerB.get_by_text("datalayer 2")).to_be_hidden()
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
@pytest.mark.xdist_group(name="websockets")
|
|
500
|
+
def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user):
|
|
427
501
|
# Create a syncable map with peerA
|
|
428
502
|
peerA = login(user, prefix="Page A")
|
|
429
|
-
peerA.goto(f"{
|
|
503
|
+
peerA.goto(f"{asgi_live_server.url}/en/map/new/")
|
|
504
|
+
peerA.get_by_role("button", name="Map advanced properties").click()
|
|
505
|
+
expect(peerA.get_by_text("Real-time collaboration", exact=True)).to_be_hidden()
|
|
430
506
|
with peerA.expect_response(re.compile("./map/create/.*")):
|
|
431
507
|
peerA.get_by_role("button", name="Save Draft").click()
|
|
432
|
-
peerA.get_by_role("
|
|
508
|
+
peerA.get_by_role("button", name="Map advanced properties").click()
|
|
509
|
+
expect(peerA.get_by_text("Real-time collaboration", exact=True)).to_be_visible()
|
|
433
510
|
peerA.get_by_text("Real-time collaboration", exact=True).click()
|
|
434
511
|
peerA.get_by_text("Enable real-time").click()
|
|
435
|
-
peerA.get_by_role("
|
|
512
|
+
peerA.get_by_role("button", name="Update permissions and editors").click()
|
|
436
513
|
peerA.locator('select[name="share_status"]').select_option(str(Map.PUBLIC))
|
|
437
514
|
with peerA.expect_response(re.compile("./update/settings/.*")):
|
|
438
515
|
peerA.get_by_role("button", name="Save").click()
|
|
@@ -458,6 +535,11 @@ def test_create_and_sync_map(
|
|
|
458
535
|
expect(markersA).to_have_count(1)
|
|
459
536
|
expect(markersB).to_have_count(1)
|
|
460
537
|
|
|
538
|
+
# Make sure only one layer has been created on peer B
|
|
539
|
+
peerB.get_by_role("button", name="Open browser").click()
|
|
540
|
+
expect(peerB.locator("h5").get_by_text("Layer 1")).to_be_visible()
|
|
541
|
+
peerB.get_by_role("button", name="Close").click()
|
|
542
|
+
|
|
461
543
|
# Save and quit edit mode again
|
|
462
544
|
with peerA.expect_response(re.compile("./datalayer/create/.*")):
|
|
463
545
|
peerA.get_by_role("button", name="Save").click()
|
|
@@ -484,3 +566,96 @@ def test_create_and_sync_map(
|
|
|
484
566
|
peerA.get_by_role("button", name="Edit").click()
|
|
485
567
|
expect(markersA).to_have_count(2)
|
|
486
568
|
expect(markersB).to_have_count(2)
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
@pytest.mark.xdist_group(name="websockets")
|
|
572
|
+
def test_saved_datalayer_are_not_duplicated(new_page, asgi_live_server, tilelayer):
|
|
573
|
+
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
574
|
+
map.settings["properties"]["syncEnabled"] = True
|
|
575
|
+
map.save()
|
|
576
|
+
|
|
577
|
+
# Create one tab
|
|
578
|
+
peerA = new_page("Page A")
|
|
579
|
+
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
580
|
+
# Create a new datalayer
|
|
581
|
+
peerA.get_by_title("Manage layers").click()
|
|
582
|
+
peerA.get_by_title("Add a layer").click()
|
|
583
|
+
peerA.locator("#map").click(position={"x": 220, "y": 220})
|
|
584
|
+
# Save layer to the server, so now the datalayer exist on the server AND
|
|
585
|
+
# is still in the live operations of peer A
|
|
586
|
+
with peerA.expect_response(re.compile(".*/datalayer/create/.*")):
|
|
587
|
+
peerA.get_by_role("button", name="Save").click()
|
|
588
|
+
|
|
589
|
+
# Now load the map from another tab
|
|
590
|
+
peerB = new_page("Page B")
|
|
591
|
+
peerB.goto(peerA.url)
|
|
592
|
+
peerB.get_by_role("button", name="Open browser").click()
|
|
593
|
+
expect(peerB.get_by_text("Layer 1")).to_be_visible()
|
|
594
|
+
peerB.get_by_role("button", name="Edit").click()
|
|
595
|
+
peerA.wait_for_timeout(300) # Let the synchro roll on.
|
|
596
|
+
expect(peerB.get_by_text("Layer 1")).to_be_visible()
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
@pytest.mark.xdist_group(name="websockets")
|
|
600
|
+
def test_should_sync_saved_status(new_page, asgi_live_server, tilelayer):
|
|
601
|
+
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
602
|
+
map.settings["properties"]["syncEnabled"] = True
|
|
603
|
+
map.save()
|
|
604
|
+
|
|
605
|
+
# Create two tabs
|
|
606
|
+
peerA = new_page("Page A")
|
|
607
|
+
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
608
|
+
peerB = new_page("Page B")
|
|
609
|
+
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
610
|
+
|
|
611
|
+
# Create a new marker from peerA
|
|
612
|
+
peerA.get_by_title("Draw a marker").click()
|
|
613
|
+
peerA.locator("#map").click(position={"x": 220, "y": 220})
|
|
614
|
+
|
|
615
|
+
# Peer A should be in dirty state
|
|
616
|
+
expect(peerA.locator("body")).to_have_class(re.compile(".*umap-is-dirty.*"))
|
|
617
|
+
|
|
618
|
+
# Peer B should not be in dirty state
|
|
619
|
+
expect(peerB.locator("body")).not_to_have_class(re.compile(".*umap-is-dirty.*"))
|
|
620
|
+
|
|
621
|
+
# Create a new marker from peerB
|
|
622
|
+
peerB.get_by_title("Draw a marker").click()
|
|
623
|
+
peerB.locator("#map").click(position={"x": 200, "y": 250})
|
|
624
|
+
|
|
625
|
+
# Peer B should be in dirty state
|
|
626
|
+
expect(peerB.locator("body")).to_have_class(re.compile(".*umap-is-dirty.*"))
|
|
627
|
+
|
|
628
|
+
# Peer A should still be in dirty state
|
|
629
|
+
expect(peerA.locator("body")).to_have_class(re.compile(".*umap-is-dirty.*"))
|
|
630
|
+
|
|
631
|
+
# Save layer to the server from peerA
|
|
632
|
+
with peerA.expect_response(re.compile(".*/datalayer/create/.*")):
|
|
633
|
+
peerA.get_by_role("button", name="Save").click()
|
|
634
|
+
|
|
635
|
+
# Peer B should not be in dirty state
|
|
636
|
+
expect(peerB.locator("body")).not_to_have_class(re.compile(".*umap-is-dirty.*"))
|
|
637
|
+
|
|
638
|
+
# Peer A should not be in dirty state
|
|
639
|
+
expect(peerA.locator("body")).not_to_have_class(re.compile(".*umap-is-dirty.*"))
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
@pytest.mark.xdist_group(name="websockets")
|
|
643
|
+
def test_should_sync_line_on_escape(new_page, asgi_live_server, tilelayer):
|
|
644
|
+
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
|
645
|
+
map.settings["properties"]["syncEnabled"] = True
|
|
646
|
+
map.save()
|
|
647
|
+
|
|
648
|
+
# Create two tabs
|
|
649
|
+
peerA = new_page("Page A")
|
|
650
|
+
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
651
|
+
peerB = new_page("Page B")
|
|
652
|
+
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
|
|
653
|
+
|
|
654
|
+
# Create a new marker from peerA
|
|
655
|
+
peerA.get_by_title("Draw a polyline").click()
|
|
656
|
+
peerA.locator("#map").click(position={"x": 220, "y": 220})
|
|
657
|
+
peerA.locator("#map").click(position={"x": 200, "y": 200})
|
|
658
|
+
peerA.locator("body").press("Escape")
|
|
659
|
+
|
|
660
|
+
expect(peerA.locator("path")).to_have_count(1)
|
|
661
|
+
expect(peerB.locator("path")).to_have_count(1)
|
umap/tests/settings.py
CHANGED
umap/tests/test_datalayer.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import tempfile
|
|
2
1
|
from pathlib import Path
|
|
3
2
|
|
|
4
3
|
import pytest
|
|
@@ -290,5 +289,5 @@ def test_should_remove_all_versions_on_delete(map, settings):
|
|
|
290
289
|
datalayer.geojson.storage.save(root / f"{path}.gz", ContentFile("{}"))
|
|
291
290
|
assert len(datalayer.geojson.storage.listdir(root)[1]) == 10 + before
|
|
292
291
|
datalayer.delete()
|
|
293
|
-
found = datalayer.geojson.storage.listdir(root)[1]
|
|
294
|
-
assert found ==
|
|
292
|
+
found = set(datalayer.geojson.storage.listdir(root)[1])
|
|
293
|
+
assert found == {other, f"{other}.gz"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from copy import deepcopy
|
|
3
|
+
from datetime import datetime, timedelta
|
|
3
4
|
from pathlib import Path
|
|
5
|
+
from unittest import mock
|
|
4
6
|
from uuid import uuid4
|
|
5
7
|
|
|
6
8
|
import pytest
|
|
@@ -156,11 +158,14 @@ def test_should_not_be_possible_to_update_with_wrong_map_id_in_url(
|
|
|
156
158
|
|
|
157
159
|
|
|
158
160
|
def test_delete(client, datalayer, map):
|
|
161
|
+
assert map.datalayers.count() == 1
|
|
159
162
|
url = reverse("datalayer_delete", args=(map.pk, datalayer.pk))
|
|
160
163
|
client.login(username=map.owner.username, password="123123")
|
|
161
164
|
response = client.post(url, {}, follow=True)
|
|
162
165
|
assert response.status_code == 200
|
|
163
|
-
assert
|
|
166
|
+
assert DataLayer.objects.filter(pk=datalayer.pk).count()
|
|
167
|
+
assert map.datalayers.count() == 0
|
|
168
|
+
assert DataLayer.objects.get(pk=datalayer.pk).share_status == DataLayer.DELETED
|
|
164
169
|
# Check that map has not been impacted
|
|
165
170
|
assert Map.objects.filter(pk=map.pk).exists()
|
|
166
171
|
# Test response is a json
|
|
@@ -621,3 +626,17 @@ def test_optimistic_merge_conflicting_change_raises(
|
|
|
621
626
|
modified_datalayer = DataLayer.objects.get(pk=datalayer.pk)
|
|
622
627
|
merged_features = json.load(modified_datalayer.geojson)["features"]
|
|
623
628
|
assert merged_features == client1_data["features"]
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
def test_saving_datalayer_should_change_map_last_modified(
|
|
632
|
+
client, datalayer, map, post_data
|
|
633
|
+
):
|
|
634
|
+
with mock.patch("django.utils.timezone.now") as mocked:
|
|
635
|
+
mocked.return_value = datetime.utcnow() - timedelta(days=8)
|
|
636
|
+
map.save() # Change last_modified to past
|
|
637
|
+
old_modified_at = map.modified_at.date()
|
|
638
|
+
url = reverse("datalayer_update", args=(map.pk, datalayer.pk))
|
|
639
|
+
client.login(username=map.owner.username, password="123123")
|
|
640
|
+
response = client.post(url, post_data, follow=True)
|
|
641
|
+
assert response.status_code == 200
|
|
642
|
+
assert Map.objects.get(pk=map.pk).modified_at.date() != old_modified_at
|
umap/tests/test_empty_trash.py
CHANGED
|
@@ -4,15 +4,17 @@ from unittest import mock
|
|
|
4
4
|
import pytest
|
|
5
5
|
from django.core.management import call_command
|
|
6
6
|
|
|
7
|
-
from umap.models import Map
|
|
7
|
+
from umap.models import DataLayer, Map
|
|
8
8
|
|
|
9
|
-
from .base import MapFactory
|
|
9
|
+
from .base import DataLayerFactory, MapFactory
|
|
10
10
|
|
|
11
11
|
pytestmark = pytest.mark.django_db
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def test_empty_trash(user):
|
|
15
15
|
recent = MapFactory(owner=user)
|
|
16
|
+
recent_layer = DataLayerFactory(map=recent)
|
|
17
|
+
deleted_layer = DataLayerFactory(map=recent)
|
|
16
18
|
recent_deleted = MapFactory(owner=user)
|
|
17
19
|
recent_deleted.move_to_trash()
|
|
18
20
|
recent_deleted.save()
|
|
@@ -20,15 +22,20 @@ def test_empty_trash(user):
|
|
|
20
22
|
mocked.return_value = datetime.utcnow() - timedelta(days=8)
|
|
21
23
|
old_deleted = MapFactory(owner=user)
|
|
22
24
|
old_deleted.move_to_trash()
|
|
23
|
-
|
|
25
|
+
deleted_layer.move_to_trash()
|
|
24
26
|
old = MapFactory(owner=user)
|
|
25
27
|
assert Map.objects.count() == 4
|
|
28
|
+
assert DataLayer.objects.count() == 2
|
|
26
29
|
call_command("empty_trash", "--days=7", "--dry-run")
|
|
27
30
|
assert Map.objects.count() == 4
|
|
31
|
+
assert DataLayer.objects.count() == 2
|
|
28
32
|
call_command("empty_trash", "--days=9")
|
|
29
33
|
assert Map.objects.count() == 4
|
|
34
|
+
assert DataLayer.objects.count() == 2
|
|
30
35
|
call_command("empty_trash", "--days=7")
|
|
31
36
|
assert not Map.objects.filter(pk=old_deleted.pk)
|
|
32
37
|
assert Map.objects.filter(pk=old.pk)
|
|
33
38
|
assert Map.objects.filter(pk=recent.pk)
|
|
34
39
|
assert Map.objects.filter(pk=recent_deleted.pk)
|
|
40
|
+
assert not DataLayer.objects.filter(pk=deleted_layer.pk)
|
|
41
|
+
assert DataLayer.objects.filter(pk=recent_layer.pk)
|