- {{ item_label_content }}
+ {{ item_link_content }}
{{ item.hint }}
From ee03753187c418036ea6089b55903d601cf8f8cb Mon Sep 17 00:00:00 2001
From: Tom Byers
Date: Tue, 9 Jun 2020 16:36:52 +0100
Subject: [PATCH 15/22] Make template-list checkbox label text match link
The checkboxes need an accessible name that
identifies the folder/template and this needs to
include their full path to avoid duplication.
There's a lot of debate about how to write out
breadcrumb/path syntax so this just puts all the
words together under the assumption that the
folder naming will describe the path (and to
introduce as little extra semantics as possible
to start with).
---
.../views/templates/_template_list.html | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/app/templates/views/templates/_template_list.html b/app/templates/views/templates/_template_list.html
index 0c7f4125c..f17fcfb07 100644
--- a/app/templates/views/templates/_template_list.html
+++ b/app/templates/views/templates/_template_list.html
@@ -1,12 +1,15 @@
{% from "components/message-count-label.html" import folder_contents_count, message_count_label %}
-{% macro format_item_name(name) -%}
+{% macro format_item_name(name, separators=True) -%}
{%- if name is string -%}
{{- name -}}
{%- else -%}
{%- for part in name -%}
- {{- format_item_name(part) -}}
- {%- if not loop.last %} {% endif -%}
+ {{- format_item_name(part, separators) -}}
+ {%- if not loop.last -%}
+ {%- if separators %}
+ {%- else %} {% endif -%}
+ {% endif -%}
{%- endfor -%}
{% endif %}
{%- endmacro %}
@@ -33,17 +36,20 @@
{% endfor %}
{% if item.is_folder %}
- {{ format_item_name(item.name) }}
+ {{- format_item_name(item.name) -}}
{% else %}
- {{ format_item_name(item.name) }}
+ {{- format_item_name(item.name) -}}
{% endif %}
{% endset %}
{% set label_content %}
- {{ format_item_name(item.name) }}
+
+ {%- for ancestor in item.ancestors %}{{ format_item_name(ancestor.name, separators=False) }} {% endfor -%}
+ {{ format_item_name(item.name, separators=False) -}}
+
{% endset %}
{% set item_meta %}
From 64cc9f25209cfb22fa46af3fcd5041d5b5aae254 Mon Sep 17 00:00:00 2001
From: Tom Byers
Date: Tue, 9 Jun 2020 16:44:05 +0100
Subject: [PATCH 16/22] Make tests work with new template list HTML
Adds the extra text added to each checkbox label.
It's a copy of the text of the link in the same
list item which does add a lot of duplication to
the test data.
This reformats a lot of the test data, stacking it
to separate out the duplicate items.
---
tests/app/main/views/test_template_folders.py | 247 +++++++++++-------
tests/app/main/views/test_templates.py | 2 +-
2 files changed, 158 insertions(+), 91 deletions(-)
diff --git a/tests/app/main/views/test_template_folders.py b/tests/app/main/views/test_template_folders.py
index 91da73454..b70a3cf79 100644
--- a/tests/app/main/views/test_template_folders.py
+++ b/tests/app/main/views/test_template_folders.py
@@ -54,29 +54,43 @@ def _folder(name, folder_id=None, parent=None, users_with_permission=None):
{},
['Email', 'Text message', 'Letter'],
[
- 'folder_one 2 folders',
- 'folder_one folder_one_one 1 template, 1 folder',
- 'folder_one folder_one_one folder_one_one_one 1 template',
- 'folder_one folder_one_one folder_one_one_one sms_template_nested Text message template',
- 'folder_one folder_one_one letter_template_nested Letter template',
- 'folder_one folder_one_two Empty',
- 'folder_two Empty',
- 'sms_template_one Text message template',
- 'sms_template_two Text message template',
- 'email_template_one Email template',
- 'email_template_two Email template',
- 'letter_template_one Letter template',
- 'letter_template_two Letter template',
+ 'folder_one folder_one 2 folders',
+ ('folder_one folder_one_one '
+ 'folder_one folder_one_one '
+ '1 template, 1 folder'),
+ ('folder_one folder_one_one folder_one_one_one '
+ 'folder_one folder_one_one folder_one_one_one '
+ '1 template'),
+ ('folder_one folder_one_one folder_one_one_one sms_template_nested '
+ 'folder_one folder_one_one folder_one_one_one sms_template_nested '
+ 'Text message template'),
+ ('folder_one folder_one_one letter_template_nested '
+ 'folder_one folder_one_one letter_template_nested '
+ 'Letter template'),
+ ('folder_one folder_one_two '
+ 'folder_one folder_one_two '
+ 'Empty'),
+ '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',
+ 'letter_template_one letter_template_one Letter template',
+ 'letter_template_two letter_template_two Letter template',
],
[
- 'folder_one 2 folders',
- 'folder_two Empty',
- 'sms_template_one Text message template',
- 'sms_template_two Text message template',
- 'email_template_one Email template',
- 'email_template_two Email template',
- 'letter_template_one Letter template',
- 'letter_template_two Letter template',
+ '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',
+ 'letter_template_one letter_template_one Letter template',
+ 'letter_template_two letter_template_two Letter template',
],
[
'folder_one',
@@ -102,29 +116,39 @@ def _folder(name, folder_id=None, parent=None, users_with_permission=None):
{'template_type': 'all'},
['Email', 'Text message', 'Letter'],
[
- 'folder_one 2 folders',
- 'folder_one folder_one_one 1 template, 1 folder',
- 'folder_one folder_one_one folder_one_one_one 1 template',
- 'folder_one folder_one_one folder_one_one_one sms_template_nested Text message template',
- 'folder_one folder_one_one letter_template_nested Letter template',
- 'folder_one folder_one_two Empty',
- 'folder_two Empty',
- 'sms_template_one Text message template',
- 'sms_template_two Text message template',
- 'email_template_one Email template',
- 'email_template_two Email template',
- 'letter_template_one Letter template',
- 'letter_template_two Letter template',
+ 'folder_one folder_one 2 folders',
+ ('folder_one folder_one_one '
+ 'folder_one folder_one_one '
+ '1 template, 1 folder'),
+ ('folder_one folder_one_one folder_one_one_one '
+ 'folder_one folder_one_one folder_one_one_one '
+ '1 template'),
+ ('folder_one folder_one_one folder_one_one_one sms_template_nested '
+ 'folder_one folder_one_one folder_one_one_one sms_template_nested '
+ 'Text message template'),
+ ('folder_one folder_one_one letter_template_nested '
+ 'folder_one folder_one_one letter_template_nested '
+ 'Letter template'),
+ ('folder_one folder_one_two '
+ 'folder_one folder_one_two '
+ 'Empty'),
+ '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',
+ 'letter_template_one letter_template_one Letter template',
+ 'letter_template_two letter_template_two Letter template',
],
[
- 'folder_one 2 folders',
- 'folder_two Empty',
- 'sms_template_one Text message template',
- 'sms_template_two Text message template',
- 'email_template_one Email template',
- 'email_template_two Email template',
- 'letter_template_one Letter template',
- 'letter_template_two Letter template',
+ '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',
+ 'letter_template_one letter_template_one Letter template',
+ 'letter_template_two letter_template_two Letter template',
],
[
'folder_one',
@@ -150,17 +174,23 @@ def _folder(name, folder_id=None, parent=None, users_with_permission=None):
{'template_type': 'sms'},
['All', 'Email', 'Letter'],
[
- 'folder_one 1 folder',
- 'folder_one folder_one_one 1 folder',
- 'folder_one folder_one_one folder_one_one_one 1 template',
- 'folder_one folder_one_one folder_one_one_one sms_template_nested Text message template',
- 'sms_template_one Text message template',
- 'sms_template_two Text message template',
+ 'folder_one folder_one 1 folder',
+ ('folder_one folder_one_one '
+ 'folder_one folder_one_one '
+ '1 folder'),
+ ('folder_one folder_one_one folder_one_one_one '
+ 'folder_one folder_one_one folder_one_one_one '
+ '1 template'),
+ ('folder_one folder_one_one folder_one_one_one sms_template_nested '
+ 'folder_one folder_one_one folder_one_one_one sms_template_nested '
+ 'Text message template'),
+ 'sms_template_one sms_template_one Text message template',
+ 'sms_template_two sms_template_two Text message template',
],
[
- 'folder_one 1 folder',
- 'sms_template_one Text message template',
- 'sms_template_two Text message template',
+ 'folder_one folder_one 1 folder',
+ 'sms_template_one sms_template_one Text message template',
+ 'sms_template_two sms_template_two Text message template',
],
[
'folder_one',
@@ -179,15 +209,21 @@ def _folder(name, folder_id=None, parent=None, users_with_permission=None):
{'template_folder_id': PARENT_FOLDER_ID},
['Email', 'Text message', 'Letter'],
[
- 'folder_one_one 1 template, 1 folder',
- 'folder_one_one folder_one_one_one 1 template',
- 'folder_one_one folder_one_one_one sms_template_nested Text message template',
- 'folder_one_one letter_template_nested Letter template',
- 'folder_one_two Empty',
+ 'folder_one_one folder_one_one 1 template, 1 folder',
+ ('folder_one_one folder_one_one_one '
+ 'folder_one_one folder_one_one_one '
+ '1 template'),
+ ('folder_one_one folder_one_one_one sms_template_nested '
+ 'folder_one_one folder_one_one_one sms_template_nested '
+ 'Text message template'),
+ ('folder_one_one letter_template_nested '
+ 'folder_one_one letter_template_nested '
+ 'Letter template'),
+ 'folder_one_two folder_one_two Empty',
],
[
- 'folder_one_one 1 template, 1 folder',
- 'folder_one_two Empty',
+ 'folder_one_one folder_one_one 1 template, 1 folder',
+ 'folder_one_two folder_one_two Empty',
],
[
'folder_one_one',
@@ -205,12 +241,16 @@ def _folder(name, folder_id=None, parent=None, users_with_permission=None):
{'template_type': 'sms', 'template_folder_id': PARENT_FOLDER_ID},
['All', 'Email', 'Letter'],
[
- 'folder_one_one 1 folder',
- 'folder_one_one folder_one_one_one 1 template',
- 'folder_one_one folder_one_one_one sms_template_nested Text message template',
+ 'folder_one_one folder_one_one 1 folder',
+ ('folder_one_one folder_one_one_one '
+ 'folder_one_one folder_one_one_one '
+ '1 template'),
+ ('folder_one_one folder_one_one_one sms_template_nested '
+ 'folder_one_one folder_one_one_one sms_template_nested '
+ 'Text message template'),
],
[
- 'folder_one_one 1 folder',
+ 'folder_one_one folder_one_one 1 folder',
],
[
'folder_one_one',
@@ -240,13 +280,15 @@ def _folder(name, folder_id=None, parent=None, users_with_permission=None):
{'template_folder_id': CHILD_FOLDER_ID},
['Email', 'Text message', 'Letter'],
[
- 'folder_one_one_one 1 template',
- 'folder_one_one_one sms_template_nested Text message template',
- 'letter_template_nested Letter template',
+ 'folder_one_one_one folder_one_one_one 1 template',
+ ('folder_one_one_one sms_template_nested '
+ 'folder_one_one_one sms_template_nested '
+ 'Text message template'),
+ 'letter_template_nested letter_template_nested Letter template',
],
[
- 'folder_one_one_one 1 template',
- 'letter_template_nested Letter template',
+ 'folder_one_one_one folder_one_one_one 1 template',
+ 'letter_template_nested letter_template_nested Letter template',
],
[
'folder_one_one_one',
@@ -266,10 +308,10 @@ def _folder(name, folder_id=None, parent=None, users_with_permission=None):
{'template_folder_id': GRANDCHILD_FOLDER_ID},
['Email', 'Text message', 'Letter'],
[
- 'sms_template_nested Text message template',
+ 'sms_template_nested sms_template_nested Text message template',
],
[
- 'sms_template_nested Text message template',
+ 'sms_template_nested sms_template_nested Text message template',
],
[
'sms_template_nested',
@@ -1462,45 +1504,70 @@ def test_show_custom_error_message(
(
{},
[
- ['folder_A', '1 template, 2 folders'],
- ['folder_E folder_F folder_G', '1 template'],
- ['email_template_root', 'Email template'],
+ ['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'],
],
[
- ['folder_A', '1 template, 2 folders'],
- ['folder_A', 'folder_C', '1 template'],
- ['folder_A', 'folder_C', 'sms_template_C', 'Text message template'],
- ['folder_A', 'folder_D', 'Empty'],
- ['folder_A', 'sms_template_A', 'Text message template'],
- ['folder_E folder_F folder_G', '1 template'],
- ['folder_E folder_F folder_G', 'email_template_G', 'Email template'],
- ['email_template_root', 'Email template'],
+ ['folder_A', 'folder_A', '1 template, 2 folders'],
+ ['folder_A folder_C',
+ 'folder_A', 'folder_C',
+ '1 template'],
+ ['folder_A folder_C sms_template_C',
+ 'folder_A', 'folder_C', 'sms_template_C',
+ 'Text message template'],
+ ['folder_A folder_D',
+ 'folder_A', 'folder_D',
+ 'Empty'],
+ ['folder_A sms_template_A',
+ 'folder_A', 'sms_template_A',
+ 'Text message template'],
+ ['folder_E folder_F folder_G',
+ 'folder_E', 'folder_F', 'folder_G',
+ '1 template'],
+ ['folder_E folder_F folder_G email_template_G',
+ 'folder_E', 'folder_F', 'folder_G', 'email_template_G',
+ 'Email template'],
+ ['email_template_root', 'email_template_root', 'Email template'],
],
None,
),
(
{'template_type': 'email'},
[
- ['folder_E folder_F folder_G', '1 template'],
- ['email_template_root', 'Email template'],
+ ['folder_E folder_F folder_G',
+ 'folder_E', 'folder_F', 'folder_G',
+ '1 template'],
+ ['email_template_root', 'email_template_root', 'Email template'],
],
[
- ['folder_E folder_F folder_G', '1 template'],
- ['folder_E folder_F folder_G', 'email_template_G', 'Email template'],
- ['email_template_root', 'Email template'],
+ ['folder_E folder_F folder_G',
+ 'folder_E', 'folder_F', 'folder_G',
+ '1 template'],
+ ['folder_E folder_F folder_G email_template_G',
+ 'folder_E', 'folder_F', 'folder_G', 'email_template_G',
+ 'Email template'],
+ ['email_template_root', 'email_template_root', 'Email template'],
],
None,
),
(
{'template_type': 'sms'},
[
- ['folder_A', '1 template, 1 folder'],
+ ['folder_A', 'folder_A', '1 template, 1 folder'],
],
[
- ['folder_A', '1 template, 1 folder'],
- ['folder_A', 'folder_C', '1 template'],
- ['folder_A', 'folder_C', 'sms_template_C', 'Text message template'],
- ['folder_A', 'sms_template_A', 'Text message template'],
+ ['folder_A', 'folder_A', '1 template, 1 folder'],
+ ['folder_A folder_C',
+ 'folder_A', 'folder_C',
+ '1 template'],
+ ['folder_A folder_C sms_template_C',
+ 'folder_A', 'folder_C', 'sms_template_C',
+ 'Text message template'],
+ ['folder_A sms_template_A', 'folder_A', 'sms_template_A',
+ 'Text message template'],
],
None,
),
diff --git a/tests/app/main/views/test_templates.py b/tests/app/main/views/test_templates.py
index a3cf74d41..e67ffa759 100644
--- a/tests/app/main/views/test_templates.py
+++ b/tests/app/main/views/test_templates.py
@@ -279,7 +279,7 @@ def test_should_show_live_search_if_service_has_lots_of_folders(
)
count_of_templates_and_folders = len(page.select('#template-list .govuk-label'))
- count_of_folders = len(page.select('.template-list-folder:first-child'))
+ count_of_folders = len(page.select('.template-list-folder:first-of-type'))
count_of_templates = count_of_templates_and_folders - count_of_folders
assert len(page.select('.live-search')) == 1
From 01f84d544370653c319794eb177b2c93a70ddd43 Mon Sep 17 00:00:00 2001
From: Tom Byers
Date: Thu, 9 Jul 2020 12:04:18 +0100
Subject: [PATCH 17/22] Convert checkboxes for broadcast areas
Includes removal of MultiCheckboxField due to it
no longer being used elsewhere in this file.
---
app/main/forms.py | 10 +++-------
app/templates/views/broadcast/areas.html | 5 ++---
2 files changed, 5 insertions(+), 10 deletions(-)
diff --git a/app/main/forms.py b/app/main/forms.py
index 0134a0274..e34eeda40 100644
--- a/app/main/forms.py
+++ b/app/main/forms.py
@@ -38,7 +38,6 @@ from wtforms import (
)
from wtforms.fields.html5 import EmailField, SearchField, TelField
from wtforms.validators import URL, DataRequired, Length, Optional, Regexp
-from wtforms.widgets import CheckboxInput, ListWidget
from app import format_thousands
from app.main.validators import (
@@ -118,11 +117,6 @@ def get_next_days_until(until):
]
-class MultiCheckboxField(SelectMultipleField):
- widget = ListWidget(prefix_label=False)
- option_widget = CheckboxInput()
-
-
class RadioField(WTFormsRadioField):
def __init__(
@@ -2135,11 +2129,13 @@ class AcceptAgreementForm(StripWhitespaceForm):
class BroadcastAreaForm(StripWhitespaceForm):
- areas = MultiCheckboxField('Choose areas to broadcast to')
+ areas = govukCheckboxesField('Choose areas to broadcast to')
def __init__(self, choices, *args, **kwargs):
super().__init__(*args, **kwargs)
self.areas.choices = choices
+ self.areas.render_as_list = True
+ self.areas.param_extensions = {'fieldset': {'legend': {'classes': 'govuk-visually-hidden'}}}
@classmethod
def from_library(cls, library):
diff --git a/app/templates/views/broadcast/areas.html b/app/templates/views/broadcast/areas.html
index 5020b587c..9ed0a38e8 100644
--- a/app/templates/views/broadcast/areas.html
+++ b/app/templates/views/broadcast/areas.html
@@ -1,6 +1,5 @@
{% from "components/page-header.html" import page_header %}
{% from "components/page-footer.html" import sticky_page_footer %}
-{% from "components/checkbox.html" import checkboxes %}
{% from "components/form.html" import form_wrapper %}
{% from "components/live-search.html" import live_search %}
@@ -17,10 +16,10 @@
back_link=url_for('.choose_broadcast_library', service_id=current_service.id, broadcast_message_id=broadcast_message.id),
)}}
- {{ live_search(target_selector='.multiple-choice', show=show_search_form, form=search_form, label='Search by name') }}
+ {{ live_search(target_selector='.govuk-checkboxes__item', show=show_search_form, form=search_form, label='Search by name') }}
{% call form_wrapper() %}
- {{ checkboxes(form.areas, hide_legend=True) }}
+ {{ form.areas }}
{{ sticky_page_footer('Add to broadcast') }}
{% endcall %}
From ca9b8a8ca35461fcd78f23b8895ed70def34a082 Mon Sep 17 00:00:00 2001
From: Tom Byers
Date: Tue, 14 Jul 2020 10:34:53 +0100
Subject: [PATCH 18/22] Add analytics error tracking to checkbox fields
The existing macros added data attributes to any
error message displayed which communicated the
error to Google Analytics (if the user had given
consent).
This re-implements that functionality.
---
app/main/forms.py | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/app/main/forms.py b/app/main/forms.py
index e34eeda40..c0ffc6e68 100644
--- a/app/main/forms.py
+++ b/app/main/forms.py
@@ -572,7 +572,14 @@ class govukCheckboxField(govukCheckboxesMixin, BooleanField):
# error messages
error_message = None
if field.errors:
- error_message = {"text": " ".join(field.errors).strip()}
+ error_message = {
+ "attributes": {
+ "data-module": "track-error",
+ "data-error-type": field.errors[0],
+ "data-error-label": field.name
+ },
+ "text": " ".join(field.errors).strip()
+ }
params = {
'name': field.name,
@@ -654,7 +661,14 @@ class govukCheckboxesField(govukCheckboxesMixin, SelectMultipleField):
# error messages
error_message = None
if field.errors:
- error_message = {"text": " ".join(field.errors).strip()}
+ error_message = {
+ "attributes": {
+ "data-module": "track-error",
+ "data-error-type": field.errors[0],
+ "data-error-label": field.name
+ },
+ "text": " ".join(field.errors).strip()
+ }
# returns either a list or a hierarchy of lists
# depending on how get_items_from_options is implemented
From e266b11ee2b2876d93040130d45b3b5414699660 Mon Sep 17 00:00:00 2001
From: Tom Byers
Date: Fri, 31 Jul 2020 10:20:44 +0100
Subject: [PATCH 19/22] Fix tests for pages using govukCheckboxField
These fields used to use govukCheckboxesField and
so stored their data in a list. They were since
migrated to govukCheckboxField, which extends
BooleanField and so keeps its data as a boolean
value.
---
tests/app/main/views/test_manage_users.py | 8 ++--
tests/app/main/views/test_service_settings.py | 46 +++++++++----------
2 files changed, 28 insertions(+), 26 deletions(-)
diff --git a/tests/app/main/views/test_manage_users.py b/tests/app/main/views/test_manage_users.py
index 0540eda03..69502ece8 100644
--- a/tests/app/main/views/test_manage_users.py
+++ b/tests/app/main/views/test_manage_users.py
@@ -1101,9 +1101,11 @@ def test_user_cant_invite_themselves(
service_id=SERVICE_ONE_ID,
_data={
'email_address': active_user_with_permissions['email_address'],
- 'send_messages': 'y',
- 'manage_service': 'y',
- 'manage_api_keys': 'y',
+ 'permissions_field': [
+ 'send_messages',
+ 'manage_service',
+ 'manage_api_keys'
+ ]
},
_follow_redirects=True,
_expected_status=200,
diff --git a/tests/app/main/views/test_service_settings.py b/tests/app/main/views/test_service_settings.py
index 39475af5c..d7a1b3da3 100644
--- a/tests/app/main/views/test_service_settings.py
+++ b/tests/app/main/views/test_service_settings.py
@@ -2299,8 +2299,8 @@ def test_incorrect_sms_sender_input(
@pytest.mark.parametrize('reply_to_addresses, data, api_default_args', [
([], {}, True),
- (create_multiple_email_reply_to_addresses(), {"is_default": []}, False),
- (create_multiple_email_reply_to_addresses(), {"is_default": ["y"]}, True)
+ (create_multiple_email_reply_to_addresses(), {}, False),
+ (create_multiple_email_reply_to_addresses(), {"is_default": "y"}, True)
])
def test_add_reply_to_email_address_sends_test_notification(
mocker, client_request, reply_to_addresses, data, api_default_args
@@ -2420,8 +2420,8 @@ def test_add_reply_to_email_address_fails_if_notification_not_delivered_in_45_se
@pytest.mark.parametrize('letter_contact_blocks, data, api_default_args', [
([], {}, True), # no existing letter contact blocks
- (create_multiple_letter_contact_blocks(), {"is_default": []}, False),
- (create_multiple_letter_contact_blocks(), {"is_default": ["y"]}, True)
+ (create_multiple_letter_contact_blocks(), {}, False),
+ (create_multiple_letter_contact_blocks(), {"is_default": "y"}, True)
])
def test_add_letter_contact(
letter_contact_blocks,
@@ -2496,8 +2496,8 @@ def test_add_letter_contact_when_coming_from_template(
@pytest.mark.parametrize('sms_senders, data, api_default_args', [
([], {}, True),
- (create_multiple_sms_senders(), {"is_default": []}, False),
- (create_multiple_sms_senders(), {"is_default": ["y"]}, True)
+ (create_multiple_sms_senders(), {}, False),
+ (create_multiple_sms_senders(), {"is_default": "y"}, True)
])
def test_add_sms_sender(
sms_senders,
@@ -2563,10 +2563,10 @@ def test_default_box_doesnt_show_on_first_letter_sender(
@pytest.mark.parametrize('reply_to_address, data, api_default_args', [
- (create_reply_to_email_address(is_default=True), {"is_default": ["y"]}, True),
- (create_reply_to_email_address(is_default=True), {"is_default": []}, True),
- (create_reply_to_email_address(is_default=False), {"is_default": []}, False),
- (create_reply_to_email_address(is_default=False), {"is_default": ["y"]}, True)
+ (create_reply_to_email_address(is_default=True), {"is_default": "y"}, True),
+ (create_reply_to_email_address(is_default=True), {}, True),
+ (create_reply_to_email_address(is_default=False), {}, False),
+ (create_reply_to_email_address(is_default=False), {"is_default": "y"}, True)
])
def test_edit_reply_to_email_address_sends_verification_notification_if_address_is_changed(
reply_to_address,
@@ -2591,10 +2591,10 @@ def test_edit_reply_to_email_address_sends_verification_notification_if_address_
@pytest.mark.parametrize('reply_to_address, data, api_default_args', [
- (create_reply_to_email_address(), {"is_default": ["y"]}, True),
- (create_reply_to_email_address(), {"is_default": []}, True),
- (create_reply_to_email_address(is_default=False), {"is_default": []}, False),
- (create_reply_to_email_address(is_default=False), {"is_default": ["y"]}, True)
+ (create_reply_to_email_address(), {"is_default": "y"}, True),
+ (create_reply_to_email_address(), {}, True),
+ (create_reply_to_email_address(is_default=False), {}, False),
+ (create_reply_to_email_address(is_default=False), {"is_default": "y"}, True)
])
def test_edit_reply_to_email_address_goes_straight_to_update_if_address_not_changed(
reply_to_address,
@@ -2651,7 +2651,7 @@ def test_add_edit_reply_to_email_address_goes_straight_to_update_if_address_not_
message=error_message
)]
)
- data = {"is_default": ["y"], 'email_address': "reply_to@example.com"}
+ data = {"is_default": "y", 'email_address': "reply_to@example.com"}
page = client_request.post(
url,
service_id=SERVICE_ONE_ID,
@@ -2751,10 +2751,10 @@ def test_delete_reply_to_email_address(
@pytest.mark.parametrize('letter_contact_block, data, api_default_args', [
- (create_letter_contact_block(), {"is_default": ["y"]}, True),
- (create_letter_contact_block(), {"is_default": []}, True),
- (create_letter_contact_block(is_default=False), {"is_default": []}, False),
- (create_letter_contact_block(is_default=False), {"is_default": ["y"]}, True)
+ (create_letter_contact_block(), {"is_default": "y"}, True),
+ (create_letter_contact_block(), {}, True),
+ (create_letter_contact_block(is_default=False), {}, False),
+ (create_letter_contact_block(is_default=False), {"is_default": "y"}, True)
])
def test_edit_letter_contact_block(
letter_contact_block,
@@ -2828,10 +2828,10 @@ def test_delete_letter_contact_block(
@pytest.mark.parametrize('sms_sender, data, api_default_args', [
- (create_sms_sender(), {"is_default": ["y"], "sms_sender": "test"}, True),
- (create_sms_sender(), {"is_default": [], "sms_sender": "test"}, True),
- (create_sms_sender(is_default=False), {"is_default": [], "sms_sender": "test"}, False),
- (create_sms_sender(is_default=False), {"is_default": ["y"], "sms_sender": "test"}, True)
+ (create_sms_sender(), {"is_default": "y", "sms_sender": "test"}, True),
+ (create_sms_sender(), {"sms_sender": "test"}, True),
+ (create_sms_sender(is_default=False), {"sms_sender": "test"}, False),
+ (create_sms_sender(is_default=False), {"is_default": "y", "sms_sender": "test"}, True)
])
def test_edit_sms_sender(
sms_sender,
From f3222296140fecde6d752624cba649c86a757125 Mon Sep 17 00:00:00 2001
From: Tom Byers
Date: Fri, 31 Jul 2020 15:06:03 +0100
Subject: [PATCH 20/22] Change tests to check permissions_field name
A comment on this group of changes mentioned the
'name' attribute of checkboxes for the
permissions_field should be checked as well as the
'value' attribute:
https://github.com/alphagov/notifications-admin/pull/3535#discussion_r460869797
This adds checks to support that point.
---
tests/app/main/views/test_manage_users.py | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/tests/app/main/views/test_manage_users.py b/tests/app/main/views/test_manage_users.py
index 69502ece8..ea8433e19 100644
--- a/tests/app/main/views/test_manage_users.py
+++ b/tests/app/main/views/test_manage_users.py
@@ -288,11 +288,15 @@ def test_service_without_caseworking_doesnt_show_admin_vs_caseworker(
service_id=SERVICE_ONE_ID,
**extra_args
)
- assert page.select('input[type=checkbox]')[0]['value'] == 'view_activity'
- assert page.select('input[type=checkbox]')[1]['value'] == 'send_messages'
- assert page.select('input[type=checkbox]')[2]['value'] == 'manage_templates'
- assert page.select('input[type=checkbox]')[3]['value'] == 'manage_service'
- assert page.select('input[type=checkbox]')[4]['value'] == 'manage_api_keys'
+ permission_checkboxes = page.select('input[type=checkbox]')
+
+ for idx in range(len(permission_checkboxes)):
+ assert permission_checkboxes[idx]['name'] == 'permissions_field'
+ assert permission_checkboxes[0]['value'] == 'view_activity'
+ assert permission_checkboxes[1]['value'] == 'send_messages'
+ assert permission_checkboxes[2]['value'] == 'manage_templates'
+ assert permission_checkboxes[3]['value'] == 'manage_service'
+ assert permission_checkboxes[4]['value'] == 'manage_api_keys'
@pytest.mark.parametrize('endpoint, extra_args', [
@@ -320,11 +324,11 @@ def test_broadcast_service_only_shows_relevant_permissions(
**extra_args
)
assert [
- field['value'] for field in page.select('input[type=checkbox]')
+ (field['name'], field['value']) for field in page.select('input[type=checkbox]')
] == [
- 'send_messages',
- 'manage_templates',
- 'manage_service',
+ ('permissions_field', 'send_messages'),
+ ('permissions_field', 'manage_templates'),
+ ('permissions_field', 'manage_service'),
]
@@ -433,6 +437,7 @@ def test_should_show_page_for_one_user(
for index, expected in enumerate(expected_checkboxes):
expected_input_value, expected_checked = expected
+ assert checkboxes[index]['name'] == 'permissions_field'
assert checkboxes[index]['value'] == expected_input_value
assert checkboxes[index].has_attr('checked') == expected_checked
From e3c434bb8fc3683616ee86eff129ad5973c4c8bd Mon Sep 17 00:00:00 2001
From: Tom Byers
Date: Fri, 31 Jul 2020 15:09:34 +0100
Subject: [PATCH 21/22] Change permissions tests to support old API
User permissions were handled by a group of
BooleanFields but introducing the new checkboxes
changed this to just one field that stores its
data in a list.
It was mentioned in a comment that there could be
a situation, when the instances roll, where clients
are using the old fields but POSTing to a server
running the new code.
https://github.com/alphagov/notifications-admin/pull/3535#discussion_r460872903
This introduces tests for that situation.
---
tests/app/main/views/test_manage_users.py | 77 +++++++++++++++++++----
1 file changed, 64 insertions(+), 13 deletions(-)
diff --git a/tests/app/main/views/test_manage_users.py b/tests/app/main/views/test_manage_users.py
index ea8433e19..82790baac 100644
--- a/tests/app/main/views/test_manage_users.py
+++ b/tests/app/main/views/test_manage_users.py
@@ -514,6 +514,22 @@ def test_should_not_show_page_for_non_team_member(
{},
set(),
),
+ ( # should be able to handle permissions being sent as booleans, until changeover to a list is complete
+ {
+ 'view_activity': 'y',
+ 'send_messages': 'y',
+ 'manage_templates': 'y',
+ 'manage_service': 'y',
+ 'manage_api_keys': 'y',
+ },
+ {
+ 'view_activity',
+ 'send_messages',
+ 'manage_templates',
+ 'manage_service',
+ 'manage_api_keys',
+ }
+ ),
])
def test_edit_user_permissions(
client_request,
@@ -584,6 +600,20 @@ def test_edit_user_permissions(
'view_activity',
}
),
+ ( # should be able to handle permissions being sent as booleans, until changeover to a list is complete
+ {
+ 'send_messages': 'y',
+ 'manage_templates': 'y',
+ 'manage_service': 'y',
+ 'manage_api_keys': 'y',
+ },
+ {
+ 'view_activity',
+ 'send_messages',
+ 'manage_service',
+ 'manage_templates',
+ }
+ ),
])
def test_edit_user_permissions_for_broadcast_service(
client_request,
@@ -828,9 +858,10 @@ def test_should_show_folder_permission_form_if_service_has_folder_permissions_en
assert len(folder_checkboxes) == 3
-@pytest.mark.parametrize('email_address, gov_user', [
- ('test@example.gov.uk', True),
- ('test@example.com', False)
+@pytest.mark.parametrize('email_address, gov_user, old_permissions', [
+ ('test@example.gov.uk', True, False),
+ ('test@example.com', False, False),
+ ('test@example.gov.uk', True, True)
])
def test_invite_user(
client_request,
@@ -839,6 +870,7 @@ def test_invite_user(
sample_invite,
email_address,
gov_user,
+ old_permissions,
mock_get_template_folders,
mock_get_organisations,
):
@@ -848,19 +880,25 @@ def test_invite_user(
mocker.patch('app.models.user.InvitedUsers.client_method', return_value=[sample_invite])
mocker.patch('app.models.user.Users.client_method', return_value=[active_user_with_permissions])
mocker.patch('app.invite_api_client.create_invite', return_value=sample_invite)
+ data = {'email_address': email_address}
+ if old_permissions:
+ data['view_activity'] = 'y'
+ data['send_messages'] = 'y'
+ data['manage_templates'] = 'y'
+ data['manage_service'] = 'y'
+ data['manage_api_keys'] = 'y'
+ else:
+ data['permissions_field'] = [
+ 'view_activity',
+ 'send_messages',
+ 'manage_templates',
+ 'manage_service',
+ 'manage_api_keys',
+ ]
page = client_request.post(
'main.invite_user',
service_id=SERVICE_ONE_ID,
- _data={
- 'email_address': email_address,
- 'permissions_field': [
- 'view_activity',
- 'send_messages',
- 'manage_templates',
- 'manage_service',
- 'manage_api_keys',
- ]
- },
+ _data=data,
_follow_redirects=True,
)
assert page.h1.string.strip() == 'Team members'
@@ -965,6 +1003,19 @@ def test_invite_user_with_email_auth_service(
'view_activity',
},
),
+ ( # should be able to handle permissions being sent as booleans, until changeover to a list is complete
+ {
+ 'send_messages': 'y',
+ 'manage_templates': 'y',
+ 'manage_service': 'y',
+ },
+ {
+ 'view_activity',
+ 'send_messages',
+ 'manage_templates',
+ 'manage_service',
+ },
+ )
))
def test_invite_user_to_broadcast_service(
client_request,
From 75bac87a4db0fa6df48b9c62af659b40c0e2e9aa Mon Sep 17 00:00:00 2001
From: Tom Byers
Date: Fri, 31 Jul 2020 10:31:39 +0100
Subject: [PATCH 22/22] Make permissions forms handle old/new params
---
app/main/forms.py | 40 +++++++++++++++++++++++++++++++++++++---
1 file changed, 37 insertions(+), 3 deletions(-)
diff --git a/app/main/forms.py b/app/main/forms.py
index c0ffc6e68..4301861fb 100644
--- a/app/main/forms.py
+++ b/app/main/forms.py
@@ -755,6 +755,20 @@ def filter_by_broadcast_permissions(valuelist):
return [entry for entry in valuelist if any(entry in role for role in broadcast_permissions)]
+# Included to support both versions of how user permissions are handled in permissions forms
+# Remove when changeover to new version (permissions_field) is complete
+PermissionsAbstract = type("PermissionsAbstract", (StripWhitespaceForm,), {
+ permission: BooleanField(label) for permission, label in permissions
+})
+
+
+# Included to support both versions of how user permissions are handled in permissions forms
+# Remove when changeover to new version (permissions_field) is complete
+BroadcastPermissionsAbstract = type("BroadcastPermissionsAbstract", (StripWhitespaceForm,), {
+ permission: BooleanField(label) for permission, label in broadcast_permissions
+})
+
+
class BasePermissionsForm(StripWhitespaceForm):
def __init__(self, all_template_folders=None, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -790,9 +804,25 @@ class BasePermissionsForm(StripWhitespaceForm):
}
)
+ # Modified to support both versions of how user permissions are handled in permissions forms
+ # Remove when changeover to new version (permissions_field) is complete
@property
def permissions(self):
- return set(self.permissions_field.data)
+ permissions_field_data = set(self.permissions_field.data)
+ permissions_fields_data = {field.id for field in self.permissions_fields if field.data is True}
+ if len(permissions_field_data) == 0 and len(permissions_fields_data) != 0:
+ return permissions_fields_data
+ else:
+ return permissions_field_data
+
+ # Included to support both versions of how user permissions are handled in permissions forms
+ # Remove when changeover to new version (permissions_field) is complete
+ @property
+ def permissions_fields(self):
+ return (
+ getattr(self, permission) for permission, field in self.__dict__.items()
+ if isinstance(field, BooleanField)
+ )
@classmethod
def from_user(cls, user, service_id, **kwargs):
@@ -806,11 +836,15 @@ class BasePermissionsForm(StripWhitespaceForm):
)
-class PermissionsForm(BasePermissionsForm):
+# Included to support both versions of how user permissions are handled in permissions forms
+# Remove when changeover to new version (permissions_field) is complete
+class PermissionsForm(PermissionsAbstract, BasePermissionsForm):
pass
-class BroadcastPermissionsForm(BasePermissionsForm):
+# Included to support both versions of how user permissions are handled in permissions forms
+# Remove when changeover to new version (permissions_field) is complete
+class BroadcastPermissionsForm(BroadcastPermissionsAbstract, BasePermissionsForm):
permissions_field = govukCheckboxesField(
'Permssions',