diff --git a/app/main/views/organisations.py b/app/main/views/organisations.py index e5de746f6..4c7fc391f 100644 --- a/app/main/views/organisations.py +++ b/app/main/views/organisations.py @@ -70,7 +70,10 @@ def add_organisation(): @login_required @user_has_permissions() def organisation_dashboard(org_id): - organisation_services = organisations_client.get_organisation_services(org_id) + organisation_services = [ + service for service in organisations_client.get_organisation_services(org_id) + if service['active'] and not service['restricted'] + ] for service in organisation_services: has_permission = current_user.has_permission_for_service(service['id'], 'view_activity') service.update({'has_permission_to_view': has_permission}) @@ -81,6 +84,19 @@ def organisation_dashboard(org_id): ) +@main.route("/organisations//trial-services", methods=['GET']) +@login_required +@user_is_platform_admin +def organisation_trial_mode_services(org_id): + organisation_services = organisations_client.get_organisation_services(org_id) + + return render_template( + 'views/organisations/organisation/trial-mode-services.html', + search_form=SearchByNameForm(), + services=[service for service in organisation_services if not service['active'] or service['restricted']] + ) + + @main.route("/organisations//users", methods=['GET']) @login_required @user_has_permissions() diff --git a/app/navigation.py b/app/navigation.py index b5f2d82fd..ce080ea04 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -207,6 +207,7 @@ class HeaderNavigation(Navigation): 'old_terms', 'old_using_notify', 'organisation_dashboard', + 'organisation_trial_mode_services', 'organisation_settings', 'organisation_preview_email_branding', 'organisation_preview_letter_branding', @@ -494,6 +495,7 @@ class MainNavigation(Navigation): 'old_terms', 'old_using_notify', 'organisation_dashboard', + 'organisation_trial_mode_services', 'organisation_preview_email_branding', 'organisation_preview_letter_branding', 'organisation_settings', @@ -712,6 +714,7 @@ class CaseworkNavigation(Navigation): 'old_terms', 'old_using_notify', 'organisation_dashboard', + 'organisation_trial_mode_services', 'organisation_preview_email_branding', 'organisation_preview_letter_branding', 'organisation_settings', @@ -870,6 +873,9 @@ class OrgNavigation(Navigation): 'invite_org_user', 'manage_org_users', 'remove_user_from_organisation', + }, + 'trial-services': { + 'organisation_trial_mode_services', } } diff --git a/app/templates/org_nav.html b/app/templates/org_nav.html index 4c44b4892..0c03bfe9c 100644 --- a/app/templates/org_nav.html +++ b/app/templates/org_nav.html @@ -3,5 +3,8 @@
  • Services
  • Team members
  • Settings
  • + {% if current_user.platform_admin %} +
  • Trial services
  • + {% endif %} diff --git a/app/templates/views/organisations/organisation/trial-mode-services.html b/app/templates/views/organisations/organisation/trial-mode-services.html new file mode 100644 index 000000000..74c734c23 --- /dev/null +++ b/app/templates/views/organisations/organisation/trial-mode-services.html @@ -0,0 +1,22 @@ +{% extends "org_template.html" %} +{% from "components/live-search.html" import live_search %} + +{% block org_page_title %} + Services +{% endblock %} + +{% block maincolumn_content %} + +

    + Trial mode services +

    + {{ live_search(target_selector='.browse-list-item', show=True, form=search_form, label='Search by name') }} + + +{% endblock %} diff --git a/tests/app/main/views/organisations/test_organisation.py b/tests/app/main/views/organisations/test_organisation.py new file mode 100644 index 000000000..261dfb8ff --- /dev/null +++ b/tests/app/main/views/organisations/test_organisation.py @@ -0,0 +1,690 @@ +from unittest.mock import Mock + +import pytest +from bs4 import BeautifulSoup +from flask import url_for +from notifications_python_client.errors import HTTPError + +from tests import organisation_json, service_json +from tests.conftest import ( + ORGANISATION_ID, + SERVICE_ONE_ID, + active_user_with_permissions, + normalize_spaces, + platform_admin_user, +) + + +def test_organisation_page_shows_all_organisations( + logged_in_platform_admin_client, + mocker +): + orgs = [ + {'id': '1', 'name': 'Test 1', 'active': True}, + {'id': '2', 'name': 'Test 2', 'active': True}, + {'id': '3', 'name': 'Test 3', 'active': False}, + ] + + mocker.patch( + 'app.organisations_client.get_organisations', return_value=orgs + ) + response = logged_in_platform_admin_client.get( + url_for('.organisations') + ) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert normalize_spaces( + page.select_one('h1').text + ) == "Organisations" + + for index, org in enumerate(orgs): + assert page.select('a.browse-list-link')[index].text == org['name'] + if not org['active']: + assert page.select_one('.table-field-status-default,heading-medium').text == '- archived' + assert normalize_spaces( + page.select_one('a.button-secondary').text + ) == 'New organisation' + + +def test_view_organisation_shows_the_correct_organisation( + client_request, + mocker +): + org = {'id': ORGANISATION_ID, 'name': 'Test 1', 'active': True} + mocker.patch( + 'app.organisations_client.get_organisation', return_value=org + ) + mocker.patch( + 'app.organisations_client.get_organisation_services', return_value=[] + ) + + page = client_request.get( + '.organisation_dashboard', + org_id=ORGANISATION_ID, + ) + + assert normalize_spaces(page.select_one('h1').text) == 'Services' + + +def test_create_new_organisation( + logged_in_platform_admin_client, + mocker, + fake_uuid +): + mock_create_organisation = mocker.patch( + 'app.organisations_client.create_organisation' + ) + + org = {'name': 'new name'} + + logged_in_platform_admin_client.post( + url_for('.add_organisation'), + content_type='multipart/form-data', + data=org + ) + + mock_create_organisation.assert_called_once_with(name=org['name']) + + +def test_organisation_services_shows_live_services_only( + client_request, + mock_get_organisation, + mocker, + active_user_with_permissions, + fake_uuid, +): + mocker.patch( + 'app.organisations_client.get_organisation_services', + return_value=[ + service_json(id_='1', name='1', restricted=False, active=True), # live + service_json(id_='2', name='2', restricted=True, active=True), # trial + service_json(id_='3', name='3', restricted=True, active=False), # trial, now archived + service_json(id_='4', name='4', restricted=False, active=False), # was live, now archived + service_json(id_=SERVICE_ONE_ID, name='5', restricted=False, active=True), # live, member of + ] + ) + + client_request.login(active_user_with_permissions) + page = client_request.get('.organisation_dashboard', org_id=ORGANISATION_ID) + + services = page.select('.browse-list-item') + assert len(services) == 2 + + assert normalize_spaces(services[0].text) == '1' + assert normalize_spaces(services[1].text) == '5' + assert services[0].find('a') is None + assert services[1].find('a')['href'] == url_for('main.service_dashboard', service_id=SERVICE_ONE_ID) + + +def test_organisation_trial_mode_services_shows_all_services( + client_request, + platform_admin_user, + mock_get_organisation, + mocker, + fake_uuid, +): + mocker.patch( + 'app.organisations_client.get_organisation_services', + return_value=[ + service_json(id_='1', name='1', restricted=False, active=True), # live + service_json(id_='2', name='2', restricted=True, active=True), # trial + service_json(id_='3', name='3', restricted=True, active=False), # archived + ] + ) + + client_request.login(platform_admin_user) + page = client_request.get( + '.organisation_trial_mode_services', + org_id=ORGANISATION_ID, + _test_page_title=False + ) + + services = page.select('.browse-list-item') + assert len(services) == 2 + + assert normalize_spaces(services[0].text) == '2' + assert normalize_spaces(services[1].text) == '3' + assert services[0].find('a')['href'] == url_for('main.service_dashboard', service_id='2') + assert services[1].find('a')['href'] == url_for('main.service_dashboard', service_id='3') + + +def test_organisation_trial_mode_services_doesnt_work_if_not_platform_admin( + client_request, + mock_get_organisation, +): + client_request.get( + '.organisation_trial_mode_services', + org_id=ORGANISATION_ID, + _expected_status=403 + ) + + +def test_organisation_settings( + client_request, + mock_get_organisation, + organisation_one +): + expected_rows = [ + 'Label Value Action', + 'Organisation name Org 1 Change', + ] + + page = client_request.get('.organisation_settings', org_id=organisation_one['id']) + + assert page.find('h1').text == 'Settings' + rows = page.select('tr') + assert len(rows) == len(expected_rows) + for index, row in enumerate(expected_rows): + assert row == " ".join(rows[index].text.split()) + mock_get_organisation.assert_called_with(organisation_one['id']) + + +def test_organisation_settings_for_platform_admin( + client_request, + platform_admin_user, + mock_get_organisation, + organisation_one +): + expected_rows = [ + 'Label Value Action', + 'Organisation name Org 1 Change', + + 'Label Value Action', + 'Organisation type Not set Change', + 'Crown organisation Yes Change', + 'Data sharing and financial agreement Not signed Change', + 'Request to go live notes None Change', + 'Default email branding GOV.UK Change', + 'Default letter branding No branding Change', + 'Known email domains None Change', + ] + + client_request.login(platform_admin_user) + page = client_request.get('.organisation_settings', org_id=organisation_one['id']) + + assert page.find('h1').text == 'Settings' + rows = page.select('tr') + assert len(rows) == len(expected_rows) + for index, row in enumerate(expected_rows): + assert row == " ".join(rows[index].text.split()) + mock_get_organisation.assert_called_with(organisation_one['id']) + + +@pytest.mark.parametrize('endpoint, expected_options, expected_selected', ( + ( + '.edit_organisation_type', + ( + ('central', 'Central government'), + ('local', 'Local government'), + ('nhs', 'NHS'), + ), + None, + ), + ( + '.edit_organisation_crown_status', + ( + ('crown', 'Yes'), + ('non-crown', 'No'), + ('unknown', 'Not sure'), + ), + 'crown', + ), + ( + '.edit_organisation_agreement', + ( + ('yes', ( + 'Yes ' + 'Users will be told their organisation has already signed the agreement' + )), + ('no', ( + 'No ' + 'Users will be prompted to sign the agreement before they can go live' + )), + ('unknown', ( + 'No (but we have some service-specific agreements in place) ' + 'Users won’t be prompted to sign the agreement' + )), + ), + 'no', + ), +)) +@pytest.mark.parametrize('user', ( + pytest.param( + platform_admin_user, + ), + pytest.param( + active_user_with_permissions, + marks=pytest.mark.xfail + ), +)) +def test_view_organisation_settings( + client_request, + fake_uuid, + organisation_one, + mock_get_organisation, + endpoint, + expected_options, + expected_selected, + user, +): + client_request.login(user(fake_uuid)) + + page = client_request.get(endpoint, org_id=organisation_one['id']) + + radios = page.select('input[type=radio]') + + for index, option in enumerate(expected_options): + label = page.select_one('label[for={}]'.format(radios[index]['id'])) + assert ( + radios[index]['value'], + normalize_spaces(label.text), + ) == option + + if expected_selected: + assert page.select_one('input[checked]')['value'] == expected_selected + else: + assert not page.select_one('input[checked]') + + +@pytest.mark.parametrize('endpoint, post_data, expected_persisted', ( + ( + '.edit_organisation_type', + {'organisation_type': 'central'}, + {'organisation_type': 'central'}, + ), + ( + '.edit_organisation_type', + {'organisation_type': 'local'}, + {'organisation_type': 'local'}, + ), + ( + '.edit_organisation_type', + {'organisation_type': 'nhs'}, + {'organisation_type': 'nhs'}, + ), + ( + '.edit_organisation_crown_status', + {'crown_status': 'crown'}, + {'crown': True}, + ), + ( + '.edit_organisation_crown_status', + {'crown_status': 'non-crown'}, + {'crown': False}, + ), + ( + '.edit_organisation_crown_status', + {'crown_status': 'unknown'}, + {'crown': None}, + ), + ( + '.edit_organisation_agreement', + {'agreement_signed': 'yes'}, + {'agreement_signed': True}, + ), + ( + '.edit_organisation_agreement', + {'agreement_signed': 'no'}, + {'agreement_signed': False}, + ), + ( + '.edit_organisation_agreement', + {'agreement_signed': 'unknown'}, + {'agreement_signed': None}, + ), +)) +@pytest.mark.parametrize('user', ( + pytest.param( + platform_admin_user, + ), + pytest.param( + active_user_with_permissions, + marks=pytest.mark.xfail + ), +)) +def test_update_organisation_settings( + client_request, + fake_uuid, + organisation_one, + mock_get_organisation, + mock_update_organisation, + endpoint, + post_data, + expected_persisted, + user, +): + client_request.login(user(fake_uuid)) + + client_request.post( + endpoint, + org_id=organisation_one['id'], + _data=post_data, + _expected_status=302, + _expected_redirect=url_for( + 'main.organisation_settings', + org_id=organisation_one['id'], + _external=True, + ), + ) + + mock_update_organisation.assert_called_once_with( + organisation_one['id'], + **expected_persisted, + ) + + +@pytest.mark.parametrize('user', ( + pytest.param( + platform_admin_user, + ), + pytest.param( + active_user_with_permissions, + marks=pytest.mark.xfail + ), +)) +def test_view_organisation_domains( + mocker, + client_request, + fake_uuid, + user, +): + client_request.login(user(fake_uuid)) + + mocker.patch( + 'app.organisations_client.get_organisation', + side_effect=lambda org_id: organisation_json( + org_id, + 'Org 1', + domains=['example.gov.uk', 'test.example.gov.uk'], + ) + ) + + page = client_request.get( + 'main.edit_organisation_domains', + org_id=ORGANISATION_ID, + ) + + assert [textbox['value'] for textbox in page.select('input[type=text]')] == [ + 'example.gov.uk', + 'test.example.gov.uk', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + ] + + +@pytest.mark.parametrize('post_data, expected_persisted', ( + ( + { + 'domains-0': 'example.gov.uk', + 'domains-2': 'example.gov.uk', + 'domains-3': 'EXAMPLE.GOV.UK', + 'domains-5': 'test.gov.uk', + }, + { + 'domains': [ + 'example.gov.uk', + 'test.gov.uk', + ] + } + ), + ( + { + 'domains-0': '', + 'domains-1': '', + 'domains-2': '', + }, + { + 'domains': [] + } + ), +)) +@pytest.mark.parametrize('user', ( + pytest.param( + platform_admin_user, + ), + pytest.param( + active_user_with_permissions, + marks=pytest.mark.xfail + ), +)) +def test_update_organisation_domains( + client_request, + fake_uuid, + organisation_one, + mock_get_organisation, + mock_update_organisation, + post_data, + expected_persisted, + user, +): + client_request.login(user(fake_uuid)) + + client_request.post( + 'main.edit_organisation_domains', + org_id=ORGANISATION_ID, + _data=post_data, + _expected_status=302, + _expected_redirect=url_for( + 'main.organisation_settings', + org_id=organisation_one['id'], + _external=True, + ), + ) + + mock_update_organisation.assert_called_once_with( + ORGANISATION_ID, + **expected_persisted, + ) + + +def test_update_organisation_name( + logged_in_platform_admin_client, + organisation_one, + mock_get_organisation, + mock_organisation_name_is_unique +): + response = logged_in_platform_admin_client.post( + url_for('.edit_organisation_name', org_id=organisation_one['id']), + data={'name': 'TestNewOrgName'} + ) + + assert response.status_code == 302 + assert response.location == url_for( + '.confirm_edit_organisation_name', + org_id=organisation_one['id'], + _external=True + ) + assert mock_organisation_name_is_unique.called + + +def test_update_organisation_with_incorrect_input( + logged_in_platform_admin_client, + organisation_one, + mock_get_organisation, +): + response = logged_in_platform_admin_client.post( + url_for('.edit_organisation_name', org_id=organisation_one['id']), + data={'name': ''} + ) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert normalize_spaces( + page.select_one('.error-message').text + ) == "Can’t be empty" + + +def test_update_organisation_with_non_unique_name( + logged_in_platform_admin_client, + organisation_one, + mock_get_organisation, + mock_organisation_name_is_not_unique +): + response = logged_in_platform_admin_client.post( + url_for('.edit_organisation_name', org_id=organisation_one['id']), + data={'name': 'TestNewOrgName'} + ) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert normalize_spaces( + page.select_one('.error-message').text + ) == 'This organisation name is already in use' + + assert mock_organisation_name_is_not_unique.called + + +def test_confirm_update_organisation( + logged_in_platform_admin_client, + organisation_one, + mock_get_organisation, + mock_verify_password, + mock_update_organisation, + mocker +): + with logged_in_platform_admin_client.session_transaction() as session: + session['organisation_name_change'] = 'newName' + + response = logged_in_platform_admin_client.post( + url_for( + '.confirm_edit_organisation_name', + org_id=organisation_one['id'], + data={'password', 'validPassword'} + ) + ) + + assert response.status_code == 302 + assert response.location == url_for('.organisation_settings', org_id=organisation_one['id'], _external=True) + + mock_update_organisation.assert_called_with( + organisation_one['id'], + name=session['organisation_name_change'] + ) + + +def test_confirm_update_organisation_with_incorrect_password( + logged_in_platform_admin_client, + organisation_one, + mock_get_organisation, + mocker +): + with logged_in_platform_admin_client.session_transaction() as session: + session['organisation_name_change'] = 'newName' + + mocker.patch('app.user_api_client.verify_password', return_value=False) + + response = logged_in_platform_admin_client.post( + url_for( + '.confirm_edit_organisation_name', + org_id=organisation_one['id'] + ) + ) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert normalize_spaces( + page.select_one('.error-message').text + ) == 'Invalid password' + + +def test_confirm_update_organisation_with_name_already_in_use( + logged_in_platform_admin_client, + organisation_one, + mock_get_organisation, + mock_verify_password, + mocker +): + with logged_in_platform_admin_client.session_transaction() as session: + session['organisation_name_change'] = 'newName' + + mocker.patch( + 'app.organisations_client.update_organisation_name', + side_effect=HTTPError( + response=Mock( + status_code=400, + json={'result': 'error', 'message': 'Organisation name already exists'} + ), + message="Organisation name already exists" + ) + ) + + response = logged_in_platform_admin_client.post( + url_for( + '.confirm_edit_organisation_name', + org_id=organisation_one['id'] + ) + ) + + assert response.status_code == 302 + assert response.location == url_for('main.edit_organisation_name', org_id=organisation_one['id'], _external=True) + + +def test_get_edit_organisation_go_live_notes_page( + logged_in_platform_admin_client, + mock_get_organisation, + organisation_one, +): + response = logged_in_platform_admin_client.get( + url_for( + '.edit_organisation_go_live_notes', + org_id=organisation_one['id'] + ) + ) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert page.find('textarea', id='request_to_go_live_notes') + + +@pytest.mark.parametrize('input_note,saved_note', [ + ('Needs permission', 'Needs permission'), + (' ', None) +]) +def test_post_edit_organisation_go_live_notes_updates_go_live_notes( + logged_in_platform_admin_client, + mock_get_organisation, + mock_update_organisation, + organisation_one, + input_note, + saved_note, +): + response = logged_in_platform_admin_client.post( + url_for( + '.edit_organisation_go_live_notes', + org_id=organisation_one['id'], + ), + data={'request_to_go_live_notes': input_note} + ) + + mock_update_organisation.assert_called_once_with( + organisation_one['id'], + request_to_go_live_notes=saved_note + ) + assert response.status_code == 302 + assert response.location == url_for( + '.organisation_settings', + org_id=organisation_one['id'], + _external=True + ) diff --git a/tests/app/main/views/organisations/test_organisation_invites.py b/tests/app/main/views/organisations/test_organisation_invites.py index fc113ab48..de744305d 100644 --- a/tests/app/main/views/organisations/test_organisation_invites.py +++ b/tests/app/main/views/organisations/test_organisation_invites.py @@ -1,119 +1,12 @@ from datetime import datetime, timedelta -from unittest.mock import ANY, Mock +from unittest.mock import ANY import pytest from bs4 import BeautifulSoup from flask import url_for -from notifications_python_client.errors import HTTPError from app.models.user import InvitedOrgUser -from tests import organisation_json -from tests.conftest import ( - ORGANISATION_ID, - active_user_with_permissions, - normalize_spaces, - platform_admin_user, -) - - -def test_organisation_page_shows_all_organisations( - logged_in_platform_admin_client, - mocker -): - orgs = [ - {'id': '1', 'name': 'Test 1', 'active': True}, - {'id': '2', 'name': 'Test 2', 'active': True}, - {'id': '3', 'name': 'Test 3', 'active': False}, - ] - - mocker.patch( - 'app.organisations_client.get_organisations', return_value=orgs - ) - response = logged_in_platform_admin_client.get( - url_for('.organisations') - ) - - assert response.status_code == 200 - page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') - - assert normalize_spaces( - page.select_one('h1').text - ) == "Organisations" - - for index, org in enumerate(orgs): - assert page.select('a.browse-list-link')[index].text == org['name'] - if not org['active']: - assert page.select_one('.table-field-status-default,heading-medium').text == '- archived' - assert normalize_spaces( - page.select_one('a.button-secondary').text - ) == 'New organisation' - - -def test_view_organisation_shows_the_correct_organisation( - client_request, - mocker -): - org = {'id': ORGANISATION_ID, 'name': 'Test 1', 'active': True} - mocker.patch( - 'app.organisations_client.get_organisation', return_value=org - ) - mocker.patch( - 'app.organisations_client.get_organisation_services', return_value=[] - ) - - page = client_request.get( - '.organisation_dashboard', - org_id=ORGANISATION_ID, - ) - - assert normalize_spaces(page.select_one('h1').text) == 'Services' - - -def test_create_new_organisation( - logged_in_platform_admin_client, - mocker, - fake_uuid -): - mock_create_organisation = mocker.patch( - 'app.organisations_client.create_organisation' - ) - - org = {'name': 'new name'} - - logged_in_platform_admin_client.post( - url_for('.add_organisation'), - content_type='multipart/form-data', - data=org - ) - - mock_create_organisation.assert_called_once_with(name=org['name']) - - -def test_organisation_services_show( - client_request, - mock_get_organisation, - mock_get_organisation_services, - mocker, - fake_uuid, -): - page = client_request.get( - '.organisation_dashboard', - org_id=ORGANISATION_ID, - ) - - assert len(page.select('.browse-list-item')) == 3 - - for i in range(0, 2): - service_name = mock_get_organisation_services(mock_get_organisation['id'])[i]['name'] - service_id = mock_get_organisation_services(mock_get_organisation['id'])[i]['id'] - - assert normalize_spaces(page.select('.browse-list-item')[i].text) == service_name - if i > 1: - assert normalize_spaces( - page.select('.browse-list-item a')[i]['href'] - ) == '/services/{}'.format(service_id) - else: - assert page.select('.browse-list-item')[i].find('a') is None +from tests.conftest import ORGANISATION_ID, normalize_spaces def test_view_team_members( @@ -467,532 +360,3 @@ def test_verified_org_user_redirects_to_dashboard( org_id=invited_org_user['organisation'], _external=True ) - - -def test_organisation_settings( - client_request, - mock_get_organisation, - organisation_one -): - expected_rows = [ - 'Label Value Action', - 'Organisation name Org 1 Change', - ] - - page = client_request.get('.organisation_settings', org_id=organisation_one['id']) - - assert page.find('h1').text == 'Settings' - rows = page.select('tr') - assert len(rows) == len(expected_rows) - for index, row in enumerate(expected_rows): - assert row == " ".join(rows[index].text.split()) - mock_get_organisation.assert_called_with(organisation_one['id']) - - -def test_organisation_settings_for_platform_admin( - client_request, - platform_admin_user, - mock_get_organisation, - organisation_one -): - expected_rows = [ - 'Label Value Action', - 'Organisation name Org 1 Change', - - 'Label Value Action', - 'Organisation type Not set Change', - 'Crown organisation Yes Change', - 'Data sharing and financial agreement Not signed Change', - 'Request to go live notes None Change', - 'Default email branding GOV.UK Change', - 'Default letter branding No branding Change', - 'Known email domains None Change', - ] - - client_request.login(platform_admin_user) - page = client_request.get('.organisation_settings', org_id=organisation_one['id']) - - assert page.find('h1').text == 'Settings' - rows = page.select('tr') - assert len(rows) == len(expected_rows) - for index, row in enumerate(expected_rows): - assert row == " ".join(rows[index].text.split()) - mock_get_organisation.assert_called_with(organisation_one['id']) - - -@pytest.mark.parametrize('endpoint, expected_options, expected_selected', ( - ( - '.edit_organisation_type', - ( - ('central', 'Central government'), - ('local', 'Local government'), - ('nhs', 'NHS'), - ), - None, - ), - ( - '.edit_organisation_crown_status', - ( - ('crown', 'Yes'), - ('non-crown', 'No'), - ('unknown', 'Not sure'), - ), - 'crown', - ), - ( - '.edit_organisation_agreement', - ( - ('yes', ( - 'Yes ' - 'Users will be told their organisation has already signed the agreement' - )), - ('no', ( - 'No ' - 'Users will be prompted to sign the agreement before they can go live' - )), - ('unknown', ( - 'No (but we have some service-specific agreements in place) ' - 'Users won’t be prompted to sign the agreement' - )), - ), - 'no', - ), -)) -@pytest.mark.parametrize('user', ( - pytest.param( - platform_admin_user, - ), - pytest.param( - active_user_with_permissions, - marks=pytest.mark.xfail - ), -)) -def test_view_organisation_settings( - client_request, - fake_uuid, - organisation_one, - mock_get_organisation, - endpoint, - expected_options, - expected_selected, - user, -): - client_request.login(user(fake_uuid)) - - page = client_request.get(endpoint, org_id=organisation_one['id']) - - radios = page.select('input[type=radio]') - - for index, option in enumerate(expected_options): - label = page.select_one('label[for={}]'.format(radios[index]['id'])) - assert ( - radios[index]['value'], - normalize_spaces(label.text), - ) == option - - if expected_selected: - assert page.select_one('input[checked]')['value'] == expected_selected - else: - assert not page.select_one('input[checked]') - - -@pytest.mark.parametrize('endpoint, post_data, expected_persisted', ( - ( - '.edit_organisation_type', - {'organisation_type': 'central'}, - {'organisation_type': 'central'}, - ), - ( - '.edit_organisation_type', - {'organisation_type': 'local'}, - {'organisation_type': 'local'}, - ), - ( - '.edit_organisation_type', - {'organisation_type': 'nhs'}, - {'organisation_type': 'nhs'}, - ), - ( - '.edit_organisation_crown_status', - {'crown_status': 'crown'}, - {'crown': True}, - ), - ( - '.edit_organisation_crown_status', - {'crown_status': 'non-crown'}, - {'crown': False}, - ), - ( - '.edit_organisation_crown_status', - {'crown_status': 'unknown'}, - {'crown': None}, - ), - ( - '.edit_organisation_agreement', - {'agreement_signed': 'yes'}, - {'agreement_signed': True}, - ), - ( - '.edit_organisation_agreement', - {'agreement_signed': 'no'}, - {'agreement_signed': False}, - ), - ( - '.edit_organisation_agreement', - {'agreement_signed': 'unknown'}, - {'agreement_signed': None}, - ), -)) -@pytest.mark.parametrize('user', ( - pytest.param( - platform_admin_user, - ), - pytest.param( - active_user_with_permissions, - marks=pytest.mark.xfail - ), -)) -def test_update_organisation_settings( - client_request, - fake_uuid, - organisation_one, - mock_get_organisation, - mock_update_organisation, - endpoint, - post_data, - expected_persisted, - user, -): - client_request.login(user(fake_uuid)) - - client_request.post( - endpoint, - org_id=organisation_one['id'], - _data=post_data, - _expected_status=302, - _expected_redirect=url_for( - 'main.organisation_settings', - org_id=organisation_one['id'], - _external=True, - ), - ) - - mock_update_organisation.assert_called_once_with( - organisation_one['id'], - **expected_persisted, - ) - - -@pytest.mark.parametrize('user', ( - pytest.param( - platform_admin_user, - ), - pytest.param( - active_user_with_permissions, - marks=pytest.mark.xfail - ), -)) -def test_view_organisation_domains( - mocker, - client_request, - fake_uuid, - user, -): - client_request.login(user(fake_uuid)) - - mocker.patch( - 'app.organisations_client.get_organisation', - side_effect=lambda org_id: organisation_json( - org_id, - 'Org 1', - domains=['example.gov.uk', 'test.example.gov.uk'], - ) - ) - - page = client_request.get( - 'main.edit_organisation_domains', - org_id=ORGANISATION_ID, - ) - - assert [textbox['value'] for textbox in page.select('input[type=text]')] == [ - 'example.gov.uk', - 'test.example.gov.uk', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - ] - - -@pytest.mark.parametrize('post_data, expected_persisted', ( - ( - { - 'domains-0': 'example.gov.uk', - 'domains-2': 'example.gov.uk', - 'domains-3': 'EXAMPLE.GOV.UK', - 'domains-5': 'test.gov.uk', - }, - { - 'domains': [ - 'example.gov.uk', - 'test.gov.uk', - ] - } - ), - ( - { - 'domains-0': '', - 'domains-1': '', - 'domains-2': '', - }, - { - 'domains': [] - } - ), -)) -@pytest.mark.parametrize('user', ( - pytest.param( - platform_admin_user, - ), - pytest.param( - active_user_with_permissions, - marks=pytest.mark.xfail - ), -)) -def test_update_organisation_domains( - client_request, - fake_uuid, - organisation_one, - mock_get_organisation, - mock_update_organisation, - post_data, - expected_persisted, - user, -): - client_request.login(user(fake_uuid)) - - client_request.post( - 'main.edit_organisation_domains', - org_id=ORGANISATION_ID, - _data=post_data, - _expected_status=302, - _expected_redirect=url_for( - 'main.organisation_settings', - org_id=organisation_one['id'], - _external=True, - ), - ) - - mock_update_organisation.assert_called_once_with( - ORGANISATION_ID, - **expected_persisted, - ) - - -def test_update_organisation_name( - logged_in_platform_admin_client, - organisation_one, - mock_get_organisation, - mock_organisation_name_is_unique -): - response = logged_in_platform_admin_client.post( - url_for('.edit_organisation_name', org_id=organisation_one['id']), - data={'name': 'TestNewOrgName'} - ) - - assert response.status_code == 302 - assert response.location == url_for( - '.confirm_edit_organisation_name', - org_id=organisation_one['id'], - _external=True - ) - assert mock_organisation_name_is_unique.called - - -def test_update_organisation_with_incorrect_input( - logged_in_platform_admin_client, - organisation_one, - mock_get_organisation, -): - response = logged_in_platform_admin_client.post( - url_for('.edit_organisation_name', org_id=organisation_one['id']), - data={'name': ''} - ) - - assert response.status_code == 200 - page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') - - assert normalize_spaces( - page.select_one('.error-message').text - ) == "Can’t be empty" - - -def test_update_organisation_with_non_unique_name( - logged_in_platform_admin_client, - organisation_one, - mock_get_organisation, - mock_organisation_name_is_not_unique -): - response = logged_in_platform_admin_client.post( - url_for('.edit_organisation_name', org_id=organisation_one['id']), - data={'name': 'TestNewOrgName'} - ) - - assert response.status_code == 200 - page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') - - assert normalize_spaces( - page.select_one('.error-message').text - ) == 'This organisation name is already in use' - - assert mock_organisation_name_is_not_unique.called - - -def test_confirm_update_organisation( - logged_in_platform_admin_client, - organisation_one, - mock_get_organisation, - mock_verify_password, - mock_update_organisation, - mocker -): - with logged_in_platform_admin_client.session_transaction() as session: - session['organisation_name_change'] = 'newName' - - response = logged_in_platform_admin_client.post( - url_for( - '.confirm_edit_organisation_name', - org_id=organisation_one['id'], - data={'password', 'validPassword'} - ) - ) - - assert response.status_code == 302 - assert response.location == url_for('.organisation_settings', org_id=organisation_one['id'], _external=True) - - mock_update_organisation.assert_called_with( - organisation_one['id'], - name=session['organisation_name_change'] - ) - - -def test_confirm_update_organisation_with_incorrect_password( - logged_in_platform_admin_client, - organisation_one, - mock_get_organisation, - mocker -): - with logged_in_platform_admin_client.session_transaction() as session: - session['organisation_name_change'] = 'newName' - - mocker.patch('app.user_api_client.verify_password', return_value=False) - - response = logged_in_platform_admin_client.post( - url_for( - '.confirm_edit_organisation_name', - org_id=organisation_one['id'] - ) - ) - - assert response.status_code == 200 - page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') - - assert normalize_spaces( - page.select_one('.error-message').text - ) == 'Invalid password' - - -def test_confirm_update_organisation_with_name_already_in_use( - logged_in_platform_admin_client, - organisation_one, - mock_get_organisation, - mock_verify_password, - mocker -): - with logged_in_platform_admin_client.session_transaction() as session: - session['organisation_name_change'] = 'newName' - - mocker.patch( - 'app.organisations_client.update_organisation_name', - side_effect=HTTPError( - response=Mock( - status_code=400, - json={'result': 'error', 'message': 'Organisation name already exists'} - ), - message="Organisation name already exists" - ) - ) - - response = logged_in_platform_admin_client.post( - url_for( - '.confirm_edit_organisation_name', - org_id=organisation_one['id'] - ) - ) - - assert response.status_code == 302 - assert response.location == url_for('main.edit_organisation_name', org_id=organisation_one['id'], _external=True) - - -def test_get_edit_organisation_go_live_notes_page( - logged_in_platform_admin_client, - mock_get_organisation, - organisation_one, -): - response = logged_in_platform_admin_client.get( - url_for( - '.edit_organisation_go_live_notes', - org_id=organisation_one['id'] - ) - ) - - assert response.status_code == 200 - page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') - - assert page.find('textarea', id='request_to_go_live_notes') - - -@pytest.mark.parametrize('input_note,saved_note', [ - ('Needs permission', 'Needs permission'), - (' ', None) -]) -def test_post_edit_organisation_go_live_notes_updates_go_live_notes( - logged_in_platform_admin_client, - mock_get_organisation, - mock_update_organisation, - organisation_one, - input_note, - saved_note, -): - response = logged_in_platform_admin_client.post( - url_for( - '.edit_organisation_go_live_notes', - org_id=organisation_one['id'], - ), - data={'request_to_go_live_notes': input_note} - ) - - mock_update_organisation.assert_called_once_with( - organisation_one['id'], - request_to_go_live_notes=saved_note - ) - assert response.status_code == 302 - assert response.location == url_for( - '.organisation_settings', - org_id=organisation_one['id'], - _external=True - )