merge from main and resolve conflicts

This commit is contained in:
Kenneth Kehl
2023-09-22 12:37:32 -07:00
35 changed files with 1162 additions and 859 deletions
+7 -9
View File
@@ -12,23 +12,21 @@
<link rel="shortcut icon" sizes="16x16 32x32 48x48" href="/static/images/favicon.ico?de7abc5226925203ac10b0a4a94af949" <link rel="shortcut icon" sizes="16x16 32x32 48x48" href="/static/images/favicon.ico?de7abc5226925203ac10b0a4a94af949"
type="image/x-icon" /> type="image/x-icon" />
<link rel="mask-icon" href="/static/images/govuk-mask-icon.svg?2afd125b1db96109388d4a0727312f3e" color="#0b0c0c"> <link rel="mask-icon" href="/static/images/usa-mask-icon.svg?2afd125b1db96109388d4a0727312f3e" color="#F0F0F0">
<link rel="apple-touch-icon" sizes="180x180" <link rel="apple-touch-icon" sizes="180x180"
href="/static/images/govuk-apple-touch-icon-180x180.png?a0f7e1b728a42016b247dc54ee40d055"> href="/static/images/apple-touch-icon.png?a0f7e1b728a42016b247dc54ee40d055">
<link rel="apple-touch-icon" sizes="167x167" <link rel="icon" type="image/png" sizes="32x32" href="/static/images/favicon-32x32.png">
href="/static/images/govuk-apple-touch-icon-167x167.png?f636cb7d471fc6239d2241d75cbdabcb"> <link rel="icon" type="image/png" sizes="16x16" href="/static/images/favicon-16x16.png">
<link rel="apple-touch-icon" sizes="152x152" <link rel="manifest" href="/static/images/site.webmanifest">
href="/static/images/govuk-apple-touch-icon-152x152.png?40846d46aa37232e2b35065769ce567c">
<link rel="apple-touch-icon" href="/static/images/govuk-apple-touch-icon.png?3c45daa1f1e716458e4da954b973002e">
<link rel="stylesheet" media="screen" href="/static/stylesheets/main.css?d077f86473501ab244de0b30600536ee" /> <link rel="stylesheet" media="screen" href="/static/stylesheets/main.css?d077f86473501ab244de0b30600536ee" />
<link rel="stylesheet" media="print" href="/static/stylesheets/print.css?28010888ca5719dc83d7c2c80f6ed2b2" /> <link rel="stylesheet" media="print" href="/static/stylesheets/print.css?28010888ca5719dc83d7c2c80f6ed2b2" />
<meta property="og:image" content="/static/images/govuk-opengraph-image.png"> <meta property="og:image" content="/static/images/usa-opengraph-image.png">
</head> </head>
<body class="usa-template__body"> <body class="usa-template__body">
<a href="#main-content" class="govuk-skip-link">Skip to main content</a> <a href="#main-content" class="usa-skip-link">Skip to main content</a>
<header class="usa-header " role="banner" data-module="header"> <header class="usa-header " role="banner" data-module="header">
<div class="usa-header__container usa-width-container"> <div class="usa-header__container usa-width-container">
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+1
View File
@@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
+4
View File
@@ -0,0 +1,4 @@
<svg width="680" height="680" viewBox="0 0 680 680" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M672 211H332V161L672 161V211ZM672 294H332V244H672V294ZM672 377H332V327H672V377ZM672 410V460H8V410H672ZM672 493V543H8V493H672Z" fill="#D83933"/>
<path d="M8 161H299V377H8V161Z" fill="#3C3B6E"/>
</svg>

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+11 -1
View File
@@ -1,3 +1,5 @@
import os
from flask import ( from flask import (
Markup, Markup,
abort, abort,
@@ -10,9 +12,10 @@ from flask import (
) )
from flask_login import current_user from flask_login import current_user
from app import login_manager from app import login_manager, user_api_client
from app.main import main from app.main import main
from app.main.forms import LoginForm from app.main.forms import LoginForm
from app.main.views.verify import activate_user
from app.models.user import InvitedUser, User from app.models.user import InvitedUser, User
from app.utils import hide_from_search_engines from app.utils import hide_from_search_engines
from app.utils.login import is_safe_redirect_url from app.utils.login import is_safe_redirect_url
@@ -22,6 +25,12 @@ from app.utils.login import is_safe_redirect_url
@hide_from_search_engines @hide_from_search_engines
def sign_in(): def sign_in():
redirect_url = request.args.get("next") redirect_url = request.args.get("next")
if os.getenv("NOTIFY_E2E_TEST_EMAIL"):
user = user_api_client.get_user_by_email(os.getenv("NOTIFY_E2E_TEST_EMAIL"))
activate_user(user["id"])
return redirect(url_for("main.show_accounts_or_dashboard", next=redirect_url))
if current_user and current_user.is_authenticated: if current_user and current_user.is_authenticated:
if redirect_url and is_safe_redirect_url(redirect_url): if redirect_url and is_safe_redirect_url(redirect_url):
return redirect(redirect_url) return redirect(redirect_url)
@@ -58,6 +67,7 @@ def sign_in():
if user.sms_auth: if user.sms_auth:
return redirect(url_for(".two_factor_sms", next=redirect_url)) return redirect(url_for(".two_factor_sms", next=redirect_url))
if user.email_auth: if user.email_auth:
return redirect( return redirect(
url_for(".two_factor_email_sent", next=redirect_url) url_for(".two_factor_email_sent", next=redirect_url)
+5 -11
View File
@@ -3,23 +3,15 @@
{% block headIcons %} {% block headIcons %}
<link rel="shortcut icon" sizes="16x16 32x32 48x48" href="{{ asset_url('images/favicon.ico') }}" type="image/x-icon" /> <link rel="shortcut icon" sizes="16x16 32x32 48x48" href="{{ asset_url('images/favicon.ico') }}" type="image/x-icon" />
<link rel="mask-icon" href="{{ asset_url('images/govuk-mask-icon.svg') }}" color="{{ themeColor | default('#0b0c0c') }}"> {# Hardcoded value of $govuk-black #} <link rel="mask-icon" href="{{ asset_url('images/usa-mask-icon.svg') }}" color="{{ themeColor | default('#F0F0F0') }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ asset_url('images/govuk-apple-touch-icon-180x180.png') }}"> <link rel="apple-touch-icon" sizes="180x180" href="{{ asset_url('images/apple-touch-icon.png') }}">
<link rel="apple-touch-icon" sizes="167x167" href="{{ asset_url('images/govuk-apple-touch-icon-167x167.png') }}"> <link rel="apple-touch-icon" href="{{ asset_url('images/apple-touch-icon.png') }}">
<link rel="apple-touch-icon" sizes="152x152" href="{{ asset_url('images/govuk-apple-touch-icon-152x152.png') }}">
<link rel="apple-touch-icon" href="{{ asset_url('images/govuk-apple-touch-icon.png') }}">
{% endblock %} {% endblock %}
{% block head %} {% block head %}
{%- for font in font_paths %}
<link rel="preload" href="{{ asset_url(font, with_querystring_hash=False) }}" as="font" type="font/woff2" crossorigin>
{%- endfor %}
<link rel="stylesheet" media="screen" href="{{ asset_url('css/styles.css') }}" /> <link rel="stylesheet" media="screen" href="{{ asset_url('css/styles.css') }}" />
{% block extra_stylesheets %} {% block extra_stylesheets %}
{% endblock %} {% endblock %}
<style nonce="{{ csp_nonce() }}">
.govuk-header__container { border-color: {{header_colour}} }
</style>
{% if g.hide_from_search_engines %} {% if g.hide_from_search_engines %}
<meta name="robots" content="noindex" /> <meta name="robots" content="noindex" />
{% endif %} {% endif %}
@@ -28,6 +20,8 @@
<meta name="format-detection" content="telephone=no"> <meta name="format-detection" content="telephone=no">
{% endblock %} {% endblock %}
{% block meta %} {% block meta %}
<meta property="og:site_name" content="Notify.gov">
<meta property="og:image" content="{{ asset_url('images/usa-opengraph-image.png') }}">
{% endblock %} {% endblock %}
<!-- <script type="text/javascript" src="{{ asset_url('js/gtm_head.js') }}"></script> --> <!-- <script type="text/javascript" src="{{ asset_url('js/gtm_head.js') }}"></script> -->
{% endblock %} {% endblock %}
+7 -7
View File
@@ -20,17 +20,17 @@
{% block headIcons %} {% block headIcons %}
<link rel="shortcut icon" sizes="16x16 32x32 48x48" href="{{ assetPath | default('/assets') }}/images/favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" sizes="16x16 32x32 48x48" href="{{ assetPath | default('/assets') }}/images/favicon.ico" type="image/x-icon" />
<link rel="mask-icon" href="{{ assetPath | default('/assets') }}/images/govuk-mask-icon.svg" color="{{ themeColor | default('#0b0c0c') }}"> {# Hardcoded value of $govuk-black #} <link rel="mask-icon" href="{{ assetPath | default('/assets') }}/images/usa-mask-icon.svg" color="{{ themeColor | default('#F0F0F0') }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ assetPath | default('/assets') }}/images/govuk-apple-touch-icon-180x180.png"> <link rel="apple-touch-icon" sizes="180x180" href="{{ assetPath | default('/assets') }}/images/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="167x167" href="{{ assetPath | default('/assets') }}/images/govuk-apple-touch-icon-167x167.png"> <link rel="apple-touch-icon" href="{{ assetPath | default('/assets') }}/images/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="152x152" href="{{ assetPath | default('/assets') }}/images/govuk-apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" href="{{ assetPath | default('/assets') }}/images/govuk-apple-touch-icon.png">
{% endblock %} {% endblock %}
{% block head %}{% endblock %} {% block head %}{% endblock %}
{# The default og:image is added below head so that scrapers see any custom metatags first, and this is just a fallback #} {# The default og:image is added below head so that scrapers see any custom metatags first, and this is just a fallback #}
{# image url needs to be absolute e.g. http://wwww.domain.com/.../govuk-opengraph-image.png #} {% block meta %}
<!-- <meta property="og:image" content="{{ assetUrl | default('/assets') }}/images/govuk-opengraph-image.png"> --> <meta property="og:site_name" content="Notify.gov">
<meta property="og:image" content="{{ asset_url('images/usa-opengraph-image.png') }}">
{% endblock %}
</head> </head>
<body class="usa-template__body {{ bodyClasses }}"> <body class="usa-template__body {{ bodyClasses }}">
<script nonce="{{ csp_nonce() }}">document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled');</script> <script nonce="{{ csp_nonce() }}">document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled');</script>
+1 -2
View File
@@ -6,8 +6,7 @@ applications:
instances: ((instances)) instances: ((instances))
memory: ((memory)) memory: ((memory))
command: newrelic-admin run-program gunicorn -c /home/vcap/app/gunicorn_config.py application command: newrelic-admin run-program gunicorn -c /home/vcap/app/gunicorn_config.py application
health-check-type: http health-check-type: port
health-check-http-endpoint: '/_status?simple=true'
health-check-invocation-timeout: 10 health-check-invocation-timeout: 10
routes: routes:
- route: ((public_admin_route)) - route: ((public_admin_route))
+2 -7
View File
@@ -12,14 +12,9 @@
<link rel="shortcut icon" sizes="16x16 32x32 48x48" <link rel="shortcut icon" sizes="16x16 32x32 48x48"
href="/static_503/images/favicon.ico?de7abc5226925203ac10b0a4a94af949" type="image/x-icon" /> href="/static_503/images/favicon.ico?de7abc5226925203ac10b0a4a94af949" type="image/x-icon" />
<link rel="mask-icon" href="/static_503/images/govuk-mask-icon.svg?2afd125b1db96109388d4a0727312f3e" color="#0b0c0c"> <link rel="mask-icon" href="/static_503/images/usa-mask-icon.svg?2afd125b1db96109388d4a0727312f3e" color="#0b0c0c">
<link rel="apple-touch-icon" sizes="180x180" <link rel="apple-touch-icon" sizes="180x180"
href="/static_503/images/govuk-apple-touch-icon-180x180.png?a0f7e1b728a42016b247dc54ee40d055"> href="/static_503/images/apple-touch-icon.png?a0f7e1b728a42016b247dc54ee40d055">
<link rel="apple-touch-icon" sizes="167x167"
href="/static_503/images/govuk-apple-touch-icon-167x167.png?f636cb7d471fc6239d2241d75cbdabcb">
<link rel="apple-touch-icon" sizes="152x152"
href="/static_503/images/govuk-apple-touch-icon-152x152.png?40846d46aa37232e2b35065769ce567c">
<link rel="apple-touch-icon" href="/static_503/images/govuk-apple-touch-icon.png?3c45daa1f1e716458e4da954b973002e">
<link rel="stylesheet" media="screen" href="/static_503/stylesheets/main.css?d077f86473501ab244de0b30600536ee" /> <link rel="stylesheet" media="screen" href="/static_503/stylesheets/main.css?d077f86473501ab244de0b30600536ee" />
<link rel="stylesheet" media="print" href="/static_503/stylesheets/print.css?28010888ca5719dc83d7c2c80f6ed2b2" /> <link rel="stylesheet" media="print" href="/static_503/stylesheets/print.css?28010888ca5719dc83d7c2c80f6ed2b2" />
+918 -613
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -11,7 +11,8 @@
"test-watch": "jest --watch --config tests/javascripts/jest.config.js tests/javascripts", "test-watch": "jest --watch --config tests/javascripts/jest.config.js tests/javascripts",
"build": "gulp", "build": "gulp",
"watch": "gulp watch", "watch": "gulp watch",
"audit": "better-npm-audit audit --production --level low" "audit": "better-npm-audit audit --production --level low",
"pa11y-ci": "pa11y-ci"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
Generated
+32 -38
View File
@@ -832,13 +832,13 @@ pyflakes = ">=3.1.0,<3.2.0"
[[package]] [[package]]
name = "flake8-bugbear" name = "flake8-bugbear"
version = "23.7.10" version = "23.9.16"
description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
optional = false optional = false
python-versions = ">=3.8.1" python-versions = ">=3.8.1"
files = [ files = [
{file = "flake8-bugbear-23.7.10.tar.gz", hash = "sha256:0ebdc7d8ec1ca8bd49347694562381f099f4de2f8ec6bda7a7dca65555d9e0d4"}, {file = "flake8-bugbear-23.9.16.tar.gz", hash = "sha256:90cf04b19ca02a682feb5aac67cae8de742af70538590509941ab10ae8351f71"},
{file = "flake8_bugbear-23.7.10-py3-none-any.whl", hash = "sha256:d99d005114020fbef47ed5e4aebafd22f167f9a0fbd0d8bf3c9e90612cb25c34"}, {file = "flake8_bugbear-23.9.16-py3-none-any.whl", hash = "sha256:b182cf96ea8f7a8595b2f87321d7d9b28728f4d9c3318012d896543d19742cb5"},
] ]
[package.dependencies] [package.dependencies]
@@ -2427,36 +2427,38 @@ diagrams = ["jinja2", "railroad-diagrams"]
[[package]] [[package]]
name = "pyproj" name = "pyproj"
version = "3.6.0" version = "3.6.1"
description = "Python interface to PROJ (cartographic projections and coordinate transformations library)" description = "Python interface to PROJ (cartographic projections and coordinate transformations library)"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "pyproj-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e600f6a2771d3b41aeb2cc1efd96771ae9a01451013da1dd48ff272e7c6e34ef"}, {file = "pyproj-3.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab7aa4d9ff3c3acf60d4b285ccec134167a948df02347585fdd934ebad8811b4"},
{file = "pyproj-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7f6cd045df29aae960391dfe06a575c110af598f1dea5add8be6ca42332b0f5"}, {file = "pyproj-3.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4bc0472302919e59114aa140fd7213c2370d848a7249d09704f10f5b062031fe"},
{file = "pyproj-3.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:557e6592855111c84eda176ddf6b130f55d5e2b9cb1c017b8c91b69f37f474f5"}, {file = "pyproj-3.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5279586013b8d6582e22b6f9e30c49796966770389a9d5b85e25a4223286cd3f"},
{file = "pyproj-3.6.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de6288b6ceabdeeac01abf627c74414822d322d8f55dc8efe4d29dedd27c5719"}, {file = "pyproj-3.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fafd1f3eb421694857f254a9bdbacd1eb22fc6c24ca74b136679f376f97d35"},
{file = "pyproj-3.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e427ccdbb1763872416549bdfa9fa1f5f169054653c4daf674e71480cc39cf11"}, {file = "pyproj-3.6.1-cp310-cp310-win32.whl", hash = "sha256:c41e80ddee130450dcb8829af7118f1ab69eaf8169c4bf0ee8d52b72f098dc2f"},
{file = "pyproj-3.6.0-cp310-cp310-win32.whl", hash = "sha256:1283d3c1960edbb74828f5f3405b27578a9a27f7766ab6a3956f4bd851f08239"}, {file = "pyproj-3.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:db3aedd458e7f7f21d8176f0a1d924f1ae06d725228302b872885a1c34f3119e"},
{file = "pyproj-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:9de1aab71234bfd3fd648a1152519b5ee152c43113d7d8ea52590a0140129501"}, {file = "pyproj-3.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ebfbdbd0936e178091309f6cd4fcb4decd9eab12aa513cdd9add89efa3ec2882"},
{file = "pyproj-3.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:00fab048596c17572fa8980014ef117dbb2a445e6f7ba3b9ddfcc683efc598e7"}, {file = "pyproj-3.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:447db19c7efad70ff161e5e46a54ab9cc2399acebb656b6ccf63e4bc4a04b97a"},
{file = "pyproj-3.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ba5e7c8ddd6ed5a3f9fcf95ea80ba44c931913723de2ece841c94bb38b200c4a"}, {file = "pyproj-3.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7e13c40183884ec7f94eb8e0f622f08f1d5716150b8d7a134de48c6110fee85"},
{file = "pyproj-3.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08dfc5c9533c78a97afae9d53b99b810a4a8f97c3be9eb2b8f323b726c736403"}, {file = "pyproj-3.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65ad699e0c830e2b8565afe42bd58cc972b47d829b2e0e48ad9638386d994915"},
{file = "pyproj-3.6.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18a8bdb87aeb41b60a2e91d32f623227de3569fb83b4c64b174c3a7c5b0ed3ae"}, {file = "pyproj-3.6.1-cp311-cp311-win32.whl", hash = "sha256:8b8acc31fb8702c54625f4d5a2a6543557bec3c28a0ef638778b7ab1d1772132"},
{file = "pyproj-3.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfe392dfc0eba2248dc08c976a72f52ff9da2bddfddfd9ff5dcf18e8e88200c7"}, {file = "pyproj-3.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:38a3361941eb72b82bd9a18f60c78b0df8408416f9340521df442cebfc4306e2"},
{file = "pyproj-3.6.0-cp311-cp311-win32.whl", hash = "sha256:78276c6b0c831255c97c56dff7313a3571f327a284d8ac63d6a56437a72ed0e0"}, {file = "pyproj-3.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1e9fbaf920f0f9b4ee62aab832be3ae3968f33f24e2e3f7fbb8c6728ef1d9746"},
{file = "pyproj-3.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:8fbac2eb9a0e425d7d6b7c6f4ebacd675cf3bdef0c59887057b8b4b0374e7c12"}, {file = "pyproj-3.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d227a865356f225591b6732430b1d1781e946893789a609bb34f59d09b8b0f8"},
{file = "pyproj-3.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:95120d65cbc5983dfd877076f28dbc18b9b329cbee38ca6e217bb7a5a043c099"}, {file = "pyproj-3.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83039e5ae04e5afc974f7d25ee0870a80a6bd6b7957c3aca5613ccbe0d3e72bf"},
{file = "pyproj-3.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:830e6de7cfe43853967afee5ef908dfd5aa72d1ec12af9b9e3fecc179886e346"}, {file = "pyproj-3.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb059ba3bced6f6725961ba758649261d85ed6ce670d3e3b0a26e81cf1aa8d"},
{file = "pyproj-3.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e342b3010b2b20134671564ff9a8c476e5e512bf589477480aded1a5813af7c8"}, {file = "pyproj-3.6.1-cp312-cp312-win32.whl", hash = "sha256:2d6ff73cc6dbbce3766b6c0bce70ce070193105d8de17aa2470009463682a8eb"},
{file = "pyproj-3.6.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23787460fab85ba2f857ee60ffb2e8e21fd9bd5db9833c51c1c05b2a6d9f0be5"}, {file = "pyproj-3.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:7a27151ddad8e1439ba70c9b4b2b617b290c39395fa9ddb7411ebb0eb86d6fb0"},
{file = "pyproj-3.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595376e4d3bb72b7dceeccbce0f4c43053d47561f17a1ad0224407e9980ee849"}, {file = "pyproj-3.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4ba1f9b03d04d8cab24d6375609070580a26ce76eaed54631f03bab00a9c737b"},
{file = "pyproj-3.6.0-cp39-cp39-win32.whl", hash = "sha256:4d8a9773503085eada59b6892c96ddf686ab8cf64cfdc18ad744d13ee76dfa6f"}, {file = "pyproj-3.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18faa54a3ca475bfe6255156f2f2874e9a1c8917b0004eee9f664b86ccc513d3"},
{file = "pyproj-3.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:137a07404f937f264b11b7130cd4cfa00002dbe4333b222e8056db84849c2ea4"}, {file = "pyproj-3.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd43bd9a9b9239805f406fd82ba6b106bf4838d9ef37c167d3ed70383943ade1"},
{file = "pyproj-3.6.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2799499a4045e4fb73e44c31bdacab0593a253a7a4b6baae6fdd27d604cf9bc2"}, {file = "pyproj-3.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50100b2726a3ca946906cbaa789dd0749f213abf0cbb877e6de72ca7aa50e1ae"},
{file = "pyproj-3.6.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f04f6297c615c3b17f835df2556ac8fb9b4f51f281e960437eaf0cd80e7ae26a"}, {file = "pyproj-3.6.1-cp39-cp39-win32.whl", hash = "sha256:9274880263256f6292ff644ca92c46d96aa7e57a75c6df3f11d636ce845a1877"},
{file = "pyproj-3.6.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a4d2d438b007cb1f8d5f6f308d53d7ff9a2508cff8f9da6e2a93b76ffd98aaf"}, {file = "pyproj-3.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:36b64c2cb6ea1cc091f329c5bd34f9c01bb5da8c8e4492c709bda6a09f96808f"},
{file = "pyproj-3.6.0.tar.gz", hash = "sha256:a5b111865b3f0f8b77b3983f2fbe4dd6248fc09d3730295949977c8dcd988062"}, {file = "pyproj-3.6.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd93c1a0c6c4aedc77c0fe275a9f2aba4d59b8acf88cebfc19fe3c430cfabf4f"},
{file = "pyproj-3.6.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6420ea8e7d2a88cb148b124429fba8cd2e0fae700a2d96eab7083c0928a85110"},
{file = "pyproj-3.6.1.tar.gz", hash = "sha256:44aa7c704c2b7d8fb3d483bbf75af6cb2350d30a63b144279a09b75fead501bf"},
] ]
[package.dependencies] [package.dependencies]
@@ -3491,12 +3493,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "acc2115b5eaa279182864792a37cca84eb5ac24b4901a896c7ffa12b71e5f378" content-hash = "3d8f02aa66cdf17e8295d198f663dbd21eb0ea9bfd22d72f2e8563dd26de50e4"
cf login -a api.fr.cloud.gov --sso
cf t -s notify-staging
cf logs notify-staging-api (get list of apps with cf apps)
+3 -3
View File
@@ -29,7 +29,7 @@ pyexcel-ods3 = "==0.6.1"
pyexcel-xls = "==0.7.0" pyexcel-xls = "==0.7.0"
pyexcel-xlsx = "==0.6.0" pyexcel-xlsx = "==0.6.0"
openpyxl = "==3.0.10" openpyxl = "==3.0.10"
pyproj = "==3.6.0" pyproj = "==3.6.1"
python-dotenv = "==1.0.0" python-dotenv = "==1.0.0"
pytz = "==2023.3.post1" pytz = "==2023.3.post1"
rtreelib = "==0.2.0" rtreelib = "==0.2.0"
@@ -53,9 +53,9 @@ pytest-xdist = "==3.3.1"
beautifulsoup4 = "==4.12.2" beautifulsoup4 = "==4.12.2"
freezegun = "==1.2.2" freezegun = "==1.2.2"
flake8 = "==6.1.0" flake8 = "==6.1.0"
flake8-bugbear = "==23.7.10" flake8-bugbear = "==23.9.16"
flake8-print = "==5.0.0" flake8-print = "==5.0.0"
moto = "~=4.1" moto = "~=4.2"
requests-mock = "==1.11.0" requests-mock = "==1.11.0"
jinja2-cli = {version = "==0.8.2", extras = ["yaml"]} jinja2-cli = {version = "==0.8.2", extras = ["yaml"]}
pip-audit = "*" pip-audit = "*"
-20
View File
@@ -294,26 +294,6 @@ def test_email_branding_preview(
assert mock_get_email_branding.called is email_branding_retrieved assert mock_get_email_branding.called is email_branding_retrieved
def test_font_preload(
client_request,
mock_get_service_and_organization_counts,
):
client_request.logout()
page = client_request.get("main.index", _test_page_title=False)
preload_tags = page.select(
'link[rel=preload][as=font][type="font/woff2"][crossorigin]'
)
assert (
len(preload_tags) == 4
), "Run `npm run build` to copy fonts into app/static/fonts/"
for element in preload_tags:
assert element["href"].startswith("https://static.example.com/fonts/")
assert element["href"].endswith(".woff2")
@pytest.mark.parametrize("current_date, expected_rate", (("2022-05-01", "1.72"),)) @pytest.mark.parametrize("current_date, expected_rate", (("2022-05-01", "1.72"),))
@pytest.mark.skip(reason="Currently hidden for TTS") @pytest.mark.skip(reason="Currently hidden for TTS")
def test_sms_price( def test_sms_price(
+3
View File
@@ -9,6 +9,7 @@ from uuid import UUID, uuid4
import pytest import pytest
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from dotenv import load_dotenv
from flask import Flask, url_for from flask import Flask, url_for
from notifications_python_client.errors import HTTPError from notifications_python_client.errors import HTTPError
from notifications_utils.url_safe_token import generate_token from notifications_utils.url_safe_token import generate_token
@@ -33,6 +34,8 @@ from . import (
user_json, user_json,
) )
load_dotenv()
class ElementNotFound(Exception): class ElementNotFound(Exception):
pass pass
+48 -30
View File
@@ -2,50 +2,45 @@ import datetime
import os import os
import re import re
import pytest
from playwright.sync_api import expect from playwright.sync_api import expect
@pytest.mark.skip(reason="Not authenticating test users.") def _bypass_sign_in(end_to_end_context):
def test_accounts_page(end_to_end_authenticated_context):
# Open a new page and go to the staging site. # Open a new page and go to the staging site.
page = end_to_end_authenticated_context.new_page() page = end_to_end_context.new_page()
accounts_uri = "{}accounts".format(os.getenv("NOTIFY_E2E_TEST_URI")) page.goto(os.getenv("NOTIFY_E2E_TEST_URI"))
page.goto(accounts_uri) sign_in_button = page.get_by_role("link", name="Sign in")
# Check to make sure that we've arrived at the next page. # Test trying to sign in. Because we are loading the email and password
sign_in_button.click()
# Wait for the next page to fully load.
page.wait_for_load_state("domcontentloaded")
return page
def test_add_new_service_workflow(end_to_end_context):
# page = end_to_end_context.new_page()
page = _bypass_sign_in(end_to_end_context)
page.goto(os.getenv("NOTIFY_E2E_TEST_URI"))
# sign_in_button = page.get_by_role("link", name="Sign in")
#
# Test trying to sign in. Because we are loading the email and password
# sign_in_button.click()
#
# Wait for the next page to fully load.
page.wait_for_load_state("domcontentloaded") page.wait_for_load_state("domcontentloaded")
# Check to make sure that we've arrived at the next page.
# Check the page title exists and matches what we expect.
expect(page).to_have_title(re.compile("Choose service"))
# Check for the sign in heading.
sign_in_heading = page.get_by_role("heading", name="Choose service")
expect(sign_in_heading).to_be_visible()
# Retrieve some prominent elements on the page for testing.
add_service_button = page.get_by_role(
"button", name=re.compile("Add a new service")
)
expect(add_service_button).to_be_visible()
@pytest.mark.skip(reason="Not authenticating test users.")
def test_add_new_service_workflow(end_to_end_authenticated_context):
# Prepare for adding a new service later in the test. # Prepare for adding a new service later in the test.
current_date_time = datetime.datetime.now() current_date_time = datetime.datetime.now()
new_service_name = "E2E Federal Test Service {now} - {browser_type}".format( new_service_name = "E2E Federal Test Service {now} - {browser_type}".format(
now=current_date_time.strftime("%m/%d/%Y %H:%M:%S"), now=current_date_time.strftime("%m/%d/%Y %H:%M:%S"),
browser_type=end_to_end_authenticated_context.browser.browser_type.name, browser_type=end_to_end_context.browser.browser_type.name,
) )
# Open a new page and go to the staging site.
page = end_to_end_authenticated_context.new_page()
accounts_uri = "{}accounts".format(os.getenv("NOTIFY_E2E_TEST_URI")) accounts_uri = "{}accounts".format(os.getenv("NOTIFY_E2E_TEST_URI"))
page.goto(accounts_uri) page.goto(accounts_uri)
@@ -100,7 +95,11 @@ def test_add_new_service_workflow(end_to_end_authenticated_context):
# Fill in the form. # Fill in the form.
service_name_input.fill(new_service_name) service_name_input.fill(new_service_name)
federal_radio_button.click() expect(federal_radio_button).to_be_enabled()
# Trying to click directly on the radio button resulted in a "not in viewport error" and this is the
# suggested workaround. Googling, the reason seems to be that there might be some (invisible?) css positioned
# above the radio button itself.
page.click("text='Federal government'")
# Click on add service. # Click on add service.
add_service_button.click() add_service_button.click()
@@ -112,3 +111,22 @@ def test_add_new_service_workflow(end_to_end_authenticated_context):
service_heading = page.get_by_text(new_service_name) service_heading = page.get_by_text(new_service_name)
expect(service_heading).to_be_visible() expect(service_heading).to_be_visible()
expect(page).to_have_title(re.compile(new_service_name)) expect(page).to_have_title(re.compile(new_service_name))
page.click("text='Settings'")
# Check to make sure that we've arrived at the next page.
page.wait_for_load_state("domcontentloaded")
page.click("text='Delete this service'")
# Check to make sure that we've arrived at the next page.
page.wait_for_load_state("domcontentloaded")
page.click("text='Yes, delete'")
# Check to make sure that we've arrived at the next page.
page.wait_for_load_state("domcontentloaded")
# Check to make sure that we've arrived at the next page.
# Check the page title exists and matches what we expect.
expect(page).to_have_title(re.compile("Choose service"))
+118 -117
View File
@@ -1,13 +1,12 @@
import os import os
import re import re
import pytest
from playwright.sync_api import expect from playwright.sync_api import expect
def test_landing_page(end_to_end_context): def test_landing_page(end_to_end_context):
# Open a new page and go to the staging site. # Open a new page and go to the staging site.
page = end_to_end_context.new_page() page = end_to_end_context.browser.new_page()
page.goto(os.getenv("NOTIFY_E2E_TEST_URI")) page.goto(os.getenv("NOTIFY_E2E_TEST_URI"))
# Check to make sure that we've arrived at the next page. # Check to make sure that we've arrived at the next page.
@@ -49,118 +48,120 @@ def test_landing_page(end_to_end_context):
).to_be_visible() ).to_be_visible()
@pytest.mark.skip(reason="Not authenticating test users.") # def test_sign_in_and_mfa_pages(end_to_end_context):
def test_sign_in_and_mfa_pages(end_to_end_context): # # Open a new page and go to the staging site.
# Open a new page and go to the staging site. # page = end_to_end_context.new_page()
page = end_to_end_context.new_page() # page.goto(os.getenv("NOTIFY_E2E_TEST_URI"))
page.goto(os.getenv("NOTIFY_E2E_TEST_URI")) # print(f"test_sign_in_and_mfa_pages initial {page}")
#
sign_in_button = page.get_by_role("link", name="Sign in") # sign_in_button = page.get_by_role("link", name="Sign in")
#
# Test trying to sign in. # # Test trying to sign in.
sign_in_button.click() # sign_in_button.click()
#
# Check to make sure that we've arrived at the next page. # # Check to make sure that we've arrived at the next page.
page.wait_for_load_state("domcontentloaded") # page.wait_for_load_state("domcontentloaded")
#
# Check the page title exists and matches what we expect. # # Check the page title exists and matches what we expect.
expect(page).to_have_title(re.compile("Sign in")) # expect(page).to_have_title(re.compile("Sign in"))
#
# Check for the sign in heading. # # Check for the sign in heading.
sign_in_heading = page.get_by_role("heading", name="Sign in") # sign_in_heading = page.get_by_role("heading", name="Sign in")
expect(sign_in_heading).to_be_visible() # expect(sign_in_heading).to_be_visible()
#
# Check for the sign in form elements. # # Check for the sign in form elements.
# NOTE: Playwright cannot find input elements by role and recommends using # # NOTE: Playwright cannot find input elements by role and recommends using
# get_by_label() instead; however, hidden form elements do not have # # get_by_label() instead; however, hidden form elements do not have
# labels associated with them, hence the XPath! # # labels associated with them, hence the XPath!
# See https://playwright.dev/python/docs/api/class-page#page-get-by-label # # See https://playwright.dev/python/docs/api/class-page#page-get-by-label
# and https://playwright.dev/python/docs/locators#locate-by-css-or-xpath # # and https://playwright.dev/python/docs/locators#locate-by-css-or-xpath
# for more information. # # for more information.
email_address_input = page.get_by_label("Email address") # email_address_input = page.get_by_label("Email address")
password_input = page.get_by_label("Password") # password_input = page.get_by_label("Password")
csrf_token = page.locator('xpath=//input[@name="csrf_token"]') # csrf_token = page.locator('xpath=//input[@name="csrf_token"]')
continue_button = page.get_by_role("button", name=re.compile("Continue")) # continue_button = page.get_by_role("button", name=re.compile("Continue"))
forgot_password_link = page.get_by_role("link", name="Forgot your password?") # forgot_password_link = page.get_by_role("link", name="Forgot your password?")
#
# Make sure form elements are visible and not visible as expected. # # Make sure form elements are visible and not visible as expected.
expect(email_address_input).to_be_visible() # expect(email_address_input).to_be_visible()
expect(password_input).to_be_visible() # expect(password_input).to_be_visible()
expect(continue_button).to_be_visible() # expect(continue_button).to_be_visible()
expect(forgot_password_link).to_be_visible() # expect(forgot_password_link).to_be_visible()
#
expect(csrf_token).to_be_hidden() # expect(csrf_token).to_be_hidden()
#
# Make sure form elements are configured correctly with the right # # Make sure form elements are configured correctly with the right
# attributes. # # attributes.
expect(email_address_input).to_have_attribute("type", "email") # expect(email_address_input).to_have_attribute("type", "email")
expect(password_input).to_have_attribute("type", "password") # expect(password_input).to_have_attribute("type", "password")
expect(csrf_token).to_have_attribute("type", "hidden") # expect(csrf_token).to_have_attribute("type", "hidden")
expect(continue_button).to_have_attribute("type", "submit") # expect(continue_button).to_have_attribute("type", "submit")
expect(forgot_password_link).to_have_attribute("href", "/forgot-password") # expect(forgot_password_link).to_have_attribute("href", "/forgot-password")
#
# Sign in to the site. # # Sign in to the site.
email_address_input.fill(os.getenv("NOTIFY_E2E_TEST_EMAIL")) # email_address_input.fill(os.getenv("NOTIFY_E2E_TEST_EMAIL"))
password_input.fill(os.getenv("NOTIFY_E2E_TEST_PASSWORD")) # password_input.fill(os.getenv("NOTIFY_E2E_TEST_PASSWORD"))
continue_button.click() # print(f"email and password {os.getenv('NOTIFY_E2E_TEST_EMAIL')} {os.getenv('NOTIFY_E2E_TEST_PASSWORD')}")
# continue_button.click()
# Wait for the next page to fully load. #
page.wait_for_load_state("domcontentloaded") # # Wait for the next page to fully load.
# page.wait_for_load_state("domcontentloaded")
# Check the page title exists and matches what we expect. #
expect(page).to_have_title(re.compile("Check your phone")) # # Check the page title exists and matches what we expect.
# print(f"test_sign_in_and_mfa_pages finally is {page}")
# Check for the sign in heading. # expect(page).to_have_title(re.compile("Check your phone"))
sign_in_heading = page.get_by_role("heading", name="Check your phone") #
expect(sign_in_heading).to_be_visible() # # Check for the sign in heading.
# sign_in_heading = page.get_by_role("heading", name="Check your phone")
# Check for the sign in form elements. # expect(sign_in_heading).to_be_visible()
# NOTE: Playwright cannot find input elements by role and recommends using #
# get_by_label() instead; however, hidden form elements do not have # # Check for the sign in form elements.
# labels associated with them, hence the XPath! # # NOTE: Playwright cannot find input elements by role and recommends using
# See https://playwright.dev/python/docs/api/class-page#page-get-by-label # # get_by_label() instead; however, hidden form elements do not have
# and https://playwright.dev/python/docs/locators#locate-by-css-or-xpath # # labels associated with them, hence the XPath!
# for more information. # # See https://playwright.dev/python/docs/api/class-page#page-get-by-label
mfa_input = page.get_by_label("Text message code") # # and https://playwright.dev/python/docs/locators#locate-by-css-or-xpath
csrf_token = page.locator('xpath=//input[@name="csrf_token"]') # # for more information.
continue_button = page.get_by_role("button", name=re.compile("Continue")) # mfa_input = page.get_by_label("Text message code")
not_received_message_link = page.get_by_role( # csrf_token = page.locator('xpath=//input[@name="csrf_token"]')
"link", name="Not received a text message?" # continue_button = page.get_by_role("button", name=re.compile("Continue"))
) # not_received_message_link = page.get_by_role(
# "link", name="Not received a text message?"
# Make sure form elements are visible and not visible as expected. # )
expect(mfa_input).to_be_visible() #
expect(continue_button).to_be_visible() # # Make sure form elements are visible and not visible as expected.
expect(not_received_message_link).to_be_visible() # expect(mfa_input).to_be_visible()
# expect(continue_button).to_be_visible()
expect(csrf_token).to_be_hidden() # expect(not_received_message_link).to_be_visible()
#
# Make sure form elements are configured correctly with the right # expect(csrf_token).to_be_hidden()
# attributes. #
expect(mfa_input).to_have_attribute("type", "tel") # # Make sure form elements are configured correctly with the right
expect(mfa_input).to_have_attribute("pattern", "[0-9]*") # # attributes.
expect(csrf_token).to_have_attribute("type", "hidden") # expect(mfa_input).to_have_attribute("type", "tel")
expect(continue_button).to_have_attribute("type", "submit") # expect(mfa_input).to_have_attribute("pattern", "[0-9]*")
expect(not_received_message_link).to_have_attribute("href", "/text-not-received") # expect(csrf_token).to_have_attribute("type", "hidden")
# expect(continue_button).to_have_attribute("type", "submit")
# Enter MFA code and continue. # expect(not_received_message_link).to_have_attribute("href", "/text-not-received")
# TODO: Revisit this at a later point in time. #
# totp = pyotp.TOTP( # # Enter MFA code and continue.
# os.getenv('MFA_TOTP_SECRET'), # # TODO: Revisit this at a later point in time.
# digits=int(os.getenv('MFA_TOTP_LENGTH')) # # totp = pyotp.TOTP(
# ) # # os.getenv('MFA_TOTP_SECRET'),
# # digits=int(os.getenv('MFA_TOTP_LENGTH'))
# mfa_input.fill('totp.now()') # # )
# continue_button.click() #
# # mfa_input.fill('totp.now()')
# # Check to make sure that we've arrived at the next page. # # continue_button.click()
# page.wait_for_load_state('domcontentloaded') #
# # # Check to make sure that we've arrived at the next page.
# # Check that no MFA code error happened. # # page.wait_for_load_state('domcontentloaded')
# code_not_found_error = page.get_by_text('Code not found') #
# expect(code_not_found_error).to_have_count(0) # # # Check that no MFA code error happened.
# # code_not_found_error = page.get_by_text('Code not found')
# # Check the page title exists and matches what we expect. # # expect(code_not_found_error).to_have_count(0)
# # This could be either the Dashboard of a service if there is only #
# # one, or choosing a service if there are multiple. # # # Check the page title exists and matches what we expect.
# expect(page).to_have_title(re.compile('Dashboard|Choose service')) # # # This could be either the Dashboard of a service if there is only
# # # one, or choosing a service if there are multiple.
# # expect(page).to_have_title(re.compile('Dashboard|Choose service'))