mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-26 09:13:58 -04:00
Compare commits
69 Commits
04/03/2025
...
53034b7e89
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53034b7e89 | ||
|
|
5464da155a | ||
|
|
01678ce67a | ||
|
|
67b7dc28b4 | ||
|
|
fbd70b7c38 | ||
|
|
06ce617f0a | ||
|
|
1afe8121b8 | ||
|
|
9d7b6d3147 | ||
|
|
7b2d031591 | ||
|
|
81d4fc0b8c | ||
|
|
0b441e36f1 | ||
|
|
ae3be157e6 | ||
|
|
1bf7e58445 | ||
|
|
16b1429eb0 | ||
|
|
64ac382af9 | ||
|
|
d3e17c743b | ||
|
|
9eec7cdba5 | ||
|
|
e59ddfcae5 | ||
|
|
38d8bdea0a | ||
|
|
1967195da0 | ||
|
|
fb11fbfba3 | ||
|
|
17a7080f46 | ||
|
|
a962da9c7d | ||
|
|
3a468c77aa | ||
|
|
f3c3a60875 | ||
|
|
487866d81c | ||
|
|
b71a941634 | ||
|
|
c599407850 | ||
|
|
42ac6912b0 | ||
|
|
253934a495 | ||
|
|
fff81870e5 | ||
|
|
970483ef0b | ||
|
|
ba661a0c53 | ||
|
|
f5ce055079 | ||
|
|
29c2eafbd7 | ||
|
|
6ecbc7ebab | ||
|
|
f3688d62d5 | ||
|
|
6f6fd01ea2 | ||
|
|
1f13176fa6 | ||
|
|
09c6d0445d | ||
|
|
c328353e18 | ||
|
|
e9e0601237 | ||
|
|
9921809f90 | ||
|
|
88f29a6727 | ||
|
|
9d4d55a670 | ||
|
|
3211c5a16f | ||
|
|
9c8aa9c0e2 | ||
|
|
c2508f7230 | ||
|
|
b5675e586e | ||
|
|
823b490331 | ||
|
|
e69d6f807f | ||
|
|
ee8213f670 | ||
|
|
c43174be78 | ||
|
|
548072fe66 | ||
|
|
7f3e368427 | ||
|
|
caed2ad5d6 | ||
|
|
3401b994cd | ||
|
|
d5798bb818 | ||
|
|
0cf9abf5e8 | ||
|
|
0bdf0cb073 | ||
|
|
b8b7c2df69 | ||
|
|
db37a4de94 | ||
|
|
b3b31d0732 | ||
|
|
ef779daec1 | ||
|
|
50df9c79a2 | ||
|
|
e4a4217dba | ||
|
|
3179471122 | ||
|
|
96890199f3 | ||
|
|
700c76807b |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -14,6 +14,10 @@
|
||||
*.XLSX
|
||||
|
||||
## Non user files allowed to be commited
|
||||
!app/assets/pdf/best-practices-for-texting-the-public.pdf
|
||||
!app/assets/pdf/investing-in-notifications-tts-public-benefits-studio-decision-memo.pdf
|
||||
!app/assets/pdf/best-practices-section-outline.pdf
|
||||
!app/assets/pdf/standing-up-your-own-notify.pdf
|
||||
!app/assets/pdf/tcpa_overview.pdf
|
||||
!app/assets/pdf/investing-notifications-tts-public-benefits-memo.pdf
|
||||
!app/assets/pdf/out-of-pilot-announcement.pdf
|
||||
|
||||
@@ -8,6 +8,7 @@ repos:
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
exclude: ^app/assets/pdf/.*\.pdf$
|
||||
- id: debug-statements
|
||||
- id: check-merge-conflict
|
||||
- id: check-toml
|
||||
|
||||
18
Makefile
18
Makefile
@@ -6,6 +6,7 @@ APP_VERSION_FILE = app/version.py
|
||||
|
||||
GIT_BRANCH ?= $(shell git symbolic-ref --short HEAD 2> /dev/null || echo "detached")
|
||||
GIT_COMMIT ?= $(shell git rev-parse HEAD 2> /dev/null || echo "")
|
||||
GIT_HOOKS_PATH ?= $(shell git config --global core.hooksPath || echo "")
|
||||
|
||||
VIRTUALENV_ROOT := $(shell [ -z $$VIRTUAL_ENV ] && echo $$(pwd)/venv || echo $$VIRTUAL_ENV)
|
||||
|
||||
@@ -14,7 +15,8 @@ NVMSH := $(shell [ -f "$(HOME)/.nvm/nvm.sh" ] && echo "$(HOME)/.nvm/nvm.sh" || e
|
||||
## DEVELOPMENT
|
||||
|
||||
.PHONY: bootstrap
|
||||
bootstrap: generate-version-file ## Set up everything to run the app
|
||||
bootstrap: ## Set up everything to run the app
|
||||
make generate-version-file
|
||||
poetry self add poetry-dotenv-plugin
|
||||
poetry lock --no-update
|
||||
poetry install --sync --no-root
|
||||
@@ -24,6 +26,20 @@ bootstrap: generate-version-file ## Set up everything to run the app
|
||||
source $(NVMSH) && npm ci --no-audit
|
||||
source $(NVMSH) && npm run build
|
||||
|
||||
.PHONY: bootstrap-with-git-hooks
|
||||
bootstrap-with-git-hooks: ## Sets everything up and accounts for pre-existing git hooks
|
||||
make generate-version-file
|
||||
poetry self add poetry-dotenv-plugin
|
||||
poetry lock --no-update
|
||||
poetry install --sync --no-root
|
||||
poetry run playwright install --with-deps
|
||||
git config --global --unset-all core.hooksPath
|
||||
poetry run pre-commit install
|
||||
git config --global core.hookspath "${GIT_HOOKS_PATH}"
|
||||
source $(NVMSH) --no-use && nvm install && npm install
|
||||
source $(NVMSH) && npm ci --no-audit
|
||||
source $(NVMSH) && npm run build
|
||||
|
||||
.PHONY: watch-frontend
|
||||
watch-frontend: ## Build frontend and watch for changes
|
||||
source $(NVMSH) && npm run watch
|
||||
|
||||
@@ -145,6 +145,7 @@ def _csp(config):
|
||||
"frame-src": [
|
||||
"https://www.youtube.com",
|
||||
"https://www.youtube-nocookie.com",
|
||||
"https://www.googletagmanager.com",
|
||||
],
|
||||
"frame-ancestors": "'none'",
|
||||
"form-action": "'self'",
|
||||
@@ -169,6 +170,11 @@ def _csp(config):
|
||||
|
||||
|
||||
def create_app(application):
|
||||
@application.after_request
|
||||
def add_csp_header(response):
|
||||
existing_csp = response.headers.get("Content-Security-Policy", "")
|
||||
response.headers["Content-Security-Policy"] = existing_csp + "; form-action 'self';"
|
||||
return response
|
||||
# @application.context_processor
|
||||
# def inject_feature_flags():
|
||||
# this is where feature flags can be easily added as a dictionary within context
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
(function(Modules) {
|
||||
"use strict";
|
||||
|
||||
let isSixDigitHex = value => value.match(/^#[0-9A-F]{6}$/i);
|
||||
let colourOrWhite = value => isSixDigitHex(value) ? value : '#FFFFFF';
|
||||
|
||||
Modules.ColourPreview = function() {
|
||||
|
||||
this.start = component => {
|
||||
|
||||
this.$input = $(component);
|
||||
|
||||
this.$input.closest('.usa-form-group').append(
|
||||
this.$preview = $('<span class="textbox-colour-preview"></span>')
|
||||
);
|
||||
|
||||
this.$input
|
||||
.on('input', this.update)
|
||||
.trigger('input');
|
||||
|
||||
};
|
||||
|
||||
this.update = () => this.$preview.css(
|
||||
'background', colourOrWhite(this.$input.val())
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
})(window.GOVUK.Modules);
|
||||
@@ -1,124 +1,41 @@
|
||||
(function(Modules) {
|
||||
"use strict";
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
Modules.FullscreenTable = function() {
|
||||
function initFullscreenTables() {
|
||||
const tables = document.querySelectorAll('.fullscreen-table');
|
||||
|
||||
this.start = function(component) {
|
||||
tables.forEach((component) => {
|
||||
const table = component.querySelector('table');
|
||||
const wrapper = component.querySelector('.table-wrapper');
|
||||
const rightShadow = component.querySelector('.fullscreen-right-shadow');
|
||||
|
||||
this.$component = $(component);
|
||||
this.$table = this.$component.find('table');
|
||||
this.nativeHeight = this.$component.innerHeight() + 20; // 20px to allow room for scrollbar
|
||||
this.topOffset = this.$component.offset().top;
|
||||
if (!table || !wrapper) return;
|
||||
|
||||
this.insertShims();
|
||||
this.maintainWidth();
|
||||
this.maintainHeight();
|
||||
this.toggleShadows();
|
||||
const toggleShadows = () => {
|
||||
const scrollLeft = wrapper.scrollLeft;
|
||||
const maxScrollLeft = wrapper.scrollWidth - wrapper.clientWidth;
|
||||
|
||||
$(window)
|
||||
.on('scroll resize', this.maintainHeight)
|
||||
.on('resize', this.maintainWidth);
|
||||
wrapper.classList.toggle('scrolled', scrollLeft > 0);
|
||||
|
||||
this.$scrollableTable
|
||||
.on('scroll', this.toggleShadows)
|
||||
.on('scroll', this.maintainHeight)
|
||||
.on('focus blur', () => this.$component.toggleClass('js-focus-style'));
|
||||
if (rightShadow) {
|
||||
rightShadow.classList.toggle('visible', scrollLeft < maxScrollLeft);
|
||||
}
|
||||
};
|
||||
|
||||
if (
|
||||
window.GOVUK.stickAtBottomWhenScrolling &&
|
||||
window.GOVUK.stickAtBottomWhenScrolling.recalculate
|
||||
) {
|
||||
window.GOVUK.stickAtBottomWhenScrolling.recalculate();
|
||||
}
|
||||
wrapper.addEventListener('scroll', toggleShadows);
|
||||
toggleShadows(); // init state
|
||||
});
|
||||
}
|
||||
|
||||
this.maintainWidth();
|
||||
// Auto-init on page load in browser
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initFullscreenTables);
|
||||
} else {
|
||||
initFullscreenTables();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.insertShims = () => {
|
||||
|
||||
const attributesForFocus = 'role aria-labelledby tabindex';
|
||||
let captionId = this.$table.find('caption').text().toLowerCase().replace(/[^A-Za-z]+/g, '');
|
||||
|
||||
this.$table.find('caption').attr('id', captionId);
|
||||
this.$table.wrap(`<div class="fullscreen-scrollable-table" role="region" aria-labelledby="${captionId}" tabindex="0"/>`);
|
||||
|
||||
this.$component
|
||||
.append(
|
||||
this.$component.find('.fullscreen-scrollable-table')
|
||||
.clone()
|
||||
.addClass('fullscreen-fixed-table')
|
||||
.removeClass('fullscreen-scrollable-table')
|
||||
.removeAttr(attributesForFocus)
|
||||
.attr('aria-hidden', true)
|
||||
.find('caption')
|
||||
.removeAttr('id')
|
||||
.closest('.fullscreen-fixed-table')
|
||||
)
|
||||
.append(
|
||||
'<div class="fullscreen-right-shadow" />'
|
||||
)
|
||||
.after(
|
||||
$("<div class='fullscreen-shim'/>").css({
|
||||
'height': this.nativeHeight,
|
||||
'top': this.topOffset
|
||||
})
|
||||
)
|
||||
.css('position', 'absolute');
|
||||
|
||||
this.$scrollableTable = this.$component.find('.fullscreen-scrollable-table');
|
||||
this.$fixedTable = this.$component.find('.fullscreen-fixed-table');
|
||||
|
||||
};
|
||||
|
||||
this.maintainHeight = () => {
|
||||
|
||||
let height = Math.min(
|
||||
$(window).height() - this.topOffset + $(window).scrollTop(),
|
||||
this.nativeHeight
|
||||
);
|
||||
|
||||
this.$scrollableTable.outerHeight(height);
|
||||
this.$fixedTable.outerHeight(height);
|
||||
|
||||
};
|
||||
|
||||
this.maintainWidth = () => {
|
||||
|
||||
let indexColumnWidth = this.$fixedTable.find('.table-field-index').outerWidth();
|
||||
|
||||
this.$scrollableTable
|
||||
.css({
|
||||
'width': this.$component.parent('main').width() - indexColumnWidth,
|
||||
'margin-left': indexColumnWidth
|
||||
});
|
||||
|
||||
this.$fixedTable
|
||||
.width(indexColumnWidth + 4);
|
||||
|
||||
};
|
||||
|
||||
this.toggleShadows = () => {
|
||||
|
||||
this.$fixedTable
|
||||
.toggleClass(
|
||||
'fullscreen-scrolled-table',
|
||||
this.$scrollableTable.scrollLeft() > 0
|
||||
);
|
||||
|
||||
this.$component.find('.fullscreen-right-shadow')
|
||||
.toggleClass(
|
||||
'visible',
|
||||
this.$scrollableTable.scrollLeft() < (this.$table.width() - this.$scrollableTable.width())
|
||||
);
|
||||
|
||||
setTimeout(
|
||||
() => this.$component.find('.fullscreen-right-shadow').addClass('with-transition'),
|
||||
3000
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
})(window.GOVUK.Modules);
|
||||
// Expose it globally in test environments
|
||||
if (typeof window !== 'undefined') {
|
||||
window.initFullscreenTables = initFullscreenTables;
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
(function(window) {
|
||||
"use strict";
|
||||
|
||||
window.GOVUK.Modules.Homepage = function() {
|
||||
|
||||
this.start = function(component) {
|
||||
|
||||
let $component = $(component);
|
||||
let iterations = 0;
|
||||
let timeout = null;
|
||||
|
||||
$component.on('click', () => {
|
||||
if (++iterations == 5) {
|
||||
$component.toggleClass('product-page-intro-wrapper--alternative');
|
||||
}
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => iterations = 0, 1500);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
})(window);
|
||||
@@ -1,27 +1,38 @@
|
||||
window.GOVUK.Frontend.initAll();
|
||||
|
||||
$(() => $("time.timeago").timeago());
|
||||
// Initialize GOV.UK Frontend components (modern init)
|
||||
window.GOVUKFrontend.initAll();
|
||||
|
||||
var showHideContent = new GOVUK.ShowHideContent();
|
||||
showHideContent.init();
|
||||
|
||||
$(() => GOVUK.modules.start());
|
||||
|
||||
$(() => $('.error-message, .usa-error-message').eq(0).parent('label').next('input').trigger('focus'));
|
||||
|
||||
$(() => $('.govuk-header__container').on('click', function() {
|
||||
$(this).css('border-color', '#005ea5');
|
||||
}));
|
||||
|
||||
// Applies our expanded focus style to the siblings of links when that link is wrapped in a heading.
|
||||
//
|
||||
// This will be possible in CSS in the future, using the :has pseudo-class. When :has is available
|
||||
// in the browsers we support, this code can be replaced with a CSS-only solution.
|
||||
$('.js-mark-focus-on-parent').on('focus blur', '*', e => {
|
||||
$target = $(e.target);
|
||||
if (e.type === 'focusin') {
|
||||
$target.parent().addClass('js-child-has-focus');
|
||||
} else {
|
||||
$target.parent().removeClass('js-child-has-focus');
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Autofocus the first error input
|
||||
const errorEl = document.querySelector('.error-message, .usa-error-message');
|
||||
if (errorEl) {
|
||||
const label = errorEl.closest('label');
|
||||
if (label) {
|
||||
const input = label.nextElementSibling;
|
||||
if (input && input.focus) {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Optional: highlight header on click
|
||||
const header = document.querySelector('.govuk-header__container');
|
||||
if (header) {
|
||||
header.addEventListener('click', () => {
|
||||
header.style.borderColor = '#005ea5';
|
||||
});
|
||||
}
|
||||
|
||||
// Focus styling for heading-wrapped links
|
||||
const containers = document.querySelectorAll('.js-mark-focus-on-parent');
|
||||
containers.forEach(container => {
|
||||
container.addEventListener('focusin', e => {
|
||||
e.target.parentElement.classList.add('js-child-has-focus');
|
||||
});
|
||||
container.addEventListener('focusout', e => {
|
||||
e.target.parentElement.classList.remove('js-child-has-focus');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
BIN
app/assets/pdf/best-practices-for-texting-the-public.pdf
Normal file
BIN
app/assets/pdf/best-practices-for-texting-the-public.pdf
Normal file
Binary file not shown.
BIN
app/assets/pdf/best-practices-section-outline.pdf
Normal file
BIN
app/assets/pdf/best-practices-section-outline.pdf
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/assets/pdf/standing-up-your-own-notify.pdf
Normal file
BIN
app/assets/pdf/standing-up-your-own-notify.pdf
Normal file
Binary file not shown.
@@ -49,12 +49,12 @@
|
||||
}
|
||||
|
||||
.sms-message-sender, .sms-message-file-name, .sms-message-scheduler, .sms-message-template, .sms-message-sender {
|
||||
margin:0.25rem 0 0;
|
||||
margin: units(0.5) 0 0;
|
||||
}
|
||||
|
||||
.sms-message-recipient {
|
||||
color: color('gray-cool-90');
|
||||
margin: units(1) 0 units(1);
|
||||
margin: units(0.5) 0 units(2);
|
||||
}
|
||||
|
||||
.sms-message-status {
|
||||
@@ -174,8 +174,10 @@ h2.recipient-list {
|
||||
}
|
||||
|
||||
.spreadsheet {
|
||||
|
||||
margin-bottom: units (1);
|
||||
margin-bottom: units(1);
|
||||
overflow-x: auto;
|
||||
position: relative;
|
||||
padding-left: 0;
|
||||
|
||||
.table {
|
||||
margin-bottom: 0;
|
||||
@@ -186,27 +188,56 @@ h2.recipient-list {
|
||||
}
|
||||
|
||||
th,
|
||||
.table-field-index {
|
||||
background: color('gray-cool-10');
|
||||
td {
|
||||
padding-inline: 10px;
|
||||
border: 1px solid color('gray-cool-10');
|
||||
background: white;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
td {
|
||||
min-width: calc(194px - 11px);
|
||||
|
||||
&:first-child {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
th.table-field-heading {
|
||||
background: color('gray-cool-10');
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
border: 1px solid color('gray-cool-10');
|
||||
}
|
||||
.table-field-index,
|
||||
.table-field-heading-first {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
background: white;
|
||||
text-align: center;
|
||||
width: units(2);
|
||||
|
||||
td {
|
||||
border-top: 0;
|
||||
// 194 is the width of the table * 1/3.5, so the overflow cuts off
|
||||
// at 3.5 columns wide.
|
||||
// 11 accounts for the padding of the table cell
|
||||
min-width: 194px - 11px;
|
||||
&:first-child {
|
||||
min-width: auto;
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 0px;
|
||||
z-index: 11;
|
||||
outline: 1px solid color('gray-cool-10');
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
background: color('gray-cool-10');
|
||||
z-index: 11;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -722,7 +722,6 @@ details form {
|
||||
.placeholder,
|
||||
.placeholder-conditional {
|
||||
background-color: #face00;
|
||||
border: 1px solid #face00;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,17 @@ def get_csv_upload(service_id, upload_id):
|
||||
return get_s3_object(*get_csv_location(service_id, upload_id))
|
||||
|
||||
|
||||
def remove_blank_lines(filedata):
|
||||
# sometimes people upload files with hundreds of blank lines at the end
|
||||
data = filedata["data"]
|
||||
cleaned_data = "\n".join(line for line in data.splitlines() if line.strip())
|
||||
filedata["data"] = cleaned_data
|
||||
return filedata
|
||||
|
||||
|
||||
def s3upload(service_id, filedata):
|
||||
|
||||
filedata = remove_blank_lines(filedata)
|
||||
upload_id = str(uuid.uuid4())
|
||||
bucket_name, file_location, access_key, secret_key, region = get_csv_location(
|
||||
service_id, upload_id
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<script nonce="{{ csp_nonce() }}">document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled');</script>
|
||||
{% block bodyStart %}
|
||||
{% block extra_javascripts_before_body %}
|
||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WX5NGWF"
|
||||
<noscript><iframe sandbox src="https://www.googletagmanager.com/ns.html?id=GTM-WX5NGWF"
|
||||
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
'class': 'file-upload-field',
|
||||
'accept': allowed_file_extensions|format_list_items('.{item}')|join(',')|e
|
||||
}) }}
|
||||
<label class="file-upload-button" for="{{ field.name }}">
|
||||
<label class="file-upload-button usa-button" for="{{ field.name }}">
|
||||
{{ button_text }}
|
||||
</label>
|
||||
{% if alternate_link and alternate_link_text %}
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
<li>More effectively deliver program outcomes</li>
|
||||
<li>Save administrative costs</li>
|
||||
<li>Implement <a class="use-link usa-link--external"
|
||||
href="https://digital.gov/resources/delivering-digital-first-public-experience/" target="_blank">21st Century
|
||||
href="https://digital.gov/resources/delivering-digital-first-public-experience/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
aria-label="21st Century IDEA (opens in a new tab)">21st Century
|
||||
IDEA</a> and other directives</li>
|
||||
</ul>
|
||||
<p>Notify.gov is an easy-to-use, web-based platform. It requires no technical expertise or system integration — users
|
||||
|
||||
@@ -22,16 +22,26 @@
|
||||
</ul>
|
||||
<p>
|
||||
Notify.gov operates under a full three-year <a class="use-link usa-link--external"
|
||||
href="https://digital.gov/resources/an-introduction-to-ato/" target="_blank">Authority-to-Operate (ATO)</a>. This
|
||||
href="https://digital.gov/resources/an-introduction-to-ato/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Authority-to-Operate (ATO) (opens in a new tab)">Authority-to-Operate (ATO)</a>. This
|
||||
federal security authorization process leverages security
|
||||
controls provided by National Institute of Standards and Technology (NIST).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Our infrastructure runs on <a class="use-link usa-link--external" href="https://www.cloud.gov/"
|
||||
target="_blank">cloud.gov</a> and utilizes several
|
||||
Our infrastructure runs on <a class="use-link usa-link--external"
|
||||
href="https://www.cloud.gov/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="cloud dot gov (opens in a new tab)">cloud.gov</a> and utilizes several
|
||||
services through Amazon Web
|
||||
Services (AWS), including <a class="use-link usa-link--external" href="https://aws.amazon.com/sns/" target="_blank">
|
||||
Services (AWS), including <a class="use-link usa-link--external"
|
||||
href="https://aws.amazon.com/sns/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
aria-label="AWS SNS (opens in a new tab)">
|
||||
AWS SNS </a> for sending SMS
|
||||
messages.
|
||||
</p>
|
||||
@@ -53,15 +63,22 @@
|
||||
</p>
|
||||
<h2>Multi-Factor Authentication</h2>
|
||||
<p>
|
||||
Notify.gov uses <a class="use-link usa-link--external" href="https://login.gov/what-is-login/"
|
||||
target="_blank">Login.gov</a> for enhanced security.
|
||||
Notify.gov uses <a class="use-link usa-link--external"
|
||||
href="https://login.gov/what-is-login/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
aria-label="Login dot gov (opens in a new tab)">Login.gov</a> for enhanced security.
|
||||
Login.gov is an extra layer of security created by the government that uses multi-factor authentication and stronger
|
||||
passwords to protect your account.
|
||||
</p>
|
||||
<p>
|
||||
To access Notify.gov, users will use a Login.gov account associated with their agency (.gov) email with one of the
|
||||
<a class="use-link usa-link--external" href="https://login.gov/help/get-started/authentication-methods/"
|
||||
target="_blank">multi-factor authentication
|
||||
<a class="use-link usa-link--external"
|
||||
href="https://login.gov/help/get-started/authentication-methods/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
aria-label="login dot gov multi-factor authentication methods (opens in a new tab)"
|
||||
>multi-factor authentication
|
||||
methods</a> offered through Login.gov.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
<p>
|
||||
Confusing or <a class="use-link usa-link--external"
|
||||
href="https://kffhealthnews.org/news/tougher-returned-mail-policies-add-to-medicaid-enrollment-drop/"
|
||||
target="_blank">unreceived notifications</a> are one of the largest barriers to people getting and keeping
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="unreceived notifications (opens in a new tab)">unreceived notifications</a> are one of the largest barriers to people getting and keeping
|
||||
benefits. The typical ways the government communicates with people often fall short. Low income households are more
|
||||
likely to experience housing instability, which means paper mail, already slow, can easily be missed.
|
||||
</p>
|
||||
@@ -24,7 +26,9 @@
|
||||
<p>
|
||||
<a class="use-link usa-link--external"
|
||||
href="https://www.pewresearch.org/internet/fact-sheet/mobile/?tabItem=64e32376-5a21-4b1d-8f8b-5f92406db984"
|
||||
target="_blank">Pew Research shows that nearly all adults in the US have a cell phone.</a> Reliance on smartphones
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Pew Research shows that nearly all adults in the US have a cell phone (opens in a new tab)">Pew Research shows that nearly all adults in the US have a cell phone.</a> Reliance on smartphones
|
||||
for online access is especially common among Americans with lower household incomes and those with lower levels of
|
||||
formal education. Of those earning less than $30,000 a year, 28% say their mobile phone is the sole method to
|
||||
digitally connect.
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
{{notification.key_name}}
|
||||
</span>
|
||||
<span class="grid-col-6 api-notifications-item__meta-time">
|
||||
<time class="timeago" datetime="{{ notification.created_at }}">
|
||||
<time datetime="{{ notification.created_at }}">
|
||||
{{ notification.created_at|format_delta }}
|
||||
</time>
|
||||
</span>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
{% from "components/radios.html" import radio_select %}
|
||||
{% from "components/table.html" import list_table, field, text_field, index_field, hidden_field_heading %}
|
||||
{% from "components/table.html" import list_table, field, text_field, index_field, index_field_heading %}
|
||||
{% from "components/file-upload.html" import file_upload %}
|
||||
{% from "components/components/back-link/macro.njk" import usaBackLink %}
|
||||
|
||||
@@ -161,18 +161,15 @@ recipients.column_headers %}
|
||||
|
||||
<h2 class="font-body-lg" id="file-preview">{{ original_file_name }}</h2>
|
||||
|
||||
<div class="fullscreen-content" data-module="fullscreen-table">
|
||||
<div class="table-wrapper spreadsheet" data-module="fullscreen-table">
|
||||
{% call(item, row_number) list_table(
|
||||
recipients.displayed_rows,
|
||||
caption=original_file_name,
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'<span class="usa-sr-only">Row in file</span><span aria-hidden="true">1</span>'|safe
|
||||
] + column_headers
|
||||
) %}
|
||||
field_headings=[''] + column_headers )%}
|
||||
{% call index_field() %}
|
||||
<span>
|
||||
{% set displayed_index = item.index + 2 %}
|
||||
{% set displayed_index = item.index + 1 %}
|
||||
{{ displayed_index }}
|
||||
</span>
|
||||
{% endcall %}
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fullscreen-content" data-module="fullscreen-table">
|
||||
<div class="table-wrapper spreadsheet" data-module="fullscreen-table">
|
||||
{% call(item, row_number) mapping_table(
|
||||
caption="Errors in " + original_file_name,
|
||||
caption_visible=False,
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<p class="hint">This person has never logged in</p>
|
||||
{% else %}
|
||||
<p>Last logged in
|
||||
<time class="timeago" datetime="{{ user.logged_in_at }}">
|
||||
<time datetime="{{ user.logged_in_at }}">
|
||||
{{ user.logged_in_at|format_delta }}
|
||||
</time>
|
||||
</p>
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
<p>
|
||||
When the <a class="use-link usa-link--external"
|
||||
href="https://www.cbpp.org/research/food-assistance/targeted-text-message-outreach-can-increase-wic-enrollment-pilots-show"
|
||||
target="_blank">Center on Budget and Policy Priorities</a> studied WIC, they found key learnings about the
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Center on Budget and Policy Priorities (opens in a new tab)">Center on Budget and Policy Priorities</a> studied WIC, they found key learnings about the
|
||||
quantity of messages delivered, how people engage with messages, and how they take action.
|
||||
</p>
|
||||
|
||||
@@ -68,8 +70,11 @@
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
The <a class="use-link usa-link--external" href="https://codeforamerica.org/resources/texting-playbook/"
|
||||
target="_blank">Code for America’s Texting
|
||||
The <a class="use-link usa-link--external"
|
||||
href="https://codeforamerica.org/resources/texting-playbook/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Code for America's Texting Playbook (opens in a new tab)">Code for America’s Texting
|
||||
Playbook</a>
|
||||
reported specific learnings around appointment reminders, completing
|
||||
document submission, and maintenance reminders.
|
||||
|
||||
@@ -142,7 +142,9 @@
|
||||
<p>
|
||||
The Department of Veterans Affairs provides a <a class="use-link usa-link--external"
|
||||
href="https://github.com/department-of-veterans-affairs/va.gov-team/blob/master/products/va-notify/notification-guide.md#vanotify-notification-guide"
|
||||
target="_blank">helpful flow-chart</a> that can help you decide if a text message is needed
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="helpful flow-chart (opens in a new tab)">helpful flow-chart</a> that can help you decide if a text message is needed
|
||||
for the communication problem you are trying to solve.
|
||||
</p>
|
||||
<h2>Use a hypothesis framework to plan your campaign</h2>
|
||||
|
||||
@@ -101,8 +101,11 @@
|
||||
<p class="sms-message-wrapper text-left">
|
||||
{{circle_number(7) }}<b>Dept. of Social Services:</b> Hi {{circle_number(8) }}<b>Julie,</b> Your Medicaid
|
||||
renewal is closing December 31, 2023. You can renew online at {{circle_number(9) }}<a
|
||||
class="use-link usa-link--external" href="https://www.application.yourstate.gov" target="_blank"
|
||||
rel="application.yourstate.gov">https://www.application.yourstate.gov</a> or {{circle_number(10) }}
|
||||
class="use-link usa-link--external"
|
||||
href="https://www.application.yourstate.gov"
|
||||
target="_blank"
|
||||
rel="application.yourstate.gov"
|
||||
aria-label="Yourstate dot gov (opens in a new tab)">https://www.application.yourstate.gov</a> or {{circle_number(10) }}
|
||||
<b>call the number on the back</b> of your Medicaid card.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,10 @@
|
||||
{% set links = [
|
||||
{
|
||||
"p_text": 'The <a class="use-link usa-link--external" href="https://www.fcc.gov/sites/default/files/tcpa-rules.pdf"
|
||||
target="_blank">Telephone Consumer
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Telephone Consumer Protection Act (TCPA) (opens in a new tab)"
|
||||
>Telephone Consumer
|
||||
Protection Act (TCPA)</a> (47 USC § 227) is the federal law that impacts how organizations are allowed to
|
||||
communicate
|
||||
in bulk with the public via telephone (including text message or SMS).'
|
||||
@@ -37,7 +40,9 @@
|
||||
{
|
||||
"p_text": 'The <a class="use-link usa-link--external"
|
||||
href="https://www.fcc.gov/document/fcc-provides-best-practices-enable-critical-health-care-coverage-calls"
|
||||
target="_blank">FCC has ruled</a> that <b>Federal</b> and <b>State</b> programs are exempt from the TCPA and can
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="FCC has ruled (opens in a new tab)">FCC has ruled</a> that <b>Federal</b> and <b>State</b> programs are exempt from the TCPA and can
|
||||
send
|
||||
text messages to the public without consent if conducting official business. Without explicit mention in the ruling,
|
||||
local governments, phone carriers, or any texting intermediaries might require it.'
|
||||
@@ -52,7 +57,10 @@
|
||||
case.
|
||||
For additional questions about the enforcement of the TCPA, you can watch <a class="use-link usa-link--external"
|
||||
href="https://www.performance.gov/cx/life-experiences/facing-a-financial-shock/outputs/fcc-training/"
|
||||
target="_blank">a recorded training on public
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="a recorded training on public
|
||||
benefits texting (opens in a new tab)">a recorded training on public
|
||||
benefits texting</a> provided by the FCC.'
|
||||
},
|
||||
] %}
|
||||
@@ -65,7 +73,9 @@
|
||||
<h4 class="usa-collection__heading">
|
||||
<a class="usa-link {% if item.is_downloadable %}usa-link--downloadable{% else %}usa-link--external{% endif %}"
|
||||
href="{% if item.is_downloadable %}{{ asset_url(item.url_link) }}{% else %}{{ item.url_link }}{% endif %}"
|
||||
target="_blank">
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="{{ item.url_text }} (opens in a new tab)">
|
||||
{{ item.url_text }}
|
||||
</a>
|
||||
</h4>
|
||||
@@ -119,6 +129,7 @@
|
||||
process
|
||||
to keep your phone number list up-to-date. The FCC offers a <a class="use-link usa-link--external"
|
||||
href="https://www.fcc.gov/reassigned-numbers-database" target="_blank"
|
||||
aria-label="Reassigned Numbers Database (opens in a new tab)"
|
||||
rel="Reassigned Numbers Database">Reassigned
|
||||
Numbers Database</a> to identify phone numbers that may have been reassigned since your agency obtained them.
|
||||
</p>
|
||||
|
||||
@@ -81,7 +81,10 @@
|
||||
</ul>
|
||||
<h2>What provoking action looks like</h2>
|
||||
<p class="padding-bottom-2"><a class="use-link usa-link--external"
|
||||
href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC10002044/" target="_blank">Evidence
|
||||
href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC10002044/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Evidence shows (opens in a new tab)">Evidence
|
||||
shows</a> that employing
|
||||
behavioral science is an effective way to increase the
|
||||
likelihood of a recipient
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</h1>
|
||||
|
||||
<p>
|
||||
<a class="usa-link" target="_blank" href="{{ url_for('main.live_services_csv') }}">Download live services csv report</a>
|
||||
<a class="usa-link" target="_blank" rel="noopener noreferrer" aria-label="Download live services CSV report (opens in a new tab)" href="{{ url_for('main.live_services_csv') }}">Download live services csv report</a>
|
||||
</p>
|
||||
<p>
|
||||
<a class="usa-link" href="{{ url_for('main.notifications_sent_by_service') }}">Monthly notification statuses for live services</a>
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
{% from "components/table.html" import list_table, text_field, index_field, index_field_heading %}
|
||||
{% from "components/components/back-link/macro.njk" import usaBackLink %}
|
||||
<script type="text/javascript" src="{{ asset_url('js/setTimezone.js') }}"></script>
|
||||
import { FullscreenTable } from './fullscreenTable.js';
|
||||
|
||||
{% block service_page_title %}
|
||||
Upload a list of {{ 999|recipient_count_label(template.template_type) }}
|
||||
Upload your bulk-sending spreadsheet
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -15,22 +16,63 @@
|
||||
{{ usaBackLink(params) }}
|
||||
{% endblock %}
|
||||
|
||||
{% set phone_numbers = [
|
||||
{
|
||||
"svg_src": "#check_circle",
|
||||
"card_heading": "Label column A (the first column) as Phone number",
|
||||
},
|
||||
{
|
||||
"svg_src": "#check_circle",
|
||||
"card_heading": "Double check it's the only column with Phone number as its label",
|
||||
},
|
||||
{
|
||||
"svg_src": "#check_circle",
|
||||
"card_heading": "Make sure no duplicate phone numbers are listed in Column A",
|
||||
}
|
||||
]
|
||||
%}
|
||||
|
||||
{% set additional_data = [
|
||||
{
|
||||
"svg_src": "#check_circle",
|
||||
"card_heading": "Match column labels one-to-one to the message template placeholders",
|
||||
},
|
||||
{
|
||||
"svg_src": "#check_circle",
|
||||
"card_heading": "Label each additional <a class=\"usa-link\" href=\"/using-notify/how-to#personalize-content\">personalized placeholder</a> separately",
|
||||
},
|
||||
{
|
||||
"svg_src": "#check_circle",
|
||||
"card_heading": "Separate each word in a column label with a space or dash, but no commas",
|
||||
},
|
||||
{
|
||||
"svg_src": "#check_circle",
|
||||
"card_heading": "Fill in each personalized placeholder with the appropriate data or information",
|
||||
},
|
||||
{
|
||||
"svg_src": "#check_circle",
|
||||
"card_heading": "Fill in each <a class=\"usa-link\" href=\"/using-notify/how-to#conditional-content\">conditional placeholder</a> column with a Yes (Y) or No (N) to “answer” whether the recipient meets its criteria",
|
||||
}
|
||||
]
|
||||
%}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
{{ page_header('Upload a list of {}'.format(999|recipient_count_label(template.template_type))) }}
|
||||
{{ page_header('Upload your bulk-sending spreadsheet')}}
|
||||
<p class="font-sans-lg text-base">Organize phone numbers and information in a single spreadsheet and upload when you have multiple messages to send. Column headers in the spreadsheet will place the data in the right spots within the template that's selected.</p>
|
||||
|
||||
<div class="page-footer bottom-gutter">
|
||||
<div class="page-footer bottom-gutter margin-top-3">
|
||||
{{file_upload(
|
||||
form.file,
|
||||
allowed_file_extensions=allowed_file_extensions,
|
||||
button_text='Choose a file',
|
||||
button_text='Choose and upload a spreadsheet',
|
||||
show_errors=False
|
||||
)}}
|
||||
</div>
|
||||
|
||||
<h2 class="font-body-lg">Your file needs to look like this example</h2>
|
||||
<h2 class="font-body-lg">A spreadsheet is available to use</h2>
|
||||
|
||||
<div class="spreadsheet" data-module="fullscreen-table">
|
||||
<div class="table-wrapper spreadsheet" data-module="fullscreen-table">
|
||||
{% call(item, row_number) list_table(
|
||||
example,
|
||||
caption="Example",
|
||||
@@ -44,14 +86,71 @@
|
||||
{% endcall %}
|
||||
</div>
|
||||
<p class="hint">
|
||||
Save your spreadsheet as a <abbr title="Comma Separated Values">CSV</abbr> file for bulk messaging. It is the most reliable when uploading your contact list. Start by downloading this example for your message template.
|
||||
Each template saved in Notify is given an example <abbr title="Comma Separated Values">CSV</abbr> formatted spreadsheet like this. It's the most reliable file format for uploading your contact list. Use this example to populate your template with the right data items. Start by downloading this example for your message template. Then save it as a <abbr title="Comma Separated Values">CSV</abbr> file for bulk messaging.
|
||||
</p>
|
||||
<p class="table-show-more-link">
|
||||
<a class="usa-link display-flex margin-top-1" href="{{ url_for('.get_example_csv', service_id=current_service.id, template_id=template.id) }}" download>Download this example (<abbr title="Comma separated values">CSV</abbr>)
|
||||
<img class="margin-left-05" src="{{ asset_url('img/material-icons/download.svg') }}" alt="" />
|
||||
</a>
|
||||
</p>
|
||||
<h2 class="font-body-lg margin-bottom-1">Your file will populate this template:<br><span class="font-body-lg">({{ template.name }})</span></h2>
|
||||
<h2 class="font-body-lg">Your bulk-sending spreadsheet checklist</h2>
|
||||
<div class="grid-container margin-top-2 padding-0">
|
||||
<div class="grid-row">
|
||||
<div class="grid-col-12">
|
||||
<h3>Phone numbers</h3>
|
||||
<ul class="usa-icon-list">
|
||||
{% for item in phone_numbers %}
|
||||
<li class="usa-icon-list__item">
|
||||
<div class="usa-icon-list__icon text-green">
|
||||
<svg aria-hidden="true" focusable="false" role="img" class="usa-icon">
|
||||
<use xlink:href="{{ asset_url('img/sprite.svg') }}{{ item.svg_src }}"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="usa-icon-list__content">
|
||||
{{item.card_heading | safe }}
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="grid-col-12">
|
||||
<h3>Additional data</h3>
|
||||
<ul class="usa-icon-list">
|
||||
{% for item in additional_data %}
|
||||
<li class="usa-icon-list__item">
|
||||
<div class="usa-icon-list__icon text-green">
|
||||
<svg aria-hidden="true" focusable="false" role="img" class="usa-icon">
|
||||
<use xlink:href="{{ asset_url('img/sprite.svg') }}{{ item.svg_src }}"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="usa-icon-list__content">
|
||||
{{item.card_heading | safe }}
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="grid-col-12">
|
||||
<h3>If an error occurs</h3>
|
||||
<ul class="usa-icon-list">
|
||||
<li class="usa-icon-list__item">
|
||||
<div class="usa-icon-list__icon text-red">
|
||||
<svg aria-hidden="true" focusable="false" role="img" class="usa-icon">
|
||||
<use xlink:href="{{ asset_url('img/sprite.svg') }}#error"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="usa-icon-list__content">
|
||||
If you're receiving any errors, double check your file format and make sure your data is pasted in with values only
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="font-body-lg margin-bottom-2">You are filling in this template</h2>
|
||||
<p>Template: {{ template.name }}</p>
|
||||
{{ template|string }}
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -47,7 +47,6 @@ const javascripts = () => {
|
||||
paths.npm + 'hogan.js/dist/hogan-3.0.2.js',
|
||||
paths.npm + 'jquery/dist/jquery.min.js',
|
||||
paths.npm + 'query-command-supported/dist/queryCommandSupported.min.js',
|
||||
paths.npm + 'timeago/jquery.timeago.js',
|
||||
paths.npm + 'textarea-caret/index.js',
|
||||
paths.npm + 'cbor-js/cbor.js',
|
||||
paths.npm + 'd3/dist/d3.min.js',
|
||||
@@ -68,13 +67,11 @@ const javascripts = () => {
|
||||
paths.src + 'javascripts/errorTracking.js',
|
||||
paths.src + 'javascripts/preventDuplicateFormSubmissions.js',
|
||||
paths.src + 'javascripts/fullscreenTable.js',
|
||||
paths.src + 'javascripts/colourPreview.js',
|
||||
paths.src + 'javascripts/templateFolderForm.js',
|
||||
paths.src + 'javascripts/collapsibleCheckboxes.js',
|
||||
paths.src + 'javascripts/radioSlider.js',
|
||||
paths.src + 'javascripts/updateStatus.js',
|
||||
paths.src + 'javascripts/errorBanner.js',
|
||||
paths.src + 'javascripts/homepage.js',
|
||||
paths.src + 'javascripts/timeoutPopup.js',
|
||||
paths.src + 'javascripts/date.js',
|
||||
paths.src + 'javascripts/loginAlert.js',
|
||||
|
||||
@@ -608,8 +608,6 @@ def validate_phone_number(number, international=False):
|
||||
|
||||
try:
|
||||
parsed = phonenumbers.parse(number, None)
|
||||
if parsed.country_code != 1:
|
||||
raise InvalidPhoneError("Invalid country code")
|
||||
number = f"{parsed.country_code}{parsed.national_number}"
|
||||
if len(number) < 8:
|
||||
raise InvalidPhoneError("Not enough digits")
|
||||
|
||||
780
package-lock.json
generated
780
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -40,16 +40,15 @@
|
||||
"playwright": "^1.51.1",
|
||||
"python": "^0.0.4",
|
||||
"query-command-supported": "1.0.0",
|
||||
"sass-embedded": "^1.86.1",
|
||||
"sass-embedded": "^1.86.3",
|
||||
"textarea-caret": "3.1.0",
|
||||
"timeago": "1.6.7",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.26.10",
|
||||
"@babel/preset-env": "^7.26.9",
|
||||
"@uswds/compile": "^1.2.1",
|
||||
"@uswds/compile": "^1.2.2",
|
||||
"backstopjs": "^6.3.25",
|
||||
"better-npm-audit": "^3.11.0",
|
||||
"gulp": "^5.0.0",
|
||||
@@ -66,7 +65,7 @@
|
||||
"jest-environment-jsdom": "^29.2.2",
|
||||
"jshint": "2.13.6",
|
||||
"jshint-stylish": "2.2.1",
|
||||
"rollup": "^4.38.0",
|
||||
"rollup": "^4.40.0",
|
||||
"rollup-plugin-commonjs": "10.1.0",
|
||||
"rollup-plugin-node-resolve": "5.2.0"
|
||||
}
|
||||
|
||||
228
poetry.lock
generated
228
poetry.lock
generated
@@ -2,25 +2,25 @@
|
||||
|
||||
[[package]]
|
||||
name = "ago"
|
||||
version = "0.0.95"
|
||||
version = "0.1.0"
|
||||
description = "ago: Human readable timedeltas"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "ago-0.0.95.tar.gz", hash = "sha256:d2010f5eac3df544ec48ec116102e068591a345b1a580f32973db8a505fca744"},
|
||||
{file = "ago-0.1.0-py3-none-any.whl", hash = "sha256:c0c60d106fe333ac06e2ff2dd7fb5de2c4ec3183ee39708a35b6144ccb86dece"},
|
||||
{file = "ago-0.1.0.tar.gz", hash = "sha256:15604159711c47e08f21251f28bfb4383942087535660dbe640b2f6193f92be4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-timeout"
|
||||
version = "4.0.3"
|
||||
version = "5.0.1"
|
||||
description = "Timeout context manager for asyncio programs"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
|
||||
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
|
||||
{file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"},
|
||||
{file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1546,72 +1546,72 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "2.1.5"
|
||||
version = "3.0.2"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main", "dev"]
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
|
||||
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"},
|
||||
{file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1652,14 +1652,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "moto"
|
||||
version = "5.1.2"
|
||||
version = "5.1.3"
|
||||
description = "A library that allows you to easily mock out tests based on AWS infrastructure"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "moto-5.1.2-py3-none-any.whl", hash = "sha256:3789084bb20052b6eb846fe6f4831ce6dfe8a3b197c8f63789b40281b5e1731d"},
|
||||
{file = "moto-5.1.2.tar.gz", hash = "sha256:0e4c650d31eacfbe726c37e956efa04d36948e23f7d3228a7c3746aa839e66c2"},
|
||||
{file = "moto-5.1.3-py3-none-any.whl", hash = "sha256:6355b4c7208bd8d884354127824989034f1979da7b96d6e9789a0f934c0f7d6c"},
|
||||
{file = "moto-5.1.3.tar.gz", hash = "sha256:078e73f6fe27a76283f82c6c5507b9c32c0d5cfe32ad4a3d1434f62798da6166"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1784,41 +1784,41 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "newrelic"
|
||||
version = "10.8.1"
|
||||
version = "10.9.0"
|
||||
description = "New Relic Python Agent"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "newrelic-10.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c1c8438138ca530ecbb4a8d52805520b6815e65e93856739494aa657e1b464a"},
|
||||
{file = "newrelic-10.8.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:824d36a9be37c43f7d66bed46689c1c2168eba60c591299e932684e03eb10e42"},
|
||||
{file = "newrelic-10.8.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e2d02e6edd5a9c0e2229e6ca1fa114d9ee95e7c137d3eaa9de3ce8b1ce73b62b"},
|
||||
{file = "newrelic-10.8.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0c5f96c2ec732258980ea1c2ae8e66ec834ff4c40a815ffdacf3154f1e53d3d5"},
|
||||
{file = "newrelic-10.8.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03552deef7ee3d9d40b579beca702ac65105d7b5ebd603da0520961b96acadfe"},
|
||||
{file = "newrelic-10.8.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:600aa9b4dafcc4e0d16cc9c0bd40e2fe07a5fa3aa03f4293ba69045738885473"},
|
||||
{file = "newrelic-10.8.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:75dbdceb1601368becaf0e37af98eadcd5c41d0fb362b906d11eb6697528b52b"},
|
||||
{file = "newrelic-10.8.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a928b17f4c4d729f0522b3029da5ece1d32d2c5f0a89d500a037619bc53a0b06"},
|
||||
{file = "newrelic-10.8.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:055a8316e3eb1ddb230495928c5d4ad71ad18d11a295092123a1de9f01efa385"},
|
||||
{file = "newrelic-10.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a25ce0228eb104274b71eadf110971c51c90f80c7c6f31f62b3fda4d732eb0a3"},
|
||||
{file = "newrelic-10.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c025d52c45b17abaaeb1907e18104567e65c0d36f855a8fe9834cf105e0ca46"},
|
||||
{file = "newrelic-10.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:28c26c54b6be6a4827ce0cbd056bbb12c5aec148a7de0b664438cc35bb93afdb"},
|
||||
{file = "newrelic-10.8.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1688c9dcfb101b30acb4baf2e2f45159c3d298cdd585204eedf4642e2850fe3"},
|
||||
{file = "newrelic-10.8.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b68067a89eca771e766370fdf3a7f4dd5e02372957051e54fad23096b93e1819"},
|
||||
{file = "newrelic-10.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59055c2265183f7ec9fff9c657451accba6f84eb0ceb5dca9a6dcaa012f2df6b"},
|
||||
{file = "newrelic-10.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b3c70ef477e91e9c4419905940769c8223a9d21bf388473687aa500998f23543"},
|
||||
{file = "newrelic-10.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1ce54252d6e3c0b02887b284b58011c9b5a0d82deafc2dc9313be9cb757a720"},
|
||||
{file = "newrelic-10.8.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9a053f1ba62b89c2a1543a8a55e36bc1cd3c77a39a98744927ba298ce4e3af3"},
|
||||
{file = "newrelic-10.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5444474ba9aa6a08e0235b931c4506c9d78911d9bdbb3a68132107186bd53b35"},
|
||||
{file = "newrelic-10.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89d75646853ce429ba7fe6d7a0d50100107040525094d8f14b965660a6ea41f7"},
|
||||
{file = "newrelic-10.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9a3410ea09e70af92181db485b7772ae7c9243d4605dba20e9dd6cea08e0a1"},
|
||||
{file = "newrelic-10.8.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7396a8e0b52ce7cb8a6f5cbef5196da7356b3197421617648b9c6d861aa9fe3"},
|
||||
{file = "newrelic-10.8.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:457c74a3fbc4484111f0e5473dc8186da5cd371ced820fb28107fed9790ddc55"},
|
||||
{file = "newrelic-10.8.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:935fdb99883636da4f8aca3d44646c0262c74e1322342e86f0ab0016ecd4cb37"},
|
||||
{file = "newrelic-10.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b823bd4ef787e47f4a3c39a03ba9634dfadee609bb11d050634316f376a9368b"},
|
||||
{file = "newrelic-10.8.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17a696256c76af9f186af93aa4ef97e9b3e2bd832bec263f5e0b69f47e7ad2f6"},
|
||||
{file = "newrelic-10.8.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c63924d7748555af011de1d04c0f51069d78fb45112ded407cec39d171763cd3"},
|
||||
{file = "newrelic-10.8.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d0b01b8a7ff3596825d99ef88b5c04ad6a312d0c3969e97651fda3a35b6212c2"},
|
||||
{file = "newrelic-10.8.1.tar.gz", hash = "sha256:4eac63a95a4565198c36a4ffacb3798bd53affb6fb188531e201f8eb239ee86b"},
|
||||
{file = "newrelic-10.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c35e1405a0e0bbe2f6a2d48c54155a5d826f9d06822e6aed76e1bd4e3dd19a22"},
|
||||
{file = "newrelic-10.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:113db07e79bd0583420793a7db32639038ad04b20b0c73f9d06888a29ece2a6d"},
|
||||
{file = "newrelic-10.9.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:28775472638d837e1aef0dc462526e4d75e4a187317fccffc215ecc6066c8a6d"},
|
||||
{file = "newrelic-10.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f955f997a6581d604115123f4a35a6cbfab9a0e0540f9341c61998d8a6302900"},
|
||||
{file = "newrelic-10.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeffadc14bd0c2985d2f1872039969a4ddad217e772b881b2d2938f65da894d9"},
|
||||
{file = "newrelic-10.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a841884b1f51b13d95ee119fee69fb7b6191abbb3ecfeab5f3d5cb51b633e735"},
|
||||
{file = "newrelic-10.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b72834b5634116c258caf1ab0dd0d7157648596ed2c98a1128849a4689e92d0"},
|
||||
{file = "newrelic-10.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:95263e182be8ccef73e847136c0fa8d70d728c9dab2be3402f6e7e174ff14fa6"},
|
||||
{file = "newrelic-10.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e5e4894a6fdb7d91ad411d201bdf4b44d9e1b8652c5174bb992086ed2e5d099"},
|
||||
{file = "newrelic-10.9.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7039dcaced71a59ba5cb5007f303ac02dd0e5ae6eabbe1d836987d563a55d4b"},
|
||||
{file = "newrelic-10.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9a2b8b2e76d7db53f4152843cc13ae2db6bc7689f496e5db5643e68a4820332d"},
|
||||
{file = "newrelic-10.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25e500c001d2ab29cffb012a60a2e2638343f199931706ffc302208a92b20b24"},
|
||||
{file = "newrelic-10.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7906b7631e58c8ad71e1eb4f2538f92a02b15678687045c92cfaf89ed88ff608"},
|
||||
{file = "newrelic-10.9.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19a6ff878e1b611cf3b0c407c91702863a5233b38ebb16be5d8767df54a355b9"},
|
||||
{file = "newrelic-10.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:992a67711e9946ed48856450d68d5764c809a0d8970bd4eb84ef03ab4faf4e76"},
|
||||
{file = "newrelic-10.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:32feb34886284c73d16a6b44a349af8b2a3d01e6ae63f0579d82226b5f9dc0da"},
|
||||
{file = "newrelic-10.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6de880814d2eaa9aa68a046666dfec90880c1af8b949263471c9e40a9476a00e"},
|
||||
{file = "newrelic-10.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01b06ef9e3725e92d92978fe3526188864ae912569d63728fd0c7cb76914fff9"},
|
||||
{file = "newrelic-10.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0ff3dd40fbdbc93886be217d4430a3d6352d7456e92cfd59f463865d3f76d704"},
|
||||
{file = "newrelic-10.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4be2e2bc1cdb129c130a31378c1b031b544e332d7f085adcdb1f50911d88b782"},
|
||||
{file = "newrelic-10.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6e9049bc6f72a90911c3ea79a5559b41c83e136b8eea07db622405450fa09fe"},
|
||||
{file = "newrelic-10.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:227ef495e64544d8ed87d4829bb64246ebcba5c192eb58b5e8b009bc761fe06a"},
|
||||
{file = "newrelic-10.9.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4901baf6f3235945f4238465b5f9e07c52f7eb81d283d23252349265e21ab6f8"},
|
||||
{file = "newrelic-10.9.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c7e8764d126b8f905a889ce84792ec220a991ed639068a4278ff939f8147c218"},
|
||||
{file = "newrelic-10.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fbdc89951bd13f134984e475fd47ee9a03e1f8d5ee5992c0988654cc0675252"},
|
||||
{file = "newrelic-10.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb0529857393ae54c8f5c36c4360d8a5bc30c326c37e240af3e39823f4c974f7"},
|
||||
{file = "newrelic-10.9.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a95d6dd7f02d1f00df030e222115ece8f1fe3153d7670e3f619b16f0923ccbc1"},
|
||||
{file = "newrelic-10.9.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ddf43e2fbad8500df10a59d4638902c8b1781dbecd28ad97a3df5bf923eee0a9"},
|
||||
{file = "newrelic-10.9.0.tar.gz", hash = "sha256:0741de2138b41a1ae1cfad397878774de4131196d66f1443a23b055d9f47e706"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -2006,14 +2006,14 @@ setuptools = "*"
|
||||
|
||||
[[package]]
|
||||
name = "phonenumbers"
|
||||
version = "8.13.55"
|
||||
version = "9.0.3"
|
||||
description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "phonenumbers-8.13.55-py2.py3-none-any.whl", hash = "sha256:25feaf46135f0fb1e61b69513dc97c477285ba98a69204bf5a8cf241a844a718"},
|
||||
{file = "phonenumbers-8.13.55.tar.gz", hash = "sha256:57c989dda3eabab1b5a9e3d24438a39ebd032fa0172bf68bfd90ab70b3d5e08b"},
|
||||
{file = "phonenumbers-9.0.3-py2.py3-none-any.whl", hash = "sha256:0509d9b70ef66c04a1538f0b31c3e0c0b023127dad0eb1199adef0bc1841ff34"},
|
||||
{file = "phonenumbers-9.0.3.tar.gz", hash = "sha256:c5084e6c6a77e0e8d725086c7f69002ef8e39529155b8cd31cbf8c987f344a33"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2045,19 +2045,19 @@ pip = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pip-audit"
|
||||
version = "2.8.0"
|
||||
version = "2.9.0"
|
||||
description = "A tool for scanning Python environments for known vulnerabilities"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pip_audit-2.8.0-py3-none-any.whl", hash = "sha256:200f50d56cb6fba3a9189c20d53250354f72f161d63b6ef77ae5de2b53044566"},
|
||||
{file = "pip_audit-2.8.0.tar.gz", hash = "sha256:9816cbd94de6f618a8965c117433006b3d565a708dc05d5a7be47ab65b66fa05"},
|
||||
{file = "pip_audit-2.9.0-py3-none-any.whl", hash = "sha256:348b16e60895749a0839875d7cc27ebd692e1584ebe5d5cb145941c8e25a80bd"},
|
||||
{file = "pip_audit-2.9.0.tar.gz", hash = "sha256:0b998410b58339d7a231e5aa004326a294e4c7c6295289cdc9d5e1ef07b1f44d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
CacheControl = {version = ">=0.13.0", extras = ["filecache"]}
|
||||
cyclonedx-python-lib = ">=5,<9"
|
||||
cyclonedx-python-lib = ">=5,<10"
|
||||
packaging = ">=23.0.0"
|
||||
pip-api = ">=0.0.28"
|
||||
pip-requirements-parser = ">=32.0.0"
|
||||
@@ -2328,14 +2328,14 @@ xlwt = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pyexcel-xlsx"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
description = "A wrapper library to read, manipulate and write data in xlsx and xlsmformat"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pyexcel-xlsx-0.6.0.tar.gz", hash = "sha256:55754f764252461aca6871db203f4bd1370ec877828e305e6be1de5f9aa6a79d"},
|
||||
{file = "pyexcel_xlsx-0.6.0-py2.py3-none-any.whl", hash = "sha256:16530f96a77c97ebcba7941517d2756ac52d3ce2903d81eecd7f300778d5242a"},
|
||||
{file = "pyexcel_xlsx-0.6.1-py2.py3-none-any.whl", hash = "sha256:c17d8c90ae6c2ae5672e469e6afe4336597e4ff479e5a93ba846ae46b7b80769"},
|
||||
{file = "pyexcel_xlsx-0.6.1.tar.gz", hash = "sha256:80b7985d394b73abf5c52212027bbbc0c7f6090192bcbc76403f8672d74c7960"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -3300,4 +3300,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = "^3.12.2"
|
||||
content-hash = "fc363e188984011a288603a65ba33b2f8c7658c3e0ade8e87ba986bd7ff65620"
|
||||
content-hash = "418596e226a87272f31ee6db71b26e45754dad12d29761c9cff83cd86363c0a5"
|
||||
|
||||
@@ -9,7 +9,7 @@ package-mode = false
|
||||
[tool.poetry.dependencies]
|
||||
axe-core-python = "^0.1.0"
|
||||
python = "^3.12.2"
|
||||
ago = "~=0.0.95"
|
||||
ago = "~=0.1.0"
|
||||
beautifulsoup4 = "^4.13.3"
|
||||
blinker = "~=1.8"
|
||||
exceptiongroup = "==1.2.2"
|
||||
@@ -28,7 +28,7 @@ pyexcel = "==0.7.2"
|
||||
pyexcel-io = "==0.6.7"
|
||||
pyexcel-ods3 = "==0.6.1"
|
||||
pyexcel-xls = "==0.7.1"
|
||||
pyexcel-xlsx = "==0.6.0"
|
||||
pyexcel-xlsx = "==0.6.1"
|
||||
openpyxl = "==3.0.10"
|
||||
pyproj = "==3.7.1"
|
||||
python-dotenv = "==1.1.0"
|
||||
@@ -38,7 +38,7 @@ werkzeug = "^3.1.3"
|
||||
wtforms = "~=3.1"
|
||||
markdown = "^3.7"
|
||||
mistune = "^3.1.3"
|
||||
async-timeout = "^4.0.3"
|
||||
async-timeout = "^5.0.1"
|
||||
bleach = "^6.1.0"
|
||||
boto3 = "^1.35.99"
|
||||
botocore = "^1.35.99"
|
||||
@@ -50,7 +50,7 @@ geojson = "^3.1.0"
|
||||
jmespath = "^1.0.1"
|
||||
numpy = "^2.2.4"
|
||||
ordered-set = "^4.1.0"
|
||||
phonenumbers = "^8.13.52"
|
||||
phonenumbers = "^9.0.3"
|
||||
pycparser = "^2.22"
|
||||
python-json-logger = "^3.3.0"
|
||||
redis = "^5.2.1"
|
||||
@@ -62,7 +62,7 @@ certifi = "^2025.1.31"
|
||||
charset-normalizer = "^3.4.1"
|
||||
click = "^8.1.8"
|
||||
idna = "^3.7"
|
||||
markupsafe = "^2.1.5"
|
||||
markupsafe = "^3.0.2"
|
||||
python-dateutil = "^2.9.0.post0"
|
||||
pyyaml = "^6.0.1"
|
||||
requests = "^2.32.3"
|
||||
|
||||
@@ -14,7 +14,7 @@ def test_owasp_useful_headers_set(
|
||||
assert response.headers["X-Frame-Options"] == "deny"
|
||||
assert response.headers["X-Content-Type-Options"] == "nosniff"
|
||||
csp = response.headers["Content-Security-Policy"]
|
||||
assert search(r"default-src 'self' static\.example\.com;", csp)
|
||||
assert search(r"frame-src.*https://www\.googletagmanager\.com", csp)
|
||||
assert search(r"frame-ancestors 'none';", csp)
|
||||
assert search(r"form-action 'self';", csp)
|
||||
assert search(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
from app.s3_client.s3_csv_client import set_metadata_on_csv_upload
|
||||
from app.s3_client.s3_csv_client import remove_blank_lines, set_metadata_on_csv_upload
|
||||
|
||||
|
||||
def test_sets_metadata(client_request, mocker):
|
||||
@@ -21,3 +21,11 @@ def test_sets_metadata(client_request, mocker):
|
||||
MetadataDirective="REPLACE",
|
||||
ServerSideEncryption="AES256",
|
||||
)
|
||||
|
||||
|
||||
def test_removes_blank_lines():
|
||||
filedata = {
|
||||
"data": "phone number\r\n15555555555\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
|
||||
}
|
||||
file_data = remove_blank_lines(filedata)
|
||||
assert file_data == {"data": "phone number\n15555555555"}
|
||||
|
||||
@@ -18,6 +18,12 @@ Object.defineProperty(HTMLElement.prototype, 'clientWidth', {
|
||||
});
|
||||
|
||||
// beforeAll hook to set up the DOM and load D3.js script
|
||||
beforeAll(() => {
|
||||
jest.spyOn(Intl, 'DateTimeFormat').mockImplementation(() => ({
|
||||
resolvedOptions: () => ({ timeZone: 'UTC' })
|
||||
}));
|
||||
});
|
||||
|
||||
beforeAll(done => {
|
||||
// Set up the DOM with the D3 script included
|
||||
document.body.innerHTML = `
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
const helpers = require('./support/helpers.js');
|
||||
|
||||
beforeAll(() => {
|
||||
require('../../app/assets/javascripts/colourPreview.js');
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
require('./support/teardown.js');
|
||||
});
|
||||
|
||||
describe('Colour preview', () => {
|
||||
|
||||
let field;
|
||||
let textbox;
|
||||
let swatchEl;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
// set up DOM
|
||||
document.body.innerHTML = `
|
||||
<div class="usa-form-group">
|
||||
<label class="govuk-form-label" for="colour">
|
||||
Colour
|
||||
</label>
|
||||
<input class="govuk-input govuk-input--width-6" id="colour" name="colour" rows="8" type="text" value="" data-module="colour-preview">
|
||||
</div>`;
|
||||
|
||||
field = document.querySelector('.usa-form-group');
|
||||
textbox = document.querySelector('input[type=text]');
|
||||
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
document.body.innerHTML = '';
|
||||
|
||||
});
|
||||
|
||||
describe("When the page loads", () => {
|
||||
|
||||
test("It should add a swatch element for the preview", () => {
|
||||
|
||||
// start the module
|
||||
window.GOVUK.modules.start();
|
||||
|
||||
swatchEl = document.querySelector('.textbox-colour-preview');
|
||||
|
||||
expect(swatchEl).not.toBeNull();
|
||||
|
||||
});
|
||||
|
||||
test("If the textbox is empty it should make the swatch white", () => {
|
||||
|
||||
// start the module
|
||||
window.GOVUK.modules.start();
|
||||
|
||||
swatchEl = document.querySelector('.textbox-colour-preview');
|
||||
|
||||
// textbox defaults to empty
|
||||
// colours are output in RGB
|
||||
expect(swatchEl.style.background).toEqual('rgb(255, 255, 255)');
|
||||
|
||||
});
|
||||
|
||||
test("If the textbox has a value which is a hex code it should add that colour to the swatch", () => {
|
||||
|
||||
textbox.setAttribute('value', '#00FF00');
|
||||
|
||||
// start the module
|
||||
window.GOVUK.modules.start();
|
||||
|
||||
swatchEl = document.querySelector('.textbox-colour-preview');
|
||||
|
||||
// colours are output in RGB
|
||||
expect(swatchEl.style.background).toEqual('rgb(0, 255, 0)');
|
||||
|
||||
});
|
||||
|
||||
test("If the textbox has a value which isn't a hex code it should make the swatch white", () => {
|
||||
|
||||
textbox.setAttribute('value', 'green');
|
||||
|
||||
// start the module
|
||||
window.GOVUK.modules.start();
|
||||
|
||||
swatchEl = document.querySelector('.textbox-colour-preview');
|
||||
|
||||
// colours are output in RGB
|
||||
expect(swatchEl.style.background).toEqual('rgb(255, 255, 255)');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("When input is added to the textbox", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
// start the module
|
||||
window.GOVUK.modules.start();
|
||||
|
||||
swatchEl = document.querySelector('.textbox-colour-preview');
|
||||
|
||||
});
|
||||
|
||||
test("If the textbox is empty it should make the swatch white", () => {
|
||||
|
||||
helpers.triggerEvent(document.querySelector('input[type=text]'), 'input');
|
||||
|
||||
// textbox defaults to empty
|
||||
expect(swatchEl.style.background).toEqual('rgb(255, 255, 255)');
|
||||
|
||||
});
|
||||
|
||||
test("If the textbox has a value which is a hex code it should add that colour to the swatch", () => {
|
||||
|
||||
textbox.setAttribute('value', '#00FF00');
|
||||
|
||||
helpers.triggerEvent(document.querySelector('input[type=text]'), 'input');
|
||||
|
||||
// textbox defaults to empty
|
||||
expect(swatchEl.style.background).toEqual('rgb(0, 255, 0)');
|
||||
|
||||
});
|
||||
|
||||
test("If the textbox has a value which isn't a hex code it should make the swatch white", () => {
|
||||
|
||||
textbox.setAttribute('value', 'green');
|
||||
|
||||
helpers.triggerEvent(document.querySelector('input[type=text]'), 'input');
|
||||
|
||||
// textbox defaults to empty
|
||||
expect(swatchEl.style.background).toEqual('rgb(255, 255, 255)');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,370 +1,88 @@
|
||||
const helpers = require('./support/helpers');
|
||||
require('../../app/assets/javascripts/fullscreenTable.js');
|
||||
|
||||
beforeAll(() => {
|
||||
// TODO: remove this when tests for sticky JS are written
|
||||
require('../../app/assets/javascripts/stick-to-window-when-scrolling.js');
|
||||
describe('fullscreenTable', () => {
|
||||
let wrapper, rightShadow;
|
||||
|
||||
require('../../app/assets/javascripts/fullscreenTable.js');
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
require('./support/teardown.js');
|
||||
});
|
||||
|
||||
describe('FullscreenTable', () => {
|
||||
let screenMock;
|
||||
let container;
|
||||
let tableFrame;
|
||||
let table;
|
||||
let numberColumnFrame;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
const tableHeadings = () => {
|
||||
let result = '';
|
||||
const headings = ['1', 'name', 'email address', 'age', 'building number', 'address line 1', 'address line 2', 'postcode'];
|
||||
|
||||
headings.forEach((heading, idx) => {
|
||||
if (idx === 0) {
|
||||
result += `<th scope="col" class="table-field-heading-first">
|
||||
<span class="visually-hidden">Row in file</span><span aria-hidden="true" class="table-field-invisible-error">${heading}</span>
|
||||
</th>`;
|
||||
} else {
|
||||
result += `<th scope="col" class="table-field-heading">
|
||||
${heading}
|
||||
</th>`;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const rowCells = (cells) => {
|
||||
let result = '';
|
||||
|
||||
Object.keys(cells).forEach((key, idx) => {
|
||||
if (idx === 0) {
|
||||
result += `<td class="table-field-index">
|
||||
<span class="table-field-error">
|
||||
${key}
|
||||
</span>
|
||||
</td>`;
|
||||
} else {
|
||||
result += `<td class="table-field-left-aligned ">
|
||||
<div class="table-field-status-default">
|
||||
${key}
|
||||
</div>
|
||||
</td>`;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const tableRows = () => {
|
||||
let result = '';
|
||||
|
||||
const rows = [
|
||||
['John Logie Baird', 'johnlbaird@gmail.com', '37', '22', 'Frith Street', 'Soho, London', 'W1D 4RF'],
|
||||
['Guglielmo Marconi', 'gmarconi@hotmail.com', '21', 'Pontecchio Marconi', 'Via Celestini 1', 'Bologna', ''],
|
||||
['Louis Braille', 'louisbraille@yahoo.co.uk', '', '56', 'Boulevard des Invalides', 'Paris', '75007'],
|
||||
['Ray Tomlinson', 'hedy.lamarr@msn.com', '25', '870', '870 Winter Street', 'Waltham', 'MA 02451']
|
||||
];
|
||||
|
||||
rows.forEach(row => {
|
||||
result += `<tr class="table-row">${rowCells(row)}</tr>`;
|
||||
});
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
screenMock = new helpers.ScreenMock(jest);
|
||||
screenMock.setWindow({
|
||||
width: 1990,
|
||||
height: 940,
|
||||
scrollTop: 0
|
||||
});
|
||||
|
||||
// set up DOM
|
||||
document.body.innerHTML =
|
||||
`<main>
|
||||
<div class="fullscreen-content" data-module="fullscreen-table">
|
||||
<table class="table table-font-xsmall">
|
||||
<caption class="heading-medium table-heading visuallyhidden">
|
||||
people.csv
|
||||
</caption>
|
||||
<thead class="table-field-headings-visible">
|
||||
<tr>
|
||||
${tableHeadings()}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${tableRows()}
|
||||
</tbody>
|
||||
const setupDOM = (scrollLeft = 0, scrollWidth = 1000, clientWidth = 500) => {
|
||||
document.body.innerHTML = `
|
||||
<div class="fullscreen-table">
|
||||
<div class="table-wrapper" style="overflow-x: scroll;">
|
||||
<table>
|
||||
<tr><td>Test</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</main>`;
|
||||
<div class="fullscreen-right-shadow"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
container = document.querySelector('.fullscreen-content');
|
||||
wrapper = document.querySelector('.table-wrapper');
|
||||
rightShadow = document.querySelector('.fullscreen-right-shadow');
|
||||
|
||||
});
|
||||
// Mock scroll values
|
||||
Object.defineProperty(wrapper, 'scrollLeft', {
|
||||
get: () => scrollLeft,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(wrapper, 'scrollWidth', {
|
||||
get: () => scrollWidth,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(wrapper, 'clientWidth', {
|
||||
get: () => clientWidth,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
// 👇 Now that DOM is ready, initialize the logic
|
||||
window.initFullscreenTables();
|
||||
|
||||
// Force trigger scroll event if needed
|
||||
wrapper.dispatchEvent(new Event('scroll'));
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
document.body.innerHTML = '';
|
||||
|
||||
});
|
||||
|
||||
describe("when it loads", () => {
|
||||
|
||||
test("it fixes the number column for each row without changing the semantics", () => {
|
||||
|
||||
// start module
|
||||
window.GOVUK.modules.start();
|
||||
|
||||
tableFrame = document.querySelector('.fullscreen-scrollable-table');
|
||||
numberColumnFrame = document.querySelector('.fullscreen-fixed-table');
|
||||
|
||||
expect(tableFrame).not.toBeNull();
|
||||
expect(numberColumnFrame).not.toBeNull();
|
||||
expect(numberColumnFrame.getAttribute('aria-hidden')).toEqual('true');
|
||||
|
||||
});
|
||||
|
||||
test("it calls the sticky JS to update any cached dimensions", () => {
|
||||
|
||||
const stickyJSSpy = jest.spyOn(window.GOVUK.stickAtBottomWhenScrolling, 'recalculate');
|
||||
|
||||
// start module
|
||||
window.GOVUK.modules.start();
|
||||
|
||||
expect(stickyJSSpy.mock.calls.length).toBe(1);
|
||||
|
||||
stickyJSSpy.mockClear();
|
||||
|
||||
});
|
||||
|
||||
test("the scrolling section is focusable and has an accessible name matching the table caption", () => {
|
||||
|
||||
// start module
|
||||
window.GOVUK.modules.start();
|
||||
|
||||
tableFrame = document.querySelector('.fullscreen-scrollable-table');
|
||||
caption = tableFrame.querySelector('caption');
|
||||
|
||||
expect(tableFrame.hasAttribute('role')).toBe(true);
|
||||
expect(tableFrame.getAttribute('role')).toEqual('region');
|
||||
expect(tableFrame.hasAttribute('aria-labelledby')).toBe(true);
|
||||
expect(tableFrame.getAttribute('aria-labelledby')).toEqual(caption.getAttribute('id'));
|
||||
|
||||
});
|
||||
|
||||
test("the section providing the fixed row headers is not focusable and is hidden from assistive tech'", () => {
|
||||
|
||||
// start module
|
||||
window.GOVUK.modules.start();
|
||||
|
||||
fixedRowHeaders = document.querySelector('.fullscreen-fixed-table');
|
||||
|
||||
expect(fixedRowHeaders.hasAttribute('role')).toBe(false);
|
||||
expect(fixedRowHeaders.hasAttribute('aria-labelledby')).toBe(false);
|
||||
expect(fixedRowHeaders.hasAttribute('tabindex')).toBe(false);
|
||||
expect(fixedRowHeaders.hasAttribute('aria-hidden')).toBe(true);
|
||||
expect(fixedRowHeaders.getAttribute('aria-hidden')).toEqual('true');
|
||||
|
||||
});
|
||||
|
||||
test('adds right shadow on initial load if scrollable', () => {
|
||||
setupDOM(0, 1000, 500);
|
||||
expect(wrapper.classList.contains('scrolled')).toBe(false);
|
||||
expect(rightShadow.classList.contains('visible')).toBe(true);
|
||||
});
|
||||
|
||||
describe("the height of the table should fit the vertical space available to it", () => {
|
||||
|
||||
let containerBoundingClientRectSpy;
|
||||
let containerClientRectsSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
// set the height and offset of the window and table container from the top of the document
|
||||
// so just the top 268px of it appears on-screen
|
||||
screenMock.window.setHeightTo(768);
|
||||
screenMock.mockPositionAndDimension('container', container, {
|
||||
'offsetHeight': 1000,
|
||||
'offsetWidth': 641,
|
||||
'offsetTop': 500
|
||||
});
|
||||
|
||||
// start module
|
||||
window.GOVUK.modules.start();
|
||||
|
||||
tableFrame = document.querySelector('.fullscreen-scrollable-table');
|
||||
numberColumnFrame = document.querySelector('.fullscreen-fixed-table');
|
||||
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
screenMock.reset();
|
||||
|
||||
});
|
||||
|
||||
test("when the page has loaded", () => {
|
||||
|
||||
// the frames should crop to the top 268px of the table that is visible
|
||||
expect(window.getComputedStyle(tableFrame)['height']).toEqual('268px');
|
||||
expect(window.getComputedStyle(numberColumnFrame)['height']).toEqual('268px');
|
||||
|
||||
});
|
||||
|
||||
test("when the page has scrolled", () => {
|
||||
|
||||
// scroll the window so the table fills the height of the window (768px)
|
||||
screenMock.window.scrollTo(500);
|
||||
|
||||
// the frames should crop to the window height
|
||||
expect(window.getComputedStyle(tableFrame)['height']).toEqual('768px');
|
||||
expect(window.getComputedStyle(numberColumnFrame)['height']).toEqual('768px');
|
||||
|
||||
});
|
||||
|
||||
test("when the page has resized", () => {
|
||||
|
||||
// resize the window by 232px (from 768px to 1000px)
|
||||
screenMock.window.resizeTo({ height: 1000, width: 1024 });
|
||||
|
||||
// the frames should crop to the top 500px of the table now visible
|
||||
expect(window.getComputedStyle(tableFrame)['height']).toEqual('500px');
|
||||
expect(window.getComputedStyle(numberColumnFrame)['height']).toEqual('500px');
|
||||
|
||||
});
|
||||
|
||||
test('hides right shadow when scrolled to max', () => {
|
||||
setupDOM(500, 1000, 500);
|
||||
expect(wrapper.classList.contains('scrolled')).toBe(true);
|
||||
expect(rightShadow.classList.contains('visible')).toBe(false);
|
||||
});
|
||||
|
||||
describe("the width of the table should fit the horizontal space available to it", () => {
|
||||
let rowNumberColumnCell;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
rowNumberColumnCell = container.querySelector('.table-field-index');
|
||||
|
||||
// set main content column width (used as module as gauge for table width)
|
||||
screenMock.window.setWidthTo(1024);
|
||||
document.querySelector('main').setAttribute('style', 'width: 742px');
|
||||
|
||||
// set total width of column for row numbers in table to 40px
|
||||
rowNumberColumnCell.setAttribute('style', 'width: 40px');
|
||||
|
||||
// start module
|
||||
window.GOVUK.modules.start();
|
||||
|
||||
tableFrame = document.querySelector('.fullscreen-scrollable-table');
|
||||
numberColumnFrame = document.querySelector('.fullscreen-fixed-table');
|
||||
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
screenMock.reset();
|
||||
|
||||
});
|
||||
|
||||
test("when the page has loaded", () => {
|
||||
|
||||
// table should set its width to be that of `<main>`, minus margin-left for the row numbers column
|
||||
expect(window.getComputedStyle(tableFrame)['width']).toEqual('702px'); // width of content column - numbers column
|
||||
expect(window.getComputedStyle(tableFrame)['margin-left']).toEqual('40px'); // width of numbers column
|
||||
|
||||
// table for number column has 4px extra to allow space for drop shadow
|
||||
expect(window.getComputedStyle(numberColumnFrame)['width']).toEqual('44px');
|
||||
|
||||
});
|
||||
|
||||
test("when the page has resized", () => {
|
||||
|
||||
// resize window and content column
|
||||
document.querySelector('main').setAttribute('style', 'width: 720px');
|
||||
screenMock.window.resizeTo({ height: 768, width: 960 });
|
||||
|
||||
// table should set its width to be that of `<main>`, minus margin-left for the row numbers column
|
||||
expect(window.getComputedStyle(tableFrame)['width']).toEqual('680px'); // width of content column - numbers column
|
||||
expect(window.getComputedStyle(tableFrame)['margin-left']).toEqual('40px'); // width of numbers column
|
||||
|
||||
// table for number column has 4px extra to allow space for drop shadow
|
||||
expect(window.getComputedStyle(numberColumnFrame)['width']).toEqual('44px');
|
||||
|
||||
});
|
||||
|
||||
test('handles mid-scroll: left + right shadows active', () => {
|
||||
setupDOM(250, 1000, 500);
|
||||
expect(wrapper.classList.contains('scrolled')).toBe(true);
|
||||
expect(rightShadow.classList.contains('visible')).toBe(true);
|
||||
});
|
||||
|
||||
describe("when the table scrolls horizontally", () => {
|
||||
let rightEdgeShadow;
|
||||
test('gracefully handles missing right shadow', () => {
|
||||
document.body.innerHTML = `
|
||||
<div class="fullscreen-table">
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<tr><td>Only content</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = document.querySelector('.table-wrapper');
|
||||
|
||||
// start module
|
||||
window.GOVUK.modules.start();
|
||||
Object.defineProperty(wrapper, 'scrollLeft', { get: () => 100, configurable: true });
|
||||
Object.defineProperty(wrapper, 'scrollWidth', { get: () => 500, configurable: true });
|
||||
Object.defineProperty(wrapper, 'clientWidth', { get: () => 200, configurable: true });
|
||||
|
||||
tableFrame = document.querySelector('.fullscreen-scrollable-table');
|
||||
table = tableFrame.querySelector('table');
|
||||
numberColumnFrame = document.querySelector('.fullscreen-fixed-table');
|
||||
rightEdgeShadow = container.querySelector('.fullscreen-right-shadow');
|
||||
|
||||
tableFrame.setAttribute('style', 'width: 742px');
|
||||
table.setAttribute('style', 'width: 1000px');
|
||||
|
||||
});
|
||||
|
||||
test("the right edge of the table scroll area should have a drop-shadow if it isn't scrolled", () => {
|
||||
|
||||
tableFrame.scrollLeft = 0;
|
||||
helpers.triggerEvent(tableFrame, 'scroll');
|
||||
|
||||
expect(numberColumnFrame.classList.contains('fullscreen-scrolled-table')).toBe(false);
|
||||
expect(rightEdgeShadow.classList.contains('visible')).toBe(true);
|
||||
|
||||
});
|
||||
|
||||
test("the left edge of the table scroll area should have a drop-shadow if the table is scrolled to 100%", () => {
|
||||
|
||||
// scroll to end of table
|
||||
tableFrame.scrollLeft = 258;
|
||||
helpers.triggerEvent(tableFrame, 'scroll');
|
||||
|
||||
expect(numberColumnFrame.classList.contains('fullscreen-scrolled-table')).toBe(true);
|
||||
expect(rightEdgeShadow.classList.contains('visible')).toBe(false);
|
||||
|
||||
});
|
||||
|
||||
test("both edges of the table scroll area should have a drop-shadow if the table is scrolled between 0% and 100%", () => {
|
||||
|
||||
// scroll to middle of table
|
||||
tableFrame.scrollLeft = 129;
|
||||
helpers.triggerEvent(tableFrame, 'scroll');
|
||||
|
||||
expect(numberColumnFrame.classList.contains('fullscreen-scrolled-table')).toBe(true);
|
||||
expect(rightEdgeShadow.classList.contains('visible')).toBe(true);
|
||||
|
||||
});
|
||||
// 👇 Call after building DOM
|
||||
window.initFullscreenTables();
|
||||
wrapper.dispatchEvent(new Event('scroll'));
|
||||
|
||||
expect(wrapper.classList.contains('scrolled')).toBe(true);
|
||||
// No error thrown for missing .fullscreen-right-shadow
|
||||
});
|
||||
|
||||
describe("when the table is focused", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
// start module
|
||||
window.GOVUK.modules.start();
|
||||
|
||||
tableFrame = document.querySelector('.fullscreen-scrollable-table');
|
||||
tableFrame.focus();
|
||||
|
||||
});
|
||||
|
||||
test("it should make the parent frame a focus style", () => {
|
||||
|
||||
expect(container.classList.contains('js-focus-style')).toBe(true);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
0
tests/javascripts/main.test.js
Normal file
0
tests/javascripts/main.test.js
Normal file
@@ -765,6 +765,10 @@ def test_bad_or_missing_data(
|
||||
assert recipients.has_errors is True
|
||||
|
||||
|
||||
# TODO in the first test where the expected result is {0,1}, the original
|
||||
# expected result was {0,1,2}. Why? In restoring international capability
|
||||
# for some reason +447900123 now looks legit. It may have more to do with
|
||||
# formatting than the actually validity of the number, not sure.
|
||||
@pytest.mark.parametrize(
|
||||
("file_contents", "rows_with_bad_recipients"),
|
||||
[
|
||||
@@ -775,7 +779,7 @@ def test_bad_or_missing_data(
|
||||
1234
|
||||
+447900123
|
||||
""",
|
||||
{0, 1, 2},
|
||||
{0, 1},
|
||||
),
|
||||
(
|
||||
"""
|
||||
@@ -784,7 +788,7 @@ def test_bad_or_missing_data(
|
||||
+12022340104, USA
|
||||
+23051234567, Mauritius
|
||||
""",
|
||||
{2},
|
||||
set(),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -23,21 +23,17 @@ valid_us_phone_numbers = [
|
||||
"(202) 555-0104",
|
||||
]
|
||||
|
||||
# TODO
|
||||
# International phone number tests are commented out as a result of issue #943 in notifications-admin. We are
|
||||
# deliberately eliminating the ability to send to numbers outside of country code 1. These tests should
|
||||
# be removed at some point when we are sure we are never going to support international numbers
|
||||
|
||||
valid_international_phone_numbers = [
|
||||
# "+71234567890", # Russia
|
||||
# "+447123456789", # UK
|
||||
# "+4407123456789", # UK
|
||||
# "+4407123 456789", # UK
|
||||
# "+4407123-456-789", # UK
|
||||
# "+23051234567", # Mauritius,
|
||||
# "+682 12345", # Cook islands
|
||||
# "+3312345678",
|
||||
# "+9-2345-12345-12345", # 15 digits
|
||||
"+71234567890", # Russia
|
||||
"+447123456789", # UK
|
||||
"+4407123456789", # UK
|
||||
"+4407123 456789", # UK
|
||||
"+4407123-456-789", # UK
|
||||
"+23051234567", # Mauritius,
|
||||
"+682 12345", # Cook islands
|
||||
"+3312345678",
|
||||
"+9-2345-12345-12345", # 15 digits
|
||||
]
|
||||
|
||||
|
||||
@@ -84,7 +80,7 @@ invalid_us_phone_numbers = sum(
|
||||
invalid_phone_numbers = [
|
||||
("+80233456789", "Not a valid country prefix"),
|
||||
("1234567", "Not enough digits"),
|
||||
("+682 1234", "Invalid country code"), # Cook Islands phone numbers can be 5 digits
|
||||
("+682 1234", "Not enough digits"),
|
||||
("+12345 12345 12345 6", "Too many digits"),
|
||||
]
|
||||
|
||||
@@ -155,46 +151,46 @@ def test_detect_us_phone_numbers(phone_number):
|
||||
@pytest.mark.parametrize(
|
||||
("phone_number", "expected_info"),
|
||||
[
|
||||
# (
|
||||
# "+4407900900123",
|
||||
# international_phone_info(
|
||||
# international=True,
|
||||
# country_prefix="44", # UK
|
||||
# billable_units=1,
|
||||
# ),
|
||||
# ),
|
||||
# (
|
||||
# "+4407700900123",
|
||||
# international_phone_info(
|
||||
# international=True,
|
||||
# country_prefix="44", # Number in TV range
|
||||
# billable_units=1,
|
||||
# ),
|
||||
# ),
|
||||
# (
|
||||
# "+4407700800123",
|
||||
# international_phone_info(
|
||||
# international=True,
|
||||
# country_prefix="44", # UK Crown dependency, so prefix same as UK
|
||||
# billable_units=1,
|
||||
# ),
|
||||
# ),
|
||||
# ( #
|
||||
# "+20-12-1234-1234",
|
||||
# international_phone_info(
|
||||
# international=True,
|
||||
# country_prefix="20", # Egypt
|
||||
# billable_units=1,
|
||||
# ),
|
||||
# ),
|
||||
# (
|
||||
# "+201212341234",
|
||||
# international_phone_info(
|
||||
# international=True,
|
||||
# country_prefix="20", # Egypt
|
||||
# billable_units=1,
|
||||
# ),
|
||||
# ),
|
||||
(
|
||||
"+4407900900123",
|
||||
international_phone_info(
|
||||
international=True,
|
||||
country_prefix="44", # UK
|
||||
billable_units=1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"+4407700900123",
|
||||
international_phone_info(
|
||||
international=True,
|
||||
country_prefix="44", # Number in TV range
|
||||
billable_units=1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"+4407700800123",
|
||||
international_phone_info(
|
||||
international=True,
|
||||
country_prefix="44", # UK Crown dependency, so prefix same as UK
|
||||
billable_units=1,
|
||||
),
|
||||
),
|
||||
( #
|
||||
"+20-12-1234-1234",
|
||||
international_phone_info(
|
||||
international=True,
|
||||
country_prefix="20", # Egypt
|
||||
billable_units=1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"+201212341234",
|
||||
international_phone_info(
|
||||
international=True,
|
||||
country_prefix="20", # Egypt
|
||||
billable_units=1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"+1 664-491-3434",
|
||||
international_phone_info(
|
||||
@@ -203,14 +199,14 @@ def test_detect_us_phone_numbers(phone_number):
|
||||
billable_units=1,
|
||||
),
|
||||
),
|
||||
# (
|
||||
# "+71234567890",
|
||||
# international_phone_info(
|
||||
# international=True,
|
||||
# country_prefix="7", # Russia
|
||||
# billable_units=1,
|
||||
# ),
|
||||
# ),
|
||||
(
|
||||
"+71234567890",
|
||||
international_phone_info(
|
||||
international=True,
|
||||
country_prefix="7", # Russia
|
||||
billable_units=1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"1-202-555-0104",
|
||||
international_phone_info(
|
||||
@@ -227,14 +223,14 @@ def test_detect_us_phone_numbers(phone_number):
|
||||
billable_units=1,
|
||||
),
|
||||
),
|
||||
# (
|
||||
# "+23051234567",
|
||||
# international_phone_info(
|
||||
# international=True,
|
||||
# country_prefix="230", # Mauritius
|
||||
# billable_units=1,
|
||||
# ),
|
||||
# ),
|
||||
(
|
||||
"+23051234567",
|
||||
international_phone_info(
|
||||
international=True,
|
||||
country_prefix="230", # Mauritius
|
||||
billable_units=1,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_get_international_info(phone_number, expected_info):
|
||||
@@ -287,11 +283,11 @@ def test_valid_us_phone_number_can_be_formatted_consistently(phone_number):
|
||||
@pytest.mark.parametrize(
|
||||
("phone_number", "expected_formatted"),
|
||||
[
|
||||
# ("+44071234567890", "+4471234567890"),
|
||||
("+44071234567890", "+4471234567890"),
|
||||
("1-202-555-0104", "+12025550104"),
|
||||
("+12025550104", "+12025550104"),
|
||||
("12025550104", "+12025550104"),
|
||||
# ("+23051234567", "+23051234567"),
|
||||
("+23051234567", "+23051234567"),
|
||||
],
|
||||
)
|
||||
def test_valid_international_phone_number_can_be_formatted_consistently(
|
||||
@@ -362,17 +358,17 @@ def test_validates_against_guestlist_of_phone_numbers(phone_number):
|
||||
)
|
||||
|
||||
|
||||
# @pytest.mark.parametrize(
|
||||
# "recipient_number, allowlist_number",
|
||||
# [
|
||||
# ["+4407123-456-789", "+4407123456789"],
|
||||
# ["+4407123456789", "+4407123-456-789"],
|
||||
# ],
|
||||
# )
|
||||
# def test_validates_against_guestlist_of_international_phone_numbers(
|
||||
# recipient_number, allowlist_number
|
||||
# ):
|
||||
# assert allowed_to_send_to(recipient_number, [allowlist_number])
|
||||
@pytest.mark.parametrize(
|
||||
("recipient_number", "allowlist_number"),
|
||||
[
|
||||
("+4407123-456-789", "+4407123456789"),
|
||||
("+4407123456789", "+4407123-456-789"),
|
||||
],
|
||||
)
|
||||
def test_validates_against_guestlist_of_international_phone_numbers(
|
||||
recipient_number, allowlist_number
|
||||
):
|
||||
assert allowed_to_send_to(recipient_number, [allowlist_number])
|
||||
|
||||
|
||||
@pytest.mark.parametrize("email_address", valid_email_addresses)
|
||||
@@ -385,16 +381,17 @@ def test_validates_against_guestlist_of_email_addresses(email_address):
|
||||
@pytest.mark.parametrize(
|
||||
("phone_number", "expected_formatted"),
|
||||
[
|
||||
# ("+4407900900123", "+44 7900 900123"), # UK
|
||||
# ("+44(0)7900900123", "+44 7900 900123"), # UK
|
||||
# ("+447900900123", "+44 7900 900123"), # UK
|
||||
("+4407900900123", "+44 7900 900123"), # UK
|
||||
("+44(0)7900900123", "+44 7900 900123"), # UK
|
||||
("+447900900123", "+44 7900 900123"), # UK
|
||||
# TODO these should be fixed, but affect readability, not sendability
|
||||
# ("+20-12-1234-1234", "+20 121 234 1234"), # Egypt
|
||||
# ("+201212341234", "+20 121 234 1234"), # Egypt
|
||||
("+1 664 491-3434", "+1 664-491-3434"), # Montserrat
|
||||
# ("+7 499 1231212", "+7 499 123-12-12"), # Moscow (Russia)
|
||||
("+7 499 1231212", "+7 499 123-12-12"), # Moscow (Russia)
|
||||
("1-202-555-0104", "(202) 555-0104"), # Washington DC (USA)
|
||||
# ("+23051234567", "+230 5123 4567"), # Mauritius
|
||||
# ("+33(0)1 12345678", "+33 1 12 34 56 78"), # Paris (France)
|
||||
("+23051234567", "+230 5123 4567"), # Mauritius
|
||||
("+33(0)1 12345678", "+33 1 12 34 56 78"), # Paris (France)
|
||||
],
|
||||
)
|
||||
def test_format_us_and_international_phone_numbers(phone_number, expected_formatted):
|
||||
@@ -411,7 +408,7 @@ def test_format_us_and_international_phone_numbers(phone_number, expected_format
|
||||
(None, ""),
|
||||
("foo", "foo"),
|
||||
("TeSt@ExAmPl3.com", "test@exampl3.com"),
|
||||
# ("+4407900 900 123", "+447900900123"),
|
||||
("+4407900 900 123", "+447900900123"),
|
||||
("+1 800 555 5555", "+18005555555"),
|
||||
],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user