Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: error templates for in-person enrollment #2382

Merged
merged 6 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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):

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commenting on the line just below the line I'm talking about, since it isn't available for comment

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

Minor suggestion, now that an in_person/templates/in_person/enrollment/ directory exists, maybe we should move this enrollment.html template in there as index.html.


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"
Loading