Merge pull request #3498 from alphagov/add-broadcast-template

Allow adding broadcast templates
This commit is contained in:
Chris Hill-Scott
2020-07-06 09:06:16 +01:00
committed by GitHub
18 changed files with 285 additions and 87 deletions
+23 -3
View File
@@ -777,7 +777,12 @@ class BaseTemplateForm(StripWhitespaceForm):
class SMSTemplateForm(BaseTemplateForm): class SMSTemplateForm(BaseTemplateForm):
def validate_template_content(self, field): def validate_template_content(self, field):
OnlySMSCharacters()(None, field) OnlySMSCharacters(template_type='sms')(None, field)
class BroadcastTemplateForm(SMSTemplateForm):
def validate_template_content(self, field):
OnlySMSCharacters(template_type='broadcast')(None, field)
class LetterAddressForm(StripWhitespaceForm): class LetterAddressForm(StripWhitespaceForm):
@@ -1644,7 +1649,7 @@ class TemplateAndFoldersSelectionForm(Form):
self, self,
all_template_folders, all_template_folders,
template_list, template_list,
allow_adding_letter_template, available_template_types,
allow_adding_copy_of_template, allow_adding_copy_of_template,
*args, *args,
**kwargs **kwargs
@@ -1652,6 +1657,8 @@ class TemplateAndFoldersSelectionForm(Form):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.available_template_types = available_template_types
self.templates_and_folders.choices = template_list.as_id_and_name self.templates_and_folders.choices = template_list.as_id_and_name
self.op = None self.op = None
@@ -1664,12 +1671,25 @@ class TemplateAndFoldersSelectionForm(Form):
] ]
self.add_template_by_template_type.choices = list(filter(None, [ self.add_template_by_template_type.choices = list(filter(None, [
# We want to show email and text message to everyone,
# whether or not the service has them switched on. The
# option to add letter or broadcast templates should only
# be shown to services which have that permission
('email', 'Email'), ('email', 'Email'),
('sms', 'Text message'), ('sms', 'Text message'),
('letter', 'Letter') if allow_adding_letter_template else None, ('letter', 'Letter') if 'letter' in available_template_types else None,
('broadcast', 'Broadcast') if 'broadcast' in available_template_types else None,
('copy-existing', 'Copy an existing template') if allow_adding_copy_of_template else None, ('copy-existing', 'Copy an existing template') if allow_adding_copy_of_template else None,
])) ]))
@property
def trying_to_add_unavailable_template_type(self):
return all((
self.is_add_template_op,
self.add_template_by_template_type.data,
self.add_template_by_template_type.data not in self.available_template_types,
))
def is_selected(self, template_folder_id): def is_selected(self, template_folder_id):
return template_folder_id in (self.templates_and_folders.data or []) return template_folder_id in (self.templates_and_folders.data or [])
+10 -1
View File
@@ -88,12 +88,21 @@ class NoEmbeddedImagesInSVG:
class OnlySMSCharacters: class OnlySMSCharacters:
def __init__(self, *args, template_type, **kwargs):
self._template_type = template_type
super().__init__(*args, **kwargs)
def __call__(self, form, field): def __call__(self, form, field):
non_sms_characters = sorted(list(SanitiseSMS.get_non_compatible_characters(field.data))) non_sms_characters = sorted(list(SanitiseSMS.get_non_compatible_characters(field.data)))
if non_sms_characters: if non_sms_characters:
raise ValidationError( raise ValidationError(
'You cannot use {} in text messages. {} will not show up properly on everyones phones.'.format( 'You cannot use {} in {}. {} will not show up properly on everyones phones.'.format(
formatted_list(non_sms_characters, conjunction='or', before_each='', after_each=''), formatted_list(non_sms_characters, conjunction='or', before_each='', after_each=''),
{
'broadcast': 'broadcasts',
'sms': 'text messages',
}.get(self._template_type),
('It' if len(non_sms_characters) == 1 else 'They') ('It' if len(non_sms_characters) == 1 else 'They')
) )
) )
+5 -8
View File
@@ -53,7 +53,6 @@ from app.template_previews import TemplatePreview, get_page_count_for_letter
from app.utils import ( from app.utils import (
PermanentRedirect, PermanentRedirect,
Spreadsheet, Spreadsheet,
email_or_sms_not_enabled,
get_errors_for_csv, get_errors_for_csv,
get_help_argument, get_help_argument,
get_template, get_template,
@@ -128,7 +127,7 @@ def send_messages(service_id, template_id):
elif db_template['template_type'] == 'sms': elif db_template['template_type'] == 'sms':
sms_sender = get_sms_sender_from_session() sms_sender = get_sms_sender_from_session()
if email_or_sms_not_enabled(db_template['template_type'], current_service.permissions): if db_template['template_type'] not in current_service.available_template_types:
return redirect(url_for( return redirect(url_for(
'.action_blocked', '.action_blocked',
service_id=service_id, service_id=service_id,
@@ -302,8 +301,11 @@ def send_test(service_id, template_id):
db_template = current_service.get_template_with_user_permission_or_403(template_id, current_user) db_template = current_service.get_template_with_user_permission_or_403(template_id, current_user)
if db_template['template_type'] == 'letter': if db_template['template_type'] == 'letter':
session['sender_id'] = None session['sender_id'] = None
return redirect(
url_for('.send_one_off_letter_address', service_id=service_id, template_id=template_id)
)
if email_or_sms_not_enabled(db_template['template_type'], current_service.permissions): if db_template['template_type'] not in current_service.available_template_types:
return redirect(url_for( return redirect(url_for(
'.action_blocked', '.action_blocked',
service_id=service_id, service_id=service_id,
@@ -311,11 +313,6 @@ def send_test(service_id, template_id):
return_to='view_template', return_to='view_template',
template_id=template_id)) template_id=template_id))
if db_template['template_type'] == 'letter':
return redirect(
url_for('.send_one_off_letter_address', service_id=service_id, template_id=template_id)
)
return redirect(url_for( return redirect(url_for(
{ {
'main.send_test': '.send_test_step', 'main.send_test': '.send_test_step',
+35 -37
View File
@@ -16,6 +16,7 @@ from app import (
) )
from app.main import main, no_cookie from app.main import main, no_cookie
from app.main.forms import ( from app.main.forms import (
BroadcastTemplateForm,
EmailTemplateForm, EmailTemplateForm,
LetterTemplateForm, LetterTemplateForm,
LetterTemplatePostageForm, LetterTemplatePostageForm,
@@ -30,7 +31,6 @@ from app.models.service import Service
from app.models.template_list import TemplateList, TemplateLists from app.models.template_list import TemplateList, TemplateLists
from app.template_previews import TemplatePreview, get_page_count_for_letter from app.template_previews import TemplatePreview, get_page_count_for_letter
from app.utils import ( from app.utils import (
email_or_sms_not_enabled,
get_template, get_template,
should_skip_template_page, should_skip_template_page,
user_has_permissions, user_has_permissions,
@@ -40,7 +40,8 @@ from app.utils import (
form_objects = { form_objects = {
'email': EmailTemplateForm, 'email': EmailTemplateForm,
'sms': SMSTemplateForm, 'sms': SMSTemplateForm,
'letter': LetterTemplateForm 'letter': LetterTemplateForm,
'broadcast': BroadcastTemplateForm,
} }
@@ -122,7 +123,7 @@ def choose_template(service_id, template_type='all', template_folder_id=None):
all_template_folders=current_service.get_user_template_folders(current_user), all_template_folders=current_service.get_user_template_folders(current_user),
template_list=template_list, template_list=template_list,
template_type=template_type, template_type=template_type,
allow_adding_letter_template=current_service.has_permission('letter'), available_template_types=current_service.available_template_types,
allow_adding_copy_of_template=( allow_adding_copy_of_template=(
current_service.all_templates or len(current_user.service_ids) > 1 current_service.all_templates or len(current_user.service_ids) > 1
), ),
@@ -136,6 +137,13 @@ def choose_template(service_id, template_type='all', template_folder_id=None):
return process_folder_management_form(templates_and_folders_form, template_folder_id) return process_folder_management_form(templates_and_folders_form, template_folder_id)
except HTTPError as e: except HTTPError as e:
flash(e.message) flash(e.message)
elif templates_and_folders_form.trying_to_add_unavailable_template_type:
return redirect(url_for(
'.action_blocked',
service_id=current_service.id,
notification_type=templates_and_folders_form.add_template_by_template_type.data,
return_to='add_new_template',
))
if 'templates_and_folders' in templates_and_folders_form.errors: if 'templates_and_folders' in templates_and_folders_form.errors:
flash('Select at least one template or folder') flash('Select at least one template or folder')
@@ -199,6 +207,7 @@ def get_template_nav_label(value):
'sms': 'Text message', 'sms': 'Text message',
'email': 'Email', 'email': 'Email',
'letter': 'Letter', 'letter': 'Letter',
'broadcast': 'Broadcast',
}[value] }[value]
@@ -310,20 +319,12 @@ def _add_template_by_type(template_type, template_folder_id):
template_id=blank_letter['data']['id'], template_id=blank_letter['data']['id'],
)) ))
if email_or_sms_not_enabled(template_type, current_service.permissions): return redirect(url_for(
return redirect(url_for( '.add_service_template',
'.action_blocked', service_id=current_service.id,
service_id=current_service.id, template_type=template_type,
notification_type=template_type, template_folder_id=template_folder_id,
return_to='add_new_template', ))
))
else:
return redirect(url_for(
'.add_service_template',
service_id=current_service.id,
template_type=template_type,
template_folder_id=template_folder_id,
))
@main.route("/services/<uuid:service_id>/templates/copy") @main.route("/services/<uuid:service_id>/templates/copy")
@@ -437,7 +438,7 @@ def action_blocked(service_id, notification_type, return_to, template_id=None):
service_id=service_id, service_id=service_id,
notification_type=notification_type, notification_type=notification_type,
back_link=back_link(), back_link=back_link(),
) ), 403
@main.route("/services/<uuid:service_id>/templates/folders/<uuid:template_folder_id>/manage", methods=['GET', 'POST']) @main.route("/services/<uuid:service_id>/templates/folders/<uuid:template_folder_id>/manage", methods=['GET', 'POST'])
@@ -527,8 +528,14 @@ def delete_template_folder(service_id, template_folder_id):
@user_has_permissions('manage_templates') @user_has_permissions('manage_templates')
def add_service_template(service_id, template_type, template_folder_id=None): def add_service_template(service_id, template_type, template_folder_id=None):
if not current_service.has_permission('letter') and template_type == 'letter': if template_type not in current_service.available_template_types:
abort(403) return redirect(url_for(
'.action_blocked',
service_id=service_id,
notification_type=template_type,
template_folder_id=template_folder_id,
return_to='templates',
))
form = form_objects[template_type]() form = form_objects[template_type]()
if form.validate_on_submit(): if form.validate_on_submit():
@@ -558,22 +565,13 @@ def add_service_template(service_id, template_type, template_folder_id=None):
url_for('.view_template', service_id=service_id, template_id=new_template['data']['id']) url_for('.view_template', service_id=service_id, template_id=new_template['data']['id'])
) )
if email_or_sms_not_enabled(template_type, current_service.permissions): return render_template(
return redirect(url_for( 'views/edit-{}-template.html'.format(template_type),
'.action_blocked', form=form,
service_id=service_id, template_type=template_type,
notification_type=template_type, template_folder_id=template_folder_id,
template_folder_id=template_folder_id, heading_action='New',
return_to='templates', )
))
else:
return render_template(
'views/edit-{}-template.html'.format(template_type),
form=form,
template_type=template_type,
template_folder_id=template_folder_id,
heading_action='New',
)
def abort_403_if_not_admin_user(): def abort_403_if_not_admin_user():
@@ -642,7 +640,7 @@ def edit_service_template(service_id, template_id):
template_id=template_id template_id=template_id
)) ))
if email_or_sms_not_enabled(template['template_type'], current_service.permissions): if template['template_type'] not in current_service.available_template_types:
return redirect(url_for( return redirect(url_for(
'.action_blocked', '.action_blocked',
service_id=service_id, service_id=service_id,
+1
View File
@@ -54,6 +54,7 @@ class Service(JSONModel):
'email', 'email',
'sms', 'sms',
'letter', 'letter',
'broadcast',
) )
ALL_PERMISSIONS = TEMPLATE_TYPES + ( ALL_PERMISSIONS = TEMPLATE_TYPES + (
+1
View File
@@ -132,6 +132,7 @@ class TemplateListTemplate(TemplateListItem):
'email': 'Email template', 'email': 'Email template',
'sms': 'Text message template', 'sms': 'Text message template',
'letter': 'Letter template', 'letter': 'Letter template',
'broadcast': 'Broadcast template',
}.get(template['template_type']) }.get(template['template_type'])
@@ -24,6 +24,12 @@
{%- else -%} {%- else -%}
letters letters
{%- endif -%} {%- endif -%}
{%- elif template_type == 'broadcast' -%}
{%- if count == 1 -%}
broadcast
{%- else -%}
broadcasts
{%- endif -%}
{%- endif %} {{ suffix }} {%- endif %} {{ suffix }}
{%- endmacro %} {%- endmacro %}
@@ -0,0 +1,31 @@
{% extends "withnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-header.html" import page_header %}
{% from "components/page-footer.html" import sticky_page_footer %}
{% from "components/form.html" import form_wrapper %}
{% block service_page_title %}
{{ heading_action }} broadcast template
{% endblock %}
{% block maincolumn_content %}
{{ page_header(
'{} broadcast template'.format(heading_action),
back_link=url_for('main.view_template', service_id=current_service.id, template_id=template.id) if template else url_for('main.choose_template', service_id=current_service.id, template_folder_id=template_folder_id)
) }}
{% call form_wrapper() %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
{{ textbox(form.name, width='1-1', hint='Your recipients will not see this') }}
</div>
<div class="govuk-grid-column-two-thirds">
{{ textbox(form.template_content, highlight_placeholders=True, width='1-1', rows=5) }}
{{ sticky_page_footer('Save') }}
</div>
</div>
{% endcall %}
{% endblock %}
@@ -28,6 +28,14 @@
</a> </a>
</div> </div>
{% endif %} {% endif %}
{% elif template.template_type == 'broadcast' %}
{% if current_user.has_permissions('manage_templates') %}
<div class="govuk-grid-column-full">
<a href="{{ url_for(".edit_service_template", service_id=current_service.id, template_id=template.id) }}" class="govuk-link govuk-link--no-visited-state pill-separate-item">
Edit
</a>
</div>
{% endif %}
{% else %} {% else %}
{% if current_user.has_permissions('send_messages', restrict_admin_usage=True) %} {% if current_user.has_permissions('send_messages', restrict_admin_usage=True) %}
<div class="govuk-grid-column-one-half"> <div class="govuk-grid-column-one-half">
+2 -1
View File
@@ -5,11 +5,12 @@ from app.models.feedback import (
PROBLEM_TICKET_TYPE, PROBLEM_TICKET_TYPE,
QUESTION_TICKET_TYPE, QUESTION_TICKET_TYPE,
) )
from app.models.service import Service
class TemplateTypeConverter(BaseConverter): class TemplateTypeConverter(BaseConverter):
regex = '(?:email|sms|letter)' regex = '(?:{})'.format('|'.join(Service.TEMPLATE_TYPES))
class TicketTypeConverter(BaseConverter): class TicketTypeConverter(BaseConverter):
+5 -4
View File
@@ -37,6 +37,7 @@ from notifications_utils.postal_address import PostalAddress
from notifications_utils.recipients import RecipientCSV from notifications_utils.recipients import RecipientCSV
from notifications_utils.take import Take from notifications_utils.take import Take
from notifications_utils.template import ( from notifications_utils.template import (
BroadcastPreviewTemplate,
EmailPreviewTemplate, EmailPreviewTemplate,
LetterImageTemplate, LetterImageTemplate,
LetterPreviewTemplate, LetterPreviewTemplate,
@@ -451,6 +452,10 @@ def get_template(
admin_base_url=current_app.config['ADMIN_BASE_URL'], admin_base_url=current_app.config['ADMIN_BASE_URL'],
redact_missing_personalisation=redact_missing_personalisation, redact_missing_personalisation=redact_missing_personalisation,
) )
if 'broadcast' == template['template_type']:
return BroadcastPreviewTemplate(
template,
)
def get_current_financial_year(): def get_current_financial_year():
@@ -475,10 +480,6 @@ def get_time_left(created_at, service_data_retention_days=7):
) )
def email_or_sms_not_enabled(template_type, permissions):
return (template_type in ['email', 'sms']) and (template_type not in permissions)
def get_logo_cdn_domain(): def get_logo_cdn_domain():
parsed_uri = urlparse(current_app.config['ADMIN_BASE_URL']) parsed_uri = urlparse(current_app.config['ADMIN_BASE_URL'])
+1 -1
View File
@@ -23,7 +23,7 @@ notifications-python-client==5.6.0
awscli-cwlogs>=1.4,<1.5 awscli-cwlogs>=1.4,<1.5
itsdangerous==1.1.0 itsdangerous==1.1.0
git+https://github.com/alphagov/notifications-utils.git@39.7.0#egg=notifications-utils==39.7.0 git+https://github.com/alphagov/notifications-utils.git@40.1.0#egg=notifications-utils==40.1.0
git+https://github.com/alphagov/govuk-frontend-jinja.git@v0.5.1-alpha#egg=govuk-frontend-jinja==0.5.1-alpha git+https://github.com/alphagov/govuk-frontend-jinja.git@v0.5.1-alpha#egg=govuk-frontend-jinja==0.5.1-alpha
# gds-metrics requires prometheseus 0.2.0, override that requirement as later versions bring significant performance gains # gds-metrics requires prometheseus 0.2.0, override that requirement as later versions bring significant performance gains
+3 -3
View File
@@ -25,7 +25,7 @@ notifications-python-client==5.6.0
awscli-cwlogs>=1.4,<1.5 awscli-cwlogs>=1.4,<1.5
itsdangerous==1.1.0 itsdangerous==1.1.0
git+https://github.com/alphagov/notifications-utils.git@39.7.0#egg=notifications-utils==39.7.0 git+https://github.com/alphagov/notifications-utils.git@40.1.0#egg=notifications-utils==40.1.0
git+https://github.com/alphagov/govuk-frontend-jinja.git@v0.5.1-alpha#egg=govuk-frontend-jinja==0.5.1-alpha git+https://github.com/alphagov/govuk-frontend-jinja.git@v0.5.1-alpha#egg=govuk-frontend-jinja==0.5.1-alpha
# gds-metrics requires prometheseus 0.2.0, override that requirement as later versions bring significant performance gains # gds-metrics requires prometheseus 0.2.0, override that requirement as later versions bring significant performance gains
@@ -33,10 +33,10 @@ prometheus-client==0.8.0
gds-metrics==0.2.0 gds-metrics==0.2.0
## The following requirements were added by pip freeze: ## The following requirements were added by pip freeze:
awscli==1.18.90 awscli==1.18.91
bleach==3.1.4 bleach==3.1.4
boto3==1.10.38 boto3==1.10.38
botocore==1.17.13 botocore==1.17.14
cachetools==4.1.0 cachetools==4.1.0
certifi==2020.6.20 certifi==2020.6.20
chardet==3.0.4 chardet==3.0.4
+2 -2
View File
@@ -154,7 +154,7 @@ def test_for_commas_in_placeholders(
@pytest.mark.parametrize('msg', ['The quick brown fox', 'Thé “quick” bröwn fox\u200B']) @pytest.mark.parametrize('msg', ['The quick brown fox', 'Thé “quick” bröwn fox\u200B'])
def test_sms_character_validation(client, msg): def test_sms_character_validation(client, msg):
OnlySMSCharacters()(None, _gen_mock_field(msg)) OnlySMSCharacters(template_type='sms')(None, _gen_mock_field(msg))
@pytest.mark.parametrize('data, err_msg', [ @pytest.mark.parametrize('data, err_msg', [
@@ -175,7 +175,7 @@ def test_sms_character_validation(client, msg):
]) ])
def test_non_sms_character_validation(data, err_msg, client): def test_non_sms_character_validation(data, err_msg, client):
with pytest.raises(ValidationError) as error: with pytest.raises(ValidationError) as error:
OnlySMSCharacters()(None, _gen_mock_field(data)) OnlySMSCharacters(template_type='sms')(None, _gen_mock_field(data))
assert str(error.value) == err_msg assert str(error.value) == err_msg
+8 -7
View File
@@ -14,7 +14,8 @@ letters_urls = [
([], 403) ([], 403)
]) ])
def test_letters_access_restricted( def test_letters_access_restricted(
platform_admin_client, client_request,
platform_admin_user,
mocker, mocker,
permissions, permissions,
response_code, response_code,
@@ -23,12 +24,12 @@ def test_letters_access_restricted(
service_one, service_one,
): ):
service_one['permissions'] = permissions service_one['permissions'] = permissions
client_request.login(platform_admin_user)
mocker.patch('app.service_api_client.get_service', return_value={"data": service_one}) client_request.get_url(
url(service_id=service_one['id']),
response = platform_admin_client.get(url(service_id=service_one['id'])) _follow_redirects=True,
_expected_status=response_code,
assert response.status_code == response_code )
@pytest.mark.parametrize('url', letters_urls) @pytest.mark.parametrize('url', letters_urls)
+6 -1
View File
@@ -280,6 +280,7 @@ def test_should_not_allow_files_to_be_uploaded_without_the_correct_permission(
service_id=SERVICE_ONE_ID, service_id=SERVICE_ONE_ID,
template_id=template_id, template_id=template_id,
_follow_redirects=True, _follow_redirects=True,
_expected_status=403,
) )
assert page.select('main p')[0].text.strip() == "Sending text messages has been disabled for your service." assert page.select('main p')[0].text.strip() == "Sending text messages has been disabled for your service."
@@ -312,10 +313,12 @@ def test_example_spreadsheet(
def test_example_spreadsheet_for_letters( def test_example_spreadsheet_for_letters(
client_request, client_request,
service_one,
mocker, mocker,
mock_get_service_letter_template_with_placeholders, mock_get_service_letter_template_with_placeholders,
fake_uuid, fake_uuid,
): ):
service_one['permissions'] += ['letter']
mocker.patch('app.main.views.send.get_page_count_for_letter', return_value=1) mocker.patch('app.main.views.send.get_page_count_for_letter', return_value=1)
page = client_request.get( page = client_request.get(
@@ -600,6 +603,7 @@ def test_upload_csv_file_with_bad_postal_address_shows_check_page_with_errors(
mock_get_jobs, mock_get_jobs,
fake_uuid, fake_uuid,
): ):
service_one['permissions'] += ['letter']
mocker.patch('app.main.views.send.get_page_count_for_letter', return_value=9) mocker.patch('app.main.views.send.get_page_count_for_letter', return_value=9)
mocker.patch( mocker.patch(
'app.main.views.send.s3download', 'app.main.views.send.s3download',
@@ -658,7 +662,7 @@ def test_upload_csv_file_with_international_letters_permission_shows_appropriate
mock_get_jobs, mock_get_jobs,
fake_uuid, fake_uuid,
): ):
service_one['permissions'] += ['international_letters'] service_one['permissions'] += ['letter', 'international_letters']
mocker.patch('app.main.views.send.get_page_count_for_letter', return_value=9) mocker.patch('app.main.views.send.get_page_count_for_letter', return_value=9)
mocker.patch( mocker.patch(
'app.main.views.send.s3download', 'app.main.views.send.s3download',
@@ -1328,6 +1332,7 @@ def test_send_one_off_does_not_send_without_the_correct_permissions(
service_id=SERVICE_ONE_ID, service_id=SERVICE_ONE_ID,
template_id=template_id, template_id=template_id,
_follow_redirects=True, _follow_redirects=True,
_expected_status=403,
) )
assert page.select('main p')[0].text.strip() == "Sending text messages has been disabled for your service." assert page.select('main p')[0].text.strip() == "Sending text messages has been disabled for your service."
+118 -19
View File
@@ -674,6 +674,41 @@ def test_should_be_able_to_view_a_template_with_links(
) )
def test_view_broadcast_template(
client_request,
service_one,
mock_get_broadcast_template,
mock_get_template_folders,
active_user_with_permissions,
fake_uuid,
):
page = client_request.get(
'.view_template',
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_test_page_title=False,
)
assert [
(link.text.strip(), link['href'])
for link in page.select('.pill-separate-item')
] == [
('Edit', url_for(
'.edit_service_template',
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
)),
]
assert (
normalize_spaces(page.select_one('.template-container').text)
) == (
normalize_spaces(page.select_one('.broadcast-message-wrapper').text)
) == (
'This is a test'
)
def test_should_show_template_id_on_template_page( def test_should_show_template_id_on_template_page(
client_request, client_request,
mock_get_service_template, mock_get_service_template,
@@ -1241,9 +1276,9 @@ def test_cant_copy_template_from_non_member_service(
assert mock_get_service_email_template.call_args_list == [] assert mock_get_service_email_template.call_args_list == []
@pytest.mark.parametrize('endpoint, data, expected_error', ( @pytest.mark.parametrize('service_permissions, data, expected_error', (
( (
'main.choose_template', ['letter'],
{ {
'operation': 'add-new-template', 'operation': 'add-new-template',
'add_template_by_template_type': 'email', 'add_template_by_template_type': 'email',
@@ -1251,30 +1286,47 @@ def test_cant_copy_template_from_non_member_service(
"Sending emails has been disabled for your service." "Sending emails has been disabled for your service."
), ),
( (
'main.choose_template', ['email'],
{ {
'operation': 'add-new-template', 'operation': 'add-new-template',
'add_template_by_template_type': 'sms', 'add_template_by_template_type': 'sms',
}, },
"Sending text messages has been disabled for your service." "Sending text messages has been disabled for your service."
), ),
(
['sms'],
{
'operation': 'add-new-template',
'add_template_by_template_type': 'letter',
},
"Sending letters has been disabled for your service."
),
(
['letter'],
{
'operation': 'add-new-template',
'add_template_by_template_type': 'broadcast',
},
"Sending broadcasts has been disabled for your service."
),
)) ))
def test_should_not_allow_creation_of_template_through_form_without_correct_permission( def test_should_not_allow_creation_of_template_through_form_without_correct_permission(
client_request, client_request,
service_one, service_one,
mock_get_service_templates, mock_get_service_templates,
mock_get_template_folders, mock_get_template_folders,
endpoint, service_permissions,
data, data,
expected_error, expected_error,
fake_uuid, fake_uuid,
): ):
service_one['permissions'] = [] service_one['permissions'] = service_permissions
page = client_request.post( page = client_request.post(
endpoint, 'main.choose_template',
service_id=SERVICE_ONE_ID, service_id=SERVICE_ONE_ID,
_data=data, _data=data,
_follow_redirects=True, _follow_redirects=True,
_expected_status=403,
) )
assert normalize_spaces(page.select('main p')[0].text) == expected_error assert normalize_spaces(page.select('main p')[0].text) == expected_error
assert page.select(".govuk-back-link")[0].text == "Back" assert page.select(".govuk-back-link")[0].text == "Back"
@@ -1284,24 +1336,31 @@ def test_should_not_allow_creation_of_template_through_form_without_correct_perm
) )
@pytest.mark.parametrize('type_of_template', ['email', 'sms']) @pytest.mark.parametrize('method', ('get', 'post'))
@pytest.mark.parametrize('type_of_template, expected_error', [
('email', 'Sending emails has been disabled for your service.'),
('sms', 'Sending text messages has been disabled for your service.'),
('letter', 'Sending letters has been disabled for your service.'),
('broadcast', 'Sending broadcasts has been disabled for your service.'),
])
def test_should_not_allow_creation_of_a_template_without_correct_permission( def test_should_not_allow_creation_of_a_template_without_correct_permission(
client_request, client_request,
service_one, service_one,
mocker, mocker,
method,
type_of_template, type_of_template,
expected_error,
): ):
service_one['permissions'] = [] service_one['permissions'] = []
template_description = {'sms': 'text messages', 'email': 'emails'}
page = client_request.get( page = getattr(client_request, method)(
'.add_service_template', '.add_service_template',
service_id=SERVICE_ONE_ID, service_id=SERVICE_ONE_ID,
template_type=type_of_template, template_type=type_of_template,
_follow_redirects=True, _follow_redirects=True,
_expected_status=403,
) )
assert page.select('main p')[0].text.strip() == \ assert page.select('main p')[0].text.strip() == expected_error
"Sending {} has been disabled for your service.".format(template_description[type_of_template])
assert page.select(".govuk-back-link")[0].text == "Back" assert page.select(".govuk-back-link")[0].text == "Back"
assert page.select(".govuk-back-link")[0]['href'] == url_for( assert page.select(".govuk-back-link")[0]['href'] == url_for(
'.choose_template', '.choose_template',
@@ -1419,6 +1478,7 @@ def test_should_not_allow_template_edits_without_correct_permission(
service_id=SERVICE_ONE_ID, service_id=SERVICE_ONE_ID,
template_id=fake_uuid, template_id=fake_uuid,
_follow_redirects=True, _follow_redirects=True,
_expected_status=403,
) )
assert page.select('main p')[0].text.strip() == "Sending text messages has been disabled for your service." assert page.select('main p')[0].text.strip() == "Sending text messages has been disabled for your service."
@@ -1934,15 +1994,26 @@ def test_can_create_email_template_with_emoji(
assert mock_create_service_template.called is True assert mock_create_service_template.called is True
def test_should_not_create_sms_template_with_emoji( @pytest.mark.parametrize('template_type, expected_error', (
('sms', (
'You cannot use 🍜 in text messages.'
)),
('broadcast', (
'You cannot use 🍜 in broadcasts.'
)),
))
def test_should_not_create_sms_or_broadcast_template_with_emoji(
client_request, client_request,
service_one, service_one,
mock_create_service_template, mock_create_service_template,
template_type,
expected_error,
): ):
service_one['permissions'] += [template_type]
page = client_request.post( page = client_request.post(
'.add_service_template', '.add_service_template',
service_id=SERVICE_ONE_ID, service_id=SERVICE_ONE_ID,
template_type='sms', template_type=template_type,
_data={ _data={
'name': "new name", 'name': "new name",
'template_content': "here are some noodles 🍜", 'template_content': "here are some noodles 🍜",
@@ -1952,16 +2023,37 @@ def test_should_not_create_sms_template_with_emoji(
}, },
_expected_status=200, _expected_status=200,
) )
assert "You cannot use 🍜 in text messages." in page.text assert expected_error in page.text
assert mock_create_service_template.called is False assert mock_create_service_template.called is False
@pytest.mark.parametrize('template_type, expected_error', (
('sms', (
'You cannot use 🍔 in text messages.'
)),
('broadcast', (
'You cannot use 🍔 in broadcasts.'
)),
))
def test_should_not_update_sms_template_with_emoji( def test_should_not_update_sms_template_with_emoji(
mocker,
client_request, client_request,
service_one,
mock_get_service_template, mock_get_service_template,
mock_update_service_template, mock_update_service_template,
fake_uuid, fake_uuid,
template_type,
expected_error,
): ):
service_one['permissions'] += [template_type]
return mocker.patch(
'app.service_api_client.get_service_template',
return_value=template_json(
SERVICE_ONE_ID,
fake_uuid,
type_=template_type,
),
)
page = client_request.post( page = client_request.post(
'.edit_service_template', '.edit_service_template',
service_id=SERVICE_ONE_ID, service_id=SERVICE_ONE_ID,
@@ -1971,19 +2063,26 @@ def test_should_not_update_sms_template_with_emoji(
'name': "new name", 'name': "new name",
'template_content': "here's a burger 🍔", 'template_content': "here's a burger 🍔",
'service': SERVICE_ONE_ID, 'service': SERVICE_ONE_ID,
'template_type': 'sms', 'template_type': template_type,
'process_type': 'normal' 'process_type': 'normal'
}, },
_expected_status=200, _expected_status=200,
) )
assert "You cannot use 🍔 in text messages." in page.text assert expected_error in page.text
assert mock_update_service_template.called is False assert mock_update_service_template.called is False
def test_should_create_sms_template_without_downgrading_unicode_characters( @pytest.mark.parametrize('template_type', (
'sms', 'broadcast'
))
def test_should_create_sms_or_broadcast_template_without_downgrading_unicode_characters(
client_request, client_request,
mock_create_service_template service_one,
mock_create_service_template,
template_type,
): ):
service_one['permissions'] += [template_type]
msg = 'here:\tare some “fancy quotes” and non\u200Bbreaking\u200Bspaces' msg = 'here:\tare some “fancy quotes” and non\u200Bbreaking\u200Bspaces'
client_request.post( client_request.post(
@@ -1993,7 +2092,7 @@ def test_should_create_sms_template_without_downgrading_unicode_characters(
_data={ _data={
'name': "new name", 'name': "new name",
'template_content': msg, 'template_content': msg,
'template_type': 'sms', 'template_type': template_type,
'service': SERVICE_ONE_ID, 'service': SERVICE_ONE_ID,
'process_type': 'normal' 'process_type': 'normal'
}, },
+20
View File
@@ -841,6 +841,26 @@ def mock_get_service_email_template(mocker):
'app.service_api_client.get_service_template', side_effect=_get) 'app.service_api_client.get_service_template', side_effect=_get)
@pytest.fixture(scope='function')
def mock_get_broadcast_template(mocker):
def _get(service_id, template_id, version=None):
template = template_json(
service_id,
template_id,
'Test alert',
'broadcast',
'This is a test',
)
if version:
template.update({'version': version})
return {'data': template}
return mocker.patch(
'app.service_api_client.get_service_template',
side_effect=_get
)
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def mock_get_service_email_template_without_placeholders(mocker): def mock_get_service_email_template_without_placeholders(mocker):
def _get(service_id, template_id, version=None): def _get(service_id, template_id, version=None):