Merge branch 'main' into za/2596-view-only-domain-request-page

This commit is contained in:
zandercymatics 2024-09-19 09:48:16 -06:00
commit 8644b0d456
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7
24 changed files with 1164 additions and 1024 deletions

View file

@ -9,10 +9,10 @@ We use [django-waffle](https://waffle.readthedocs.io/en/stable/) for our feature
3. Click `Add waffle flag`. 3. Click `Add waffle flag`.
4. Add the model as you would normally. Refer to waffle's documentation [regarding attributes](https://waffle.readthedocs.io/en/stable/types/flag.html#flag-attributes) for more information on them. 4. Add the model as you would normally. Refer to waffle's documentation [regarding attributes](https://waffle.readthedocs.io/en/stable/types/flag.html#flag-attributes) for more information on them.
### Enabling the profile_feature flag ### Enabling a feature flag
1. On the app, navigate to `\admin`. 1. On the app, navigate to `\admin`.
2. Under models, click `Waffle flags`. 2. Under models, click `Waffle flags`.
3. Click the `profile_feature` record. This should exist by default, if not - create one with that name. 3. Click the featue flag record. This should exist by default, if not - create one with that name.
4. (Important) Set the field `Everyone` to `Unknown`. This field overrides all other settings when set to anything else. 4. (Important) Set the field `Everyone` to `Unknown`. This field overrides all other settings when set to anything else.
5. Configure the settings as you see fit. 5. Configure the settings as you see fit.

View file

@ -126,7 +126,15 @@ class AvailableAPITest(MockEppLib):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.user = get_user_model().objects.create(username="username") username = "test_user"
first_name = "First"
last_name = "Last"
email = "info@example.com"
title = "title"
phone = "8080102431"
self.user = get_user_model().objects.create(
username=username, title=title, first_name=first_name, last_name=last_name, email=email, phone=phone
)
def test_available_get(self): def test_available_get(self):
self.client.force_login(self.user) self.client.force_login(self.user)

View file

@ -1977,11 +1977,7 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
so we should display that information using this function. so we should display that information using this function.
""" """
recipient = obj.creator
if hasattr(obj, "creator"):
recipient = obj.creator
else:
recipient = None
# Displays a warning in admin when an email cannot be sent # Displays a warning in admin when an email cannot be sent
if recipient and recipient.email: if recipient and recipient.email:
@ -2186,7 +2182,6 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
extra_context["filtered_audit_log_entries"] = filtered_audit_log_entries extra_context["filtered_audit_log_entries"] = filtered_audit_log_entries
emails = self.get_all_action_needed_reason_emails(obj) emails = self.get_all_action_needed_reason_emails(obj)
extra_context["action_needed_reason_emails"] = json.dumps(emails) extra_context["action_needed_reason_emails"] = json.dumps(emails)
extra_context["has_profile_feature_flag"] = flag_is_active(request, "profile_feature")
# Denote if an action needed email was sent or not # Denote if an action needed email was sent or not
email_sent = request.session.get("action_needed_email_sent", False) email_sent = request.session.get("action_needed_email_sent", False)

File diff suppressed because it is too large Load diff

View file

@ -245,7 +245,6 @@ TEMPLATES = [
"registrar.context_processors.is_production", "registrar.context_processors.is_production",
"registrar.context_processors.org_user_status", "registrar.context_processors.org_user_status",
"registrar.context_processors.add_path_to_context", "registrar.context_processors.add_path_to_context",
"registrar.context_processors.add_has_profile_feature_flag_to_context",
"registrar.context_processors.portfolio_permissions", "registrar.context_processors.portfolio_permissions",
], ],
}, },

View file

