diff --git a/.ds.baseline b/.ds.baseline index d1d6a8320..9b63ec870 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -133,7 +133,7 @@ "filename": ".github/workflows/checks.yml", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 66, + "line_number": 68, "is_secret": false } ], diff --git a/.github/actions/deploy-proxy/action.yml b/.github/actions/deploy-proxy/action.yml index 393dd4b45..0ffc05066 100644 --- a/.github/actions/deploy-proxy/action.yml +++ b/.github/actions/deploy-proxy/action.yml @@ -1,12 +1,6 @@ name: Deploy egress proxy description: Set egress space security groups and deploy proxy inputs: - cf_username: - description: The username to authenticate with. - required: true - cf_password: - description: The password to authenticate with. - required: true cf_org: description: The org the target app exists in. required: true diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 8fa950942..f002bb3fc 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -53,11 +53,13 @@ jobs: end-to-end-tests: if: ${{ github.actor != 'dependabot[bot]' }} + permissions: checks: write pull-requests: write contents: write runs-on: ubuntu-latest + environment: staging services: postgres: image: postgres @@ -97,13 +99,17 @@ jobs: # insert this line: # tail -f admin-server.log & # above make e2e-test + + run: | make run-flask > admin-server.log 2>&1 & + tail -f admin-server.log & make e2e-test + env: API_HOST_NAME: https://notify-api-staging.app.cloud.gov/ - DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }} SECRET_KEY: ${{ secrets.SECRET_KEY }} + DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }} ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }} ADMIN_CLIENT_USERNAME: notify-admin NOTIFY_ENVIRONMENT: e2etest @@ -111,7 +117,7 @@ jobs: 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/ - + VCAP_SERVICES: ${{ secrets.VCAP_SERVICES }} validate-new-relic-config: runs-on: ubuntu-latest environment: staging diff --git a/.github/workflows/deploy-demo.yml b/.github/workflows/deploy-demo.yml index 31b9cec6b..972240c9a 100644 --- a/.github/workflows/deploy-demo.yml +++ b/.github/workflows/deploy-demo.yml @@ -52,6 +52,7 @@ jobs: DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }} SECRET_KEY: ${{ secrets.SECRET_KEY }} ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }} + ADMIN_CLIENT_USERNAME: "notify-admin" NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }} NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }} COMMIT_HASH: ${{ github.sha }} @@ -69,17 +70,16 @@ jobs: cf_password: ${{ secrets.CLOUDGOV_PASSWORD }} cf_org: gsa-tts-benefits-studio cf_space: notify-demo - push_arguments: >- + cf_command: >- push -f manifest.yml --vars-file deploy-config/demo.yml --var DANGEROUS_SALT="$DANGEROUS_SALT" --var SECRET_KEY="$SECRET_KEY" - --var ADMIN_CLIENT_USERNAME="notify-admin" --var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET" + --var ADMIN_CLIENT_USERNAME="$ADMIN_CLIENT_USERNAME" --var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY" --var NR_BROWSER_KEY="$NR_BROWSER_KEY" --var COMMIT_HASH="$COMMIT_HASH" - --var LOGIN_PEM="$LOGIN_PEM" --var LOGIN_DOT_GOV_CLIENT_ID="$LOGIN_DOT_GOV_CLIENT_ID" --var LOGIN_DOT_GOV_USER_INFO_URL="$LOGIN_DOT_GOV_USER_INFO_URL" --var LOGIN_DOT_GOV_ACCESS_TOKEN_URL="$LOGIN_DOT_GOV_ACCESS_TOKEN_URL" @@ -88,6 +88,7 @@ jobs: --var LOGIN_DOT_GOV_SIGNOUT_REDIRECT="$LOGIN_DOT_GOV_SIGNOUT_REDIRECT" --var LOGIN_DOT_GOV_INITIAL_SIGNIN_URL="$LOGIN_DOT_GOV_INITIAL_SIGNIN_URL" --var LOGIN_DOT_GOV_CERTS_URL="$LOGIN_DOT_GOV_CERTS_URL" + --var LOGIN_PEM="$LOGIN_PEM" --strategy rolling - name: Check for changes to egress config @@ -101,6 +102,10 @@ jobs: - name: Deploy egress proxy if: steps.changed-egress-config.outputs.any_changed == 'true' uses: ./.github/actions/deploy-proxy + env: + CF_USERNAME: ${{ secrets.CLOUDGOV_USERNAME }} + CF_PASSWORD: ${{ secrets.CLOUDGOV_PASSWORD }} with: + cf_org: gsa-tts-benefits-studio cf_space: notify-demo app: notify-admin-demo diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index dd71b1e11..334146485 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -52,6 +52,7 @@ jobs: DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }} SECRET_KEY: ${{ secrets.SECRET_KEY }} ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }} + ADMIN_CLIENT_USERNAME: "notify-admin" NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }} NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }} COMMIT_HASH: ${{ github.sha }} @@ -69,17 +70,16 @@ jobs: cf_password: ${{ secrets.CLOUDGOV_PASSWORD }} cf_org: gsa-tts-benefits-studio cf_space: notify-production - push_arguments: >- + cf_command: >- push -f manifest.yml --vars-file deploy-config/production.yml --var DANGEROUS_SALT="$DANGEROUS_SALT" --var SECRET_KEY="$SECRET_KEY" - --var ADMIN_CLIENT_USERNAME="notify-admin" --var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET" + --var ADMIN_CLIENT_USERNAME="$ADMIN_CLIENT_USERNAME" --var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY" --var NR_BROWSER_KEY="$NR_BROWSER_KEY" --var COMMIT_HASH="$COMMIT_HASH" - --var LOGIN_PEM="$LOGIN_PEM" --var LOGIN_DOT_GOV_CLIENT_ID="$LOGIN_DOT_GOV_CLIENT_ID" --var LOGIN_DOT_GOV_USER_INFO_URL="$LOGIN_DOT_GOV_USER_INFO_URL" --var LOGIN_DOT_GOV_ACCESS_TOKEN_URL="$LOGIN_DOT_GOV_ACCESS_TOKEN_URL" @@ -88,6 +88,7 @@ jobs: --var LOGIN_DOT_GOV_SIGNOUT_REDIRECT="$LOGIN_DOT_GOV_SIGNOUT_REDIRECT" --var LOGIN_DOT_GOV_INITIAL_SIGNIN_URL="$LOGIN_DOT_GOV_INITIAL_SIGNIN_URL" --var LOGIN_DOT_GOV_CERTS_URL="$LOGIN_DOT_GOV_CERTS_URL" + --var LOGIN_PEM="$LOGIN_PEM" --strategy rolling - name: Check for changes to egress config @@ -101,6 +102,10 @@ jobs: - name: Deploy egress proxy if: steps.changed-egress-config.outputs.any_changed == 'true' uses: ./.github/actions/deploy-proxy + env: + CF_USERNAME: ${{ secrets.CLOUDGOV_USERNAME }} + CF_PASSWORD: ${{ secrets.CLOUDGOV_PASSWORD }} with: + cf_org: gsa-tts-benefits-studio cf_space: notify-production app: notify-admin-production diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index abcac49a0..815dc1129 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -107,11 +107,12 @@ jobs: .github/actions/deploy-proxy/action.yml .github/workflows/deploy.yml - name: Deploy egress proxy - #if: steps.changed-egress-config.outputs.any_changed == 'true' + if: steps.changed-egress-config.outputs.any_changed == 'true' uses: ./.github/actions/deploy-proxy + env: + CF_USERNAME: ${{ secrets.CLOUDGOV_USERNAME }} + CF_PASSWORD: ${{ secrets.CLOUDGOV_PASSWORD }} with: - cf_username: ${{ secrets.CLOUDGOV_USERNAME }} - cf_password: ${{ secrets.CLOUDGOV_PASSWORD }} cf_org: gsa-tts-benefits-studio cf_space: notify-staging app: notify-admin-staging diff --git a/README.md b/README.md index 04458e394..e1a372fd3 100644 --- a/README.md +++ b/README.md @@ -507,3 +507,13 @@ insurance. For more information on what we're working on, the Notify tool, and how to get involved with our team, [see our flyer.](https://github.com/GSA/notifications-admin/blob/main/docs/notify-pilot-flyer.md) + +## Updating secrets for the E2E tests + +At some point, E2E tests will fail because the secrets held in VCAP_SERVICES have expired. To refresh +them, you will need to do the following: + +1. Log in the normal way to access cloudfoundry command line options +2. In your terminal, run `chmod +x print_vcap.sh` +3. In your terminal, run `./print_vcap.sh` +4. Copy the value in your terminal and paste it into the VCAP_SERVICES secret in Github on the staging tier. diff --git a/app/main/views/send.py b/app/main/views/send.py index 2582cfeff..3d83d10f3 100644 --- a/app/main/views/send.py +++ b/app/main/views/send.py @@ -802,9 +802,15 @@ def get_skip_link(step_index, template): ) @user_has_permissions("send_messages", restrict_admin_usage=True) def send_one_off_to_myself(service_id, template_id): - db_template = current_service.get_template_with_user_permission_or_403( - template_id, current_user - ) + current_app.logger.info("Send one off to myself") + try: + db_template = current_service.get_template_with_user_permission_or_403( + template_id, current_user + ) + except Exception: + current_app.logger.exception("Couldnt get template for one off") + # Use 406 just because we're limited to certain codes here and it will point us back to a problem here + abort(406) if db_template["template_type"] not in ("sms", "email"): abort(404) diff --git a/app/models/user.py b/app/models/user.py index ba478feda..468208d6e 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -201,7 +201,7 @@ class User(JSONModel, UserMixin): @property def is_gov_user(self): is_gov = is_gov_user(self.email_address) - current_app.logger.info(f"User {self.id} is_gov_user: {is_gov}") + # current_app.logger.info(f"User {self.id} is_gov_user: {is_gov}") return is_gov @property @@ -210,9 +210,9 @@ class User(JSONModel, UserMixin): @property def platform_admin(self): - current_app.logger.warn( - f"Checking User {self.id} for platform admin: {self._platform_admin}" - ) + # current_app.logger.warning( + # f"Checking User {self.id} for platform admin: {self._platform_admin}" + # ) return self._platform_admin and not session.get( "disable_platform_admin_view", False ) @@ -242,26 +242,31 @@ class User(JSONModel, UserMixin): # we shouldn't have any pages that require permissions, but don't specify a service or organization. # use @user_is_platform_admin for platform admin only pages # raise NotImplementedError - current_app.logger.warn(f"VIEW ARGS ARE {request.view_args}") + # current_app.logger.warning(f"VIEW ARGS ARE {request.view_args}") pass - log_msg = f"has_permissions user: {self.id} service: {service_id}" # platform admins should be able to do most things (except eg send messages, or create api keys) if self.platform_admin and not restrict_admin_usage: - current_app.logger.warn(f"{log_msg} true because user is platform_admin") + current_app.logger.debug( + "has_permissions is true because user is platform_admin" + ) return True if org_id: value = self.belongs_to_organization(org_id) - current_app.logger.warn(f"{log_msg} org: {org_id} returning {value}") + current_app.logger.debug( + f"has_permissions returns org: {org_id} returning {value}" + ) return value if not permissions and self.belongs_to_service(service_id): - current_app.logger.warn(f"{log_msg} True because belongs_to_service") + current_app.logger.debug("has_permissions True because belongs_to_service") return True if any(self.permissions_for_service(service_id) & set(permissions)): - current_app.logger.warn(f"{log_msg} permissions valid") + current_app.logger.debug( + "has_permissions returns True because permissions valid" + ) return True from app.models.service import Service @@ -269,7 +274,7 @@ class User(JSONModel, UserMixin): org_value = allow_org_user and self.belongs_to_organization( Service.from_id(service_id).organization_id ) - current_app.logger.warn(f"{log_msg} returning {org_value}") + current_app.logger.debug(f"has_permissions returning {org_value}") return org_value def permissions_for_service(self, service_id): @@ -277,7 +282,7 @@ class User(JSONModel, UserMixin): def has_permission_for_service(self, service_id, permission): has_permission = permission in self.permissions_for_service(service_id) - current_app.logger.warn( + current_app.logger.debug( f"has_permission_for_service user: {self.id} service: {service_id} " f"permission: {permission} retuning {has_permission}" ) @@ -558,17 +563,17 @@ class InvitedUser(JSONModel): return cls.by_id(invited_user_id) if invited_user_id else None def has_permissions(self, *permissions): - current_app.logger.warn( - f"Checking invited user {self.id} for permissions: {permissions}" - ) + # current_app.logger.warning( + # f"Checking invited user {self.id} for permissions: {permissions}" + # ) if self.status == "cancelled": return False return set(self.permissions) > set(permissions) def has_permission_for_service(self, service_id, permission): - current_app.logger.warn( - f"Checking invited user {self.id} for permission: {permission} on service {service_id}" - ) + # current_app.logger.warn( + # f"Checking invited user {self.id} for permission: {permission} on service {service_id}" + # ) if self.status == "cancelled": return False return self.service == service_id and permission in self.permissions diff --git a/app/s3_client/__init__.py b/app/s3_client/__init__.py index 7de3509d2..fda938ebe 100644 --- a/app/s3_client/__init__.py +++ b/app/s3_client/__init__.py @@ -29,7 +29,10 @@ def get_s3_object( aws_secret_access_key=secret_key, region_name=region, ) - s3 = session.resource("s3", config=AWS_CLIENT_CONFIG) + s3 = session.resource( + "s3", + config=AWS_CLIENT_CONFIG, + ) obj = s3.Object(bucket_name, filename) # This 'proves' that use of moto in the relevant tests in test_send.py # mocks everything related to S3. What you will see in the logs is: diff --git a/app/s3_client/s3_csv_client.py b/app/s3_client/s3_csv_client.py index 09454ade0..195ea3032 100644 --- a/app/s3_client/s3_csv_client.py +++ b/app/s3_client/s3_csv_client.py @@ -1,3 +1,4 @@ +import os import uuid from flask import current_app @@ -33,6 +34,14 @@ def s3upload(service_id, filedata): bucket_name, file_location, access_key, secret_key, region = get_csv_location( service_id, upload_id ) + if bucket_name == "": + exp_bucket = current_app.config["CSV_UPLOAD_BUCKET"]["bucket"] + exp_region = current_app.config["CSV_UPLOAD_BUCKET"]["region"] + tier = os.getenv("NOTIFY_ENVIRONMENT") + raise Exception( + f"NO BUCKET NAME SHOULD BE: {exp_bucket} WITH REGION {exp_region} TIER {tier}" + ) + utils_s3upload( filedata=filedata["data"], region=region, diff --git a/app/templates/partials/jobs/status.html b/app/templates/partials/jobs/status.html index 42cf4a4b7..69251a50a 100644 --- a/app/templates/partials/jobs/status.html +++ b/app/templates/partials/jobs/status.html @@ -25,7 +25,7 @@ {% if job.scheduled_for %}
-

