-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replaced function-based `consent_form_view` with `ConsentFormView` class-based view, leveraging Django forms for better structure and maintainability. Introduced a `ConsentForm` for managing consent input and added a `BreadcrumbContextMixin` to streamline breadcrumb handling. Updated templates and URLs accordingly for this refactor. Refactored consent views by splitting and improving helper functions for better clarity and reusability. Introduced comprehensive tests for `_bulk_update_consent`, ensuring proper validation of consent status updates with varied scenarios. This enhances maintainability and test coverage.
- Loading branch information
Showing
9 changed files
with
362 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
"""Dashboard consent app forms.""" | ||
|
||
from django import forms | ||
from django.forms.widgets import CheckboxInput | ||
from django.utils.translation import gettext_lazy as _ | ||
|
||
|
||
class ConsentCheckboxInput(CheckboxInput): | ||
"""Custom CheckboxInput widget for rendering a checkbox input field.""" | ||
|
||
template_name = "consent/forms/widgets/checkbox.html" | ||
|
||
|
||
class ConsentForm(forms.Form): | ||
"""Save user consent through a checkbox field.""" | ||
|
||
consent_agreed = forms.BooleanField( | ||
required=True, | ||
initial=False, | ||
widget=ConsentCheckboxInput( | ||
attrs={ | ||
"label": _("I agree to give my consent"), | ||
"help_text": _("Please confirm your consent by checking this box."), | ||
}, | ||
), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
"""Dashboard consent app mixins.""" | ||
|
||
from django.views.generic.base import ContextMixin | ||
from django_stubs_ext import StrOrPromise | ||
|
||
|
||
class BreadcrumbContextMixin(ContextMixin): | ||
"""Mixin to simplify usage of the `dsfr_breadcrumb` in class based views. | ||
Add the breadcrumb elements in the view context for the dsfr breadcrumb: | ||
https://numerique-gouv.github.io/django-dsfr/components/breadcrumb/. | ||
```python | ||
breadcrumb_links = [{"url": "first-url", "title": "First title"}, {...}], | ||
breadcrumb_current: "Current page title", | ||
breadcrumb_root_dir: "the root directory, if the site is not installed at the | ||
root of the domain" | ||
} | ||
``` | ||
""" | ||
|
||
breadcrumb_links: list[dict[StrOrPromise, StrOrPromise]] | None = None | ||
breadcrumb_current: StrOrPromise | None = None | ||
breadcrumb_root_dir: StrOrPromise | None = None | ||
|
||
def get_context_data(self, **kwargs) -> dict: | ||
"""Add breadcrumb context to the view.""" | ||
context = super().get_context_data(**kwargs) | ||
|
||
context["breadcrumb_data"] = { | ||
"links": self.breadcrumb_links, | ||
"current": self.breadcrumb_current, | ||
} | ||
if self.breadcrumb_root_dir: | ||
context["breadcrumb_data"]["root_dir"] = self.breadcrumb_root_dir | ||
|
||
return context |
7 changes: 7 additions & 0 deletions
7
src/dashboard/apps/consent/templates/consent/forms/widgets/checkbox.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{% include "django/forms/widgets/input.html" %} | ||
|
||
<label class="fr-label" | ||
{% if widget.attrs.id %}for="{{ widget.attrs.id }}"{% endif %}> | ||
{{ widget.attrs.label }} | ||
<span class="fr-hint-text">{{ widget.attrs.help_text }}</span> | ||
</label> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
"""Dashboard consent views tests.""" | ||
|
||
import pytest | ||
from django.urls import reverse | ||
|
||
from apps.auth.factories import UserFactory | ||
from apps.consent import AWAITING, VALIDATED | ||
from apps.consent.factories import ConsentFactory | ||
from apps.consent.models import Consent | ||
from apps.consent.views import ConsentFormView | ||
from apps.core.factories import DeliveryPointFactory, EntityFactory | ||
from apps.core.models import DeliveryPoint | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_bulk_update_consent_status_without_ids(rf): | ||
"""Test that no status is updated if no id is passed.""" | ||
request = rf.get(reverse("consent:manage")) | ||
request.user = UserFactory() | ||
|
||
view = ConsentFormView() | ||
view.setup(request) | ||
|
||
ConsentFactory.create_batch(4) | ||
|
||
ids = [] | ||
assert view._bulk_update_consent(ids, VALIDATED) == 0 | ||
for consent in Consent.objects.all(): | ||
assert consent.status == AWAITING | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_bulk_update_consent_status(rf): | ||
"""Test that all consents are correctly updated.""" | ||
user1 = UserFactory() | ||
|
||
request = rf.get(reverse("consent:manage")) | ||
request.user = user1 | ||
|
||
view = ConsentFormView() | ||
view.setup(request) | ||
ids = [] | ||
entity1 = EntityFactory(users=(user1,), name="entity1") | ||
for i in range(1, 4): | ||
DeliveryPointFactory(provider_assigned_id=f"entity1_{i}", entity=entity1) | ||
|
||
# create awaiting consent for each delivery points | ||
ids = [] | ||
for delivery_point in DeliveryPoint.objects.all(): | ||
consent = ConsentFactory(delivery_point=delivery_point, created_by=user1) | ||
ids.append(consent.id) | ||
|
||
assert view._bulk_update_consent(ids, VALIDATED) == 3 # noqa: PLR2004 | ||
for consent in Consent.objects.all(): | ||
assert consent.status == VALIDATED | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_bulk_update_consent_status_with_fake_id(rf): | ||
"""Test update with wrong ID in list of ids to update.""" | ||
user1 = UserFactory() | ||
|
||
request = rf.get(reverse("consent:manage")) | ||
request.user = user1 | ||
|
||
view = ConsentFormView() | ||
view.setup(request) | ||
ids = ["fa62cf1d-c510-498a-b428-fdf72fa35651"] | ||
entity1 = EntityFactory(users=(user1,), name="entity1") | ||
for i in range(1, 4): | ||
DeliveryPointFactory(provider_assigned_id=f"entity1_{i}", entity=entity1) | ||
|
||
# create awaiting consent for each delivery points | ||
ids = [] | ||
for delivery_point in DeliveryPoint.objects.all(): | ||
consent = ConsentFactory(delivery_point=delivery_point, created_by=user1) | ||
ids.append(consent.id) | ||
|
||
assert view._bulk_update_consent(ids, VALIDATED) == 3 # noqa: PLR2004 | ||
for consent in Consent.objects.all(): | ||
assert consent.status == VALIDATED | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_bulk_update_consent_without_user_perms(rf): | ||
"""Test the update of consents for which the user does not have the rights.""" | ||
user1 = UserFactory() | ||
|
||
request = rf.get(reverse("consent:manage")) | ||
request.user = user1 | ||
|
||
view = ConsentFormView() | ||
view.setup(request) | ||
|
||
entity1 = EntityFactory(users=(user1,), name="entity1") | ||
for i in range(1, 4): | ||
DeliveryPointFactory(provider_assigned_id=f"entity1_{i}", entity=entity1) | ||
|
||
# create wrong consent | ||
user2 = UserFactory() | ||
entity2 = EntityFactory(users=(user2,), name="entity2") | ||
wrong_id = DeliveryPointFactory(provider_assigned_id="entity2_1234", entity=entity2) | ||
|
||
# create awaiting consent for each delivery points | ||
ids = [] | ||
for delivery_point in DeliveryPoint.objects.all(): | ||
consent = ConsentFactory(delivery_point=delivery_point, created_by=user1) | ||
ids.append(consent.id) | ||
|
||
assert len(ids) == 4 # noqa: PLR2004 | ||
wrong_consent = Consent.objects.get(delivery_point=wrong_id) | ||
assert wrong_consent.id in ids | ||
assert view._bulk_update_consent(ids, VALIDATED) == 3 # noqa: PLR2004 | ||
for consent in Consent.objects.filter(delivery_point__entity=entity1): | ||
assert consent.status == VALIDATED | ||
for consent in Consent.objects.filter(delivery_point__entity=entity2): | ||
assert consent.status == AWAITING | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_get_awaiting_ids(rf): | ||
"""Test getting of awaiting ids inferred from validated consents.""" | ||
user1 = UserFactory() | ||
|
||
request = rf.get(reverse("consent:manage")) | ||
request.user = user1 | ||
|
||
view = ConsentFormView() | ||
view.setup(request) | ||
|
||
entity1 = EntityFactory(users=(user1,), name="entity1") | ||
for i in range(1, 4): | ||
DeliveryPointFactory(provider_assigned_id=f"entity1_{i}", entity=entity1) | ||
|
||
# create awaiting consent for each delivery points | ||
ids = [] | ||
for delivery_point in DeliveryPoint.objects.all(): | ||
consent = ConsentFactory(delivery_point=delivery_point, created_by=user1) | ||
ids.append(str(consent.id)) | ||
|
||
# removes one `id` from the list `ids`, | ||
# this is the one we must find with _get_awaiting_ids() | ||
id_not_include = ids.pop() | ||
awaiting_ids = view._get_awaiting_ids(validated_ids=ids) | ||
assert len(awaiting_ids) == 1 | ||
assert id_not_include in awaiting_ids |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.