Files
notifications-admin/tests/app/main/views/organizations/test_organizations.py

1525 lines
43 KiB
Python
Raw Normal View History

import pytest
from flask import url_for
from freezegun import freeze_time
2025-06-10 11:40:14 -07:00
from notifications_python_client.errors import HTTPError
2023-07-12 12:09:44 -04:00
from tests import organization_json, service_json
from tests.conftest import (
ORGANISATION_ID,
SERVICE_ONE_ID,
SERVICE_TWO_ID,
create_active_user_with_permissions,
create_platform_admin_user,
normalize_spaces,
)
2023-07-12 12:09:44 -04:00
def test_organization_page_shows_all_organizations(
client_request, platform_admin_user, mocker
):
orgs = [
{"id": "A3", "name": "Test 3", "active": True, "count_of_live_services": 0},
{"id": "B1", "name": "Test 1", "active": True, "count_of_live_services": 1},
{"id": "C2", "name": "Test 2", "active": False, "count_of_live_services": 2},
]
2023-07-12 12:09:44 -04:00
get_organizations = mocker.patch(
"app.models.organization.AllOrganizations.client_method", return_value=orgs
)
client_request.login(platform_admin_user)
page = client_request.get(".organizations")
assert normalize_spaces(page.select_one("h1").text) == "Organizations"
Removed all govuk css (#2814) * Removed all govuk css * Updated reference files * Removing govuk js * Fixed casing for modules, removed unused page * Got more reference images * Updated template page * Removed govuk padding util * Updated hint to uswds hint * More govuk cleanup * Commiting backstopjs ref files * Fixed all unit tests that broke due to brittleness around govuk styling * Added new ref images * Final removal of govuk * Officially removed all govuk references * Updated reference file * Updated link to button * UI modernization * Cleanup * removed govuk escaping tests since they are no longer needed * Fix CodeQL security issue in escapeElementName function - Escape backslashes first before other special characters - Prevents potential double-escaping vulnerability - Addresses CodeQL alert about improper string escaping * Found more govuk removal. Fixed unit tests * Add missing pipeline check to pre-commit * updated test * Updated e2e test * More update to e2e test * Fixed another e2e test * Simple PR comments addressed * More updates * Updated backstop ref files * Refactored folder selection for non-admins * Updated redundant line * Updated tests to include correct mocks * Added more ref files * Addressing carlos comments * Addressing Carlo comments, cleanup of window initing * More cleanup and addressing carlo comments * Fixing a11 scan * Fixed a few issues with javascript * Fixed for pr * Fixing e2e tests * Tweaking e2e test * Added more ref files and cleaned up urls.js * Fixed bug with creating new template * Removed brittle test - addressed code ql comment * e2e race condition fix * More e2e test fixes * Updated e2e tests to not wait for text sent * Updated test to not wait for button click response * Made tear down more resilent if staging is down * reverted e2e test to what was working before main merge * Updated backstopRef images * Updated gulp to include job-polling differently
2025-10-06 09:38:54 -04:00
assert page.select_one("a.usa-button")
2021-11-11 14:35:55 +00:00
2023-07-12 12:09:44 -04:00
get_organizations.assert_called_once_with()
def test_view_organization_shows_the_correct_organization(client_request, mocker):
org = {"id": ORGANISATION_ID, "name": "Test 1", "active": True}
mocker.patch("app.organizations_client.get_organization", return_value=org)
mocker.patch(
"app.organizations_client.get_services_and_usage", return_value={"services": {}}
)
page = client_request.get(
2025-10-14 13:20:05 -07:00
".organization_usage",
org_id=ORGANISATION_ID,
)
assert normalize_spaces(page.select_one("h1").text) == "Usage"
2023-08-28 12:12:21 -04:00
assert normalize_spaces(page.select_one(".usa-hint").text) == (
"Test 1 has no live services on Notify.gov"
)
assert not page.select("a[download]")
2023-07-12 12:09:44 -04:00
def test_page_to_create_new_organization(
client_request,
platform_admin_user,
mocker,
):
client_request.login(platform_admin_user)
page = client_request.get(".add_organization")
assert [
(input["type"], input["name"], input.get("value"))
for input in page.select("input")
] == [
("text", "name", None),
("radio", "organization_type", "federal"),
("radio", "organization_type", "state"),
2023-07-12 12:09:44 -04:00
# ('radio', 'organization_type', 'nhs_central'),
# ('radio', 'organization_type', 'nhs_local'),
# ('radio', 'organization_type', 'nhs_gp'),
# ('radio', 'organization_type', 'emergency_service'),
# ('radio', 'organization_type', 'school_or_college'),
("radio", "organization_type", "other"),
("hidden", "csrf_token", mocker.ANY),
]
2023-07-12 12:09:44 -04:00
def test_create_new_organization(
client_request,
platform_admin_user,
mocker,
):
2023-07-12 12:09:44 -04:00
mock_create_organization = mocker.patch(
"app.organizations_client.create_organization",
2023-07-12 12:09:44 -04:00
return_value=organization_json(ORGANISATION_ID),
)
client_request.login(platform_admin_user)
client_request.post(
".add_organization",
_data={
"name": "new name",
"organization_type": "federal",
},
_expected_redirect=url_for(
"main.organization_settings",
org_id=ORGANISATION_ID,
),
)
2023-07-12 12:09:44 -04:00
mock_create_organization.assert_called_once_with(
name="new name",
organization_type="federal",
)
2023-07-12 12:09:44 -04:00
def test_create_new_organization_validates(
client_request,
platform_admin_user,
mocker,
):
2023-07-12 12:09:44 -04:00
mock_create_organization = mocker.patch(
"app.organizations_client.create_organization"
)
client_request.login(platform_admin_user)
page = client_request.post(
".add_organization",
_expected_status=200,
)
assert [
(error["data-error-label"], normalize_spaces(error.text))
for error in page.select(".usa-error-message")
] == [
("name", "Error: Cannot be empty"),
("organization_type", "Error: Select the type of organization"),
]
2023-07-12 12:09:44 -04:00
assert mock_create_organization.called is False
@pytest.mark.parametrize(
("name", "error_message"),
[
("", "Cannot be empty"),
("a", "at least two alphanumeric characters"),
("a" * 256, "Organization name must be 255 characters or fewer"),
],
)
2023-07-12 12:09:44 -04:00
def test_create_new_organization_fails_with_incorrect_input(
2019-11-08 17:12:32 +00:00
client_request,
platform_admin_user,
mocker,
name,
error_message,
2019-11-08 17:12:32 +00:00
):
2023-07-12 12:09:44 -04:00
mock_create_organization = mocker.patch(
"app.organizations_client.create_organization"
2019-11-08 17:12:32 +00:00
)
client_request.login(platform_admin_user)
page = client_request.post(
".add_organization",
2019-11-08 17:12:32 +00:00
_data={
"name": name,
"organization_type": "local",
2019-11-08 17:12:32 +00:00
},
_expected_status=200,
)
2023-07-12 12:09:44 -04:00
assert mock_create_organization.called is False
assert error_message in page.select_one(".usa-error-message").text
2019-11-08 17:12:32 +00:00
2023-07-12 12:09:44 -04:00
def test_create_new_organization_fails_with_duplicate_name(
Handle exception is org name already exists Previously this would return a 500 error, as the 400 exception was not handled from the API [1]. Note that: - We tend to rely on exception messages to identify the error that occurred [2][3], with services being a notable deviation [4]. I've used the exception message approach, as this is more granular and broadly consistent with the rest of the app. - There is already code to cover this scenario when a user changes the name of an existing organisation or service, but the mechanism is different [5][6]. It makes sense to just get any error from the call to try and create the organisation. - The API mock is based on one for services [7], but I've chosen to have it inline with the test, since we're unlikely to reuse it, and it's clearer to have the test setup as part of the test. [1]: https://github.com/alphagov/notifications-api/blob/8f99da525dad3bf653a4e1f9e4a7b7b689219d78/app/organisation/rest.py#L34-L47 [2]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/manage_users.py#L166 [3]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/templates.py#L499 [4]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/add_service.py#L30 [5]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/service_settings.py#L102-L104 [6]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/organisations.py#L264-L266 [7]: https://github.com/alphagov/notifications-admin/blob/0abc143147c98ac50c059729eb5c1a4755d607fa/tests/conftest.py#L590-L606
2021-02-11 12:02:42 +00:00
client_request,
platform_admin_user,
mocker,
):
def _create(**_kwargs):
json_mock = mocker.Mock(
return_value={"message": "Organization name already exists"}
)
resp_mock = mocker.Mock(status_code=400, json=json_mock)
Handle exception is org name already exists Previously this would return a 500 error, as the 400 exception was not handled from the API [1]. Note that: - We tend to rely on exception messages to identify the error that occurred [2][3], with services being a notable deviation [4]. I've used the exception message approach, as this is more granular and broadly consistent with the rest of the app. - There is already code to cover this scenario when a user changes the name of an existing organisation or service, but the mechanism is different [5][6]. It makes sense to just get any error from the call to try and create the organisation. - The API mock is based on one for services [7], but I've chosen to have it inline with the test, since we're unlikely to reuse it, and it's clearer to have the test setup as part of the test. [1]: https://github.com/alphagov/notifications-api/blob/8f99da525dad3bf653a4e1f9e4a7b7b689219d78/app/organisation/rest.py#L34-L47 [2]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/manage_users.py#L166 [3]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/templates.py#L499 [4]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/add_service.py#L30 [5]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/service_settings.py#L102-L104 [6]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/organisations.py#L264-L266 [7]: https://github.com/alphagov/notifications-admin/blob/0abc143147c98ac50c059729eb5c1a4755d607fa/tests/conftest.py#L590-L606
2021-02-11 12:02:42 +00:00
http_error = HTTPError(response=resp_mock, message="Default message")
raise http_error
mocker.patch("app.organizations_client.create_organization", side_effect=_create)
Handle exception is org name already exists Previously this would return a 500 error, as the 400 exception was not handled from the API [1]. Note that: - We tend to rely on exception messages to identify the error that occurred [2][3], with services being a notable deviation [4]. I've used the exception message approach, as this is more granular and broadly consistent with the rest of the app. - There is already code to cover this scenario when a user changes the name of an existing organisation or service, but the mechanism is different [5][6]. It makes sense to just get any error from the call to try and create the organisation. - The API mock is based on one for services [7], but I've chosen to have it inline with the test, since we're unlikely to reuse it, and it's clearer to have the test setup as part of the test. [1]: https://github.com/alphagov/notifications-api/blob/8f99da525dad3bf653a4e1f9e4a7b7b689219d78/app/organisation/rest.py#L34-L47 [2]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/manage_users.py#L166 [3]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/templates.py#L499 [4]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/add_service.py#L30 [5]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/service_settings.py#L102-L104 [6]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/organisations.py#L264-L266 [7]: https://github.com/alphagov/notifications-admin/blob/0abc143147c98ac50c059729eb5c1a4755d607fa/tests/conftest.py#L590-L606
2021-02-11 12:02:42 +00:00
client_request.login(platform_admin_user)
page = client_request.post(
".add_organization",
Handle exception is org name already exists Previously this would return a 500 error, as the 400 exception was not handled from the API [1]. Note that: - We tend to rely on exception messages to identify the error that occurred [2][3], with services being a notable deviation [4]. I've used the exception message approach, as this is more granular and broadly consistent with the rest of the app. - There is already code to cover this scenario when a user changes the name of an existing organisation or service, but the mechanism is different [5][6]. It makes sense to just get any error from the call to try and create the organisation. - The API mock is based on one for services [7], but I've chosen to have it inline with the test, since we're unlikely to reuse it, and it's clearer to have the test setup as part of the test. [1]: https://github.com/alphagov/notifications-api/blob/8f99da525dad3bf653a4e1f9e4a7b7b689219d78/app/organisation/rest.py#L34-L47 [2]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/manage_users.py#L166 [3]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/templates.py#L499 [4]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/add_service.py#L30 [5]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/service_settings.py#L102-L104 [6]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/organisations.py#L264-L266 [7]: https://github.com/alphagov/notifications-admin/blob/0abc143147c98ac50c059729eb5c1a4755d607fa/tests/conftest.py#L590-L606
2021-02-11 12:02:42 +00:00
_data={
"name": "Existing org",
"organization_type": "federal",
Handle exception is org name already exists Previously this would return a 500 error, as the 400 exception was not handled from the API [1]. Note that: - We tend to rely on exception messages to identify the error that occurred [2][3], with services being a notable deviation [4]. I've used the exception message approach, as this is more granular and broadly consistent with the rest of the app. - There is already code to cover this scenario when a user changes the name of an existing organisation or service, but the mechanism is different [5][6]. It makes sense to just get any error from the call to try and create the organisation. - The API mock is based on one for services [7], but I've chosen to have it inline with the test, since we're unlikely to reuse it, and it's clearer to have the test setup as part of the test. [1]: https://github.com/alphagov/notifications-api/blob/8f99da525dad3bf653a4e1f9e4a7b7b689219d78/app/organisation/rest.py#L34-L47 [2]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/manage_users.py#L166 [3]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/templates.py#L499 [4]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/add_service.py#L30 [5]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/service_settings.py#L102-L104 [6]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/organisations.py#L264-L266 [7]: https://github.com/alphagov/notifications-admin/blob/0abc143147c98ac50c059729eb5c1a4755d607fa/tests/conftest.py#L590-L606
2021-02-11 12:02:42 +00:00
},
_expected_status=200,
)
error_message = "This organization name is already in use"
assert error_message in page.select_one(".usa-error-message").text
Handle exception is org name already exists Previously this would return a 500 error, as the 400 exception was not handled from the API [1]. Note that: - We tend to rely on exception messages to identify the error that occurred [2][3], with services being a notable deviation [4]. I've used the exception message approach, as this is more granular and broadly consistent with the rest of the app. - There is already code to cover this scenario when a user changes the name of an existing organisation or service, but the mechanism is different [5][6]. It makes sense to just get any error from the call to try and create the organisation. - The API mock is based on one for services [7], but I've chosen to have it inline with the test, since we're unlikely to reuse it, and it's clearer to have the test setup as part of the test. [1]: https://github.com/alphagov/notifications-api/blob/8f99da525dad3bf653a4e1f9e4a7b7b689219d78/app/organisation/rest.py#L34-L47 [2]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/manage_users.py#L166 [3]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/templates.py#L499 [4]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/add_service.py#L30 [5]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/service_settings.py#L102-L104 [6]: https://github.com/alphagov/notifications-admin/blob/70b606a2d4cb03f2bd9f3a5742a40970a928e696/app/main/views/organisations.py#L264-L266 [7]: https://github.com/alphagov/notifications-admin/blob/0abc143147c98ac50c059729eb5c1a4755d607fa/tests/conftest.py#L590-L606
2021-02-11 12:02:42 +00:00
@pytest.mark.parametrize(
("organization_type", "organization", "expected_status"),
[
("nhs_gp", None, 200),
("central", None, 403),
("nhs_gp", organization_json(organization_type="nhs_gp"), 403),
],
)
@pytest.mark.skip(reason="Update for TTS")
2023-07-12 12:09:44 -04:00
def test_gps_can_create_own_organizations(
client_request,
mocker,
2023-07-12 12:09:44 -04:00
mock_get_service_organization,
service_one,
2023-07-12 12:09:44 -04:00
organization_type,
organization,
expected_status,
):
mocker.patch("app.organizations_client.get_organization", return_value=organization)
service_one["organization_type"] = organization_type
page = client_request.get(
".add_organization_from_gp_service",
service_id=SERVICE_ONE_ID,
_expected_status=expected_status,
)
if expected_status == 403:
return
assert page.select_one("input[type=text]")["name"] == "name"
assert normalize_spaces(page.select_one("label[for=name]").text) == (
"Whats your practice called?"
)
@pytest.mark.parametrize(
("data", "expected_service_name"),
[
(
{
"same_as_service_name": False,
"name": "Dr. Example",
},
"Dr. Example",
),
(
{
"same_as_service_name": True,
"name": "This is ignored",
},
"service one",
),
],
)
@pytest.mark.skip(reason="Update for TTS")
2023-07-12 12:09:44 -04:00
def test_gps_can_name_their_organization(
client_request,
mocker,
service_one,
2023-07-12 12:09:44 -04:00
mock_update_service_organization,
data,
expected_service_name,
):
service_one["organization_type"] = "nhs_gp"
2023-07-12 12:09:44 -04:00
mock_create_organization = mocker.patch(
"app.organizations_client.create_organization",
2023-07-12 12:09:44 -04:00
return_value=organization_json(ORGANISATION_ID),
)
client_request.post(
".add_organization_from_gp_service",
service_id=SERVICE_ONE_ID,
_data=data,
_expected_status=302,
)
2023-07-12 12:09:44 -04:00
mock_create_organization.assert_called_once_with(
name=expected_service_name,
organization_type="nhs_gp",
)
mock_update_service_organization.assert_called_once_with(
SERVICE_ONE_ID, ORGANISATION_ID
)
@pytest.mark.parametrize(
("data", "expected_error"),
[
(
{
"name": "Dr. Example",
},
"Select yes or no",
),
(
{
"same_as_service_name": False,
"name": "",
},
"Cannot be empty",
),
],
)
@pytest.mark.skip(reason="Update for TTS")
2023-07-12 12:09:44 -04:00
def test_validation_of_gps_creating_organizations(
client_request,
mocker,
service_one,
data,
expected_error,
):
service_one["organization_type"] = "nhs_gp"
page = client_request.post(
".add_organization_from_gp_service",
service_id=SERVICE_ONE_ID,
_data=data,
_expected_status=200,
)
assert expected_error in page.select_one(".usa-error-message, .error-message").text
@freeze_time("2020-02-20 20:20")
2023-07-12 12:09:44 -04:00
def test_organization_services_shows_live_services_and_usage(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mocker,
active_user_with_permissions,
fake_uuid,
):
2020-02-25 17:49:55 +00:00
mock = mocker.patch(
"app.organizations_client.get_services_and_usage",
return_value={
"services": [
{
"service_id": SERVICE_ONE_ID,
"service_name": "1",
"chargeable_billable_sms": 250122,
"emails_sent": 13000,
"free_sms_limit": 250000,
"sms_billable_units": 122,
"sms_cost": 0,
"sms_remainder": None,
},
{
"service_id": SERVICE_TWO_ID,
"service_name": "5",
"chargeable_billable_sms": 0,
"emails_sent": 20000,
"free_sms_limit": 250000,
"sms_billable_units": 2500,
"sms_cost": 42.0,
"sms_remainder": None,
},
]
},
)
client_request.login(active_user_with_permissions)
2025-10-14 13:20:05 -07:00
page = client_request.get(".organization_usage", org_id=ORGANISATION_ID)
2024-02-07 09:38:18 -07:00
mock.assert_called_once_with(ORGANISATION_ID, 2020)
services = page.select("main h3")
2023-08-28 12:12:21 -04:00
usage_rows = page.select("main .grid-col-6")
assert len(services) == 2
# Totals
assert normalize_spaces(usage_rows[0].text) == "Emails 33,000 sent"
2022-10-12 20:16:22 +00:00
assert normalize_spaces(usage_rows[1].text) == "Text messages $42.00 spent"
assert normalize_spaces(services[0].text) == "1"
assert normalize_spaces(services[1].text) == "5"
assert services[0].find("a")["href"] == url_for(
"main.usage", service_id=SERVICE_ONE_ID
)
2022-12-05 15:33:44 -05:00
assert normalize_spaces(usage_rows[2].text) == "13,000 emails sent"
assert normalize_spaces(usage_rows[3].text) == "122 free text messages sent"
assert services[1].find("a")["href"] == url_for(
"main.usage", service_id=SERVICE_TWO_ID
)
2022-12-05 15:33:44 -05:00
assert normalize_spaces(usage_rows[4].text) == "20,000 emails sent"
assert normalize_spaces(usage_rows[5].text) == "$42.00 spent on text messages"
# Ensure theres no this org has no services message
2023-08-28 12:12:21 -04:00
assert not page.select(".usa-hint")
@freeze_time("2020-02-20 20:20")
2023-07-12 12:09:44 -04:00
def test_organization_services_shows_live_services_and_usage_with_count_of_1(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mocker,
active_user_with_permissions,
fake_uuid,
):
mocker.patch(
"app.organizations_client.get_services_and_usage",
return_value={
"services": [
{
"service_id": SERVICE_ONE_ID,
"service_name": "1",
"chargeable_billable_sms": 1,
"emails_sent": 1,
"free_sms_limit": 250000,
"sms_billable_units": 1,
"sms_cost": 0,
"sms_remainder": None,
},
]
},
)
client_request.login(active_user_with_permissions)
2025-10-14 13:20:05 -07:00
page = client_request.get(".organization_usage", org_id=ORGANISATION_ID)
2023-08-28 12:12:21 -04:00
usage_rows = page.select("main .grid-col-6")
# Totals
assert normalize_spaces(usage_rows[0].text) == "Emails 1 sent"
2022-10-12 20:16:22 +00:00
assert normalize_spaces(usage_rows[1].text) == "Text messages $0.00 spent"
2022-12-05 15:33:44 -05:00
assert normalize_spaces(usage_rows[2].text) == "1 email sent"
assert normalize_spaces(usage_rows[3].text) == "1 free text message sent"
@freeze_time("2020-02-20 20:20")
@pytest.mark.parametrize(
("financial_year", "expected_selected"),
[
(2018, "2018 to 2019 fiscal year"),
(2019, "2019 to 2020 fiscal year"),
2024-02-07 09:38:18 -07:00
(2020, "2020 to 2021 fiscal year"),
],
)
2023-07-12 12:09:44 -04:00
def test_organization_services_filters_by_financial_year(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mocker,
active_user_with_permissions,
fake_uuid,
financial_year,
expected_selected,
):
mock = mocker.patch(
"app.organizations_client.get_services_and_usage", return_value={"services": []}
)
page = client_request.get(
2025-10-14 13:20:05 -07:00
".organization_usage",
org_id=ORGANISATION_ID,
year=financial_year,
)
mock.assert_called_once_with(ORGANISATION_ID, financial_year)
assert normalize_spaces(page.select_one(".pill").text) == (
2024-02-07 09:38:18 -07:00
"2020 to 2021 fiscal year "
"2019 to 2020 fiscal year "
2024-02-07 09:38:18 -07:00
"2018 to 2019 fiscal year"
)
assert normalize_spaces(page.select_one(".pill-item--selected").text) == (
expected_selected
)
@freeze_time("2020-02-20 20:20")
2023-07-12 12:09:44 -04:00
def test_organization_services_shows_search_bar(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mocker,
active_user_with_permissions,
fake_uuid,
):
mocker.patch(
"app.organizations_client.get_services_and_usage",
return_value={
"services": [
{
"service_id": SERVICE_ONE_ID,
"service_name": "Service 1",
"chargeable_billable_sms": 250122,
"emails_sent": 13000,
"free_sms_limit": 250000,
"sms_billable_units": 122,
"sms_cost": 1.93,
"sms_remainder": None,
},
]
* 8
},
)
client_request.login(active_user_with_permissions)
2025-10-14 13:20:05 -07:00
page = client_request.get(".organization_usage", org_id=ORGANISATION_ID)
services = page.select(".organization-service")
assert len(services) == 8
assert page.select_one(".live-search")["data-targets"] == ".organization-service"
assert [
normalize_spaces(service_name.text)
for service_name in page.select(".live-search-relevant")
] == [
"Service 1",
"Service 1",
"Service 1",
"Service 1",
"Service 1",
"Service 1",
"Service 1",
"Service 1",
]
@freeze_time("2020-02-20 20:20")
2023-07-12 12:09:44 -04:00
def test_organization_services_hides_search_bar_for_7_or_fewer_services(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mocker,
active_user_with_permissions,
fake_uuid,
):
mocker.patch(
"app.organizations_client.get_services_and_usage",
return_value={
"services": [
{
"service_id": SERVICE_ONE_ID,
"service_name": "Service 1",
"chargeable_billable_sms": 250122,
"emails_sent": 13000,
"free_sms_limit": 250000,
"sms_billable_units": 122,
"sms_cost": 1.93,
"sms_remainder": None,
},
]
* 7
},
)
client_request.login(active_user_with_permissions)
2025-10-14 13:20:05 -07:00
page = client_request.get(".organization_usage", org_id=ORGANISATION_ID)
services = page.select(".organization-service")
assert len(services) == 7
assert not page.select_one(".live-search")
@freeze_time("2021-11-12 11:09:00.061258")
2023-07-12 12:09:44 -04:00
def test_organization_services_links_to_downloadable_report(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mocker,
active_user_with_permissions,
fake_uuid,
):
mocker.patch(
"app.organizations_client.get_services_and_usage",
return_value={
"services": [
{
"service_id": SERVICE_ONE_ID,
"service_name": "Service 1",
"chargeable_billable_sms": 250122,
"emails_sent": 13000,
"free_sms_limit": 250000,
"sms_billable_units": 122,
"sms_cost": 1.93,
"sms_remainder": None,
},
]
* 2
},
)
client_request.login(active_user_with_permissions)
2025-10-14 13:20:05 -07:00
page = client_request.get(".organization_usage", org_id=ORGANISATION_ID)
link_to_report = page.select_one("a[download]")
assert normalize_spaces(link_to_report.text) == "Download this report (CSV)"
assert link_to_report.attrs["href"] == url_for(
".download_organization_usage_report",
org_id=ORGANISATION_ID,
2024-10-09 09:56:19 -04:00
selected_year=2021,
)
@freeze_time("2021-11-12 11:09:00.061258")
2023-07-12 12:09:44 -04:00
def test_download_organization_usage_report(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mocker,
active_user_with_permissions,
fake_uuid,
):
mocker.patch(
"app.organizations_client.get_services_and_usage",
return_value={
"services": [
{
"service_id": SERVICE_ONE_ID,
"service_name": "Service 1",
"chargeable_billable_sms": 22,
"emails_sent": 13000,
"free_sms_limit": 100,
"sms_billable_units": 122,
"sms_cost": 1.934,
"sms_remainder": 0,
},
{
"service_id": SERVICE_TWO_ID,
"service_name": "Service 1",
"chargeable_billable_sms": 222,
"emails_sent": 23000,
"free_sms_limit": 250000,
"sms_billable_units": 322,
"sms_cost": 3.935,
"sms_remainder": 0,
},
]
},
)
client_request.login(active_user_with_permissions)
csv_report = client_request.get(
".download_organization_usage_report",
org_id=ORGANISATION_ID,
selected_year=2021,
_test_page_title=False,
)
assert csv_report.string == (
"Service ID,Service Name,Emails sent,Free text message allowance remaining,"
2022-12-05 15:33:44 -05:00
"Spent on text messages ($)"
"\r\n596364a0-858e-42c8-9062-a8fe822260eb,Service 1,13000,0,1.93"
"\r\n147ad62a-2951-4fa1-9ca0-093cd1a52c52,Service 1,23000,0,3.94\r\n"
)
2023-07-12 12:09:44 -04:00
def test_organization_trial_mode_services_shows_all_non_live_services(
client_request,
platform_admin_user,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mocker,
fake_uuid,
):
mocker.patch(
"app.organizations_client.get_organization_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=False, active=False), # archived
],
)
client_request.login(platform_admin_user)
page = client_request.get(
".organization_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"
)
2023-07-12 12:09:44 -04:00
def test_organization_trial_mode_services_doesnt_work_if_not_platform_admin(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
):
client_request.get(
".organization_trial_mode_services",
org_id=ORGANISATION_ID,
_expected_status=403,
)
def test_manage_org_users_shows_correct_link_next_to_each_user(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mock_get_users_for_organization,
mock_get_invited_users_for_organization,
):
page = client_request.get(
".manage_org_users",
org_id=ORGANISATION_ID,
)
# No banner confirming a user to be deleted shown
assert not page.select_one(".banner-dangerous")
users = page.find_all(class_="user-list-item")
# The first user is an invited user, so has the link to cancel the invitation.
# The second two users are active users, so have the link to be removed from the org
assert (
normalize_spaces(users[0].text)
== "invited_user@test.gsa.gov (invited) Cancel invitation for invited_user@test.gsa.gov"
)
assert (
normalize_spaces(users[1].text)
== "Test User 1 test@gsa.gov Remove Test User 1 test@gsa.gov"
)
assert (
normalize_spaces(users[2].text)
== "Test User 2 testt@gsa.gov Remove Test User 2 testt@gsa.gov"
)
assert users[0].a["href"] == url_for(
".cancel_invited_org_user",
org_id=ORGANISATION_ID,
invited_user_id="73616d70-6c65-4f6f-b267-5f696e766974",
)
assert users[1].a["href"] == url_for(
".edit_organization_user", org_id=ORGANISATION_ID, user_id="1234"
)
assert users[2].a["href"] == url_for(
".edit_organization_user", org_id=ORGANISATION_ID, user_id="5678"
)
def test_manage_org_users_shows_no_link_for_cancelled_users(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mock_get_users_for_organization,
sample_org_invite,
mocker,
):
sample_org_invite["status"] = "cancelled"
mocker.patch(
"app.models.user.OrganizationInvitedUsers.client_method",
return_value=[sample_org_invite],
)
page = client_request.get(
".manage_org_users",
org_id=ORGANISATION_ID,
)
users = page.find_all(class_="user-list-item")
assert (
normalize_spaces(users[0].text)
== "invited_user@test.gsa.gov (cancelled invite)"
)
assert not users[0].a
@pytest.mark.parametrize(
"number_of_users",
[
pytest.param(8),
pytest.param(800),
],
)
def test_manage_org_users_should_show_live_search_if_more_than_7_users(
client_request,
mocker,
2023-07-12 12:09:44 -04:00
mock_get_organization,
active_user_with_permissions,
number_of_users,
):
mocker.patch(
"app.models.user.OrganizationInvitedUsers.client_method",
return_value=[],
)
mocker.patch(
"app.models.user.OrganizationUsers.client_method",
return_value=[active_user_with_permissions] * number_of_users,
)
page = client_request.get(
".manage_org_users",
org_id=ORGANISATION_ID,
)
assert page.select_one("div[data-module=live-search]")["data-targets"] == (
".user-list-item"
)
assert len(page.select(".user-list-item")) == number_of_users
2025-04-17 15:38:27 -04:00
textbox = page.select_one(".usa-input")
assert textbox is not None
assert textbox.get("value") is None
assert textbox["name"] == "search"
# data-module=autofocus is set on a containing element so it
# shouldnt also be set on the textbox itself
assert "data-module" not in textbox
assert not page.select_one("[data-force-focus]")
assert textbox["class"] == ["usa-input"]
assert (
normalize_spaces(page.select_one("label[class=usa-label]").text)
== "Search by name or email address"
)
@pytest.mark.parametrize(
"number_of_users",
[
pytest.param(3),
pytest.param(7),
],
)
2023-05-26 12:35:48 -07:00
def test_manage_org_users_should_show_live_search_if_7_users_or_less(
client_request,
mocker,
2023-07-12 12:09:44 -04:00
mock_get_organization,
2023-05-26 12:35:48 -07:00
active_user_with_permissions,
number_of_users,
):
mocker.patch(
"app.models.user.OrganizationInvitedUsers.client_method",
2023-05-26 12:35:48 -07:00
return_value=[],
)
mocker.patch(
"app.models.user.OrganizationUsers.client_method",
2023-05-26 12:35:48 -07:00
return_value=[active_user_with_permissions] * number_of_users,
)
page = client_request.get(
".manage_org_users",
2023-05-26 12:35:48 -07:00
org_id=ORGANISATION_ID,
)
with pytest.raises(expected_exception=TypeError):
assert page.select_one("div[data-module=live-search]")["data-targets"] == (
2023-05-26 12:35:48 -07:00
".user-list-item"
)
2023-07-12 12:09:44 -04:00
def test_edit_organization_user_shows_the_delete_confirmation_banner(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mock_get_invites_for_organization,
mock_get_users_for_organization,
active_user_with_permissions,
):
page = client_request.get(
".edit_organization_user",
org_id=ORGANISATION_ID,
user_id=active_user_with_permissions["id"],
)
assert normalize_spaces(page.h1) == "Team members"
banner = page.select_one(".banner-dangerous")
assert "Are you sure you want to remove Test User?" in normalize_spaces(
banner.contents[0]
)
assert banner.form.attrs["action"] == url_for(
"main.remove_user_from_organization",
org_id=ORGANISATION_ID,
user_id=active_user_with_permissions["id"],
)
2023-07-12 12:09:44 -04:00
def test_remove_user_from_organization_makes_api_request_to_remove_user(
client_request,
mocker,
2023-07-12 12:09:44 -04:00
mock_get_organization,
fake_uuid,
):
mock_remove_user = mocker.patch(
"app.organizations_client.remove_user_from_organization"
)
client_request.post(
".remove_user_from_organization",
org_id=ORGANISATION_ID,
user_id=fake_uuid,
_expected_redirect=url_for(
"main.show_accounts_or_dashboard",
),
)
mock_remove_user.assert_called_with(ORGANISATION_ID, fake_uuid)
2023-07-12 12:09:44 -04:00
def test_organization_settings_platform_admin_only(
client_request, mock_get_organization, organization_one
):
client_request.get(
".organization_settings",
org_id=organization_one["id"],
_expected_status=403,
)
2023-07-12 12:09:44 -04:00
def test_organization_settings_for_platform_admin(
client_request, platform_admin_user, mock_get_organization, organization_one
):
expected_rows = [
"Label Value Action",
"Name Test organization Change organization name",
"Sector Federal government Change sector for the organization",
"Billing details None Change billing details for the organization",
"Notes None Change the notes for the organization",
"Known email domains None Change known email domains for the organization",
]
client_request.login(platform_admin_user)
page = client_request.get(".organization_settings", org_id=organization_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_organization.assert_called_with(organization_one["id"])
@pytest.mark.parametrize(
("endpoint", "expected_options", "expected_selected"),
[
(
".edit_organization_type",
(
{"value": "federal", "label": "Federal government"},
{"value": "state", "label": "State government"},
{"value": "other", "label": "Other"},
),
"federal",
),
],
)
@pytest.mark.parametrize(
"user",
[
pytest.param(
create_platform_admin_user(),
),
pytest.param(create_active_user_with_permissions(), marks=pytest.mark.xfail),
],
)
2023-07-12 12:09:44 -04:00
def test_view_organization_settings(
client_request,
fake_uuid,
2023-07-12 12:09:44 -04:00
organization_one,
mock_get_organization,
endpoint,
expected_options,
expected_selected,
user,
):
client_request.login(user)
page = client_request.get(endpoint, org_id=organization_one["id"])
radios = page.select("input[type=radio]")
2025-02-27 14:21:43 -05:00
labels = page.select("label.usa-radio__label") # Select all radio labels in order
for index, option in enumerate(expected_options):
option_values = {
"value": radios[index]["value"],
2025-02-27 14:21:43 -05:00
"label": normalize_spaces(labels[index].text), # Match labels using index
}
assert option_values == 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_organization_type",
{"organization_type": "federal"},
{"cached_service_ids": [], "organization_type": "federal"},
),
(
".edit_organization_type",
{"organization_type": "state"},
{"cached_service_ids": [], "organization_type": "state"},
),
],
)
@pytest.mark.parametrize(
"user",
[
pytest.param(
create_platform_admin_user(),
),
pytest.param(
create_active_user_with_permissions(),
),
],
)
2023-07-12 12:09:44 -04:00
def test_update_organization_settings(
mocker,
client_request,
fake_uuid,
2023-07-12 12:09:44 -04:00
organization_one,
mock_get_organization,
mock_update_organization,
endpoint,
post_data,
expected_persisted,
user,
):
mocker.patch("app.organizations_client.get_organization_services", return_value=[])
client_request.login(user)
if user["email_address"] == "platform@admin.gsa.gov":
2023-05-26 12:35:48 -07:00
expected_status = 302
expected_redirect = url_for(
"main.organization_settings",
org_id=organization_one["id"],
2023-05-26 12:35:48 -07:00
)
else:
expected_status = 403
expected_redirect = None
client_request.post(
endpoint,
org_id=organization_one["id"],
_data=post_data,
2023-05-26 12:35:48 -07:00
_expected_status=expected_status,
_expected_redirect=expected_redirect,
)
if user["email_address"] == "platform@admin.gsa.gov":
2023-07-12 12:09:44 -04:00
mock_update_organization.assert_called_once_with(
organization_one["id"],
2023-05-26 12:35:48 -07:00
**expected_persisted,
)
2023-07-12 12:09:44 -04:00
def test_update_organization_sector_sends_service_id_data_to_api_client(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
organization_one,
mock_get_organization_services,
mock_update_organization,
platform_admin_user,
):
client_request.login(platform_admin_user)
client_request.post(
"main.edit_organization_type",
org_id=organization_one["id"],
2025-02-27 14:21:43 -05:00
_data={"organization_type": "state"},
_expected_status=302,
_expected_redirect=url_for(
"main.organization_settings",
org_id=organization_one["id"],
),
)
2023-07-12 12:09:44 -04:00
mock_update_organization.assert_called_once_with(
organization_one["id"],
cached_service_ids=["12345", "67890", SERVICE_ONE_ID],
2025-02-27 14:21:43 -05:00
organization_type="state",
)
@pytest.mark.parametrize(
"user",
[
pytest.param(
create_platform_admin_user(),
),
pytest.param(create_active_user_with_permissions(), marks=pytest.mark.xfail),
],
)
2023-07-12 12:09:44 -04:00
def test_view_organization_domains(
mocker,
client_request,
fake_uuid,
user,
):
client_request.login(user)
mocker.patch(
"app.organizations_client.get_organization",
2023-07-12 12:09:44 -04:00
side_effect=lambda org_id: organization_json(
org_id,
"Org 1",
domains=["example.gsa.gov", "test.example.gsa.gov"],
),
)
page = client_request.get(
"main.edit_organization_domains",
org_id=ORGANISATION_ID,
)
assert [textbox.get("value") for textbox in page.select("input[type=text]")] == [
"example.gsa.gov",
"test.example.gsa.gov",
Convert ListEntry component to use new fields ListEntry component uses FieldList field to group textboxes. Textboxes can be text inputs, email fields or international phone number fields. This converts all field-lists to use: - GovukTextInputField - GovukEmailField - InternationalPhoneNumber Affects these forms: - OrganisationDomainsForm - GuestList Also changes to related Javascript: Update list-entry JS tests to match new HTML Updates the HTML the JS operates on in the test (a fixture representing the HTML in the page on load) to match the new GOVUK Frontend we are generating. Make list-entry JS work with GOVUK Frontend HTML The existing list-entry JS did a few things that clashed with how the new HTML works: - added a 'input-' prefix to the id attributes of all text-inputs - did not make its name and id attributes values match The new HTML has id and name attributes that match so these changes remove the prefix for id attributes and makes them match the name attribute. To understand these changes, it is useful to know how the values for id and name attributes are generated: 1. the id attribute for the component element is stored 2. the 'list-entry-' prefix is removed and the remainder is used to generate ids For example, if the component's id is 'list-entry-domains', the id will be 'domains-1', where the text-input is the first one. This also adds some logic to the HoganJS template to make the value attribute optional, so it is only added if it has a non-null value. This matches the behaviour of the text-input component used in the new list-entry component. Also change whitelist references to guestlist in tests - we forgot to do it earlier, when we moved from calling this feature whitelist to calling it guestlist.
2020-08-07 10:48:21 +01:00
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
]
@pytest.mark.parametrize(
("post_data", "expected_persisted"),
[
(
{
"domains-0": "example.gsa.gov",
"domains-2": "example.gsa.gov",
"domains-3": "EXAMPLE.gsa.gov",
"domains-5": "test.gsa.gov",
},
{
"domains": [
"example.gsa.gov",
"test.gsa.gov",
]
},
),
(
{
"domains-0": "",
"domains-1": "",
"domains-2": "",
},
{"domains": []},
),
],
)
@pytest.mark.parametrize(
"user",
[
pytest.param(
create_platform_admin_user(),
),
pytest.param(
create_active_user_with_permissions(),
),
],
)
2023-07-12 12:09:44 -04:00
def test_update_organization_domains(
client_request,
fake_uuid,
2023-07-12 12:09:44 -04:00
organization_one,
mock_get_organization,
mock_update_organization,
post_data,
expected_persisted,
user,
):
client_request.login(user)
if user["email_address"] == "platform@admin.gsa.gov":
2023-05-26 12:35:48 -07:00
expected_status = 302
expected_redirect = url_for(
"main.organization_settings",
org_id=organization_one["id"],
2023-05-26 12:35:48 -07:00
)
else:
expected_status = 403
expected_redirect = None
client_request.post(
"main.edit_organization_domains",
org_id=ORGANISATION_ID,
_data=post_data,
2023-05-26 12:35:48 -07:00
_expected_status=expected_status,
_expected_redirect=expected_redirect,
)
if user["email_address"] == "platform@admin.gsa.gov":
2023-07-12 12:09:44 -04:00
mock_update_organization.assert_called_once_with(
2023-05-26 12:35:48 -07:00
ORGANISATION_ID,
**expected_persisted,
)
2023-07-12 12:09:44 -04:00
def test_update_organization_domains_when_domain_already_exists(
mocker,
client_request,
fake_uuid,
2023-07-12 12:09:44 -04:00
organization_one,
mock_get_organization,
):
user = create_platform_admin_user()
client_request.login(user)
mocker.patch(
"app.organizations_client.update_organization",
side_effect=HTTPError(
response=mocker.Mock(
status_code=400,
json={"result": "error", "message": "Domain already exists"},
),
message="Domain already exists",
),
)
response = client_request.post(
"main.edit_organization_domains",
org_id=ORGANISATION_ID,
_data={
"domains": [
"example.gsa.gov",
]
},
_expected_status=200,
)
assert (
response.find("div", class_="banner-dangerous").text.strip()
== "This domain is already in use"
)
2023-07-12 12:09:44 -04:00
def test_update_organization_name(
client_request,
platform_admin_user,
fake_uuid,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mock_update_organization,
):
client_request.login(platform_admin_user)
client_request.post(
".edit_organization_name",
org_id=fake_uuid,
_data={"name": "TestNewOrgName"},
_expected_redirect=url_for(
".organization_settings",
org_id=fake_uuid,
),
)
2023-07-12 12:09:44 -04:00
mock_update_organization.assert_called_once_with(
fake_uuid,
name="TestNewOrgName",
cached_service_ids=None,
)
@pytest.mark.parametrize(
("name", "error_message"),
[
("", "Cannot be empty"),
("a", "at least two alphanumeric characters"),
("a" * 256, "Organization name must be 255 characters or fewer"),
],
)
2023-07-12 12:09:44 -04:00
def test_update_organization_with_incorrect_input(
client_request,
platform_admin_user,
2023-07-12 12:09:44 -04:00
organization_one,
mock_get_organization,
name,
error_message,
):
client_request.login(platform_admin_user)
page = client_request.post(
".edit_organization_name",
org_id=organization_one["id"],
_data={"name": name},
_expected_status=200,
)
assert error_message in page.select_one(".usa-error-message").text
2023-07-12 12:09:44 -04:00
def test_update_organization_with_non_unique_name(
client_request,
platform_admin_user,
fake_uuid,
2023-07-12 12:09:44 -04:00
mock_get_organization,
mocker,
):
mocker.patch(
"app.organizations_client.update_organization",
side_effect=HTTPError(
response=mocker.Mock(
status_code=400,
json={"result": "error", "message": "Organization name already exists"},
),
message="Organization name already exists",
),
)
client_request.login(platform_admin_user)
page = client_request.post(
".edit_organization_name",
org_id=fake_uuid,
_data={"name": "TestNewOrgName"},
_expected_status=200,
)
assert (
"This organization name is already in use"
in page.select_one(".usa-error-message").text
)
2023-07-12 12:09:44 -04:00
def test_organization_settings_links_to_edit_organization_notes_page(
mocker,
2023-07-12 12:09:44 -04:00
mock_get_organization,
organization_one,
client_request,
platform_admin_user,
):
client_request.login(platform_admin_user)
page = client_request.get(".organization_settings", org_id=organization_one["id"])
assert (
len(
page.find_all(
"a",
attrs={
"href": "/organizations/{}/settings/notes".format(
organization_one["id"]
)
},
)
)
== 1
)
2023-07-12 12:09:44 -04:00
def test_view_edit_organization_notes(
client_request,
platform_admin_user,
organization_one,
mock_get_organization,
):
client_request.login(platform_admin_user)
page = client_request.get(
"main.edit_organization_notes",
org_id=organization_one["id"],
)
assert page.select_one("h1").text == "Edit organization notes"
assert page.find("label", class_="usa-label").text.strip() == "Notes"
assert page.find("textarea").attrs["name"] == "notes"
2021-02-04 17:58:35 +00:00
2023-07-12 12:09:44 -04:00
def test_update_organization_notes(
client_request,
platform_admin_user,
organization_one,
mock_get_organization,
mock_update_organization,
2021-02-04 17:58:35 +00:00
):
client_request.login(platform_admin_user)
client_request.post(
"main.edit_organization_notes",
org_id=organization_one["id"],
_data={"notes": "Very fluffy"},
_expected_redirect=url_for(
"main.organization_settings",
org_id=organization_one["id"],
2021-02-04 17:58:35 +00:00
),
)
2023-07-12 12:09:44 -04:00
mock_update_organization.assert_called_with(
organization_one["id"], cached_service_ids=None, notes="Very fluffy"
2021-02-04 17:58:35 +00:00
)
2023-07-12 12:09:44 -04:00
def test_update_organization_notes_errors_when_user_not_platform_admin(
client_request,
organization_one,
mock_get_organization,
mock_update_organization,
):
client_request.post(
"main.edit_organization_notes",
org_id=organization_one["id"],
_data={"notes": "Very fluffy"},
_expected_status=403,
)
2023-07-12 12:09:44 -04:00
def test_update_organization_notes_doesnt_call_api_when_notes_dont_change(
client_request,
platform_admin_user,
organization_one,
mock_update_organization,
mocker,
):
mocker.patch(
"app.organizations_client.get_organization",
return_value=organization_json(
id_=organization_one["id"], name="Test Org", notes="Very fluffy"
),
)
client_request.login(platform_admin_user)
client_request.post(
"main.edit_organization_notes",
org_id=organization_one["id"],
_data={"notes": "Very fluffy"},
_expected_redirect=url_for(
"main.organization_settings",
org_id=organization_one["id"],
),
)
2023-07-12 12:09:44 -04:00
assert not mock_update_organization.called
2023-07-12 12:09:44 -04:00
def test_organization_settings_links_to_edit_organization_billing_details_page(
mocker,
2023-07-12 12:09:44 -04:00
mock_get_organization,
organization_one,
client_request,
platform_admin_user,
):
client_request.login(platform_admin_user)
page = client_request.get(".organization_settings", org_id=organization_one["id"])
assert (
len(
page.find_all(
"a",
attrs={
"href": "/organizations/{}/settings/edit-billing-details".format(
organization_one["id"]
)
},
)
)
== 1
)
2023-07-12 12:09:44 -04:00
def test_view_edit_organization_billing_details(
client_request,
platform_admin_user,
organization_one,
mock_get_organization,
):
client_request.login(platform_admin_user)
page = client_request.get(
"main.edit_organization_billing_details",
org_id=organization_one["id"],
)
assert page.select_one("h1").text == "Edit organization billing details"
labels = page.find_all("label", class_="usa-label")
labels_list = [
"Contact email addresses",
"Contact names",
"Reference",
"Purchase order number",
"Notes",
]
for label in labels:
assert label.text.strip() in labels_list
2023-08-28 14:40:33 -04:00
textbox_names = page.find_all("input", class_="usa-input ")
names_list = [
"billing_contact_email_addresses",
"billing_contact_names",
"billing_reference",
"purchase_order_number",
]
for name in textbox_names:
assert name.attrs["name"] in names_list
assert page.find("textarea").attrs["name"] == "notes"
2021-02-05 11:59:31 +00:00
2023-07-12 12:09:44 -04:00
def test_update_organization_billing_details(
client_request,
platform_admin_user,
organization_one,
mock_get_organization,
mock_update_organization,
2021-02-05 11:59:31 +00:00
):
client_request.login(platform_admin_user)
client_request.post(
"main.edit_organization_billing_details",
org_id=organization_one["id"],
_data={
"billing_contact_email_addresses": "accounts@fluff.gsa.gov",
"billing_contact_names": "Flannellette von Fluff",
"billing_reference": "",
"purchase_order_number": "PO1234",
"notes": "very fluffy, give extra allowance",
},
_expected_redirect=url_for(
"main.organization_settings",
org_id=organization_one["id"],
),
2021-02-05 11:59:31 +00:00
)
2023-07-12 12:09:44 -04:00
mock_update_organization.assert_called_with(
organization_one["id"],
2021-02-05 11:59:31 +00:00
cached_service_ids=None,
billing_contact_email_addresses="accounts@fluff.gsa.gov",
billing_contact_names="Flannellette von Fluff",
billing_reference="",
purchase_order_number="PO1234",
notes="very fluffy, give extra allowance",
2021-02-05 11:59:31 +00:00
)
2023-07-12 12:09:44 -04:00
def test_update_organization_billing_details_errors_when_user_not_platform_admin(
client_request,
organization_one,
mock_get_organization,
mock_update_organization,
2021-02-05 11:59:31 +00:00
):
client_request.post(
"main.edit_organization_billing_details",
org_id=organization_one["id"],
_data={"notes": "Very fluffy"},
2021-02-05 11:59:31 +00:00
_expected_status=403,
)
2023-07-12 12:09:44 -04:00
def test_organization_billing_page_not_accessible_if_not_platform_admin(
client_request,
2023-07-12 12:09:44 -04:00
mock_get_organization,
):
client_request.get(
".organization_billing", org_id=ORGANISATION_ID, _expected_status=403
)