@ -1,5 +1,4 @@
from django.conf import settings from django.conf import settings
from waffle.decorators import flag_is_active
def language_code(request): def language_code(request):
@ -54,10 +53,6 @@ def add_path_to_context(request):
return {"path": getattr(request, "path", None)} return {"path": getattr(request, "path", None)}
def add_has_profile_feature_flag_to_context(request):
return {"has_profile_feature_flag": flag_is_active(request, "profile_feature")}
def portfolio_permissions(request): def portfolio_permissions(request):
"""Make portfolio permissions for the request user available in global context""" """Make portfolio permissions for the request user available in global context"""
portfolio_context = { portfolio_context = {

View file

@ -9,7 +9,7 @@ class WaffleFlag(AbstractUserFlag):
Custom implementation of django-waffles 'Flag' object. Custom implementation of django-waffles 'Flag' object.
Read more here: https://waffle.readthedocs.io/en/stable/types/flag.html Read more here: https://waffle.readthedocs.io/en/stable/types/flag.html
Use this class when dealing with feature flags, such as profile_feature. Use this class when dealing with feature flags.
""" """
class Meta: class Meta:

View file

@ -72,12 +72,6 @@ class CheckUserProfileMiddleware:
"""Runs pre-processing logic for each view. Checks for the """Runs pre-processing logic for each view. Checks for the
finished_setup flag on the current user. If they haven't done so, finished_setup flag on the current user. If they haven't done so,
then we redirect them to the finish setup page.""" then we redirect them to the finish setup page."""
# Check that the user is "opted-in" to the profile feature flag
has_profile_feature_flag = flag_is_active(request, "profile_feature")
# If they aren't, skip this check entirely
if not has_profile_feature_flag:
return None
if request.user.is_authenticated: if request.user.is_authenticated:
profile_page = self.profile_page profile_page = self.profile_page

View file

@ -83,13 +83,6 @@
{% include "includes/summary_item.html" with title='Senior official' value=domain.domain_info.senior_official contact='true' edit_link=url editable=is_editable %} {% include "includes/summary_item.html" with title='Senior official' value=domain.domain_info.senior_official contact='true' edit_link=url editable=is_editable %}
{% endif %} {% endif %}
{# Conditionally display profile #}
{% if not has_profile_feature_flag %}
{% url 'domain-your-contact-information' pk=domain.id as url %}
{% include "includes/summary_item.html" with title='Your contact information' value=request.user contact='true' edit_link=url editable=is_editable %}
{% endif %}
{% url 'domain-security-email' pk=domain.id as url %} {% url 'domain-security-email' pk=domain.id as url %}
{% if security_email is not None and security_email not in hidden_security_emails%} {% if security_email is not None and security_email not in hidden_security_emails%}
{% include "includes/summary_item.html" with title='Security email' value=security_email edit_link=url editable=is_editable %} {% include "includes/summary_item.html" with title='Security email' value=security_email edit_link=url editable=is_editable %}

View file

@ -16,11 +16,9 @@
<h2>Time to complete the form</h2> <h2>Time to complete the form</h2>
<p>If you have <a href="{% public_site_url 'domains/before/#information-you%E2%80%99ll-need-to-complete-the-domain-request-form' %}" target="_blank" class="usa-link">all the information you need</a>, <p>If you have <a href="{% public_site_url 'domains/before/#information-you%E2%80%99ll-need-to-complete-the-domain-request-form' %}" target="_blank" class="usa-link">all the information you need</a>,
completing your domain request might take around 15 minutes.</p> completing your domain request might take around 15 minutes.</p>
{% if has_profile_feature_flag %}
<h2>How well reach you</h2> <h2>How well reach you</h2>
<p>While reviewing your domain request, we may need to reach out with questions. Well also email you when we complete our review. If the contact information below is not correct, visit <a href="{% url 'user-profile' %}?redirect=domain-request:" class="usa-link">your profile</a> to make updates.</p> <p>While reviewing your domain request, we may need to reach out with questions. Well also email you when we complete our review. If the contact information below is not correct, visit <a href="{% url 'user-profile' %}?redirect=domain-request:" class="usa-link">your profile</a> to make updates.</p>
{% include "includes/profile_information.html" with user=user%} {% include "includes/profile_information.html" with user=user%}
{% endif %}
{% block form_buttons %} {% block form_buttons %}

View file

@ -23,13 +23,21 @@
</svg> </svg>
Reset Reset
</button> </button>
{% if portfolio %}
<label class="usa-sr-only" for="domain-requests__search-field">Search by domain name or creator</label>
{% else %}
<label class="usa-sr-only" for="domain-requests__search-field">Search by domain name</label> <label class="usa-sr-only" for="domain-requests__search-field">Search by domain name</label>
{% endif %}
<input <input
class="usa-input" class="usa-input"
id="domain-requests__search-field" id="domain-requests__search-field"
type="search" type="search"
name="search" name="search"
{% if portfolio %}
placeholder="Search by domain name or creator"
{% else %}
placeholder="Search by domain name" placeholder="Search by domain name"
{% endif %}
/> />
<button class="usa-button" type="submit" id="domain-requests__search-field-submit"> <button class="usa-button" type="submit" id="domain-requests__search-field-submit">
<img <img
@ -42,6 +50,125 @@
</section> </section>
</div> </div>
</div> </div>
{% if portfolio %}
<div class="display-flex flex-align-center">
<span class="margin-right-2 margin-top-neg-1 usa-prose text-base-darker">Filter by</span>
<div class="usa-accordion usa-accordion--select margin-right-2">
<div class="usa-accordion__heading">
<button
type="button"
class="usa-button usa-button--small padding--8-8-9 usa-button--outline usa-button--filter usa-accordion__button"
aria-expanded="false"
aria-controls="filter-status"
>
<span class="filter-indicator text-bold display-none"></span> Status
<svg class="usa-icon top-2px" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="/public/img/sprite.svg#expand_more"></use>
</svg>
</button>
</div>
<div id="filter-status" class="usa-accordion__content usa-prose shadow-1">
<h2>Status</h2>
<fieldset class="usa-fieldset margin-top-0">
<legend class="usa-legend">Select to apply <span class="sr-only">status</span> filter</legend>
<div class="usa-checkbox">
<input
class="usa-checkbox__input"
id="filter-status-started"
type="checkbox"
name="filter-status"
value="started"
/>
<label class="usa-checkbox__label" for="filter-status-started"
>Started</label
>
</div>
<div class="usa-checkbox">
<input
class="usa-checkbox__input"
id="filter-status-submitted"
type="checkbox"
name="filter-status"
value="submitted"
/>
<label class="usa-checkbox__label" for="filter-status-submitted"
>Submitted</label
>
</div>
<div class="usa-checkbox">
<input
class="usa-checkbox__input"
id="filter-status-in-review"
type="checkbox"
name="filter-status"
value="in review"
/>
<label class="usa-checkbox__label" for="filter-status-in-review"
>In review</label
>
</div>
<div class="usa-checkbox">
<input
class="usa-checkbox__input"
id="filter-status-action-needed"
type="checkbox"
name="filter-status"
value="action needed"
/>
<label class="usa-checkbox__label" for="filter-status-action-needed"
>Action needed</label
>
</div>
<div class="usa-checkbox">
<input
class="usa-checkbox__input"
id="filter-status-rejected"
type="checkbox"
name="filter-status"
value="rejected"
/>
<label class="usa-checkbox__label" for="filter-status-rejected"
>Rejected</label
>
</div>
<div class="usa-checkbox">
<input
class="usa-checkbox__input"
id="filter-status-withdrawn"
type="checkbox"
name="filter-status"
value="withdrawn"
/>
<label class="usa-checkbox__label" for="filter-status-withdrawn"
>Withdrawn</label
>
</div>
<div class="usa-checkbox">
<input
class="usa-checkbox__input"
id="filter-status-ineligible"
type="checkbox"
name="filter-status"
value="ineligible"
/>
<label class="usa-checkbox__label" for="filter-status-ineligible"
>Ineligible</label
>
</div>
</fieldset>
</div>
</div>
<button
type="button"
class="usa-button usa-button--small padding--8-12-9-12 usa-button--outline usa-button--filter domain-requests__reset-filters display-none"
>
Clear filters
<svg class="usa-icon top-1px" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="/public/img/sprite.svg#close"></use>
</svg>
</button>
</div>
{% endif %}
<div class="domain-requests__table-wrapper display-none usa-table-container--scrollable margin-top-0" tabindex="0"> <div class="domain-requests__table-wrapper display-none usa-table-container--scrollable margin-top-0" tabindex="0">
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked domain-requests__table"> <table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked domain-requests__table">
<caption class="sr-only">Your domain requests</caption> <caption class="sr-only">Your domain requests</caption>

View file

@ -64,7 +64,7 @@
aria-expanded="false" aria-expanded="false"
aria-controls="filter-status" aria-controls="filter-status"
> >
<span class="domain__filter-indicator text-bold display-none"></span> Status <span class="filter-indicator text-bold display-none"></span> Status
<svg class="usa-icon top-2px" aria-hidden="true" focusable="false" role="img" width="24"> <svg class="usa-icon top-2px" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="/public/img/sprite.svg#expand_more"></use> <use xlink:href="/public/img/sprite.svg#expand_more"></use>
</svg> </svg>

View file

@ -16,7 +16,6 @@
{% if user.is_authenticated %} {% if user.is_authenticated %}
<span class="usa-nav__username ellipsis">{{ user.email }}</span> <span class="usa-nav__username ellipsis">{{ user.email }}</span>
</li> </li>
{% if has_profile_feature_flag %}
<li class="usa-nav__primary-item"> <li class="usa-nav__primary-item">
{% url 'user-profile' as user_profile_url %} {% url 'user-profile' as user_profile_url %}
{% url 'finish-user-profile-setup' as finish_setup_url %} {% url 'finish-user-profile-setup' as finish_setup_url %}
@ -24,7 +23,6 @@
<span class="text-primary">Your profile</span> <span class="text-primary">Your profile</span>
</a> </a>
</li> </li>
{% endif %}
<li class="usa-nav__primary-item"> <li class="usa-nav__primary-item">
<a href="{% url 'logout' %}"><span class="text-primary">Sign out</span></a> <a href="{% url 'logout' %}"><span class="text-primary">Sign out</span></a>
{% else %} {% else %}

View file

@ -18,7 +18,6 @@
{% if user.is_authenticated %} {% if user.is_authenticated %}
<span class="ellipsis usa-nav__username">{{ user.email }}</span> <span class="ellipsis usa-nav__username">{{ user.email }}</span>
</li> </li>
{% if has_profile_feature_flag %}
<li class="usa-nav__secondary-item"> <li class="usa-nav__secondary-item">
{% url 'user-profile' as user_profile_url %} {% url 'user-profile' as user_profile_url %}
{% url 'finish-user-profile-setup' as finish_setup_url %} {% url 'finish-user-profile-setup' as finish_setup_url %}
@ -26,7 +25,6 @@
Your profile Your profile
</a> </a>
</li> </li>
{% endif %}
<li class="usa-nav__secondary-item"> <li class="usa-nav__secondary-item">
<a class="usa-nav-link" href="{% url 'logout' %}">Sign out</a> <a class="usa-nav-link" href="{% url 'logout' %}">Sign out</a>
{% else %} {% else %}

View file

@ -2,9 +2,6 @@
<div class="usa-alert"> <div class="usa-alert">
<div class="usa-alert__body {% if add_body_class %}{{ add_body_class }}{% endif %}"> <div class="usa-alert__body {% if add_body_class %}{{ add_body_class }}{% endif %}">
<b>Attention:</b> You are on a test site. <b>Attention:</b> You are on a test site.
{% if has_profile_feature_flag %}
The profile_feature flag is active.
{% endif %}
</div> </div>
</div> </div>
</div> </div>

View file

@ -535,8 +535,10 @@ class MockDb(TestCase):
first_name = "First" first_name = "First"
last_name = "Last" last_name = "Last"
email = "info@example.com" email = "info@example.com"
title = "title"
phone = "8080102431"
cls.user = get_user_model().objects.create( cls.user = get_user_model().objects.create(
username=username, first_name=first_name, last_name=last_name, email=email username=username, first_name=first_name, last_name=last_name, email=email, title=title, phone=phone
) )
current_date = get_time_aware_date(datetime(2024, 4, 2)) current_date = get_time_aware_date(datetime(2024, 4, 2))
@ -845,6 +847,7 @@ def create_superuser():
last_name="last", last_name="last",
is_staff=True, is_staff=True,
password=p, password=p,
phone="8003111234",
) )
# Retrieve the group or create it if it doesn't exist # Retrieve the group or create it if it doesn't exist
group, _ = UserGroup.objects.get_or_create(name="full_access_group") group, _ = UserGroup.objects.get_or_create(name="full_access_group")
@ -862,7 +865,9 @@ def create_user():
first_name="first", first_name="first",
last_name="last", last_name="last",
is_staff=True, is_staff=True,
title="title",
password=p, password=p,
phone="8003111234",
) )
# Retrieve the group or create it if it doesn't exist # Retrieve the group or create it if it doesn't exist
group, _ = UserGroup.objects.get_or_create(name="cisa_analysts_group") group, _ = UserGroup.objects.get_or_create(name="cisa_analysts_group")
@ -879,7 +884,12 @@ def create_test_user():
phone = "8003111234" phone = "8003111234"
title = "test title" title = "test title"
user = get_user_model().objects.create( user = get_user_model().objects.create(
username=username, first_name=first_name, last_name=last_name, email=email, phone=phone, title=title username=username,
first_name=first_name,
last_name=last_name,
email=email,
phone=phone,
title=title,
) )
return user return user

