2018-11-05 16:58:53 +00:00
|
|
|
|
import uuid
|
|
|
|
|
|
|
|
|
|
|
|
import pytest
|
2019-03-22 17:33:47 +00:00
|
|
|
|
from flask import abort, url_for
|
2018-12-17 15:24:24 +00:00
|
|
|
|
from notifications_python_client.errors import HTTPError
|
2018-11-01 15:33:09 +00:00
|
|
|
|
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
from app.models.user import User
|
2019-04-02 15:23:46 +01:00
|
|
|
|
from tests import sample_uuid
|
2018-11-19 17:33:33 +00:00
|
|
|
|
from tests.conftest import (
|
|
|
|
|
|
SERVICE_ONE_ID,
|
|
|
|
|
|
TEMPLATE_ONE_ID,
|
2019-03-19 18:30:05 +00:00
|
|
|
|
_template,
|
2019-12-19 16:59:07 +00:00
|
|
|
|
create_active_caseworking_user,
|
|
|
|
|
|
create_active_user_view_permissions,
|
|
|
|
|
|
create_active_user_with_permissions,
|
2022-05-26 18:47:42 +01:00
|
|
|
|
create_template,
|
2018-11-19 17:33:33 +00:00
|
|
|
|
normalize_spaces,
|
|
|
|
|
|
)
|
2018-11-05 16:58:53 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
ROOT_FOLDER_ID = "__NONE__"
|
|
|
|
|
|
PARENT_FOLDER_ID = "7e979e79-d970-43a5-ac69-b625a8d147b0"
|
|
|
|
|
|
CHILD_FOLDER_ID = "92ee1ee0-e4ee-4dcc-b1a7-a5da9ebcfa2b"
|
|
|
|
|
|
GRANDCHILD_FOLDER_ID = "fafe723f-1d39-4a10-865f-e551e03d8886"
|
|
|
|
|
|
FOLDER_TWO_ID = "bbbb222b-2b22-2b22-222b-b222b22b2222"
|
|
|
|
|
|
FOLDER_B_ID = "dddb222b-2b22-2b22-222b-b222b22b6789"
|
|
|
|
|
|
FOLDER_C_ID = "ccbb222b-2b22-2b22-222b-b222b22b2345"
|
2018-11-05 16:58:53 +00:00
|
|
|
|
|
2018-11-01 15:33:09 +00:00
|
|
|
|
|
2019-03-07 12:02:02 +00:00
|
|
|
|
def _folder(name, folder_id=None, parent=None, users_with_permission=None):
|
2018-11-12 11:45:47 +00:00
|
|
|
|
return {
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"name": name,
|
|
|
|
|
|
"id": folder_id or str(uuid.uuid4()),
|
|
|
|
|
|
"parent_id": parent,
|
2024-02-23 17:38:47 -05:00
|
|
|
|
"users_with_permission": (
|
|
|
|
|
|
users_with_permission
|
|
|
|
|
|
if users_with_permission is not None
|
|
|
|
|
|
else [sample_uuid()]
|
|
|
|
|
|
),
|
2018-11-12 11:45:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-11-05 16:58:53 +00:00
|
|
|
|
@pytest.mark.parametrize(
|
2018-11-16 13:41:55 +00:00
|
|
|
|
(
|
2023-09-08 17:58:06 -04:00
|
|
|
|
"expected_title_tag",
|
|
|
|
|
|
"expected_page_title",
|
|
|
|
|
|
"expected_parent_link_args",
|
|
|
|
|
|
"extra_args",
|
|
|
|
|
|
"expected_nav_links",
|
|
|
|
|
|
"expected_items",
|
|
|
|
|
|
"expected_displayed_items",
|
|
|
|
|
|
"expected_searchable_text",
|
|
|
|
|
|
"expected_empty_message",
|
2018-11-16 13:41:55 +00:00
|
|
|
|
),
|
2018-11-05 16:58:53 +00:00
|
|
|
|
[
|
|
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"Templates – service one – Notify.gov",
|
|
|
|
|
|
"Templates",
|
2019-01-30 10:09:04 +00:00
|
|
|
|
[],
|
2018-11-05 16:58:53 +00:00
|
|
|
|
{},
|
2023-08-25 09:12:23 -07:00
|
|
|
|
["Email", "Text message"],
|
2018-11-05 16:58:53 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one folder_one 2 folders",
|
2023-12-08 13:30:04 -08:00
|
|
|
|
("folder_one folder_one_one " "folder_one_one"),
|
2023-12-08 13:38:53 -08:00
|
|
|
|
("folder_one folder_one_one folder_one_one_one " "folder_one_one_one"),
|
|
|
|
|
|
("folder_one folder_one_one folder_one_one_one sms_template_nested"),
|
2023-12-08 13:30:04 -08:00
|
|
|
|
("folder_one folder_one_two " "folder_one_two"),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_two folder_two Empty",
|
|
|
|
|
|
("sms_template_one " "sms_template_one " "Text message template"),
|
|
|
|
|
|
("sms_template_two " "sms_template_two " "Text message template"),
|
|
|
|
|
|
"email_template_one email_template_one Email template",
|
|
|
|
|
|
"email_template_two email_template_two Email template",
|
2019-11-13 13:38:02 +00:00
|
|
|
|
],
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one folder_one 2 folders",
|
|
|
|
|
|
"folder_two folder_two Empty",
|
|
|
|
|
|
"sms_template_one sms_template_one Text message template",
|
|
|
|
|
|
"sms_template_two sms_template_two Text message template",
|
|
|
|
|
|
"email_template_one email_template_one Email template",
|
|
|
|
|
|
"email_template_two email_template_two Email template",
|
2019-11-13 13:38:02 +00:00
|
|
|
|
],
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one",
|
|
|
|
|
|
"folder_one_one",
|
|
|
|
|
|
"folder_one_one_one",
|
|
|
|
|
|
"folder_one_two",
|
|
|
|
|
|
"folder_two",
|
|
|
|
|
|
"sms_template_one",
|
|
|
|
|
|
"sms_template_two",
|
|
|
|
|
|
"email_template_one",
|
|
|
|
|
|
"email_template_two",
|
2019-11-13 13:38:02 +00:00
|
|
|
|
],
|
|
|
|
|
|
None,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"Templates – service one – Notify.gov",
|
|
|
|
|
|
"Templates",
|
2019-11-13 13:38:02 +00:00
|
|
|
|
[],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{"template_type": "all"},
|
|
|
|
|
|
["Email", "Text message"],
|
2019-11-13 13:38:02 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one folder_one 2 folders",
|
2023-12-08 13:30:04 -08:00
|
|
|
|
("folder_one folder_one_one " "folder_one_one"),
|
2023-12-08 13:38:53 -08:00
|
|
|
|
("folder_one folder_one_one folder_one_one_one " "folder_one_one_one"),
|
|
|
|
|
|
("folder_one folder_one_one folder_one_one_one sms_template_nested"),
|
2023-12-08 13:30:04 -08:00
|
|
|
|
("folder_one folder_one_two " "folder_one_two"),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_two folder_two Empty",
|
|
|
|
|
|
"sms_template_one sms_template_one Text message template",
|
|
|
|
|
|
"sms_template_two sms_template_two Text message template",
|
|
|
|
|
|
"email_template_one email_template_one Email template",
|
|
|
|
|
|
"email_template_two email_template_two Email template",
|
2018-11-19 16:52:21 +00:00
|
|
|
|
],
|
2018-11-22 17:32:28 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one folder_one 2 folders",
|
|
|
|
|
|
"folder_two folder_two Empty",
|
|
|
|
|
|
"sms_template_one sms_template_one Text message template",
|
|
|
|
|
|
"sms_template_two sms_template_two Text message template",
|
|
|
|
|
|
"email_template_one email_template_one Email template",
|
|
|
|
|
|
"email_template_two email_template_two Email template",
|
2018-11-22 17:32:28 +00:00
|
|
|
|
],
|
2018-11-22 17:57:05 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one",
|
|
|
|
|
|
"folder_one_one",
|
|
|
|
|
|
"folder_one_one_one",
|
|
|
|
|
|
"folder_one_two",
|
|
|
|
|
|
"folder_two",
|
|
|
|
|
|
"sms_template_one",
|
|
|
|
|
|
"sms_template_two",
|
|
|
|
|
|
"email_template_one",
|
|
|
|
|
|
"email_template_two",
|
2018-11-22 17:57:05 +00:00
|
|
|
|
],
|
2018-11-19 16:52:21 +00:00
|
|
|
|
None,
|
2018-11-05 16:58:53 +00:00
|
|
|
|
),
|
|
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"Templates – service one – Notify.gov",
|
|
|
|
|
|
"Templates",
|
2019-01-30 10:09:04 +00:00
|
|
|
|
[],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{"template_type": "sms"},
|
|
|
|
|
|
["All", "Email"],
|
2018-11-12 11:46:39 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one folder_one 1 folder",
|
2023-12-08 13:30:04 -08:00
|
|
|
|
("folder_one folder_one_one " "folder_one_one"),
|
2023-12-08 13:38:53 -08:00
|
|
|
|
("folder_one folder_one_one folder_one_one_one " "folder_one_one_one"),
|
|
|
|
|
|
("folder_one folder_one_one folder_one_one_one sms_template_nested"),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"sms_template_one sms_template_one Text message template",
|
|
|
|
|
|
"sms_template_two sms_template_two Text message template",
|
2018-11-12 11:46:39 +00:00
|
|
|
|
],
|
2018-11-22 17:32:28 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one folder_one 1 folder",
|
|
|
|
|
|
"sms_template_one sms_template_one Text message template",
|
|
|
|
|
|
"sms_template_two sms_template_two Text message template",
|
2018-11-22 17:32:28 +00:00
|
|
|
|
],
|
2018-11-22 17:57:05 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one",
|
|
|
|
|
|
"folder_one_one",
|
|
|
|
|
|
"folder_one_one_one",
|
|
|
|
|
|
"sms_template_one",
|
|
|
|
|
|
"sms_template_two",
|
2018-11-22 17:57:05 +00:00
|
|
|
|
],
|
2018-11-19 16:52:21 +00:00
|
|
|
|
None,
|
2018-11-05 16:58:53 +00:00
|
|
|
|
),
|
2018-11-12 14:43:47 +00:00
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one – Templates – service one – Notify.gov",
|
|
|
|
|
|
"Templates folder_one",
|
|
|
|
|
|
[{"template_type": "all"}],
|
|
|
|
|
|
{"template_folder_id": PARENT_FOLDER_ID},
|
|
|
|
|
|
["Email", "Text message"],
|
2018-11-12 14:43:47 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one_one folder_one_one 1 folder",
|
2023-12-08 13:38:53 -08:00
|
|
|
|
("folder_one_one folder_one_one_one " "folder_one_one_one"),
|
|
|
|
|
|
("folder_one_one folder_one_one_one sms_template_nested"),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one_two folder_one_two Empty",
|
2018-11-12 14:43:47 +00:00
|
|
|
|
],
|
2018-11-22 17:32:28 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one_one folder_one_one 1 folder",
|
|
|
|
|
|
"folder_one_two folder_one_two Empty",
|
2018-11-22 17:32:28 +00:00
|
|
|
|
],
|
2018-11-22 17:57:05 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one_one",
|
|
|
|
|
|
"folder_one_one_one",
|
|
|
|
|
|
"folder_one_two",
|
2018-11-22 17:57:05 +00:00
|
|
|
|
],
|
2018-11-19 16:52:21 +00:00
|
|
|
|
None,
|
2018-11-12 14:43:47 +00:00
|
|
|
|
),
|
2018-11-05 16:58:53 +00:00
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one – Templates – service one – Notify.gov",
|
|
|
|
|
|
"Templates folder_one",
|
|
|
|
|
|
[{"template_type": "sms"}],
|
|
|
|
|
|
{"template_type": "sms", "template_folder_id": PARENT_FOLDER_ID},
|
|
|
|
|
|
["All", "Email"],
|
2018-11-12 11:46:39 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one_one folder_one_one 1 folder",
|
2023-12-08 13:38:53 -08:00
|
|
|
|
("folder_one_one folder_one_one_one " "folder_one_one_one"),
|
|
|
|
|
|
("folder_one_one folder_one_one_one sms_template_nested"),
|
2018-11-12 11:46:39 +00:00
|
|
|
|
],
|
2018-11-22 17:32:28 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one_one folder_one_one 1 folder",
|
2018-11-22 17:32:28 +00:00
|
|
|
|
],
|
2023-12-08 13:38:53 -08:00
|
|
|
|
["folder_one_one", "folder_one_one_one"],
|
2018-11-19 16:52:21 +00:00
|
|
|
|
None,
|
2018-11-05 16:58:53 +00:00
|
|
|
|
),
|
2018-11-12 14:43:47 +00:00
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one – Templates – service one – Notify.gov",
|
|
|
|
|
|
"Templates folder_one",
|
|
|
|
|
|
[{"template_type": "email"}],
|
|
|
|
|
|
{"template_type": "email", "template_folder_id": PARENT_FOLDER_ID},
|
|
|
|
|
|
["All", "Text message"],
|
2018-11-12 14:43:47 +00:00
|
|
|
|
[],
|
2018-11-22 17:32:28 +00:00
|
|
|
|
[],
|
2018-11-22 17:57:05 +00:00
|
|
|
|
[],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"There are no email templates in this folder",
|
2018-11-12 14:43:47 +00:00
|
|
|
|
),
|
2018-11-05 16:58:53 +00:00
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one_one – folder_one – Templates – service one – Notify.gov",
|
|
|
|
|
|
"Templates folder_one folder_one_one",
|
2019-01-30 10:09:04 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{"template_type": "all"},
|
|
|
|
|
|
{"template_type": "all", "template_folder_id": PARENT_FOLDER_ID},
|
2019-01-30 10:09:04 +00:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{"template_folder_id": CHILD_FOLDER_ID},
|
|
|
|
|
|
["Email", "Text message"],
|
2018-11-12 12:52:41 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one_one_one folder_one_one_one 1 template",
|
2023-12-08 13:38:53 -08:00
|
|
|
|
("folder_one_one_one sms_template_nested"),
|
2018-11-12 12:52:41 +00:00
|
|
|
|
],
|
2018-11-22 17:32:28 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one_one_one folder_one_one_one 1 template",
|
2018-11-22 17:32:28 +00:00
|
|
|
|
],
|
2023-12-08 13:38:53 -08:00
|
|
|
|
["folder_one_one_one"],
|
2018-11-19 16:52:21 +00:00
|
|
|
|
None,
|
2018-11-12 12:52:41 +00:00
|
|
|
|
),
|
|
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one_one_one – folder_one_one – folder_one – Templates – service one – Notify.gov",
|
|
|
|
|
|
"Templates folder_one folder_one_one folder_one_one_one",
|
2019-01-30 10:09:04 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{"template_type": "all"},
|
|
|
|
|
|
{"template_type": "all", "template_folder_id": PARENT_FOLDER_ID},
|
|
|
|
|
|
{"template_type": "all", "template_folder_id": CHILD_FOLDER_ID},
|
2019-01-30 10:09:04 +00:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{"template_folder_id": GRANDCHILD_FOLDER_ID},
|
|
|
|
|
|
["Email", "Text message"],
|
2018-11-12 12:52:41 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"sms_template_nested sms_template_nested Text message template",
|
2018-11-12 12:52:41 +00:00
|
|
|
|
],
|
2018-11-22 17:32:28 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"sms_template_nested sms_template_nested Text message template",
|
2018-11-22 17:32:28 +00:00
|
|
|
|
],
|
2018-11-22 17:57:05 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"sms_template_nested",
|
2018-11-22 17:57:05 +00:00
|
|
|
|
],
|
2018-11-19 16:52:21 +00:00
|
|
|
|
None,
|
|
|
|
|
|
),
|
2019-02-05 13:49:15 +00:00
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_one_one_one – folder_one_one – folder_one – Templates – service one – Notify.gov",
|
|
|
|
|
|
"Templates folder_one folder_one_one folder_one_one_one",
|
2019-02-05 13:49:15 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{"template_type": "email"},
|
|
|
|
|
|
{"template_type": "email", "template_folder_id": PARENT_FOLDER_ID},
|
|
|
|
|
|
{"template_type": "email", "template_folder_id": CHILD_FOLDER_ID},
|
2019-02-05 13:49:15 +00:00
|
|
|
|
],
|
|
|
|
|
|
{
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"template_type": "email",
|
|
|
|
|
|
"template_folder_id": GRANDCHILD_FOLDER_ID,
|
2019-02-05 13:49:15 +00:00
|
|
|
|
},
|
2023-08-25 09:12:23 -07:00
|
|
|
|
["All", "Text message"],
|
2019-02-05 13:49:15 +00:00
|
|
|
|
[],
|
|
|
|
|
|
[],
|
|
|
|
|
|
[],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"There are no email templates in this folder",
|
2019-02-05 13:49:15 +00:00
|
|
|
|
),
|
2018-11-19 16:52:21 +00:00
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_two – Templates – service one – Notify.gov",
|
|
|
|
|
|
"Templates folder_two",
|
|
|
|
|
|
[{"template_type": "all"}],
|
|
|
|
|
|
{"template_folder_id": FOLDER_TWO_ID},
|
|
|
|
|
|
["Email", "Text message"],
|
2018-11-19 16:52:21 +00:00
|
|
|
|
[],
|
2018-11-22 17:32:28 +00:00
|
|
|
|
[],
|
2018-11-22 17:57:05 +00:00
|
|
|
|
[],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"This folder is empty",
|
2018-11-20 12:13:50 +00:00
|
|
|
|
),
|
|
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_two – Templates – service one – Notify.gov",
|
|
|
|
|
|
"Templates folder_two",
|
|
|
|
|
|
[{"template_type": "sms"}],
|
|
|
|
|
|
{"template_folder_id": FOLDER_TWO_ID, "template_type": "sms"},
|
|
|
|
|
|
["All", "Email"],
|
2018-11-20 12:13:50 +00:00
|
|
|
|
[],
|
2018-11-22 17:32:28 +00:00
|
|
|
|
[],
|
2018-11-22 17:57:05 +00:00
|
|
|
|
[],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"This folder is empty",
|
2018-11-05 16:58:53 +00:00
|
|
|
|
),
|
2019-11-13 12:45:26 +00:00
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder_two – Templates – service one – Notify.gov",
|
|
|
|
|
|
"Templates folder_two",
|
|
|
|
|
|
[{"template_type": "all"}],
|
|
|
|
|
|
{"template_folder_id": FOLDER_TWO_ID, "template_type": "all"},
|
|
|
|
|
|
["Email", "Text message"],
|
2019-11-13 12:45:26 +00:00
|
|
|
|
[],
|
|
|
|
|
|
[],
|
|
|
|
|
|
[],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"This folder is empty",
|
2019-11-13 12:45:26 +00:00
|
|
|
|
),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
],
|
2018-11-05 16:58:53 +00:00
|
|
|
|
)
|
|
|
|
|
|
def test_should_show_templates_folder_page(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_has_no_jobs,
|
2021-04-13 13:59:51 +01:00
|
|
|
|
mock_get_no_api_keys,
|
2018-11-05 16:58:53 +00:00
|
|
|
|
service_one,
|
|
|
|
|
|
mocker,
|
2018-11-16 13:41:55 +00:00
|
|
|
|
expected_title_tag,
|
2018-11-05 16:58:53 +00:00
|
|
|
|
expected_page_title,
|
2018-11-16 13:41:55 +00:00
|
|
|
|
expected_parent_link_args,
|
2018-11-05 16:58:53 +00:00
|
|
|
|
extra_args,
|
|
|
|
|
|
expected_nav_links,
|
2018-11-12 11:50:53 +00:00
|
|
|
|
expected_items,
|
2018-11-22 17:32:28 +00:00
|
|
|
|
expected_displayed_items,
|
2018-11-22 17:57:05 +00:00
|
|
|
|
expected_searchable_text,
|
2018-11-19 16:52:21 +00:00
|
|
|
|
expected_empty_message,
|
2018-11-05 16:58:53 +00:00
|
|
|
|
):
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("folder_two", FOLDER_TWO_ID),
|
|
|
|
|
|
_folder("folder_one", PARENT_FOLDER_ID),
|
|
|
|
|
|
_folder("folder_one_two", parent=PARENT_FOLDER_ID),
|
|
|
|
|
|
_folder("folder_one_one", CHILD_FOLDER_ID, parent=PARENT_FOLDER_ID),
|
|
|
|
|
|
_folder("folder_one_one_one", GRANDCHILD_FOLDER_ID, parent=CHILD_FOLDER_ID),
|
2018-11-05 16:58:53 +00:00
|
|
|
|
]
|
2018-11-12 12:52:41 +00:00
|
|
|
|
mock_get_service_templates = mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.service_api_client.get_service_templates",
|
|
|
|
|
|
return_value={
|
|
|
|
|
|
"data": [
|
|
|
|
|
|
_template("sms", "sms_template_one"),
|
|
|
|
|
|
_template("sms", "sms_template_two"),
|
|
|
|
|
|
_template("email", "email_template_one"),
|
|
|
|
|
|
_template("email", "email_template_two"),
|
|
|
|
|
|
_template("sms", "sms_template_nested", parent=GRANDCHILD_FOLDER_ID),
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
2018-11-12 12:52:41 +00:00
|
|
|
|
)
|
2018-11-05 16:58:53 +00:00
|
|
|
|
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-11-05 16:58:53 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
2018-11-16 13:41:55 +00:00
|
|
|
|
_test_page_title=False,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
**extra_args,
|
2018-11-05 16:58:53 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select_one("title").text) == expected_title_tag
|
|
|
|
|
|
assert normalize_spaces(page.select_one("h1").text) == expected_page_title
|
2018-11-05 16:58:53 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert len(page.select("h1 a")) == len(expected_parent_link_args)
|
2019-01-30 10:09:04 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
for index, parent_link in enumerate(page.select("h1 a")):
|
|
|
|
|
|
assert parent_link["href"] == url_for(
|
|
|
|
|
|
"main.choose_template",
|
2018-11-16 13:41:55 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
**expected_parent_link_args[index],
|
2018-11-16 13:41:55 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
links_in_page = page.select(".pill a:not(.pill-item--selected)")
|
2018-11-05 16:58:53 +00:00
|
|
|
|
|
|
|
|
|
|
assert len(links_in_page) == len(expected_nav_links)
|
|
|
|
|
|
|
|
|
|
|
|
for index, expected_link in enumerate(expected_nav_links):
|
|
|
|
|
|
assert links_in_page[index].text.strip() == expected_link
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
all_page_items = page.select(".template-list-item")
|
|
|
|
|
|
all_page_items_styled_with_checkboxes = page.select(
|
|
|
|
|
|
".usa-checkbox.template-list-item"
|
|
|
|
|
|
)
|
2019-02-13 17:25:06 +00:00
|
|
|
|
|
|
|
|
|
|
assert len(all_page_items) == len(all_page_items_styled_with_checkboxes)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
checkboxes = page.select("input[name=templates_and_folders]")
|
|
|
|
|
|
unique_checkbox_values = set(item["value"] for item in checkboxes)
|
2018-11-22 17:32:28 +00:00
|
|
|
|
assert len(all_page_items) == len(expected_items)
|
2018-11-23 16:29:21 +00:00
|
|
|
|
assert len(checkboxes) == len(expected_items)
|
|
|
|
|
|
assert len(unique_checkbox_values) == len(expected_items)
|
2018-11-05 16:58:53 +00:00
|
|
|
|
|
2018-11-12 11:50:53 +00:00
|
|
|
|
for index, expected_item in enumerate(expected_items):
|
2018-11-22 17:32:28 +00:00
|
|
|
|
assert normalize_spaces(all_page_items[index].text) == expected_item
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
displayed_page_items = page.find_all(
|
|
|
|
|
|
lambda tag: (
|
|
|
|
|
|
tag.has_attr("class")
|
|
|
|
|
|
and "template-list-item" in tag["class"]
|
|
|
|
|
|
and "template-list-item-hidden-by-default" not in tag["class"]
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
2018-11-22 17:32:28 +00:00
|
|
|
|
assert len(displayed_page_items) == len(expected_displayed_items)
|
|
|
|
|
|
|
|
|
|
|
|
for index, expected_item in enumerate(expected_displayed_items):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert "/" not in expected_item # Yo dawg I heard you like tests…
|
2018-11-22 17:32:28 +00:00
|
|
|
|
assert normalize_spaces(displayed_page_items[index].text) == expected_item
|
2018-11-05 16:58:53 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
all_searchable_text = page.select(
|
|
|
|
|
|
"#template-list .template-list-item .live-search-relevant"
|
|
|
|
|
|
)
|
2018-11-22 17:57:05 +00:00
|
|
|
|
assert len(all_searchable_text) == len(expected_searchable_text)
|
|
|
|
|
|
|
|
|
|
|
|
for index, expected_item in enumerate(expected_searchable_text):
|
|
|
|
|
|
assert normalize_spaces(all_searchable_text[index].text) == expected_item
|
|
|
|
|
|
|
2018-11-19 16:52:21 +00:00
|
|
|
|
if expected_empty_message:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select_one(".template-list-empty").text) == (
|
2018-11-19 16:52:21 +00:00
|
|
|
|
expected_empty_message
|
|
|
|
|
|
)
|
|
|
|
|
|
else:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert not page.select(".template-list-empty")
|
2018-11-19 16:52:21 +00:00
|
|
|
|
|
2018-11-05 16:58:53 +00:00
|
|
|
|
mock_get_service_templates.assert_called_once_with(SERVICE_ONE_ID)
|
2018-11-08 15:53:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
2021-04-13 13:59:51 +01:00
|
|
|
|
def test_template_id_is_searchable_for_services_with_api_keys(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_has_no_jobs,
|
|
|
|
|
|
mock_get_api_keys,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
):
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("folder one", PARENT_FOLDER_ID),
|
2021-04-13 13:59:51 +01:00
|
|
|
|
]
|
2023-08-25 09:12:23 -07:00
|
|
|
|
template_1 = _template("sms", "template one")
|
|
|
|
|
|
template_2 = _template("sms", "template two", parent=PARENT_FOLDER_ID)
|
2021-04-13 13:59:51 +01:00
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.service_api_client.get_service_templates",
|
|
|
|
|
|
return_value={
|
|
|
|
|
|
"data": [
|
|
|
|
|
|
template_1,
|
|
|
|
|
|
template_2,
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
2021-04-13 13:59:51 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2021-04-13 13:59:51 +01:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
_test_page_title=False,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
assert [
|
|
|
|
|
|
normalize_spaces(item.text)
|
|
|
|
|
|
for item in page.select(
|
|
|
|
|
|
# Elements which the live search will filter by
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".template-list-item .live-search-relevant"
|
2021-04-13 13:59:51 +01:00
|
|
|
|
)
|
|
|
|
|
|
] == [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder one",
|
2021-04-13 13:59:51 +01:00
|
|
|
|
f'{template_1["id"]} template one',
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
assert [
|
|
|
|
|
|
normalize_spaces(item.text)
|
|
|
|
|
|
for item in page.select(
|
|
|
|
|
|
# Elements the user will see when first loading the page
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".template-list-item:not(.template-list-item-hidden-by-default)"
|
2021-04-13 13:59:51 +01:00
|
|
|
|
)
|
|
|
|
|
|
] == [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"folder one folder one 1 template",
|
2021-04-13 13:59:51 +01:00
|
|
|
|
f'template one {template_1["id"]} template one Text message template',
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
assert [
|
|
|
|
|
|
normalize_spaces(item.text)
|
|
|
|
|
|
for item in page.select(
|
|
|
|
|
|
# Text which should be hidden from all users
|
2023-08-28 12:12:21 -04:00
|
|
|
|
r".template-list-item .display-none"
|
2021-04-13 13:59:51 +01:00
|
|
|
|
)
|
2023-12-08 13:38:53 -08:00
|
|
|
|
] == [template_1["id"]]
|
2021-04-13 13:59:51 +01:00
|
|
|
|
|
|
|
|
|
|
mock_get_api_keys.assert_called_once_with(SERVICE_ONE_ID)
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-11-09 10:47:21 +00:00
|
|
|
|
def test_can_create_email_template_with_parent_folder(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request, mock_create_service_template
|
2018-11-09 10:47:21 +00:00
|
|
|
|
):
|
|
|
|
|
|
data = {
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"name": "new name",
|
|
|
|
|
|
"subject": "Food incoming!",
|
|
|
|
|
|
"template_content": "here's a burrito 🌯",
|
|
|
|
|
|
"template_type": "email",
|
|
|
|
|
|
"service": SERVICE_ONE_ID,
|
|
|
|
|
|
"parent_folder_id": PARENT_FOLDER_ID,
|
2018-11-09 10:47:21 +00:00
|
|
|
|
}
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request.post(
|
|
|
|
|
|
".add_service_template",
|
|
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
template_type="email",
|
|
|
|
|
|
template_folder_id=PARENT_FOLDER_ID,
|
|
|
|
|
|
_data=data,
|
|
|
|
|
|
_expected_redirect=url_for(
|
|
|
|
|
|
"main.view_template",
|
|
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
template_id="new%20name",
|
|
|
|
|
|
),
|
|
|
|
|
|
)
|
2018-11-09 10:47:21 +00:00
|
|
|
|
mock_create_service_template.assert_called_once_with(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
data["name"],
|
|
|
|
|
|
data["template_type"],
|
|
|
|
|
|
data["template_content"],
|
2018-11-09 10:47:21 +00:00
|
|
|
|
SERVICE_ONE_ID,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
data["subject"],
|
|
|
|
|
|
data["parent_folder_id"],
|
|
|
|
|
|
)
|
2018-11-12 16:37:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
2018-11-16 13:41:55 +00:00
|
|
|
|
def test_get_manage_folder_page(
|
|
|
|
|
|
client_request,
|
2019-03-15 14:13:27 +00:00
|
|
|
|
active_user_with_permissions,
|
2018-11-16 13:41:55 +00:00
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_template_folders,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mocker,
|
2018-11-16 13:41:55 +00:00
|
|
|
|
):
|
2018-11-12 16:37:37 +00:00
|
|
|
|
folder_id = str(uuid.uuid4())
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("folder_two", folder_id, None, [active_user_with_permissions["id"]]),
|
2018-11-12 16:37:37 +00:00
|
|
|
|
]
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.user.Users.client_method",
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
return_value=[active_user_with_permissions],
|
|
|
|
|
|
)
|
2018-11-12 16:37:37 +00:00
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.manage_template_folder",
|
|
|
|
|
|
service_id=service_one["id"],
|
2018-11-16 14:09:14 +00:00
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
_test_page_title=False,
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select_one("title").text) == (
|
|
|
|
|
|
"folder_two – Templates – service one – Notify.gov"
|
|
|
|
|
|
)
|
|
|
|
|
|
assert page.select_one("input[name=name]")["value"] == "folder_two"
|
|
|
|
|
|
delete_link = page.find("a", string="Delete this folder")
|
|
|
|
|
|
expected_delete_url = "/services/{}/templates/folders/{}/delete".format(
|
|
|
|
|
|
service_one["id"], folder_id
|
2018-11-12 16:37:37 +00:00
|
|
|
|
)
|
2018-11-13 15:49:25 +00:00
|
|
|
|
assert expected_delete_url in delete_link["href"]
|
2018-11-13 16:05:05 +00:00
|
|
|
|
|
2019-03-18 16:13:39 +00:00
|
|
|
|
|
|
|
|
|
|
def test_get_manage_folder_viewing_permissions_for_users(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_template_folders,
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
mock_get_users_by_service,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mocker,
|
2019-03-18 16:13:39 +00:00
|
|
|
|
):
|
|
|
|
|
|
folder_id = str(uuid.uuid4())
|
2019-12-19 16:59:07 +00:00
|
|
|
|
team_member = create_active_user_view_permissions(with_unique_id=True)
|
|
|
|
|
|
team_member_2 = create_active_user_view_permissions(with_unique_id=True)
|
|
|
|
|
|
|
2019-03-18 16:13:39 +00:00
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder(
|
|
|
|
|
|
"folder_two",
|
|
|
|
|
|
folder_id,
|
|
|
|
|
|
None,
|
|
|
|
|
|
[active_user_with_permissions["id"], team_member_2["id"]],
|
|
|
|
|
|
),
|
2019-03-18 16:13:39 +00:00
|
|
|
|
]
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.user.Users.client_method",
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
return_value=[active_user_with_permissions, team_member, team_member_2],
|
|
|
|
|
|
)
|
2019-03-18 16:13:39 +00:00
|
|
|
|
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.manage_template_folder",
|
|
|
|
|
|
service_id=service_one["id"],
|
2019-03-18 16:13:39 +00:00
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
_test_page_title=False,
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select_one("title").text) == (
|
|
|
|
|
|
"folder_two – Templates – service one – Notify.gov"
|
|
|
|
|
|
)
|
|
|
|
|
|
form_labels = page.select("legend.usa-legend")
|
|
|
|
|
|
assert (
|
|
|
|
|
|
normalize_spaces(form_labels[0].text) == "Team members who can see this folder"
|
2019-03-18 16:13:39 +00:00
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
checkboxes = page.select("input[name=users_with_permission]")
|
2019-03-18 16:13:39 +00:00
|
|
|
|
|
|
|
|
|
|
assert len(checkboxes) == 2
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert checkboxes[0]["value"] == team_member["id"]
|
2019-03-18 16:13:39 +00:00
|
|
|
|
assert "checked" not in checkboxes[0].attrs
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert checkboxes[1]["value"] == team_member_2["id"]
|
2019-03-18 16:13:39 +00:00
|
|
|
|
assert "checked" in checkboxes[1].attrs
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert (
|
|
|
|
|
|
"Test User"
|
|
|
|
|
|
in page.find_all("label", {"for": "users_with_permission-1"})[0].text
|
|
|
|
|
|
)
|
2019-03-14 17:21:18 +00:00
|
|
|
|
|
2018-11-13 16:05:05 +00:00
|
|
|
|
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
def test_get_manage_folder_viewing_permissions_for_users_not_visible_when_no_manage_settings_permission(
|
2019-04-02 12:07:11 +01:00
|
|
|
|
client_request,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_template_folders,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mocker,
|
2019-04-02 12:07:11 +01:00
|
|
|
|
):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
active_user_with_permissions["permissions"][SERVICE_ONE_ID] = [
|
|
|
|
|
|
"send_texts",
|
|
|
|
|
|
"send_emails",
|
|
|
|
|
|
"manage_templates",
|
|
|
|
|
|
"manage_api_keys",
|
|
|
|
|
|
"view_activity",
|
2019-04-02 12:07:11 +01:00
|
|
|
|
]
|
|
|
|
|
|
folder_id = str(uuid.uuid4())
|
2019-12-19 16:59:07 +00:00
|
|
|
|
team_member = create_active_user_view_permissions(with_unique_id=True)
|
|
|
|
|
|
team_member_2 = create_active_user_view_permissions(with_unique_id=True)
|
2019-04-02 12:07:11 +01:00
|
|
|
|
service_one["permissions"] += ["edit_folder_permissions"]
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{
|
|
|
|
|
|
"id": folder_id,
|
|
|
|
|
|
"name": "folder_two",
|
|
|
|
|
|
"parent_id": None,
|
|
|
|
|
|
"users_with_permission": [
|
|
|
|
|
|
active_user_with_permissions["id"],
|
|
|
|
|
|
team_member_2["id"],
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
2019-04-02 12:07:11 +01:00
|
|
|
|
]
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.user.Users.client_method",
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
return_value=[active_user_with_permissions, team_member, team_member_2],
|
|
|
|
|
|
)
|
2019-04-02 12:07:11 +01:00
|
|
|
|
|
2023-01-19 17:29:21 -05:00
|
|
|
|
client_request.login(active_user_with_permissions)
|
2019-04-02 12:07:11 +01:00
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.manage_template_folder",
|
|
|
|
|
|
service_id=service_one["id"],
|
2019-04-02 12:07:11 +01:00
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
_test_page_title=False,
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select_one("title").text) == (
|
|
|
|
|
|
"folder_two – Templates – service one – Notify.gov"
|
2019-04-02 12:07:11 +01:00
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
form_labels = page.select("legend[class=form-label]")
|
2019-04-02 12:07:11 +01:00
|
|
|
|
assert len(form_labels) == 0
|
2023-08-25 09:12:23 -07:00
|
|
|
|
checkboxes = page.select("input[name=users_with_permission]")
|
2019-04-02 12:07:11 +01:00
|
|
|
|
assert len(checkboxes) == 0
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-04-02 12:26:41 +01:00
|
|
|
|
def test_get_manage_folder_viewing_permissions_for_users_not_visible_for_services_with_one_user(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_template_folders,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mocker,
|
2019-04-02 12:26:41 +01:00
|
|
|
|
):
|
|
|
|
|
|
folder_id = str(uuid.uuid4())
|
|
|
|
|
|
service_one["permissions"] += ["edit_folder_permissions"]
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{
|
|
|
|
|
|
"id": folder_id,
|
|
|
|
|
|
"name": "folder_two",
|
|
|
|
|
|
"parent_id": None,
|
|
|
|
|
|
"users_with_permission": [active_user_with_permissions["id"]],
|
|
|
|
|
|
},
|
2019-04-02 12:26:41 +01:00
|
|
|
|
]
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.user.Users.client_method",
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
return_value=[active_user_with_permissions],
|
|
|
|
|
|
)
|
2019-04-02 12:26:41 +01:00
|
|
|
|
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.manage_template_folder",
|
|
|
|
|
|
service_id=service_one["id"],
|
2019-04-02 12:26:41 +01:00
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
_test_page_title=False,
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select_one("title").text) == (
|
|
|
|
|
|
"folder_two – Templates – service one – Notify.gov"
|
2019-04-02 12:26:41 +01:00
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
form_labels = page.select("legend[class=form-label]")
|
2019-04-02 12:26:41 +01:00
|
|
|
|
assert len(form_labels) == 0
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
def test_manage_folder_page_404s(
|
|
|
|
|
|
client_request, service_one, mock_get_template_folders
|
|
|
|
|
|
):
|
2018-11-13 16:05:05 +00:00
|
|
|
|
client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.manage_template_folder",
|
|
|
|
|
|
service_id=service_one["id"],
|
2018-11-13 16:05:05 +00:00
|
|
|
|
template_folder_id=str(uuid.uuid4()),
|
|
|
|
|
|
_expected_status=404,
|
|
|
|
|
|
)
|
2018-11-12 16:37:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
2019-02-11 16:06:46 +00:00
|
|
|
|
def test_get_manage_folder_page_no_permissions(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
active_user_view_permissions,
|
|
|
|
|
|
):
|
|
|
|
|
|
client_request.login(active_user_view_permissions)
|
2018-11-12 16:37:37 +00:00
|
|
|
|
client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.manage_template_folder",
|
2019-02-11 16:06:46 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
template_folder_id=PARENT_FOLDER_ID,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_expected_status=403,
|
2018-11-12 16:37:37 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"endpoint",
|
|
|
|
|
|
[
|
|
|
|
|
|
"main.manage_template_folder",
|
|
|
|
|
|
"main.delete_template_folder",
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
2019-03-22 17:33:47 +00:00
|
|
|
|
def test_user_access_denied_to_template_folder_actions_without_folder_permission(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mocker,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
endpoint,
|
2019-03-22 17:33:47 +00:00
|
|
|
|
):
|
|
|
|
|
|
mock = mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.service.Service.get_template_folder_with_user_permission_or_403",
|
|
|
|
|
|
side_effect=lambda *args: abort(403),
|
2019-03-22 17:33:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
folder_id = str(uuid.uuid4())
|
|
|
|
|
|
client_request.get(
|
|
|
|
|
|
endpoint,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_id=service_one["id"],
|
2019-03-22 17:33:47 +00:00
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
_expected_status=403,
|
|
|
|
|
|
_test_page_title=False,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
mock.assert_called_once_with(folder_id, User(active_user_with_permissions))
|
2019-03-22 17:33:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"endpoint",
|
|
|
|
|
|
[
|
|
|
|
|
|
"main.check_notification",
|
|
|
|
|
|
"main.confirm_redact_template",
|
|
|
|
|
|
"main.delete_service_template",
|
|
|
|
|
|
"main.edit_service_template",
|
|
|
|
|
|
"main.send_messages",
|
|
|
|
|
|
"main.send_one_off",
|
|
|
|
|
|
"main.set_sender",
|
|
|
|
|
|
"main.set_template_sender",
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
2019-03-22 17:33:47 +00:00
|
|
|
|
def test_user_access_denied_to_template_actions_without_folder_permission(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request, active_user_with_permissions, service_one, mocker, endpoint
|
2019-03-22 17:33:47 +00:00
|
|
|
|
):
|
|
|
|
|
|
mock = mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.service.Service.get_template_with_user_permission_or_403",
|
|
|
|
|
|
side_effect=lambda *args: abort(403),
|
2019-03-22 17:33:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
template_id = str(uuid.uuid4())
|
|
|
|
|
|
client_request.get(
|
|
|
|
|
|
endpoint,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_id=service_one["id"],
|
2019-03-22 17:33:47 +00:00
|
|
|
|
template_id=template_id,
|
|
|
|
|
|
_expected_status=403,
|
|
|
|
|
|
_test_page_title=False,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
mock.assert_called_once_with(template_id, User(active_user_with_permissions))
|
2019-03-22 17:33:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
def test_rename_folder(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
):
|
|
|
|
|
|
mock_update = mocker.patch("app.template_folder_api_client.update_template_folder")
|
2018-11-12 16:37:37 +00:00
|
|
|
|
folder_id = str(uuid.uuid4())
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("folder_two", folder_id, None, [active_user_with_permissions["id"]])
|
2018-11-12 16:37:37 +00:00
|
|
|
|
]
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.user.Users.client_method",
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
return_value=[active_user_with_permissions],
|
|
|
|
|
|
)
|
2018-11-12 16:37:37 +00:00
|
|
|
|
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.manage_template_folder",
|
|
|
|
|
|
service_id=service_one["id"],
|
2018-11-12 16:37:37 +00:00
|
|
|
|
template_folder_id=folder_id,
|
2019-03-18 16:13:39 +00:00
|
|
|
|
_data={"name": "new beautiful name", "users_with_permission": []},
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_expected_redirect=url_for(
|
|
|
|
|
|
"main.choose_template",
|
|
|
|
|
|
service_id=service_one["id"],
|
|
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
),
|
2018-11-12 16:37:37 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
mock_update.assert_called_once_with(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_one["id"],
|
2018-11-12 16:37:37 +00:00
|
|
|
|
folder_id,
|
2019-03-15 17:19:18 +00:00
|
|
|
|
name="new beautiful name",
|
2023-08-25 09:12:23 -07:00
|
|
|
|
users_with_permission=None,
|
2019-03-15 17:19:18 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_manage_folder_users(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mocker,
|
2019-03-15 17:19:18 +00:00
|
|
|
|
):
|
2019-12-19 16:59:07 +00:00
|
|
|
|
team_member = create_active_user_view_permissions(with_unique_id=True)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mock_update = mocker.patch("app.template_folder_api_client.update_template_folder")
|
2019-03-15 17:19:18 +00:00
|
|
|
|
folder_id = str(uuid.uuid4())
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder(
|
|
|
|
|
|
"folder_two",
|
|
|
|
|
|
folder_id,
|
|
|
|
|
|
None,
|
|
|
|
|
|
[active_user_with_permissions["id"], team_member["id"]],
|
|
|
|
|
|
)
|
2019-03-15 17:19:18 +00:00
|
|
|
|
]
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.user.Users.client_method",
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
return_value=[active_user_with_permissions, team_member],
|
|
|
|
|
|
)
|
2019-03-15 17:19:18 +00:00
|
|
|
|
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.manage_template_folder",
|
|
|
|
|
|
service_id=service_one["id"],
|
2019-03-15 17:19:18 +00:00
|
|
|
|
template_folder_id=folder_id,
|
2019-03-18 16:13:39 +00:00
|
|
|
|
_data={"name": "new beautiful name", "users_with_permission": []},
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_expected_redirect=url_for(
|
|
|
|
|
|
"main.choose_template",
|
|
|
|
|
|
service_id=service_one["id"],
|
|
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
),
|
2019-03-15 17:19:18 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
mock_update.assert_called_once_with(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_one["id"],
|
2019-03-15 17:19:18 +00:00
|
|
|
|
folder_id,
|
|
|
|
|
|
name="new beautiful name",
|
2023-08-25 09:12:23 -07:00
|
|
|
|
users_with_permission=[active_user_with_permissions["id"]],
|
2018-11-12 16:37:37 +00:00
|
|
|
|
)
|
2018-11-13 17:56:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
2019-04-02 12:26:41 +01:00
|
|
|
|
def test_manage_folder_users_doesnt_change_permissions_current_user_cannot_manage_users(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mocker,
|
2019-04-02 12:26:41 +01:00
|
|
|
|
):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
active_user_with_permissions["permissions"][SERVICE_ONE_ID] = [
|
|
|
|
|
|
"send_texts",
|
|
|
|
|
|
"send_emails",
|
|
|
|
|
|
"manage_templates",
|
|
|
|
|
|
"manage_api_keys",
|
|
|
|
|
|
"view_activity",
|
2019-04-02 12:26:41 +01:00
|
|
|
|
]
|
2019-12-19 16:59:07 +00:00
|
|
|
|
team_member = create_active_user_view_permissions(with_unique_id=True)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mock_update = mocker.patch("app.template_folder_api_client.update_template_folder")
|
2019-04-02 12:26:41 +01:00
|
|
|
|
folder_id = str(uuid.uuid4())
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{
|
|
|
|
|
|
"id": folder_id,
|
|
|
|
|
|
"name": "folder_two",
|
|
|
|
|
|
"parent_id": None,
|
|
|
|
|
|
"users_with_permission": [
|
|
|
|
|
|
active_user_with_permissions["id"],
|
|
|
|
|
|
team_member["id"],
|
|
|
|
|
|
],
|
|
|
|
|
|
}
|
2019-04-02 12:26:41 +01:00
|
|
|
|
]
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.models.user.Users.client_method",
|
Make user API client return JSON, not a model
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
2019-05-23 15:27:35 +01:00
|
|
|
|
return_value=[active_user_with_permissions, team_member],
|
|
|
|
|
|
)
|
2019-04-02 12:26:41 +01:00
|
|
|
|
|
2023-01-19 17:29:21 -05:00
|
|
|
|
client_request.login(active_user_with_permissions)
|
2019-04-02 12:26:41 +01:00
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.manage_template_folder",
|
|
|
|
|
|
service_id=service_one["id"],
|
2019-04-02 12:26:41 +01:00
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
_data={"name": "new beautiful name", "users_with_permission": []},
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_expected_redirect=url_for(
|
|
|
|
|
|
"main.choose_template",
|
|
|
|
|
|
service_id=service_one["id"],
|
|
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
),
|
2019-04-02 12:26:41 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
mock_update.assert_called_once_with(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_one["id"],
|
2019-04-02 12:26:41 +01:00
|
|
|
|
folder_id,
|
|
|
|
|
|
name="new beautiful name",
|
2023-08-25 09:12:23 -07:00
|
|
|
|
users_with_permission=None,
|
2019-04-02 12:26:41 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-11-16 15:10:12 +00:00
|
|
|
|
def test_delete_template_folder_should_request_confirmation(
|
2022-05-26 18:47:42 +01:00
|
|
|
|
client_request,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
mock_get_service_templates_when_no_templates_exist,
|
2018-11-16 15:10:12 +00:00
|
|
|
|
):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mocker.patch("app.models.service.Service.active_users", [])
|
2018-11-16 15:10:12 +00:00
|
|
|
|
folder_id = str(uuid.uuid4())
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mock_get_template_folders.side_effect = [
|
|
|
|
|
|
[
|
|
|
|
|
|
_folder("sacrifice", folder_id, None),
|
|
|
|
|
|
],
|
|
|
|
|
|
[],
|
|
|
|
|
|
]
|
2018-11-16 15:10:12 +00:00
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.delete_template_folder",
|
|
|
|
|
|
service_id=service_one["id"],
|
2018-11-16 14:09:14 +00:00
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
_test_page_title=False,
|
2018-11-16 15:10:12 +00:00
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select(".banner-dangerous")[0].text) == (
|
|
|
|
|
|
"Are you sure you want to delete the ‘sacrifice’ folder? " "Yes, delete"
|
2018-11-16 15:10:12 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.select_one("input[name=name]")["value"] == "sacrifice"
|
2018-11-20 10:37:33 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert len(page.select("main form")) == 2
|
|
|
|
|
|
assert len(page.select("main button")) == 2
|
2018-11-20 10:37:33 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert "action" not in page.select("main form")[0]
|
|
|
|
|
|
assert normalize_spaces(page.select("main form button")[0].text) == "Yes, delete"
|
2018-11-20 10:37:33 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.select("main form")[1]["action"] == url_for(
|
|
|
|
|
|
"main.manage_template_folder",
|
|
|
|
|
|
service_id=service_one["id"],
|
2018-11-20 10:37:33 +00:00
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select("main form button")[1].text) == "Save"
|
2018-11-16 15:10:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_delete_template_folder_should_detect_non_empty_folder_on_get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request, service_one, mock_get_template_folders, mocker
|
2018-11-16 15:10:12 +00:00
|
|
|
|
):
|
|
|
|
|
|
folder_id = str(uuid.uuid4())
|
2018-11-16 15:53:35 +00:00
|
|
|
|
mock_get_template_folders.side_effect = [
|
2019-04-02 15:23:46 +01:00
|
|
|
|
[_folder("can't touch me", folder_id, None)],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
[],
|
2018-11-16 15:53:35 +00:00
|
|
|
|
]
|
2018-11-16 15:10:12 +00:00
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.service_api_client.get_service_templates",
|
|
|
|
|
|
return_value={"data": [create_template(folder=folder_id)]},
|
2018-11-16 15:10:12 +00:00
|
|
|
|
)
|
2018-11-16 15:53:35 +00:00
|
|
|
|
client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.delete_template_folder",
|
|
|
|
|
|
service_id=service_one["id"],
|
2018-11-16 15:53:35 +00:00
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
_expected_redirect=url_for(
|
|
|
|
|
|
"main.choose_template",
|
|
|
|
|
|
template_type="all",
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_id=service_one["id"],
|
2018-11-16 15:53:35 +00:00
|
|
|
|
template_folder_id=folder_id,
|
|
|
|
|
|
),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_expected_status=302,
|
2018-11-16 15:10:12 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"parent_folder_id",
|
2023-09-08 17:58:06 -04:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
None,
|
|
|
|
|
|
PARENT_FOLDER_ID,
|
2023-09-08 17:58:06 -04:00
|
|
|
|
],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
)
|
2022-05-26 18:47:42 +01:00
|
|
|
|
def test_delete_folder(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
parent_folder_id,
|
|
|
|
|
|
mock_get_service_templates_when_no_templates_exist,
|
|
|
|
|
|
):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mock_delete = mocker.patch("app.template_folder_api_client.delete_template_folder")
|
2018-11-13 17:56:47 +00:00
|
|
|
|
folder_id = str(uuid.uuid4())
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mock_get_template_folders.side_effect = [
|
|
|
|
|
|
[
|
|
|
|
|
|
_folder("sacrifice", folder_id, parent_folder_id),
|
|
|
|
|
|
],
|
|
|
|
|
|
[],
|
|
|
|
|
|
]
|
2018-11-13 17:56:47 +00:00
|
|
|
|
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.delete_template_folder",
|
|
|
|
|
|
service_id=service_one["id"],
|
2018-11-13 17:56:47 +00:00
|
|
|
|
template_folder_id=folder_id,
|
2018-11-20 10:44:09 +00:00
|
|
|
|
_expected_redirect=url_for(
|
|
|
|
|
|
"main.choose_template",
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_id=service_one["id"],
|
2018-11-20 10:44:09 +00:00
|
|
|
|
template_folder_id=parent_folder_id,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
),
|
2018-11-13 17:56:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mock_delete.assert_called_once_with(service_one["id"], folder_id)
|
2018-11-19 17:33:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"user",
|
|
|
|
|
|
[
|
|
|
|
|
|
pytest.param(create_active_user_with_permissions()),
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
2018-11-19 17:33:33 +00:00
|
|
|
|
def test_should_show_checkboxes_for_selecting_templates(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_has_no_jobs,
|
2021-04-13 13:59:51 +01:00
|
|
|
|
mock_get_no_api_keys,
|
2018-11-19 17:33:33 +00:00
|
|
|
|
user,
|
|
|
|
|
|
):
|
2019-12-19 16:59:07 +00:00
|
|
|
|
client_request.login(user)
|
2018-11-19 17:33:33 +00:00
|
|
|
|
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-11-19 17:33:33 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
checkboxes = page.select("input[name=templates_and_folders]")
|
2018-11-19 17:33:33 +00:00
|
|
|
|
|
|
|
|
|
|
assert len(checkboxes) == 4
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert checkboxes[0]["value"] == TEMPLATE_ONE_ID
|
|
|
|
|
|
assert checkboxes[0]["id"] == "templates-or-folder-{}".format(TEMPLATE_ONE_ID)
|
2018-11-19 17:33:33 +00:00
|
|
|
|
|
|
|
|
|
|
for index in (1, 2, 3):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert checkboxes[index]["value"] != TEMPLATE_ONE_ID
|
|
|
|
|
|
assert TEMPLATE_ONE_ID not in checkboxes[index]["id"]
|
2023-05-26 12:35:48 -07:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"user",
|
|
|
|
|
|
[
|
|
|
|
|
|
pytest.param(
|
|
|
|
|
|
create_active_user_view_permissions(),
|
|
|
|
|
|
),
|
|
|
|
|
|
pytest.param(
|
|
|
|
|
|
create_active_caseworking_user(),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
2023-05-26 12:35:48 -07:00
|
|
|
|
def test_should_show_checkboxes_for_selecting_templates_assertion_error(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_has_no_jobs,
|
|
|
|
|
|
mock_get_no_api_keys,
|
|
|
|
|
|
user,
|
|
|
|
|
|
):
|
2023-10-30 08:42:11 -07:00
|
|
|
|
with pytest.raises(expected_exception=AssertionError):
|
|
|
|
|
|
_stmt_for_test_should_show_checkboxes_for_selecting_templates_assertion_error(
|
|
|
|
|
|
client_request, user
|
2023-05-26 12:35:48 -07:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-10-30 08:42:11 -07:00
|
|
|
|
def _stmt_for_test_should_show_checkboxes_for_selecting_templates_assertion_error(
|
|
|
|
|
|
client_request, user
|
|
|
|
|
|
):
|
|
|
|
|
|
client_request.login(user)
|
2023-05-26 12:35:48 -07:00
|
|
|
|
|
2023-10-30 08:42:11 -07:00
|
|
|
|
page = client_request.get(
|
|
|
|
|
|
"main.choose_template",
|
|
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
)
|
|
|
|
|
|
checkboxes = page.select("input[name=templates_and_folders]")
|
|
|
|
|
|
|
|
|
|
|
|
assert len(checkboxes) == 4
|
|
|
|
|
|
|
|
|
|
|
|
assert checkboxes[0]["value"] == TEMPLATE_ONE_ID
|
|
|
|
|
|
assert checkboxes[0]["id"] == "templates-or-folder-{}".format(TEMPLATE_ONE_ID)
|
|
|
|
|
|
|
|
|
|
|
|
for index in (1, 2, 3):
|
|
|
|
|
|
assert checkboxes[index]["value"] != TEMPLATE_ONE_ID
|
|
|
|
|
|
assert TEMPLATE_ONE_ID not in checkboxes[index]["id"]
|
2018-11-19 17:33:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"user",
|
|
|
|
|
|
[
|
|
|
|
|
|
create_active_user_view_permissions(),
|
|
|
|
|
|
create_active_caseworking_user(),
|
|
|
|
|
|
pytest.param(
|
|
|
|
|
|
create_active_user_with_permissions(),
|
|
|
|
|
|
marks=pytest.mark.xfail(raises=AssertionError),
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
2018-11-20 18:03:57 +00:00
|
|
|
|
def test_should_not_show_radios_and_buttons_for_move_destination_if_incorrect_permissions(
|
2018-11-19 17:33:33 +00:00
|
|
|
|
client_request,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_has_no_jobs,
|
2021-04-13 13:59:51 +01:00
|
|
|
|
mock_get_no_api_keys,
|
2018-11-19 17:33:33 +00:00
|
|
|
|
user,
|
|
|
|
|
|
):
|
2019-12-19 16:59:07 +00:00
|
|
|
|
client_request.login(user)
|
2018-11-19 17:33:33 +00:00
|
|
|
|
|
2018-11-20 18:03:57 +00:00
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-11-20 18:03:57 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
radios = page.select("input[type=radio]")
|
|
|
|
|
|
radio_div = page.find("div", {"id": "move_to_folder_radios"})
|
|
|
|
|
|
assert radios == page.select("input[name=move_to]")
|
2018-11-20 18:03:57 +00:00
|
|
|
|
|
|
|
|
|
|
assert not radios
|
|
|
|
|
|
assert not radio_div
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.find_all("button", {"name": "operation"}) == []
|
2018-11-20 18:03:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_should_show_radios_and_buttons_for_move_destination_if_correct_permissions(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_has_no_jobs,
|
2021-04-13 13:59:51 +01:00
|
|
|
|
mock_get_no_api_keys,
|
2018-11-20 18:03:57 +00:00
|
|
|
|
fake_uuid,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
active_user_with_permissions,
|
2018-11-20 18:03:57 +00:00
|
|
|
|
):
|
|
|
|
|
|
client_request.login(active_user_with_permissions)
|
|
|
|
|
|
|
2018-11-19 17:33:33 +00:00
|
|
|
|
FOLDER_TWO_ID = str(uuid.uuid4())
|
|
|
|
|
|
FOLDER_ONE_TWO_ID = str(uuid.uuid4())
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("folder_one", PARENT_FOLDER_ID, None),
|
|
|
|
|
|
_folder("folder_two", FOLDER_TWO_ID, None),
|
|
|
|
|
|
_folder("folder_one_one", CHILD_FOLDER_ID, PARENT_FOLDER_ID),
|
|
|
|
|
|
_folder("folder_one_two", FOLDER_ONE_TWO_ID, PARENT_FOLDER_ID),
|
2018-11-19 17:33:33 +00:00
|
|
|
|
]
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-11-19 17:33:33 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
radios = page.select("#move_to_folder_radios input[type=radio]")
|
|
|
|
|
|
radio_div = page.find("div", {"id": "move_to_folder_radios"})
|
|
|
|
|
|
assert radios == page.select("input[name=move_to]")
|
2018-11-19 17:33:33 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert [x["value"] for x in radios] == [
|
|
|
|
|
|
ROOT_FOLDER_ID,
|
|
|
|
|
|
PARENT_FOLDER_ID,
|
|
|
|
|
|
CHILD_FOLDER_ID,
|
|
|
|
|
|
FOLDER_ONE_TWO_ID,
|
|
|
|
|
|
FOLDER_TWO_ID,
|
2018-11-20 18:03:57 +00:00
|
|
|
|
]
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert [x.text.strip() for x in radio_div.select("label")] == [
|
|
|
|
|
|
"Templates",
|
|
|
|
|
|
"folder_one",
|
|
|
|
|
|
"folder_one_one",
|
|
|
|
|
|
"folder_one_two",
|
|
|
|
|
|
"folder_two",
|
2018-11-20 18:03:57 +00:00
|
|
|
|
]
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert set(x["value"] for x in page.find_all("button", {"name": "operation"})) == {
|
|
|
|
|
|
"unknown",
|
|
|
|
|
|
"move-to-existing-folder",
|
|
|
|
|
|
"move-to-new-folder",
|
|
|
|
|
|
"add-new-folder",
|
|
|
|
|
|
"add-new-template",
|
2018-11-20 18:03:57 +00:00
|
|
|
|
}
|
2018-11-19 17:33:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
2019-01-04 17:21:54 +00:00
|
|
|
|
def test_move_to_shouldnt_select_a_folder_by_default(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_has_no_jobs,
|
2021-04-13 13:59:51 +01:00
|
|
|
|
mock_get_no_api_keys,
|
2019-01-04 17:21:54 +00:00
|
|
|
|
fake_uuid,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
active_user_with_permissions,
|
2019-01-04 17:21:54 +00:00
|
|
|
|
):
|
|
|
|
|
|
client_request.login(active_user_with_permissions)
|
|
|
|
|
|
|
|
|
|
|
|
FOLDER_TWO_ID = str(uuid.uuid4())
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("folder_one", PARENT_FOLDER_ID, None),
|
|
|
|
|
|
_folder("folder_two", FOLDER_TWO_ID, None),
|
2019-01-04 17:21:54 +00:00
|
|
|
|
]
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2019-01-04 17:21:54 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
checked_radio = page.find("input", attrs={"name": "move_to", "checked": "checked"})
|
2019-01-04 17:21:54 +00:00
|
|
|
|
assert checked_radio is None
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-11-19 17:33:33 +00:00
|
|
|
|
def test_should_be_able_to_move_to_existing_folder(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_move_to_template_folder,
|
|
|
|
|
|
):
|
|
|
|
|
|
FOLDER_TWO_ID = str(uuid.uuid4())
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("folder_one", PARENT_FOLDER_ID, None),
|
|
|
|
|
|
_folder("folder_two", FOLDER_TWO_ID, None),
|
2018-11-19 17:33:33 +00:00
|
|
|
|
]
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-11-19 17:33:33 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
_data={
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"operation": "move-to-existing-folder",
|
|
|
|
|
|
"move_to": PARENT_FOLDER_ID,
|
|
|
|
|
|
"templates_and_folders": [
|
2018-11-19 17:33:33 +00:00
|
|
|
|
FOLDER_TWO_ID,
|
|
|
|
|
|
TEMPLATE_ONE_ID,
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
_expected_status=302,
|
|
|
|
|
|
_expected_redirect=url_for(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-11-19 17:33:33 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
_external=True,
|
|
|
|
|
|
),
|
|
|
|
|
|
)
|
|
|
|
|
|
mock_move_to_template_folder.assert_called_once_with(
|
|
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
folder_id=PARENT_FOLDER_ID,
|
|
|
|
|
|
folder_ids={FOLDER_TWO_ID},
|
|
|
|
|
|
template_ids={TEMPLATE_ONE_ID},
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
2023-09-08 17:58:06 -04:00
|
|
|
|
("user", "expected_status", "expected_called"),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
[
|
|
|
|
|
|
(create_active_user_view_permissions(), 403, False),
|
|
|
|
|
|
(create_active_user_with_permissions(), 302, True),
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
2018-11-19 17:33:33 +00:00
|
|
|
|
def test_should_not_be_able_to_move_to_existing_folder_if_dont_have_permission(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
fake_uuid,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_move_to_template_folder,
|
|
|
|
|
|
user,
|
2019-02-11 16:06:46 +00:00
|
|
|
|
expected_status,
|
|
|
|
|
|
expected_called,
|
2018-11-19 17:33:33 +00:00
|
|
|
|
):
|
2019-12-19 16:59:07 +00:00
|
|
|
|
client_request.login(user)
|
2018-11-19 17:33:33 +00:00
|
|
|
|
FOLDER_TWO_ID = str(uuid.uuid4())
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("folder_one", PARENT_FOLDER_ID, None),
|
|
|
|
|
|
_folder("folder_two", FOLDER_TWO_ID, None),
|
2018-11-19 17:33:33 +00:00
|
|
|
|
]
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-11-19 17:33:33 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
_data={
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"operation": "move-to-existing-folder",
|
|
|
|
|
|
"move_to": PARENT_FOLDER_ID,
|
|
|
|
|
|
"templates_and_folders": [
|
2018-11-19 17:33:33 +00:00
|
|
|
|
FOLDER_TWO_ID,
|
|
|
|
|
|
TEMPLATE_ONE_ID,
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_expected_status=expected_status,
|
2018-11-19 17:33:33 +00:00
|
|
|
|
)
|
2019-02-11 16:06:46 +00:00
|
|
|
|
assert mock_move_to_template_folder.called is expected_called
|
2018-11-19 17:33:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
2019-01-02 17:12:18 +00:00
|
|
|
|
def test_move_folder_form_shows_current_folder_hint_when_in_a_folder(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
2021-04-13 13:59:51 +01:00
|
|
|
|
mock_get_no_api_keys,
|
2019-01-02 17:12:18 +00:00
|
|
|
|
):
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("parent_folder", PARENT_FOLDER_ID, None),
|
|
|
|
|
|
_folder("child_folder", CHILD_FOLDER_ID, PARENT_FOLDER_ID),
|
2019-01-02 17:12:18 +00:00
|
|
|
|
]
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2019-01-02 17:12:18 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
template_folder_id=PARENT_FOLDER_ID,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_test_page_title=False,
|
2019-01-02 17:12:18 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
page.find("input", attrs={"name": "move_to", "value": PARENT_FOLDER_ID})
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
move_form_labels = page.find("div", id="move_to_folder_radios").find_all("label")
|
2019-01-02 17:12:18 +00:00
|
|
|
|
|
|
|
|
|
|
assert len(move_form_labels) == 3
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(move_form_labels[0].text) == "Templates"
|
|
|
|
|
|
assert normalize_spaces(move_form_labels[1].text) == "parent_folder current folder"
|
|
|
|
|
|
assert normalize_spaces(move_form_labels[2].text) == "child_folder"
|
2019-01-02 17:12:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_move_folder_form_does_not_show_current_folder_hint_at_the_top_level(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
2021-04-13 13:59:51 +01:00
|
|
|
|
mock_get_no_api_keys,
|
2019-01-02 17:12:18 +00:00
|
|
|
|
):
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("parent_folder", PARENT_FOLDER_ID, None),
|
|
|
|
|
|
_folder("child_folder", CHILD_FOLDER_ID, PARENT_FOLDER_ID),
|
2019-01-02 17:12:18 +00:00
|
|
|
|
]
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template", service_id=SERVICE_ONE_ID, _test_page_title=False
|
2019-01-02 17:12:18 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
page.find("input", attrs={"name": "move_to", "value": PARENT_FOLDER_ID})
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
move_form_labels = page.find("div", id="move_to_folder_radios").find_all("label")
|
2019-01-02 17:12:18 +00:00
|
|
|
|
|
|
|
|
|
|
assert len(move_form_labels) == 3
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(move_form_labels[0].text) == "Templates"
|
|
|
|
|
|
assert normalize_spaces(move_form_labels[1].text) == "parent_folder"
|
|
|
|
|
|
assert normalize_spaces(move_form_labels[2].text) == "child_folder"
|
2019-01-02 17:12:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
2018-11-19 17:33:33 +00:00
|
|
|
|
def test_should_be_able_to_move_a_sub_item(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
fake_uuid,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_move_to_template_folder,
|
|
|
|
|
|
):
|
|
|
|
|
|
GRANDCHILD_FOLDER_ID = str(uuid.uuid4())
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("folder_one", PARENT_FOLDER_ID, None),
|
|
|
|
|
|
_folder("folder_one_one", CHILD_FOLDER_ID, PARENT_FOLDER_ID),
|
|
|
|
|
|
_folder("folder_one_one_one", GRANDCHILD_FOLDER_ID, CHILD_FOLDER_ID),
|
2018-11-19 17:33:33 +00:00
|
|
|
|
]
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-11-19 17:33:33 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
template_folder_id=PARENT_FOLDER_ID,
|
|
|
|
|
|
_data={
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"operation": "move-to-existing-folder",
|
|
|
|
|
|
"move_to": ROOT_FOLDER_ID,
|
|
|
|
|
|
"templates_and_folders": [GRANDCHILD_FOLDER_ID],
|
2018-11-19 17:33:33 +00:00
|
|
|
|
},
|
|
|
|
|
|
_expected_status=302,
|
|
|
|
|
|
)
|
|
|
|
|
|
mock_move_to_template_folder.assert_called_once_with(
|
|
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
folder_id=None,
|
|
|
|
|
|
folder_ids={GRANDCHILD_FOLDER_ID},
|
|
|
|
|
|
template_ids=set(),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
"data",
|
|
|
|
|
|
[
|
|
|
|
|
|
# move to existing, but add new folder name given
|
|
|
|
|
|
{
|
|
|
|
|
|
"operation": "move-to-existing-folder",
|
|
|
|
|
|
"templates_and_folders": [],
|
|
|
|
|
|
"add_new_folder_name": "foo",
|
|
|
|
|
|
"move_to": PARENT_FOLDER_ID,
|
|
|
|
|
|
},
|
|
|
|
|
|
# move to existing, but move to new folder name given
|
|
|
|
|
|
{
|
|
|
|
|
|
"operation": "move-to-existing-folder",
|
|
|
|
|
|
"templates_and_folders": [TEMPLATE_ONE_ID],
|
|
|
|
|
|
"move_to_new_folder_name": "foo",
|
|
|
|
|
|
"move_to": PARENT_FOLDER_ID,
|
|
|
|
|
|
},
|
|
|
|
|
|
# move to existing, but no templates to move
|
|
|
|
|
|
{
|
|
|
|
|
|
"operation": "move-to-existing-folder",
|
|
|
|
|
|
"templates_and_folders": [],
|
|
|
|
|
|
"move_to_new_folder_name": "",
|
|
|
|
|
|
"move_to": PARENT_FOLDER_ID,
|
|
|
|
|
|
},
|
|
|
|
|
|
# move to new, but nothing selected to move
|
|
|
|
|
|
{
|
|
|
|
|
|
"operation": "move-to-new-folder",
|
|
|
|
|
|
"templates_and_folders": [],
|
|
|
|
|
|
"move_to_new_folder_name": "foo",
|
|
|
|
|
|
"move_to": None,
|
|
|
|
|
|
},
|
|
|
|
|
|
# add a new template, but also select move destination
|
|
|
|
|
|
{
|
|
|
|
|
|
"operation": "add-new-template",
|
|
|
|
|
|
"templates_and_folders": [],
|
|
|
|
|
|
"move_to_new_folder_name": "",
|
|
|
|
|
|
"move_to": PARENT_FOLDER_ID,
|
|
|
|
|
|
"add_template_by_template_type": "email",
|
|
|
|
|
|
},
|
|
|
|
|
|
# add a new template, but also move to root folder
|
|
|
|
|
|
{
|
|
|
|
|
|
"operation": "add-new-template",
|
|
|
|
|
|
"templates_and_folders": [],
|
|
|
|
|
|
"move_to_new_folder_name": "",
|
|
|
|
|
|
"move_to": ROOT_FOLDER_ID,
|
|
|
|
|
|
"add_template_by_template_type": "email",
|
|
|
|
|
|
},
|
|
|
|
|
|
# add a new template, but don't select anything
|
|
|
|
|
|
{
|
|
|
|
|
|
"operation": "add-new-template",
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
2018-11-20 18:03:57 +00:00
|
|
|
|
def test_no_action_if_user_fills_in_ambiguous_fields(
|
2018-11-19 17:33:33 +00:00
|
|
|
|
client_request,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_move_to_template_folder,
|
2018-11-20 18:03:57 +00:00
|
|
|
|
mock_create_template_folder,
|
2021-04-13 13:59:51 +01:00
|
|
|
|
mock_get_no_api_keys,
|
2018-11-20 18:03:57 +00:00
|
|
|
|
data,
|
2018-11-19 17:33:33 +00:00
|
|
|
|
):
|
2018-11-30 15:02:55 +00:00
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("parent_folder", PARENT_FOLDER_ID, None),
|
|
|
|
|
|
_folder("folder_two", FOLDER_TWO_ID, None),
|
2018-11-30 15:02:55 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
page = client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-11-20 18:03:57 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
_data=data,
|
|
|
|
|
|
_expected_status=200,
|
|
|
|
|
|
_expected_redirect=None,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
assert mock_move_to_template_folder.called is False
|
|
|
|
|
|
assert mock_create_template_folder.called is False
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.select_one("button[value={}]".format(data["operation"]))
|
2018-11-30 15:02:55 +00:00
|
|
|
|
|
|
|
|
|
|
assert [
|
2022-12-02 14:04:54 -05:00
|
|
|
|
# 'email',
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"sms",
|
|
|
|
|
|
"copy-existing",
|
2018-11-30 15:02:55 +00:00
|
|
|
|
] == [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
radio["value"]
|
|
|
|
|
|
for radio in page.select("#add_new_template_form input[type=radio]")
|
2018-11-30 15:02:55 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
assert [
|
2018-12-18 14:40:53 +00:00
|
|
|
|
ROOT_FOLDER_ID,
|
2018-11-30 15:02:55 +00:00
|
|
|
|
FOLDER_TWO_ID,
|
|
|
|
|
|
PARENT_FOLDER_ID,
|
|
|
|
|
|
] == [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
radio["value"]
|
|
|
|
|
|
for radio in page.select("#move_to_folder_radios input[type=radio]")
|
2018-11-30 15:02:55 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
2018-11-20 18:03:57 +00:00
|
|
|
|
|
|
|
|
|
|
def test_new_folder_is_created_if_only_new_folder_is_filled_out(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_move_to_template_folder,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mock_create_template_folder,
|
2018-11-20 18:03:57 +00:00
|
|
|
|
):
|
|
|
|
|
|
data = {
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"move_to_new_folder_name": "",
|
|
|
|
|
|
"add_new_folder_name": "new folder",
|
|
|
|
|
|
"operation": "add-new-folder",
|
2018-11-20 18:03:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-11-20 18:03:57 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
_data=data,
|
|
|
|
|
|
_expected_status=302,
|
|
|
|
|
|
_expected_redirect=url_for(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
|
|
|
|
|
service_id=service_one["id"],
|
2018-11-20 18:03:57 +00:00
|
|
|
|
template_folder_id=None,
|
|
|
|
|
|
_external=True,
|
|
|
|
|
|
),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
assert mock_move_to_template_folder.called is False
|
|
|
|
|
|
mock_create_template_folder.assert_called_once_with(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
SERVICE_ONE_ID, name="new folder", parent_id=None
|
2018-11-20 18:03:57 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_should_be_able_to_move_to_new_folder(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_move_to_template_folder,
|
|
|
|
|
|
mock_create_template_folder,
|
|
|
|
|
|
):
|
|
|
|
|
|
new_folder_id = mock_create_template_folder.return_value
|
2018-11-19 17:33:33 +00:00
|
|
|
|
FOLDER_TWO_ID = str(uuid.uuid4())
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("parent_folder", PARENT_FOLDER_ID, None),
|
|
|
|
|
|
_folder("folder_two", FOLDER_TWO_ID, None),
|
2018-11-19 17:33:33 +00:00
|
|
|
|
]
|
2018-11-20 18:03:57 +00:00
|
|
|
|
|
2018-11-19 17:33:33 +00:00
|
|
|
|
client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-11-19 17:33:33 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
2018-11-20 18:03:57 +00:00
|
|
|
|
template_folder_id=None,
|
2018-11-19 17:33:33 +00:00
|
|
|
|
_data={
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"operation": "move-to-new-folder",
|
|
|
|
|
|
"move_to_new_folder_name": "new folder",
|
|
|
|
|
|
"templates_and_folders": [
|
2018-11-20 18:03:57 +00:00
|
|
|
|
FOLDER_TWO_ID,
|
|
|
|
|
|
TEMPLATE_ONE_ID,
|
2018-11-19 17:33:33 +00:00
|
|
|
|
],
|
|
|
|
|
|
},
|
2018-11-20 18:03:57 +00:00
|
|
|
|
_expected_status=302,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_expected_redirect=url_for(
|
|
|
|
|
|
"main.choose_template", service_id=SERVICE_ONE_ID, _external=True
|
|
|
|
|
|
),
|
2018-11-20 18:03:57 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
mock_create_template_folder.assert_called_once_with(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
SERVICE_ONE_ID, name="new folder", parent_id=None
|
2018-11-20 18:03:57 +00:00
|
|
|
|
)
|
|
|
|
|
|
mock_move_to_template_folder.assert_called_once_with(
|
|
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
folder_id=new_folder_id,
|
|
|
|
|
|
folder_ids={FOLDER_TWO_ID},
|
|
|
|
|
|
template_ids={TEMPLATE_ONE_ID},
|
2018-11-19 17:33:33 +00:00
|
|
|
|
)
|
2018-12-07 17:09:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_radio_button_with_no_value_shows_custom_error_message(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_move_to_template_folder,
|
|
|
|
|
|
mock_create_template_folder,
|
2021-04-13 13:59:51 +01:00
|
|
|
|
mock_get_no_api_keys,
|
2018-12-07 17:09:15 +00:00
|
|
|
|
):
|
|
|
|
|
|
page = client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-12-07 17:09:15 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_data={"operation": "add-new-template"},
|
2018-12-07 17:09:15 +00:00
|
|
|
|
_expected_status=200,
|
|
|
|
|
|
_expected_redirect=None,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
assert mock_move_to_template_folder.called is False
|
|
|
|
|
|
assert mock_create_template_folder.called is False
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert (
|
|
|
|
|
|
page.select_one("span.error-message").text.strip()
|
|
|
|
|
|
== "Select the type of template you want to add"
|
2018-12-17 15:24:24 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2023-09-08 17:58:06 -04:00
|
|
|
|
("data", "error_msg"),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
[
|
|
|
|
|
|
# nothing selected when moving
|
|
|
|
|
|
(
|
|
|
|
|
|
{
|
|
|
|
|
|
"operation": "move-to-new-folder",
|
|
|
|
|
|
"templates_and_folders": [],
|
|
|
|
|
|
"move_to_new_folder_name": "foo",
|
|
|
|
|
|
},
|
|
|
|
|
|
"Select at least one template or folder",
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
{
|
|
|
|
|
|
"operation": "move-to-existing-folder",
|
|
|
|
|
|
"templates_and_folders": [],
|
|
|
|
|
|
"move_to": PARENT_FOLDER_ID,
|
|
|
|
|
|
},
|
|
|
|
|
|
"Select at least one template or folder",
|
|
|
|
|
|
),
|
|
|
|
|
|
# api error (eg moving folder to itself)
|
|
|
|
|
|
(
|
|
|
|
|
|
{
|
|
|
|
|
|
"operation": "move-to-existing-folder",
|
|
|
|
|
|
"templates_and_folders": [FOLDER_TWO_ID],
|
|
|
|
|
|
"move_to": FOLDER_TWO_ID,
|
|
|
|
|
|
},
|
|
|
|
|
|
"Some api error msg",
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
2018-12-17 15:24:24 +00:00
|
|
|
|
def test_show_custom_error_message(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
client_request,
|
|
|
|
|
|
service_one,
|
|
|
|
|
|
mock_get_service_templates,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_move_to_template_folder,
|
|
|
|
|
|
mock_create_template_folder,
|
|
|
|
|
|
mock_get_no_api_keys,
|
|
|
|
|
|
data,
|
|
|
|
|
|
error_msg,
|
2018-12-17 15:24:24 +00:00
|
|
|
|
):
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("folder_one", PARENT_FOLDER_ID, None),
|
|
|
|
|
|
_folder("folder_two", FOLDER_TWO_ID, None),
|
2018-12-17 15:24:24 +00:00
|
|
|
|
]
|
2023-08-25 09:12:23 -07:00
|
|
|
|
mock_move_to_template_folder.side_effect = HTTPError(message="Some api error msg")
|
2018-12-17 15:24:24 +00:00
|
|
|
|
|
|
|
|
|
|
page = client_request.post(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2018-12-17 15:24:24 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
_data=data,
|
|
|
|
|
|
_expected_status=200,
|
|
|
|
|
|
_expected_redirect=None,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert page.select_one("div.banner-dangerous").text.strip() == error_msg
|
2019-03-07 12:02:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
|
(
|
2023-09-08 17:58:06 -04:00
|
|
|
|
"extra_args",
|
|
|
|
|
|
"expected_displayed_items",
|
|
|
|
|
|
"expected_items",
|
|
|
|
|
|
"expected_empty_message",
|
2019-03-07 12:02:02 +00:00
|
|
|
|
),
|
|
|
|
|
|
[
|
|
|
|
|
|
(
|
|
|
|
|
|
{},
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
["folder_A", "folder_A", "1 template, 2 folders"],
|
|
|
|
|
|
[
|
|
|
|
|
|
"folder_E folder_F folder_G",
|
|
|
|
|
|
"folder_E",
|
|
|
|
|
|
"folder_F",
|
|
|
|
|
|
"folder_G",
|
|
|
|
|
|
"1 template",
|
|
|
|
|
|
],
|
|
|
|
|
|
["email_template_root", "email_template_root", "Email template"],
|
2019-03-07 12:02:02 +00:00
|
|
|
|
],
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
["folder_A", "folder_A", "1 template, 2 folders"],
|
2023-12-08 13:30:04 -08:00
|
|
|
|
["folder_A folder_C", "folder_C"],
|
2023-12-08 13:38:53 -08:00
|
|
|
|
["folder_A folder_C sms_template_C"],
|
2023-12-08 13:30:04 -08:00
|
|
|
|
["folder_A folder_D", "folder_D"],
|
2023-12-08 13:38:53 -08:00
|
|
|
|
["folder_A sms_template_A"],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
[
|
|
|
|
|
|
"folder_E folder_F folder_G",
|
|
|
|
|
|
"folder_E",
|
|
|
|
|
|
"folder_F",
|
|
|
|
|
|
"folder_G",
|
|
|
|
|
|
"1 template",
|
|
|
|
|
|
],
|
2023-12-08 13:38:53 -08:00
|
|
|
|
["folder_E folder_F folder_G email_template_G"],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
["email_template_root", "email_template_root", "Email template"],
|
2019-03-07 12:02:02 +00:00
|
|
|
|
],
|
|
|
|
|
|
None,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{"template_type": "email"},
|
2019-03-07 12:02:02 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
[
|
|
|
|
|
|
"folder_E folder_F folder_G",
|
|
|
|
|
|
"folder_E",
|
|
|
|
|
|
"folder_F",
|
|
|
|
|
|
"folder_G",
|
|
|
|
|
|
"1 template",
|
|
|
|
|
|
],
|
|
|
|
|
|
["email_template_root", "email_template_root", "Email template"],
|
2019-03-07 12:02:02 +00:00
|
|
|
|
],
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
[
|
|
|
|
|
|
"folder_E folder_F folder_G",
|
|
|
|
|
|
"folder_E",
|
|
|
|
|
|
"folder_F",
|
|
|
|
|
|
"folder_G",
|
|
|
|
|
|
"1 template",
|
|
|
|
|
|
],
|
|
|
|
|
|
[
|
|
|
|
|
|
"folder_E folder_F folder_G email_template_G",
|
|
|
|
|
|
],
|
|
|
|
|
|
["email_template_root", "email_template_root", "Email template"],
|
2019-03-07 12:02:02 +00:00
|
|
|
|
],
|
|
|
|
|
|
None,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{"template_type": "sms"},
|
2019-03-07 12:02:02 +00:00
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
["folder_A", "folder_A", "1 template, 1 folder"],
|
2019-03-07 12:02:02 +00:00
|
|
|
|
],
|
|
|
|
|
|
[
|
2023-08-25 09:12:23 -07:00
|
|
|
|
["folder_A", "folder_A", "1 template, 1 folder"],
|
2023-12-08 13:30:04 -08:00
|
|
|
|
["folder_A folder_C", "folder_C"],
|
2023-12-08 13:38:53 -08:00
|
|
|
|
["folder_A folder_C sms_template_C"],
|
|
|
|
|
|
["folder_A sms_template_A"],
|
2019-03-07 12:02:02 +00:00
|
|
|
|
],
|
|
|
|
|
|
None,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
{
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"template_type": "email",
|
|
|
|
|
|
"template_folder_id": FOLDER_C_ID,
|
2019-03-07 12:02:02 +00:00
|
|
|
|
},
|
|
|
|
|
|
[],
|
|
|
|
|
|
[],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"There are no email templates in this folder",
|
2019-03-07 12:02:02 +00:00
|
|
|
|
),
|
|
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{"template_folder_id": CHILD_FOLDER_ID},
|
2019-03-07 12:02:02 +00:00
|
|
|
|
[],
|
|
|
|
|
|
[],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"This folder is empty",
|
2019-03-07 12:02:02 +00:00
|
|
|
|
),
|
|
|
|
|
|
(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
{"template_folder_id": CHILD_FOLDER_ID, "template_type": "sms"},
|
2019-03-07 12:02:02 +00:00
|
|
|
|
[],
|
|
|
|
|
|
[],
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"This folder is empty",
|
2019-03-07 12:02:02 +00:00
|
|
|
|
),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
],
|
2019-03-07 12:02:02 +00:00
|
|
|
|
)
|
|
|
|
|
|
def test_should_filter_templates_folder_page_based_on_user_permissions(
|
|
|
|
|
|
client_request,
|
|
|
|
|
|
mock_get_template_folders,
|
|
|
|
|
|
mock_has_no_jobs,
|
2021-04-13 13:59:51 +01:00
|
|
|
|
mock_get_no_api_keys,
|
2019-03-07 12:02:02 +00:00
|
|
|
|
service_one,
|
|
|
|
|
|
mocker,
|
|
|
|
|
|
active_user_with_permissions,
|
|
|
|
|
|
extra_args,
|
|
|
|
|
|
expected_displayed_items,
|
|
|
|
|
|
expected_items,
|
|
|
|
|
|
expected_empty_message,
|
|
|
|
|
|
):
|
|
|
|
|
|
mock_get_template_folders.return_value = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
_folder("folder_A", FOLDER_TWO_ID, None, [active_user_with_permissions["id"]]),
|
|
|
|
|
|
_folder("folder_B", FOLDER_B_ID, FOLDER_TWO_ID, []),
|
|
|
|
|
|
_folder(
|
|
|
|
|
|
"folder_C", FOLDER_C_ID, FOLDER_TWO_ID, [active_user_with_permissions["id"]]
|
|
|
|
|
|
),
|
|
|
|
|
|
_folder("folder_D", None, FOLDER_TWO_ID, [active_user_with_permissions["id"]]),
|
|
|
|
|
|
_folder("folder_E", PARENT_FOLDER_ID, users_with_permission=[]),
|
|
|
|
|
|
_folder("folder_F", CHILD_FOLDER_ID, PARENT_FOLDER_ID, []),
|
|
|
|
|
|
_folder(
|
|
|
|
|
|
"folder_G",
|
|
|
|
|
|
GRANDCHILD_FOLDER_ID,
|
|
|
|
|
|
CHILD_FOLDER_ID,
|
|
|
|
|
|
[active_user_with_permissions["id"]],
|
|
|
|
|
|
),
|
2019-03-07 12:02:02 +00:00
|
|
|
|
]
|
|
|
|
|
|
mocker.patch(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"app.service_api_client.get_service_templates",
|
|
|
|
|
|
return_value={
|
|
|
|
|
|
"data": [
|
|
|
|
|
|
_template("email", "email_template_root"),
|
|
|
|
|
|
_template("sms", "sms_template_A", parent=FOLDER_TWO_ID),
|
|
|
|
|
|
_template("sms", "sms_template_C", parent=FOLDER_C_ID),
|
|
|
|
|
|
_template("email", "email_template_B", parent=FOLDER_B_ID),
|
|
|
|
|
|
_template("email", "email_template_G", parent=GRANDCHILD_FOLDER_ID),
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
2019-03-07 12:02:02 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
page = client_request.get(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"main.choose_template",
|
2019-03-07 12:02:02 +00:00
|
|
|
|
service_id=SERVICE_ONE_ID,
|
|
|
|
|
|
_test_page_title=False,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
**extra_args,
|
2019-03-07 12:02:02 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
displayed_page_items = page.find_all(
|
|
|
|
|
|
lambda tag: (
|
|
|
|
|
|
tag.has_attr("class")
|
|
|
|
|
|
and "template-list-item" in tag["class"]
|
|
|
|
|
|
and "template-list-item-hidden-by-default" not in tag["class"]
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
2019-03-07 12:02:02 +00:00
|
|
|
|
assert [
|
|
|
|
|
|
[i.strip() for i in e.text.split("\n") if i.strip()]
|
|
|
|
|
|
for e in displayed_page_items
|
|
|
|
|
|
] == expected_displayed_items
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
all_page_items = page.select(".template-list-item")
|
2019-03-07 12:02:02 +00:00
|
|
|
|
assert [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
[i.strip() for i in e.text.split("\n") if i.strip()] for e in all_page_items
|
2019-03-07 12:02:02 +00:00
|
|
|
|
] == expected_items
|
|
|
|
|
|
|
|
|
|
|
|
if expected_empty_message:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert normalize_spaces(page.select_one(".template-list-empty").text) == (
|
2019-03-07 12:02:02 +00:00
|
|
|
|
expected_empty_message
|
|
|
|
|
|
)
|
|
|
|
|
|
else:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
assert not page.select(".template-list-empty")
|