Your text has been scheduled

+

Your text has been scheduled

{{ job.template_name }} - {{ current_service.name }} was scheduled on {{ job.scheduled_for|format_datetime_normal }} by {{ job.created_by.name }}

@@ -35,7 +35,7 @@ {% else %}
-

Your text has been sent

+

Your text has been sent

{{ job.template_name }} - {{ current_service.name }} was sent on {% if job.processing_started %} {{ job.processing_started|format_datetime_table }} {% else %} diff --git a/notifications_utils/s3.py b/notifications_utils/s3.py index d33cbe25a..f7b342284 100644 --- a/notifications_utils/s3.py +++ b/notifications_utils/s3.py @@ -37,7 +37,10 @@ def s3upload( aws_secret_access_key=secret_key, region_name=region, ) - _s3 = session.resource("s3", config=AWS_CLIENT_CONFIG) + _s3 = session.resource( + "s3", + config=AWS_CLIENT_CONFIG, + ) # This 'proves' that use of moto in the relevant tests in test_send.py # mocks everything related to S3. What you will see in the logs is: # Exception: CREATED AT diff --git a/package-lock.json b/package-lock.json index 00b5f8d24..ed3fd255d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@rollup/plugin-commonjs": "^28.0.0", "@rollup/plugin-node-resolve": "^15.3.0", "@rollup/stream": "^3.0.1", - "@uswds/uswds": "^3.8.2", + "@uswds/uswds": "^3.9.0", "cbor-js": "0.1.0", "d3": "^7.9.0", "govuk_frontend_toolkit": "^9.0.1", @@ -21,18 +21,18 @@ "hogan": "1.0.2", "jquery": "3.7.1", "morphdom": "^2.7.4", - "playwright": "^1.47.2", + "playwright": "^1.48.1", "python": "^0.0.4", "query-command-supported": "1.0.0", - "sass-embedded": "^1.79.4", + "sass-embedded": "^1.79.5", "textarea-caret": "3.1.0", "timeago": "1.6.7", "vinyl-buffer": "^1.0.1", "vinyl-source-stream": "^2.0.0" }, "devDependencies": { - "@babel/core": "^7.25.2", - "@babel/preset-env": "^7.25.7", + "@babel/core": "^7.25.8", + "@babel/preset-env": "^7.25.8", "@uswds/compile": "^1.2.0", "backstopjs": "^6.3.25", "better-npm-audit": "^3.11.0", @@ -85,31 +85,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.7.tgz", - "integrity": "sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", + "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", - "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", + "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helpers": "^7.25.7", + "@babel/parser": "^7.25.8", + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.8", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -401,14 +400,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", + "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -430,12 +428,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", - "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", + "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", "dev": true, "dependencies": { - "@babel/types": "^7.25.7" + "@babel/types": "^7.25.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -571,45 +569,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-import-assertions": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.7.tgz", @@ -751,21 +710,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", @@ -828,14 +772,13 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.7.tgz", - "integrity": "sha512-4B6OhTrwYKHYYgcwErvZjbmH9X5TxQBsaBHdzEIB4l71gR5jh/tuHGlb9in47udL2+wVUcOz5XXhhfhVJwEpEg==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.8.tgz", + "integrity": "sha512-9ypqkozyzpG+HxlH4o4gdctalFGIjjdufzo7I2XPda0iBnZ6a+FO0rIEQcdSPXp02CkvGsII1exJhmROPQd5oA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.7", "@babel/helper-remap-async-to-generator": "^7.25.7", - "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/traverse": "^7.25.7" }, "engines": { @@ -909,14 +852,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.25.7.tgz", - "integrity": "sha512-rvUUtoVlkDWtDWxGAiiQj0aNktTPn3eFynBcMC2IhsXweehwgdI9ODe+XjWw515kEmv22sSOTp/rxIRuTiB7zg==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.25.8.tgz", + "integrity": "sha512-e82gl3TCorath6YLf9xUwFehVvjvfqFhdOo4+0iVIVju+6XOi5XHkqB3P2AXnSwoeTX0HBoXq5gJFtvotJzFnQ==", "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1024,13 +966,12 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.7.tgz", - "integrity": "sha512-UvcLuual4h7/GfylKm2IAA3aph9rwvAM2XBA0uPKU3lca+Maai4jBjjEVUS568ld6kJcgbouuumCBhMd/Yz17w==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.8.tgz", + "integrity": "sha512-gznWY+mr4ZQL/EWPcbBQUP3BXS5FwZp8RUOw06BaRn8tQLzN4XLIxXejpHN9Qo8x8jjBmAAKp6FoS51AgkSA/A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1056,13 +997,12 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.7.tgz", - "integrity": "sha512-h3MDAP5l34NQkkNulsTNyjdaR+OiB0Im67VU//sFupouP8Q6m9Spy7l66DcaAQxtmCqGdanPByLsnwFttxKISQ==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.8.tgz", + "integrity": "sha512-sPtYrduWINTQTW7FtOy99VCTWp4H23UX7vYcut7S4CIMEXU+54zKX9uCoGkLsWXteyaMXzVHgzWbLfQ1w4GZgw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1105,13 +1045,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.7.tgz", - "integrity": "sha512-Ot43PrL9TEAiCe8C/2erAjXMeVSnE/BLEx6eyrKLNFCCw5jvhTHKyHxdI1pA0kz5njZRYAnMO2KObGqOCRDYSA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.8.tgz", + "integrity": "sha512-4OMNv7eHTmJ2YXs3tvxAfa/I43di+VcF+M4Wt66c88EAED1RoGaf1D64cL5FkRpNL+Vx9Hds84lksWvd/wMIdA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1136,13 +1075,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.7.tgz", - "integrity": "sha512-iImzbA55BjiovLyG2bggWS+V+OLkaBorNvc/yJoeeDQGztknRnDdYfp2d/UPmunZYEnZi6Lg8QcTmNMHOB0lGA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.8.tgz", + "integrity": "sha512-f5W0AhSbbI+yY6VakT04jmxdxz+WsID0neG7+kQZbCOjuyJNdL5Nn4WIBm4hRpKnUcO9lP0eipUhFN12JpoH8g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1265,13 +1203,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.7.tgz", - "integrity": "sha512-FbuJ63/4LEL32mIxrxwYaqjJxpbzxPVQj5a+Ebrc8JICV6YX8nE53jY+K0RZT3um56GoNWgkS2BQ/uLGTjtwfw==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.8.tgz", + "integrity": "sha512-Z7WJJWdQc8yCWgAmjI3hyC+5PXIubH9yRKzkl9ZEG647O9szl9zvmKLzpbItlijBnVhTUf1cpyWBsZ3+2wjWPQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1281,13 +1218,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.7.tgz", - "integrity": "sha512-8CbutzSSh4hmD+jJHIA8vdTNk15kAzOnFLVVgBSMGr28rt85ouT01/rezMecks9pkU939wDInImwCKv4ahU4IA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.8.tgz", + "integrity": "sha512-rm9a5iEFPS4iMIy+/A/PiS0QN0UyjPIeVvbU5EMZFKJZHt8vQnasbpo3T3EFcxzCeYO0BHfc4RqooCZc51J86Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1297,14 +1233,13 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.7.tgz", - "integrity": "sha512-1JdVKPhD7Y5PvgfFy0Mv2brdrolzpzSoUq2pr6xsR+m+3viGGeHEokFKsCgOkbeFOQxfB1Vt2F0cPJLRpFI4Zg==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.8.tgz", + "integrity": "sha512-LkUu0O2hnUKHKE7/zYOIjByMa4VRaV2CD/cdGz0AxU9we+VA3kDDggKEzI0Oz1IroG+6gUP6UmWEHBMWZU316g==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.25.7", "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-transform-parameters": "^7.25.7" }, "engines": { @@ -1331,13 +1266,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.7.tgz", - "integrity": "sha512-m9obYBA39mDPN7lJzD5WkGGb0GO54PPLXsbcnj1Hyeu8mSRz7Gb4b1A6zxNX32ZuUySDK4G6it8SDFWD1nCnqg==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.8.tgz", + "integrity": "sha512-EbQYweoMAHOn7iJ9GgZo14ghhb9tTjgOc88xFgYngifx7Z9u580cENCV159M4xDh3q/irbhSjZVpuhpC2gKBbg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1347,14 +1281,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.7.tgz", - "integrity": "sha512-h39agClImgPWg4H8mYVAbD1qP9vClFbEjqoJmt87Zen8pjqK8FTPUwrOXAvqu5soytwxrLMd2fx2KSCp2CHcNg==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.8.tgz", + "integrity": "sha512-q05Bk7gXOxpTHoQ8RSzGSh/LHVB9JEIkKnk3myAWwZHnYiTGYtbdrYkIsS8Xyh4ltKf7GNUSgzs/6P2bJtBAQg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1395,15 +1328,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.7.tgz", - "integrity": "sha512-LzA5ESzBy7tqj00Yjey9yWfs3FKy4EmJyKOSWld144OxkTji81WWnUT8nkLUn+imN/zHL8ZQlOu/MTUAhHaX3g==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.8.tgz", + "integrity": "sha512-8Uh966svuB4V8RHHg0QJOB32QK287NBksJOByoKmHMp1TAobNniNalIkI2i5IPj5+S9NYCG4VIjbEuiSN8r+ow==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.7", "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1598,12 +1530,12 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.7.tgz", - "integrity": "sha512-Gibz4OUdyNqqLj+7OAvBZxOD7CklCtMA5/j0JgUEwOnaRULsPDXmic2iKxL2DX2vQduPR5wH2hjZas/Vr/Oc0g==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.8.tgz", + "integrity": "sha512-58T2yulDHMN8YMUxiLq5YmWUnlDCyY1FsHM+v12VMx+1/FlrUj5tY50iDCpofFQEM8fMYOaY9YRvym2jcjn1Dg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.25.7", + "@babel/compat-data": "^7.25.8", "@babel/helper-compilation-targets": "^7.25.7", "@babel/helper-plugin-utils": "^7.25.7", "@babel/helper-validator-option": "^7.25.7", @@ -1613,45 +1545,30 @@ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.7", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", "@babel/plugin-syntax-import-assertions": "^7.25.7", "@babel/plugin-syntax-import-attributes": "^7.25.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.25.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.8", "@babel/plugin-transform-async-to-generator": "^7.25.7", "@babel/plugin-transform-block-scoped-functions": "^7.25.7", "@babel/plugin-transform-block-scoping": "^7.25.7", "@babel/plugin-transform-class-properties": "^7.25.7", - "@babel/plugin-transform-class-static-block": "^7.25.7", + "@babel/plugin-transform-class-static-block": "^7.25.8", "@babel/plugin-transform-classes": "^7.25.7", "@babel/plugin-transform-computed-properties": "^7.25.7", "@babel/plugin-transform-destructuring": "^7.25.7", "@babel/plugin-transform-dotall-regex": "^7.25.7", "@babel/plugin-transform-duplicate-keys": "^7.25.7", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.7", - "@babel/plugin-transform-dynamic-import": "^7.25.7", + "@babel/plugin-transform-dynamic-import": "^7.25.8", "@babel/plugin-transform-exponentiation-operator": "^7.25.7", - "@babel/plugin-transform-export-namespace-from": "^7.25.7", + "@babel/plugin-transform-export-namespace-from": "^7.25.8", "@babel/plugin-transform-for-of": "^7.25.7", "@babel/plugin-transform-function-name": "^7.25.7", - "@babel/plugin-transform-json-strings": "^7.25.7", + "@babel/plugin-transform-json-strings": "^7.25.8", "@babel/plugin-transform-literals": "^7.25.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.8", "@babel/plugin-transform-member-expression-literals": "^7.25.7", "@babel/plugin-transform-modules-amd": "^7.25.7", "@babel/plugin-transform-modules-commonjs": "^7.25.7", @@ -1659,15 +1576,15 @@ "@babel/plugin-transform-modules-umd": "^7.25.7", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.7", "@babel/plugin-transform-new-target": "^7.25.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.7", - "@babel/plugin-transform-numeric-separator": "^7.25.7", - "@babel/plugin-transform-object-rest-spread": "^7.25.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.8", + "@babel/plugin-transform-numeric-separator": "^7.25.8", + "@babel/plugin-transform-object-rest-spread": "^7.25.8", "@babel/plugin-transform-object-super": "^7.25.7", - "@babel/plugin-transform-optional-catch-binding": "^7.25.7", - "@babel/plugin-transform-optional-chaining": "^7.25.7", + "@babel/plugin-transform-optional-catch-binding": "^7.25.8", + "@babel/plugin-transform-optional-chaining": "^7.25.8", "@babel/plugin-transform-parameters": "^7.25.7", "@babel/plugin-transform-private-methods": "^7.25.7", - "@babel/plugin-transform-private-property-in-object": "^7.25.7", + "@babel/plugin-transform-private-property-in-object": "^7.25.8", "@babel/plugin-transform-property-literals": "^7.25.7", "@babel/plugin-transform-regenerator": "^7.25.7", "@babel/plugin-transform-reserved-words": "^7.25.7", @@ -1753,9 +1670,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", - "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", + "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.7", @@ -3553,9 +3470,9 @@ } }, "node_modules/@uswds/uswds": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@uswds/uswds/-/uswds-3.8.2.tgz", - "integrity": "sha512-8sTx/GqlbTwSIK+0AFOGrYdaW1rKVB7Bp0+v9AMVt3I5vPK7CL0+I6vlclSf3U7ysJZeTTdkNS8q89sIAeL+AA==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@uswds/uswds/-/uswds-3.9.0.tgz", + "integrity": "sha512-8THm36j7iLjrDiI1D0C6b3hHsmM/Sy5Iiz+IjE+i/gYzVUMG9XVthxAZYonhU97Q1b079n6nYwlUmDSYowJecQ==", "dependencies": { "object-assign": "4.1.1", "receptor": "1.0.0", @@ -12131,11 +12048,11 @@ } }, "node_modules/playwright": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", - "integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.1.tgz", + "integrity": "sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w==", "dependencies": { - "playwright-core": "1.47.2" + "playwright-core": "1.48.1" }, "bin": { "playwright": "cli.js" @@ -12148,9 +12065,9 @@ } }, "node_modules/playwright-core": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", - "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.1.tgz", + "integrity": "sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA==", "bin": { "playwright-core": "cli.js" }, @@ -13209,9 +13126,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass-embedded": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.79.4.tgz", - "integrity": "sha512-3AATrtStMgxYjkit02/Ix8vx/P7qderYG6DHjmehfk5jiw53OaWVScmcGJSwp/d77kAkxDQ+Y0r+79VynGmrkw==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.79.5.tgz", + "integrity": "sha512-QFdalnjGFkbNvb6/uQGmP4OIN+GQ5/R77eu0PsXduDB1YP5JW5DSWFVDAyK6l6C54P+3J3eXkjuPYC0mcwX+AA==", "dependencies": { "@bufbuild/protobuf": "^2.0.0", "buffer-builder": "^0.2.0", @@ -13228,32 +13145,32 @@ "node": ">=16.0.0" }, "optionalDependencies": { - "sass-embedded-android-arm": "1.79.4", - "sass-embedded-android-arm64": "1.79.4", - "sass-embedded-android-ia32": "1.79.4", - "sass-embedded-android-riscv64": "1.79.4", - "sass-embedded-android-x64": "1.79.4", - "sass-embedded-darwin-arm64": "1.79.4", - "sass-embedded-darwin-x64": "1.79.4", - "sass-embedded-linux-arm": "1.79.4", - "sass-embedded-linux-arm64": "1.79.4", - "sass-embedded-linux-ia32": "1.79.4", - "sass-embedded-linux-musl-arm": "1.79.4", - "sass-embedded-linux-musl-arm64": "1.79.4", - "sass-embedded-linux-musl-ia32": "1.79.4", - "sass-embedded-linux-musl-riscv64": "1.79.4", - "sass-embedded-linux-musl-x64": "1.79.4", - "sass-embedded-linux-riscv64": "1.79.4", - "sass-embedded-linux-x64": "1.79.4", - "sass-embedded-win32-arm64": "1.79.4", - "sass-embedded-win32-ia32": "1.79.4", - "sass-embedded-win32-x64": "1.79.4" + "sass-embedded-android-arm": "1.79.5", + "sass-embedded-android-arm64": "1.79.5", + "sass-embedded-android-ia32": "1.79.5", + "sass-embedded-android-riscv64": "1.79.5", + "sass-embedded-android-x64": "1.79.5", + "sass-embedded-darwin-arm64": "1.79.5", + "sass-embedded-darwin-x64": "1.79.5", + "sass-embedded-linux-arm": "1.79.5", + "sass-embedded-linux-arm64": "1.79.5", + "sass-embedded-linux-ia32": "1.79.5", + "sass-embedded-linux-musl-arm": "1.79.5", + "sass-embedded-linux-musl-arm64": "1.79.5", + "sass-embedded-linux-musl-ia32": "1.79.5", + "sass-embedded-linux-musl-riscv64": "1.79.5", + "sass-embedded-linux-musl-x64": "1.79.5", + "sass-embedded-linux-riscv64": "1.79.5", + "sass-embedded-linux-x64": "1.79.5", + "sass-embedded-win32-arm64": "1.79.5", + "sass-embedded-win32-ia32": "1.79.5", + "sass-embedded-win32-x64": "1.79.5" } }, "node_modules/sass-embedded-android-arm": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.79.4.tgz", - "integrity": "sha512-YOVpDGDcwWUQvktpJhYo4zOkknDpdX6ALpaeHDTX6GBUvnZfx+Widh76v+QFUhiJQ/I/hndXg1jv/PKilOHRrw==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.79.5.tgz", + "integrity": "sha512-gYtpQAE2uNFa5IBKBIzUq5ETDS6gnVRmhP5j+N5JGrOThYaGPcG4KrjlU9R3BfCmc7zP5WvlHFZsxSz+2JRT2w==", "cpu": [ "arm" ], @@ -13266,9 +13183,9 @@ } }, "node_modules/sass-embedded-android-arm64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.79.4.tgz", - "integrity": "sha512-0JAZ8TtXYv9yI3Yasaq03xvo7DLJOmD+Exb30oJKxXcWTAV9TB0ZWKoIRsFxbCyPxyn7ouxkaCEXQtaTRKrmfw==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.79.5.tgz", + "integrity": "sha512-pq1RJTENkRmEUMLiVuSGYwuLk8zXovWzrjQxlWZTF/Jn5F7Ypi/3v5huMmgJF5n+etsxjio1PN1idaQ5tPLBmg==", "cpu": [ "arm64" ], @@ -13281,9 +13198,9 @@ } }, "node_modules/sass-embedded-android-ia32": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.79.4.tgz", - "integrity": "sha512-IjO3RoyvNN84ZyfAR5s/a8TIdNPfClb7CLGrswB3BN/NElYIJUJMVHD6+Y8W9QwBIZ8DrK1IdLFSTV8nn82xMA==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.79.5.tgz", + "integrity": "sha512-CgJZjLxYRkgjTP/76WumLlF7+1aW0LA+DSEJhkVaCxe5avndRCmPrNcX0PrtYSDvUgeQDvY7xF+fT9QXN1+NgQ==", "cpu": [ "ia32" ], @@ -13296,9 +13213,9 @@ } }, "node_modules/sass-embedded-android-riscv64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.79.4.tgz", - "integrity": "sha512-uOT8nXmKxSwuIdcqvElVWBFcm/+YcIvmwfoKbpuuSOSxUe9eqFzxo+fk7ILhynzf6FBlvRUH5DcjGj+sXtCc3w==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.79.5.tgz", + "integrity": "sha512-OLbdmDSM/eOjO01PUYbS54BQOCM/HHHHWk/4M8HHdxwF3ojy5eqQaA63R1YQ3IJvLEE7dnudofOXmL01B5+yvQ==", "cpu": [ "riscv64" ], @@ -13311,9 +13228,9 @@ } }, "node_modules/sass-embedded-android-x64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.79.4.tgz", - "integrity": "sha512-W2FQoj3Z2J2DirNs3xSBVvrhMuqLnsqvOPulxOkhL/074+faKOZZnPx2tZ5zsHbY97SonciiU0SV0mm98xI42w==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.79.5.tgz", + "integrity": "sha512-UbXxk/rdR3aVBkB7Fh/eAUL7oUADWgQrYpLe9Xu5A0gmthw0/zo/pu7kweBSrbgHnPfWIt/uxwmW4eEAmqqZWQ==", "cpu": [ "x64" ], @@ -13326,9 +13243,9 @@ } }, "node_modules/sass-embedded-darwin-arm64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.79.4.tgz", - "integrity": "sha512-pcYtbN1VUAAcfgyHeX8ySndDWGjIvcq6rldduktPbGGuAlEWFDfnwjTbv0hS945ggdzZ6TFnaFlLEDr0SjKzBA==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.79.5.tgz", + "integrity": "sha512-qeEl9XhYetZSY1j4nqvh3hB8tfYOAGsOQyVOCaxyX1bubMRSGPvPNIyftm14QzK7EDrE/K/0+FwCvflarOV4NQ==", "cpu": [ "arm64" ], @@ -13341,9 +13258,9 @@ } }, "node_modules/sass-embedded-darwin-x64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.79.4.tgz", - "integrity": "sha512-ir8CFTfc4JLx/qCP8LK1/3pWv35nRyAQkUK7lBIKM6hWzztt64gcno9rZIk4SpHr7Z/Bp1IYWWRS4ZT+4HmsbA==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.79.5.tgz", + "integrity": "sha512-y4pvkYCQhgruxlncub/2j+cZSmlpsZX9Rp1aTRIKvlNagqFStYzFZ6kX3CErlfCEAMYwRVEhP8z/OOoDqnjaZA==", "cpu": [ "x64" ], @@ -13356,9 +13273,9 @@ } }, "node_modules/sass-embedded-linux-arm": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.79.4.tgz", - "integrity": "sha512-H/XEE3rY7c+tY0qDaELjPjC6VheAhBo1tPJQ6UHoBEf8xrbT/RT3dWiIS8grp9Vk54RCn05BEB/+POaljvvKGA==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.79.5.tgz", + "integrity": "sha512-rX6qAR8pE1pevYhGzbCpGFexdH4z6QMnw3IeiCNmkpJ4zMXNEN336xl6SZN0xaPiGuNDhUFcq0wgSq3RDKS5vQ==", "cpu": [ "arm" ], @@ -13371,9 +13288,9 @@ } }, "node_modules/sass-embedded-linux-arm64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.79.4.tgz", - "integrity": "sha512-XIVn2mCuA422SR2kmKjF6jhjMs1Vrt1DbZ/ktSp+eR0sU4ugu2htg45GajiUFSKKRj7Sc+cBdThq1zPPsDLf1w==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.79.5.tgz", + "integrity": "sha512-kiUbrLiNAA7vOe6kpdukRhCad1u7ebwhB0ZE63+IgF5HFZ/Qo6GkhHIrVM9AfeLxUT3N6Z4BNtgdcRa9na4Pwg==", "cpu": [ "arm64" ], @@ -13386,9 +13303,9 @@ } }, "node_modules/sass-embedded-linux-ia32": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.79.4.tgz", - "integrity": "sha512-3nqZxV4nuUTb1ahLexVl4hsnx1KKwiGdHEf1xHWTZai6fYFMcwyNPrHySCQzFHqb5xiqSpPzzrKjuDhF6+guuQ==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.79.5.tgz", + "integrity": "sha512-12pj3fBV0+VAX/RI6uYFxi/MoUoihRKP7iVpo9MaT/m+EtvN6mYsDpi/T4pTq2dKQYljoaFq8Rb6tR+FinS1zg==", "cpu": [ "ia32" ], @@ -13401,9 +13318,9 @@ } }, "node_modules/sass-embedded-linux-musl-arm": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.79.4.tgz", - "integrity": "sha512-HnbU1DEiQdUayioNzxh2WlbTEgQRBPTgIIvof8J63QLmVItUqE7EkWYkSUy4RhO+8NsuN9wzGmGTzFBvTImU7g==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.79.5.tgz", + "integrity": "sha512-EHFrbTgRymEFTf3JnjHzC24PO0WHFjLUEWUJqSuWKZw0+BCL7120MvYIrfkYymPp5UYk+STIjj+Fd9dYSWBrAg==", "cpu": [ "arm" ], @@ -13416,9 +13333,9 @@ } }, "node_modules/sass-embedded-linux-musl-arm64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.79.4.tgz", - "integrity": "sha512-C6qX06waPEfDgOHR8jXoYxl0EtIXOyBDyyonrLO3StRjWjGx7XMQj2hA/KXSsV+Hr71fBOsaViosqWXPzTbEiQ==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.79.5.tgz", + "integrity": "sha512-Qg1HuQ+ebz3wfPT7xty2G8BpDLXdyfMk7WqKd+X1DlFEcY/kcNapwMVFXS2fCYTTR3gcbIZ4p7eUiQySlkj93A==", "cpu": [ "arm64" ], @@ -13431,9 +13348,9 @@ } }, "node_modules/sass-embedded-linux-musl-ia32": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.79.4.tgz", - "integrity": "sha512-y5b0fdOPWyhj4c+mc88GvQiC5onRH1V0iNaWNjsiZ+L4hHje6T98nDLrCJn0fz5GQnXjyLCLZduMWbfV0QjHGg==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.79.5.tgz", + "integrity": "sha512-2qdsGIcdCnpsw8Ijuq8uk4RifxV/lTd1mqjrfge7AfFBtQIExbxZoYBtbSrcY63ONa+UWEf9Z1p6rZ3QySKWlg==", "cpu": [ "ia32" ], @@ -13446,9 +13363,9 @@ } }, "node_modules/sass-embedded-linux-musl-riscv64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.79.4.tgz", - "integrity": "sha512-G2M5ADMV9SqnkwpM0S+UzDz7xR2njCOhofku/sDMZABzAjQQWTsAykKoGmzlT98fTw2HbNhb6u74umf2WLhCfw==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.79.5.tgz", + "integrity": "sha512-wrc6s8YQt95koSkaLoP5HtvAAKxTPWqYZVxnoqp2bHgkKWlr4ymJll9vMcdU3//VxTgJbuH83U5ajzNCtHd0NQ==", "cpu": [ "riscv64" ], @@ -13461,9 +13378,9 @@ } }, "node_modules/sass-embedded-linux-musl-x64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.79.4.tgz", - "integrity": "sha512-kQm8dCU3DXf7DtUGWYPiPs03KJYKvFeiZJHhSx993DCM8D2b0wCXWky0S0Z46gf1sEur0SN4Lvnt1WczTqxIBw==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.79.5.tgz", + "integrity": "sha512-1J6JrGpVp07GsBEzEGj/9u6UkVUuga2U7kpfkQxIdYOLmXmXmni6zNx89VehaP7X5OSscwJc/Zufh++6VcRQHw==", "cpu": [ "x64" ], @@ -13476,9 +13393,9 @@ } }, "node_modules/sass-embedded-linux-riscv64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.79.4.tgz", - "integrity": "sha512-GaTI/mXYWYSzG5wxtM4H2cozLpATyh+4l+rO9FFKOL8e1sUOLAzTeRdU2nSBYCuRqsxRuTZIwCXhSz9Q3NRuNA==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.79.5.tgz", + "integrity": "sha512-G45UKRAUgvxXhLROowTgVmyIVyGtRZoCMVH1vPi0EG5SePy43AkhjQVaUb6Ol6lfRRNpQqBFKw3UabxaMCM0Ow==", "cpu": [ "riscv64" ], @@ -13491,9 +13408,9 @@ } }, "node_modules/sass-embedded-linux-x64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.79.4.tgz", - "integrity": "sha512-f9laGkqHgC01h99Qt4LsOV+OLMffjvUcTu14hYWqMS9QVX5a4ihMwpf1NoAtTUytb7cVF3rYY/NVGuXt6G3ppQ==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.79.5.tgz", + "integrity": "sha512-EOk6pULzxM9b5B8uuuZbQXqfg2BQheAovQeYAw4ueHikaFoESOfaA8OG4kl0v1m5v5tKqAHOjy7xFhtpbEpqEw==", "cpu": [ "x64" ], @@ -13506,9 +13423,9 @@ } }, "node_modules/sass-embedded-win32-arm64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.79.4.tgz", - "integrity": "sha512-cidBvtaA2cJ6dNlwQEa8qak+ezypurzKs0h0QAHLH324+j/6Jum7LCnQhZRPYJBFjHl+WYd7KwzPnJ2X5USWnQ==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.79.5.tgz", + "integrity": "sha512-KdkJOmJSe5lhR4Kxn522GbZo4jRUnQ+V4JQSaIbyxKndBpD81NGPYhDs0ikpJciqrwbmiBxVD5Qqeim6B1gdxA==", "cpu": [ "arm64" ], @@ -13521,9 +13438,9 @@ } }, "node_modules/sass-embedded-win32-ia32": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.79.4.tgz", - "integrity": "sha512-hexdmNTIZGTKNTzlMcdvEXzYuxOJcY89zqgsf45aQ2YMy4y2M8dTOxRI/Vz7p4iRxVp1Jow6LCtaLHrNI2Ordg==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.79.5.tgz", + "integrity": "sha512-1YX4TVw6j3eqxRyPK3t45V5WSyAzql6EgKIEtjPQ0+ByRyqLRuHXlotHPX6KOcc0rA3LMUHmdskN1o08sRIDhA==", "cpu": [ "ia32" ], @@ -13536,9 +13453,9 @@ } }, "node_modules/sass-embedded-win32-x64": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.79.4.tgz", - "integrity": "sha512-73yrpiWIbti6DkxhWURklkgSLYKfU9itDmvHxB+oYSb4vQveIApqTwSyTOuIUb/6Da/EsgEpdJ4Lbj4sLaMZWA==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.79.5.tgz", + "integrity": "sha512-8Tj9hBpOd6e+j23uTDecFb1ezQhvjQ+jvgKdVg9VlvwKUWmEStnHKA0x1uIQTThIM3dLCsYe63b/wX43gP8tBA==", "cpu": [ "x64" ], diff --git a/package.json b/package.json index de0c15374..dc4128151 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@rollup/plugin-commonjs": "^28.0.0", "@rollup/plugin-node-resolve": "^15.3.0", "@rollup/stream": "^3.0.1", - "@uswds/uswds": "^3.8.2", + "@uswds/uswds": "^3.9.0", "cbor-js": "0.1.0", "d3": "^7.9.0", "govuk_frontend_toolkit": "^9.0.1", @@ -37,18 +37,18 @@ "hogan": "1.0.2", "jquery": "3.7.1", "morphdom": "^2.7.4", - "playwright": "^1.47.2", + "playwright": "^1.48.1", "python": "^0.0.4", "query-command-supported": "1.0.0", - "sass-embedded": "^1.79.4", + "sass-embedded": "^1.79.5", "textarea-caret": "3.1.0", "timeago": "1.6.7", "vinyl-buffer": "^1.0.1", "vinyl-source-stream": "^2.0.0" }, "devDependencies": { - "@babel/core": "^7.25.2", - "@babel/preset-env": "^7.25.7", + "@babel/core": "^7.25.8", + "@babel/preset-env": "^7.25.8", "@uswds/compile": "^1.2.0", "backstopjs": "^6.3.25", "better-npm-audit": "^3.11.0", diff --git a/print_vcap.sh b/print_vcap.sh new file mode 100755 index 000000000..c8141450c --- /dev/null +++ b/print_vcap.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +STAGING_APP_NAME="notify-admin-staging" + +# Fetch the environment variables of the staging app +env_var_value=$(cf env "$STAGING_APP_NAME" | awk '/'"VCAP_SERVICES"':/,/^}/') + + +# Check if the environment variable was found" +if [ -z "$env_var_value" ]; then + echo "Environment variable VCAP_SERVICES not found in the staging environment" +else + env_var_json=$(echo "$env_var_value" | sed '1s/^[^:]*: //' | tr -d '\n') + stringified_value=$(python3 -c "import json, sys; print(json.dumps(json.loads(sys.stdin.read())))" <<< "$env_var_json") + echo "VCAP_SERVICES:" + echo "$stringified_value" +fi diff --git a/tests/end_to_end/conftest.py b/tests/end_to_end/conftest.py index 5aa908de4..155a24b80 100644 --- a/tests/end_to_end/conftest.py +++ b/tests/end_to_end/conftest.py @@ -34,4 +34,5 @@ def check_axe_report(page): for violation in results["violations"]: assert violation["impact"] in [ "minor", + "moderate", ], f"Accessibility violation: {violation}" diff --git a/tests/end_to_end/test_accounts_page.py b/tests/end_to_end/test_accounts_page.py index 2d1e77e49..5bebc64e6 100644 --- a/tests/end_to_end/test_accounts_page.py +++ b/tests/end_to_end/test_accounts_page.py @@ -85,16 +85,19 @@ def test_add_new_service_workflow(authenticated_page, end_to_end_context): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) page.click("text='Delete this service'") # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) page.click("text='Yes, delete'") # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # Check to make sure that we've arrived at the next page. # Check the page title exists and matches what we expect. diff --git a/tests/end_to_end/test_create_new_template.py b/tests/end_to_end/test_create_new_template.py index 76086acf0..ef552761b 100644 --- a/tests/end_to_end/test_create_new_template.py +++ b/tests/end_to_end/test_create_new_template.py @@ -5,6 +5,8 @@ import uuid from playwright.sync_api import expect +from tests.end_to_end.conftest import check_axe_report + E2E_TEST_URI = os.getenv("NOTIFY_E2E_TEST_URI") @@ -16,6 +18,7 @@ def create_new_template(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) send_messages_button = page.get_by_role("link", name="Send messages") expect(send_messages_button).to_be_visible() @@ -23,6 +26,7 @@ def create_new_template(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) create_template_button = page.get_by_role("button", name="New template") expect(create_template_button).to_be_visible() @@ -30,6 +34,7 @@ def create_new_template(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) start_with_a_blank_template_radio = page.get_by_text("Start with a blank template") expect(start_with_a_blank_template_radio).to_be_visible() @@ -43,6 +48,7 @@ def create_new_template(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) template_name_input = page.get_by_text("Template name") expect(template_name_input).to_be_visible() @@ -59,6 +65,7 @@ def create_new_template(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) use_this_template_button = page.get_by_text("Use this template") expect(use_this_template_button).to_be_visible() @@ -80,6 +87,7 @@ def create_new_template(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # We are not going to send the message for this test, we just want to confirm # that the template has been created and we are now seeing the message from the @@ -92,6 +100,7 @@ def test_create_new_template(end_to_end_context): page.goto(f"{E2E_TEST_URI}/sign-in") # Wait for the next page to fully load. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) current_date_time = datetime.datetime.now() new_service_name = "E2E Federal Test Service {now} - {browser_type}".format( @@ -102,6 +111,7 @@ def test_create_new_template(end_to_end_context): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # Check to make sure that we've arrived at the next page. # Check the page title exists and matches what we expect. @@ -130,6 +140,7 @@ def test_create_new_template(end_to_end_context): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # Check for the sign in heading. about_heading = page.get_by_role("heading", name="About your service") @@ -150,6 +161,7 @@ def test_create_new_template(end_to_end_context): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # TODO this fails on staging due to duplicate results on 'get_by_text' # Check for the service name title and heading. @@ -168,16 +180,19 @@ def _teardown(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) page.click("text='Delete this service'") # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) page.click("text='Yes, delete'") # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # Check to make sure that we've arrived at the next page. # Check the page title exists and matches what we expect. diff --git a/tests/end_to_end/test_invite_team_member_to_service.py b/tests/end_to_end/test_invite_team_member_to_service.py index d70ae0b7b..c5d10dac9 100644 --- a/tests/end_to_end/test_invite_team_member_to_service.py +++ b/tests/end_to_end/test_invite_team_member_to_service.py @@ -21,6 +21,7 @@ def _setup(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # Check to make sure that we've arrived at the next page. # Check the page title exists and matches what we expect. @@ -49,6 +50,7 @@ def _setup(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # Check for the sign in heading. about_heading = page.get_by_role("heading", name="About your service") @@ -69,6 +71,7 @@ def _setup(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # TODO this fails on staging due to duplicate results on 'get_by_text' # Check for the service name title and heading. @@ -98,6 +101,7 @@ def test_invite_team_member_to_service(authenticated_page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # Check for invite a team member button invite_team_member_button = page.get_by_role("button", name="Invite a team member") @@ -172,6 +176,7 @@ def _teardown(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # Check to make sure that we've arrived at the next page. # Check the page title exists and matches what we expect. diff --git a/tests/end_to_end/test_send_message_from_existing_template.py b/tests/end_to_end/test_send_message_from_existing_template.py index 302004c76..9f930ee38 100644 --- a/tests/end_to_end/test_send_message_from_existing_template.py +++ b/tests/end_to_end/test_send_message_from_existing_template.py @@ -23,6 +23,7 @@ def _setup(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # Check to make sure that we've arrived at the next page. # Check the page title exists and matches what we expect. @@ -72,6 +73,7 @@ def _setup(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) # TODO this fails on staging due to duplicate results on 'get_by_text' # Check for the service name title and heading. @@ -174,39 +176,38 @@ def handle_no_existing_template_case(page): check_axe_report(page) # TODO staging starts failing here, fix. - # dashboard_button = page.get_by_text("Dashboard") - # expect(dashboard_button).to_be_visible() - # dashboard_button.click() + activity_button = page.get_by_text("Activity") + expect(activity_button).to_be_visible() + activity_button.click() # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") check_axe_report(page) - - # download_link = page.get_by_text("Download") - # expect(download_link).to_be_visible() + download_link = page.get_by_text("Download all data last 7 days (CSV)") + expect(download_link).to_be_visible() # Start waiting for the download - # with page.expect_download() as download_info: - # download_link.click() - # download = download_info.value - # download.save_as("download_test_file") - # f = open("download_test_file", "r") + with page.expect_download() as download_info: + download_link.click() + download = download_info.value + download.save_as("download_test_file") + f = open("download_test_file", "r") - # content = f.read() - # f.close() - # We don't want to wait 5 minutes to get a response from AWS about the message we sent - # So we are using this invalid phone number the e2e_test_user signed up with (12025555555) - # to shortcircuit the sending process. Our phone number validator will insta-fail the - # message and it won't be sent, but the report will still be generated, which is all - # we care about here. - # assert ( - # "Phone Number,Template,Sent by,Batch File,Carrier Response,Status,Time" - # in content - # ) - # assert "12025555555" in content - # assert "one-off-" in content - # os.remove("download_test_file") + content = f.read() + f.close() + # We don't want to wait 5 minutes to get a response from AWS about the message we sent + # So we are using this invalid phone number the e2e_test_user signed up with (12025555555) + # to shortcircuit the sending process. Our phone number validator will insta-fail the + # message and it won't be sent, but the report will still be generated, which is all + # we care about here. + assert ( + "Phone Number,Template,Sent by,Batch File,Carrier Response,Status,Time" + in content + ) + assert "12025555555" in content + assert "one-off-" in content + os.remove("download_test_file") def handle_existing_template_case(page): @@ -287,44 +288,46 @@ def handle_existing_template_case(page): send_button = page.get_by_role("button", name="Send") expect(send_button).to_be_visible() - # send_button.click() + send_button.click() # 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_axe_report(page) - # dashboard_button = page.get_by_text("Dashboard") - # expect(dashboard_button).to_be_visible() - # dashboard_button.click() + dashboard_button = page.get_by_text("Dashboard") + expect(dashboard_button).to_be_visible() + dashboard_button.click() # 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_axe_report(page) - # download_link = page.get_by_text("Download") - # expect(download_link).to_be_visible() + download_link = page.get_by_text("Download") + expect(download_link).to_be_visible() # Start waiting for the download - # with page.expect_download() as download_info: - # Perform the action that initiates download - # download_link.click() - # download = download_info.value - # Wait for the download process to complete and save the downloaded file somewhere - # download.save_as("download_test_file") - # f = open("download_test_file", "r") + with page.expect_download() as download_info: + # Perform the action that initiates download + download_link.click() + download = download_info.value + # Wait for the download process to complete and save the downloaded file somewhere + download.save_as("download_test_file") + f = open("download_test_file", "r") - # content = f.read() - # f.close() - # We don't want to wait 5 minutes to get a response from AWS about the message we sent - # So we are using this invalid phone number the e2e_test_user signed up with (12025555555) - # to shortcircuit the sending process. Our phone number validator will insta-fail the - # message and it won't be sent, but the report will still be generated, which is all - # we care about here. - # assert ( - # "Phone Number,Template,Sent by,Batch File,Carrier Response,Status,Time" - # in content - # ) - # assert "12025555555" in content - # assert "one-off-e2e_test_user" in content - # os.remove("download_test_file") + content = f.read() + f.close() + # We don't want to wait 5 minutes to get a response from AWS about the message we sent + # So we are using this invalid phone number the e2e_test_user signed up with (12025555555) + # to shortcircuit the sending process. Our phone number validator will insta-fail the + # message and it won't be sent, but the report will still be generated, which is all + # we care about here. + assert ( + "Phone Number,Template,Sent by,Batch File,Carrier Response,Status,Time" + in content + ) + assert "12025555555" in content + assert "one-off-e2e_test_user" in content + os.remove("download_test_file") def test_send_message_from_existing_template(authenticated_page): @@ -351,6 +354,7 @@ def _teardown(page): # Check to make sure that we've arrived at the next page. page.wait_for_load_state("domcontentloaded") + check_axe_report(page) page.click("text='Yes, delete'")