View file

@ -37,7 +37,6 @@ from .common import (
GenericTestHelper, GenericTestHelper,
) )
from unittest.mock import patch from unittest.mock import patch
from waffle.testutils import override_flag
from django.conf import settings from django.conf import settings
import boto3_mocking # type: ignore import boto3_mocking # type: ignore
@ -957,7 +956,6 @@ class TestDomainRequestAdmin(MockEppLib):
self.transition_state_and_send_email(domain_request, DomainRequest.DomainRequestStatus.SUBMITTED) self.transition_state_and_send_email(domain_request, DomainRequest.DomainRequestStatus.SUBMITTED)
self.assertEqual(len(self.mock_client.EMAILS_SENT), 3) self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
@override_flag("profile_feature", True)
@less_console_noise_decorator @less_console_noise_decorator
def test_save_model_sends_approved_email(self): def test_save_model_sends_approved_email(self):
"""When transitioning to approved on a domain request, """When transitioning to approved on a domain request,

View file

@ -256,19 +256,9 @@ class TestDomainRequest(TestCase):
email_allowed.delete() email_allowed.delete()
@override_flag("profile_feature", active=False)
@less_console_noise_decorator
def test_submit_from_started_sends_email(self):
msg = "Create a domain request and submit it and see if email was sent."
domain_request = completed_domain_request(user=self.dummy_user_2)
self.check_email_sent(
domain_request, msg, "submit", 1, expected_content="Lava", expected_email=self.dummy_user_2.email
)
@override_flag("profile_feature", active=True)
@less_console_noise_decorator @less_console_noise_decorator
def test_submit_from_started_sends_email_to_creator(self): def test_submit_from_started_sends_email_to_creator(self):
"""Tests if, when the profile feature flag is on, we send an email to the creator""" """tests that we send an email to the creator"""
msg = "Create a domain request and submit it and see if email was sent when the feature flag is on." msg = "Create a domain request and submit it and see if email was sent when the feature flag is on."
domain_request = completed_domain_request(user=self.dummy_user_2) domain_request = completed_domain_request(user=self.dummy_user_2)
self.check_email_sent( self.check_email_sent(

View file

@ -494,7 +494,13 @@ class HomeTests(TestWithUser):
phone = "8003111234" phone = "8003111234"
status = User.RESTRICTED status = User.RESTRICTED
restricted_user = get_user_model().objects.create( restricted_user = get_user_model().objects.create(
username=username, first_name=first_name, last_name=last_name, email=email, phone=phone, status=status username=username,
first_name=first_name,
last_name=last_name,
email=email,
phone=phone,
status=status,
title="title",
) )
self.client.force_login(restricted_user) self.client.force_login(restricted_user)
response = self.client.get("/request/", follow=True) response = self.client.get("/request/", follow=True)
@ -546,7 +552,6 @@ class FinishUserProfileTests(TestWithUser, WebTest):
return page.follow() if follow else page return page.follow() if follow else page
@less_console_noise_decorator @less_console_noise_decorator
@override_flag("profile_feature", active=True)
def test_full_name_initial_value(self): def test_full_name_initial_value(self):
"""Test that full_name initial value is empty when first_name or last_name is empty. """Test that full_name initial value is empty when first_name or last_name is empty.
This will later be displayed as "unknown" using javascript.""" This will later be displayed as "unknown" using javascript."""
@ -600,8 +605,8 @@ class FinishUserProfileTests(TestWithUser, WebTest):
incomplete_regular_user.delete() incomplete_regular_user.delete()
@less_console_noise_decorator @less_console_noise_decorator
def test_new_user_with_profile_feature_on(self): def test_new_user(self):
"""Tests that a new user is redirected to the profile setup page when profile_feature is on""" """Tests that a new user is redirected to the profile setup page"""
username_regular_incomplete = "test_regular_user_incomplete" username_regular_incomplete = "test_regular_user_incomplete"
first_name_2 = "Incomplete" first_name_2 = "Incomplete"
email_2 = "unicorn@igorville.com" email_2 = "unicorn@igorville.com"
@ -614,39 +619,37 @@ class FinishUserProfileTests(TestWithUser, WebTest):
) )
self.app.set_user(incomplete_regular_user.username) self.app.set_user(incomplete_regular_user.username)
with override_flag("profile_feature", active=True): # This will redirect the user to the setup page.
# This will redirect the user to the setup page. # Follow implicity checks if our redirect is working.
# Follow implicity checks if our redirect is working. finish_setup_page = self.app.get(reverse("home")).follow()
finish_setup_page = self.app.get(reverse("home")).follow() self._set_session_cookie()
self._set_session_cookie() # Assert that we're on the right page
self.assertContains(finish_setup_page, "Finish setting up your profile")
# Assert that we're on the right page finish_setup_page = self._submit_form_webtest(finish_setup_page.form)
self.assertContains(finish_setup_page, "Finish setting up your profile")
finish_setup_page = self._submit_form_webtest(finish_setup_page.form) self.assertEqual(finish_setup_page.status_code, 200)
self.assertEqual(finish_setup_page.status_code, 200) # We're missing a phone number, so the page should tell us that
self.assertContains(finish_setup_page, "Enter your phone number.")
# We're missing a phone number, so the page should tell us that # Check for the name of the save button
self.assertContains(finish_setup_page, "Enter your phone number.") self.assertContains(finish_setup_page, "user_setup_save_button")
# Check for the name of the save button # Add a phone number
self.assertContains(finish_setup_page, "user_setup_save_button") finish_setup_form = finish_setup_page.form
finish_setup_form["phone"] = "(201) 555-0123"
finish_setup_form["title"] = "CEO"
finish_setup_form["last_name"] = "example"
save_page = self._submit_form_webtest(finish_setup_form, follow=True)
# Add a phone number self.assertEqual(save_page.status_code, 200)
finish_setup_form = finish_setup_page.form self.assertContains(save_page, "Your profile has been updated.")
finish_setup_form["phone"] = "(201) 555-0123"
finish_setup_form["title"] = "CEO"
finish_setup_form["last_name"] = "example"
save_page = self._submit_form_webtest(finish_setup_form, follow=True)
self.assertEqual(save_page.status_code, 200) # Try to navigate back to the home page.
self.assertContains(save_page, "Your profile has been updated.") # This is the same as clicking the back button.
completed_setup_page = self.app.get(reverse("home"))
# Try to navigate back to the home page. self.assertContains(completed_setup_page, "Manage your domain")
# This is the same as clicking the back button.
completed_setup_page = self.app.get(reverse("home"))
self.assertContains(completed_setup_page, "Manage your domain")
incomplete_regular_user.delete() incomplete_regular_user.delete()
@less_console_noise_decorator @less_console_noise_decorator
@ -663,46 +666,45 @@ class FinishUserProfileTests(TestWithUser, WebTest):
verification_type=User.VerificationTypeChoices.REGULAR, verification_type=User.VerificationTypeChoices.REGULAR,
) )
self.app.set_user(incomplete_regular_user.username) self.app.set_user(incomplete_regular_user.username)
with override_flag("profile_feature", active=True): # This will redirect the user to the setup page.
# This will redirect the user to the setup page. # Follow implicity checks if our redirect is working.
# Follow implicity checks if our redirect is working. finish_setup_page = self.app.get(reverse("home")).follow()
finish_setup_page = self.app.get(reverse("home")).follow() self._set_session_cookie()
self._set_session_cookie()
# Assert that we're on the right page # Assert that we're on the right page
self.assertContains(finish_setup_page, "Finish setting up your profile") self.assertContains(finish_setup_page, "Finish setting up your profile")
finish_setup_page = self._submit_form_webtest(finish_setup_page.form) finish_setup_page = self._submit_form_webtest(finish_setup_page.form)
self.assertEqual(finish_setup_page.status_code, 200) self.assertEqual(finish_setup_page.status_code, 200)
# We're missing a phone number, so the page should tell us that # We're missing a phone number, so the page should tell us that
self.assertContains(finish_setup_page, "Enter your phone number.") self.assertContains(finish_setup_page, "Enter your phone number.")
# Check for the name of the save button # Check for the name of the save button
self.assertContains(finish_setup_page, "user_setup_save_button") self.assertContains(finish_setup_page, "user_setup_save_button")
# Add a phone number # Add a phone number
finish_setup_form = finish_setup_page.form finish_setup_form = finish_setup_page.form
finish_setup_form["first_name"] = "test" finish_setup_form["first_name"] = "test"
finish_setup_form["last_name"] = "test2" finish_setup_form["last_name"] = "test2"
finish_setup_form["phone"] = "(201) 555-0123" finish_setup_form["phone"] = "(201) 555-0123"
finish_setup_form["title"] = "CEO" finish_setup_form["title"] = "CEO"
finish_setup_form["last_name"] = "example" finish_setup_form["last_name"] = "example"
save_page = self._submit_form_webtest(finish_setup_form, follow=True) save_page = self._submit_form_webtest(finish_setup_form, follow=True)
self.assertEqual(save_page.status_code, 200) self.assertEqual(save_page.status_code, 200)
self.assertContains(save_page, "Your profile has been updated.") self.assertContains(save_page, "Your profile has been updated.")
# Try to navigate back to the home page. # Try to navigate back to the home page.
# This is the same as clicking the back button. # This is the same as clicking the back button.
completed_setup_page = self.app.get(reverse("home")) completed_setup_page = self.app.get(reverse("home"))
self.assertContains(completed_setup_page, "Manage your domain") self.assertContains(completed_setup_page, "Manage your domain")
incomplete_regular_user.delete() incomplete_regular_user.delete()
@less_console_noise_decorator @less_console_noise_decorator
def test_new_user_goes_to_domain_request_with_profile_feature_on(self): def test_new_user_goes_to_domain_request(self):
"""Tests that a new user is redirected to the domain request page when profile_feature is on""" """Tests that a new user is redirected to the domain request page"""
username_regular_incomplete = "test_regular_user_incomplete" username_regular_incomplete = "test_regular_user_incomplete"
first_name_2 = "Incomplete" first_name_2 = "Incomplete"
email_2 = "unicorn@igorville.com" email_2 = "unicorn@igorville.com"
@ -714,7 +716,7 @@ class FinishUserProfileTests(TestWithUser, WebTest):
verification_type=User.VerificationTypeChoices.REGULAR, verification_type=User.VerificationTypeChoices.REGULAR,
) )
self.app.set_user(incomplete_regular_user.username) self.app.set_user(incomplete_regular_user.username)
with override_flag("profile_feature", active=True): with override_flag("", active=True):
# This will redirect the user to the setup page # This will redirect the user to the setup page
finish_setup_page = self.app.get(reverse("domain-request:")).follow() finish_setup_page = self.app.get(reverse("domain-request:")).follow()
self._set_session_cookie() self._set_session_cookie()
@ -758,25 +760,6 @@ class FinishUserProfileTests(TestWithUser, WebTest):
self.assertContains(completed_setup_page, "Youre about to start your .gov domain request") self.assertContains(completed_setup_page, "Youre about to start your .gov domain request")
incomplete_regular_user.delete() incomplete_regular_user.delete()
@less_console_noise_decorator
def test_new_user_with_profile_feature_off(self):
"""Tests that a new user is not redirected to the profile setup page when profile_feature is off"""
with override_flag("profile_feature", active=False):
response = self.client.get("/")
self.assertNotContains(response, "Finish setting up your profile")
@less_console_noise_decorator
def test_new_user_goes_to_domain_request_with_profile_feature_off(self):
"""Tests that a new user is redirected to the domain request page
when profile_feature is off but not the setup page"""
with override_flag("profile_feature", active=False):
response = self.client.get("/request/")
self.assertNotContains(response, "Finish setting up your profile")
self.assertNotContains(response, "What contact information should we use to reach you?")
self.assertContains(response, "Youre about to start your .gov domain request")
class FinishUserProfileForOtherUsersTests(TestWithUser, WebTest): class FinishUserProfileForOtherUsersTests(TestWithUser, WebTest):
"""A series of tests that target the user profile page intercept for incomplete IAL1 user profiles.""" """A series of tests that target the user profile page intercept for incomplete IAL1 user profiles."""
@ -816,8 +799,8 @@ class FinishUserProfileForOtherUsersTests(TestWithUser, WebTest):
return page.follow() if follow else page return page.follow() if follow else page
@less_console_noise_decorator @less_console_noise_decorator
def test_new_user_with_profile_feature_on(self): def test_new_user(self):
"""Tests that a new user is redirected to the profile setup page when profile_feature is on, """Tests that a new user is redirected to the profile setup page,
and testing that the confirmation modal is present""" and testing that the confirmation modal is present"""
username_other_incomplete = "test_other_user_incomplete" username_other_incomplete = "test_other_user_incomplete"
first_name_2 = "Incomplete" first_name_2 = "Incomplete"
@ -831,66 +814,63 @@ class FinishUserProfileForOtherUsersTests(TestWithUser, WebTest):
verification_type=User.VerificationTypeChoices.VERIFIED_BY_STAFF, verification_type=User.VerificationTypeChoices.VERIFIED_BY_STAFF,
) )
self.app.set_user(incomplete_other_user.username) self.app.set_user(incomplete_other_user.username)
with override_flag("profile_feature", active=True): # This will redirect the user to the user profile page.
# This will redirect the user to the user profile page. # Follow implicity checks if our redirect is working.
# Follow implicity checks if our redirect is working. user_profile_page = self.app.get(reverse("home")).follow()
user_profile_page = self.app.get(reverse("home")).follow() self._set_session_cookie()
self._set_session_cookie()
# Assert that we're on the right page by testing for the modal # Assert that we're on the right page by testing for the modal
self.assertContains(user_profile_page, "domain registrants must maintain accurate contact information") self.assertContains(user_profile_page, "domain registrants must maintain accurate contact information")
user_profile_page = self._submit_form_webtest(user_profile_page.form) user_profile_page = self._submit_form_webtest(user_profile_page.form)
self.assertEqual(user_profile_page.status_code, 200) self.assertEqual(user_profile_page.status_code, 200)
# Assert that modal does not appear on subsequent submits # Assert that modal does not appear on subsequent submits
self.assertNotContains(user_profile_page, "domain registrants must maintain accurate contact information") self.assertNotContains(user_profile_page, "domain registrants must maintain accurate contact information")
# Assert that unique error message appears by testing the message in a specific div # Assert that unique error message appears by testing the message in a specific div
html_content = user_profile_page.content.decode("utf-8") html_content = user_profile_page.content.decode("utf-8")
# Normalize spaces and line breaks in the HTML content # Normalize spaces and line breaks in the HTML content
normalized_html_content = " ".join(html_content.split()) normalized_html_content = " ".join(html_content.split())
# Expected string without extra spaces and line breaks # Expected string without extra spaces and line breaks
expected_string = "Before you can manage your domain, we need you to add contact information." expected_string = "Before you can manage your domain, we need you to add contact information."
# Check for the presence of the <div> element with the specific text # Check for the presence of the <div> element with the specific text
self.assertIn(f'<div class="usa-alert__body"> {expected_string} </div>', normalized_html_content) self.assertIn(f'<div class="usa-alert__body"> {expected_string} </div>', normalized_html_content)
# We're missing a phone number, so the page should tell us that # We're missing a phone number, so the page should tell us that
self.assertContains(user_profile_page, "Enter your phone number.") self.assertContains(user_profile_page, "Enter your phone number.")
# We need to assert that links to manage your domain are not present (in both body and footer) # We need to assert that links to manage your domain are not present (in both body and footer)
self.assertNotContains(user_profile_page, "Manage your domains") self.assertNotContains(user_profile_page, "Manage your domains")
# Assert the tooltip on the logo, indicating that the logo is not clickable # Assert the tooltip on the logo, indicating that the logo is not clickable
self.assertContains( self.assertContains(
user_profile_page, 'title="Before you can manage your domains, we need you to add contact information."' user_profile_page, 'title="Before you can manage your domains, we need you to add contact information."'
) )
# Assert that modal does not appear on subsequent submits # Assert that modal does not appear on subsequent submits
self.assertNotContains(user_profile_page, "domain registrants must maintain accurate contact information") self.assertNotContains(user_profile_page, "domain registrants must maintain accurate contact information")
# Add a phone number # Add a phone number
finish_setup_form = user_profile_page.form finish_setup_form = user_profile_page.form
finish_setup_form["phone"] = "(201) 555-0123" finish_setup_form["phone"] = "(201) 555-0123"
finish_setup_form["title"] = "CEO" finish_setup_form["title"] = "CEO"
finish_setup_form["last_name"] = "example" finish_setup_form["last_name"] = "example"
save_page = self._submit_form_webtest(finish_setup_form, follow=True) save_page = self._submit_form_webtest(finish_setup_form, follow=True)
self.assertEqual(save_page.status_code, 200) self.assertEqual(save_page.status_code, 200)
self.assertContains(save_page, "Your profile has been updated.") self.assertContains(save_page, "Your profile has been updated.")
# We need to assert that logo is not clickable and links to manage your domain are not present # We need to assert that logo is not clickable and links to manage your domain are not present
# NOTE: "anage" is not a typo. It is to accomodate the fact that the "m" is uppercase in one # NOTE: "anage" is not a typo. It is to accomodate the fact that the "m" is uppercase in one
# instance and lowercase in the other. # instance and lowercase in the other.
self.assertContains(save_page, "anage your domains", count=2) self.assertContains(save_page, "anage your domains", count=2)
self.assertNotContains( self.assertNotContains(save_page, "Before you can manage your domains, we need you to add contact information")
save_page, "Before you can manage your domains, we need you to add contact information" # Assert that modal does not appear on subsequent submits
) self.assertNotContains(save_page, "domain registrants must maintain accurate contact information")
# Assert that modal does not appear on subsequent submits
self.assertNotContains(save_page, "domain registrants must maintain accurate contact information")
# Try to navigate back to the home page. # Try to navigate back to the home page.
# This is the same as clicking the back button. # This is the same as clicking the back button.
completed_setup_page = self.app.get(reverse("home")) completed_setup_page = self.app.get(reverse("home"))
self.assertContains(completed_setup_page, "Manage your domain") self.assertContains(completed_setup_page, "Manage your domain")
class UserProfileTests(TestWithUser, WebTest): class UserProfileTests(TestWithUser, WebTest):
@ -915,113 +895,59 @@ class UserProfileTests(TestWithUser, WebTest):
DomainInformation.objects.all().delete() DomainInformation.objects.all().delete()
@less_console_noise_decorator @less_console_noise_decorator
def error_500_main_nav_with_profile_feature_turned_on(self): def error_500_main_nav(self):
"""test that Your profile is in main nav of 500 error page when profile_feature is on. """test that Your profile is in main nav of 500 error page.
Our treatment of 401 and 403 error page handling with that waffle feature is similar, so we Our treatment of 401 and 403 error page handling with that waffle feature is similar, so we
assume that the same test results hold true for 401 and 403.""" assume that the same test results hold true for 401 and 403."""
with override_flag("profile_feature", active=True): with self.assertRaises(Exception):
with self.assertRaises(Exception): response = self.client.get(reverse("home"), follow=True)
response = self.client.get(reverse("home"), follow=True) self.assertEqual(response.status_code, 500)
self.assertEqual(response.status_code, 500) self.assertContains(response, "Your profile")
self.assertContains(response, "Your profile")
@less_console_noise_decorator @less_console_noise_decorator
def error_500_main_nav_with_profile_feature_turned_off(self): def test_home_page_main_nav(self):
"""test that Your profile is not in main nav of 500 error page when profile_feature is off. """test that Your profile is in main nav of home page"""
response = self.client.get("/", follow=True)
Our treatment of 401 and 403 error page handling with that waffle feature is similar, so we
assume that the same test results hold true for 401 and 403."""
with override_flag("profile_feature", active=False):
with self.assertRaises(Exception):
response = self.client.get(reverse("home"), follow=True)
self.assertEqual(response.status_code, 500)
self.assertNotContains(response, "Your profile")
@less_console_noise_decorator
def test_home_page_main_nav_with_profile_feature_on(self):
"""test that Your profile is in main nav of home page when profile_feature is on"""
with override_flag("profile_feature", active=True):
response = self.client.get("/", follow=True)
self.assertContains(response, "Your profile") self.assertContains(response, "Your profile")
@less_console_noise_decorator @less_console_noise_decorator
def test_home_page_main_nav_with_profile_feature_off(self): def test_new_request_main_nav(self):
"""test that Your profile is not in main nav of home page when profile_feature is off""" """test that Your profile is in main nav of new request"""
with override_flag("profile_feature", active=False): response = self.client.get("/request/", follow=True)
response = self.client.get("/", follow=True)
self.assertNotContains(response, "Your profile")
@less_console_noise_decorator
def test_new_request_main_nav_with_profile_feature_on(self):
"""test that Your profile is in main nav of new request when profile_feature is on"""
with override_flag("profile_feature", active=True):
response = self.client.get("/request/", follow=True)
self.assertContains(response, "Your profile") self.assertContains(response, "Your profile")
@less_console_noise_decorator @less_console_noise_decorator
def test_new_request_main_nav_with_profile_feature_off(self): def test_user_profile_main_nav(self):
"""test that Your profile is not in main nav of new request when profile_feature is off""" """test that Your profile is in main nav of user profile"""
with override_flag("profile_feature", active=False): response = self.client.get("/user-profile", follow=True)
response = self.client.get("/request/", follow=True)
self.assertNotContains(response, "Your profile")
@less_console_noise_decorator
def test_user_profile_main_nav_with_profile_feature_on(self):
"""test that Your profile is in main nav of user profile when profile_feature is on"""
with override_flag("profile_feature", active=True):
response = self.client.get("/user-profile", follow=True)
self.assertContains(response, "Your profile") self.assertContains(response, "Your profile")
@less_console_noise_decorator
def test_user_profile_returns_404_when_feature_off(self):
"""test that Your profile returns 404 when profile_feature is off"""
with override_flag("profile_feature", active=False):
response = self.client.get("/user-profile", follow=True)
self.assertEqual(response.status_code, 404)
@less_console_noise_decorator @less_console_noise_decorator
def test_user_profile_back_button_when_coming_from_domain_request(self): def test_user_profile_back_button_when_coming_from_domain_request(self):
"""tests user profile when profile_feature is on, """tests user profile,
and when they are redirected from the domain request page""" and when they are redirected from the domain request page"""
with override_flag("profile_feature", active=True): response = self.client.get("/user-profile?redirect=domain-request:")
response = self.client.get("/user-profile?redirect=domain-request:")
self.assertContains(response, "Your profile") self.assertContains(response, "Your profile")
self.assertContains(response, "Go back to your domain request") self.assertContains(response, "Go back to your domain request")
self.assertNotContains(response, "Back to manage your domains") self.assertNotContains(response, "Back to manage your domains")
@less_console_noise_decorator @less_console_noise_decorator
def test_domain_detail_profile_feature_on(self): def test_domain_detail_contains_your_profile(self):
"""test that domain detail view when profile_feature is on""" """Tests that the domain detail view contains 'your profile' rather than 'your contact information'"""
with override_flag("profile_feature", active=True): response = self.client.get(reverse("domain", args=[self.domain.pk]))
response = self.client.get(reverse("domain", args=[self.domain.pk]))
self.assertContains(response, "Your profile") self.assertContains(response, "Your profile")
self.assertNotContains(response, "Your contact information") self.assertNotContains(response, "Your contact information")
@less_console_noise_decorator @less_console_noise_decorator
def test_request_when_profile_feature_on(self): def test_domain_your_contact_information(self):
"""test that Your profile is in request page when profile feature is on""" """test that your contact information is not accessible"""
response = self.client.get(f"/domain/{self.domain.id}/your-contact-information", follow=True)
contact_user, _ = Contact.objects.get_or_create( self.assertEqual(response.status_code, 404)
first_name="Hank",
last_name="McFakerson",
)
site = DraftDomain.objects.create(name="igorville.gov")
domain_request = DomainRequest.objects.create(
creator=self.user,
requested_domain=site,
status=DomainRequest.DomainRequestStatus.SUBMITTED,
senior_official=contact_user,
)
with override_flag("profile_feature", active=True):
response = self.client.get(f"/domain-request/{domain_request.id}", follow=True)
self.assertContains(response, "Your profile")
response = self.client.get(f"/domain-request/{domain_request.id}/withdraw", follow=True)
self.assertContains(response, "Your profile")
@less_console_noise_decorator @less_console_noise_decorator
def test_request_when_profile_feature_off(self): def test_profile_request_page(self):
"""test that Your profile is not in request page when profile feature is off""" """test that your profile is in request"""
contact_user, _ = Contact.objects.get_or_create( contact_user, _ = Contact.objects.get_or_create(
first_name="Hank", first_name="Hank",
@ -1034,31 +960,27 @@ class UserProfileTests(TestWithUser, WebTest):
status=DomainRequest.DomainRequestStatus.SUBMITTED, status=DomainRequest.DomainRequestStatus.SUBMITTED,
senior_official=contact_user, senior_official=contact_user,
) )
with override_flag("profile_feature", active=False):
response = self.client.get(f"/domain-request/{domain_request.id}", follow=True) response = self.client.get(f"/domain-request/{domain_request.id}", follow=True)
self.assertNotContains(response, "Your profile") self.assertContains(response, "Your profile")
response = self.client.get(f"/domain-request/{domain_request.id}/withdraw", follow=True) response = self.client.get(f"/domain-request/{domain_request.id}/withdraw", follow=True)
self.assertNotContains(response, "Your profile") self.assertContains(response, "Your profile")
# cleanup
domain_request.delete()
site.delete()
@less_console_noise_decorator @less_console_noise_decorator
def test_user_profile_form_submission(self): def test_user_profile_form_submission(self):
"""test user profile form submission""" """test user profile form submission"""
self.app.set_user(self.user.username) self.app.set_user(self.user.username)
with override_flag("profile_feature", active=True): profile_page = self.app.get(reverse("user-profile"))
profile_page = self.app.get(reverse("user-profile")) session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) profile_form = profile_page.form
profile_form = profile_page.form profile_form["title"] = "sample title"
profile_form["title"] = "sample title" profile_form["phone"] = "(201) 555-1212"
profile_form["phone"] = "(201) 555-1212" profile_page = profile_form.submit()
profile_page = profile_form.submit() self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) profile_page = profile_page.follow()
profile_page = profile_page.follow() self.assertEqual(profile_page.status_code, 200)
self.assertEqual(profile_page.status_code, 200) self.assertContains(profile_page, "Your profile has been updated")
self.assertContains(profile_page, "Your profile has been updated")
class PortfoliosTests(TestWithUser, WebTest): class PortfoliosTests(TestWithUser, WebTest):

