c2cgeoportal-admin 2.5.0.100__py3-none-any.whl → 2.9rc44__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.
- c2cgeoportal_admin/__init__.py +44 -14
- c2cgeoportal_admin/lib/__init__.py +0 -0
- c2cgeoportal_admin/lib/lingva_extractor.py +77 -0
- c2cgeoportal_admin/lib/ogcserver_synchronizer.py +410 -0
- c2cgeoportal_admin/py.typed +0 -0
- c2cgeoportal_admin/routes.py +30 -11
- c2cgeoportal_admin/schemas/dimensions.py +17 -11
- c2cgeoportal_admin/schemas/functionalities.py +60 -22
- c2cgeoportal_admin/schemas/interfaces.py +27 -19
- c2cgeoportal_admin/schemas/metadata.py +122 -48
- c2cgeoportal_admin/schemas/restriction_areas.py +26 -20
- c2cgeoportal_admin/schemas/roles.py +13 -7
- c2cgeoportal_admin/schemas/treegroup.py +90 -20
- c2cgeoportal_admin/schemas/treeitem.py +3 -4
- c2cgeoportal_admin/static/layertree.css +26 -4
- c2cgeoportal_admin/static/navbar.css +59 -36
- c2cgeoportal_admin/static/theme.css +51 -11
- c2cgeoportal_admin/subscribers.py +3 -3
- c2cgeoportal_admin/templates/404.jinja2 +41 -2
- c2cgeoportal_admin/templates/edit.jinja2 +23 -0
- c2cgeoportal_admin/templates/home.jinja2 +23 -0
- c2cgeoportal_admin/templates/index.jinja2 +23 -0
- c2cgeoportal_admin/templates/layertree.jinja2 +55 -11
- c2cgeoportal_admin/templates/layout.jinja2 +23 -0
- c2cgeoportal_admin/templates/navigation_navbar.jinja2 +56 -0
- c2cgeoportal_admin/templates/ogcserver_synchronize.jinja2 +90 -0
- c2cgeoportal_admin/templates/widgets/child.pt +35 -3
- c2cgeoportal_admin/templates/widgets/children.pt +121 -92
- c2cgeoportal_admin/templates/widgets/dimension.pt +23 -0
- c2cgeoportal_admin/templates/widgets/dimensions.pt +23 -0
- c2cgeoportal_admin/templates/widgets/functionality_fields.pt +51 -0
- c2cgeoportal_admin/templates/widgets/layer_fields.pt +23 -0
- c2cgeoportal_admin/templates/widgets/layer_group_fields.pt +23 -0
- c2cgeoportal_admin/templates/widgets/layer_v1_fields.pt +23 -0
- c2cgeoportal_admin/templates/widgets/metadata.pt +30 -1
- c2cgeoportal_admin/templates/widgets/metadatas.pt +23 -0
- c2cgeoportal_admin/templates/widgets/ogcserver_fields.pt +23 -0
- c2cgeoportal_admin/templates/widgets/restriction_area_fields.pt +25 -9
- c2cgeoportal_admin/templates/widgets/role_fields.pt +52 -25
- c2cgeoportal_admin/templates/widgets/theme_fields.pt +23 -0
- c2cgeoportal_admin/templates/widgets/user_fields.pt +23 -0
- c2cgeoportal_admin/views/__init__.py +29 -0
- c2cgeoportal_admin/views/dimension_layers.py +14 -9
- c2cgeoportal_admin/views/functionalities.py +52 -18
- c2cgeoportal_admin/views/home.py +5 -5
- c2cgeoportal_admin/views/interfaces.py +29 -21
- c2cgeoportal_admin/views/layer_groups.py +36 -25
- c2cgeoportal_admin/views/layers.py +17 -13
- c2cgeoportal_admin/views/layers_cog.py +135 -0
- c2cgeoportal_admin/views/layers_vectortiles.py +62 -27
- c2cgeoportal_admin/views/layers_wms.py +61 -36
- c2cgeoportal_admin/views/layers_wmts.py +54 -32
- c2cgeoportal_admin/views/layertree.py +37 -28
- c2cgeoportal_admin/views/logged_views.py +83 -0
- c2cgeoportal_admin/views/logs.py +91 -0
- c2cgeoportal_admin/views/oauth2_clients.py +96 -0
- c2cgeoportal_admin/views/ogc_servers.py +192 -21
- c2cgeoportal_admin/views/restriction_areas.py +78 -25
- c2cgeoportal_admin/views/roles.py +88 -25
- c2cgeoportal_admin/views/themes.py +47 -35
- c2cgeoportal_admin/views/themes_ordering.py +44 -24
- c2cgeoportal_admin/views/treeitems.py +21 -17
- c2cgeoportal_admin/views/users.py +46 -26
- c2cgeoportal_admin/widgets.py +79 -28
- {c2cgeoportal_admin-2.5.0.100.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/METADATA +15 -13
- c2cgeoportal_admin-2.9rc44.dist-info/RECORD +97 -0
- {c2cgeoportal_admin-2.5.0.100.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/WHEEL +1 -1
- c2cgeoportal_admin-2.9rc44.dist-info/entry_points.txt +5 -0
- tests/__init__.py +36 -27
- tests/conftest.py +23 -24
- tests/test_edit_url.py +16 -19
- tests/test_functionalities.py +52 -14
- tests/test_home.py +0 -1
- tests/test_interface.py +35 -12
- tests/test_layer_groups.py +58 -32
- tests/test_layers_cog.py +243 -0
- tests/test_layers_vectortiles.py +46 -30
- tests/test_layers_wms.py +77 -82
- tests/test_layers_wmts.py +51 -30
- tests/test_layertree.py +107 -101
- tests/test_learn.py +1 -1
- tests/test_left_menu.py +0 -1
- tests/test_lingva_extractor_config.py +64 -0
- tests/test_logs.py +102 -0
- tests/test_main.py +4 -2
- tests/test_metadatas.py +79 -71
- tests/test_oauth2_clients.py +186 -0
- tests/test_ogc_servers.py +110 -28
- tests/test_restriction_areas.py +109 -20
- tests/test_role.py +142 -82
- tests/test_themes.py +75 -41
- tests/test_themes_ordering.py +1 -2
- tests/test_treegroup.py +2 -2
- tests/test_user.py +72 -70
- tests/themes_ordering.py +1 -2
- c2cgeoportal_admin/templates/navigation_vertical.jinja2 +0 -10
- c2cgeoportal_admin-2.5.0.100.dist-info/RECORD +0 -84
- c2cgeoportal_admin-2.5.0.100.dist-info/entry_points.txt +0 -3
- {c2cgeoportal_admin-2.5.0.100.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/top_level.txt +0 -0
tests/test_themes.py
CHANGED
@@ -13,33 +13,38 @@ def theme_test_data(dbsession, transact):
|
|
13
13
|
del transact
|
14
14
|
|
15
15
|
from c2cgeoportal_commons.models.main import (
|
16
|
-
Theme,
|
17
|
-
Role,
|
18
16
|
Functionality,
|
19
|
-
LayergroupTreeitem,
|
20
17
|
Interface,
|
21
|
-
Metadata,
|
22
18
|
LayerGroup,
|
19
|
+
LayergroupTreeitem,
|
23
20
|
LayerWMS,
|
21
|
+
Metadata,
|
24
22
|
OGCServer,
|
23
|
+
Role,
|
24
|
+
Theme,
|
25
25
|
)
|
26
26
|
|
27
27
|
interfaces = [Interface(name) for name in ["desktop", "mobile", "edit", "routing"]]
|
28
|
+
dbsession.add_all(interfaces)
|
28
29
|
|
29
|
-
groups = [LayerGroup(name="layer_group_{}"
|
30
|
+
groups = [LayerGroup(name=f"layer_group_{i}") for i in range(0, 5)]
|
31
|
+
dbsession.add_all(groups)
|
30
32
|
|
31
33
|
layer = LayerWMS(name="layer_wms")
|
32
34
|
layer.ogc_server = OGCServer(name="server")
|
33
|
-
dbsession.add(layer)
|
34
35
|
layers = [layer]
|
36
|
+
dbsession.add_all(layers)
|
35
37
|
|
38
|
+
# Note that "default_theme" is not relevant for themes
|
36
39
|
functionalities = [
|
37
|
-
Functionality(name=name, value="value_{}"
|
38
|
-
for name in ("default_basemap", "
|
40
|
+
Functionality(name=name, value=f"value_{v}")
|
41
|
+
for name in ("default_basemap", "default_theme")
|
39
42
|
for v in range(0, 4)
|
40
43
|
]
|
44
|
+
dbsession.add_all(functionalities)
|
41
45
|
|
42
46
|
roles = [Role("secretary_" + str(i)) for i in range(0, 4)]
|
47
|
+
dbsession.add_all(roles)
|
43
48
|
|
44
49
|
metadatas_protos = [
|
45
50
|
("copyable", "true"),
|
@@ -48,7 +53,7 @@ def theme_test_data(dbsession, transact):
|
|
48
53
|
]
|
49
54
|
themes = []
|
50
55
|
for i in range(0, 25):
|
51
|
-
theme = Theme(name="theme_{}"
|
56
|
+
theme = Theme(name=f"theme_{i}", ordering=1, icon=f"icon_{i}")
|
52
57
|
theme.public = 1 == i % 2
|
53
58
|
theme.interfaces = [interfaces[i % 4], interfaces[(i + 2) % 4]]
|
54
59
|
theme.metadatas = [
|
@@ -57,7 +62,7 @@ def theme_test_data(dbsession, transact):
|
|
57
62
|
]
|
58
63
|
for metadata in theme.metadatas:
|
59
64
|
metadata.item = theme
|
60
|
-
theme.functionalities = [functionalities[i %
|
65
|
+
theme.functionalities = [functionalities[i % 4]]
|
61
66
|
theme.restricted_roles = [roles[i % 4], roles[(i + 2) % 4]]
|
62
67
|
|
63
68
|
dbsession.add(
|
@@ -86,7 +91,6 @@ def theme_test_data(dbsession, transact):
|
|
86
91
|
|
87
92
|
@pytest.mark.usefixtures("theme_test_data", "test_app")
|
88
93
|
class TestTheme(TestTreeGroup):
|
89
|
-
|
90
94
|
_prefix = "/admin/themes"
|
91
95
|
|
92
96
|
def test_index_rendering(self, test_app):
|
@@ -117,7 +121,7 @@ class TestTheme(TestTreeGroup):
|
|
117
121
|
|
118
122
|
assert first_theme.id == int(first_row["_id_"])
|
119
123
|
assert first_theme.name == first_row["name"]
|
120
|
-
assert "default_basemap=value_0
|
124
|
+
assert "default_basemap=value_0" == first_row["functionalities"]
|
121
125
|
assert "secretary_0, secretary_2" == first_row["restricted_roles"]
|
122
126
|
assert "desktop, edit" == first_row["interfaces"]
|
123
127
|
assert 'copyable: true, snappingConfig: {"tolerance": 50}' == first_row["metadatas"]
|
@@ -146,16 +150,18 @@ class TestTheme(TestTreeGroup):
|
|
146
150
|
|
147
151
|
def test_public_checkbox_edit(self, test_app, theme_test_data):
|
148
152
|
theme = theme_test_data["themes"][10]
|
149
|
-
form10 = test_app.get("/admin/themes/{
|
153
|
+
form10 = test_app.get(f"/admin/themes/{theme.id}", status=200).form
|
150
154
|
assert not form10["public"].checked
|
151
155
|
theme = theme_test_data["themes"][11]
|
152
|
-
form11 = test_app.get("/admin/themes/{
|
156
|
+
form11 = test_app.get(f"/admin/themes/{theme.id}", status=200).form
|
153
157
|
assert form11["public"].checked
|
154
158
|
|
155
159
|
def test_edit(self, test_app, theme_test_data, dbsession):
|
160
|
+
from c2cgeoportal_commons.models.main import Log, LogAction
|
161
|
+
|
156
162
|
theme = theme_test_data["themes"][0]
|
157
163
|
|
158
|
-
resp = test_app.get("/admin/themes/{
|
164
|
+
resp = test_app.get(f"/admin/themes/{theme.id}", status=200)
|
159
165
|
form = resp.form
|
160
166
|
|
161
167
|
assert str(theme.id) == self.get_first_field_named(form, "id").value
|
@@ -166,21 +172,24 @@ class TestTheme(TestTreeGroup):
|
|
166
172
|
assert theme.public == form["public"].checked
|
167
173
|
|
168
174
|
interfaces = theme_test_data["interfaces"]
|
169
|
-
assert
|
175
|
+
assert {interfaces[0].id, interfaces[2].id} == {i.id for i in theme.interfaces}
|
170
176
|
self._check_interfaces(form, interfaces, theme)
|
171
177
|
|
172
178
|
functionalities = theme_test_data["functionalities"]
|
173
|
-
assert
|
179
|
+
assert {functionalities[0].id} == {f.id for f in theme.functionalities}
|
174
180
|
self.check_checkboxes(
|
175
181
|
form,
|
176
182
|
"functionalities",
|
177
183
|
[
|
178
184
|
{
|
179
|
-
"label": "{}={
|
185
|
+
"label": f"{f.name}={f.value}",
|
180
186
|
"value": str(f.id),
|
181
187
|
"checked": f in theme.functionalities,
|
182
188
|
}
|
183
|
-
for f in sorted(
|
189
|
+
for f in sorted(
|
190
|
+
[f for f in functionalities if f.name in ("default_basemap")],
|
191
|
+
key=lambda f: (f.name, f.value),
|
192
|
+
)
|
184
193
|
],
|
185
194
|
)
|
186
195
|
|
@@ -220,21 +229,25 @@ class TestTheme(TestTreeGroup):
|
|
220
229
|
assert value == getattr(theme, key)
|
221
230
|
else:
|
222
231
|
assert str(value or "") == str(getattr(theme, key) or "")
|
223
|
-
assert
|
224
|
-
|
225
|
-
)
|
226
|
-
assert set([functionalities[2].id]) == set(
|
227
|
-
[functionality.id for functionality in theme.functionalities]
|
228
|
-
)
|
232
|
+
assert {interfaces[1].id, interfaces[3].id} == {interface.id for interface in theme.interfaces}
|
233
|
+
assert {functionalities[2].id} == {functionality.id for functionality in theme.functionalities}
|
229
234
|
assert 0 == len(theme.restricted_roles)
|
230
235
|
|
236
|
+
log = dbsession.query(Log).one()
|
237
|
+
assert log.date != None
|
238
|
+
assert log.action == LogAction.UPDATE
|
239
|
+
assert log.element_type == "theme"
|
240
|
+
assert log.element_id == theme.id
|
241
|
+
assert log.element_name == theme.name
|
242
|
+
assert log.username == "test_user"
|
243
|
+
|
231
244
|
def test_post_new_with_children_invalid(self, test_app, theme_test_data):
|
232
245
|
"""
|
233
246
|
Check there is no rendering error when validation fails.
|
234
247
|
"""
|
235
248
|
groups = theme_test_data["groups"]
|
236
249
|
resp = test_app.post(
|
237
|
-
"{}/new"
|
250
|
+
f"{self._prefix}/new",
|
238
251
|
(
|
239
252
|
("_charset_", "UTF-8"),
|
240
253
|
("__formid__", "deform"),
|
@@ -254,9 +267,11 @@ class TestTheme(TestTreeGroup):
|
|
254
267
|
self._check_submission_problem(resp, "Required")
|
255
268
|
|
256
269
|
def test_post_new_with_children_success(self, test_app, dbsession, theme_test_data):
|
270
|
+
from c2cgeoportal_commons.models.main import Log, LogAction
|
271
|
+
|
257
272
|
groups = theme_test_data["groups"]
|
258
273
|
resp = test_app.post(
|
259
|
-
"{}/new"
|
274
|
+
f"{self._prefix}/new",
|
260
275
|
(
|
261
276
|
("_charset_", "UTF-8"),
|
262
277
|
("__formid__", "deform"),
|
@@ -298,13 +313,21 @@ class TestTheme(TestTreeGroup):
|
|
298
313
|
rel.treeitem_id for rel in theme.children_relation
|
299
314
|
]
|
300
315
|
|
316
|
+
log = dbsession.query(Log).one()
|
317
|
+
assert log.date != None
|
318
|
+
assert log.action == LogAction.INSERT
|
319
|
+
assert log.element_type == "theme"
|
320
|
+
assert log.element_id == theme.id
|
321
|
+
assert log.element_name == theme.name
|
322
|
+
assert log.username == "test_user"
|
323
|
+
|
301
324
|
def test_post_new_with_child_layer(self, theme_test_data, test_app):
|
302
325
|
"""
|
303
326
|
Check layers are rejected by the validator (also means that they are not proposed to the user).
|
304
327
|
"""
|
305
328
|
layers = theme_test_data["layers"]
|
306
329
|
resp = test_app.post(
|
307
|
-
"{}/new"
|
330
|
+
f"{self._prefix}/new",
|
308
331
|
(
|
309
332
|
("_charset_", "UTF-8"),
|
310
333
|
("__formid__", "deform"),
|
@@ -324,7 +347,7 @@ class TestTheme(TestTreeGroup):
|
|
324
347
|
status=200,
|
325
348
|
)
|
326
349
|
assert (
|
327
|
-
"Value {} does not exist in table treeitem or is not allowed to avoid cycles"
|
350
|
+
f"Value {layers[0].id} does not exist in table treeitem or is not allowed to avoid cycles"
|
328
351
|
== resp.html.select_one(".item-children_relation + .help-block").getText().strip()
|
329
352
|
)
|
330
353
|
|
@@ -333,7 +356,7 @@ class TestTheme(TestTreeGroup):
|
|
333
356
|
|
334
357
|
theme = theme_test_data["themes"][1]
|
335
358
|
|
336
|
-
resp = test_app.get("{}/{}/duplicate"
|
359
|
+
resp = test_app.get(f"{self._prefix}/{theme.id}/duplicate", status=200)
|
337
360
|
form = resp.form
|
338
361
|
|
339
362
|
assert "" == self.get_first_field_named(form, "id").value
|
@@ -345,22 +368,25 @@ class TestTheme(TestTreeGroup):
|
|
345
368
|
assert theme.public == form["public"].checked
|
346
369
|
|
347
370
|
interfaces = theme_test_data["interfaces"]
|
348
|
-
assert
|
371
|
+
assert {interfaces[1].id, interfaces[3].id} == {i.id for i in theme.interfaces}
|
349
372
|
|
350
373
|
self._check_interfaces(form, interfaces, theme)
|
351
374
|
|
352
375
|
functionalities = theme_test_data["functionalities"]
|
353
|
-
assert
|
376
|
+
assert {functionalities[1].id} == {f.id for f in theme.functionalities}
|
354
377
|
self.check_checkboxes(
|
355
378
|
form,
|
356
379
|
"functionalities",
|
357
380
|
[
|
358
381
|
{
|
359
|
-
"label": "{}={
|
382
|
+
"label": f"{f.name}={f.value}",
|
360
383
|
"value": str(f.id),
|
361
384
|
"checked": f in theme.functionalities,
|
362
385
|
}
|
363
|
-
for f in sorted(
|
386
|
+
for f in sorted(
|
387
|
+
[f for f in functionalities if f.name in ("default_basemap")],
|
388
|
+
key=lambda f: (f.name, f.value),
|
389
|
+
)
|
364
390
|
],
|
365
391
|
)
|
366
392
|
|
@@ -381,23 +407,31 @@ class TestTheme(TestTreeGroup):
|
|
381
407
|
duplicated = dbsession.query(Theme).filter(Theme.name == "duplicated").one()
|
382
408
|
|
383
409
|
assert str(duplicated.id) == re.match(
|
384
|
-
|
410
|
+
rf"http://localhost{self._prefix}/(.*)\?msg_col=submit_ok", resp.location
|
385
411
|
).group(1)
|
386
412
|
assert duplicated.id != theme.id
|
387
413
|
assert duplicated.children_relation[0].id != theme.children_relation[0].id
|
388
414
|
assert duplicated.children_relation[0].treeitem.id == theme.children_relation[0].treeitem.id
|
389
415
|
|
390
416
|
def test_delete(self, test_app, dbsession):
|
391
|
-
from c2cgeoportal_commons.models.main import Theme
|
417
|
+
from c2cgeoportal_commons.models.main import Log, LogAction, Theme
|
418
|
+
|
419
|
+
theme = dbsession.query(Theme).first()
|
420
|
+
test_app.delete(f"/admin/themes/{theme.id}", status=200)
|
421
|
+
assert dbsession.query(Theme).get(theme.id) is None
|
392
422
|
|
393
|
-
|
394
|
-
|
395
|
-
assert
|
423
|
+
log = dbsession.query(Log).one()
|
424
|
+
assert log.date != None
|
425
|
+
assert log.action == LogAction.DELETE
|
426
|
+
assert log.element_type == "theme"
|
427
|
+
assert log.element_id == theme.id
|
428
|
+
assert log.element_name == theme.name
|
429
|
+
assert log.username == "test_user"
|
396
430
|
|
397
431
|
def test_unicity_validator(self, theme_test_data, test_app):
|
398
432
|
theme = theme_test_data["themes"][1]
|
399
|
-
resp = test_app.get("{}/{}/duplicate"
|
433
|
+
resp = test_app.get(f"{self._prefix}/{theme.id}/duplicate", status=200)
|
400
434
|
|
401
435
|
resp = resp.form.submit("submit")
|
402
436
|
|
403
|
-
self._check_submission_problem(resp, "{} is already used."
|
437
|
+
self._check_submission_problem(resp, f"{theme.name} is already used.")
|
tests/test_themes_ordering.py
CHANGED
@@ -14,7 +14,7 @@ def theme_test_data(dbsession, transact):
|
|
14
14
|
|
15
15
|
themes = []
|
16
16
|
for i in range(0, 3):
|
17
|
-
theme = Theme(name="theme_{}"
|
17
|
+
theme = Theme(name=f"theme_{i}", ordering=i, icon=f"icon_{i}")
|
18
18
|
|
19
19
|
dbsession.add(theme)
|
20
20
|
themes.append(theme)
|
@@ -28,7 +28,6 @@ def theme_test_data(dbsession, transact):
|
|
28
28
|
|
29
29
|
@pytest.mark.usefixtures("theme_test_data", "test_app")
|
30
30
|
class TestThemeOrdering(TestTreeGroup):
|
31
|
-
|
32
31
|
_prefix = "/admin/layertree/ordering"
|
33
32
|
|
34
33
|
def test_edit(self, test_app, theme_test_data, dbsession):
|
tests/test_treegroup.py
CHANGED
@@ -5,10 +5,10 @@ from . import AbstractViewsTests
|
|
5
5
|
|
6
6
|
class TestTreeGroup(AbstractViewsTests):
|
7
7
|
def check_children(self, form, group, expected):
|
8
|
-
form_group = form.html.select_one(".item-{}"
|
8
|
+
form_group = form.html.select_one(f".item-{group}")
|
9
9
|
items = form_group.select(".deform-seq-item")
|
10
10
|
assert len(expected) == len(items)
|
11
11
|
for item, exp in zip(items, expected):
|
12
12
|
assert exp["label"] == item.select_one(".well").getText().strip()
|
13
13
|
for key, value in exp["values"].items():
|
14
|
-
assert value == item.select_one('input[name="{}"]'
|
14
|
+
assert value == item.select_one(f'input[name="{key}"]')["value"]
|
tests/test_user.py
CHANGED
@@ -4,11 +4,11 @@ import email
|
|
4
4
|
import re
|
5
5
|
from unittest.mock import MagicMock, patch
|
6
6
|
|
7
|
-
|
7
|
+
import pyramid.httpexceptions
|
8
8
|
import pytest
|
9
|
+
from pyramid.testing import DummyRequest
|
9
10
|
|
10
|
-
from . import AbstractViewsTests
|
11
|
-
from .selenium.page import IndexPage
|
11
|
+
from . import AbstractViewsTests
|
12
12
|
|
13
13
|
|
14
14
|
@pytest.fixture(scope="function")
|
@@ -16,18 +16,18 @@ from .selenium.page import IndexPage
|
|
16
16
|
def users_test_data(dbsession, transact):
|
17
17
|
del transact
|
18
18
|
|
19
|
-
from c2cgeoportal_commons.models.static import User
|
20
19
|
from c2cgeoportal_commons.models.main import Role
|
20
|
+
from c2cgeoportal_commons.models.static import User
|
21
21
|
|
22
22
|
roles = []
|
23
23
|
for i in range(0, 4):
|
24
|
-
roles.append(Role("secretary_{}"
|
24
|
+
roles.append(Role(f"secretary_{i}"))
|
25
25
|
dbsession.add(roles[i])
|
26
26
|
users = []
|
27
27
|
for i in range(0, 23):
|
28
28
|
user = User(
|
29
|
-
"babar_{}"
|
30
|
-
email="mail{}@valid.net"
|
29
|
+
f"babar_{i}",
|
30
|
+
email=f"mail{i}@valid.net",
|
31
31
|
settings_role=roles[i % 4],
|
32
32
|
roles=[roles[i % 4]],
|
33
33
|
)
|
@@ -49,7 +49,6 @@ EXPECTED_WELCOME_MAIL = (
|
|
49
49
|
|
50
50
|
@pytest.mark.usefixtures("users_test_data", "test_app")
|
51
51
|
class TestUser(AbstractViewsTests):
|
52
|
-
|
53
52
|
_prefix = "/admin/users"
|
54
53
|
|
55
54
|
def test_index_rendering(self, test_app):
|
@@ -61,6 +60,7 @@ class TestUser(AbstractViewsTests):
|
|
61
60
|
("actions", "", "false"),
|
62
61
|
("id", "id", "true"),
|
63
62
|
("username", "Username"),
|
63
|
+
("display_name", "Display name", "true"),
|
64
64
|
("email", "Email"),
|
65
65
|
("last_login", "Last login"),
|
66
66
|
("expire_on", "Expiration date"),
|
@@ -71,20 +71,33 @@ class TestUser(AbstractViewsTests):
|
|
71
71
|
|
72
72
|
self.check_grid_headers(resp, expected)
|
73
73
|
|
74
|
-
@pytest.mark.skip(reason="
|
74
|
+
@pytest.mark.skip(reason="Translations seems not available in tests")
|
75
75
|
def test_index_rendering_fr(self, test_app):
|
76
76
|
resp = self.get(test_app, locale="fr")
|
77
77
|
|
78
78
|
self.check_left_menu(resp, "Utilisateurs")
|
79
79
|
|
80
|
-
expected = [
|
81
|
-
|
80
|
+
expected = [
|
81
|
+
("actions", "", "false"),
|
82
|
+
("id", "id", "true"),
|
83
|
+
("username", "Nom d'utilisateur"),
|
84
|
+
("email", "Email"),
|
85
|
+
("last_login", "Dernière connection"),
|
86
|
+
("expire_on", "Date d'expiration"),
|
87
|
+
("deactivated", "Désactivé"),
|
88
|
+
("settings_role", "Rôle de configuration"),
|
89
|
+
("roles", "Rôles", "false"),
|
90
|
+
]
|
91
|
+
self.check_grid_headers(resp, expected, new="Nouveau")
|
92
|
+
|
93
|
+
def test_view_edit(self, test_app, users_test_data, dbsession):
|
94
|
+
from c2cgeoportal_commons.models.main import LogAction
|
95
|
+
from c2cgeoportal_commons.models.static import Log
|
82
96
|
|
83
|
-
def test_view_edit(self, test_app, users_test_data):
|
84
97
|
user = users_test_data["users"][9]
|
85
98
|
roles = users_test_data["roles"]
|
86
99
|
|
87
|
-
resp = test_app.get("/admin/users/{
|
100
|
+
resp = test_app.get(f"/admin/users/{user.id}", status=200)
|
88
101
|
|
89
102
|
assert resp.form["username"].value == user.username
|
90
103
|
assert resp.form["email"].value == user.email
|
@@ -111,17 +124,34 @@ class TestUser(AbstractViewsTests):
|
|
111
124
|
assert value == getattr(user, key)
|
112
125
|
else:
|
113
126
|
assert str(value or "") == str(getattr(user, key) or "")
|
114
|
-
assert
|
127
|
+
assert {roles[2].id, roles[3].id} == {role.id for role in user.roles}
|
128
|
+
|
129
|
+
log = dbsession.query(Log).one()
|
130
|
+
assert log.date != None
|
131
|
+
assert log.action == LogAction.UPDATE
|
132
|
+
assert log.element_type == "user"
|
133
|
+
assert log.element_id == user.id
|
134
|
+
assert log.element_name == user.username
|
135
|
+
assert log.username == "test_user"
|
115
136
|
|
116
137
|
def test_delete(self, test_app, users_test_data, dbsession):
|
117
|
-
from c2cgeoportal_commons.models.
|
138
|
+
from c2cgeoportal_commons.models.main import LogAction
|
139
|
+
from c2cgeoportal_commons.models.static import Log, User, user_role
|
118
140
|
|
119
141
|
user = users_test_data["users"][9]
|
120
142
|
deleted_id = user.id
|
121
|
-
test_app.delete("/admin/users/{}"
|
143
|
+
test_app.delete(f"/admin/users/{deleted_id}", status=200)
|
122
144
|
assert dbsession.query(User).get(deleted_id) is None
|
123
145
|
assert dbsession.query(user_role).filter(user_role.c.user_id == user.id).count() == 0
|
124
146
|
|
147
|
+
log = dbsession.query(Log).one()
|
148
|
+
assert log.date != None
|
149
|
+
assert log.action == LogAction.DELETE
|
150
|
+
assert log.element_type == "user"
|
151
|
+
assert log.element_id == user.id
|
152
|
+
assert log.element_name == user.username
|
153
|
+
assert log.username == "test_user"
|
154
|
+
|
125
155
|
@patch("c2cgeoportal_commons.lib.email_.smtplib.SMTP")
|
126
156
|
@patch("c2cgeoportal_admin.views.users.pwgenerator.generate")
|
127
157
|
def test_submit_update(self, pw_gen_mock, smtp_mock, dbsession, test_app, users_test_data):
|
@@ -129,7 +159,7 @@ class TestUser(AbstractViewsTests):
|
|
129
159
|
roles = users_test_data["roles"]
|
130
160
|
|
131
161
|
resp = test_app.post(
|
132
|
-
"/admin/users/{
|
162
|
+
f"/admin/users/{user.id}",
|
133
163
|
(
|
134
164
|
("__formid__", "deform"),
|
135
165
|
("_charset_", "UTF-8"),
|
@@ -137,6 +167,7 @@ class TestUser(AbstractViewsTests):
|
|
137
167
|
("item_type", "user"),
|
138
168
|
("id", user.id),
|
139
169
|
("username", "new_name_withéàô"),
|
170
|
+
("display_name", "New name withéàô"),
|
140
171
|
("email", "new_mail@valid.net"),
|
141
172
|
("settings_role_id", roles[2].id),
|
142
173
|
("__start__", "roles:sequence"),
|
@@ -146,13 +177,13 @@ class TestUser(AbstractViewsTests):
|
|
146
177
|
),
|
147
178
|
status=302,
|
148
179
|
)
|
149
|
-
assert resp.location == "http://localhost/admin/users/{}?msg_col=submit_ok"
|
180
|
+
assert resp.location == f"http://localhost/admin/users/{user.id}?msg_col=submit_ok"
|
150
181
|
|
151
182
|
dbsession.expire(user)
|
152
183
|
assert user.username == "new_name_withéàô"
|
153
184
|
assert user.email == "new_mail@valid.net"
|
154
185
|
assert user.settings_role.name == "secretary_2"
|
155
|
-
assert
|
186
|
+
assert {r.id for r in user.roles} == {roles[i].id for i in [0, 3]}
|
156
187
|
assert user.validate_password("pré$ident")
|
157
188
|
|
158
189
|
assert not pw_gen_mock.called, "method should not have been called"
|
@@ -163,7 +194,7 @@ class TestUser(AbstractViewsTests):
|
|
163
194
|
roles = users_test_data["roles"]
|
164
195
|
|
165
196
|
resp = test_app.post(
|
166
|
-
"/admin/users/{
|
197
|
+
f"/admin/users/{user.id}",
|
167
198
|
{
|
168
199
|
"__formid__": "deform",
|
169
200
|
"_charset_": "UTF-8",
|
@@ -190,7 +221,7 @@ class TestUser(AbstractViewsTests):
|
|
190
221
|
user = users_test_data["users"][7]
|
191
222
|
roles = users_test_data["roles"]
|
192
223
|
|
193
|
-
resp = test_app.get("/admin/users/{}/duplicate"
|
224
|
+
resp = test_app.get(f"/admin/users/{user.id}/duplicate", status=200)
|
194
225
|
form = resp.form
|
195
226
|
|
196
227
|
assert "" == form["id"].value
|
@@ -210,7 +241,7 @@ class TestUser(AbstractViewsTests):
|
|
210
241
|
).group(1)
|
211
242
|
assert user.id != new_user.id
|
212
243
|
assert user.settings_role_id == new_user.settings_role_id
|
213
|
-
assert
|
244
|
+
assert {role.id for role in user.roles} == {role.id for role in new_user.roles}
|
214
245
|
assert not new_user.is_password_changed
|
215
246
|
assert not new_user.validate_password("pré$ident")
|
216
247
|
|
@@ -224,10 +255,12 @@ class TestUser(AbstractViewsTests):
|
|
224
255
|
@patch("c2cgeoportal_admin.views.users.pwgenerator.generate")
|
225
256
|
@pytest.mark.usefixtures("test_app")
|
226
257
|
def test_submit_new(self, pw_gen_mock, smtp_mock, dbsession, test_app, users_test_data):
|
258
|
+
from c2cgeoportal_commons.models.main import LogAction
|
259
|
+
from c2cgeoportal_commons.models.static import Log, User
|
260
|
+
|
227
261
|
sender_mock = MagicMock()
|
228
262
|
smtp_mock.return_value = sender_mock
|
229
263
|
pw_gen_mock.return_value = "basile"
|
230
|
-
from c2cgeoportal_commons.models.static import User
|
231
264
|
|
232
265
|
roles = users_test_data["roles"]
|
233
266
|
|
@@ -240,6 +273,7 @@ class TestUser(AbstractViewsTests):
|
|
240
273
|
("item_type", "user"),
|
241
274
|
("id", ""),
|
242
275
|
("username", "new_user"),
|
276
|
+
("display_name", "New user"),
|
243
277
|
("email", "valid@email.net"),
|
244
278
|
("settings_role_id", roles[2].id),
|
245
279
|
),
|
@@ -253,6 +287,7 @@ class TestUser(AbstractViewsTests):
|
|
253
287
|
).group(1)
|
254
288
|
|
255
289
|
assert user.username == "new_user"
|
290
|
+
assert user.display_name == "New user"
|
256
291
|
assert user.email == "valid@email.net"
|
257
292
|
assert user.settings_role_id == roles[2].id
|
258
293
|
assert user.password is not None and len(user.password)
|
@@ -266,6 +301,14 @@ class TestUser(AbstractViewsTests):
|
|
266
301
|
).decode("utf8")
|
267
302
|
assert "valid@email.net" == parts[0].items()[3][1]
|
268
303
|
|
304
|
+
log = dbsession.query(Log).one()
|
305
|
+
assert log.date != None
|
306
|
+
assert log.action == LogAction.INSERT
|
307
|
+
assert log.element_type == "user"
|
308
|
+
assert log.element_id == user.id
|
309
|
+
assert log.element_name == user.username
|
310
|
+
assert log.username == "test_user"
|
311
|
+
|
269
312
|
def test_invalid_email(self, test_app):
|
270
313
|
resp = test_app.post(
|
271
314
|
"/admin/users/new",
|
@@ -276,6 +319,7 @@ class TestUser(AbstractViewsTests):
|
|
276
319
|
"item_type": "user",
|
277
320
|
"id": "",
|
278
321
|
"username": "invalid_email",
|
322
|
+
"display_name": "Invalid email",
|
279
323
|
"email": "new_mail",
|
280
324
|
"role_name": "secretary_2",
|
281
325
|
"is_password_changed": "false",
|
@@ -291,56 +335,14 @@ class TestUser(AbstractViewsTests):
|
|
291
335
|
from c2cgeoportal_admin.views.users import UserViews
|
292
336
|
|
293
337
|
request = DummyRequest(dbsession=dbsession, params={"offset": 0, "limit": 10})
|
294
|
-
|
295
|
-
|
338
|
+
with pytest.raises(pyramid.httpexceptions.HTTPInternalServerError):
|
339
|
+
UserViews(request).grid()
|
296
340
|
|
297
341
|
def test_grid_settings_role_none(self, dbsession, test_app):
|
298
|
-
"""
|
342
|
+
"""
|
343
|
+
Grid view must work even if a user's settings_role is None.
|
344
|
+
"""
|
299
345
|
from c2cgeoportal_commons.models.static import User
|
300
346
|
|
301
347
|
dbsession.add(User("test", email="test@valid.net"))
|
302
348
|
self.check_search(test_app, "test", total=1)
|
303
|
-
|
304
|
-
|
305
|
-
@skip_if_ci
|
306
|
-
@pytest.mark.selenium
|
307
|
-
@pytest.mark.usefixtures("selenium", "selenium_app", "users_test_data")
|
308
|
-
class TestUserSelenium:
|
309
|
-
|
310
|
-
_prefix = "/admin/users"
|
311
|
-
|
312
|
-
def test_index(self, selenium, selenium_app, users_test_data, dbsession):
|
313
|
-
from c2cgeoportal_commons.models.static import User
|
314
|
-
|
315
|
-
selenium.get(selenium_app + self._prefix)
|
316
|
-
|
317
|
-
index_page = IndexPage(selenium)
|
318
|
-
index_page.select_language("en")
|
319
|
-
index_page.check_pagination_info("Showing 1 to 23 of 23 rows", 10)
|
320
|
-
index_page.select_page_size(10)
|
321
|
-
index_page.check_pagination_info("Showing 1 to 10 of 23 rows", 10)
|
322
|
-
|
323
|
-
# delete
|
324
|
-
user = users_test_data["users"][3]
|
325
|
-
deleted_id = user.id
|
326
|
-
index_page.click_delete(deleted_id)
|
327
|
-
index_page.check_pagination_info("Showing 1 to 10 of 22 rows", 10)
|
328
|
-
assert dbsession.query(User).get(deleted_id) is None
|
329
|
-
|
330
|
-
# edit
|
331
|
-
user = users_test_data["users"][4]
|
332
|
-
index_page.find_item_action(user.id, "edit").click()
|
333
|
-
|
334
|
-
elem = selenium.find_element_by_xpath("//input[@name ='username']")
|
335
|
-
elem.clear()
|
336
|
-
elem.send_keys("new_name_éôù")
|
337
|
-
elem = selenium.find_element_by_xpath("//input[@name ='email']")
|
338
|
-
elem.clear()
|
339
|
-
elem.send_keys("new_email@valid.net")
|
340
|
-
elem = selenium.find_element_by_xpath("//button[@name='formsubmit']")
|
341
|
-
elem.click()
|
342
|
-
|
343
|
-
user = dbsession.query(User).filter(User.id == user.id).one()
|
344
|
-
dbsession.expire(user)
|
345
|
-
assert user.username == "new_name_éôù"
|
346
|
-
assert user.email == "new_email@valid.net"
|
tests/themes_ordering.py
CHANGED
@@ -14,7 +14,7 @@ def themes_ordering_test_data(dbsession, transact):
|
|
14
14
|
|
15
15
|
themes = []
|
16
16
|
for i in range(0, 25):
|
17
|
-
theme = Theme(name="theme_{}"
|
17
|
+
theme = Theme(name=f"theme_{i}", ordering=100)
|
18
18
|
dbsession.add(theme)
|
19
19
|
themes.append(theme)
|
20
20
|
|
@@ -25,7 +25,6 @@ def themes_ordering_test_data(dbsession, transact):
|
|
25
25
|
|
26
26
|
@pytest.mark.usefixtures("themes_ordering_test_data", "test_app")
|
27
27
|
class TestThemesOrdering(TestTreeGroup):
|
28
|
-
|
29
28
|
_prefix = "/admin/layertree/ordering"
|
30
29
|
|
31
30
|
def test_edit(self, test_app, themes_ordering_test_data):
|
@@ -1,10 +0,0 @@
|
|
1
|
-
<ul id="main-menu" class="nav nav-pills nav-stacked">
|
2
|
-
<li role="presentation" class="{{'active' if request.matched_route.name == 'layertree' else ''}}">
|
3
|
-
<a href="{{request.route_url('layertree')}}">{{_("Layer tree")}}</a>
|
4
|
-
</li>
|
5
|
-
{% for table in tables %}
|
6
|
-
<li role="presentation" class="{{'active' if request.matchdict and request.matchdict.get('table') == table['key'] else ''}}">
|
7
|
-
<a href="{{request.route_url('c2cgeoform_index', table=table['key'])}}">{{table['plural']}}</a>
|
8
|
-
</li>
|
9
|
-
{% endfor %}
|
10
|
-
</ul>
|