mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-10 10:03:21 -04:00
Merge branch 'main' of https://github.com/GSA/notifications-admin into 1216-login-gov-pre-sign-in-page
This commit is contained in:
115
.github/workflows/checks.yml
vendored
115
.github/workflows/checks.yml
vendored
@@ -7,12 +7,12 @@ permissions:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
NOTIFY_ENVIRONMENT: test
|
NOTIFY_ENVIRONMENT: test
|
||||||
NEW_RELIC_CONFIG_FILE: newrelic.ini
|
|
||||||
NEW_RELIC_ENVIRONMENT: test
|
|
||||||
FLASK_APP: application.py
|
FLASK_APP: application.py
|
||||||
WERKZEUG_DEBUG_PIN: off
|
WERKZEUG_DEBUG_PIN: off
|
||||||
REDIS_ENABLED: 0
|
REDIS_ENABLED: 0
|
||||||
NODE_VERSION: 16.15.1
|
NODE_VERSION: 16.15.1
|
||||||
|
AWS_US_TOLL_FREE_NUMBER: "+18556438890"
|
||||||
|
ADMIN_BASE_URL: http://localhost:6012
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -44,29 +44,97 @@ jobs:
|
|||||||
run: npm test
|
run: npm test
|
||||||
- name: Run py tests with coverage
|
- name: Run py tests with coverage
|
||||||
run: poetry run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10 --ignore=tests/end_to_end tests/
|
run: poetry run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10 --ignore=tests/end_to_end tests/
|
||||||
# - name: Run E2E tests
|
|
||||||
# run: poetry run pytest -v --browser chromium --browser firefox --browser webkit tests/end_to_end
|
|
||||||
# env:
|
|
||||||
# NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
|
|
||||||
# NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
|
||||||
# NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
|
||||||
# NOTIFY_E2E_TEST_URI: ${{ secrets.NOTIFY_E2E_TEST_URI }}
|
|
||||||
- name: Check coverage threshold
|
- name: Check coverage threshold
|
||||||
run: poetry run coverage report --fail-under=90
|
run: poetry run coverage report --fail-under=90
|
||||||
# - name: Health check
|
|
||||||
# run: |
|
end-to-end-tests:
|
||||||
# response=$(curl -url ${{secrets.NOTIFY_E2E_TEST_URI}}_status)
|
permissions:
|
||||||
# if grep -q "ok" <<< "$response"; then
|
checks: write
|
||||||
# echo "Health check passed"
|
pull-requests: write
|
||||||
# else
|
contents: write
|
||||||
# echo "Health check failed"
|
runs-on: ubuntu-latest
|
||||||
# exit 1
|
services:
|
||||||
# fi
|
postgres:
|
||||||
# env:
|
image: postgres
|
||||||
# NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
|
env:
|
||||||
# NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
POSTGRES_USER: user
|
||||||
# NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
POSTGRES_PASSWORD: password
|
||||||
# NOTIFY_E2E_TEST_URI: ${{ secrets.NOTIFY_E2E_TEST_URI }}
|
POSTGRES_DB: test_notification_api
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
ports:
|
||||||
|
# Maps tcp port 5432 on service container to the host
|
||||||
|
- 5432:5432
|
||||||
|
redis:
|
||||||
|
image: redis
|
||||||
|
options: >-
|
||||||
|
--health-cmd "redis-cli ping"
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
ports:
|
||||||
|
# Maps tcp port 6379 on service container to the host
|
||||||
|
- 6379:6379
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/setup-project
|
||||||
|
- uses: jwalton/gh-find-current-pr@v1
|
||||||
|
id: findPr
|
||||||
|
- name: Clone API
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: GSA/notifications-api
|
||||||
|
path: 'notifications-api'
|
||||||
|
- name: Install API dependencies
|
||||||
|
working-directory: 'notifications-api'
|
||||||
|
run: make bootstrap
|
||||||
|
env:
|
||||||
|
DATABASE_URL: postgresql://user:password@localhost:5432/test_notification_api
|
||||||
|
SQLALCHEMY_DATABASE_TEST_URI: postgresql://user:password@localhost:5432/test_notification_api
|
||||||
|
REDIS_URL: redis://localhost:6379
|
||||||
|
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
||||||
|
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
||||||
|
NOTIFY_ENVIRONMENT: development
|
||||||
|
- name: Run API server
|
||||||
|
working-directory: 'notifications-api'
|
||||||
|
run: make run-procfile &
|
||||||
|
env:
|
||||||
|
DATABASE_URL: postgresql://user:password@localhost:5432/test_notification_api
|
||||||
|
SQLALCHEMY_DATABASE_TEST_URI: postgresql://user:password@localhost:5432/test_notification_api
|
||||||
|
REDIS_URL: redis://localhost:6379
|
||||||
|
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
||||||
|
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
||||||
|
NOTIFY_ENVIRONMENT: development
|
||||||
|
- name: Run Admin server
|
||||||
|
run: make run-flask &
|
||||||
|
env:
|
||||||
|
# API_HOST_NAME: https://notify-api-staging.app.cloud.gov
|
||||||
|
API_HOST_NAME: http://localhost:6011
|
||||||
|
DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }}
|
||||||
|
SECRET_KEY: ${{ secrets.SECRET_KEY }}
|
||||||
|
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
|
||||||
|
ADMIN_CLIENT_USERNAME: notify-admin
|
||||||
|
NOTIFY_ENVIRONMENT: e2etest
|
||||||
|
NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
|
||||||
|
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
||||||
|
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
||||||
|
NOTIFY_E2E_TEST_URI: http://localhost:6012
|
||||||
|
- name: Run E2E tests
|
||||||
|
# Run the E2E tests against the code found in this PR.
|
||||||
|
# run: poetry run pytest -v --browser chromium --browser firefox --browser webkit tests/end_to_end
|
||||||
|
# --browser webkit doesn't work at this time.
|
||||||
|
run: make e2e-test
|
||||||
|
# Debugging for now to troubleshoot a connectivity issue to the local servers
|
||||||
|
# run: curl --request GET --url "http://localhost:6012"
|
||||||
|
env:
|
||||||
|
NOTIFY_ENVIRONMENT: e2etest
|
||||||
|
NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
|
||||||
|
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
||||||
|
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
||||||
|
NOTIFY_E2E_TEST_URI: http://localhost:6012
|
||||||
|
|
||||||
validate-new-relic-config:
|
validate-new-relic-config:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -76,6 +144,7 @@ jobs:
|
|||||||
- uses: ./.github/actions/setup-project
|
- uses: ./.github/actions/setup-project
|
||||||
- name: Validate NewRelic config
|
- name: Validate NewRelic config
|
||||||
env:
|
env:
|
||||||
|
NEW_RELIC_CONFIG_FILE: newrelic.ini
|
||||||
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
|
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
|
||||||
# Need to set a NEW_RELIC_ENVIRONMENT with monitor_mode: true
|
# Need to set a NEW_RELIC_ENVIRONMENT with monitor_mode: true
|
||||||
NEW_RELIC_ENVIRONMENT: staging
|
NEW_RELIC_ENVIRONMENT: staging
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -80,8 +80,8 @@ dead-code:
|
|||||||
|
|
||||||
.PHONY: e2e-test
|
.PHONY: e2e-test
|
||||||
e2e-test: export NEW_RELIC_ENVIRONMENT=test
|
e2e-test: export NEW_RELIC_ENVIRONMENT=test
|
||||||
e2e-test: ## Run end-to-end integration tests
|
e2e-test: ## Run end-to-end integration tests; note that --browser webkit isn't currently working
|
||||||
poetry run pytest -v --browser chromium --browser firefox --browser webkit tests/end_to_end
|
poetry run pytest -v --browser chromium --browser firefox tests/end_to_end
|
||||||
|
|
||||||
.PHONY: js-lint
|
.PHONY: js-lint
|
||||||
js-lint: ## Run javascript linting scanners
|
js-lint: ## Run javascript linting scanners
|
||||||
|
|||||||
@@ -81,8 +81,7 @@ class Config(object):
|
|||||||
"IBAN": "GB33BUKB20201555555555",
|
"IBAN": "GB33BUKB20201555555555",
|
||||||
"swift": "ABCDEF12",
|
"swift": "ABCDEF12",
|
||||||
"notify_billing_email_addresses": [
|
"notify_billing_email_addresses": [
|
||||||
"generic@digital.cabinet-office.gov.uk",
|
"tts-benefits-studio@gsa.gov",
|
||||||
"first.last@digital.cabinet-office.gov.uk",
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +151,34 @@ class Staging(Production):
|
|||||||
HEADER_COLOUR = "#00ff00" # $green
|
HEADER_COLOUR = "#00ff00" # $green
|
||||||
|
|
||||||
|
|
||||||
|
class E2ETest(Staging):
|
||||||
|
"""
|
||||||
|
An environment config that is intended to operate as if it were in the
|
||||||
|
staging environment but with the configuration of the development and test
|
||||||
|
environments so the E2E tests work.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Borrowed from development environment
|
||||||
|
DEBUG = True
|
||||||
|
SESSION_COOKIE_SECURE = False
|
||||||
|
SESSION_PROTECTION = None
|
||||||
|
HTTP_PROTOCOL = "http"
|
||||||
|
ASSET_DOMAIN = ""
|
||||||
|
ASSET_PATH = "/static/"
|
||||||
|
|
||||||
|
# Borrowed from test environment
|
||||||
|
TESTING = True
|
||||||
|
WTF_CSRF_ENABLED = False
|
||||||
|
|
||||||
|
# buckets - mirror staging
|
||||||
|
CSV_UPLOAD_BUCKET = cloud_config.s3_credentials(
|
||||||
|
"notify-api-csv-upload-bucket-staging"
|
||||||
|
)
|
||||||
|
LOGO_UPLOAD_BUCKET = cloud_config.s3_credentials(
|
||||||
|
"notify-admin-logo-upload-bucket-staging"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Demo(Staging):
|
class Demo(Staging):
|
||||||
HEADER_COLOUR = "#6F72AF" # $mauve
|
HEADER_COLOUR = "#6F72AF" # $mauve
|
||||||
|
|
||||||
@@ -173,6 +200,7 @@ class Scanning(Production):
|
|||||||
configs = {
|
configs = {
|
||||||
"development": Development,
|
"development": Development,
|
||||||
"test": Test,
|
"test": Test,
|
||||||
|
"e2etest": E2ETest,
|
||||||
"scanning": Scanning,
|
"scanning": Scanning,
|
||||||
"staging": Staging,
|
"staging": Staging,
|
||||||
"demo": Demo,
|
"demo": Demo,
|
||||||
|
|||||||
6
poetry.lock
generated
6
poetry.lock
generated
@@ -1672,12 +1672,12 @@ setuptools = "*"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notifications-python-client"
|
name = "notifications-python-client"
|
||||||
version = "8.2.0"
|
version = "9.0.0"
|
||||||
description = "Python API client for GOV.UK Notify."
|
description = "Python API client for GOV.UK Notify."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "notifications_python_client-8.2.0-py3-none-any.whl", hash = "sha256:8cd8bd01ae603a972a5413c430ca42b16f6481f37d98406c3e9b68c1c192f59e"},
|
{file = "notifications_python_client-9.0.0-py3-none-any.whl", hash = "sha256:664a5b5da2aa1a00efa8106bfa4855db04da95d79586e5edfb0411637d20d2d9"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -3075,4 +3075,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,<3.12"
|
python-versions = ">=3.9,<3.12"
|
||||||
content-hash = "1021999aefa9b1f83453706c25c87226b61da5d07b58bf2593dbd97eb5db844c"
|
content-hash = "30e3f9f8e1a094e67cc780b496641dfbe33e639cefccf35ee3b1553c9a683b71"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ humanize = "~=4.9"
|
|||||||
itsdangerous = "~=2.1"
|
itsdangerous = "~=2.1"
|
||||||
jinja2 = "~=3.1"
|
jinja2 = "~=3.1"
|
||||||
newrelic = "*"
|
newrelic = "*"
|
||||||
notifications-python-client = "==8.2.0"
|
notifications-python-client = "==9.0.0"
|
||||||
notifications-utils = {git = "https://github.com/GSA/notifications-utils.git"}
|
notifications-utils = {git = "https://github.com/GSA/notifications-utils.git"}
|
||||||
pyexcel = "==0.7.0"
|
pyexcel = "==0.7.0"
|
||||||
pyexcel-io = "==0.6.6"
|
pyexcel-io = "==0.6.6"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
from unittest.mock import Mock, PropertyMock
|
from unittest.mock import Mock, PropertyMock
|
||||||
@@ -529,7 +528,7 @@ def mock_update_service(mocker):
|
|||||||
"sms_sender",
|
"sms_sender",
|
||||||
"permissions",
|
"permissions",
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
return {"data": service}
|
return {"data": service}
|
||||||
|
|
||||||
@@ -2353,7 +2352,7 @@ def client_request(logged_in_client, mocker, service_one): # noqa (C901 too com
|
|||||||
_test_page_title=True,
|
_test_page_title=True,
|
||||||
_test_for_elements_without_class=True,
|
_test_for_elements_without_class=True,
|
||||||
_optional_args="",
|
_optional_args="",
|
||||||
**endpoint_kwargs
|
**endpoint_kwargs,
|
||||||
):
|
):
|
||||||
return ClientRequest.get_url(
|
return ClientRequest.get_url(
|
||||||
url_for(endpoint, **(endpoint_kwargs or {})) + _optional_args,
|
url_for(endpoint, **(endpoint_kwargs or {})) + _optional_args,
|
||||||
@@ -2372,7 +2371,7 @@ def client_request(logged_in_client, mocker, service_one): # noqa (C901 too com
|
|||||||
_expected_redirect=None,
|
_expected_redirect=None,
|
||||||
_test_page_title=True,
|
_test_page_title=True,
|
||||||
_test_for_elements_without_class=True,
|
_test_for_elements_without_class=True,
|
||||||
**endpoint_kwargs
|
**endpoint_kwargs,
|
||||||
):
|
):
|
||||||
resp = logged_in_client.get(
|
resp = logged_in_client.get(
|
||||||
url,
|
url,
|
||||||
@@ -2414,7 +2413,7 @@ def client_request(logged_in_client, mocker, service_one): # noqa (C901 too com
|
|||||||
_follow_redirects=False,
|
_follow_redirects=False,
|
||||||
_expected_redirect=None,
|
_expected_redirect=None,
|
||||||
_content_type=None,
|
_content_type=None,
|
||||||
**endpoint_kwargs
|
**endpoint_kwargs,
|
||||||
):
|
):
|
||||||
return ClientRequest.post_url(
|
return ClientRequest.post_url(
|
||||||
url_for(endpoint, **(endpoint_kwargs or {})),
|
url_for(endpoint, **(endpoint_kwargs or {})),
|
||||||
@@ -2473,7 +2472,7 @@ def client_request(logged_in_client, mocker, service_one): # noqa (C901 too com
|
|||||||
_expected_status=302,
|
_expected_status=302,
|
||||||
_optional_args="",
|
_optional_args="",
|
||||||
_content_type=None,
|
_content_type=None,
|
||||||
**endpoint_kwargs
|
**endpoint_kwargs,
|
||||||
):
|
):
|
||||||
return ClientRequest.post_response_from_url(
|
return ClientRequest.post_response_from_url(
|
||||||
url_for(endpoint, **(endpoint_kwargs or {})) + _optional_args,
|
url_for(endpoint, **(endpoint_kwargs or {})) + _optional_args,
|
||||||
@@ -3519,89 +3518,6 @@ def mock_get_invited_org_user_by_id(mocker, sample_org_invite):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def login_for_end_to_end_testing(browser):
|
|
||||||
# Open a new page and go to the staging site.
|
|
||||||
context = browser.new_context()
|
|
||||||
page = context.new_page()
|
|
||||||
page.goto(os.getenv("NOTIFY_E2E_TEST_URI"))
|
|
||||||
|
|
||||||
sign_in_button = page.get_by_role("link", name="Sign in")
|
|
||||||
|
|
||||||
# Test trying to sign in.
|
|
||||||
sign_in_button.click()
|
|
||||||
|
|
||||||
# Wait for the next page to fully load.
|
|
||||||
page.wait_for_load_state("domcontentloaded")
|
|
||||||
|
|
||||||
# Check for the sign in form elements.
|
|
||||||
# NOTE: Playwright cannot find input elements by role and recommends using
|
|
||||||
# get_by_label() instead; however, hidden form elements do not have
|
|
||||||
# labels associated with them, hence the XPath!
|
|
||||||
# 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
|
|
||||||
# for more information.
|
|
||||||
email_address_input = page.get_by_label("Email address")
|
|
||||||
password_input = page.get_by_label("Password")
|
|
||||||
continue_button = page.get_by_role("button", name=re.compile("Continue"))
|
|
||||||
|
|
||||||
# Sign in to the site.
|
|
||||||
email_address_input.fill(os.getenv("NOTIFY_E2E_TEST_EMAIL"))
|
|
||||||
password_input.fill(os.getenv("NOTIFY_E2E_TEST_PASSWORD"))
|
|
||||||
continue_button.click()
|
|
||||||
|
|
||||||
# Wait for the next page to fully load.
|
|
||||||
page.wait_for_load_state("domcontentloaded")
|
|
||||||
|
|
||||||
# Check for the sign in form elements.
|
|
||||||
# NOTE: Playwright cannot find input elements by role and recommends using
|
|
||||||
# get_by_label() instead; however, hidden form elements do not have
|
|
||||||
# labels associated with them, hence the XPath!
|
|
||||||
# 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
|
|
||||||
# for more information.
|
|
||||||
# mfa_input = page.get_by_label('Text message code')
|
|
||||||
# continue_button = page.get_by_role('button', name=re.compile('Continue'))
|
|
||||||
|
|
||||||
# # Enter MFA code and continue.
|
|
||||||
# TODO: Revisit this at a later point in time.
|
|
||||||
# totp = pyotp.TOTP(
|
|
||||||
# os.getenv('MFA_TOTP_SECRET'),
|
|
||||||
# digits=int(os.getenv('MFA_TOTP_LENGTH'))
|
|
||||||
# )
|
|
||||||
|
|
||||||
# mfa_input.fill(totp.now())
|
|
||||||
# continue_button.click()
|
|
||||||
|
|
||||||
# page.wait_for_load_state('domcontentloaded')
|
|
||||||
|
|
||||||
# # Save storage state into the file.
|
|
||||||
# auth_state_path = os.path.join(
|
|
||||||
# os.getenv('NOTIFY_E2E_AUTH_STATE_PATH'),
|
|
||||||
# 'state.json'
|
|
||||||
# )
|
|
||||||
# context.storage_state(path=auth_state_path)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def end_to_end_context(browser):
|
|
||||||
context = browser.new_context()
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def end_to_end_authenticated_context(browser):
|
|
||||||
# Create and load a previously authenticated context for Playwright E2E
|
|
||||||
# tests.
|
|
||||||
login_for_end_to_end_testing(browser)
|
|
||||||
|
|
||||||
auth_state_path = os.path.join(
|
|
||||||
os.getenv("NOTIFY_E2E_AUTH_STATE_PATH"), "state.json"
|
|
||||||
)
|
|
||||||
context = browser.new_context(storage_state=auth_state_path)
|
|
||||||
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def fake_markdown_file():
|
def fake_markdown_file():
|
||||||
input = "#Test"
|
input = "#Test"
|
||||||
|
|||||||
0
tests/end_to_end/__init__.py
Normal file
0
tests/end_to_end/__init__.py
Normal file
105
tests/end_to_end/conftest.py
Normal file
105
tests/end_to_end/conftest.py
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
E2E_TEST_URI = os.getenv("NOTIFY_E2E_TEST_URI")
|
||||||
|
|
||||||
|
|
||||||
|
def login_for_end_to_end_testing(browser):
|
||||||
|
# Open a new page and go to the staging site.
|
||||||
|
context = browser.new_context()
|
||||||
|
page = context.new_page()
|
||||||
|
page.goto(f"{E2E_TEST_URI}/")
|
||||||
|
|
||||||
|
sign_in_button = page.get_by_role("link", name="Sign in")
|
||||||
|
|
||||||
|
# Test trying to sign in.
|
||||||
|
sign_in_button.click()
|
||||||
|
|
||||||
|
# Wait for the next page to fully load.
|
||||||
|
page.wait_for_load_state("domcontentloaded")
|
||||||
|
|
||||||
|
# Check for the sign in form elements.
|
||||||
|
# NOTE: Playwright cannot find input elements by role and recommends using
|
||||||
|
# get_by_label() instead; however, hidden form elements do not have
|
||||||
|
# labels associated with them, hence the XPath!
|
||||||
|
# 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
|
||||||
|
# for more information.
|
||||||
|
email_address_input = page.get_by_label("Email address")
|
||||||
|
password_input = page.get_by_label("Password")
|
||||||
|
continue_button = page.get_by_role("button", name=re.compile("Continue"))
|
||||||
|
|
||||||
|
# Sign in to the site.
|
||||||
|
email_address_input.fill(os.getenv("NOTIFY_E2E_TEST_EMAIL"))
|
||||||
|
password_input.fill(os.getenv("NOTIFY_E2E_TEST_PASSWORD"))
|
||||||
|
continue_button.click()
|
||||||
|
|
||||||
|
# Wait for the next page to fully load.
|
||||||
|
page.wait_for_load_state("domcontentloaded")
|
||||||
|
|
||||||
|
# Check for the sign in form elements.
|
||||||
|
# NOTE: Playwright cannot find input elements by role and recommends using
|
||||||
|
# get_by_label() instead; however, hidden form elements do not have
|
||||||
|
# labels associated with them, hence the XPath!
|
||||||
|
# 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
|
||||||
|
# for more information.
|
||||||
|
# mfa_input = page.get_by_label('Text message code')
|
||||||
|
# continue_button = page.get_by_role('button', name=re.compile('Continue'))
|
||||||
|
|
||||||
|
# # Enter MFA code and continue.
|
||||||
|
# TODO: Revisit this at a later point in time.
|
||||||
|
# totp = pyotp.TOTP(
|
||||||
|
# os.getenv('MFA_TOTP_SECRET'),
|
||||||
|
# digits=int(os.getenv('MFA_TOTP_LENGTH'))
|
||||||
|
# )
|
||||||
|
|
||||||
|
# mfa_input.fill(totp.now())
|
||||||
|
# continue_button.click()
|
||||||
|
|
||||||
|
# page.wait_for_load_state('domcontentloaded')
|
||||||
|
|
||||||
|
# Save storage state into the file.
|
||||||
|
auth_state_path = os.path.join(
|
||||||
|
os.getenv("NOTIFY_E2E_AUTH_STATE_PATH"), "state.json"
|
||||||
|
)
|
||||||
|
context.storage_state(path=auth_state_path)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def end_to_end_context(browser):
|
||||||
|
context = browser.new_context()
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def end_to_end_authenticated_context(browser):
|
||||||
|
# Create and load a previously authenticated context for Playwright E2E
|
||||||
|
# tests.
|
||||||
|
# login_for_end_to_end_testing(browser)
|
||||||
|
|
||||||
|
auth_state_path = os.path.join(
|
||||||
|
os.getenv("NOTIFY_E2E_AUTH_STATE_PATH"), "state.json"
|
||||||
|
)
|
||||||
|
context = browser.new_context(storage_state=auth_state_path)
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def authenticated_page(end_to_end_context):
|
||||||
|
# Open a new page and go to the staging site.
|
||||||
|
page = end_to_end_context.new_page()
|
||||||
|
|
||||||
|
page.goto(f"{E2E_TEST_URI}/")
|
||||||
|
|
||||||
|
sign_in_button = page.get_by_role("link", name="Sign in")
|
||||||
|
|
||||||
|
# Sign in to the site - E2E test accounts are set to flow through.
|
||||||
|
sign_in_button.click()
|
||||||
|
|
||||||
|
# Wait for the next page to fully load.
|
||||||
|
page.wait_for_load_state("domcontentloaded")
|
||||||
|
return page
|
||||||
@@ -4,33 +4,13 @@ import re
|
|||||||
|
|
||||||
from playwright.sync_api import expect
|
from playwright.sync_api import expect
|
||||||
|
|
||||||
|
E2E_TEST_URI = os.getenv("NOTIFY_E2E_TEST_URI")
|
||||||
def _bypass_sign_in(end_to_end_context):
|
|
||||||
# Open a new page and go to the staging site.
|
|
||||||
page = end_to_end_context.new_page()
|
|
||||||
|
|
||||||
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")
|
|
||||||
return page
|
|
||||||
|
|
||||||
|
|
||||||
def test_add_new_service_workflow(end_to_end_context):
|
def test_add_new_service_workflow(authenticated_page, end_to_end_context):
|
||||||
# page = end_to_end_context.new_page()
|
page = authenticated_page
|
||||||
page = _bypass_sign_in(end_to_end_context)
|
page.goto(f"{E2E_TEST_URI}/")
|
||||||
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.
|
# Wait for the next page to fully load.
|
||||||
page.wait_for_load_state("domcontentloaded")
|
page.wait_for_load_state("domcontentloaded")
|
||||||
|
|
||||||
@@ -41,9 +21,7 @@ def test_add_new_service_workflow(end_to_end_context):
|
|||||||
browser_type=end_to_end_context.browser.browser_type.name,
|
browser_type=end_to_end_context.browser.browser_type.name,
|
||||||
)
|
)
|
||||||
|
|
||||||
accounts_uri = "{}accounts".format(os.getenv("NOTIFY_E2E_TEST_URI"))
|
page.goto(f"{E2E_TEST_URI}/accounts")
|
||||||
|
|
||||||
page.goto(accounts_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.
|
||||||
page.wait_for_load_state("domcontentloaded")
|
page.wait_for_load_state("domcontentloaded")
|
||||||
@@ -82,24 +60,13 @@ def test_add_new_service_workflow(end_to_end_context):
|
|||||||
|
|
||||||
# Retrieve some prominent elements on the page for testing.
|
# Retrieve some prominent elements on the page for testing.
|
||||||
service_name_input = page.locator('xpath=//input[@name="name"]')
|
service_name_input = page.locator('xpath=//input[@name="name"]')
|
||||||
federal_radio_button = page.locator('xpath=//input[@value="federal"]')
|
|
||||||
state_radio_button = page.locator('xpath=//input[@value="state"]')
|
|
||||||
other_radio_button = page.locator('xpath=//input[@value="other"]')
|
|
||||||
add_service_button = page.get_by_role("button", name=re.compile("Add service"))
|
add_service_button = page.get_by_role("button", name=re.compile("Add service"))
|
||||||
|
|
||||||
expect(service_name_input).to_be_visible()
|
expect(service_name_input).to_be_visible()
|
||||||
expect(federal_radio_button).to_be_visible()
|
|
||||||
expect(state_radio_button).to_be_visible()
|
|
||||||
expect(other_radio_button).to_be_visible()
|
|
||||||
expect(add_service_button).to_be_visible()
|
expect(add_service_button).to_be_visible()
|
||||||
|
|
||||||
# Fill in the form.
|
# Fill in the form.
|
||||||
service_name_input.fill(new_service_name)
|
service_name_input.fill(new_service_name)
|
||||||
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()
|
||||||
@@ -108,7 +75,7 @@ def test_add_new_service_workflow(end_to_end_context):
|
|||||||
page.wait_for_load_state("domcontentloaded")
|
page.wait_for_load_state("domcontentloaded")
|
||||||
|
|
||||||
# Check for the service name title and heading.
|
# Check for the service name title and heading.
|
||||||
service_heading = page.get_by_text(new_service_name)
|
service_heading = page.get_by_text(new_service_name, exact=True)
|
||||||
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))
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ import re
|
|||||||
|
|
||||||
from playwright.sync_api import expect
|
from playwright.sync_api import expect
|
||||||
|
|
||||||
|
E2E_TEST_URI = os.getenv("NOTIFY_E2E_TEST_URI")
|
||||||
|
|
||||||
|
|
||||||
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.browser.new_page()
|
page = end_to_end_context.browser.new_page()
|
||||||
page.goto(os.getenv("NOTIFY_E2E_TEST_URI"))
|
page.goto(f"{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.
|
||||||
page.wait_for_load_state("domcontentloaded")
|
page.wait_for_load_state("domcontentloaded")
|
||||||
@@ -17,7 +19,8 @@ def test_landing_page(end_to_end_context):
|
|||||||
|
|
||||||
# Retrieve some prominent elements on the page for testing.
|
# Retrieve some prominent elements on the page for testing.
|
||||||
main_header = page.get_by_role(
|
main_header = page.get_by_role(
|
||||||
"heading", name="Send text messages to your participants"
|
"heading",
|
||||||
|
name="Reach people where they are with government-powered text messages",
|
||||||
)
|
)
|
||||||
sign_in_button = page.get_by_role("link", name="Sign in")
|
sign_in_button = page.get_by_role("link", name="Sign in")
|
||||||
benefits_studio_email = page.get_by_role("link", name="tts-benefits-studio@gsa.gov")
|
benefits_studio_email = page.get_by_role("link", name="tts-benefits-studio@gsa.gov")
|
||||||
@@ -51,7 +54,7 @@ def test_landing_page(end_to_end_context):
|
|||||||
# 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(f"{E2E_TEST_URI}/")
|
||||||
# print(f"test_sign_in_and_mfa_pages initial {page}")
|
# 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")
|
||||||
|
|||||||
Reference in New Issue
Block a user