View file

@ -723,7 +723,7 @@ class TestDomainManagers(TestDomainOverview):
email_address = "mayor@igorville.gov" email_address = "mayor@igorville.gov"
invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address) invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address)
other_user = User() other_user = create_user()
other_user.save() other_user.save()
self.client.force_login(other_user) self.client.force_login(other_user)
mock_client = MagicMock() mock_client = MagicMock()
@ -737,6 +737,12 @@ class TestDomainManagers(TestDomainOverview):
def test_domain_invitation_flow(self): def test_domain_invitation_flow(self):
"""Send an invitation to a new user, log in and load the dashboard.""" """Send an invitation to a new user, log in and load the dashboard."""
email_address = "mayor@igorville.gov" email_address = "mayor@igorville.gov"
username = "mayor"
first_name = "First"
last_name = "Last"
title = "title"
phone = "8080102431"
title = "title"
User.objects.filter(email=email_address).delete() User.objects.filter(email=email_address).delete()
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
@ -752,7 +758,9 @@ class TestDomainManagers(TestDomainOverview):
add_page.form.submit() add_page.form.submit()
# user was invited, create them # user was invited, create them
new_user = User.objects.create(username=email_address, email=email_address) new_user = User.objects.create(
username=username, email=email_address, first_name=first_name, last_name=last_name, title=title, phone=phone
)
# log them in to `self.app` # log them in to `self.app`
self.app.set_user(new_user.username) self.app.set_user(new_user.username)
# and manually call the on each login callback # and manually call the on each login callback

View file

@ -2247,7 +2247,6 @@ class DomainRequestTests(TestWithUser, WebTest):
senior_official = domain_request.senior_official senior_official = domain_request.senior_official
self.assertEquals("Testy2", senior_official.first_name) self.assertEquals("Testy2", senior_official.first_name)
@override_flag("profile_feature", active=True)
@less_console_noise_decorator @less_console_noise_decorator
def test_edit_creator_in_place(self): def test_edit_creator_in_place(self):
"""When you: """When you:

View file

@ -458,3 +458,81 @@ class GetRequestsJsonTest(TestWithUser, WebTest):
# Ensure no approved requests are included # Ensure no approved requests are included
for domain_request in data["domain_requests"]: for domain_request in data["domain_requests"]:
self.assertNotEqual(domain_request["status"], DomainRequest.DomainRequestStatus.APPROVED) self.assertNotEqual(domain_request["status"], DomainRequest.DomainRequestStatus.APPROVED)
def test_search(self):
"""Tests our search functionality. We expect that search filters on creator only when we are in a portfolio"""
# Test search for domain name
response = self.app.get(reverse("get_domain_requests_json"), {"search_term": "lamb"})
self.assertEqual(response.status_code, 200)
data = response.json
self.assertEqual(len(data["domain_requests"]), 1)
requested_domain = data["domain_requests"][0]["requested_domain"]
self.assertEqual(requested_domain, "lamb-chops.gov")
# Test search for 'New domain request'
response = self.app.get(reverse("get_domain_requests_json"), {"search_term": "new domain"})
self.assertEqual(response.status_code, 200)
data = response.json
self.assertTrue(any(req["requested_domain"] is None for req in data["domain_requests"]))
# Test search with portfolio (including creator search)
self.client.force_login(self.user)
with override_flag("organization_feature", active=True), override_flag("organization_requests", active=True):
user_perm, _ = UserPortfolioPermission.objects.get_or_create(
user=self.user,
portfolio=self.portfolio,
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
)
response = self.app.get(
reverse("get_domain_requests_json"), {"search_term": "info", "portfolio": self.portfolio.id}
)
self.assertEqual(response.status_code, 200)
data = response.json
self.assertTrue(any(req["creator"].startswith("info") for req in data["domain_requests"]))
# Test search without portfolio (should not search on creator)
with override_flag("organization_feature", active=False), override_flag("organization_requests", active=False):
user_perm.delete()
response = self.app.get(reverse("get_domain_requests_json"), {"search_term": "info"})
self.assertEqual(response.status_code, 200)
data = response.json
self.assertEqual(len(data["domain_requests"]), 0)
@override_flag("organization_feature", active=True)
@override_flag("organization_requests", active=True)
def test_status_filter(self):
"""Test that status filtering works properly"""
# Test a single status
response = self.app.get(reverse("get_domain_requests_json"), {"status": "started"})
self.assertEqual(response.status_code, 200)
data = response.json
self.assertTrue(all(req["status"] == "Started" for req in data["domain_requests"]))
# Test an invalid status
response = self.app.get(reverse("get_domain_requests_json"), {"status": "approved"})
self.assertEqual(response.status_code, 200)
data = response.json
self.assertEqual(len(data["domain_requests"]), 0)
@override_flag("organization_feature", active=True)
@override_flag("organization_requests", active=True)
def test_combined_filtering_and_sorting(self):
"""Test that combining filters and sorting works properly"""
user_perm, _ = UserPortfolioPermission.objects.get_or_create(
user=self.user,
portfolio=self.portfolio,
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
)
self.client.force_login(self.user)
response = self.app.get(
reverse("get_domain_requests_json"),
{"search_term": "beef", "status": "started", "portfolio": self.portfolio.id},
)
self.assertEqual(response.status_code, 200)
data = response.json
self.assertTrue(all("beef" in req["requested_domain"] for req in data["domain_requests"]))
self.assertTrue(all(req["status"] == "Started" for req in data["domain_requests"]))
created_at_dates = [req["created_at"] for req in data["domain_requests"]]
self.assertEqual(created_at_dates, sorted(created_at_dates, reverse=True))
user_perm.delete()

View file

@ -20,6 +20,7 @@ def get_domain_requests_json(request):
unfiltered_total = objects.count() unfiltered_total = objects.count()
objects = apply_search(objects, request) objects = apply_search(objects, request)
objects = apply_status_filter(objects, request)
objects = apply_sorting(objects, request) objects = apply_sorting(objects, request)
paginator = Paginator(objects, 10) paginator = Paginator(objects, 10)
@ -63,6 +64,7 @@ def get_domain_request_ids_from_request(request):
def apply_search(queryset, request): def apply_search(queryset, request):
search_term = request.GET.get("search_term") search_term = request.GET.get("search_term")
is_portfolio = request.GET.get("portfolio")
if search_term: if search_term:
search_term_lower = search_term.lower() search_term_lower = search_term.lower()
@ -75,11 +77,34 @@ def apply_search(queryset, request):
queryset = queryset.filter( queryset = queryset.filter(
Q(requested_domain__name__icontains=search_term) | Q(requested_domain__isnull=True) Q(requested_domain__name__icontains=search_term) | Q(requested_domain__isnull=True)
) )
elif is_portfolio:
queryset = queryset.filter(
Q(requested_domain__name__icontains=search_term)
| Q(creator__first_name__icontains=search_term)
| Q(creator__last_name__icontains=search_term)
| Q(creator__email__icontains=search_term)
)
# For non org users
else: else:
queryset = queryset.filter(Q(requested_domain__name__icontains=search_term)) queryset = queryset.filter(Q(requested_domain__name__icontains=search_term))
return queryset return queryset
def apply_status_filter(queryset, request):
status_param = request.GET.get("status")
if status_param:
status_list = status_param.split(",")
statuses = [status for status in status_list if status in DomainRequest.DomainRequestStatus.values]
# Construct Q objects for statuses that can be queried through ORM
status_query = Q()
if statuses:
status_query |= Q(status__in=statuses)
# Apply the combined query
queryset = queryset.filter(status_query)
return queryset
def apply_sorting(queryset, request): def apply_sorting(queryset, request):
sort_by = request.GET.get("sort_by", "id") # Default to 'id' sort_by = request.GET.get("sort_by", "id") # Default to 'id'
order = request.GET.get("order", "asc") # Default to 'asc' order = request.GET.get("order", "asc") # Default to 'asc'

View file

@ -11,7 +11,6 @@ from django.urls import NoReverseMatch, reverse
from registrar.models.user import User from registrar.models.user import User
from registrar.models.utility.generic_helper import replace_url_queryparams from registrar.models.utility.generic_helper import replace_url_queryparams
from registrar.views.utility.permission_views import UserProfilePermissionView from registrar.views.utility.permission_views import UserProfilePermissionView
from waffle.decorators import waffle_flag
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -46,7 +45,6 @@ class UserProfileView(UserProfilePermissionView, FormMixin):
return self.render_to_response(context) return self.render_to_response(context)
@waffle_flag("profile_feature") # type: ignore
def dispatch(self, request, *args, **kwargs): # type: ignore def dispatch(self, request, *args, **kwargs): # type: ignore
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)