mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-18 10:29:23 +02:00
Merge remote-tracking branch 'origin/main' into nl/2136-CISA-rep-additional-details
This commit is contained in:
commit
21f6f0a508
15 changed files with 8159 additions and 367 deletions
|
@ -291,13 +291,13 @@ We use the [CSS Block Element Modifier (BEM)](https://getbem.com/naming/) naming
|
|||
|
||||
### Upgrading USWDS and other JavaScript packages
|
||||
|
||||
Version numbers can be manually controlled in `package.json`. Edit that, if desired.
|
||||
|
||||
Now run `docker-compose run node npm update`.
|
||||
|
||||
Then run `docker-compose up` to recompile and recopy the assets.
|
||||
|
||||
Examine the results in the running application (remember to empty your cache!) and commit `package.json` and `package-lock.json` if all is well.
|
||||
1. Version numbers can be manually controlled in `package.json`. Edit that, if desired.
|
||||
2. Now run `docker-compose run node npm update`.
|
||||
3. Then run `docker-compose up` to recompile and recopy the assets, or run `docker-compose updateUswds` if your docker is already up.
|
||||
4. Make note of the dotgov changes in uswds-edited.js.
|
||||
5. Copy over the newly compiled code from uswds.js into uswds-edited.js.
|
||||
6. Put back the dotgov changes you made note of into uswds-edited.js.
|
||||
7. Examine the results in the running application (remember to empty your cache!) and commit `package.json` and `package-lock.json` if all is well.
|
||||
|
||||
## Finite State Machines
|
||||
|
||||
|
|
|
@ -834,3 +834,417 @@ function hideDeletedForms() {
|
|||
(function cisaRepresentativesFormListener() {
|
||||
HookupYesNoListener("additional_details-has_cisa_representative",'cisa-representative', null)
|
||||
})();
|
||||
|
||||
/**
|
||||
* Initialize USWDS tooltips by calling initialization method. Requires that uswds-edited.js
|
||||
* be loaded before get-gov.js. uswds-edited.js adds the tooltip module to the window to be
|
||||
* accessible directly in get-gov.js
|
||||
*
|
||||
*/
|
||||
function initializeTooltips() {
|
||||
function checkTooltip() {
|
||||
// Check that the tooltip library is loaded, and if not, wait and retry
|
||||
if (window.tooltip && typeof window.tooltip.init === 'function') {
|
||||
window.tooltip.init();
|
||||
} else {
|
||||
// Retry after a short delay
|
||||
setTimeout(checkTooltip, 100);
|
||||
}
|
||||
}
|
||||
checkTooltip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize USWDS modals by calling on method. Requires that uswds-edited.js be loaded
|
||||
* before get-gov.js. uswds-edited.js adds the modal module to the window to be accessible
|
||||
* directly in get-gov.js.
|
||||
* initializeModals adds modal-related DOM elements, based on other DOM elements existing in
|
||||
* the page. It needs to be called only once for any particular DOM element; otherwise, it
|
||||
* will initialize improperly. Therefore, if DOM elements change dynamically and include
|
||||
* DOM elements with modal classes, unloadModals needs to be called before initializeModals.
|
||||
*
|
||||
*/
|
||||
function initializeModals() {
|
||||
window.modal.on();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload existing USWDS modals by calling off method. Requires that uswds-edited.js be
|
||||
* loaded before get-gov.js. uswds-edited.js adds the modal module to the window to be
|
||||
* accessible directly in get-gov.js.
|
||||
* See note above with regards to calling this method relative to initializeModals.
|
||||
*
|
||||
*/
|
||||
function unloadModals() {
|
||||
window.modal.off();
|
||||
}
|
||||
|
||||
/**
|
||||
* An IIFE that listens for DOM Content to be loaded, then executes. This function
|
||||
* initializes the domains list and associated functionality on the home page of the app.
|
||||
*
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let domainsWrapper = document.querySelector('.domains-wrapper');
|
||||
|
||||
if (domainsWrapper) {
|
||||
let currentSortBy = 'id';
|
||||
let currentOrder = 'asc';
|
||||
let noDomainsWrapper = document.querySelector('.no-domains-wrapper');
|
||||
|
||||
/**
|
||||
* Loads rows in the domains list, as well as updates pagination around the domains list
|
||||
* based on the supplied attributes.
|
||||
* @param {*} page - the page number of the results (starts with 1)
|
||||
* @param {*} sortBy - the sort column option
|
||||
* @param {*} order - the sort order {asc, desc}
|
||||
*/
|
||||
function loadDomains(page, sortBy = currentSortBy, order = currentOrder) {
|
||||
//fetch json of page of domains, given page # and sort
|
||||
fetch(`/get-domains-json/?page=${page}&sort_by=${sortBy}&order=${order}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
console.log('Error in AJAX call: ' + data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle the display of proper messaging in the event that no domains exist in the list
|
||||
if (data.domains.length) {
|
||||
domainsWrapper.classList.remove('display-none');
|
||||
noDomainsWrapper.classList.add('display-none');
|
||||
} else {
|
||||
domainsWrapper.classList.add('display-none');
|
||||
noDomainsWrapper.classList.remove('display-none');
|
||||
}
|
||||
|
||||
// identify the DOM element where the domain list will be inserted into the DOM
|
||||
const domainList = document.querySelector('.dotgov-table__registered-domains tbody');
|
||||
domainList.innerHTML = '';
|
||||
|
||||
data.domains.forEach(domain => {
|
||||
const expirationDate = domain.expiration_date ? new Date(domain.expiration_date) : null;
|
||||
const expirationDateSortValue = expirationDate ? expirationDate.getTime() : '';
|
||||
const actionUrl = domain.action_url;
|
||||
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<th scope="row" role="rowheader" data-label="Domain name">
|
||||
${domain.name}
|
||||
</th>
|
||||
<td data-sort-value="${expirationDateSortValue}" data-label="Expires">
|
||||
${expirationDate ? expirationDate.toLocaleDateString() : ''}
|
||||
</td>
|
||||
<td data-label="Status">
|
||||
${domain.state_display}
|
||||
<svg
|
||||
class="usa-icon usa-tooltip usa-tooltip--registrar text-middle margin-bottom-05 text-accent-cool no-click-outline-and-cursor-help"
|
||||
data-position="top"
|
||||
title="${domain.get_state_help_text}"
|
||||
focusable="true"
|
||||
aria-label="Status Information"
|
||||
role="tooltip"
|
||||
>
|
||||
<use aria-hidden="true" xlink:href="/public/img/sprite.svg#info_outline"></use>
|
||||
</svg>
|
||||
</td>
|
||||
<td>
|
||||
<a href="${actionUrl}">
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="/public/img/sprite.svg#${domain.svg_icon}"></use>
|
||||
</svg>
|
||||
${domain.action_label} <span class="usa-sr-only">${domain.name}</span>
|
||||
</a>
|
||||
</td>
|
||||
`;
|
||||
domainList.appendChild(row);
|
||||
});
|
||||
// initialize tool tips immediately after the associated DOM elements are added
|
||||
initializeTooltips();
|
||||
|
||||
hasLoaded = true;
|
||||
|
||||
// update pagination
|
||||
updateDomainsPagination(data.page, data.num_pages, data.has_previous, data.has_next, data.total);
|
||||
currentSortBy = sortBy;
|
||||
currentOrder = order;
|
||||
})
|
||||
.catch(error => console.error('Error fetching domains:', error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the pagination below the domains list.
|
||||
* @param {*} currentPage - the current page number (starting with 1)
|
||||
* @param {*} numPages - the number of pages indicated by the domains list response
|
||||
* @param {*} hasPrevious - if there is a page of results prior to the current page
|
||||
* @param {*} hasNext - if there is a page of results after the current page
|
||||
*/
|
||||
function updateDomainsPagination(currentPage, numPages, hasPrevious, hasNext, totalItems) {
|
||||
// identify the DOM element where the pagination will be inserted
|
||||
const paginationContainer = document.querySelector('#domains-pagination');
|
||||
const paginationCounter = document.querySelector('#domains-pagination .usa-pagination__counter');
|
||||
const paginationButtons = document.querySelector('#domains-pagination .usa-pagination__list');
|
||||
paginationCounter.innerHTML = '';
|
||||
paginationButtons.innerHTML = '';
|
||||
|
||||
// Buttons should only be displayed if there are more than one pages of results
|
||||
paginationButtons.classList.toggle('display-none', numPages <= 1);
|
||||
|
||||
// Counter should only be displayed if there is more than 1 item
|
||||
paginationContainer.classList.toggle('display-none', totalItems < 1);
|
||||
|
||||
paginationCounter.innerHTML = `${totalItems} domain${totalItems > 1 ? 's' : ''}`;
|
||||
|
||||
if (hasPrevious) {
|
||||
const prevPageItem = document.createElement('li');
|
||||
prevPageItem.className = 'usa-pagination__item usa-pagination__arrow';
|
||||
prevPageItem.innerHTML = `
|
||||
<a href="#domains-header" class="usa-pagination__link usa-pagination__previous-page" aria-label="Domains previous page">
|
||||
<svg class="usa-icon" aria-hidden="true" role="img">
|
||||
<use xlink:href="/public/img/sprite.svg#navigate_before"></use>
|
||||
</svg>
|
||||
<span class="usa-pagination__link-text">Previous</span>
|
||||
</a>
|
||||
`;
|
||||
prevPageItem.querySelector('a').addEventListener('click', () => loadDomains(currentPage - 1));
|
||||
paginationButtons.appendChild(prevPageItem);
|
||||
}
|
||||
|
||||
for (let i = 1; i <= numPages; i++) {
|
||||
const pageItem = document.createElement('li');
|
||||
pageItem.className = 'usa-pagination__item usa-pagination__page-no';
|
||||
pageItem.innerHTML = `
|
||||
<a href="#domains-header" class="usa-pagination__button" aria-label="Domains page ${i}">${i}</a>
|
||||
`;
|
||||
if (i === currentPage) {
|
||||
pageItem.querySelector('a').classList.add('usa-current');
|
||||
pageItem.querySelector('a').setAttribute('aria-current', 'page');
|
||||
}
|
||||
pageItem.querySelector('a').addEventListener('click', () => loadDomains(i));
|
||||
paginationButtons.appendChild(pageItem);
|
||||
}
|
||||
|
||||
if (hasNext) {
|
||||
const nextPageItem = document.createElement('li');
|
||||
nextPageItem.className = 'usa-pagination__item usa-pagination__arrow';
|
||||
nextPageItem.innerHTML = `
|
||||
<a href="#domains-header" class="usa-pagination__link usa-pagination__next-page" aria-label="Domains next page">
|
||||
<span class="usa-pagination__link-text">Next</span>
|
||||
<svg class="usa-icon" aria-hidden="true" role="img">
|
||||
<use xlink:href="/public/img/sprite.svg#navigate_next"></use>
|
||||
</svg>
|
||||
</a>
|
||||
`;
|
||||
nextPageItem.querySelector('a').addEventListener('click', () => loadDomains(currentPage + 1));
|
||||
paginationButtons.appendChild(nextPageItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Add event listeners to table headers for sorting
|
||||
document.querySelectorAll('.dotgov-table__registered-domains th[data-sortable]').forEach(header => {
|
||||
header.addEventListener('click', function() {
|
||||
const sortBy = this.getAttribute('data-sortable');
|
||||
let order = 'asc';
|
||||
// sort order will be ascending, unless the currently sorted column is ascending, and the user
|
||||
// is selecting the same column to sort in descending order
|
||||
if (sortBy === currentSortBy) {
|
||||
order = currentOrder === 'asc' ? 'desc' : 'asc';
|
||||
}
|
||||
// load the results with the updated sort
|
||||
loadDomains(1, sortBy, order);
|
||||
});
|
||||
});
|
||||
|
||||
// Load the first page initially
|
||||
loadDomains(1);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An IIFE that listens for DOM Content to be loaded, then executes. This function
|
||||
* initializes the domain requests list and associated functionality on the home page of the app.
|
||||
*
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let domainRequestsWrapper = document.querySelector('.domain-requests-wrapper');
|
||||
|
||||
if (domainRequestsWrapper) {
|
||||
let currentSortBy = 'id';
|
||||
let currentOrder = 'asc';
|
||||
let noDomainRequestsWrapper = document.querySelector('.no-domain-requests-wrapper');
|
||||
|
||||
/**
|
||||
* Loads rows in the domain requests list, as well as updates pagination around the domain requests list
|
||||
* based on the supplied attributes.
|
||||
* @param {*} page - the page number of the results (starts with 1)
|
||||
* @param {*} sortBy - the sort column option
|
||||
* @param {*} order - the sort order {asc, desc}
|
||||
*/
|
||||
function loadDomainRequests(page, sortBy = currentSortBy, order = currentOrder) {
|
||||
//fetch json of page of domain requests, given page # and sort
|
||||
fetch(`/get-domain-requests-json/?page=${page}&sort_by=${sortBy}&order=${order}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
console.log('Error in AJAX call: ' + data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle the display of proper messaging in the event that no domain requests exist in the list
|
||||
if (data.domain_requests.length) {
|
||||
domainRequestsWrapper.classList.remove('display-none');
|
||||
noDomainRequestsWrapper.classList.add('display-none');
|
||||
} else {
|
||||
domainRequestsWrapper.classList.add('display-none');
|
||||
noDomainRequestsWrapper.classList.remove('display-none');
|
||||
}
|
||||
|
||||
// identify the DOM element where the domain request list will be inserted into the DOM
|
||||
const tbody = document.querySelector('.dotgov-table__domain-requests tbody');
|
||||
tbody.innerHTML = '';
|
||||
|
||||
// remove any existing modal elements from the DOM so they can be properly re-initialized
|
||||
// after the DOM content changes and there are new delete modal buttons added
|
||||
unloadModals();
|
||||
data.domain_requests.forEach(request => {
|
||||
const domainName = request.requested_domain ? request.requested_domain : `New domain request <span class="text-base font-body-xs">(${new Date(request.created_at).toLocaleString()} UTC)</span>`;
|
||||
const actionUrl = request.action_url;
|
||||
const actionLabel = request.action_label;
|
||||
const options = { year: 'numeric', month: 'short', day: 'numeric' };
|
||||
const submissionDate = request.submission_date ? new Date(request.submission_date).toLocaleDateString('en-US', options) : `<span class="text-base">Not submitted</span>`;
|
||||
const deleteButton = request.is_deletable ? `
|
||||
<a
|
||||
role="button"
|
||||
id="button-toggle-delete-domain-alert-${request.id}"
|
||||
href="#toggle-delete-domain-alert-${request.id}"
|
||||
class="usa-button--unstyled text-no-underline late-loading-modal-trigger"
|
||||
aria-controls="toggle-delete-domain-alert-${request.id}"
|
||||
data-open-modal
|
||||
>
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="/public/img/sprite.svg#delete"></use>
|
||||
</svg> Delete <span class="usa-sr-only">${domainName}</span>
|
||||
</a>` : '';
|
||||
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<th scope="row" role="rowheader" data-label="Domain name">
|
||||
${domainName}
|
||||
</th>
|
||||
<td data-sort-value="${new Date(request.submission_date).getTime()}" data-label="Date submitted">
|
||||
${submissionDate}
|
||||
</td>
|
||||
<td data-label="Status">
|
||||
${request.status}
|
||||
</td>
|
||||
<td>
|
||||
<a href="${actionUrl}">
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="/public/img/sprite.svg#${request.svg_icon}"></use>
|
||||
</svg>
|
||||
${actionLabel} <span class="usa-sr-only">${request.requested_domain ? request.requested_domain : 'New domain request'}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>${deleteButton}</td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
// initialize modals immediately after the DOM content is updated
|
||||
initializeModals();
|
||||
|
||||
hasLoaded = true;
|
||||
|
||||
// update the pagination after the domain requests list is updated
|
||||
updateDomainRequestsPagination(data.page, data.num_pages, data.has_previous, data.has_next, data.total);
|
||||
currentSortBy = sortBy;
|
||||
currentOrder = order;
|
||||
})
|
||||
.catch(error => console.error('Error fetching domain requests:', error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the pagination below the domain requests list.
|
||||
* @param {*} currentPage - the current page number (starting with 1)
|
||||
* @param {*} numPages - the number of pages indicated by the domain request list response
|
||||
* @param {*} hasPrevious - if there is a page of results prior to the current page
|
||||
* @param {*} hasNext - if there is a page of results after the current page
|
||||
*/
|
||||
function updateDomainRequestsPagination(currentPage, numPages, hasPrevious, hasNext, totalItems) {
|
||||
// identify the DOM element where pagination is contained
|
||||
const paginationContainer = document.querySelector('#domain-requests-pagination');
|
||||
const paginationCounter = document.querySelector('#domain-requests-pagination .usa-pagination__counter');
|
||||
const paginationButtons = document.querySelector('#domain-requests-pagination .usa-pagination__list');
|
||||
paginationCounter.innerHTML = '';
|
||||
paginationButtons.innerHTML = '';
|
||||
|
||||
// Buttons should only be displayed if there are more than one pages of results
|
||||
paginationButtons.classList.toggle('display-none', numPages <= 1);
|
||||
|
||||
// Counter should only be displayed if there is more than 1 item
|
||||
paginationContainer.classList.toggle('display-none', totalItems < 1);
|
||||
|
||||
paginationCounter.innerHTML = `${totalItems} domain request${totalItems > 1 ? 's' : ''}`;
|
||||
|
||||
if (hasPrevious) {
|
||||
const prevPageItem = document.createElement('li');
|
||||
prevPageItem.className = 'usa-pagination__item usa-pagination__arrow';
|
||||
prevPageItem.innerHTML = `
|
||||
<a href="#domain-requests-header" class="usa-pagination__link usa-pagination__previous-page" aria-label="Domain requests previous page">
|
||||
<svg class="usa-icon" aria-hidden="true" role="img">
|
||||
<use xlink:href="/public/img/sprite.svg#navigate_before"></use>
|
||||
</svg>
|
||||
<span class="usa-pagination__link-text">Previous</span>
|
||||
</a>
|
||||
`;
|
||||
prevPageItem.querySelector('a').addEventListener('click', () => loadDomainRequests(currentPage - 1));
|
||||
paginationButtons.appendChild(prevPageItem);
|
||||
}
|
||||
|
||||
for (let i = 1; i <= numPages; i++) {
|
||||
const pageItem = document.createElement('li');
|
||||
pageItem.className = 'usa-pagination__item usa-pagination__page-no';
|
||||
pageItem.innerHTML = `
|
||||
<a href="#domain-requests-header" class="usa-pagination__button" aria-label="Domain requests page ${i}">${i}</a>
|
||||
`;
|
||||
if (i === currentPage) {
|
||||
pageItem.querySelector('a').classList.add('usa-current');
|
||||
pageItem.querySelector('a').setAttribute('aria-current', 'page');
|
||||
}
|
||||
pageItem.querySelector('a').addEventListener('click', () => loadDomainRequests(i));
|
||||
paginationButtons.appendChild(pageItem);
|
||||
}
|
||||
|
||||
if (hasNext) {
|
||||
const nextPageItem = document.createElement('li');
|
||||
nextPageItem.className = 'usa-pagination__item usa-pagination__arrow';
|
||||
nextPageItem.innerHTML = `
|
||||
<a href="#domain-requests-header" class="usa-pagination__link usa-pagination__next-page" aria-label="Domain requests next page">
|
||||
<span class="usa-pagination__link-text">Next</span>
|
||||
<svg class="usa-icon" aria-hidden="true" role="img">
|
||||
<use xlink:href="/public/img/sprite.svg#navigate_next"></use>
|
||||
</svg>
|
||||
</a>
|
||||
`;
|
||||
nextPageItem.querySelector('a').addEventListener('click', () => loadDomainRequests(currentPage + 1));
|
||||
paginationButtons.appendChild(nextPageItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Add event listeners to table headers for sorting
|
||||
document.querySelectorAll('.dotgov-table__domain-requests th[data-sortable]').forEach(header => {
|
||||
header.addEventListener('click', function() {
|
||||
const sortBy = this.getAttribute('data-sortable');
|
||||
let order = 'asc';
|
||||
// sort order will be ascending, unless the currently sorted column is ascending, and the user
|
||||
// is selecting the same column to sort in descending order
|
||||
if (sortBy === currentSortBy) {
|
||||
order = currentOrder === 'asc' ? 'desc' : 'asc';
|
||||
}
|
||||
loadDomainRequests(1, sortBy, order);
|
||||
});
|
||||
});
|
||||
|
||||
// Load the first page initially
|
||||
loadDomainRequests(1);
|
||||
}
|
||||
});
|
||||
|
|
7042
src/registrar/assets/js/uswds-edited.js
Normal file
7042
src/registrar/assets/js/uswds-edited.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -21,6 +21,8 @@ from registrar.views.admin_views import (
|
|||
)
|
||||
|
||||
from registrar.views.domain_request import Step
|
||||
from registrar.views.domain_requests_json import get_domain_requests_json
|
||||
from registrar.views.domains_json import get_domains_json
|
||||
from registrar.views.utility import always_404
|
||||
from api.views import available, get_current_federal, get_current_full
|
||||
|
||||
|
@ -198,6 +200,8 @@ urlpatterns = [
|
|||
views.DomainDeleteUserView.as_view(http_method_names=["post"]),
|
||||
name="domain-user-delete",
|
||||
),
|
||||
path("get-domains-json/", get_domains_json, name="get_domains_json"),
|
||||
path("get-domain-requests-json/", get_domain_requests_json, name="get_domain_requests_json"),
|
||||
]
|
||||
|
||||
# Djangooidc strips out context data from that context, so we define a custom error
|
||||
|
|
|
@ -1062,6 +1062,15 @@ class Domain(TimeStampedModel, DomainHelper):
|
|||
now = timezone.now().date()
|
||||
return self.expiration_date < now
|
||||
|
||||
def state_display(self):
|
||||
"""Return the display status of the domain."""
|
||||
if self.is_expired() and self.state != self.State.UNKNOWN:
|
||||
return "Expired"
|
||||
elif self.state == self.State.UNKNOWN or self.state == self.State.DNS_NEEDED:
|
||||
return "DNS needed"
|
||||
else:
|
||||
return self.state.capitalize()
|
||||
|
||||
def map_epp_contact_to_public_contact(self, contact: eppInfo.InfoContactResultData, contact_id, contact_type):
|
||||
"""Maps the Epp contact representation to a PublicContact object.
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@
|
|||
{% block css %}
|
||||
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
|
||||
<script src="{% static 'js/uswds-init.min.js' %}" defer></script>
|
||||
<!-- We override with our own copy to make some classes accessible in our JS -->
|
||||
<script src="{% static 'js/uswds-edited.js' %}" defer></script>
|
||||
<script src="{% static 'js/get-gov.js' %}" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -67,7 +69,6 @@
|
|||
</head>
|
||||
|
||||
<body id="{% block body_id %}default{% endblock %}" class="{% block body_class %}section_front{% endblock %}">
|
||||
<script src="{% static 'js/uswds.min.js' %}" defer></script>
|
||||
<a class="usa-skipnav" href="#main-content">Skip to main content</a>
|
||||
|
||||
{% if not IS_PRODUCTION %}
|
||||
|
|
|
@ -24,224 +24,123 @@
|
|||
</p>
|
||||
|
||||
<section class="section--outlined">
|
||||
<h2>Domains</h2>
|
||||
{% if domains %}
|
||||
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked dotgov-table__registered-domains">
|
||||
<caption class="sr-only">Your registered domains</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-sortable scope="col" role="columnheader">Domain name</th>
|
||||
<th data-sortable scope="col" role="columnheader">Expires</th>
|
||||
<th data-sortable scope="col" role="columnheader">Status</th>
|
||||
<th
|
||||
scope="col"
|
||||
role="columnheader"
|
||||
>
|
||||
<span class="usa-sr-only">Action</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for domain in domains %}
|
||||
<tr>
|
||||
<th th scope="row" role="rowheader" data-label="Domain name">
|
||||
{{ domain.name }}
|
||||
</th>
|
||||
<td data-sort-value="{{ domain.expiration_date|date:"U" }}" data-label="Expires">{{ domain.expiration_date|date }}</td>
|
||||
<td data-label="Status">
|
||||
{# UNKNOWN domains would not have an expiration date and thus would show 'Expired' #}
|
||||
{% if domain.is_expired and domain.state != domain.State.UNKNOWN %}
|
||||
Expired
|
||||
{% elif domain.state == domain.State.UNKNOWN or domain.state == domain.State.DNS_NEEDED %}
|
||||
DNS needed
|
||||
{% else %}
|
||||
{{ domain.state|capfirst }}
|
||||
{% endif %}
|
||||
<svg
|
||||
class="usa-icon usa-tooltip usa-tooltip--registrar text-middle margin-bottom-05 text-accent-cool no-click-outline-and-cursor-help"
|
||||
data-position="top"
|
||||
title="{{domain.get_state_help_text}}"
|
||||
focusable="true"
|
||||
aria-label="Status Information"
|
||||
role="tooltip"
|
||||
<h2 id="domains-header">Domains</h2>
|
||||
<div class="domains-wrapper display-none">
|
||||
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked dotgov-table__registered-domains">
|
||||
<caption class="sr-only">Your registered domains</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-sortable="name" scope="col" role="columnheader">Domain name</th>
|
||||
<th data-sortable="expiration_date" scope="col" role="columnheader">Expires</th>
|
||||
<th data-sortable="state_display" scope="col" role="columnheader">Status</th>
|
||||
<th
|
||||
scope="col"
|
||||
role="columnheader"
|
||||
>
|
||||
<use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#info_outline"></use>
|
||||
</svg>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{% url "domain" pk=domain.pk %}">
|
||||
<svg
|
||||
class="usa-icon"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
role="img"
|
||||
width="24"
|
||||
>
|
||||
{% if domain.state == "deleted" or domain.state == "on hold" %}
|
||||
<use xlink:href="{%static 'img/sprite.svg'%}#visibility"></use>
|
||||
</svg>
|
||||
View <span class="usa-sr-only">{{ domain.name }}</span>
|
||||
{% else %}
|
||||
<use xlink:href="{%static 'img/sprite.svg'%}#settings"></use>
|
||||
</svg>
|
||||
Manage <span class="usa-sr-only">{{ domain.name }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div
|
||||
class="usa-sr-only usa-table__announcement-region"
|
||||
aria-live="polite"
|
||||
></div>
|
||||
{% else %}
|
||||
<p>You don't have any registered domains.</p>
|
||||
<p class="maxw-none clearfix">
|
||||
<a href="https://get.gov/help/faq/#do-not-see-my-domain" class="float-right-tablet display-flex flex-align-start usa-link" target="_blank">
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="{%static 'img/sprite.svg'%}#help_outline"></use>
|
||||
</svg>
|
||||
Why don't I see my domain when I sign in to the registrar?
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<span class="usa-sr-only">Action</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- AJAX will populate this tbody -->
|
||||
</tbody>
|
||||
</table>
|
||||
<div
|
||||
class="usa-sr-only usa-table__announcement-region"
|
||||
aria-live="polite"
|
||||
></div>
|
||||
</div>
|
||||
<div class="no-domains-wrapper display-none">
|
||||
<p>You don't have any registered domains.</p>
|
||||
<p class="maxw-none clearfix">
|
||||
<a href="https://get.gov/help/faq/#do-not-see-my-domain" class="float-right-tablet display-flex flex-align-start usa-link" target="_blank">
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="{%static 'img/sprite.svg'%}#help_outline"></use>
|
||||
</svg>
|
||||
Why don't I see my domain when I sign in to the registrar?
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<nav aria-label="Pagination" class="usa-pagination flex-justify" id="domains-pagination">
|
||||
<span class="usa-pagination__counter text-base-dark padding-left-2">
|
||||
<!-- Count will be dynamically populated by JS -->
|
||||
</span>
|
||||
<ul class="usa-pagination__list">
|
||||
<!-- Pagination links will be dynamically populated by JS -->
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<section class="section--outlined">
|
||||
<h2>Domain requests</h2>
|
||||
{% if domain_requests %}
|
||||
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked dotgov-table__domain-requests">
|
||||
<caption class="sr-only">Your domain requests</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-sortable scope="col" role="columnheader">Domain name</th>
|
||||
<th data-sortable scope="col" role="columnheader">Date submitted</th>
|
||||
<th data-sortable scope="col" role="columnheader">Status</th>
|
||||
<th scope="col" role="columnheader"><span class="usa-sr-only">Action</span></th>
|
||||
{% if has_deletable_domain_requests %}
|
||||
<th scope="col" role="columnheader"><span class="usa-sr-only">Delete Action</span></th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for domain_request in domain_requests %}
|
||||
<tr>
|
||||
<th th scope="row" role="rowheader" data-label="Domain name">
|
||||
<h2 id="domain-requests-header">Domain requests</h2>
|
||||
<div class="domain-requests-wrapper display-none">
|
||||
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked dotgov-table__domain-requests">
|
||||
<caption class="sr-only">Your domain requests</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-sortable="requested_domain__name" scope="col" role="columnheader">Domain name</th>
|
||||
<th data-sortable="submission_date" scope="col" role="columnheader">Date submitted</th>
|
||||
<th data-sortable="status" scope="col" role="columnheader">Status</th>
|
||||
<th scope="col" role="columnheader"><span class="usa-sr-only">Action</span></th>
|
||||
<th scope="col" role="columnheader"><span class="usa-sr-only">Delete Action</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="domain-requests-tbody">
|
||||
<!-- AJAX will populate this tbody -->
|
||||
</tbody>
|
||||
</table>
|
||||
<div
|
||||
class="usa-sr-only usa-table__announcement-region"
|
||||
aria-live="polite"
|
||||
></div>
|
||||
|
||||
{% for domain_request in domain_requests %}
|
||||
{% if has_deletable_domain_requests %}
|
||||
{% if domain_request.status == domain_request.DomainRequestStatus.STARTED or domain_request.status == domain_request.DomainRequestStatus.WITHDRAWN %}
|
||||
<div
|
||||
class="usa-modal"
|
||||
id="toggle-delete-domain-alert-{{ domain_request.id }}"
|
||||
aria-labelledby="Are you sure you want to continue?"
|
||||
aria-describedby="Domain will be removed"
|
||||
data-force-action
|
||||
>
|
||||
<form method="POST" action="{% url "domain-request-delete" pk=domain_request.id %}">
|
||||
{% if domain_request.requested_domain is None %}
|
||||
New domain request
|
||||
{# Add a breakpoint #}
|
||||
<div aria-hidden="true"></div>
|
||||
<span class="text-base font-body-xs">({{ domain_request.created_at }} UTC)</span>
|
||||
{% else %}
|
||||
{{ domain_request.requested_domain.name }}
|
||||
{% endif %}
|
||||
</th>
|
||||
<td data-sort-value="{{ domain_request.submission_date|date:"U" }}" data-label="Date submitted">
|
||||
{% if domain_request.submission_date %}
|
||||
{{ domain_request.submission_date|date }}
|
||||
{% else %}
|
||||
<span class="text-base">Not submitted</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td data-label="Status">{{ domain_request.get_status_display }}</td>
|
||||
<td>
|
||||
{% with prefix="New domain request ("%}
|
||||
{% with date=domain_request.created_at|date:"DATETIME_FORMAT"%}
|
||||
{% with name_default=prefix|add:date|add:" UTC)"%}
|
||||
{% if domain_request.status == domain_request.DomainRequestStatus.STARTED or domain_request.status == domain_request.DomainRequestStatus.ACTION_NEEDED or domain_request.status == domain_request.DomainRequestStatus.WITHDRAWN %}
|
||||
<a href="{% url 'edit-domain-request' domain_request.pk %}">
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="{%static 'img/sprite.svg'%}#edit"></use>
|
||||
</svg>
|
||||
{% if domain_request.requested_domain is not None%}
|
||||
Edit <span class="usa-sr-only">{{ domain_request.requested_domain.name }}</span>
|
||||
{% else %}
|
||||
Edit <span class="usa-sr-only">{{ name_default }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<a href="{% url 'domain-request-status' domain_request.pk %}">
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="{%static 'img/sprite.svg'%}#settings"></use>
|
||||
</svg>
|
||||
Manage <span class="usa-sr-only">{{ domain_request.requested_domain.name|default:name_default }}</span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
</a>
|
||||
</td>
|
||||
{% if has_deletable_domain_requests %}
|
||||
<td>
|
||||
{% if domain_request.status == "started" or domain_request.status == "withdrawn" %}
|
||||
<a
|
||||
role="button"
|
||||
id="button-toggle-delete-domain-alert-{{ forloop.counter }}"
|
||||
href="#toggle-delete-domain-alert-{{ forloop.counter }}"
|
||||
class="usa-button--unstyled text-no-underline"
|
||||
aria-controls="toggle-delete-domain-alert-{{ forloop.counter }}"
|
||||
data-open-modal
|
||||
>
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||
<use xlink:href="{%static 'img/sprite.svg'%}#delete"></use>
|
||||
</svg>
|
||||
{% with prefix="New domain request ("%}
|
||||
{% with date=domain_request.created_at|date:"DATETIME_FORMAT"%}
|
||||
{% with name_default=prefix|add:date|add:" UTC)"%}
|
||||
{% if domain_request.requested_domain is not None %}
|
||||
Delete <span class="usa-sr-only">{{ domain_request.requested_domain.name }}</span>
|
||||
{% else %}
|
||||
Delete <span class="usa-sr-only">{{ name_default }}</span>
|
||||
{% endif %}
|
||||
{% if domain_request.created_at %}
|
||||
{% with prefix="(created " %}
|
||||
{% with formatted_date=domain_request.created_at|date:"DATETIME_FORMAT" %}
|
||||
{% with modal_content=prefix|add:formatted_date|add:" UTC)" %}
|
||||
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete this domain request?" modal_description="This will remove the domain request "|add:modal_content|add:" from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete New domain request?" modal_description="This will remove the domain request from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% with modal_heading_value=domain_request.requested_domain.name|add:"?" %}
|
||||
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete" heading_value=modal_heading_value modal_description="This will remove the domain request from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
|
||||
{% endwith %}
|
||||
</a>
|
||||
|
||||
<div
|
||||
class="usa-modal"
|
||||
id="toggle-delete-domain-alert-{{ forloop.counter }}"
|
||||
aria-labelledby="Are you sure you want to continue?"
|
||||
aria-describedby="Domain will be removed"
|
||||
data-force-action
|
||||
>
|
||||
<form method="POST" action="{% url "domain-request-delete" pk=domain_request.id %}">
|
||||
{% if domain_request.requested_domain is None %}
|
||||
{% if domain_request.created_at %}
|
||||
{% with prefix="(created " %}
|
||||
{% with formatted_date=domain_request.created_at|date:"DATETIME_FORMAT" %}
|
||||
{% with modal_content=prefix|add:formatted_date|add:" UTC)" %}
|
||||
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete this domain request?" modal_description="This will remove the domain request "|add:modal_content|add:" from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete New domain request?" modal_description="This will remove the domain request from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% with modal_heading_value=domain_request.requested_domain.name|add:"?" %}
|
||||
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete" heading_value=modal_heading_value modal_description="This will remove the domain request from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div
|
||||
class="usa-sr-only usa-table__announcement-region"
|
||||
aria-live="polite"
|
||||
></div>
|
||||
{% else %}
|
||||
<p>You haven't requested any domains.</p>
|
||||
<!-- <p><a href="{% url 'domain-request:' %}" class="usa-button">Start a new domain request</a></p> -->
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
<div class="no-domain-requests-wrapper display-none">
|
||||
<p>You haven't requested any domains.</p>
|
||||
</div>
|
||||
</section>
|
||||
<nav aria-label="Pagination" class="usa-pagination flex-justify" id="domain-requests-pagination">
|
||||
<span class="usa-pagination__counter text-base-dark padding-left-2">
|
||||
<!-- Count will be dynamically populated by JS -->
|
||||
</span>
|
||||
<ul class="usa-pagination__list">
|
||||
<!-- Pagination links will be dynamically populated by JS -->
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{# Note: Reimplement this after MVP #}
|
||||
<!--
|
||||
|
|
|
@ -161,31 +161,14 @@ class HomeTests(TestWithUser):
|
|||
self.assertContains(response, "You don't have any registered domains.")
|
||||
self.assertContains(response, "Why don't I see my domain when I sign in to the registrar?")
|
||||
|
||||
def test_home_lists_domain_requests(self):
|
||||
response = self.client.get("/")
|
||||
self.assertNotContains(response, "igorville.gov")
|
||||
site = DraftDomain.objects.create(name="igorville.gov")
|
||||
domain_request = DomainRequest.objects.create(creator=self.user, requested_domain=site)
|
||||
response = self.client.get("/")
|
||||
|
||||
# count = 7 because of screenreader content
|
||||
self.assertContains(response, "igorville.gov", count=7)
|
||||
|
||||
# clean up
|
||||
domain_request.delete()
|
||||
|
||||
def test_state_help_text(self):
|
||||
"""Tests if each domain state has help text"""
|
||||
|
||||
# Get the expected text content of each state
|
||||
deleted_text = "This domain has been removed and " "is no longer registered to your organization."
|
||||
dns_needed_text = "Before this domain can be used, " "you’ll need to add name server addresses."
|
||||
dns_needed_text = "Before this domain can be used, "
|
||||
ready_text = "This domain has name servers and is ready for use."
|
||||
on_hold_text = (
|
||||
"This domain is administratively paused, "
|
||||
"so it can’t be edited and won’t resolve in DNS. "
|
||||
"Contact help@get.gov for details."
|
||||
)
|
||||
on_hold_text = "This domain is administratively paused, "
|
||||
deleted_text = "This domain has been removed and " "is no longer registered to your organization."
|
||||
# Generate a mapping of domain names, the state, and expected messages for the subtest
|
||||
test_cases = [
|
||||
|
@ -206,12 +189,11 @@ class HomeTests(TestWithUser):
|
|||
user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER
|
||||
)
|
||||
|
||||
# Grab the home page
|
||||
response = self.client.get("/")
|
||||
# Grab the json response for domain list
|
||||
response = self.client.get("/get-domains-json/")
|
||||
|
||||
# Make sure the user can actually see the domain.
|
||||
# We expect two instances because of SR content.
|
||||
self.assertContains(response, domain_name, count=2)
|
||||
# Make sure the domain is in the list.
|
||||
self.assertContains(response, domain_name, count=1)
|
||||
|
||||
# Check that we have the right text content.
|
||||
self.assertContains(response, expected_message, count=1)
|
||||
|
@ -222,19 +204,18 @@ class HomeTests(TestWithUser):
|
|||
|
||||
def test_state_help_text_expired(self):
|
||||
"""Tests if each domain state has help text when expired"""
|
||||
expired_text = "This domain has expired, but it is still online. " "To renew this domain, contact help@get.gov."
|
||||
expired_text = "This domain has expired, but it is still online. "
|
||||
test_domain, _ = Domain.objects.get_or_create(name="expired.gov", state=Domain.State.READY)
|
||||
test_domain.expiration_date = date(2011, 10, 10)
|
||||
test_domain.save()
|
||||
|
||||
UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER)
|
||||
|
||||
# Grab the home page
|
||||
response = self.client.get("/")
|
||||
# Grab the json response of the domains list
|
||||
response = self.client.get("/get-domains-json/")
|
||||
|
||||
# Make sure the user can actually see the domain.
|
||||
# We expect two instances because of SR content.
|
||||
self.assertContains(response, "expired.gov", count=2)
|
||||
# Make sure the domain is in the response
|
||||
self.assertContains(response, "expired.gov", count=1)
|
||||
|
||||
# Check that we have the right text content.
|
||||
self.assertContains(response, expired_text, count=1)
|
||||
|
@ -243,19 +224,18 @@ class HomeTests(TestWithUser):
|
|||
"""Tests if each domain state has help text when expiration date is None"""
|
||||
|
||||
# == Test a expiration of None for state ready. This should be expired. == #
|
||||
expired_text = "This domain has expired, but it is still online. " "To renew this domain, contact help@get.gov."
|
||||
expired_text = "This domain has expired, but it is still online. "
|
||||
test_domain, _ = Domain.objects.get_or_create(name="imexpired.gov", state=Domain.State.READY)
|
||||
test_domain.expiration_date = None
|
||||
test_domain.save()
|
||||
|
||||
UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER)
|
||||
|
||||
# Grab the home page
|
||||
response = self.client.get("/")
|
||||
# Grab the json response of the domains list
|
||||
response = self.client.get("/get-domains-json/")
|
||||
|
||||
# Make sure the user can actually see the domain.
|
||||
# We expect two instances because of SR content.
|
||||
self.assertContains(response, "imexpired.gov", count=2)
|
||||
# Make sure domain is in the response
|
||||
self.assertContains(response, "imexpired.gov", count=1)
|
||||
|
||||
# Make sure the expiration date is None
|
||||
self.assertEqual(test_domain.expiration_date, None)
|
||||
|
@ -264,19 +244,18 @@ class HomeTests(TestWithUser):
|
|||
self.assertContains(response, expired_text, count=1)
|
||||
|
||||
# == Test a expiration of None for state unknown. This should not display expired text. == #
|
||||
unknown_text = "Before this domain can be used, " "you’ll need to add name server addresses."
|
||||
unknown_text = "Before this domain can be used, "
|
||||
test_domain_2, _ = Domain.objects.get_or_create(name="notexpired.gov", state=Domain.State.UNKNOWN)
|
||||
test_domain_2.expiration_date = None
|
||||
test_domain_2.save()
|
||||
|
||||
UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain_2, role=UserDomainRole.Roles.MANAGER)
|
||||
|
||||
# Grab the home page
|
||||
response = self.client.get("/")
|
||||
# Grab the json response of the domains list
|
||||
response = self.client.get("/get-domains-json/")
|
||||
|
||||
# Make sure the user can actually see the domain.
|
||||
# We expect two instances because of SR content.
|
||||
self.assertContains(response, "notexpired.gov", count=2)
|
||||
# Make sure the response contains the domain
|
||||
self.assertContains(response, "notexpired.gov", count=1)
|
||||
|
||||
# Make sure the expiration date is None
|
||||
self.assertEqual(test_domain_2.expiration_date, None)
|
||||
|
@ -292,14 +271,6 @@ class HomeTests(TestWithUser):
|
|||
creator=self.user, requested_domain=site, status=DomainRequest.DomainRequestStatus.WITHDRAWN
|
||||
)
|
||||
|
||||
# Ensure that igorville.gov exists on the page
|
||||
home_page = self.client.get("/")
|
||||
self.assertContains(home_page, "igorville.gov")
|
||||
|
||||
# Check if the delete button exists. We can do this by checking for its id and text content.
|
||||
self.assertContains(home_page, "Delete")
|
||||
self.assertContains(home_page, "button-toggle-delete-domain-alert-1")
|
||||
|
||||
# Trigger the delete logic
|
||||
response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
|
||||
|
||||
|
@ -316,14 +287,6 @@ class HomeTests(TestWithUser):
|
|||
creator=self.user, requested_domain=site, status=DomainRequest.DomainRequestStatus.STARTED
|
||||
)
|
||||
|
||||
# Ensure that igorville.gov exists on the page
|
||||
home_page = self.client.get("/")
|
||||
self.assertContains(home_page, "igorville.gov")
|
||||
|
||||
# Check if the delete button exists. We can do this by checking for its id and text content.
|
||||
self.assertContains(home_page, "Delete")
|
||||
self.assertContains(home_page, "button-toggle-delete-domain-alert-1")
|
||||
|
||||
# Trigger the delete logic
|
||||
response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
|
||||
|
||||
|
|
|
@ -222,27 +222,6 @@ class TestDomainDetail(TestDomainOverview):
|
|||
self.assertContains(detail_page, "igorville.gov")
|
||||
self.assertContains(detail_page, "Status")
|
||||
|
||||
def test_unknown_domain_does_not_show_as_expired_on_homepage(self):
|
||||
"""An UNKNOWN domain does not show as expired on the homepage.
|
||||
It shows as 'DNS needed'"""
|
||||
# At the time of this test's writing, there are 6 UNKNOWN domains inherited
|
||||
# from constructors. Let's reset.
|
||||
with less_console_noise():
|
||||
Domain.objects.all().delete()
|
||||
UserDomainRole.objects.all().delete()
|
||||
self.domain, _ = Domain.objects.get_or_create(name="igorville.gov")
|
||||
home_page = self.app.get("/")
|
||||
self.assertNotContains(home_page, "igorville.gov")
|
||||
self.role, _ = UserDomainRole.objects.get_or_create(
|
||||
user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER
|
||||
)
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "igorville.gov")
|
||||
igorville = Domain.objects.get(name="igorville.gov")
|
||||
self.assertEquals(igorville.state, Domain.State.UNKNOWN)
|
||||
self.assertNotContains(home_page, "Expired")
|
||||
self.assertContains(home_page, "DNS needed")
|
||||
|
||||
def test_unknown_domain_does_not_show_as_expired_on_detail_page(self):
|
||||
"""An UNKNOWN domain should not exist on the detail_page anymore.
|
||||
It shows as 'DNS needed'"""
|
||||
|
@ -258,11 +237,9 @@ class TestDomainDetail(TestDomainOverview):
|
|||
user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER
|
||||
)
|
||||
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "igorville.gov")
|
||||
igorville = Domain.objects.get(name="igorville.gov")
|
||||
self.assertEquals(igorville.state, Domain.State.UNKNOWN)
|
||||
detail_page = home_page.click("Manage", index=0)
|
||||
detail_page = self.app.get(f"/domain/{igorville.id}")
|
||||
self.assertContains(detail_page, "Expired")
|
||||
|
||||
self.assertNotContains(detail_page, "DNS needed")
|
||||
|
@ -274,26 +251,18 @@ class TestDomainDetail(TestDomainOverview):
|
|||
with less_console_noise():
|
||||
self.user.status = User.RESTRICTED
|
||||
self.user.save()
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "igorville.gov")
|
||||
response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_domain_detail_allowed_for_on_hold(self):
|
||||
"""Test that the domain overview page displays for on hold domain"""
|
||||
with less_console_noise():
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "on-hold.gov")
|
||||
|
||||
# View domain overview page
|
||||
detail_page = self.client.get(reverse("domain", kwargs={"pk": self.domain_on_hold.id}))
|
||||
self.assertNotContains(detail_page, "Edit")
|
||||
|
||||
def test_domain_detail_see_just_nameserver(self):
|
||||
with less_console_noise():
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "justnameserver.com")
|
||||
|
||||
# View nameserver on Domain Overview page
|
||||
detail_page = self.app.get(reverse("domain", kwargs={"pk": self.domain_just_nameserver.id}))
|
||||
|
||||
|
@ -303,9 +272,6 @@ class TestDomainDetail(TestDomainOverview):
|
|||
|
||||
def test_domain_detail_see_nameserver_and_ip(self):
|
||||
with less_console_noise():
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "nameserverwithip.gov")
|
||||
|
||||
# View nameserver on Domain Overview page
|
||||
detail_page = self.app.get(reverse("domain", kwargs={"pk": self.domain_with_ip.id}))
|
||||
|
||||
|
@ -1643,8 +1609,6 @@ class TestDomainSecurityEmail(TestDomainOverview):
|
|||
management pages share the same permissions class"""
|
||||
self.user.status = User.RESTRICTED
|
||||
self.user.save()
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "igorville.gov")
|
||||
with less_console_noise():
|
||||
response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
|
151
src/registrar/tests/test_views_domains_json.py
Normal file
151
src/registrar/tests/test_views_domains_json.py
Normal file
|
@ -0,0 +1,151 @@
|
|||
from registrar.models import UserDomainRole, Domain
|
||||
from django.urls import reverse
|
||||
from .test_views import TestWithUser
|
||||
from django_webtest import WebTest # type: ignore
|
||||
from django.utils.dateparse import parse_date
|
||||
|
||||
|
||||
class GetDomainsJsonTest(TestWithUser, WebTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.app.set_user(self.user.username)
|
||||
|
||||
# Create test domains
|
||||
self.domain1 = Domain.objects.create(name="example1.com", expiration_date="2024-01-01", state="active")
|
||||
self.domain2 = Domain.objects.create(name="example2.com", expiration_date="2024-02-01", state="inactive")
|
||||
self.domain3 = Domain.objects.create(name="example3.com", expiration_date="2024-03-01", state="active")
|
||||
|
||||
# Create UserDomainRoles
|
||||
UserDomainRole.objects.create(user=self.user, domain=self.domain1)
|
||||
UserDomainRole.objects.create(user=self.user, domain=self.domain2)
|
||||
UserDomainRole.objects.create(user=self.user, domain=self.domain3)
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
UserDomainRole.objects.all().delete()
|
||||
UserDomainRole.objects.all().delete()
|
||||
|
||||
def test_get_domains_json_unauthenticated(self):
|
||||
"""for an unauthenticated user, test that the user is redirected for auth"""
|
||||
self.app.reset()
|
||||
|
||||
response = self.client.get(reverse("get_domains_json"))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_get_domains_json_authenticated(self):
|
||||
"""Test that an authenticated user gets the list of 3 domains."""
|
||||
response = self.app.get(reverse("get_domains_json"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json
|
||||
|
||||
# Check pagination info
|
||||
self.assertEqual(data["page"], 1)
|
||||
self.assertFalse(data["has_next"])
|
||||
self.assertFalse(data["has_previous"])
|
||||
self.assertEqual(data["num_pages"], 1)
|
||||
|
||||
# Check the number of domains
|
||||
self.assertEqual(len(data["domains"]), 3)
|
||||
|
||||
# Expected domains
|
||||
expected_domains = [self.domain1, self.domain2, self.domain3]
|
||||
|
||||
# Extract fields from response
|
||||
domain_ids = [domain["id"] for domain in data["domains"]]
|
||||
names = [domain["name"] for domain in data["domains"]]
|
||||
expiration_dates = [domain["expiration_date"] for domain in data["domains"]]
|
||||
states = [domain["state"] for domain in data["domains"]]
|
||||
state_displays = [domain["state_display"] for domain in data["domains"]]
|
||||
get_state_help_texts = [domain["get_state_help_text"] for domain in data["domains"]]
|
||||
action_urls = [domain["action_url"] for domain in data["domains"]]
|
||||
action_labels = [domain["action_label"] for domain in data["domains"]]
|
||||
svg_icons = [domain["svg_icon"] for domain in data["domains"]]
|
||||
|
||||
# Check fields for each domain
|
||||
for i, expected_domain in enumerate(expected_domains):
|
||||
self.assertEqual(expected_domain.id, domain_ids[i])
|
||||
self.assertEqual(expected_domain.name, names[i])
|
||||
self.assertEqual(expected_domain.expiration_date, expiration_dates[i])
|
||||
self.assertEqual(expected_domain.state, states[i])
|
||||
|
||||
# Parsing the expiration date from string to date
|
||||
parsed_expiration_date = parse_date(expiration_dates[i])
|
||||
expected_domain.expiration_date = parsed_expiration_date
|
||||
|
||||
# Check state_display and get_state_help_text
|
||||
self.assertEqual(expected_domain.state_display(), state_displays[i])
|
||||
self.assertEqual(expected_domain.get_state_help_text(), get_state_help_texts[i])
|
||||
|
||||
self.assertEqual(reverse("domain", kwargs={"pk": expected_domain.id}), action_urls[i])
|
||||
|
||||
# Check action_label
|
||||
action_label_expected = (
|
||||
"View"
|
||||
if expected_domains[i].state
|
||||
in [
|
||||
Domain.State.DELETED,
|
||||
Domain.State.ON_HOLD,
|
||||
]
|
||||
else "Manage"
|
||||
)
|
||||
self.assertEqual(action_label_expected, action_labels[i])
|
||||
|
||||
# Check svg_icon
|
||||
svg_icon_expected = (
|
||||
"visibility"
|
||||
if expected_domains[i].state
|
||||
in [
|
||||
Domain.State.DELETED,
|
||||
Domain.State.ON_HOLD,
|
||||
]
|
||||
else "settings"
|
||||
)
|
||||
self.assertEqual(svg_icon_expected, svg_icons[i])
|
||||
|
||||
def test_pagination(self):
|
||||
"""Test that pagination is correct in the response"""
|
||||
response = self.app.get(reverse("get_domains_json"), {"page": 1})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json
|
||||
|
||||
# Check pagination info
|
||||
self.assertEqual(data["page"], 1)
|
||||
self.assertFalse(data["has_next"])
|
||||
self.assertFalse(data["has_previous"])
|
||||
self.assertEqual(data["num_pages"], 1)
|
||||
|
||||
def test_sorting(self):
|
||||
"""test that sorting works properly in the response"""
|
||||
response = self.app.get(reverse("get_domains_json"), {"sort_by": "expiration_date", "order": "desc"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json
|
||||
|
||||
# Check if sorted by expiration_date in descending order
|
||||
expiration_dates = [domain["expiration_date"] for domain in data["domains"]]
|
||||
self.assertEqual(expiration_dates, sorted(expiration_dates, reverse=True))
|
||||
|
||||
response = self.app.get(reverse("get_domains_json"), {"sort_by": "expiration_date", "order": "asc"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json
|
||||
|
||||
# Check if sorted by expiration_date in ascending order
|
||||
expiration_dates = [domain["expiration_date"] for domain in data["domains"]]
|
||||
self.assertEqual(expiration_dates, sorted(expiration_dates))
|
||||
|
||||
def test_sorting_by_state_display(self):
|
||||
"""test that the state_display sorting works properly"""
|
||||
response = self.app.get(reverse("get_domains_json"), {"sort_by": "state_display", "order": "asc"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json
|
||||
|
||||
# Check if sorted by state_display in ascending order
|
||||
states = [domain["state_display"] for domain in data["domains"]]
|
||||
self.assertEqual(states, sorted(states))
|
||||
|
||||
response = self.app.get(reverse("get_domains_json"), {"sort_by": "state_display", "order": "desc"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json
|
||||
|
||||
# Check if sorted by state_display in descending order
|
||||
states = [domain["state_display"] for domain in data["domains"]]
|
||||
self.assertEqual(states, sorted(states, reverse=True))
|
|
@ -47,11 +47,8 @@ class DomainRequestTests(TestWithUser, WebTest):
|
|||
|
||||
def test_domain_request_form_intro_is_skipped_when_edit_access(self):
|
||||
"""Tests that user is NOT presented with intro acknowledgement page when accessed through 'edit'"""
|
||||
completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user)
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "city.gov")
|
||||
# click the "Edit" link
|
||||
detail_page = home_page.click("Edit", index=0)
|
||||
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user)
|
||||
detail_page = self.app.get(f"/domain-request/{domain_request.id}/edit/")
|
||||
# Check that the response is a redirect
|
||||
self.assertEqual(detail_page.status_code, 302)
|
||||
# You can access the 'Location' header to get the redirect URL
|
||||
|
@ -2425,10 +2422,7 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
|
|||
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
|
||||
domain_request.save()
|
||||
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "city.gov")
|
||||
# click the "Manage" link
|
||||
detail_page = home_page.click("Manage", index=0)
|
||||
detail_page = self.app.get(f"/domain-request/{domain_request.id}")
|
||||
self.assertContains(detail_page, "city.gov")
|
||||
self.assertContains(detail_page, "city1.gov")
|
||||
self.assertContains(detail_page, "Chief Tester")
|
||||
|
@ -2445,10 +2439,7 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
|
|||
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
|
||||
domain_request.save()
|
||||
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "city.gov")
|
||||
# click the "Manage" link
|
||||
detail_page = home_page.click("Manage", index=0)
|
||||
detail_page = self.app.get(f"/domain-request/{domain_request.id}")
|
||||
self.assertContains(detail_page, "city.gov")
|
||||
self.assertContains(detail_page, "Chief Tester")
|
||||
self.assertContains(detail_page, "testy@town.com")
|
||||
|
@ -2460,10 +2451,7 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
|
|||
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
|
||||
domain_request.save()
|
||||
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "city.gov")
|
||||
# click the "Manage" link
|
||||
detail_page = home_page.click("Manage", index=0)
|
||||
detail_page = self.app.get(f"/domain-request/{domain_request.id}")
|
||||
self.assertContains(detail_page, "city.gov")
|
||||
self.assertContains(detail_page, "city1.gov")
|
||||
self.assertContains(detail_page, "Chief Tester")
|
||||
|
@ -2485,8 +2473,8 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
|
|||
target_status_code=200,
|
||||
fetch_redirect_response=True,
|
||||
)
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "Withdrawn")
|
||||
response = self.client.get("/get-domain-requests-json/")
|
||||
self.assertContains(response, "Withdrawn")
|
||||
|
||||
def test_domain_request_withdraw_no_permissions(self):
|
||||
"""Can't withdraw domain requests as a restricted user."""
|
||||
|
@ -2495,10 +2483,7 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
|
|||
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
|
||||
domain_request.save()
|
||||
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "city.gov")
|
||||
# click the "Manage" link
|
||||
detail_page = home_page.click("Manage", index=0)
|
||||
detail_page = self.app.get(f"/domain-request/{domain_request.id}")
|
||||
self.assertContains(detail_page, "city.gov")
|
||||
self.assertContains(detail_page, "city1.gov")
|
||||
self.assertContains(detail_page, "Chief Tester")
|
||||
|
@ -2568,9 +2553,9 @@ class TestWizardUnlockingSteps(TestWithUser, WebTest):
|
|||
def test_unlocked_steps_full_domain_request(self):
|
||||
"""Test when all fields in the domain request are filled."""
|
||||
|
||||
completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user)
|
||||
# Make a request to the home page
|
||||
home_page = self.app.get("/")
|
||||
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user)
|
||||
|
||||
response = self.app.get(f"/domain-request/{domain_request.id}/edit/")
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
|
@ -2578,13 +2563,6 @@ class TestWizardUnlockingSteps(TestWithUser, WebTest):
|
|||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
||||
# Assert that the response contains "city.gov"
|
||||
self.assertContains(home_page, "city.gov")
|
||||
|
||||
# Click the "Edit" link
|
||||
response = home_page.click("Edit", index=0)
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
||||
# Check if the response is a redirect
|
||||
if response.status_code == 302:
|
||||
# Follow the redirect manually
|
||||
|
@ -2635,8 +2613,7 @@ class TestWizardUnlockingSteps(TestWithUser, WebTest):
|
|||
)
|
||||
domain_request.other_contacts.set([contact_2])
|
||||
|
||||
# Make a request to the home page
|
||||
home_page = self.app.get("/")
|
||||
response = self.app.get(f"/domain-request/{domain_request.id}/edit/")
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
|
@ -2644,13 +2621,6 @@ class TestWizardUnlockingSteps(TestWithUser, WebTest):
|
|||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
||||
# Assert that the response contains "city.gov"
|
||||
self.assertContains(home_page, "igorville.gov")
|
||||
|
||||
# Click the "Edit" link
|
||||
response = home_page.click("Edit", index=0)
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
||||
# Check if the response is a redirect
|
||||
if response.status_code == 302:
|
||||
# Follow the redirect manually
|
||||
|
|
248
src/registrar/tests/test_views_requests_json.py
Normal file
248
src/registrar/tests/test_views_requests_json.py
Normal file
|
@ -0,0 +1,248 @@
|
|||
from registrar.models import DomainRequest
|
||||
from django.urls import reverse
|
||||
from .test_views import TestWithUser
|
||||
from django_webtest import WebTest # type: ignore
|
||||
from django.utils.dateparse import parse_datetime
|
||||
|
||||
|
||||
class GetRequestsJsonTest(TestWithUser, WebTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.app.set_user(self.user.username)
|
||||
|
||||
# Create domain requests for the user
|
||||
self.domain_requests = [
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-01-01",
|
||||
status=DomainRequest.DomainRequestStatus.STARTED,
|
||||
created_at="2024-01-01",
|
||||
),
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-02-01",
|
||||
status=DomainRequest.DomainRequestStatus.WITHDRAWN,
|
||||
created_at="2024-02-01",
|
||||
),
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-03-01",
|
||||
status=DomainRequest.DomainRequestStatus.REJECTED,
|
||||
created_at="2024-03-01",
|
||||
),
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-04-01",
|
||||
status=DomainRequest.DomainRequestStatus.STARTED,
|
||||
created_at="2024-04-01",
|
||||
),
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-05-01",
|
||||
status=DomainRequest.DomainRequestStatus.STARTED,
|
||||
created_at="2024-05-01",
|
||||
),
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-06-01",
|
||||
status=DomainRequest.DomainRequestStatus.WITHDRAWN,
|
||||
created_at="2024-06-01",
|
||||
),
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-07-01",
|
||||
status=DomainRequest.DomainRequestStatus.REJECTED,
|
||||
created_at="2024-07-01",
|
||||
),
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-08-01",
|
||||
status=DomainRequest.DomainRequestStatus.STARTED,
|
||||
created_at="2024-08-01",
|
||||
),
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-09-01",
|
||||
status=DomainRequest.DomainRequestStatus.STARTED,
|
||||
created_at="2024-09-01",
|
||||
),
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-10-01",
|
||||
status=DomainRequest.DomainRequestStatus.WITHDRAWN,
|
||||
created_at="2024-10-01",
|
||||
),
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-11-01",
|
||||
status=DomainRequest.DomainRequestStatus.REJECTED,
|
||||
created_at="2024-11-01",
|
||||
),
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-11-02",
|
||||
status=DomainRequest.DomainRequestStatus.WITHDRAWN,
|
||||
created_at="2024-11-02",
|
||||
),
|
||||
DomainRequest.objects.create(
|
||||
creator=self.user,
|
||||
requested_domain=None,
|
||||
submission_date="2024-12-01",
|
||||
status=DomainRequest.DomainRequestStatus.APPROVED,
|
||||
created_at="2024-12-01",
|
||||
),
|
||||
]
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
DomainRequest.objects.all().delete()
|
||||
|
||||
def test_get_domain_requests_json_authenticated(self):
|
||||
"""Test that domain requests are returned properly for an authenticated user."""
|
||||
response = self.app.get(reverse("get_domain_requests_json"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json
|
||||
|
||||
# Check pagination info
|
||||
self.assertEqual(data["page"], 1)
|
||||
self.assertTrue(data["has_next"])
|
||||
self.assertFalse(data["has_previous"])
|
||||
self.assertEqual(data["num_pages"], 2)
|
||||
|
||||
# Check the number of domain requests
|
||||
self.assertEqual(len(data["domain_requests"]), 10)
|
||||
|
||||
# Extract fields from response
|
||||
requested_domains = [request["requested_domain"] for request in data["domain_requests"]]
|
||||
submission_dates = [request["submission_date"] for request in data["domain_requests"]]
|
||||
statuses = [request["status"] for request in data["domain_requests"]]
|
||||
created_ats = [request["created_at"] for request in data["domain_requests"]]
|
||||
ids = [request["id"] for request in data["domain_requests"]]
|
||||
is_deletables = [request["is_deletable"] for request in data["domain_requests"]]
|
||||
action_urls = [request["action_url"] for request in data["domain_requests"]]
|
||||
action_labels = [request["action_label"] for request in data["domain_requests"]]
|
||||
svg_icons = [request["svg_icon"] for request in data["domain_requests"]]
|
||||
|
||||
# Check fields for each domain request
|
||||
for i in range(10):
|
||||
self.assertNotEqual(data["domain_requests"][i]["status"], "Approved")
|
||||
self.assertEqual(
|
||||
self.domain_requests[i].requested_domain.name if self.domain_requests[i].requested_domain else None,
|
||||
requested_domains[i],
|
||||
)
|
||||
self.assertEqual(self.domain_requests[i].submission_date, submission_dates[i])
|
||||
self.assertEqual(self.domain_requests[i].get_status_display(), statuses[i])
|
||||
self.assertEqual(
|
||||
parse_datetime(self.domain_requests[i].created_at.isoformat()), parse_datetime(created_ats[i])
|
||||
)
|
||||
self.assertEqual(self.domain_requests[i].id, ids[i])
|
||||
|
||||
# Check is_deletable
|
||||
is_deletable_expected = self.domain_requests[i].status in [
|
||||
DomainRequest.DomainRequestStatus.STARTED,
|
||||
DomainRequest.DomainRequestStatus.WITHDRAWN,
|
||||
]
|
||||
self.assertEqual(is_deletable_expected, is_deletables[i])
|
||||
|
||||
# Check action_url
|
||||
action_url_expected = (
|
||||
reverse("edit-domain-request", kwargs={"id": self.domain_requests[i].id})
|
||||
if self.domain_requests[i].status
|
||||
in [
|
||||
DomainRequest.DomainRequestStatus.STARTED,
|
||||
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
|
||||
DomainRequest.DomainRequestStatus.WITHDRAWN,
|
||||
]
|
||||
else reverse("domain-request-status", kwargs={"pk": self.domain_requests[i].id})
|
||||
)
|
||||
self.assertEqual(action_url_expected, action_urls[i])
|
||||
|
||||
# Check action_label
|
||||
action_label_expected = (
|
||||
"Edit"
|
||||
if self.domain_requests[i].status
|
||||
in [
|
||||
DomainRequest.DomainRequestStatus.STARTED,
|
||||
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
|
||||
DomainRequest.DomainRequestStatus.WITHDRAWN,
|
||||
]
|
||||
else "Manage"
|
||||
)
|
||||
self.assertEqual(action_label_expected, action_labels[i])
|
||||
|
||||
# Check svg_icon
|
||||
svg_icon_expected = (
|
||||
"edit"
|
||||
if self.domain_requests[i].status
|
||||
in [
|
||||
DomainRequest.DomainRequestStatus.STARTED,
|
||||
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
|
||||
DomainRequest.DomainRequestStatus.WITHDRAWN,
|
||||
]
|
||||
else "settings"
|
||||
)
|
||||
self.assertEqual(svg_icon_expected, svg_icons[i])
|
||||
|
||||
def test_pagination(self):
|
||||
"""Test that pagination works properly. There are 11 total non-approved requests and
|
||||
a page size of 10"""
|
||||
response = self.app.get(reverse("get_domain_requests_json"), {"page": 1})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json
|
||||
|
||||
# Check pagination info
|
||||
self.assertEqual(data["page"], 1)
|
||||
self.assertTrue(data["has_next"])
|
||||
self.assertFalse(data["has_previous"])
|
||||
self.assertEqual(data["num_pages"], 2)
|
||||
|
||||
response = self.app.get(reverse("get_domain_requests_json"), {"page": 2})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json
|
||||
|
||||
# Check pagination info
|
||||
self.assertEqual(data["page"], 2)
|
||||
self.assertFalse(data["has_next"])
|
||||
self.assertTrue(data["has_previous"])
|
||||
self.assertEqual(data["num_pages"], 2)
|
||||
|
||||
def test_sorting(self):
|
||||
"""test that sorting works properly on the result set"""
|
||||
response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "submission_date", "order": "desc"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json
|
||||
|
||||
# Check if sorted by submission_date in descending order
|
||||
submission_dates = [req["submission_date"] for req in data["domain_requests"]]
|
||||
self.assertEqual(submission_dates, sorted(submission_dates, reverse=True))
|
||||
|
||||
response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "submission_date", "order": "asc"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json
|
||||
|
||||
# Check if sorted by submission_date in ascending order
|
||||
submission_dates = [req["submission_date"] for req in data["domain_requests"]]
|
||||
self.assertEqual(submission_dates, sorted(submission_dates))
|
||||
|
||||
def test_filter_approved_excluded(self):
|
||||
"""test that approved requests are excluded from result set."""
|
||||
# sort in reverse chronological order of submission date, since most recent request is approved
|
||||
response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "submission_date", "order": "desc"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json
|
||||
|
||||
# Ensure no approved requests are included
|
||||
for domain_request in data["domain_requests"]:
|
||||
self.assertNotEqual(domain_request["status"], DomainRequest.DomainRequestStatus.APPROVED)
|
79
src/registrar/views/domain_requests_json.py
Normal file
79
src/registrar/views/domain_requests_json.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
from django.http import JsonResponse
|
||||
from django.core.paginator import Paginator
|
||||
from registrar.models import DomainRequest
|
||||
from django.utils.dateformat import format
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
@login_required
|
||||
def get_domain_requests_json(request):
|
||||
"""Given the current request,
|
||||
get all domain requests that are associated with the request user and exclude the APPROVED ones"""
|
||||
|
||||
domain_requests = DomainRequest.objects.filter(creator=request.user).exclude(
|
||||
status=DomainRequest.DomainRequestStatus.APPROVED
|
||||
)
|
||||
# Handle sorting
|
||||
sort_by = request.GET.get("sort_by", "id") # Default to 'id'
|
||||
order = request.GET.get("order", "asc") # Default to 'asc'
|
||||
if order == "desc":
|
||||
sort_by = f"-{sort_by}"
|
||||
domain_requests = domain_requests.order_by(sort_by)
|
||||
page_number = request.GET.get("page", 1)
|
||||
paginator = Paginator(domain_requests, 10)
|
||||
page_obj = paginator.get_page(page_number)
|
||||
|
||||
domain_requests_data = [
|
||||
{
|
||||
"requested_domain": domain_request.requested_domain.name if domain_request.requested_domain else None,
|
||||
"submission_date": domain_request.submission_date,
|
||||
"status": domain_request.get_status_display(),
|
||||
"created_at": format(domain_request.created_at, "c"), # Serialize to ISO 8601
|
||||
"id": domain_request.id,
|
||||
"is_deletable": domain_request.status
|
||||
in [DomainRequest.DomainRequestStatus.STARTED, DomainRequest.DomainRequestStatus.WITHDRAWN],
|
||||
"action_url": (
|
||||
reverse("edit-domain-request", kwargs={"id": domain_request.id})
|
||||
if domain_request.status
|
||||
in [
|
||||
DomainRequest.DomainRequestStatus.STARTED,
|
||||
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
|
||||
DomainRequest.DomainRequestStatus.WITHDRAWN,
|
||||
]
|
||||
else reverse("domain-request-status", kwargs={"pk": domain_request.id})
|
||||
),
|
||||
"action_label": (
|
||||
"Edit"
|
||||
if domain_request.status
|
||||
in [
|
||||
DomainRequest.DomainRequestStatus.STARTED,
|
||||
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
|
||||
DomainRequest.DomainRequestStatus.WITHDRAWN,
|
||||
]
|
||||
else "Manage"
|
||||
),
|
||||
"svg_icon": (
|
||||
"edit"
|
||||
if domain_request.status
|
||||
in [
|
||||
DomainRequest.DomainRequestStatus.STARTED,
|
||||
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
|
||||
DomainRequest.DomainRequestStatus.WITHDRAWN,
|
||||
]
|
||||
else "settings"
|
||||
),
|
||||
}
|
||||
for domain_request in page_obj
|
||||
]
|
||||
|
||||
return JsonResponse(
|
||||
{
|
||||
"domain_requests": domain_requests_data,
|
||||
"has_next": page_obj.has_next(),
|
||||
"has_previous": page_obj.has_previous(),
|
||||
"page": page_obj.number,
|
||||
"num_pages": paginator.num_pages,
|
||||
"total": paginator.count,
|
||||
}
|
||||
)
|
60
src/registrar/views/domains_json.py
Normal file
60
src/registrar/views/domains_json.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
from django.http import JsonResponse
|
||||
from django.core.paginator import Paginator
|
||||
from registrar.models import UserDomainRole, Domain
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
@login_required
|
||||
def get_domains_json(request):
|
||||
"""Given the current request,
|
||||
get all domains that are associated with the UserDomainRole object"""
|
||||
|
||||
user_domain_roles = UserDomainRole.objects.filter(user=request.user)
|
||||
domain_ids = user_domain_roles.values_list("domain_id", flat=True)
|
||||
|
||||
objects = Domain.objects.filter(id__in=domain_ids)
|
||||
|
||||
# Handle sorting
|
||||
sort_by = request.GET.get("sort_by", "id") # Default to 'id'
|
||||
order = request.GET.get("order", "asc") # Default to 'asc'
|
||||
|
||||
if sort_by == "state_display":
|
||||
# Fetch the objects and sort them in Python
|
||||
objects = list(objects) # Evaluate queryset to a list
|
||||
objects.sort(key=lambda domain: domain.state_display(), reverse=(order == "desc"))
|
||||
else:
|
||||
if order == "desc":
|
||||
sort_by = f"-{sort_by}"
|
||||
objects = objects.order_by(sort_by)
|
||||
|
||||
paginator = Paginator(objects, 10)
|
||||
page_number = request.GET.get("page")
|
||||
page_obj = paginator.get_page(page_number)
|
||||
|
||||
# Convert objects to JSON-serializable format
|
||||
domains = [
|
||||
{
|
||||
"id": domain.id,
|
||||
"name": domain.name,
|
||||
"expiration_date": domain.expiration_date,
|
||||
"state": domain.state,
|
||||
"state_display": domain.state_display(),
|
||||
"get_state_help_text": domain.get_state_help_text(),
|
||||
"action_url": reverse("domain", kwargs={"pk": domain.id}),
|
||||
"action_label": ("View" if domain.state in [Domain.State.DELETED, Domain.State.ON_HOLD] else "Manage"),
|
||||
"svg_icon": ("visibility" if domain.state in [Domain.State.DELETED, Domain.State.ON_HOLD] else "settings"),
|
||||
}
|
||||
for domain in page_obj.object_list
|
||||
]
|
||||
|
||||
return JsonResponse(
|
||||
{
|
||||
"domains": domains,
|
||||
"page": page_obj.number,
|
||||
"num_pages": paginator.num_pages,
|
||||
"has_previous": page_obj.has_previous(),
|
||||
"has_next": page_obj.has_next(),
|
||||
"total": paginator.count,
|
||||
}
|
||||
)
|
|
@ -1,22 +1,18 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
from registrar.models import DomainRequest, Domain, UserDomainRole
|
||||
from registrar.models import DomainRequest
|
||||
from waffle.decorators import flag_is_active
|
||||
|
||||
|
||||
def index(request):
|
||||
"""This page is available to anyone without logging in."""
|
||||
context = {}
|
||||
|
||||
if request.user.is_authenticated:
|
||||
# Get all domain requests the user has access to
|
||||
domain_requests, deletable_domain_requests = _get_domain_requests(request)
|
||||
|
||||
context["domain_requests"] = domain_requests
|
||||
|
||||
# Get all domains the user has access to
|
||||
domains = _get_domains(request)
|
||||
context["domains"] = domains
|
||||
|
||||
# Determine if the user will see domain requests that they can delete
|
||||
has_deletable_domain_requests = deletable_domain_requests.exists()
|
||||
context["has_deletable_domain_requests"] = has_deletable_domain_requests
|
||||
|
@ -55,11 +51,3 @@ def _get_domain_requests(request):
|
|||
deletable_domain_requests = domain_requests.filter(status__in=valid_statuses)
|
||||
|
||||
return (domain_requests, deletable_domain_requests)
|
||||
|
||||
|
||||
def _get_domains(request):
|
||||
"""Given the current request,
|
||||
get all domains that are associated with the UserDomainRole object"""
|
||||
user_domain_roles = UserDomainRole.objects.filter(user=request.user)
|
||||
domain_ids = user_domain_roles.values_list("domain_id", flat=True)
|
||||
return Domain.objects.filter(id__in=domain_ids)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue