mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-02-25 12:51:05 -05:00
For someone who has retrieved a template ID from their system the only way to find it in Notify is: - hack the URL - click through every template, visually inspecting the ID shown on the page until you find the right one Neither of these is ideal. This commit adds searching by ID, for those services who have an API integration. This means we don’t need to confuse teams who aren’t using the API by talking about IDs. This is similar to how we let these teams search for notifications by reference[1] 1. https://github.com/alphagov/notifications-admin/pull/3223/files
544 lines
15 KiB
Python
544 lines
15 KiB
Python
import pytest
|
|
from flask import Flask
|
|
|
|
from app import create_app
|
|
from app.navigation import (
|
|
CaseworkNavigation,
|
|
HeaderNavigation,
|
|
MainNavigation,
|
|
Navigation,
|
|
OrgNavigation,
|
|
)
|
|
from tests.conftest import ORGANISATION_ID, SERVICE_ONE_ID, normalize_spaces
|
|
|
|
EXCLUDED_ENDPOINTS = tuple(map(Navigation.get_endpoint_with_blueprint, {
|
|
'accept_invite',
|
|
'accept_org_invite',
|
|
'accessibility_statement',
|
|
'action_blocked',
|
|
'add_data_retention',
|
|
'add_organisation',
|
|
'add_organisation_from_gp_service',
|
|
'add_organisation_from_nhs_local_service',
|
|
'add_service',
|
|
'add_service_template',
|
|
'api_callbacks',
|
|
'api_documentation',
|
|
'api_integration',
|
|
'api_keys',
|
|
'approve_broadcast_message',
|
|
'archive_service',
|
|
'archive_user',
|
|
'bat_phone',
|
|
'begin_tour',
|
|
'branding_and_customisation',
|
|
'branding_request',
|
|
'broadcast',
|
|
'broadcast_dashboard',
|
|
'broadcast_dashboard_previous',
|
|
'broadcast_dashboard_rejected',
|
|
'broadcast_dashboard_updates',
|
|
'broadcast_tour',
|
|
'callbacks',
|
|
'cancel_broadcast_message',
|
|
'cancel_invited_org_user',
|
|
'cancel_invited_user',
|
|
'cancel_job',
|
|
'cancel_letter',
|
|
'cancel_letter_job',
|
|
'check_and_resend_text_code',
|
|
'check_and_resend_verification_code',
|
|
'check_contact_list',
|
|
'check_messages',
|
|
'check_notification',
|
|
'check_tour_notification',
|
|
'choose_account',
|
|
'choose_broadcast_area',
|
|
'choose_broadcast_library',
|
|
'choose_broadcast_sub_area',
|
|
'choose_from_contact_list',
|
|
'choose_service',
|
|
'choose_template',
|
|
'choose_template_to_copy',
|
|
'clear_cache',
|
|
'confirm_edit_organisation_name',
|
|
'confirm_edit_user_email',
|
|
'confirm_edit_user_mobile_number',
|
|
'confirm_redact_template',
|
|
'contact_list',
|
|
'conversation',
|
|
'conversation_reply',
|
|
'conversation_reply_with_template',
|
|
'conversation_updates',
|
|
'cookies',
|
|
'copy_template',
|
|
'count_content_length',
|
|
'create_and_send_messages',
|
|
'create_api_key',
|
|
'create_email_branding',
|
|
'create_letter_branding',
|
|
'data_retention',
|
|
'delete_contact_list',
|
|
'delete_service_template',
|
|
'delete_template_folder',
|
|
'delivery_and_failure',
|
|
'delivery_status_callback',
|
|
'design_content',
|
|
'documentation',
|
|
'download_contact_list',
|
|
'download_notifications_csv',
|
|
'edit_and_format_messages',
|
|
'edit_data_retention',
|
|
'edit_organisation_agreement',
|
|
'edit_organisation_billing_details',
|
|
'edit_organisation_crown_status',
|
|
'edit_organisation_domains',
|
|
'edit_organisation_email_branding',
|
|
'edit_organisation_go_live_notes',
|
|
'edit_organisation_letter_branding',
|
|
'edit_organisation_name',
|
|
'edit_organisation_notes',
|
|
'edit_organisation_type',
|
|
'edit_provider',
|
|
'edit_service_billing_details',
|
|
'edit_service_notes',
|
|
'edit_service_template',
|
|
'edit_sms_provider_ratio',
|
|
'edit_template_postage',
|
|
'edit_user_email',
|
|
'edit_user_mobile_number',
|
|
'edit_user_org_permissions',
|
|
'edit_user_permissions',
|
|
'email_branding',
|
|
'email_not_received',
|
|
'email_template',
|
|
'error',
|
|
'estimate_usage',
|
|
'features',
|
|
'features_email',
|
|
'features_letters',
|
|
'features_sms',
|
|
'feedback',
|
|
'find_services_by_name',
|
|
'find_users_by_email',
|
|
'forgot_password',
|
|
'get_billing_report',
|
|
'get_example_csv',
|
|
'get_notifications_as_json',
|
|
'get_started',
|
|
'get_started_old',
|
|
'go_to_dashboard_after_tour',
|
|
'guest_list',
|
|
'guidance_index',
|
|
'history',
|
|
'how_to_pay',
|
|
'inbound_sms_admin',
|
|
'inbox',
|
|
'inbox_download',
|
|
'inbox_updates',
|
|
'index',
|
|
'information_risk_management',
|
|
'information_security',
|
|
'integration_testing',
|
|
'invite_org_user',
|
|
'invite_user',
|
|
'letter_branding',
|
|
'letter_spec',
|
|
'letter_specification',
|
|
'letter_template',
|
|
'link_service_to_organisation',
|
|
'live_services',
|
|
'live_services_csv',
|
|
'manage_org_users',
|
|
'manage_template_folder',
|
|
'manage_users',
|
|
'message_status',
|
|
'monthly',
|
|
'new_broadcast',
|
|
'new_password',
|
|
'no_cookie.check_messages_preview',
|
|
'no_cookie.check_notification_preview',
|
|
'no_cookie.letter_branding_preview_image',
|
|
'no_cookie.send_test_preview',
|
|
'no_cookie.view_letter_template_preview',
|
|
'no_cookie.view_template_version_preview',
|
|
'notifications_sent_by_service',
|
|
'old_guest_list',
|
|
'old_integration_testing',
|
|
'old_roadmap',
|
|
'old_service_dashboard',
|
|
'old_terms',
|
|
'old_using_notify',
|
|
'organisation_dashboard',
|
|
'organisation_preview_email_branding',
|
|
'organisation_preview_letter_branding',
|
|
'organisation_settings',
|
|
'organisation_trial_mode_services',
|
|
'organisations',
|
|
'performance',
|
|
'platform_admin',
|
|
'platform_admin_list_complaints',
|
|
'platform_admin_reports',
|
|
'platform_admin_returned_letters',
|
|
'platform_admin_splash_page',
|
|
'preview_broadcast_areas',
|
|
'preview_broadcast_message',
|
|
'pricing',
|
|
'privacy',
|
|
'public_agreement',
|
|
'public_download_agreement',
|
|
'received_text_messages_callback',
|
|
'redact_template',
|
|
'register',
|
|
'register_from_invite',
|
|
'register_from_org_invite',
|
|
'registration_continue',
|
|
'reject_broadcast_message',
|
|
'remove_broadcast_area',
|
|
'remove_user_from_organisation',
|
|
'remove_user_from_service',
|
|
'request_to_go_live',
|
|
'resend_email_link',
|
|
'resend_email_verification',
|
|
'resume_service',
|
|
'returned_letter_summary',
|
|
'returned_letters',
|
|
'returned_letters_report',
|
|
'revalidate_email_sent',
|
|
'revoke_api_key',
|
|
'roadmap',
|
|
'save_contact_list',
|
|
'security',
|
|
'send_files_by_email',
|
|
'send_files_by_email_contact_details',
|
|
'send_from_contact_list',
|
|
'send_messages',
|
|
'send_notification',
|
|
'send_one_off',
|
|
'send_one_off_letter_address',
|
|
'send_one_off_step',
|
|
'send_one_off_to_myself',
|
|
'send_uploaded_letter',
|
|
'service_accept_agreement',
|
|
'service_add_email_reply_to',
|
|
'service_add_letter_contact',
|
|
'service_add_sms_sender',
|
|
'service_agreement',
|
|
'service_confirm_agreement',
|
|
'service_confirm_delete_email_reply_to',
|
|
'service_confirm_delete_letter_contact',
|
|
'service_confirm_delete_sms_sender',
|
|
'service_dashboard',
|
|
'service_dashboard_updates',
|
|
'service_delete_email_reply_to',
|
|
'service_delete_letter_contact',
|
|
'service_delete_sms_sender',
|
|
'service_download_agreement',
|
|
'service_edit_email_reply_to',
|
|
'service_edit_letter_contact',
|
|
'service_edit_sms_sender',
|
|
'service_email_reply_to',
|
|
'service_letter_contact_details',
|
|
'service_make_blank_default_letter_contact',
|
|
'service_name_change',
|
|
'service_name_change_confirm',
|
|
'service_preview_email_branding',
|
|
'service_preview_letter_branding',
|
|
'service_set_auth_type',
|
|
'service_set_broadcast_account_type',
|
|
'service_set_channel',
|
|
'service_set_email_branding',
|
|
'service_set_inbound_number',
|
|
'service_set_inbound_sms',
|
|
'service_set_international_letters',
|
|
'service_set_international_sms',
|
|
'service_set_letter_branding',
|
|
'service_set_letters',
|
|
'service_set_permission',
|
|
'service_set_reply_to_email',
|
|
'service_set_sms_prefix',
|
|
'service_settings',
|
|
'service_sms_senders',
|
|
'service_switch_count_as_live',
|
|
'service_switch_live',
|
|
'service_verify_reply_to_address',
|
|
'service_verify_reply_to_address_updates',
|
|
'services_or_dashboard',
|
|
'set_free_sms_allowance',
|
|
'set_message_limit',
|
|
'set_rate_limit',
|
|
'set_sender',
|
|
'set_template_sender',
|
|
'show_accounts_or_dashboard',
|
|
'sign_in',
|
|
'sign_out',
|
|
'start_job',
|
|
'submit_request_to_go_live',
|
|
'support',
|
|
'support_public',
|
|
'suspend_service',
|
|
'template_history',
|
|
'template_usage',
|
|
'terms',
|
|
'thanks',
|
|
'tour_step',
|
|
'triage',
|
|
'trial_mode',
|
|
'trial_mode_new',
|
|
'trial_services',
|
|
'two_factor',
|
|
'two_factor_email',
|
|
'two_factor_email_interstitial',
|
|
'two_factor_email_sent',
|
|
'update_email_branding',
|
|
'update_letter_branding',
|
|
'upload_a_letter',
|
|
'upload_contact_list',
|
|
'upload_letter',
|
|
'uploaded_letter_preview',
|
|
'uploaded_letters',
|
|
'uploads',
|
|
'usage',
|
|
'user_information',
|
|
'user_profile',
|
|
'user_profile_disable_platform_admin_view',
|
|
'user_profile_email',
|
|
'user_profile_email_authenticate',
|
|
'user_profile_email_confirm',
|
|
'user_profile_mobile_number',
|
|
'user_profile_mobile_number_authenticate',
|
|
'user_profile_mobile_number_confirm',
|
|
'user_profile_name',
|
|
'user_profile_password',
|
|
'using_notify',
|
|
'verify',
|
|
'verify_email',
|
|
'view_current_broadcast',
|
|
'view_job',
|
|
'view_job_csv',
|
|
'view_job_updates',
|
|
'view_jobs',
|
|
'view_letter_notification_as_preview',
|
|
'view_letter_upload_as_preview',
|
|
'view_notification',
|
|
'view_notification_updates',
|
|
'view_notifications',
|
|
'view_notifications_csv',
|
|
'view_previous_broadcast',
|
|
'view_provider',
|
|
'view_providers',
|
|
'view_rejected_broadcast',
|
|
'view_template',
|
|
'view_template_version',
|
|
'view_template_versions',
|
|
'who_can_use_notify',
|
|
'who_its_for',
|
|
'write_new_broadcast',
|
|
}))
|
|
|
|
|
|
def flask_app():
|
|
app = Flask('app')
|
|
create_app(app)
|
|
|
|
ctx = app.app_context()
|
|
ctx.push()
|
|
|
|
yield app
|
|
|
|
|
|
all_endpoints = [
|
|
rule.endpoint for rule in next(flask_app()).url_map.iter_rules()
|
|
]
|
|
|
|
navigation_instances = (
|
|
MainNavigation(),
|
|
HeaderNavigation(),
|
|
OrgNavigation(),
|
|
CaseworkNavigation(),
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize('navigation_instance', navigation_instances)
|
|
def test_navigation_items_are_properly_defined(navigation_instance):
|
|
for endpoint in navigation_instance.endpoints_with_navigation:
|
|
assert (
|
|
endpoint in all_endpoints
|
|
), '{} is not a real endpoint (in {}.mapping)'.format(
|
|
endpoint,
|
|
type(navigation_instance).__name__
|
|
)
|
|
assert (
|
|
navigation_instance.endpoints_with_navigation.count(endpoint) == 1
|
|
), '{} found more than once in {}.mapping'.format(
|
|
endpoint,
|
|
type(navigation_instance).__name__
|
|
)
|
|
|
|
|
|
def test_excluded_navigation_items_are_properly_defined():
|
|
for endpoint in EXCLUDED_ENDPOINTS:
|
|
assert (
|
|
endpoint in all_endpoints
|
|
), f'{endpoint} is not a real endpoint (in EXCLUDED_ENDPOINTS)'
|
|
|
|
assert (
|
|
EXCLUDED_ENDPOINTS.count(endpoint) == 1
|
|
), f'{endpoint} found more than once in EXCLUDED_ENDPOINTS'
|
|
|
|
|
|
@pytest.mark.parametrize('navigation_instance', navigation_instances)
|
|
def test_all_endpoints_are_covered(navigation_instance):
|
|
covered_endpoints = (
|
|
navigation_instance.endpoints_with_navigation +
|
|
EXCLUDED_ENDPOINTS +
|
|
('static', 'status.show_status', 'metrics')
|
|
)
|
|
|
|
for endpoint in all_endpoints:
|
|
assert endpoint in covered_endpoints, f'{endpoint} is not listed or excluded'
|
|
|
|
|
|
@pytest.mark.parametrize('navigation_instance', navigation_instances)
|
|
@pytest.mark.xfail(raises=KeyError)
|
|
def test_raises_on_invalid_navigation_item(
|
|
client_request, navigation_instance
|
|
):
|
|
navigation_instance.is_selected('foo')
|
|
|
|
|
|
@pytest.mark.parametrize('endpoint, selected_nav_item', [
|
|
('main.choose_template', 'Templates'),
|
|
('main.manage_users', 'Team members'),
|
|
])
|
|
def test_a_page_should_nave_selected_navigation_item(
|
|
client_request,
|
|
mock_get_service_templates,
|
|
mock_get_users_by_service,
|
|
mock_get_invites_for_service,
|
|
mock_get_template_folders,
|
|
mock_get_api_keys,
|
|
endpoint,
|
|
selected_nav_item,
|
|
):
|
|
page = client_request.get(endpoint, service_id=SERVICE_ONE_ID)
|
|
selected_nav_items = page.select('.navigation a.selected')
|
|
assert len(selected_nav_items) == 1
|
|
assert selected_nav_items[0].text.strip() == selected_nav_item
|
|
|
|
|
|
@pytest.mark.parametrize('endpoint, selected_nav_item', [
|
|
('main.documentation', 'Documentation'),
|
|
('main.support', 'Support'),
|
|
])
|
|
def test_a_page_should_nave_selected_header_navigation_item(
|
|
client_request,
|
|
endpoint,
|
|
selected_nav_item,
|
|
):
|
|
page = client_request.get(endpoint, service_id=SERVICE_ONE_ID)
|
|
selected_nav_items = page.select('.govuk-header__navigation-item--active')
|
|
assert len(selected_nav_items) == 1
|
|
assert selected_nav_items[0].text.strip() == selected_nav_item
|
|
|
|
|
|
@pytest.mark.parametrize('endpoint, selected_nav_item', [
|
|
('main.organisation_dashboard', 'Usage'),
|
|
('main.manage_org_users', 'Team members'),
|
|
])
|
|
def test_a_page_should_nave_selected_org_navigation_item(
|
|
client_request,
|
|
mock_get_organisation,
|
|
mock_get_users_for_organisation,
|
|
mock_get_invited_users_for_organisation,
|
|
endpoint,
|
|
selected_nav_item,
|
|
mocker
|
|
):
|
|
mocker.patch(
|
|
'app.organisations_client.get_services_and_usage', return_value={'services': {}}
|
|
)
|
|
page = client_request.get(endpoint, org_id=ORGANISATION_ID)
|
|
selected_nav_items = page.select('.navigation a.selected')
|
|
assert len(selected_nav_items) == 1
|
|
assert selected_nav_items[0].text.strip() == selected_nav_item
|
|
|
|
|
|
def test_navigation_urls(
|
|
client_request,
|
|
mock_get_service_templates,
|
|
mock_get_template_folders,
|
|
mock_get_api_keys,
|
|
):
|
|
page = client_request.get('main.choose_template', service_id=SERVICE_ONE_ID)
|
|
assert [
|
|
a['href'] for a in page.select('.navigation a')
|
|
] == [
|
|
'/services/{}'.format(SERVICE_ONE_ID),
|
|
'/services/{}/templates'.format(SERVICE_ONE_ID),
|
|
'/services/{}/uploads'.format(SERVICE_ONE_ID),
|
|
'/services/{}/users'.format(SERVICE_ONE_ID),
|
|
'/services/{}/usage'.format(SERVICE_ONE_ID),
|
|
'/services/{}/service-settings'.format(SERVICE_ONE_ID),
|
|
'/services/{}/api'.format(SERVICE_ONE_ID),
|
|
]
|
|
|
|
|
|
def test_navigation_for_services_with_broadcast_permission(
|
|
client_request,
|
|
service_one,
|
|
mock_get_service_templates,
|
|
mock_get_template_folders,
|
|
mock_get_api_keys,
|
|
):
|
|
service_one['permissions'] += ['broadcast']
|
|
page = client_request.get('main.choose_template', service_id=SERVICE_ONE_ID)
|
|
assert [
|
|
a['href'] for a in page.select('.navigation a')
|
|
] == [
|
|
'/services/{}/current-alerts'.format(SERVICE_ONE_ID),
|
|
'/services/{}/previous-alerts'.format(SERVICE_ONE_ID),
|
|
'/services/{}/rejected-alerts'.format(SERVICE_ONE_ID),
|
|
'/services/{}/templates'.format(SERVICE_ONE_ID),
|
|
'/services/{}/users'.format(SERVICE_ONE_ID),
|
|
'/services/{}/service-settings'.format(SERVICE_ONE_ID),
|
|
]
|
|
|
|
|
|
def test_caseworkers_get_caseworking_navigation(
|
|
client_request,
|
|
mocker,
|
|
mock_get_template_folders,
|
|
mock_get_service_templates,
|
|
mock_has_no_jobs,
|
|
mock_get_api_keys,
|
|
active_caseworking_user,
|
|
):
|
|
mocker.patch(
|
|
'app.user_api_client.get_user',
|
|
return_value=active_caseworking_user
|
|
)
|
|
page = client_request.get('main.choose_template', service_id=SERVICE_ONE_ID)
|
|
assert normalize_spaces(page.select_one('header + .govuk-width-container nav').text) == (
|
|
'Templates Sent messages Uploads Team members'
|
|
)
|
|
|
|
|
|
def test_caseworkers_see_jobs_nav_if_jobs_exist(
|
|
client_request,
|
|
mocker,
|
|
mock_get_service_templates,
|
|
mock_get_template_folders,
|
|
mock_has_jobs,
|
|
active_caseworking_user,
|
|
mock_get_api_keys,
|
|
):
|
|
mocker.patch(
|
|
'app.user_api_client.get_user',
|
|
return_value=active_caseworking_user
|
|
)
|
|
page = client_request.get('main.choose_template', service_id=SERVICE_ONE_ID)
|
|
assert normalize_spaces(page.select_one('header + .govuk-width-container nav').text) == (
|
|
'Templates Sent messages Uploads Team members'
|
|
)
|