diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index cb1062e3c..1dc92d63f 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -41,7 +41,13 @@ jobs: - name: Run js tests run: npm test - name: Run py tests with coverage - run: pipenv run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10 + run: pipenv run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10 --ignore=tests/end_to_end tests/ + - name: Run E2E tests + run: pipenv run pytest -v --browser chromium --browser firefox --browser webkit tests/end_to_end + env: + NOTIFY_STAGING_HTTP_AUTH_PASSWORD: ${{ secrets.NOTIFY_STAGING_HTTP_AUTH_PASSWORD }} + NOTIFY_STAGING_HTTP_AUTH_USER: ${{ secrets.NOTIFY_STAGING_HTTP_AUTH_USER }} + NOTIFY_STAGING_URI: ${{ secrets.NOTIFY_STAGING_URI }} - name: Check coverage threshold run: pipenv run coverage report --fail-under=90 diff --git a/Makefile b/Makefile index 709e8fe02..079c699c5 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ NVMSH := $(shell [ -f "$(HOME)/.nvm/nvm.sh" ] && echo "$(HOME)/.nvm/nvm.sh" || e .PHONY: bootstrap bootstrap: generate-version-file ## Set up everything to run the app pipenv install --dev + pipenv run playwright install --with-deps source $(NVMSH) --no-use && nvm install && npm ci --no-audit source $(NVMSH) && npm run build @@ -54,10 +55,15 @@ py-lint: ## Run python linting scanners .PHONY: py-test py-test: export NEW_RELIC_ENVIRONMENT=test py-test: ## Run python unit tests - pipenv run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10 tests/ - pipenv run coverage report --fail-under=90 + pipenv run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10 --ignore=tests/end_to_end tests/ + pipenv run coverage report --fail-under=96 pipenv run coverage html -d .coverage_cache +.PHONY: e2e-test +e2e-test: export NEW_RELIC_ENVIRONMENT=test +e2e-test: ## Run end-to-end integration tests + pipenv run pytest -v --browser chromium --browser firefox --browser webkit tests/end_to_end + .PHONY: js-lint js-lint: ## Run javascript linting scanners source $(NVMSH) && npm run lint diff --git a/Pipfile b/Pipfile index 4b0e3723f..325e2470d 100644 --- a/Pipfile +++ b/Pipfile @@ -37,6 +37,7 @@ newrelic = "*" flask-talisman = "*" notifications-utils = {editable = true, ref = "main", git = "https://github.com/GSA/notifications-utils.git"} coverage = "*" +pytest-playwright = "*" [dev-packages] isort = "==5.12.0" @@ -47,7 +48,7 @@ pytest-xdist = "==3.3.1" beautifulsoup4 = "==4.12.2" freezegun = "==1.2.2" flake8 = "==6.0.0" -flake8-bugbear = "==23.3.12" +flake8-bugbear = "==23.7.10" flake8-print = "==5.0.0" moto = "~=4.1" requests-mock = "==1.11.0" diff --git a/Pipfile.lock b/Pipfile.lock index 736bf0704..520f7b91c 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e04b206789066f85bd531289b77e299b204d677f52ff65861e7793bcbca053dd" + "sha256": "e6e49305bdee7cdd4605b8f6f7d0135cfe91e0c038abe6efdfa1784194ce3cdd" }, "pipfile-spec": 6, "requires": { @@ -50,19 +50,19 @@ }, "boto3": { "hashes": [ - "sha256:0fe7a35cf0041145c8eefebd3ae2ddf41baed62d7c963e5042b8ed8c297f648f", - "sha256:e24460d50001b517c6734dcf1c879feb43aa2062d88d9bdbb8703c986cb05941" + "sha256:b2d178c8a56fe3e4c9b123dccdff20e9555d12a597b72627fa659aa6295e238a", + "sha256:db6443fd2c65d9f35f671b03bacb0592b62d06884395ed65d75922ccddc34c2e" ], "markers": "python_version >= '3.7'", - "version": "==1.28.11" + "version": "==1.28.13" }, "botocore": { "hashes": [ - "sha256:b17ff973bb70b02b227928c2abe4992f1cfc46d13aee0228516c8f32572b88c6", - "sha256:d3cbffe554c9a1ba2ac6973734c43c21b8e7985a2ac4a4c31a09811b8029445c" + "sha256:78b96afbd88b8bd4c0967611a4cedddd9ea33d8601309dc351f81cbb5479d976", + "sha256:9a5080ea2a444f0447a7a1a79f64252ae2a1417b6c13a54656ee991cb610dd4e" ], "markers": "python_version >= '3.7'", - "version": "==1.31.11" + "version": "==1.31.13" }, "cachetools": { "hashes": [ @@ -556,6 +556,14 @@ "markers": "python_version < '3.10'", "version": "==6.8.0" }, + "iniconfig": { + "hashes": [ + "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", + "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, "itsdangerous": { "hashes": [ "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", @@ -826,6 +834,14 @@ ], "version": "==2.0.3" }, + "packaging": { + "hashes": [ + "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", + "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" + ], + "markers": "python_version >= '3.7'", + "version": "==23.1" + }, "phonenumbers": { "hashes": [ "sha256:89671217c706cbaa3ced101deefafa779836feac3e059434d886ac31f09f32c0", @@ -833,6 +849,27 @@ ], "version": "==8.13.17" }, + "playwright": { + "hashes": [ + "sha256:428a719a6c7e40781c19860ed813840ac2d63678f7587abe12e800ea030d4b7e", + "sha256:4e396853034742b76654cdab27422155d238f46e4dc6369ea75854fafb935586", + "sha256:72e80076e595f5fcd8ebd89bf6635ad78e4bafa633119faed8b2568d17dbd398", + "sha256:84213339f179fd2a70f77ea7faea0616d74871349d556c53a1ecb7dd5097973c", + "sha256:89ca2261bb00b67d3dff97691cf18f4347ee0529a11e431e47df67b703d4d8fa", + "sha256:b7c6ddfca2b141b0385387cc56c125b14ea867902c39e3fc650ddd6c429b17da", + "sha256:ffbb927679b62fad5071439d5fe0840af46ad1844bc44bf80e1a0ad706140c98" + ], + "markers": "python_version >= '3.8'", + "version": "==1.36.0" + }, + "pluggy": { + "hashes": [ + "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849", + "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3" + ], + "markers": "python_version >= '3.7'", + "version": "==1.2.0" + }, "prometheus-client": { "hashes": [ "sha256:21e674f39831ae3f8acde238afd9a27a37d0d2fb5a28ea094f0ce25d2cbf2091", @@ -848,6 +885,13 @@ ], "version": "==2.21" }, + "pyee": { + "hashes": [ + "sha256:2770c4928abc721f46b705e6a72b0c59480c4a69c9a83ca0b00bb994f1ea4b32", + "sha256:9f066570130c554e9cc12de5a9d86f57c7ee47fece163bbdaa3e9c933cfbdfa5" + ], + "version": "==9.0.4" + }, "pyexcel": { "hashes": [ "sha256:ddc6904512bfa2ecda509fb3b58229bb30db14498632fd9e7a5ba7bbfb02ed1b", @@ -942,6 +986,30 @@ "index": "pypi", "version": "==3.6.0" }, + "pytest": { + "hashes": [ + "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32", + "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a" + ], + "markers": "python_version >= '3.7'", + "version": "==7.4.0" + }, + "pytest-base-url": { + "hashes": [ + "sha256:e1e88a4fd221941572ccdcf3bf6c051392d2f8b6cef3e0bc7da95abec4b5346e", + "sha256:ed36fd632c32af9f1c08f2c2835dcf42ca8fcd097d6ed44a09f253d365ad8297" + ], + "markers": "python_version >= '3.7' and python_version < '4.0'", + "version": "==2.0.0" + }, + "pytest-playwright": { + "hashes": [ + "sha256:83a896b1b28bfaa081ca9ea27229a06a114e106e2e62fb3d5f06544748fbc1fe", + "sha256:9bf79c633c97dd1405308b8d3600e6c8c2a200a733e2f36c5a150ba4701936f8" + ], + "index": "pypi", + "version": "==0.3.3" + }, "python-dateutil": { "hashes": [ "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", @@ -966,6 +1034,14 @@ "markers": "python_version >= '3.6'", "version": "==2.0.7" }, + "python-slugify": { + "hashes": [ + "sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395", + "sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27" + ], + "markers": "python_version >= '3.7'", + "version": "==8.0.1" + }, "pytz": { "hashes": [ "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588", @@ -1118,6 +1194,13 @@ ], "version": "==2.0.1" }, + "text-unidecode": { + "hashes": [ + "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", + "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93" + ], + "version": "==1.3" + }, "texttable": { "hashes": [ "sha256:290348fb67f7746931bcdfd55ac7584ecd4e5b0846ab164333f0794b121760f2", @@ -1125,6 +1208,14 @@ ], "version": "==1.6.7" }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version < '3.11'", + "version": "==2.0.1" + }, "typing-extensions": { "hashes": [ "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36", @@ -1215,19 +1306,19 @@ }, "boto3": { "hashes": [ - "sha256:0fe7a35cf0041145c8eefebd3ae2ddf41baed62d7c963e5042b8ed8c297f648f", - "sha256:e24460d50001b517c6734dcf1c879feb43aa2062d88d9bdbb8703c986cb05941" + "sha256:b2d178c8a56fe3e4c9b123dccdff20e9555d12a597b72627fa659aa6295e238a", + "sha256:db6443fd2c65d9f35f671b03bacb0592b62d06884395ed65d75922ccddc34c2e" ], "markers": "python_version >= '3.7'", - "version": "==1.28.11" + "version": "==1.28.13" }, "botocore": { "hashes": [ - "sha256:b17ff973bb70b02b227928c2abe4992f1cfc46d13aee0228516c8f32572b88c6", - "sha256:d3cbffe554c9a1ba2ac6973734c43c21b8e7985a2ac4a4c31a09811b8029445c" + "sha256:78b96afbd88b8bd4c0967611a4cedddd9ea33d8601309dc351f81cbb5479d976", + "sha256:9a5080ea2a444f0447a7a1a79f64252ae2a1417b6c13a54656ee991cb610dd4e" ], "markers": "python_version >= '3.7'", - "version": "==1.31.11" + "version": "==1.31.13" }, "cachecontrol": { "extras": [ @@ -1476,11 +1567,11 @@ }, "flake8-bugbear": { "hashes": [ - "sha256:beb5c7efcd7ccc2039ef66a77bb8db925e7be3531ff1cb4d0b7030d0e2113d72", - "sha256:e3e7f74c8a49ad3794a7183353026dabd68c74030d5f46571f84c1fb0eb79363" + "sha256:0ebdc7d8ec1ca8bd49347694562381f099f4de2f8ec6bda7a7dca65555d9e0d4", + "sha256:d99d005114020fbef47ed5e4aebafd22f167f9a0fbd0d8bf3c9e90612cb25c34" ], "index": "pypi", - "version": "==23.3.12" + "version": "==23.7.10" }, "flake8-print": { "hashes": [ @@ -1845,7 +1936,7 @@ "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32", "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a" ], - "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==7.4.0" }, "pytest-env": { diff --git a/README.md b/README.md index bbb298684..202b69e17 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,9 @@ The [Notify API](https://github.com/GSA/notifications-api) provides the UI's bac ### Common steps -1. Install pre-requisites for setup: +1. Install pre-requisites for setup (on a Mac): + - Install XCode or at least the XCode Command Line Tools + - [Homebrew](https://brew.sh/) (follow instructions on page) - [jq](https://stedolan.github.io/jq/): `brew install jq` - [terraform](https://www.terraform.io/): `brew install terraform` or `brew install tfenv` and use `tfenv` to install `terraform ~> 1.4.0` - [cf-cli@8](https://docs.cloudfoundry.org/cf-cli/install-go-cli.html): `brew install cloudfoundry/tap/cf-cli@8` @@ -44,6 +46,23 @@ The [Notify API](https://github.com/GSA/notifications-api) provides the UI's bac `make bootstrap` + If you run into certificate errors at the `playwright install` step, try doing this: + + 1. Run `brew --prefix` to see Homebrew's root directory + + 1. Create or modify the local `.env` file in the project and add this line: + + `NODE_EXTRA_CA_CERTS=/CHANGE-TO-HOMEBREW-INSTALL-PATH/etc/ca-certificates/cert.pem` + + Make sure to change `CHANGE-TO-HOMEBREW-INSTALL-PATH` to the path given by `brew --prefix` in the step above. + For example, if `brew --prefix` gave `/opt/homebrew` as output, then the line would look like this: + + `NODE_EXTRA_CA_CERTS=/opt/homebrew/etc/ca-certificates/cert.pem` + + 1. Save the changes to the `.env` file + + 1. Run `make bootstrap` again + 1. Run the Flask server `make run-flask` diff --git a/app/assets/error_pages/5xx.html b/app/assets/error_pages/5xx.html index a6bf68571..fcf9db915 100644 --- a/app/assets/error_pages/5xx.html +++ b/app/assets/error_pages/5xx.html @@ -4,7 +4,7 @@