mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-18 18:39:21 +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
|
### Upgrading USWDS and other JavaScript packages
|
||||||
|
|
||||||
Version numbers can be manually controlled in `package.json`. Edit that, if desired.
|
1. Version numbers can be manually controlled in `package.json`. Edit that, if desired.
|
||||||
|
2. Now run `docker-compose run node npm update`.
|
||||||
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.
|
||||||
Then run `docker-compose up` to recompile and recopy the assets.
|
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.
|
||||||
Examine the results in the running application (remember to empty your cache!) and commit `package.json` and `package-lock.json` if all is well.
|
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
|
## Finite State Machines
|
||||||
|
|
||||||
|
|
|
@ -834,3 +834,417 @@ function hideDeletedForms() {
|
||||||
(function cisaRepresentativesFormListener() {
|
(function cisaRepresentativesFormListener() {
|
||||||
HookupYesNoListener("additional_details-has_cisa_representative",'cisa-representative', null)
|
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_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 registrar.views.utility import always_404
|
||||||
from api.views import available, get_current_federal, get_current_full
|
from api.views import available, get_current_federal, get_current_full
|
||||||
|
|
||||||
|
@ -198,6 +200,8 @@ urlpatterns = [
|
||||||
views.DomainDeleteUserView.as_view(http_method_names=["post"]),
|
views.DomainDeleteUserView.as_view(http_method_names=["post"]),
|
||||||
name="domain-user-delete",
|
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
|
# 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()
|
now = timezone.now().date()
|
||||||
return self.expiration_date < now
|
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):
|
def map_epp_contact_to_public_contact(self, contact: eppInfo.InfoContactResultData, contact_id, contact_type):
|
||||||
"""Maps the Epp contact representation to a PublicContact object.
|
"""Maps the Epp contact representation to a PublicContact object.
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,8 @@
|
||||||
{% block css %}
|
{% block css %}
|
||||||
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
|
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
|
||||||
<script src="{% static 'js/uswds-init.min.js' %}" defer></script>
|
<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>
|
<script src="{% static 'js/get-gov.js' %}" defer></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -67,7 +69,6 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body id="{% block body_id %}default{% endblock %}" class="{% block body_class %}section_front{% endblock %}">
|
<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>
|
<a class="usa-skipnav" href="#main-content">Skip to main content</a>
|
||||||
|
|
||||||
{% if not IS_PRODUCTION %}
|
{% if not IS_PRODUCTION %}
|
||||||
|
|
|
@ -24,224 +24,123 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<section class="section--outlined">
|
<section class="section--outlined">
|
||||||
<h2>Domains</h2>
|
<h2 id="domains-header">Domains</h2>
|
||||||
{% if domains %}
|
<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">
|
<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>
|
<caption class="sr-only">Your registered domains</caption>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th data-sortable scope="col" role="columnheader">Domain name</th>
|
<th data-sortable="name" scope="col" role="columnheader">Domain name</th>
|
||||||
<th data-sortable scope="col" role="columnheader">Expires</th>
|
<th data-sortable="expiration_date" scope="col" role="columnheader">Expires</th>
|
||||||
<th data-sortable scope="col" role="columnheader">Status</th>
|
<th data-sortable="state_display" scope="col" role="columnheader">Status</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
role="columnheader"
|
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"
|
|
||||||
>
|
>
|
||||||
<use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#info_outline"></use>
|
<span class="usa-sr-only">Action</span>
|
||||||
</svg>
|
</th>
|
||||||
</td>
|
</tr>
|
||||||
<td>
|
</thead>
|
||||||
<a href="{% url "domain" pk=domain.pk %}">
|
<tbody>
|
||||||
<svg
|
<!-- AJAX will populate this tbody -->
|
||||||
class="usa-icon"
|
</tbody>
|
||||||
aria-hidden="true"
|
</table>
|
||||||
focusable="false"
|
<div
|
||||||
role="img"
|
class="usa-sr-only usa-table__announcement-region"
|
||||||
width="24"
|
aria-live="polite"
|
||||||
>
|
></div>
|
||||||
{% if domain.state == "deleted" or domain.state == "on hold" %}
|
</div>
|
||||||
<use xlink:href="{%static 'img/sprite.svg'%}#visibility"></use>
|
<div class="no-domains-wrapper display-none">
|
||||||
</svg>
|
<p>You don't have any registered domains.</p>
|
||||||
View <span class="usa-sr-only">{{ domain.name }}</span>
|
<p class="maxw-none clearfix">
|
||||||
{% else %}
|
<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">
|
||||||
<use xlink:href="{%static 'img/sprite.svg'%}#settings"></use>
|
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||||
</svg>
|
<use xlink:href="{%static 'img/sprite.svg'%}#help_outline"></use>
|
||||||
Manage <span class="usa-sr-only">{{ domain.name }}</span>
|
</svg>
|
||||||
{% endif %}
|
Why don't I see my domain when I sign in to the registrar?
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</p>
|
||||||
</tr>
|
</div>
|
||||||
{% 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 %}
|
|
||||||
</section>
|
</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">
|
<section class="section--outlined">
|
||||||
<h2>Domain requests</h2>
|
<h2 id="domain-requests-header">Domain requests</h2>
|
||||||
{% if domain_requests %}
|
<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">
|
<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>
|
<caption class="sr-only">Your domain requests</caption>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th data-sortable scope="col" role="columnheader">Domain name</th>
|
<th data-sortable="requested_domain__name" scope="col" role="columnheader">Domain name</th>
|
||||||
<th data-sortable scope="col" role="columnheader">Date submitted</th>
|
<th data-sortable="submission_date" scope="col" role="columnheader">Date submitted</th>
|
||||||
<th data-sortable scope="col" role="columnheader">Status</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">Action</span></th>
|
||||||
{% if has_deletable_domain_requests %}
|
<th scope="col" role="columnheader"><span class="usa-sr-only">Delete Action</span></th>
|
||||||
<th scope="col" role="columnheader"><span class="usa-sr-only">Delete Action</span></th>
|
</tr>
|
||||||
{% endif %}
|
</thead>
|
||||||
</tr>
|
<tbody id="domain-requests-tbody">
|
||||||
</thead>
|
<!-- AJAX will populate this tbody -->
|
||||||
<tbody>
|
</tbody>
|
||||||
{% for domain_request in domain_requests %}
|
</table>
|
||||||
<tr>
|
<div
|
||||||
<th th scope="row" role="rowheader" data-label="Domain name">
|
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 %}
|
{% if domain_request.requested_domain is None %}
|
||||||
New domain request
|
{% if domain_request.created_at %}
|
||||||
{# Add a breakpoint #}
|
{% with prefix="(created " %}
|
||||||
<div aria-hidden="true"></div>
|
{% with formatted_date=domain_request.created_at|date:"DATETIME_FORMAT" %}
|
||||||
<span class="text-base font-body-xs">({{ domain_request.created_at }} UTC)</span>
|
{% with modal_content=prefix|add:formatted_date|add:" UTC)" %}
|
||||||
{% else %}
|
{% 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 %}
|
||||||
{{ domain_request.requested_domain.name }}
|
{% endwith %}
|
||||||
{% 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 %}
|
|
||||||
{% endwith %}
|
{% 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 %}
|
{% endwith %}
|
||||||
</a>
|
{% endif %}
|
||||||
|
</form>
|
||||||
<div
|
</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 %}
|
{% endif %}
|
||||||
</tr>
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
|
||||||
</table>
|
</div>
|
||||||
<div
|
<div class="no-domain-requests-wrapper display-none">
|
||||||
class="usa-sr-only usa-table__announcement-region"
|
<p>You haven't requested any domains.</p>
|
||||||
aria-live="polite"
|
</div>
|
||||||
></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 %}
|
|
||||||
</section>
|
</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 #}
|
{# 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, "You don't have any registered domains.")
|
||||||
self.assertContains(response, "Why don't I see my domain when I sign in to the registrar?")
|
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):
|
def test_state_help_text(self):
|
||||||
"""Tests if each domain state has help text"""
|
"""Tests if each domain state has help text"""
|
||||||
|
|
||||||
# Get the expected text content of each state
|
# Get the expected text content of each state
|
||||||
deleted_text = "This domain has been removed and " "is no longer registered to your organization."
|
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."
|
ready_text = "This domain has name servers and is ready for use."
|
||||||
on_hold_text = (
|
on_hold_text = "This domain is administratively paused, "
|
||||||
"This domain is administratively paused, "
|
|
||||||
"so it can’t be edited and won’t resolve in DNS. "
|
|
||||||
"Contact help@get.gov for details."
|
|
||||||
)
|
|
||||||
deleted_text = "This domain has been removed and " "is no longer registered to your organization."
|
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
|
# Generate a mapping of domain names, the state, and expected messages for the subtest
|
||||||
test_cases = [
|
test_cases = [
|
||||||
|
@ -206,12 +189,11 @@ class HomeTests(TestWithUser):
|
||||||
user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER
|
user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER
|
||||||
)
|
)
|
||||||
|
|
||||||
# Grab the home page
|
# Grab the json response for domain list
|
||||||
response = self.client.get("/")
|
response = self.client.get("/get-domains-json/")
|
||||||
|
|
||||||
# Make sure the user can actually see the domain.
|
# Make sure the domain is in the list.
|
||||||
# We expect two instances because of SR content.
|
self.assertContains(response, domain_name, count=1)
|
||||||
self.assertContains(response, domain_name, count=2)
|
|
||||||
|
|
||||||
# Check that we have the right text content.
|
# Check that we have the right text content.
|
||||||
self.assertContains(response, expected_message, count=1)
|
self.assertContains(response, expected_message, count=1)
|
||||||
|
@ -222,19 +204,18 @@ class HomeTests(TestWithUser):
|
||||||
|
|
||||||
def test_state_help_text_expired(self):
|
def test_state_help_text_expired(self):
|
||||||
"""Tests if each domain state has help text when expired"""
|
"""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, _ = Domain.objects.get_or_create(name="expired.gov", state=Domain.State.READY)
|
||||||
test_domain.expiration_date = date(2011, 10, 10)
|
test_domain.expiration_date = date(2011, 10, 10)
|
||||||
test_domain.save()
|
test_domain.save()
|
||||||
|
|
||||||
UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER)
|
UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER)
|
||||||
|
|
||||||
# Grab the home page
|
# Grab the json response of the domains list
|
||||||
response = self.client.get("/")
|
response = self.client.get("/get-domains-json/")
|
||||||
|
|
||||||
# Make sure the user can actually see the domain.
|
# Make sure the domain is in the response
|
||||||
# We expect two instances because of SR content.
|
self.assertContains(response, "expired.gov", count=1)
|
||||||
self.assertContains(response, "expired.gov", count=2)
|
|
||||||
|
|
||||||
# Check that we have the right text content.
|
# Check that we have the right text content.
|
||||||
self.assertContains(response, expired_text, count=1)
|
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"""
|
"""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. == #
|
# == 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, _ = Domain.objects.get_or_create(name="imexpired.gov", state=Domain.State.READY)
|
||||||
test_domain.expiration_date = None
|
test_domain.expiration_date = None
|
||||||
test_domain.save()
|
test_domain.save()
|
||||||
|
|
||||||
UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER)
|
UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER)
|
||||||
|
|
||||||
# Grab the home page
|
# Grab the json response of the domains list
|
||||||
response = self.client.get("/")
|
response = self.client.get("/get-domains-json/")
|
||||||
|
|
||||||
# Make sure the user can actually see the domain.
|
# Make sure domain is in the response
|
||||||
# We expect two instances because of SR content.
|
self.assertContains(response, "imexpired.gov", count=1)
|
||||||
self.assertContains(response, "imexpired.gov", count=2)
|
|
||||||
|
|
||||||
# Make sure the expiration date is None
|
# Make sure the expiration date is None
|
||||||
self.assertEqual(test_domain.expiration_date, None)
|
self.assertEqual(test_domain.expiration_date, None)
|
||||||
|
@ -264,19 +244,18 @@ class HomeTests(TestWithUser):
|
||||||
self.assertContains(response, expired_text, count=1)
|
self.assertContains(response, expired_text, count=1)
|
||||||
|
|
||||||
# == Test a expiration of None for state unknown. This should not display expired text. == #
|
# == 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, _ = Domain.objects.get_or_create(name="notexpired.gov", state=Domain.State.UNKNOWN)
|
||||||
test_domain_2.expiration_date = None
|
test_domain_2.expiration_date = None
|
||||||
test_domain_2.save()
|
test_domain_2.save()
|
||||||
|
|
||||||
UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain_2, role=UserDomainRole.Roles.MANAGER)
|
UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain_2, role=UserDomainRole.Roles.MANAGER)
|
||||||
|
|
||||||
# Grab the home page
|
# Grab the json response of the domains list
|
||||||
response = self.client.get("/")
|
response = self.client.get("/get-domains-json/")
|
||||||
|
|
||||||
# Make sure the user can actually see the domain.
|
# Make sure the response contains the domain
|
||||||
# We expect two instances because of SR content.
|
self.assertContains(response, "notexpired.gov", count=1)
|
||||||
self.assertContains(response, "notexpired.gov", count=2)
|
|
||||||
|
|
||||||
# Make sure the expiration date is None
|
# Make sure the expiration date is None
|
||||||
self.assertEqual(test_domain_2.expiration_date, 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
|
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
|
# Trigger the delete logic
|
||||||
response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
|
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
|
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
|
# Trigger the delete logic
|
||||||
response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
|
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, "igorville.gov")
|
||||||
self.assertContains(detail_page, "Status")
|
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):
|
def test_unknown_domain_does_not_show_as_expired_on_detail_page(self):
|
||||||
"""An UNKNOWN domain should not exist on the detail_page anymore.
|
"""An UNKNOWN domain should not exist on the detail_page anymore.
|
||||||
It shows as 'DNS needed'"""
|
It shows as 'DNS needed'"""
|
||||||
|
@ -258,11 +237,9 @@ class TestDomainDetail(TestDomainOverview):
|
||||||
user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER
|
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")
|
igorville = Domain.objects.get(name="igorville.gov")
|
||||||
self.assertEquals(igorville.state, Domain.State.UNKNOWN)
|
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.assertContains(detail_page, "Expired")
|
||||||
|
|
||||||
self.assertNotContains(detail_page, "DNS needed")
|
self.assertNotContains(detail_page, "DNS needed")
|
||||||
|
@ -274,26 +251,18 @@ class TestDomainDetail(TestDomainOverview):
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
self.user.status = User.RESTRICTED
|
self.user.status = User.RESTRICTED
|
||||||
self.user.save()
|
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}))
|
response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
def test_domain_detail_allowed_for_on_hold(self):
|
def test_domain_detail_allowed_for_on_hold(self):
|
||||||
"""Test that the domain overview page displays for on hold domain"""
|
"""Test that the domain overview page displays for on hold domain"""
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
home_page = self.app.get("/")
|
|
||||||
self.assertContains(home_page, "on-hold.gov")
|
|
||||||
|
|
||||||
# View domain overview page
|
# View domain overview page
|
||||||
detail_page = self.client.get(reverse("domain", kwargs={"pk": self.domain_on_hold.id}))
|
detail_page = self.client.get(reverse("domain", kwargs={"pk": self.domain_on_hold.id}))
|
||||||
self.assertNotContains(detail_page, "Edit")
|
self.assertNotContains(detail_page, "Edit")
|
||||||
|
|
||||||
def test_domain_detail_see_just_nameserver(self):
|
def test_domain_detail_see_just_nameserver(self):
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
home_page = self.app.get("/")
|
|
||||||
self.assertContains(home_page, "justnameserver.com")
|
|
||||||
|
|
||||||
# View nameserver on Domain Overview page
|
# View nameserver on Domain Overview page
|
||||||
detail_page = self.app.get(reverse("domain", kwargs={"pk": self.domain_just_nameserver.id}))
|
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):
|
def test_domain_detail_see_nameserver_and_ip(self):
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
home_page = self.app.get("/")
|
|
||||||
self.assertContains(home_page, "nameserverwithip.gov")
|
|
||||||
|
|
||||||
# View nameserver on Domain Overview page
|
# View nameserver on Domain Overview page
|
||||||
detail_page = self.app.get(reverse("domain", kwargs={"pk": self.domain_with_ip.id}))
|
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"""
|
management pages share the same permissions class"""
|
||||||
self.user.status = User.RESTRICTED
|
self.user.status = User.RESTRICTED
|
||||||
self.user.save()
|
self.user.save()
|
||||||
home_page = self.app.get("/")
|
|
||||||
self.assertContains(home_page, "igorville.gov")
|
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
|
response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
|
||||||
self.assertEqual(response.status_code, 403)
|
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):
|
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'"""
|
"""Tests that user is NOT presented with intro acknowledgement page when accessed through 'edit'"""
|
||||||
completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user)
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user)
|
||||||
home_page = self.app.get("/")
|
detail_page = self.app.get(f"/domain-request/{domain_request.id}/edit/")
|
||||||
self.assertContains(home_page, "city.gov")
|
|
||||||
# click the "Edit" link
|
|
||||||
detail_page = home_page.click("Edit", index=0)
|
|
||||||
# Check that the response is a redirect
|
# Check that the response is a redirect
|
||||||
self.assertEqual(detail_page.status_code, 302)
|
self.assertEqual(detail_page.status_code, 302)
|
||||||
# You can access the 'Location' header to get the redirect URL
|
# 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 = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
|
||||||
domain_request.save()
|
domain_request.save()
|
||||||
|
|
||||||
home_page = self.app.get("/")
|
detail_page = self.app.get(f"/domain-request/{domain_request.id}")
|
||||||
self.assertContains(home_page, "city.gov")
|
|
||||||
# click the "Manage" link
|
|
||||||
detail_page = home_page.click("Manage", index=0)
|
|
||||||
self.assertContains(detail_page, "city.gov")
|
self.assertContains(detail_page, "city.gov")
|
||||||
self.assertContains(detail_page, "city1.gov")
|
self.assertContains(detail_page, "city1.gov")
|
||||||
self.assertContains(detail_page, "Chief Tester")
|
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 = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
|
||||||
domain_request.save()
|
domain_request.save()
|
||||||
|
|
||||||
home_page = self.app.get("/")
|
detail_page = self.app.get(f"/domain-request/{domain_request.id}")
|
||||||
self.assertContains(home_page, "city.gov")
|
|
||||||
# click the "Manage" link
|
|
||||||
detail_page = home_page.click("Manage", index=0)
|
|
||||||
self.assertContains(detail_page, "city.gov")
|
self.assertContains(detail_page, "city.gov")
|
||||||
self.assertContains(detail_page, "Chief Tester")
|
self.assertContains(detail_page, "Chief Tester")
|
||||||
self.assertContains(detail_page, "testy@town.com")
|
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 = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
|
||||||
domain_request.save()
|
domain_request.save()
|
||||||
|
|
||||||
home_page = self.app.get("/")
|
detail_page = self.app.get(f"/domain-request/{domain_request.id}")
|
||||||
self.assertContains(home_page, "city.gov")
|
|
||||||
# click the "Manage" link
|
|
||||||
detail_page = home_page.click("Manage", index=0)
|
|
||||||
self.assertContains(detail_page, "city.gov")
|
self.assertContains(detail_page, "city.gov")
|
||||||
self.assertContains(detail_page, "city1.gov")
|
self.assertContains(detail_page, "city1.gov")
|
||||||
self.assertContains(detail_page, "Chief Tester")
|
self.assertContains(detail_page, "Chief Tester")
|
||||||
|
@ -2485,8 +2473,8 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
|
||||||
target_status_code=200,
|
target_status_code=200,
|
||||||
fetch_redirect_response=True,
|
fetch_redirect_response=True,
|
||||||
)
|
)
|
||||||
home_page = self.app.get("/")
|
response = self.client.get("/get-domain-requests-json/")
|
||||||
self.assertContains(home_page, "Withdrawn")
|
self.assertContains(response, "Withdrawn")
|
||||||
|
|
||||||
def test_domain_request_withdraw_no_permissions(self):
|
def test_domain_request_withdraw_no_permissions(self):
|
||||||
"""Can't withdraw domain requests as a restricted user."""
|
"""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 = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
|
||||||
domain_request.save()
|
domain_request.save()
|
||||||
|
|
||||||
home_page = self.app.get("/")
|
detail_page = self.app.get(f"/domain-request/{domain_request.id}")
|
||||||
self.assertContains(home_page, "city.gov")
|
|
||||||
# click the "Manage" link
|
|
||||||
detail_page = home_page.click("Manage", index=0)
|
|
||||||
self.assertContains(detail_page, "city.gov")
|
self.assertContains(detail_page, "city.gov")
|
||||||
self.assertContains(detail_page, "city1.gov")
|
self.assertContains(detail_page, "city1.gov")
|
||||||
self.assertContains(detail_page, "Chief Tester")
|
self.assertContains(detail_page, "Chief Tester")
|
||||||
|
@ -2568,9 +2553,9 @@ class TestWizardUnlockingSteps(TestWithUser, WebTest):
|
||||||
def test_unlocked_steps_full_domain_request(self):
|
def test_unlocked_steps_full_domain_request(self):
|
||||||
"""Test when all fields in the domain request are filled."""
|
"""Test when all fields in the domain request are filled."""
|
||||||
|
|
||||||
completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user)
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user)
|
||||||
# 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
|
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||||
# resetting the session key on each new request, thus destroying the concept
|
# 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
|
# 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]
|
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
|
||||||
# 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
|
# Check if the response is a redirect
|
||||||
if response.status_code == 302:
|
if response.status_code == 302:
|
||||||
# Follow the redirect manually
|
# Follow the redirect manually
|
||||||
|
@ -2635,8 +2613,7 @@ class TestWizardUnlockingSteps(TestWithUser, WebTest):
|
||||||
)
|
)
|
||||||
domain_request.other_contacts.set([contact_2])
|
domain_request.other_contacts.set([contact_2])
|
||||||
|
|
||||||
# Make a request to the home page
|
response = self.app.get(f"/domain-request/{domain_request.id}/edit/")
|
||||||
home_page = self.app.get("/")
|
|
||||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||||
# resetting the session key on each new request, thus destroying the concept
|
# 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
|
# 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]
|
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
|
||||||
# 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
|
# Check if the response is a redirect
|
||||||
if response.status_code == 302:
|
if response.status_code == 302:
|
||||||
# Follow the redirect manually
|
# 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 django.shortcuts import render
|
||||||
|
from registrar.models import DomainRequest
|
||||||
from registrar.models import DomainRequest, Domain, UserDomainRole
|
|
||||||
from waffle.decorators import flag_is_active
|
from waffle.decorators import flag_is_active
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
"""This page is available to anyone without logging in."""
|
"""This page is available to anyone without logging in."""
|
||||||
context = {}
|
context = {}
|
||||||
|
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
# Get all domain requests the user has access to
|
# Get all domain requests the user has access to
|
||||||
domain_requests, deletable_domain_requests = _get_domain_requests(request)
|
domain_requests, deletable_domain_requests = _get_domain_requests(request)
|
||||||
|
|
||||||
context["domain_requests"] = domain_requests
|
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
|
# Determine if the user will see domain requests that they can delete
|
||||||
has_deletable_domain_requests = deletable_domain_requests.exists()
|
has_deletable_domain_requests = deletable_domain_requests.exists()
|
||||||
context["has_deletable_domain_requests"] = has_deletable_domain_requests
|
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)
|
deletable_domain_requests = domain_requests.filter(status__in=valid_statuses)
|
||||||
|
|
||||||
return (domain_requests, deletable_domain_requests)
|
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