lint and unit tests

This commit is contained in:
zandercymatics 2025-03-26 10:54:56 -06:00
parent 91db8d8d50
commit 85666d2e7d
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7
3 changed files with 68 additions and 17 deletions

View file

@ -211,8 +211,12 @@ export function initPortfolioMemberPage() {
); );
} }
// Init the "edit self" warning modal // Init the "edit self" warning modal, which triggers when the user is trying to edit themselves.
if (memberForm && editSelfWarningModal) { // The dom will include these elements when this occurs.
// NOTE: This logic does not trigger when the user is the ONLY admin in the portfolio.
// This is because info alerts are used rather than modals in this case.
if (memberForm && editSelfWarningModal && editSelfWarningModalConfirm) {
// Only show the warning modal when the user is changing their ROLE.
var canSubmit = document.querySelector(`input[name="role"]:checked`)?.value != "organization_member"; var canSubmit = document.querySelector(`input[name="role"]:checked`)?.value != "organization_member";
let radioButtons = document.querySelectorAll(`input[name="role"]`); let radioButtons = document.querySelectorAll(`input[name="role"]`);
radioButtons.forEach(function (radioButton) { radioButtons.forEach(function (radioButton) {
@ -221,21 +225,20 @@ export function initPortfolioMemberPage() {
canSubmit = selectedValue != "organization_member"; canSubmit = selectedValue != "organization_member";
}); });
}); });
// Prevent form submission assuming org member is selected for role, and open the modal.
memberForm.addEventListener("submit", function(e) { memberForm.addEventListener("submit", function(e) {
if (!canSubmit) { if (!canSubmit) {
e.preventDefault(); e.preventDefault();
editSelfWarningModal.click(); editSelfWarningModal.click();
} }
}); });
if (editSelfWarningModalConfirm) { // Hook the confirm button on the modal to form submission.
editSelfWarningModalConfirm.addEventListener("click", function() { editSelfWarningModalConfirm.addEventListener("click", function() {
canSubmit = true; canSubmit = true;
memberForm.submit(); memberForm.submit();
}); });
}
} }
}); });

View file

@ -413,7 +413,7 @@ class PortfolioMemberForm(BasePortfolioMemberForm):
def clean(self): def clean(self):
""" """
Override of clean to ensure that the user isn't removing themselves Override of clean to ensure that the user isn't removing themselves
if they're the only portfolio admin if they're the only portfolio admin
""" """
super().clean() super().clean()
@ -421,11 +421,15 @@ class PortfolioMemberForm(BasePortfolioMemberForm):
if role and self.instance.user.is_only_admin_of_portfolio(self.instance.portfolio): if role and self.instance.user.is_only_admin_of_portfolio(self.instance.portfolio):
# This is how you associate a validation error to a particular field. # This is how you associate a validation error to a particular field.
# The alternative is to do this in clean_role, but execution order matters. # The alternative is to do this in clean_role, but execution order matters.
raise forms.ValidationError({"role": forms.ValidationError( raise forms.ValidationError(
"You can't change your member access because you're " {
"the only admin for this organization. " "role": forms.ValidationError(
"To change your access, you'll need to add another admin." "You can't change your member access because you're "
)}) "the only admin for this organization. "
"To change your access, you'll need to add another admin."
)
}
)
class PortfolioInvitedMemberForm(BasePortfolioMemberForm): class PortfolioInvitedMemberForm(BasePortfolioMemberForm):

View file

@ -4639,6 +4639,17 @@ class TestPortfolioMemberEditView(WebTest):
# Get the user's admin permission # Get the user's admin permission
admin_permission = UserPortfolioPermission.objects.get(user=self.user, portfolio=self.portfolio) admin_permission = UserPortfolioPermission.objects.get(user=self.user, portfolio=self.portfolio)
# Create a second permission so the user isn't just deleting themselves
member = create_test_user()
UserPortfolioPermission.objects.create(
user=member, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
# First, verify that the change modal is on the page
response = self.client.get(reverse("member-permissions", kwargs={"member_pk": admin_permission.id}))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Yes, change my access")
response = self.client.post( response = self.client.post(
reverse("member-permissions", kwargs={"member_pk": admin_permission.id}), reverse("member-permissions", kwargs={"member_pk": admin_permission.id}),
{ {
@ -4652,6 +4663,39 @@ class TestPortfolioMemberEditView(WebTest):
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(response["Location"], reverse("home")) self.assertEqual(response["Location"], reverse("home"))
@less_console_noise_decorator
@override_flag("organization_feature", active=True)
@override_flag("organization_members", active=True)
def test_admin_removing_own_admin_role_only_admin(self):
"""Tests that admin removing their own admin role when they are the only admin
throws a validation error.
"""
self.client.force_login(self.user)
# Get the user's admin permission
admin_permission = UserPortfolioPermission.objects.get(user=self.user, portfolio=self.portfolio)
# First, verify that the info alert is present on the page
response = self.client.get(reverse("member-permissions", kwargs={"member_pk": admin_permission.id}))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "To remove yourself or change your member access")
# Then, verify that the right form error is shown
response = self.client.post(
reverse("member-permissions", kwargs={"member_pk": admin_permission.id}),
{
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
"member_permissions": "no_access",
"domain_request_permissions": "no_access",
},
)
self.assertEqual(response.status_code, 200)
error_message = "the only admin for this organization"
self.assertIn(error_message, str(response.context["form"].errors))
class TestPortfolioInvitedMemberEditView(WebTest): class TestPortfolioInvitedMemberEditView(WebTest):
"""Tests for the edit invited member page on portfolios""" """Tests for the edit invited member page on portfolios"""