mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-17 23:14:11 +02:00
Merge branch 'za/1852-user-contact-info-inline' into za/1848-copy-contact-email-to-clipboard
This commit is contained in:
commit
e7edf19897
11 changed files with 459 additions and 183 deletions
|
@ -204,6 +204,16 @@ from .common import less_console_noise
|
||||||
# <test code goes here>
|
# <test code goes here>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or alternatively, if you prefer using a decorator, just use:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from .common import less_console_noise_decorator
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def some_function():
|
||||||
|
# <test code goes here>
|
||||||
|
```
|
||||||
|
|
||||||
### Accessibility Testing in the browser
|
### Accessibility Testing in the browser
|
||||||
|
|
||||||
We use the [ANDI](https://www.ssa.gov/accessibility/andi/help/install.html) browser extension
|
We use the [ANDI](https://www.ssa.gov/accessibility/andi/help/install.html) browser extension
|
||||||
|
|
|
@ -229,6 +229,32 @@ function checkToListThenInitWidget(toListId, attempts) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function addUrlsToOtherContacts() {
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Select all table rows that have a data-contact-id attribute
|
||||||
|
let contactRows = document.querySelectorAll("tr[data-contact-url]");
|
||||||
|
|
||||||
|
if (contactRows){
|
||||||
|
// Add a click event listener to each row
|
||||||
|
let index = 1;
|
||||||
|
contactRows.forEach(function(row) {
|
||||||
|
let otherContactUrl = row.getAttribute("data-contact-url");
|
||||||
|
if (otherContactUrl){
|
||||||
|
let otherContact = document.querySelector(`.other-contact__${index}`);
|
||||||
|
if (otherContact) {
|
||||||
|
otherContact.href = otherContactUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addUrlsToOtherContacts()
|
||||||
|
})();
|
||||||
|
|
||||||
// Initialize the widget:
|
// Initialize the widget:
|
||||||
// add related buttons to the widget for edit, delete and view
|
// add related buttons to the widget for edit, delete and view
|
||||||
// add event listeners on the from list, the to list, and selector buttons which either enable or disable the related buttons
|
// add event listeners on the from list, the to list, and selector buttons which either enable or disable the related buttons
|
||||||
|
|
|
@ -54,17 +54,17 @@
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
createComparativeColumnChart("myChart1", "Managed domains", "Start Date", "End Date");
|
|
||||||
createComparativeColumnChart("myChart2", "Unmanaged domains", "Start Date", "End Date");
|
|
||||||
createComparativeColumnChart("myChart3", "Deleted domains", "Start Date", "End Date");
|
|
||||||
createComparativeColumnChart("myChart4", "Ready domains", "Start Date", "End Date");
|
|
||||||
createComparativeColumnChart("myChart5", "Submitted requests", "Start Date", "End Date");
|
|
||||||
createComparativeColumnChart("myChart6", "All requests", "Start Date", "End Date");
|
|
||||||
});
|
|
||||||
|
|
||||||
function createComparativeColumnChart(canvasId, title, labelOne, labelTwo) {
|
/** An IIFE to initialize the analytics page
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
function createComparativeColumnChart(canvasId, title, labelOne, labelTwo) {
|
||||||
var canvas = document.getElementById(canvasId);
|
var canvas = document.getElementById(canvasId);
|
||||||
|
if (!canvas) {
|
||||||
|
console.log("Could not find canvas")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var ctx = canvas.getContext("2d");
|
var ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
var listOne = JSON.parse(canvas.getAttribute('data-list-one'));
|
var listOne = JSON.parse(canvas.getAttribute('data-list-one'));
|
||||||
|
@ -114,4 +114,18 @@ function createComparativeColumnChart(canvasId, title, labelOne, labelTwo) {
|
||||||
data: data,
|
data: data,
|
||||||
options: options,
|
options: options,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initComparativeColumnCharts() {
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
createComparativeColumnChart("myChart1", "Managed domains", "Start Date", "End Date");
|
||||||
|
createComparativeColumnChart("myChart2", "Unmanaged domains", "Start Date", "End Date");
|
||||||
|
createComparativeColumnChart("myChart3", "Deleted domains", "Start Date", "End Date");
|
||||||
|
createComparativeColumnChart("myChart4", "Ready domains", "Start Date", "End Date");
|
||||||
|
createComparativeColumnChart("myChart5", "Submitted requests", "Start Date", "End Date");
|
||||||
|
createComparativeColumnChart("myChart6", "All requests", "Start Date", "End Date");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
initComparativeColumnCharts();
|
||||||
|
})();
|
||||||
|
|
|
@ -361,6 +361,13 @@ details.dja-detail-table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.dja-user-detail-table {
|
||||||
|
margin-left: 160px;
|
||||||
|
tr {
|
||||||
|
background-color: var(--body-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.admin-icon-group {
|
.admin-icon-group {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
{% extends 'admin/change_form.html' %}
|
|
||||||
{% load i18n static %}
|
|
||||||
|
|
||||||
{% block field_sets %}
|
|
||||||
{# Create an invisible <a> tag so that we can use a click event to toggle the modal. #}
|
|
||||||
<a id="invisible-ineligible-modal-toggler" class="display-none" href="#toggle-set-ineligible" aria-controls="toggle-set-ineligible" data-open-modal></a>
|
|
||||||
{{ block.super }}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block submit_buttons_bottom %}
|
|
||||||
{% comment %}
|
|
||||||
Modals behave very weirdly in django admin.
|
|
||||||
They tend to "strip out" any injected form elements, leaving only the main form.
|
|
||||||
In addition, USWDS handles modals by first destroying the element, then repopulating it toward the end of the page.
|
|
||||||
In effect, this means that the modal is not, and cannot, be surrounded by any form element at compile time.
|
|
||||||
|
|
||||||
The current workaround for this is to use javascript to inject a hidden input, and bind submit of that
|
|
||||||
element to the click of the confirmation button within this modal.
|
|
||||||
|
|
||||||
This is controlled by the class `dja-form-placeholder` on the button.
|
|
||||||
|
|
||||||
In addition, the modal element MUST be placed low in the DOM. The script loads slower on DJA than on other portions
|
|
||||||
of the application, so this means that it will briefly "populate", causing unintended visual effects.
|
|
||||||
{% endcomment %}
|
|
||||||
{# Create a modal for when a domain is marked as ineligible #}
|
|
||||||
<div
|
|
||||||
class="usa-modal"
|
|
||||||
id="toggle-set-ineligible"
|
|
||||||
aria-labelledby="Are you sure you want to select ineligible status?"
|
|
||||||
aria-describedby="This request will be marked as ineligible."
|
|
||||||
>
|
|
||||||
<div class="usa-modal__content">
|
|
||||||
<div class="usa-modal__main">
|
|
||||||
<h2 class="usa-modal__heading" id="modal-1-heading">
|
|
||||||
Are you sure you want to select ineligible status?
|
|
||||||
</h2>
|
|
||||||
<div class="usa-prose">
|
|
||||||
<p>
|
|
||||||
When a domain request is in ineligible status, the registrant's permissions within the registrar are restricted as follows:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li class="font-body-sm">They cannot edit the ineligible request or any other pending requests.</li>
|
|
||||||
<li class="font-body-sm">They cannot manage any of their approved domains.</li>
|
|
||||||
<li class="font-body-sm">They cannot initiate a new domain request.</li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
The restrictions will not take effect until you “save” the changes for this domain request.
|
|
||||||
This action can be reversed, if needed.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Domain: <b>{{ original.requested_domain.name }}</b>
|
|
||||||
{# Acts as a <br> #}
|
|
||||||
<div class="display-inline"></div>
|
|
||||||
New status: <b>{{ original.DomainRequestStatus.INELIGIBLE|capfirst }}</b>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="usa-modal__footer">
|
|
||||||
<ul class="usa-button-group">
|
|
||||||
<li class="usa-button-group__item">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="usa-button"
|
|
||||||
name="_set_domain_request_ineligible"
|
|
||||||
data-close-modal
|
|
||||||
>
|
|
||||||
Yes, select ineligible status
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li class="usa-button-group__item">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="usa-button usa-button--unstyled padding-105 text-center"
|
|
||||||
name="_cancel_domain_request_ineligible"
|
|
||||||
data-close-modal
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="usa-button usa-modal__close"
|
|
||||||
aria-label="Close this window"
|
|
||||||
data-close-modal
|
|
||||||
>
|
|
||||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
|
|
||||||
<use xlink:href="{%static 'img/sprite.svg'%}#close"></use>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{ block.super }}
|
|
||||||
{% endblock %}
|
|
|
@ -2,7 +2,97 @@
|
||||||
{% load i18n static %}
|
{% load i18n static %}
|
||||||
|
|
||||||
{% block field_sets %}
|
{% block field_sets %}
|
||||||
|
{# Create an invisible <a> tag so that we can use a click event to toggle the modal. #}
|
||||||
|
<a id="invisible-ineligible-modal-toggler" class="display-none" href="#toggle-set-ineligible" aria-controls="toggle-set-ineligible" data-open-modal></a>
|
||||||
{% for fieldset in adminform %}
|
{% for fieldset in adminform %}
|
||||||
{% include "django/admin/includes/domain_request_fieldset.html" %}
|
{% include "django/admin/includes/domain_request_fieldset.html" %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block submit_buttons_bottom %}
|
||||||
|
{% comment %}
|
||||||
|
Modals behave very weirdly in django admin.
|
||||||
|
They tend to "strip out" any injected form elements, leaving only the main form.
|
||||||
|
In addition, USWDS handles modals by first destroying the element, then repopulating it toward the end of the page.
|
||||||
|
In effect, this means that the modal is not, and cannot, be surrounded by any form element at compile time.
|
||||||
|
|
||||||
|
The current workaround for this is to use javascript to inject a hidden input, and bind submit of that
|
||||||
|
element to the click of the confirmation button within this modal.
|
||||||
|
|
||||||
|
This is controlled by the class `dja-form-placeholder` on the button.
|
||||||
|
|
||||||
|
In addition, the modal element MUST be placed low in the DOM. The script loads slower on DJA than on other portions
|
||||||
|
of the application, so this means that it will briefly "populate", causing unintended visual effects.
|
||||||
|
{% endcomment %}
|
||||||
|
{# Create a modal for when a domain is marked as ineligible #}
|
||||||
|
<div
|
||||||
|
class="usa-modal"
|
||||||
|
id="toggle-set-ineligible"
|
||||||
|
aria-labelledby="Are you sure you want to select ineligible status?"
|
||||||
|
aria-describedby="This request will be marked as ineligible."
|
||||||
|
>
|
||||||
|
<div class="usa-modal__content">
|
||||||
|
<div class="usa-modal__main">
|
||||||
|
<h2 class="usa-modal__heading" id="modal-1-heading">
|
||||||
|
Are you sure you want to select ineligible status?
|
||||||
|
</h2>
|
||||||
|
<div class="usa-prose">
|
||||||
|
<p>
|
||||||
|
When a domain request is in ineligible status, the registrant's permissions within the registrar are restricted as follows:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li class="font-body-sm">They cannot edit the ineligible request or any other pending requests.</li>
|
||||||
|
<li class="font-body-sm">They cannot manage any of their approved domains.</li>
|
||||||
|
<li class="font-body-sm">They cannot initiate a new domain request.</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
The restrictions will not take effect until you “save” the changes for this domain request.
|
||||||
|
This action can be reversed, if needed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Domain: <b>{{ original.requested_domain.name }}</b>
|
||||||
|
{# Acts as a <br> #}
|
||||||
|
<div class="display-inline"></div>
|
||||||
|
New status: <b>{{ original.DomainRequestStatus.INELIGIBLE|capfirst }}</b>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="usa-modal__footer">
|
||||||
|
<ul class="usa-button-group">
|
||||||
|
<li class="usa-button-group__item">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="usa-button"
|
||||||
|
name="_set_domain_request_ineligible"
|
||||||
|
data-close-modal
|
||||||
|
>
|
||||||
|
Yes, select ineligible status
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="usa-button-group__item">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="usa-button usa-button--unstyled padding-105 text-center"
|
||||||
|
name="_cancel_domain_request_ineligible"
|
||||||
|
data-close-modal
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="usa-button usa-modal__close"
|
||||||
|
aria-label="Close this window"
|
||||||
|
data-close-modal
|
||||||
|
>
|
||||||
|
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
|
||||||
|
<use xlink:href="{%static 'img/sprite.svg'%}#close"></use>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ block.super }}
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -1,28 +1,42 @@
|
||||||
{% load i18n static %}
|
{% load i18n static %}
|
||||||
|
|
||||||
|
<table class="dja-user-detail-table" {% if field_name %} id="id_detail_table__{{field_name}}" {% endif %}>
|
||||||
<details class="margin-top-1 dja-detail-table" {% if start_opened %}open{% endif %}>
|
|
||||||
<summary class="padding-1 dja-details-summary">Details</summary>
|
|
||||||
<div class="grid-container padding-left-0 padding-right-0 dja-details-contents">
|
|
||||||
{% if user.title or user.email or user.phone %}
|
|
||||||
<table>
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Title</th>
|
<th class="padding-left-0" scope="row">Title</th>
|
||||||
|
{% if user.title or user.contact.title %}
|
||||||
|
{% if user.contact.title %}
|
||||||
|
<td>{{ user.contact.title }}</td>
|
||||||
|
{% else %}
|
||||||
<td>{{ user.title }}</td>
|
<td>{{ user.title }}</td>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<td>Nothing found</td>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Email</th>
|
<th class="padding-left-0" scope="row">Email</th>
|
||||||
|
{% if user.email or user.contact.email %}
|
||||||
|
{% if user.contact.email %}
|
||||||
|
<td>{{ user.contact.email }}</td>
|
||||||
|
{% else %}
|
||||||
<td>{{ user.email }}</td>
|
<td>{{ user.email }}</td>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<td>Nothing found</td>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Phone</th>
|
<th class="padding-left-0" scope="row">Phone</th>
|
||||||
|
{% if user.phone or user.contact.phone %}
|
||||||
|
{% if user.contact.phone %}
|
||||||
|
<td>{{ user.contact.phone }}</td>
|
||||||
|
{% else %}
|
||||||
<td>{{ user.phone }}</td>
|
<td>{{ user.phone }}</td>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<td>Nothing found</td>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% else %}
|
|
||||||
<div class="padding-1">No details found</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% extends "admin/fieldset.html" %}
|
{% extends "admin/fieldset.html" %}
|
||||||
|
{% load static url_helpers %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
@ -16,18 +16,38 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ field.label_tag }}
|
{{ field.label_tag }}
|
||||||
{% if field.is_readonly %}
|
{% if field.is_readonly %}
|
||||||
|
{% if field.field.name == "other_contacts" %}
|
||||||
|
<div class="readonly">
|
||||||
|
{% for contact in field.contents|split:", " %}
|
||||||
|
<a href="#" class="other-contact__{{forloop.counter}}">{{ contact }}</a>{% if not forloop.last %}, {% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% elif field.field.name == "current_websites" %}
|
||||||
|
{% comment %}
|
||||||
|
The "website" model is essentially just a text field.
|
||||||
|
It is not useful to be redirected to the object definition,
|
||||||
|
rather it is more useful in this scenario to be redirected to the
|
||||||
|
actual website (as its just a plaintext string otherwise).
|
||||||
|
|
||||||
|
This ONLY applies to analysts. For superusers, its business as usual.
|
||||||
|
{% endcomment %}
|
||||||
|
{% for website in field.contents|split:", " %}
|
||||||
|
<a href="{{ website }}" class="padding-top-1 current-website__{{forloop.counter}}">{{ website }}</a>{% if not forloop.last %}, {% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
<div class="readonly">{{ field.contents }}</div>
|
<div class="readonly">{{ field.contents }}</div>
|
||||||
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ field.field }}
|
{{ field.field }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if field.field.name == "creator" %}
|
{% if field.field.name == "creator" %}
|
||||||
{% include "django/admin/includes/domain_request_detail_table.html" with user=original.creator start_opened=True %}
|
{% include "django/admin/includes/domain_request_detail_table.html" with user=original.creator field_name="creator" %}
|
||||||
{% elif field.field.name == "submitter" %}
|
{% elif field.field.name == "submitter" %}
|
||||||
{% include "django/admin/includes/domain_request_detail_table.html" with user=original.submitter start_opened=True %}
|
{% include "django/admin/includes/domain_request_detail_table.html" with user=original.submitter field_name="submitter" %}
|
||||||
{% elif field.field.name == "authorizing_official" %}
|
{% elif field.field.name == "authorizing_official" %}
|
||||||
{% include "django/admin/includes/domain_request_detail_table.html" with user=original.authorizing_official start_opened=True %}
|
{% include "django/admin/includes/domain_request_detail_table.html" with user=original.authorizing_official field_name="authorizing_official" %}
|
||||||
{% elif field.field.name == "other_contacts" %}
|
{% elif field.field.name == "other_contacts" %}
|
||||||
<details class="margin-top-1 dja-detail-table">
|
<details class="margin-top-1 dja-detail-table">
|
||||||
<summary class="padding-1 dja-details-summary">Details</summary>
|
<summary class="padding-1 dja-details-summary">Details</summary>
|
||||||
|
@ -35,7 +55,11 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for contact in original.other_contacts.all %}
|
{% for contact in original.other_contacts.all %}
|
||||||
<tr>
|
{% comment %}
|
||||||
|
Since we can't get the id from field, we can embed this information here.
|
||||||
|
Then we can link these two fields using javascript.
|
||||||
|
{% endcomment %}
|
||||||
|
<tr data-contact-id="{{ contact.id }}" data-contact-url="{% url 'admin:registrar_contact_change' contact.id %}">
|
||||||
<th scope="row">{{contact.first_name}} {{contact.last_name}}</th>
|
<th scope="row">{{contact.first_name}} {{contact.last_name}}</th>
|
||||||
<td>{{ contact.title }}</td>
|
<td>{{ contact.title }}</td>
|
||||||
<td>{{ contact.email }}</td>
|
<td>{{ contact.email }}</td>
|
||||||
|
|
|
@ -26,6 +26,14 @@ def endswith(text, ends):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter("split")
|
||||||
|
def split_string(value, key):
|
||||||
|
"""
|
||||||
|
Splits a given string
|
||||||
|
"""
|
||||||
|
return value.split(key)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def public_site_url(url_path):
|
def public_site_url(url_path):
|
||||||
"""Make a full URL for this path at our public site.
|
"""Make a full URL for this path at our public site.
|
||||||
|
|
|
@ -2,6 +2,7 @@ from datetime import date
|
||||||
from django.test import TestCase, RequestFactory, Client, override_settings
|
from django.test import TestCase, RequestFactory, Client, override_settings
|
||||||
from django.contrib.admin.sites import AdminSite
|
from django.contrib.admin.sites import AdminSite
|
||||||
from contextlib import ExitStack
|
from contextlib import ExitStack
|
||||||
|
from api.tests.common import less_console_noise_decorator
|
||||||
from django_webtest import WebTest # type: ignore
|
from django_webtest import WebTest # type: ignore
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
@ -1193,6 +1194,182 @@ class TestDomainRequestAdmin(MockEppLib):
|
||||||
# Test that approved domain exists and equals requested domain
|
# Test that approved domain exists and equals requested domain
|
||||||
self.assertEqual(domain_request.requested_domain.name, domain_request.approved_domain.name)
|
self.assertEqual(domain_request.requested_domain.name, domain_request.approved_domain.name)
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_other_contacts_has_readonly_link(self):
|
||||||
|
"""Tests if the readonly other_contacts field has links"""
|
||||||
|
|
||||||
|
# Create a fake domain request
|
||||||
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW)
|
||||||
|
|
||||||
|
# Get the other contact
|
||||||
|
other_contact = domain_request.other_contacts.all().first()
|
||||||
|
|
||||||
|
p = "userpass"
|
||||||
|
self.client.login(username="staffuser", password=p)
|
||||||
|
response = self.client.get(
|
||||||
|
"/admin/registrar/domainrequest/{}/change/".format(domain_request.pk),
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make sure the page loaded, and that we're on the right page
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, domain_request.requested_domain.name)
|
||||||
|
|
||||||
|
# Check that the page contains the url we expect
|
||||||
|
expected_href = reverse("admin:registrar_contact_change", args=[other_contact.id])
|
||||||
|
self.assertContains(response, expected_href)
|
||||||
|
|
||||||
|
# Check that the page contains the link we expect.
|
||||||
|
# Since the url is dynamic (populated by JS), we can test for its existence
|
||||||
|
# by checking for the end tag.
|
||||||
|
expected_url = "Testy Tester</a>"
|
||||||
|
self.assertContains(response, expected_url)
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_other_websites_has_readonly_link(self):
|
||||||
|
"""Tests if the readonly other_websites field has links"""
|
||||||
|
|
||||||
|
# Create a fake domain request
|
||||||
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW)
|
||||||
|
|
||||||
|
p = "userpass"
|
||||||
|
self.client.login(username="staffuser", password=p)
|
||||||
|
response = self.client.get(
|
||||||
|
"/admin/registrar/domainrequest/{}/change/".format(domain_request.pk),
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make sure the page loaded, and that we're on the right page
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, domain_request.requested_domain.name)
|
||||||
|
|
||||||
|
# Check that the page contains the link we expect.
|
||||||
|
expected_url = '<a href="city.com" class="padding-top-1 current-website__1">city.com</a>'
|
||||||
|
self.assertContains(response, expected_url)
|
||||||
|
|
||||||
|
def assert_response_contains_distinct_values(self, response, expected_values):
|
||||||
|
"""
|
||||||
|
Asserts that each specified value appears exactly once in the response.
|
||||||
|
|
||||||
|
This method iterates over a list of tuples, where each tuple contains a field name
|
||||||
|
and its expected value. It then performs an assertContains check for each value,
|
||||||
|
ensuring that each value appears exactly once in the response.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- response: The HttpResponse object to inspect.
|
||||||
|
- expected_values: A list of tuples, where each tuple contains:
|
||||||
|
- field: The name of the field (used for subTest identification).
|
||||||
|
- value: The expected value to check for in the response.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
expected_values = [
|
||||||
|
("title", "Treat inspector</td>"),
|
||||||
|
("email", "meoward.jones@igorville.gov</td>"),
|
||||||
|
]
|
||||||
|
self.assert_response_contains_distinct_values(response, expected_values)
|
||||||
|
"""
|
||||||
|
for field, value in expected_values:
|
||||||
|
with self.subTest(field=field, expected_value=value):
|
||||||
|
self.assertContains(response, value, count=1)
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_contact_fields_have_detail_table(self):
|
||||||
|
"""Tests if the contact fields have the detail table which displays title, email, and phone"""
|
||||||
|
|
||||||
|
# Create fake creator
|
||||||
|
_creator = User.objects.create(
|
||||||
|
username="MrMeoward",
|
||||||
|
first_name="Meoward",
|
||||||
|
last_name="Jones",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Due to the relation between User <==> Contact,
|
||||||
|
# the underlying contact has to be modified this way.
|
||||||
|
_creator.contact.email = "meoward.jones@igorville.gov"
|
||||||
|
_creator.contact.phone = "(555) 123 12345"
|
||||||
|
_creator.contact.title = "Treat inspector"
|
||||||
|
_creator.contact.save()
|
||||||
|
|
||||||
|
# Create a fake domain request
|
||||||
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=_creator)
|
||||||
|
|
||||||
|
p = "userpass"
|
||||||
|
self.client.login(username="staffuser", password=p)
|
||||||
|
response = self.client.get(
|
||||||
|
"/admin/registrar/domainrequest/{}/change/".format(domain_request.pk),
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make sure the page loaded, and that we're on the right page
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, domain_request.requested_domain.name)
|
||||||
|
|
||||||
|
# Check that the modal has the right content
|
||||||
|
# Check for the header
|
||||||
|
|
||||||
|
# == Check for the creator == #
|
||||||
|
|
||||||
|
# Check for the right title, email, and phone number in the response.
|
||||||
|
# We only need to check for the end tag
|
||||||
|
# (Otherwise this test will fail if we change classes, etc)
|
||||||
|
expected_creator_fields = [
|
||||||
|
# Field, expected value
|
||||||
|
("title", "Treat inspector</td>"),
|
||||||
|
("email", "meoward.jones@igorville.gov</td>"),
|
||||||
|
("phone", "(555) 123 12345</td>"),
|
||||||
|
]
|
||||||
|
self.assert_response_contains_distinct_values(response, expected_creator_fields)
|
||||||
|
|
||||||
|
# Check for the field itself
|
||||||
|
self.assertContains(response, "Meoward Jones")
|
||||||
|
|
||||||
|
# == Check for the submitter == #
|
||||||
|
expected_submitter_fields = [
|
||||||
|
# Field, expected value
|
||||||
|
("title", "Admin Tester</td>"),
|
||||||
|
("email", "mayor@igorville.gov</td>"),
|
||||||
|
("phone", "(555) 555 5556</td>"),
|
||||||
|
]
|
||||||
|
self.assert_response_contains_distinct_values(response, expected_submitter_fields)
|
||||||
|
self.assertContains(response, "Testy2 Tester2")
|
||||||
|
|
||||||
|
# == Check for the authorizing_official == #
|
||||||
|
expected_ao_fields = [
|
||||||
|
# Field, expected value
|
||||||
|
("title", "Chief Tester</td>"),
|
||||||
|
("email", "testy@town.com</td>"),
|
||||||
|
("phone", "(555) 555 5555</td>"),
|
||||||
|
]
|
||||||
|
self.assert_response_contains_distinct_values(response, expected_ao_fields)
|
||||||
|
|
||||||
|
# count=4 because the underlying domain has two users with this name.
|
||||||
|
# The dropdown has 3 of these.
|
||||||
|
self.assertContains(response, "Testy Tester", count=4)
|
||||||
|
|
||||||
|
# Check for table titles. We only need to check for the end tag
|
||||||
|
# (Otherwise this test will fail if we change classes, etc)
|
||||||
|
|
||||||
|
# Title. Count=3 because this table appears on three records.
|
||||||
|
self.assertContains(response, "Title</th>", count=3)
|
||||||
|
|
||||||
|
# Email. Count=3 because this table appears on three records.
|
||||||
|
self.assertContains(response, "Email</th>", count=3)
|
||||||
|
|
||||||
|
# Phone. Count=3 because this table appears on three records.
|
||||||
|
self.assertContains(response, "Phone</th>", count=3)
|
||||||
|
|
||||||
|
# == Test the other_employees field == #
|
||||||
|
expected_other_employees_fields = [
|
||||||
|
# Field, expected value
|
||||||
|
("title", "Another Tester</td>"),
|
||||||
|
("email", "testy2@town.com</td>"),
|
||||||
|
("phone", "(555) 555 5557</td>"),
|
||||||
|
]
|
||||||
|
self.assert_response_contains_distinct_values(response, expected_other_employees_fields)
|
||||||
|
|
||||||
|
# count=1 as only one should exist in a table
|
||||||
|
self.assertContains(response, "Testy Tester</th>", count=1)
|
||||||
|
|
||||||
def test_save_model_sets_restricted_status_on_user(self):
|
def test_save_model_sets_restricted_status_on_user(self):
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
# make sure there is no user with this email
|
# make sure there is no user with this email
|
||||||
|
@ -1340,6 +1517,8 @@ class TestDomainRequestAdmin(MockEppLib):
|
||||||
"requested_domain",
|
"requested_domain",
|
||||||
"approved_domain",
|
"approved_domain",
|
||||||
"alternative_domains",
|
"alternative_domains",
|
||||||
|
"other_contacts",
|
||||||
|
"current_websites",
|
||||||
"purpose",
|
"purpose",
|
||||||
"submitter",
|
"submitter",
|
||||||
"no_other_contacts_rationale",
|
"no_other_contacts_rationale",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue