diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 99ef30c3b..53af0de2d 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -7,12 +7,12 @@ permissions: env: NOTIFY_ENVIRONMENT: test - NEW_RELIC_CONFIG_FILE: newrelic.ini - NEW_RELIC_ENVIRONMENT: test FLASK_APP: application.py WERKZEUG_DEBUG_PIN: off REDIS_ENABLED: 0 NODE_VERSION: 16.15.1 + AWS_US_TOLL_FREE_NUMBER: "+18556438890" + ADMIN_BASE_URL: http://localhost:6012 jobs: build: @@ -44,29 +44,97 @@ jobs: run: npm test - name: Run py tests with coverage 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 run: poetry run coverage report --fail-under=90 - # - name: Health check - # run: | - # response=$(curl -url ${{secrets.NOTIFY_E2E_TEST_URI}}_status) - # if grep -q "ok" <<< "$response"; then - # echo "Health check passed" - # else - # echo "Health check failed" - # exit 1 - # fi - # 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 }} + + end-to-end-tests: + permissions: + checks: write + pull-requests: write + contents: write + runs-on: ubuntu-latest + services: + postgres: + image: postgres + env: + POSTGRES_USER: user + POSTGRES_PASSWORD: password + 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: runs-on: ubuntu-latest @@ -76,6 +144,7 @@ jobs: - uses: ./.github/actions/setup-project - name: Validate NewRelic config env: + NEW_RELIC_CONFIG_FILE: newrelic.ini NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }} # Need to set a NEW_RELIC_ENVIRONMENT with monitor_mode: true NEW_RELIC_ENVIRONMENT: staging diff --git a/.github/workflows/deploy-demo.yml b/.github/workflows/deploy-demo.yml index e1d76775a..bf9757493 100644 --- a/.github/workflows/deploy-demo.yml +++ b/.github/workflows/deploy-demo.yml @@ -56,12 +56,12 @@ jobs: NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }} LOGIN_PEM: ${{ secrets.LOGIN_PEM }} LOGIN_DOT_GOV_CLIENT_ID: "urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov" - LOGIN_DOT_GOV_USER_INFO_URL: "https://idp.int.identitysandbox.gov/api/openid_connect/userinfo" - LOGIN_DOT_GOV_ACCESS_TOKEN_URL: "https://idp.int.identitysandbox.gov/api/openid_connect/token" - LOGIN_DOT_GOV_LOGOUT_URL: "https://idp.int.identitysandbox.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://notify-demo.app.cloud.gov/sign-out" - LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://idp.int.identitysandbox.gov/openid_connect/logout?" + LOGIN_DOT_GOV_USER_INFO_URL: "https://secure.login.gov/api/openid_connect/userinfo" + LOGIN_DOT_GOV_ACCESS_TOKEN_URL: "https://secure.login.gov/api/openid_connect/token" + LOGIN_DOT_GOV_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://notify-demo.app.cloud.gov/sign-out" + LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?" LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://notify-demo.app.cloud.gov/sign-out" - LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://idp.int.identitysandbox.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://notify-demo.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop" + LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://notify-demo.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop" with: cf_username: ${{ secrets.CLOUDGOV_USERNAME }} cf_password: ${{ secrets.CLOUDGOV_PASSWORD }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 767acae72..915994d58 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -62,12 +62,12 @@ jobs: NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }} LOGIN_PEM: ${{ secrets.LOGIN_PEM }} LOGIN_DOT_GOV_CLIENT_ID: "urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov" - LOGIN_DOT_GOV_USER_INFO_URL: "https://idp.int.identitysandbox.gov/api/openid_connect/userinfo" - LOGIN_DOT_GOV_ACCESS_TOKEN_URL: "https://idp.int.identitysandbox.gov/api/openid_connect/token" - LOGIN_DOT_GOV_LOGOUT_URL: "https://idp.int.identitysandbox.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://notify-staging.app.cloud.gov/sign-out" - LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://idp.int.identitysandbox.gov/openid_connect/logout?" + LOGIN_DOT_GOV_USER_INFO_URL: "https://secure.login.gov/api/openid_connect/userinfo" + LOGIN_DOT_GOV_ACCESS_TOKEN_URL: "https://secure.login.gov/api/openid_connect/token" + LOGIN_DOT_GOV_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://notify-staging.app.cloud.gov/sign-out" + LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?" LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://notify-staging.app.cloud.gov/sign-out" - LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://idp.int.identitysandbox.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop" + LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop" with: cf_username: ${{ secrets.CLOUDGOV_USERNAME }} cf_password: ${{ secrets.CLOUDGOV_PASSWORD }} diff --git a/Makefile b/Makefile index 8f950dbf6..aa9b404af 100644 --- a/Makefile +++ b/Makefile @@ -80,8 +80,8 @@ dead-code: .PHONY: e2e-test e2e-test: export NEW_RELIC_ENVIRONMENT=test -e2e-test: ## Run end-to-end integration tests - poetry run pytest -v --browser chromium --browser firefox --browser webkit tests/end_to_end +e2e-test: ## Run end-to-end integration tests; note that --browser webkit isn't currently working + poetry run pytest -v --browser chromium --browser firefox tests/end_to_end .PHONY: js-lint js-lint: ## Run javascript linting scanners diff --git a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss index addf9cc7a..1d55791df 100644 --- a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss +++ b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss @@ -403,8 +403,11 @@ td.table-empty-message { width: 5%; } th { - padding: 0.5rem 0.5rem; + padding: 0.5rem 1rem } + td { + padding: 0.5rem 1rem + } } #template-list { diff --git a/app/config.py b/app/config.py index f424076fa..58623f426 100644 --- a/app/config.py +++ b/app/config.py @@ -81,8 +81,7 @@ class Config(object): "IBAN": "GB33BUKB20201555555555", "swift": "ABCDEF12", "notify_billing_email_addresses": [ - "generic@digital.cabinet-office.gov.uk", - "first.last@digital.cabinet-office.gov.uk", + "tts-benefits-studio@gsa.gov", ], } @@ -152,6 +151,34 @@ class Staging(Production): 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): HEADER_COLOUR = "#6F72AF" # $mauve @@ -173,6 +200,7 @@ class Scanning(Production): configs = { "development": Development, "test": Test, + "e2etest": E2ETest, "scanning": Scanning, "staging": Staging, "demo": Demo, diff --git a/app/main/forms.py b/app/main/forms.py index c01ce8fcc..2e6655cc5 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -1781,12 +1781,16 @@ class TemplateAndFoldersSelectionForm(Form): None, [ # ('email', 'Email') if 'email' in available_template_types else None, - ("sms", "Start with a blank template") - if "sms" in available_template_types - else None, - ("copy-existing", "Copy an existing template") - if allow_adding_copy_of_template - else None, + ( + ("sms", "Start with a blank template") + if "sms" in available_template_types + else None + ), + ( + ("copy-existing", "Copy an existing template") + if allow_adding_copy_of_template + else None + ), ], ) ) diff --git a/app/main/views/api_keys.py b/app/main/views/api_keys.py index 4f8c3b29e..8cb28ba59 100644 --- a/app/main/views/api_keys.py +++ b/app/main/views/api_keys.py @@ -168,12 +168,14 @@ def api_callbacks(service_id): return render_template( "views/api/callbacks.html", - received_text_messages_callback=received_text_messages_callback["url"] - if received_text_messages_callback - else None, - delivery_status_callback=delivery_status_callback["url"] - if delivery_status_callback - else None, + received_text_messages_callback=( + received_text_messages_callback["url"] + if received_text_messages_callback + else None + ), + delivery_status_callback=( + delivery_status_callback["url"] if delivery_status_callback else None + ), ) @@ -262,9 +264,11 @@ def received_text_messages_callback(service_id): received_text_messages_callback = get_received_text_messages_callback() form = CallbackForm( - url=received_text_messages_callback.get("url") - if received_text_messages_callback - else "", + url=( + received_text_messages_callback.get("url") + if received_text_messages_callback + else "" + ), bearer_token=dummy_bearer_token if received_text_messages_callback else "", ) diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 24b3e0490..4d5aca14d 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -76,6 +76,7 @@ def service_dashboard(service_id): "notifications": aggregate_notifications_by_job.get(job["id"], []), } for job in job_response + if aggregate_notifications_by_job.get(job["id"], []) ] return render_template( "views/dashboard/dashboard.html", diff --git a/app/main/views/manage_users.py b/app/main/views/manage_users.py index 4ab20f363..1b80c659e 100644 --- a/app/main/views/manage_users.py +++ b/app/main/views/manage_users.py @@ -122,16 +122,18 @@ def edit_user_permissions(service_id, user_id): form = form_class.from_user( user, service_id, - folder_permissions=None - if user.platform_admin - else [ - f["id"] - for f in current_service.all_template_folders - if user.has_template_folder_permission(f) - ], - all_template_folders=None - if user.platform_admin - else current_service.all_template_folders, + folder_permissions=( + None + if user.platform_admin + else [ + f["id"] + for f in current_service.all_template_folders + if user.has_template_folder_permission(f) + ] + ), + all_template_folders=( + None if user.platform_admin else current_service.all_template_folders + ), ) if form.validate_on_submit(): diff --git a/app/main/views/performance.py b/app/main/views/performance.py index 13445967b..1d2a4d1bc 100644 --- a/app/main/views/performance.py +++ b/app/main/views/performance.py @@ -34,8 +34,8 @@ def performance(): stats["average_percentage_under_10_seconds"] = mean( [row["percentage_under_10_seconds"] for row in stats["processing_time"]] or [0] ) - stats[ - "count_of_live_services_and_organizations" - ] = status_api_client.get_count_of_live_services_and_organizations() + stats["count_of_live_services_and_organizations"] = ( + status_api_client.get_count_of_live_services_and_organizations() + ) return render_template("views/performance.html", **stats) diff --git a/app/main/views/send.py b/app/main/views/send.py index d45c1909f..10f74bb41 100644 --- a/app/main/views/send.py +++ b/app/main/views/send.py @@ -52,12 +52,14 @@ def get_example_csv_fields(column_headers, use_example_as_example, submitted_fie def get_example_csv_rows(template, use_example_as_example=True, submitted_fields=False): return { - "email": ["test@example.com"] - if use_example_as_example - else [current_user.email_address], - "sms": ["12223334444"] - if use_example_as_example - else [current_user.mobile_number], + "email": ( + ["test@example.com"] + if use_example_as_example + else [current_user.email_address] + ), + "sms": ( + ["12223334444"] if use_example_as_example else [current_user.mobile_number] + ), }[template.template_type] + get_example_csv_fields( ( placeholder @@ -511,12 +513,14 @@ def _check_messages(service_id, template_id, upload_id, preview_row): template=template, max_initial_rows_shown=50, max_errors_shown=50, - guestlist=itertools.chain.from_iterable( - [user.name, user.mobile_number, user.email_address] - for user in Users(service_id) - ) - if current_service.trial_mode - else None, + guestlist=( + itertools.chain.from_iterable( + [user.name, user.mobile_number, user.email_address] + for user in Users(service_id) + ) + if current_service.trial_mode + else None + ), remaining_messages=remaining_messages, allow_international_sms=current_service.has_permission("international_sms"), ) diff --git a/app/main/views/service_settings.py b/app/main/views/service_settings.py index 1cac1410c..628ac59e5 100644 --- a/app/main/views/service_settings.py +++ b/app/main/views/service_settings.py @@ -477,9 +477,11 @@ def service_edit_email_reply_to(service_id, reply_to_email_id): current_service.id, reply_to_email_id=reply_to_email_id, email_address=form.email_address.data, - is_default=True - if reply_to_email_address["is_default"] - else form.is_default.data, + is_default=( + True + if reply_to_email_address["is_default"] + else form.is_default.data + ), ) return redirect(url_for(".service_email_reply_to", service_id=service_id)) try: @@ -499,9 +501,11 @@ def service_edit_email_reply_to(service_id, reply_to_email_id): ".service_verify_reply_to_address", service_id=service_id, notification_id=notification_id, - is_default=True - if reply_to_email_address["is_default"] - else form.is_default.data, + is_default=( + True + if reply_to_email_address["is_default"] + else form.is_default.data + ), replace=reply_to_email_id, ) ) @@ -702,9 +706,11 @@ def service_edit_sms_sender(service_id, sms_sender_id): service_api_client.update_sms_sender( current_service.id, sms_sender_id=sms_sender_id, - sms_sender=sms_sender["sms_sender"] - if is_inbound_number - else form.sms_sender.data.replace("\r", ""), + sms_sender=( + sms_sender["sms_sender"] + if is_inbound_number + else form.sms_sender.data.replace("\r", "") + ), is_default=True if sms_sender["is_default"] else form.is_default.data, ) return redirect(url_for(".service_sms_senders", service_id=service_id)) diff --git a/app/main/views/sign_in.py b/app/main/views/sign_in.py index be313c59b..c6eaee87e 100644 --- a/app/main/views/sign_in.py +++ b/app/main/views/sign_in.py @@ -198,7 +198,7 @@ def sign_in(): form=form, again=bool(redirect_url), other_device=other_device, - login_gov_enabled=bool(notify_env in ["development", "staging"]), + login_gov_enabled=bool(notify_env in ["development", "staging", "demo"]), password_reset_url=password_reset_url, initial_signin_url=initial_signin_url, ) diff --git a/app/templates/components/table.html b/app/templates/components/table.html index 50eb73d9a..a76f2c6bd 100644 --- a/app/templates/components/table.html +++ b/app/templates/components/table.html @@ -171,8 +171,8 @@ {% endif %}
{{ notification.status|format_notification_status_as_time( - notification.created_at|format_datetime_short, - (notification.sent_at or notification.created_at)|format_datetime_short + notification.created_at|format_datetime_short_america, + (notification.sent_at or notification.created_at)|format_datetime_short_america ) }}
{% if displayed_on_single_line %}{% endif %} diff --git a/app/templates/views/guidance/index.html b/app/templates/views/guidance/index.html index e754ef245..b7d6ffa1a 100644 --- a/app/templates/views/guidance/index.html +++ b/app/templates/views/guidance/index.html @@ -41,7 +41,7 @@You can help your recipients identify your texts as legitimate by customizing your messages to clearly state who they are from. Consider using the program or benefit name that is most familiar to your recipients.
diff --git a/app/templates/views/message-status.html b/app/templates/views/message-status.html index 9f30f1170..79ea960fe 100644 --- a/app/templates/views/message-status.html +++ b/app/templates/views/message-status.html @@ -10,8 +10,8 @@Notify’s real-time dashboard lets you check the status of any message.
-For security, this information is only available for seven days after a message has been sent. You can download a report, including a list of sent messages, for your own records.
-This page describes the statuses you’ll see when you’re signed in to Notify.
+For security, this information is only available for seven days after a message has been sent. You can download a report, including a list of sent messages, for your own records.
+This page describes the statuses you'll see when you're signed in to Notify.
- - - - - {% endblock %} diff --git a/app/templates/views/support/index.html b/app/templates/views/support/index.html index d9d6abd53..dd5ad9c10 100644 --- a/app/templates/views/support/index.html +++ b/app/templates/views/support/index.html @@ -13,9 +13,9 @@Notify is designed to be easy to use.
If you have other questions, we are available at notify-support@gsa.gov.
diff --git a/deploy-config/egress_proxy/notify-admin-demo.allow.acl b/deploy-config/egress_proxy/notify-admin-demo.allow.acl index 4718f99e7..41bf3a365 100644 --- a/deploy-config/egress_proxy/notify-admin-demo.allow.acl +++ b/deploy-config/egress_proxy/notify-admin-demo.allow.acl @@ -1,2 +1,4 @@ gov-collector.newrelic.com egress-proxy-notify-admin-demo.apps.internal +idp.int.identitysandbox.gov +secure.login.gov diff --git a/deploy-config/egress_proxy/notify-admin-production.allow.acl b/deploy-config/egress_proxy/notify-admin-production.allow.acl index 2173a6b4b..8103b0915 100644 --- a/deploy-config/egress_proxy/notify-admin-production.allow.acl +++ b/deploy-config/egress_proxy/notify-admin-production.allow.acl @@ -1,2 +1,4 @@ gov-collector.newrelic.com egress-proxy-notify-admin-production.apps.internal +idp.int.identitysandbox.gov +secure.login.gov diff --git a/docs/end_to_end_tests.md b/docs/end_to_end_tests.md index 02eec2e8b..11758a16e 100644 --- a/docs/end_to_end_tests.md +++ b/docs/end_to_end_tests.md @@ -106,6 +106,104 @@ All of the E2E tests are found in the `tests/end_to_end` folder and are written as `pytest` scripts using [Playwright's Python Framework](https://playwright.dev/python/docs/writing-tests). +Inside the `tests/end_to_end` folder you'll see a `conftest.py` file, +which is similar to the one found in the root `tests` folder but is +specific to the E2E tests. + +There a few fixtures defined in here, but the two most important at this +time are these: + +- `end_to_end_context`: A Playwright context object needed to interact + with a browser instance. +- `authenticated_page`: A Playwright page object that has gone through + the sign in process the E2E user is authenticated. + +In short, if you're starting a test from scratch and testing pages that +do not require authentication, you'll start with the +`end_to_end_context` fixture and work from there. + +Any test that requires you to be authenticated, you'll start with the +`authenticated_page` object as that'll have taken care of getting +everything set for you and logged into the site with the E2E test user. + + +### Creating a new test file + +If you want to create a new test file to help organize tests (a great +idea!), it will be handy to import the Playwright `expect` and set the +base URL/URI for yourself, like this: + +```python +from playwright.sync_api import expect + +E2E_TEST_URI = os.getenv("NOTIFY_E2E_TEST_URI") +``` + +By importing Playwright's `expect` object for tests and setting +something like `E2E_TEST_URI` for yourself, it will make writing tests +much easier. + + +### Using the fixtures + +To use the `authenticated_page` or `end_to_end_context` fixtures, you +start by defining a test function and then passing in the fixture you +need as a positional argument. This works the same as the other +functions defined to create a test for pytest. + +For example, the test for the landing page starts with this: + +```python +def test_landing_page(end_to_end_context): + # Open a new page and go to the site. + page = end_to_end_context.browser.new_page() + page.goto(f"{E2E_TEST_URI}/") + + # Check to make sure that we've arrived at the next page. + page.wait_for_load_state("domcontentloaded") + ... +``` + +Note the passing in of the `end_to_end_context` fixture - there is no +need to import this or anything, just pass it into the function. pytest +takes care of everything else for you. + +The second line that defines a `page` variable is a convenience, since +you'll be referencing the page object a lot. This is recommended to +help keep tests readable while keeping fixture names descriptive. + +If you need to test an authenticate page, such as the accounts page, +use the `authenticated_page` fixture instead, like so: + +```python +def test_add_new_service_workflow(authenticated_page): + page = authenticated_page + ... +``` + +Again, it's helpful to assign the fixture to a `page` variable for easy +reference throughout the test. + +Lastly, if you need want access to the Playwright context object that is +used behind the page fixtures, you can reference it directly as well +using the `end_to_end_context` fixture: + +```python +def test_add_new_service_workflow(authenticated_page, end_to_end_context): + page = authenticated_page + + # Prepare for adding a new service later in the test. + current_date_time = datetime.datetime.now() + new_service_name = "E2E Federal Test Service {now} - {browser_type}".format( + now=current_date_time.strftime("%m/%d/%Y %H:%M:%S"), + browser_type=end_to_end_context.browser.browser_type.name, + ) + ... +``` + +In this example, I've used the context to get to the browser object +itself to get the name of the browser for test data. + ## Maintaining E2E Tests with GitHub diff --git a/poetry.lock b/poetry.lock index a7e09cf85..74a0b0ac6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1672,12 +1672,12 @@ setuptools = "*" [[package]] name = "notifications-python-client" -version = "8.2.0" +version = "9.0.0" description = "Python API client for GOV.UK Notify." optional = false python-versions = ">=3.7" 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] @@ -3075,4 +3075,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "1021999aefa9b1f83453706c25c87226b61da5d07b58bf2593dbd97eb5db844c" +content-hash = "30e3f9f8e1a094e67cc780b496641dfbe33e639cefccf35ee3b1553c9a683b71" diff --git a/pyproject.toml b/pyproject.toml index e1f2ae143..5aed7711a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ humanize = "~=4.9" itsdangerous = "~=2.1" jinja2 = "~=3.1" newrelic = "*" -notifications-python-client = "==8.2.0" +notifications-python-client = "==9.0.0" notifications-utils = {git = "https://github.com/GSA/notifications-utils.git"} pyexcel = "==0.7.0" pyexcel-io = "==0.6.6" diff --git a/tests/app/main/views/test_activity.py b/tests/app/main/views/test_activity.py index 96c50a308..e224c2e7d 100644 --- a/tests/app/main/views/test_activity.py +++ b/tests/app/main/views/test_activity.py @@ -160,8 +160,8 @@ def test_can_show_notifications( assert normalize_spaces( first_row.select_one(".table-field-right-aligned .align-with-message-body").text ) in [ - "Delivered 1 January at 02:00 US/Eastern", - "Delivered 1 January at 01:00 US/Eastern", + "Delivered 01-01-2020 at 01:00 AM", + "Delivered 01-01-2020 at 01:00 AM", ] assert page_title in page.h1.text.strip() @@ -656,36 +656,36 @@ def test_redacts_templates_that_should_be_redacted( @pytest.mark.parametrize( ("message_type", "status", "expected_hint_status", "single_line"), [ - ("email", "created", "Sending since 27 September at 08:30 US/Eastern", True), - ("email", "sending", "Sending since 27 September at 08:30 US/Eastern", True), + ("email", "created", "Sending since 09-27-2017 at 08:30 AM", True), + ("email", "sending", "Sending since 09-27-2017 at 08:30 AM", True), ( "email", "temporary-failure", - "Inbox not accepting messages right now 27 September at 08:30 US/Eastern", + "Inbox not accepting messages right now 09-27-2017 at 08:30 AM", False, ), ( "email", "permanent-failure", - "Email address does not exist 27 September at 08:30 US/Eastern", + "Email address does not exist 09-27-2017 at 08:30 AM", False, ), - ("email", "delivered", "Delivered 27 September at 08:30 US/Eastern", True), - ("sms", "created", "Sending since 27 September at 08:30 US/Eastern", True), - ("sms", "sending", "Sending since 27 September at 08:30 US/Eastern", True), + ("email", "delivered", "Delivered 09-27-2017 at 08:30 AM", True), + ("sms", "created", "Sending since 09-27-2017 at 08:30 AM", True), + ("sms", "sending", "Sending since 09-27-2017 at 08:30 AM", True), ( "sms", "temporary-failure", - "Phone not accepting messages right now 27 September at 08:30 US/Eastern", + "Phone not accepting messages right now 09-27-2017 at 08:30 AM", False, ), ( "sms", "permanent-failure", - "Not delivered 27 September at 08:30 US/Eastern", + "Not delivered 09-27-2017 at 08:30 AM", False, ), - ("sms", "delivered", "Delivered 27 September at 08:30 US/Eastern", True), + ("sms", "delivered", "Delivered 09-27-2017 at 08:30 AM", True), ], ) def test_sending_status_hint_displays_correctly_on_notifications_page( diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py index 8f82ce1d9..c7afce860 100644 --- a/tests/app/main/views/test_jobs.py +++ b/tests/app/main/views/test_jobs.py @@ -90,7 +90,7 @@ def test_should_show_page_for_one_job( assert page.h1.text.strip() == "Message status" assert " ".join(page.find("tbody").find("tr").text.split()) == ( - "2021234567 template content Delivered 1 January at 06:09 US/Eastern" + "2021234567 template content Delivered 01-01-2016 at 06:09 AM" ) assert page.find("div", {"data-key": "notifications"})["data-resource"] == url_for( "main.view_job_updates", @@ -109,7 +109,7 @@ def test_should_show_page_for_one_job( assert page.find("span", {"id": "time-left"}).text == "Data available for 7 days" assert normalize_spaces(page.select_one("tbody tr").text) == normalize_spaces( - "2021234567 " "template content " "Delivered 1 January at 06:09 US/Eastern" + "2021234567 " "template content " "Delivered 01-01-2016 at 06:09 AM" ) assert page.select_one("tbody tr a")["href"] == url_for( "main.view_notification", diff --git a/tests/app/main/views/test_manage_users.py b/tests/app/main/views/test_manage_users.py index 9bdabf925..c3d128155 100644 --- a/tests/app/main/views/test_manage_users.py +++ b/tests/app/main/views/test_manage_users.py @@ -862,9 +862,9 @@ def test_should_show_page_if_prefilled_user_is_already_invited( mock_get_invites_for_service, platform_admin_user, ): - active_user_with_permission_to_other_service[ - "email_address" - ] = "user_1@testnotify.gsa.gov" + active_user_with_permission_to_other_service["email_address"] = ( + "user_1@testnotify.gsa.gov" + ) client_request.login(platform_admin_user) mocker.patch( "app.models.user.user_api_client.get_user", diff --git a/tests/app/main/views/test_sign_in.py b/tests/app/main/views/test_sign_in.py index 7970afa08..f85c756ea 100644 --- a/tests/app/main/views/test_sign_in.py +++ b/tests/app/main/views/test_sign_in.py @@ -21,6 +21,9 @@ def test_render_sign_in_template_for_new_user(client_request): # Removing for the pilot # assert page.select('main a')[0].text == 'create one now' # assert page.select('main a')[0]['href'] == url_for('main.register') + # TODO: Fix this test to be less brittle! If the Login.gov link is enabled, + # then these indices need to be 1 instead of 0. + # Currently it's not enabled for the test or production environments. assert page.select("main a")[0].text == "Forgot your password?" assert page.select("main a")[0]["href"] == url_for("main.forgot_password") assert "Sign in again" not in normalize_spaces(page.text) diff --git a/tests/app/main/views/test_template_folders.py b/tests/app/main/views/test_template_folders.py index fae020e58..0bf2fcb08 100644 --- a/tests/app/main/views/test_template_folders.py +++ b/tests/app/main/views/test_template_folders.py @@ -31,9 +31,11 @@ def _folder(name, folder_id=None, parent=None, users_with_permission=None): "name": name, "id": folder_id or str(uuid.uuid4()), "parent_id": parent, - "users_with_permission": users_with_permission - if users_with_permission is not None - else [sample_uuid()], + "users_with_permission": ( + users_with_permission + if users_with_permission is not None + else [sample_uuid()] + ), } diff --git a/tests/conftest.py b/tests/conftest.py index 122b76826..4c2ceeea9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,6 @@ import copy import json import os -import re from contextlib import contextmanager from datetime import date, datetime, timedelta from unittest.mock import Mock, PropertyMock @@ -529,7 +528,7 @@ def mock_update_service(mocker): "sms_sender", "permissions", ] - } + }, ) return {"data": service} @@ -905,9 +904,11 @@ def create_service_templates(service_id, number_of_templates=4): "{}_template_{}".format(template_type, template_number), template_type, "{} template {} content".format(template_type, template_number), - subject="{} template {} subject".format(template_type, template_number) - if template_type == "email" - else None, + subject=( + "{} template {} subject".format(template_type, template_number) + if template_type == "email" + else None + ), ) ) @@ -1102,9 +1103,9 @@ def active_user_with_permission_to_other_service( active_user_with_permission_to_two_services["permissions"].pop(SERVICE_ONE_ID) active_user_with_permission_to_two_services["services"].pop(0) active_user_with_permission_to_two_services["name"] = "Service Two User" - active_user_with_permission_to_two_services[ - "email_address" - ] = "service-two-user@test.gsa.gov" + active_user_with_permission_to_two_services["email_address"] = ( + "service-two-user@test.gsa.gov" + ) return active_user_with_permission_to_two_services @@ -2353,7 +2354,7 @@ def client_request(logged_in_client, mocker, service_one): # noqa (C901 too com _test_page_title=True, _test_for_elements_without_class=True, _optional_args="", - **endpoint_kwargs + **endpoint_kwargs, ): return ClientRequest.get_url( url_for(endpoint, **(endpoint_kwargs or {})) + _optional_args, @@ -2372,7 +2373,7 @@ def client_request(logged_in_client, mocker, service_one): # noqa (C901 too com _expected_redirect=None, _test_page_title=True, _test_for_elements_without_class=True, - **endpoint_kwargs + **endpoint_kwargs, ): resp = logged_in_client.get( url, @@ -2414,7 +2415,7 @@ def client_request(logged_in_client, mocker, service_one): # noqa (C901 too com _follow_redirects=False, _expected_redirect=None, _content_type=None, - **endpoint_kwargs + **endpoint_kwargs, ): return ClientRequest.post_url( url_for(endpoint, **(endpoint_kwargs or {})), @@ -2473,7 +2474,7 @@ def client_request(logged_in_client, mocker, service_one): # noqa (C901 too com _expected_status=302, _optional_args="", _content_type=None, - **endpoint_kwargs + **endpoint_kwargs, ): return ClientRequest.post_response_from_url( url_for(endpoint, **(endpoint_kwargs or {})) + _optional_args, @@ -3519,89 +3520,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() def fake_markdown_file(): input = "#Test" diff --git a/tests/end_to_end/__init__.py b/tests/end_to_end/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/end_to_end/conftest.py b/tests/end_to_end/conftest.py new file mode 100644 index 000000000..16940d4e0 --- /dev/null +++ b/tests/end_to_end/conftest.py @@ -0,0 +1,107 @@ +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_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 end_to_end_context(browser): + context = browser.new_context() + return context + + +@pytest.fixture(scope="session") +def authenticated_page(end_to_end_context): + # Open a new page and go to the site. + page = end_to_end_context.new_page() + page.goto(f"{E2E_TEST_URI}/") + + # Wait for the next page to fully load. + page.wait_for_load_state("domcontentloaded") + + # Sign in to the site - E2E test accounts are set to flow through. + sign_in_button = page.get_by_role("link", name="Sign in") + sign_in_button.click() + + # Wait for the next page to fully load. + page.wait_for_load_state("domcontentloaded") + + return page diff --git a/tests/end_to_end/test_accounts_page.py b/tests/end_to_end/test_accounts_page.py index c8c8d5a6a..b6fe8c5ac 100644 --- a/tests/end_to_end/test_accounts_page.py +++ b/tests/end_to_end/test_accounts_page.py @@ -4,35 +4,11 @@ import re from playwright.sync_api import expect - -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 +E2E_TEST_URI = os.getenv("NOTIFY_E2E_TEST_URI") -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") +def test_add_new_service_workflow(authenticated_page, end_to_end_context): + page = authenticated_page # Prepare for adding a new service later in the test. current_date_time = datetime.datetime.now() @@ -41,9 +17,7 @@ def test_add_new_service_workflow(end_to_end_context): browser_type=end_to_end_context.browser.browser_type.name, ) - accounts_uri = "{}accounts".format(os.getenv("NOTIFY_E2E_TEST_URI")) - - page.goto(accounts_uri) + page.goto(f"{E2E_TEST_URI}/accounts") # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") @@ -82,24 +56,13 @@ def test_add_new_service_workflow(end_to_end_context): # Retrieve some prominent elements on the page for testing. 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")) 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() # Fill in the form. 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. add_service_button.click() @@ -108,7 +71,7 @@ def test_add_new_service_workflow(end_to_end_context): page.wait_for_load_state("domcontentloaded") # 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(page).to_have_title(re.compile(new_service_name)) diff --git a/tests/end_to_end/test_landing_and_sign_in_pages.py b/tests/end_to_end/test_landing_and_sign_in_pages.py index 895b3851f..a9148cb31 100644 --- a/tests/end_to_end/test_landing_and_sign_in_pages.py +++ b/tests/end_to_end/test_landing_and_sign_in_pages.py @@ -3,11 +3,13 @@ import re from playwright.sync_api import expect +E2E_TEST_URI = os.getenv("NOTIFY_E2E_TEST_URI") + 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 site. 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. 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. 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") 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): # # 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")) +# page.goto(f"{E2E_TEST_URI}/") # print(f"test_sign_in_and_mfa_pages initial {page}") # # sign_in_button = page.get_by_role("link", name="Sign in")