Skip to content

Commit

Permalink
Feat: error templates for in-person enrollment (#2382)
Browse files Browse the repository at this point in the history
  • Loading branch information
angela-tran authored Sep 23, 2024
2 parents 9d53919 + 8ef7dd1 commit fed968b
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 10 deletions.
25 changes: 25 additions & 0 deletions benefits/in_person/templates/error-base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{% extends "admin/agency-base.html" %}
{% load static %}

{% block content %}
<div class="row justify-content-center">
<div class="col-lg-6">
<div class="border border-3 p-3">
<h2 class="p-0 m-0 text-left">In-person enrollment</h2>
</div>
<div class="border border-3 border-top-0 p-3 min-vh-60 d-flex flex-column justify-content-between">
<div class="d-flex">
<i class="error-icon"></i>
<div class="mt-lg-3">
{% block error-message %}
{% endblock error-message %}
</div>
</div>
<div class="row">
{% block cta-buttons %}
{% endblock cta-buttons %}
</div>
</div>
</div>
</div>
{% endblock content %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{% extends "error-base.html" %}

{% block error-message %}
<h3 class="fw-bold h4">This person is still enrolled in the {{ flow_label }} benefit.</h3>

<p class="my-4">
<span class="fw-bold">This rider will enjoy a transit benefit until {{ enrollment.expires|date }}.</span> They can re-enroll for this benefit beginning on {{ enrollment.reenrollment|date }}. Please try again then.
</p>
{% endblock error-message %}

{% block cta-buttons %}
<div class="col-12">
{% url routes.ADMIN_INDEX as url_return_to_dashboard %}
<a href="{{ url_return_to_dashboard }}" class="btn btn-lg btn-primary d-block">Return to dashboard</a>
</div>
{% endblock cta-buttons %}
20 changes: 20 additions & 0 deletions benefits/in_person/templates/in_person/enrollment/retry.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{% extends "error-base.html" %}

{% block error-message %}
<h3 class="fw-bold h4">Card not found.</h3>

<p class="my-4">
The card information may not have been entered correctly. Please check the details on your card and try again.
</p>
{% endblock error-message %}

{% block cta-buttons %}
<div class="col-6">
{% url routes.ADMIN_INDEX as url_cancel %}
<a href="{{ url_cancel }}" class="btn btn-lg btn-outline-primary d-block">Cancel</a>
</div>
<div class="col-6">
{% url routes.IN_PERSON_ENROLLMENT as url_try_again %}
<a href="{{ url_try_again }}" class="btn btn-lg btn-primary d-block">Try again</a>
</div>
{% endblock cta-buttons %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{% extends "error-base.html" %}

{% block error-message %}
<h3 class="fw-bold h4">We're working to fix a problem.</h3>

<p class="my-4">
There is a problem with the application configuration, but we're working to fix it. Please try again in a little while.
</p>
{% endblock error-message %}

{% block cta-buttons %}
<div class="col-12">
{% url routes.ADMIN_INDEX as url_return_to_dashboard %}
<a href="{{ url_return_to_dashboard }}" class="btn btn-lg btn-primary d-block">Return to dashboard</a>
</div>
{% endblock cta-buttons %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% extends "error-base.html" %}

{% block error-message %}
<h3 class="fw-bold h4">The enrollment system isn't working right now.</h3>

<p class="my-4">Please wait 24 hours and try to enroll again.</p>
{% endblock error-message %}

{% block cta-buttons %}
<div class="col-12">
{% url routes.ADMIN_INDEX as url_return_to_dashboard %}
<a href="{{ url_return_to_dashboard }}" class="btn btn-lg btn-primary d-block">Return to dashboard</a>
</div>
{% endblock cta-buttons %}
27 changes: 23 additions & 4 deletions benefits/in_person/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def token(request):


def enrollment(request):
"""View handler for the in-person enrollment page."""
# POST back after transit processor form, process card token
if request.method == "POST":
form = forms.CardTokenizeSuccessForm(request.POST)
Expand All @@ -93,9 +94,11 @@ def enrollment(request):
return redirect(routes.IN_PERSON_ENROLLMENT_SUCCESS)

case Status.SYSTEM_ERROR:
sentry_sdk.capture_exception(exception)
return redirect(routes.IN_PERSON_ENROLLMENT_SYSTEM_ERROR)

case Status.EXCEPTION:
sentry_sdk.capture_exception(exception)
return redirect(routes.IN_PERSON_SERVER_ERROR)

case Status.REENROLLMENT_ERROR:
Expand Down Expand Up @@ -133,22 +136,38 @@ def enrollment(request):


def reenrollment_error(request):
return TemplateResponse(request, "in_person/enrollment/reenrollment_error.html")
"""View handler for a re-enrollment attempt that is not yet within the re-enrollment window."""
context = {**admin_site.each_context(request)}

flow = session.flow(request)
context["flow_label"] = flow.label

return TemplateResponse(request, "in_person/enrollment/reenrollment_error.html", context)


def retry(request):
return TemplateResponse(request, "in_person/enrollment/retry.html")
"""View handler for card verification failure."""
context = {**admin_site.each_context(request)}

return TemplateResponse(request, "in_person/enrollment/retry.html", context)


def system_error(request):
return TemplateResponse(request, "in_person/enrollment/system_error.html")
"""View handler for an enrollment system error."""
context = {**admin_site.each_context(request)}

return TemplateResponse(request, "in_person/enrollment/system_error.html", context)


def server_error(request):
return TemplateResponse(request, "in_person/enrollment/server_error.html")
"""View handler for errors caused by a misconfiguration or bad request."""
context = {**admin_site.each_context(request)}

return TemplateResponse(request, "in_person/enrollment/server_error.html", context)


def success(request):
"""View handler for the final success page."""
context = {**admin_site.each_context(request)}

return TemplateResponse(request, "in_person/enrollment/success.html", context)
13 changes: 13 additions & 0 deletions benefits/static/css/admin/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,19 @@ iframe.card-collection {
min-height: 60vh;
}

/* Error Pages */
.error-icon {
background-color: var(--bs-danger);
mask: url("../../../static/img/icon/exclamation-circle-fill.svg") no-repeat;
-webkit-mask: url("../../../static/img/icon/exclamation-circle-fill.svg")
no-repeat;

display: inline-block;
width: 64px;
height: 64px;
margin-right: calc(12rem / 16);
}

/* Login Page */
.login #header {
padding: 0 !important;
Expand Down
3 changes: 3 additions & 0 deletions benefits/static/img/icon/exclamation-circle-fill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 24 additions & 6 deletions tests/pytest/in_person/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ def invalid_form_data():
return {"invalid": "data"}


@pytest.fixture
def mocked_sentry_sdk_module(mocker):
return mocker.patch.object(benefits.in_person.views, "sentry_sdk")


@pytest.mark.django_db
@pytest.mark.parametrize("viewname", [routes.IN_PERSON_ELIGIBILITY, routes.IN_PERSON_ENROLLMENT])
def test_view_not_logged_in(client, viewname):
Expand Down Expand Up @@ -112,7 +117,7 @@ def test_token_valid(mocker, admin_client):

@pytest.mark.django_db
@pytest.mark.usefixtures("mocked_session_agency", "mocked_session_eligible")
def test_token_system_error(mocker, admin_client):
def test_token_system_error(mocker, admin_client, mocked_sentry_sdk_module):
mocker.patch("benefits.core.session.enrollment_token_valid", return_value=False)

mock_error = {"message": "Mock error message"}
Expand All @@ -135,11 +140,12 @@ def test_token_system_error(mocker, admin_client):
assert "token" not in data
assert "redirect" in data
assert data["redirect"] == reverse(routes.IN_PERSON_ENROLLMENT_SYSTEM_ERROR)
mocked_sentry_sdk_module.capture_exception.assert_called_once()


@pytest.mark.django_db
@pytest.mark.usefixtures("mocked_session_agency", "mocked_session_eligible")
def test_token_http_error_400(mocker, admin_client):
def test_token_http_error_400(mocker, admin_client, mocked_sentry_sdk_module):
mocker.patch("benefits.core.session.enrollment_token_valid", return_value=False)

mock_error = {"message": "Mock error message"}
Expand All @@ -162,11 +168,12 @@ def test_token_http_error_400(mocker, admin_client):
assert "token" not in data
assert "redirect" in data
assert data["redirect"] == reverse(routes.IN_PERSON_SERVER_ERROR)
mocked_sentry_sdk_module.capture_exception.assert_called_once()


@pytest.mark.django_db
@pytest.mark.usefixtures("mocked_session_agency", "mocked_session_eligible")
def test_token_misconfigured_client_id(mocker, admin_client):
def test_token_misconfigured_client_id(mocker, admin_client, mocked_sentry_sdk_module):
mocker.patch("benefits.core.session.enrollment_token_valid", return_value=False)

exception = UnsupportedTokenTypeError()
Expand All @@ -186,11 +193,12 @@ def test_token_misconfigured_client_id(mocker, admin_client):
assert "token" not in data
assert "redirect" in data
assert data["redirect"] == reverse(routes.IN_PERSON_SERVER_ERROR)
mocked_sentry_sdk_module.capture_exception_assert_called_once()


@pytest.mark.django_db
@pytest.mark.usefixtures("mocked_session_agency", "mocked_session_eligible")
def test_token_connection_error(mocker, admin_client):
def test_token_connection_error(mocker, admin_client, mocked_sentry_sdk_module):
mocker.patch("benefits.core.session.enrollment_token_valid", return_value=False)

exception = ConnectionError()
Expand All @@ -210,6 +218,7 @@ def test_token_connection_error(mocker, admin_client):
assert "token" not in data
assert "redirect" in data
assert data["redirect"] == reverse(routes.IN_PERSON_SERVER_ERROR)
mocked_sentry_sdk_module.capture_exception_assert_called_once()


@pytest.mark.django_db
Expand Down Expand Up @@ -271,26 +280,28 @@ def test_enrollment_post_valid_form_success(

@pytest.mark.django_db
@pytest.mark.usefixtures("mocked_session_agency", "mocked_session_flow", "model_EnrollmentFlow")
def test_enrollment_post_valid_form_system_error(mocker, admin_client, card_tokenize_form_data):
def test_enrollment_post_valid_form_system_error(mocker, admin_client, card_tokenize_form_data, mocked_sentry_sdk_module):
mocker.patch("benefits.in_person.views.enroll", return_value=(Status.SYSTEM_ERROR, None))

path = reverse(routes.IN_PERSON_ENROLLMENT)
response = admin_client.post(path, card_tokenize_form_data)

assert response.status_code == 302
assert response.url == reverse(routes.IN_PERSON_ENROLLMENT_SYSTEM_ERROR)
mocked_sentry_sdk_module.capture_exception.assert_called_once()


@pytest.mark.django_db
@pytest.mark.usefixtures("mocked_session_agency", "mocked_session_flow", "model_EnrollmentFlow")
def test_enrollment_post_valid_form_exception(mocker, admin_client, card_tokenize_form_data):
def test_enrollment_post_valid_form_exception(mocker, admin_client, card_tokenize_form_data, mocked_sentry_sdk_module):
mocker.patch("benefits.in_person.views.enroll", return_value=(Status.EXCEPTION, None))

path = reverse(routes.IN_PERSON_ENROLLMENT)
response = admin_client.post(path, card_tokenize_form_data)

assert response.status_code == 302
assert response.url == reverse(routes.IN_PERSON_SERVER_ERROR)
mocked_sentry_sdk_module.capture_exception.assert_called_once()


@pytest.mark.django_db
Expand All @@ -305,11 +316,14 @@ def test_enrollment_post_valid_form_reenrollment_error(mocker, admin_client, car
assert response.url == reverse(routes.IN_PERSON_ENROLLMENT_REENROLLMENT_ERROR)


@pytest.mark.django_db
@pytest.mark.usefixtures("mocked_session_flow")
def test_reenrollment_error(admin_client):
path = reverse(routes.IN_PERSON_ENROLLMENT_REENROLLMENT_ERROR)

response = admin_client.get(path)

assert response.status_code == 200
assert response.template_name == "in_person/enrollment/reenrollment_error.html"


Expand All @@ -318,6 +332,7 @@ def test_retry(admin_client):

response = admin_client.get(path)

assert response.status_code == 200
assert response.template_name == "in_person/enrollment/retry.html"


Expand All @@ -326,6 +341,7 @@ def test_system_error(admin_client):

response = admin_client.get(path)

assert response.status_code == 200
assert response.template_name == "in_person/enrollment/system_error.html"


Expand All @@ -334,6 +350,7 @@ def test_server_error(admin_client):

response = admin_client.get(path)

assert response.status_code == 200
assert response.template_name == "in_person/enrollment/server_error.html"


Expand All @@ -342,4 +359,5 @@ def test_success(admin_client):

response = admin_client.get(path)

assert response.status_code == 200
assert response.template_name == "in_person/enrollment/success.html"

0 comments on commit fed968b

Please sign in to comment.