mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-08 18:03:12 -04:00
Merge branch 'master' of https://github.com/alphagov/notifications-admin
This commit is contained in:
@@ -165,14 +165,16 @@ def init_csrf(application):
|
|||||||
|
|
||||||
@csrf.error_handler
|
@csrf.error_handler
|
||||||
def csrf_handler(reason):
|
def csrf_handler(reason):
|
||||||
|
application.logger.warning('csrf.error_message: {}'.format(reason))
|
||||||
|
|
||||||
if 'user_id' not in session:
|
if 'user_id' not in session:
|
||||||
application.logger.info(
|
application.logger.warning(
|
||||||
u'csrf.session_expired: Redirecting user to log in page'
|
u'csrf.session_expired: Redirecting user to log in page'
|
||||||
)
|
)
|
||||||
|
|
||||||
return application.login_manager.unauthorized()
|
return application.login_manager.unauthorized()
|
||||||
|
|
||||||
application.logger.info(
|
application.logger.warning(
|
||||||
u'csrf.invalid_token: Aborting request, user_id: {user_id}',
|
u'csrf.invalid_token: Aborting request, user_id: {user_id}',
|
||||||
extra={'user_id': session['user_id']})
|
extra={'user_id': session['user_id']})
|
||||||
|
|
||||||
|
|||||||
@@ -303,6 +303,15 @@ def service_set_international_sms(service_id):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@main.route("/services/<service_id>/service-settings/set-inbound-sms", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@user_has_permissions('manage_settings', admin_override=True)
|
||||||
|
def service_set_inbound_sms(service_id):
|
||||||
|
return render_template(
|
||||||
|
'views/service-settings/set-inbound-sms.html',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@main.route("/services/<service_id>/service-settings/set-letters", methods=['GET'])
|
@main.route("/services/<service_id>/service-settings/set-letters", methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
@user_has_permissions('manage_settings', admin_override=True)
|
@user_has_permissions('manage_settings', admin_override=True)
|
||||||
|
|||||||
@@ -105,10 +105,8 @@
|
|||||||
{% endcall %}
|
{% endcall %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{% macro boolean_field(yes) -%}
|
{% macro boolean_field(value) -%}
|
||||||
{% call field(status='yes' if yes else 'no') %}
|
{{ text_field('On' if value else 'Off') }}
|
||||||
{{ "Yes" if yes else "No" }}
|
|
||||||
{% endcall %}
|
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{% macro right_aligned_field_heading(text) %}
|
{% macro right_aligned_field_heading(text) %}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends "withnav_template.html" %}
|
{% extends "withnav_template.html" %}
|
||||||
{% from "components/table.html" import list_table, row, field, boolean_field, hidden_field_heading %}
|
{% from "components/table.html" import list_table, row, field, hidden_field_heading %}
|
||||||
{% from "components/page-footer.html" import page_footer %}
|
{% from "components/page-footer.html" import page_footer %}
|
||||||
{% from "components/tick-cross.html" import tick_cross %}
|
{% from "components/tick-cross.html" import tick_cross %}
|
||||||
|
|
||||||
|
|||||||
@@ -27,33 +27,30 @@
|
|||||||
<a href="https://ukgovernmentdigital.slack.com/messages/govuk-notify">cross-government Slack channel</a>.
|
<a href="https://ukgovernmentdigital.slack.com/messages/govuk-notify">cross-government Slack channel</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="heading-medium">May 2017</h2>
|
|
||||||
<ul class="list list-bullet">
|
|
||||||
<li>Supporting replies to text messages (invite only)</li>
|
|
||||||
<li>Sending individual messages via Notify interface</li>
|
|
||||||
<li>Searching for notifications by phone number or email address</li>
|
|
||||||
<li>Restricting notifications to sociable hours</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2 class="heading-medium">June 2017</h2>
|
<h2 class="heading-medium">June 2017</h2>
|
||||||
<ul class="list list-bullet">
|
<ul class="list list-bullet">
|
||||||
<li>Retiring version 1 of the API</li>
|
<li>Schedule retirement of version 1 of the API</li>
|
||||||
<li>Sending letters through the API</li>
|
<li>Sending letters through the API</li>
|
||||||
<li>Providing non-mobile phone 2-factor authentication</li>
|
<li>Posting delivery receipts and inbound messages to services</li>
|
||||||
|
<li>Explore non-mobile phone 2-factor authentication</li>
|
||||||
<li>Allowing multiple sender names and reply-to addresses</li>
|
<li>Allowing multiple sender names and reply-to addresses</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2 class="heading-medium">July to September 2017</h2>
|
<h2 class="heading-medium">July 2017</h2>
|
||||||
<ul class="list list-bullet">
|
<ul class="list list-bullet">
|
||||||
<li>Sending letters (available for everyone)</li>
|
|
||||||
<li>Sending of pre-compiled text messages and emails</li>
|
|
||||||
<li>Customising the expiry time for text messages</li>
|
<li>Customising the expiry time for text messages</li>
|
||||||
<li>Distributing delivery of notifications over a period of time</li>
|
<li>Sending of pre-compiled text messages and emails</li>
|
||||||
<li>Sending of pre-compiled letters</li>
|
|
||||||
<li>Publishing design patterns for integration</li>
|
<li>Publishing design patterns for integration</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2 class="heading-medium">October 2017 and beyond</h2>
|
<h2 class="heading-medium">August - October 2017</h2>
|
||||||
|
<ul class="list list-bullet">
|
||||||
|
<li>Sending letters (available for everyone)</li>
|
||||||
|
<li>Sending of pre-compiled letters</li>
|
||||||
|
<li>Distributing delivery of notifications over a period of time</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 class="heading-medium">November 2017 onwards</h2>
|
||||||
<ul class="list list-bullet">
|
<ul class="list list-bullet">
|
||||||
<li>Checking mobile numbers are valid before sending</li>
|
<li>Checking mobile numbers are valid before sending</li>
|
||||||
<li>Allowing people to request to join a service</li>
|
<li>Allowing people to request to join a service</li>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{% extends "withnav_template.html" %}
|
{% extends "withnav_template.html" %}
|
||||||
{% from "components/banner.html" import banner_wrapper %}
|
{% from "components/banner.html" import banner_wrapper %}
|
||||||
{% from "components/browse-list.html" import browse_list %}
|
{% from "components/browse-list.html" import browse_list %}
|
||||||
{% from "components/table.html" import mapping_table, row, text_field, edit_field, field %}
|
{% from "components/table.html" import mapping_table, row, text_field, edit_field, field, boolean_field %}
|
||||||
|
|
||||||
{% block service_page_title %}
|
{% block service_page_title %}
|
||||||
Settings
|
Settings
|
||||||
@@ -47,13 +47,19 @@
|
|||||||
|
|
||||||
{% call row() %}
|
{% call row() %}
|
||||||
{{ text_field('International text messages') }}
|
{{ text_field('International text messages') }}
|
||||||
{{ text_field('On' if current_service.can_send_international_sms else 'Off') }}
|
{{ boolean_field(current_service.can_send_international_sms) }}
|
||||||
{{ edit_field('Change', url_for('.service_set_international_sms', service_id=current_service.id)) }}
|
{{ edit_field('Change', url_for('.service_set_international_sms', service_id=current_service.id)) }}
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
|
|
||||||
|
{% call row() %}
|
||||||
|
{{ text_field('Receive text messages') }}
|
||||||
|
{{ boolean_field('inbound_sms' in current_service.permissions) }}
|
||||||
|
{{ edit_field('Change', url_for('.service_set_inbound_sms', service_id=current_service.id)) }}
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
{% call row() %}
|
{% call row() %}
|
||||||
{{ text_field('Letters') }}
|
{{ text_field('Letters') }}
|
||||||
{{ text_field('On' if current_service.can_send_letters else 'Off') }}
|
{{ boolean_field(current_service.can_send_letters) }}
|
||||||
{{ edit_field('Change', url_for('.service_set_letters', service_id=current_service.id)) }}
|
{{ edit_field('Change', url_for('.service_set_letters', service_id=current_service.id)) }}
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
|
|
||||||
|
|||||||
43
app/templates/views/service-settings/set-inbound-sms.html
Normal file
43
app/templates/views/service-settings/set-inbound-sms.html
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{% extends "withnav_template.html" %}
|
||||||
|
{% from "components/textbox.html" import textbox %}
|
||||||
|
{% from "components/page-footer.html" import page_footer %}
|
||||||
|
|
||||||
|
{% block service_page_title %}
|
||||||
|
Receive text messages
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block maincolumn_content %}
|
||||||
|
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="column-five-sixths">
|
||||||
|
<h1 class="heading-large">Receive text messages</h1>
|
||||||
|
{% if 'inbound_sms' in current_service.permissions %}
|
||||||
|
<p>
|
||||||
|
Your service can receive text messages sent to {{ current_service.sms_sender }}.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you want to turn this feature off,
|
||||||
|
<a href="{{ url_for('.support') }}">get in touch with the GOV.UK Notify team</a>.
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
<p>
|
||||||
|
Receiving text messages from your users is an
|
||||||
|
invitation‑only feature.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you want to try it out,
|
||||||
|
<a href="{{ url_for('.support') }}">get in touch with the GOV.UK Notify team</a>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
We’ll set you up with a special phone number, and you’ll be able to see
|
||||||
|
the messages on your dashboard, or get them using the API.
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{{ page_footer(
|
||||||
|
back_link=url_for('.service_settings', service_id=current_service.id),
|
||||||
|
back_link_text='Back to settings'
|
||||||
|
) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
{% from "components/page-footer.html" import page_footer %}
|
{% from "components/page-footer.html" import page_footer %}
|
||||||
|
|
||||||
{% block service_page_title %}
|
{% block service_page_title %}
|
||||||
Text message sender
|
International text messages
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block maincolumn_content %}
|
{% block maincolumn_content %}
|
||||||
|
|||||||
@@ -117,7 +117,7 @@
|
|||||||
<div class="grid-row bottom-gutter">
|
<div class="grid-row bottom-gutter">
|
||||||
<div class="column-half">
|
<div class="column-half">
|
||||||
<h3 class="visually-hidden">Services</h3>
|
<h3 class="visually-hidden">Services</h3>
|
||||||
<div class="product-page-big-number">54</div>
|
<div class="product-page-big-number">55</div>
|
||||||
services
|
services
|
||||||
</div>
|
</div>
|
||||||
<div class="column-half">
|
<div class="column-half">
|
||||||
|
|||||||
276
tests/app/main/views/test_activity.py
Normal file
276
tests/app/main/views/test_activity.py
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
import json
|
||||||
|
import uuid
|
||||||
|
from urllib.parse import urlparse, quote, parse_qs
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from flask import url_for
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
from app.main.views.jobs import get_time_left, get_status_filters
|
||||||
|
from tests import notification_json
|
||||||
|
from tests.conftest import SERVICE_ONE_ID
|
||||||
|
from freezegun import freeze_time
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"message_type,page_title", [
|
||||||
|
('email', 'Emails'),
|
||||||
|
('sms', 'Text messages')
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"status_argument, expected_api_call", [
|
||||||
|
(
|
||||||
|
'',
|
||||||
|
[
|
||||||
|
'created', 'pending', 'sending',
|
||||||
|
'delivered', 'sent',
|
||||||
|
'failed', 'temporary-failure', 'permanent-failure', 'technical-failure',
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'sending',
|
||||||
|
['sending', 'created', 'pending']
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'delivered',
|
||||||
|
['delivered', 'sent']
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'failed',
|
||||||
|
['failed', 'temporary-failure', 'permanent-failure', 'technical-failure']
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"page_argument, expected_page_argument", [
|
||||||
|
(1, 1),
|
||||||
|
(22, 22),
|
||||||
|
(None, 1)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"to_argument, expected_to_argument", [
|
||||||
|
('', ''),
|
||||||
|
('+447900900123', '+447900900123'),
|
||||||
|
('test@example.com', 'test@example.com'),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_can_show_notifications(
|
||||||
|
logged_in_client,
|
||||||
|
service_one,
|
||||||
|
mock_get_notifications,
|
||||||
|
mock_get_detailed_service,
|
||||||
|
message_type,
|
||||||
|
page_title,
|
||||||
|
status_argument,
|
||||||
|
expected_api_call,
|
||||||
|
page_argument,
|
||||||
|
expected_page_argument,
|
||||||
|
to_argument,
|
||||||
|
expected_to_argument,
|
||||||
|
):
|
||||||
|
response = logged_in_client.get(url_for(
|
||||||
|
'main.view_notifications',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
message_type=message_type,
|
||||||
|
status=status_argument,
|
||||||
|
page=page_argument,
|
||||||
|
to=to_argument,
|
||||||
|
))
|
||||||
|
assert response.status_code == 200
|
||||||
|
content = response.get_data(as_text=True)
|
||||||
|
notifications = notification_json(service_one['id'])
|
||||||
|
notification = notifications['notifications'][0]
|
||||||
|
assert notification['to'] in content
|
||||||
|
assert notification['status'] in content
|
||||||
|
assert notification['template']['name'] in content
|
||||||
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
assert page_title in page.h1.text.strip()
|
||||||
|
|
||||||
|
path_to_json = page.find("div", {'data-key': 'notifications'})['data-resource']
|
||||||
|
|
||||||
|
url = urlparse(path_to_json)
|
||||||
|
assert url.path == '/services/{}/notifications/{}.json'.format(service_one['id'], message_type)
|
||||||
|
query_dict = parse_qs(url.query)
|
||||||
|
if status_argument:
|
||||||
|
assert query_dict['status'] == [status_argument]
|
||||||
|
if expected_page_argument:
|
||||||
|
assert query_dict['page'] == [str(expected_page_argument)]
|
||||||
|
if to_argument:
|
||||||
|
assert query_dict['to'] == [to_argument]
|
||||||
|
|
||||||
|
mock_get_notifications.assert_called_with(
|
||||||
|
limit_days=7,
|
||||||
|
page=expected_page_argument,
|
||||||
|
service_id=service_one['id'],
|
||||||
|
status=expected_api_call,
|
||||||
|
template_type=[message_type],
|
||||||
|
to=expected_to_argument,
|
||||||
|
)
|
||||||
|
|
||||||
|
json_response = logged_in_client.get(url_for(
|
||||||
|
'main.get_notifications_as_json',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
message_type=message_type,
|
||||||
|
status=status_argument
|
||||||
|
))
|
||||||
|
json_content = json.loads(json_response.get_data(as_text=True))
|
||||||
|
assert json_content.keys() == {'counts', 'notifications'}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("initial_query_arguments, expected_status_field_value, expected_search_box_contents", [
|
||||||
|
(
|
||||||
|
{
|
||||||
|
'message_type': 'sms',
|
||||||
|
},
|
||||||
|
'sending,delivered,failed',
|
||||||
|
'',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
'message_type': 'sms',
|
||||||
|
'to': '+33(0)5-12-34-56-78',
|
||||||
|
},
|
||||||
|
'sending,delivered,failed',
|
||||||
|
'+33(0)5-12-34-56-78',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
'status': 'failed',
|
||||||
|
'message_type': 'email',
|
||||||
|
'page': '99',
|
||||||
|
'to': 'test@example.com',
|
||||||
|
},
|
||||||
|
'failed',
|
||||||
|
'test@example.com',
|
||||||
|
),
|
||||||
|
])
|
||||||
|
def test_search_recipient_form(
|
||||||
|
logged_in_client,
|
||||||
|
mock_get_notifications,
|
||||||
|
mock_get_detailed_service,
|
||||||
|
initial_query_arguments,
|
||||||
|
expected_status_field_value,
|
||||||
|
expected_search_box_contents,
|
||||||
|
):
|
||||||
|
response = logged_in_client.get(url_for(
|
||||||
|
'main.view_notifications',
|
||||||
|
service_id=SERVICE_ONE_ID,
|
||||||
|
**initial_query_arguments
|
||||||
|
))
|
||||||
|
assert response.status_code == 200
|
||||||
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
|
||||||
|
action_url = page.find("form")['action']
|
||||||
|
url = urlparse(action_url)
|
||||||
|
assert url.path == '/services/{}/notifications/{}'.format(
|
||||||
|
SERVICE_ONE_ID,
|
||||||
|
initial_query_arguments['message_type']
|
||||||
|
)
|
||||||
|
query_dict = parse_qs(url.query)
|
||||||
|
assert query_dict == {}
|
||||||
|
|
||||||
|
assert page.find("input", {'name': 'status'})['value'] == expected_status_field_value
|
||||||
|
assert page.find("input", {'name': 'to'})['value'] == expected_search_box_contents
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_show_notifications_for_a_service_with_next_previous(
|
||||||
|
logged_in_client,
|
||||||
|
service_one,
|
||||||
|
active_user_with_permissions,
|
||||||
|
mock_get_notifications_with_previous_next,
|
||||||
|
mock_get_detailed_service,
|
||||||
|
mocker,
|
||||||
|
):
|
||||||
|
response = logged_in_client.get(url_for(
|
||||||
|
'main.view_notifications',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
message_type='sms',
|
||||||
|
page=2
|
||||||
|
))
|
||||||
|
assert response.status_code == 200
|
||||||
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
next_page_link = page.find('a', {'rel': 'next'})
|
||||||
|
prev_page_link = page.find('a', {'rel': 'previous'})
|
||||||
|
assert (
|
||||||
|
url_for('main.view_notifications', service_id=service_one['id'], message_type='sms', page=3) in
|
||||||
|
next_page_link['href']
|
||||||
|
)
|
||||||
|
assert 'Next page' in next_page_link.text.strip()
|
||||||
|
assert 'page 3' in next_page_link.text.strip()
|
||||||
|
assert (
|
||||||
|
url_for('main.view_notifications', service_id=service_one['id'], message_type='sms', page=1) in
|
||||||
|
prev_page_link['href']
|
||||||
|
)
|
||||||
|
assert 'Previous page' in prev_page_link.text.strip()
|
||||||
|
assert 'page 1' in prev_page_link.text.strip()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"job_created_at, expected_message", [
|
||||||
|
("2016-01-10 11:09:00.000000+00:00", "Data available for 7 days"),
|
||||||
|
("2016-01-04 11:09:00.000000+00:00", "Data available for 1 day"),
|
||||||
|
("2016-01-03 11:09:00.000000+00:00", "Data available for 11 hours"),
|
||||||
|
("2016-01-02 23:59:59.000000+00:00", "Data no longer available")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@freeze_time("2016-01-10 12:00:00.000000")
|
||||||
|
def test_time_left(job_created_at, expected_message):
|
||||||
|
assert get_time_left(job_created_at) == expected_message
|
||||||
|
|
||||||
|
|
||||||
|
STATISTICS = {
|
||||||
|
'sms': {
|
||||||
|
'requested': 6,
|
||||||
|
'failed': 2,
|
||||||
|
'delivered': 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_status_filters_calculates_stats(client):
|
||||||
|
ret = get_status_filters({'id': 'foo'}, 'sms', STATISTICS)
|
||||||
|
|
||||||
|
assert {label: count for label, _option, _link, count in ret} == {
|
||||||
|
'total': 6,
|
||||||
|
'sending': 3,
|
||||||
|
'failed': 2,
|
||||||
|
'delivered': 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_status_filters_in_right_order(client):
|
||||||
|
ret = get_status_filters({'id': 'foo'}, 'sms', STATISTICS)
|
||||||
|
|
||||||
|
assert [label for label, _option, _link, _count in ret] == [
|
||||||
|
'total', 'sending', 'delivered', 'failed'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_status_filters_constructs_links(client):
|
||||||
|
ret = get_status_filters({'id': 'foo'}, 'sms', STATISTICS)
|
||||||
|
|
||||||
|
link = ret[0][2]
|
||||||
|
assert link == '/services/foo/notifications/sms?status={}'.format(quote('sending,delivered,failed'))
|
||||||
|
|
||||||
|
|
||||||
|
def test_html_contains_notification_id(
|
||||||
|
logged_in_client,
|
||||||
|
service_one,
|
||||||
|
active_user_with_permissions,
|
||||||
|
mock_get_notifications,
|
||||||
|
mock_get_detailed_service,
|
||||||
|
mocker,
|
||||||
|
):
|
||||||
|
response = logged_in_client.get(url_for(
|
||||||
|
'main.view_notifications',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
message_type='sms',
|
||||||
|
status='')
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
notifications = page.tbody.find_all('tr')
|
||||||
|
for tr in notifications:
|
||||||
|
assert uuid.UUID(tr.attrs['id'])
|
||||||
@@ -275,201 +275,6 @@ def test_should_show_updates_for_one_job_as_json(
|
|||||||
assert 'Sent by Test User on 1 January at midnight' in content['status']
|
assert 'Sent by Test User on 1 January at midnight' in content['status']
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"message_type,page_title", [
|
|
||||||
('email', 'Emails'),
|
|
||||||
('sms', 'Text messages')
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"status_argument, expected_api_call", [
|
|
||||||
(
|
|
||||||
'',
|
|
||||||
[
|
|
||||||
'created', 'pending', 'sending',
|
|
||||||
'delivered', 'sent',
|
|
||||||
'failed', 'temporary-failure', 'permanent-failure', 'technical-failure',
|
|
||||||
]
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'sending',
|
|
||||||
['sending', 'created', 'pending']
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'delivered',
|
|
||||||
['delivered', 'sent']
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'failed',
|
|
||||||
['failed', 'temporary-failure', 'permanent-failure', 'technical-failure']
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"page_argument, expected_page_argument", [
|
|
||||||
(1, 1),
|
|
||||||
(22, 22),
|
|
||||||
(None, 1)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"to_argument, expected_to_argument", [
|
|
||||||
('', ''),
|
|
||||||
('+447900900123', '+447900900123'),
|
|
||||||
('test@example.com', 'test@example.com'),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
def test_can_show_notifications(
|
|
||||||
logged_in_client,
|
|
||||||
service_one,
|
|
||||||
mock_get_notifications,
|
|
||||||
mock_get_detailed_service,
|
|
||||||
message_type,
|
|
||||||
page_title,
|
|
||||||
status_argument,
|
|
||||||
expected_api_call,
|
|
||||||
page_argument,
|
|
||||||
expected_page_argument,
|
|
||||||
to_argument,
|
|
||||||
expected_to_argument,
|
|
||||||
):
|
|
||||||
response = logged_in_client.get(url_for(
|
|
||||||
'main.view_notifications',
|
|
||||||
service_id=service_one['id'],
|
|
||||||
message_type=message_type,
|
|
||||||
status=status_argument,
|
|
||||||
page=page_argument,
|
|
||||||
to=to_argument,
|
|
||||||
))
|
|
||||||
assert response.status_code == 200
|
|
||||||
content = response.get_data(as_text=True)
|
|
||||||
notifications = notification_json(service_one['id'])
|
|
||||||
notification = notifications['notifications'][0]
|
|
||||||
assert notification['to'] in content
|
|
||||||
assert notification['status'] in content
|
|
||||||
assert notification['template']['name'] in content
|
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
|
||||||
assert page_title in page.h1.text.strip()
|
|
||||||
|
|
||||||
path_to_json = page.find("div", {'data-key': 'notifications'})['data-resource']
|
|
||||||
|
|
||||||
url = urlparse(path_to_json)
|
|
||||||
assert url.path == '/services/{}/notifications/{}.json'.format(service_one['id'], message_type)
|
|
||||||
query_dict = parse_qs(url.query)
|
|
||||||
if status_argument:
|
|
||||||
assert query_dict['status'] == [status_argument]
|
|
||||||
if expected_page_argument:
|
|
||||||
assert query_dict['page'] == [str(expected_page_argument)]
|
|
||||||
if to_argument:
|
|
||||||
assert query_dict['to'] == [to_argument]
|
|
||||||
|
|
||||||
mock_get_notifications.assert_called_with(
|
|
||||||
limit_days=7,
|
|
||||||
page=expected_page_argument,
|
|
||||||
service_id=service_one['id'],
|
|
||||||
status=expected_api_call,
|
|
||||||
template_type=[message_type],
|
|
||||||
to=expected_to_argument,
|
|
||||||
)
|
|
||||||
|
|
||||||
json_response = logged_in_client.get(url_for(
|
|
||||||
'main.get_notifications_as_json',
|
|
||||||
service_id=service_one['id'],
|
|
||||||
message_type=message_type,
|
|
||||||
status=status_argument
|
|
||||||
))
|
|
||||||
json_content = json.loads(json_response.get_data(as_text=True))
|
|
||||||
assert json_content.keys() == {'counts', 'notifications'}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("initial_query_arguments, expected_status_field_value, expected_search_box_contents", [
|
|
||||||
(
|
|
||||||
{
|
|
||||||
'message_type': 'sms',
|
|
||||||
},
|
|
||||||
'sending,delivered,failed',
|
|
||||||
'',
|
|
||||||
),
|
|
||||||
(
|
|
||||||
{
|
|
||||||
'message_type': 'sms',
|
|
||||||
'to': '+33(0)5-12-34-56-78',
|
|
||||||
},
|
|
||||||
'sending,delivered,failed',
|
|
||||||
'+33(0)5-12-34-56-78',
|
|
||||||
),
|
|
||||||
(
|
|
||||||
{
|
|
||||||
'status': 'failed',
|
|
||||||
'message_type': 'email',
|
|
||||||
'page': '99',
|
|
||||||
'to': 'test@example.com',
|
|
||||||
},
|
|
||||||
'failed',
|
|
||||||
'test@example.com',
|
|
||||||
),
|
|
||||||
])
|
|
||||||
def test_search_recipient_form(
|
|
||||||
logged_in_client,
|
|
||||||
mock_get_notifications,
|
|
||||||
mock_get_detailed_service,
|
|
||||||
initial_query_arguments,
|
|
||||||
expected_status_field_value,
|
|
||||||
expected_search_box_contents,
|
|
||||||
):
|
|
||||||
response = logged_in_client.get(url_for(
|
|
||||||
'main.view_notifications',
|
|
||||||
service_id=SERVICE_ONE_ID,
|
|
||||||
**initial_query_arguments
|
|
||||||
))
|
|
||||||
assert response.status_code == 200
|
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
|
||||||
|
|
||||||
action_url = page.find("form")['action']
|
|
||||||
url = urlparse(action_url)
|
|
||||||
assert url.path == '/services/{}/notifications/{}'.format(
|
|
||||||
SERVICE_ONE_ID,
|
|
||||||
initial_query_arguments['message_type']
|
|
||||||
)
|
|
||||||
query_dict = parse_qs(url.query)
|
|
||||||
assert query_dict == {}
|
|
||||||
|
|
||||||
assert page.find("input", {'name': 'status'})['value'] == expected_status_field_value
|
|
||||||
assert page.find("input", {'name': 'to'})['value'] == expected_search_box_contents
|
|
||||||
|
|
||||||
|
|
||||||
def test_should_show_notifications_for_a_service_with_next_previous(
|
|
||||||
logged_in_client,
|
|
||||||
service_one,
|
|
||||||
active_user_with_permissions,
|
|
||||||
mock_get_notifications_with_previous_next,
|
|
||||||
mock_get_detailed_service,
|
|
||||||
mocker,
|
|
||||||
):
|
|
||||||
response = logged_in_client.get(url_for(
|
|
||||||
'main.view_notifications',
|
|
||||||
service_id=service_one['id'],
|
|
||||||
message_type='sms',
|
|
||||||
page=2
|
|
||||||
))
|
|
||||||
assert response.status_code == 200
|
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
|
||||||
next_page_link = page.find('a', {'rel': 'next'})
|
|
||||||
prev_page_link = page.find('a', {'rel': 'previous'})
|
|
||||||
assert (
|
|
||||||
url_for('main.view_notifications', service_id=service_one['id'], message_type='sms', page=3) in
|
|
||||||
next_page_link['href']
|
|
||||||
)
|
|
||||||
assert 'Next page' in next_page_link.text.strip()
|
|
||||||
assert 'page 3' in next_page_link.text.strip()
|
|
||||||
assert (
|
|
||||||
url_for('main.view_notifications', service_id=service_one['id'], message_type='sms', page=1) in
|
|
||||||
prev_page_link['href']
|
|
||||||
)
|
|
||||||
assert 'Previous page' in prev_page_link.text.strip()
|
|
||||||
assert 'page 1' in prev_page_link.text.strip()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"job_created_at, expected_message", [
|
"job_created_at, expected_message", [
|
||||||
("2016-01-10 11:09:00.000000+00:00", "Data available for 7 days"),
|
("2016-01-10 11:09:00.000000+00:00", "Data available for 7 days"),
|
||||||
@@ -481,59 +286,3 @@ def test_should_show_notifications_for_a_service_with_next_previous(
|
|||||||
@freeze_time("2016-01-10 12:00:00.000000")
|
@freeze_time("2016-01-10 12:00:00.000000")
|
||||||
def test_time_left(job_created_at, expected_message):
|
def test_time_left(job_created_at, expected_message):
|
||||||
assert get_time_left(job_created_at) == expected_message
|
assert get_time_left(job_created_at) == expected_message
|
||||||
|
|
||||||
|
|
||||||
STATISTICS = {
|
|
||||||
'sms': {
|
|
||||||
'requested': 6,
|
|
||||||
'failed': 2,
|
|
||||||
'delivered': 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_status_filters_calculates_stats(client):
|
|
||||||
ret = get_status_filters({'id': 'foo'}, 'sms', STATISTICS)
|
|
||||||
|
|
||||||
assert {label: count for label, _option, _link, count in ret} == {
|
|
||||||
'total': 6,
|
|
||||||
'sending': 3,
|
|
||||||
'failed': 2,
|
|
||||||
'delivered': 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_status_filters_in_right_order(client):
|
|
||||||
ret = get_status_filters({'id': 'foo'}, 'sms', STATISTICS)
|
|
||||||
|
|
||||||
assert [label for label, _option, _link, _count in ret] == [
|
|
||||||
'total', 'sending', 'delivered', 'failed'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_status_filters_constructs_links(client):
|
|
||||||
ret = get_status_filters({'id': 'foo'}, 'sms', STATISTICS)
|
|
||||||
|
|
||||||
link = ret[0][2]
|
|
||||||
assert link == '/services/foo/notifications/sms?status={}'.format(quote('sending,delivered,failed'))
|
|
||||||
|
|
||||||
|
|
||||||
def test_html_contains_notification_id(
|
|
||||||
logged_in_client,
|
|
||||||
service_one,
|
|
||||||
active_user_with_permissions,
|
|
||||||
mock_get_notifications,
|
|
||||||
mock_get_detailed_service,
|
|
||||||
mocker,
|
|
||||||
):
|
|
||||||
response = logged_in_client.get(url_for(
|
|
||||||
'main.view_notifications',
|
|
||||||
service_id=service_one['id'],
|
|
||||||
message_type='sms',
|
|
||||||
status='')
|
|
||||||
)
|
|
||||||
assert response.status_code == 200
|
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
|
||||||
notifications = page.tbody.find_all('tr')
|
|
||||||
for tr in notifications:
|
|
||||||
assert uuid.UUID(tr.attrs['id'])
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from tests.conftest import active_user_with_permissions, platform_admin_user
|
|||||||
'Email reply to address None Change',
|
'Email reply to address None Change',
|
||||||
'Text message sender GOVUK Change',
|
'Text message sender GOVUK Change',
|
||||||
'International text messages Off Change',
|
'International text messages Off Change',
|
||||||
|
'Receive text messages Off Change',
|
||||||
'Letters Off Change',
|
'Letters Off Change',
|
||||||
]),
|
]),
|
||||||
(platform_admin_user, [
|
(platform_admin_user, [
|
||||||
@@ -29,6 +30,7 @@ from tests.conftest import active_user_with_permissions, platform_admin_user
|
|||||||
'Email reply to address None Change',
|
'Email reply to address None Change',
|
||||||
'Text message sender GOVUK Change',
|
'Text message sender GOVUK Change',
|
||||||
'International text messages Off Change',
|
'International text messages Off Change',
|
||||||
|
'Receive text messages Off Change',
|
||||||
'Letters Off Change',
|
'Letters Off Change',
|
||||||
'Label Value Action',
|
'Label Value Action',
|
||||||
'Email branding GOV.UK Change',
|
'Email branding GOV.UK Change',
|
||||||
@@ -67,6 +69,8 @@ def test_should_show_overview_for_service_with_more_things_set(
|
|||||||
mock_get_letter_organisations,
|
mock_get_letter_organisations,
|
||||||
):
|
):
|
||||||
client.login(active_user_with_permissions, mocker, service_with_reply_to_addresses)
|
client.login(active_user_with_permissions, mocker, service_with_reply_to_addresses)
|
||||||
|
service_with_reply_to_addresses['permissions'] = ['inbound_sms']
|
||||||
|
service_with_reply_to_addresses['can_send_international_sms'] = True
|
||||||
response = client.get(url_for(
|
response = client.get(url_for(
|
||||||
'main.service_settings', service_id=service_with_reply_to_addresses['id']
|
'main.service_settings', service_id=service_with_reply_to_addresses['id']
|
||||||
))
|
))
|
||||||
@@ -74,8 +78,9 @@ def test_should_show_overview_for_service_with_more_things_set(
|
|||||||
for index, row in enumerate([
|
for index, row in enumerate([
|
||||||
'Service name service one Change',
|
'Service name service one Change',
|
||||||
'Email reply to address test@example.com Change',
|
'Email reply to address test@example.com Change',
|
||||||
'Text message sender elevenchars Change',
|
'Text message sender elevenchars',
|
||||||
'International text messages Off Change',
|
'International text messages On Change',
|
||||||
|
'Receive text messages On Change',
|
||||||
'Letters Off Change',
|
'Letters Off Change',
|
||||||
]):
|
]):
|
||||||
assert row == " ".join(page.find_all('tr')[index + 1].text.split())
|
assert row == " ".join(page.find_all('tr')[index + 1].text.split())
|
||||||
@@ -120,7 +125,7 @@ def test_letter_contact_block_shows_none_if_not_set(
|
|||||||
))
|
))
|
||||||
|
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
div = page.find_all('tr')[6].find_all('td')[1].div
|
div = page.find_all('tr')[7].find_all('td')[1].div
|
||||||
assert div.text.strip() == 'None'
|
assert div.text.strip() == 'None'
|
||||||
assert 'default' in div.attrs['class'][0]
|
assert 'default' in div.attrs['class'][0]
|
||||||
|
|
||||||
@@ -138,7 +143,7 @@ def test_escapes_letter_contact_block(
|
|||||||
))
|
))
|
||||||
|
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
div = str(page.find_all('tr')[6].find_all('td')[1].div)
|
div = str(page.find_all('tr')[7].find_all('td')[1].div)
|
||||||
assert 'foo<br>bar' in div
|
assert 'foo<br>bar' in div
|
||||||
assert '<script>' not in div
|
assert '<script>' not in div
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user