Clean up state of E2E tests

This changeset takes care of leaving E2E tests in a clean state so that
we can revisit the work later.  The efforts to add authentication
support did not pan out, so we will go a different route in the near
future.

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
This commit is contained in:
Carlo Costino
2023-08-18 16:20:57 -04:00
parent f47deb1f99
commit d79e15c2a1
10 changed files with 422 additions and 175 deletions

View File

@@ -47,9 +47,12 @@ jobs:
- 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 }}
NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
NOTIFY_E2E_TEST_HTTP_AUTH_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_HTTP_AUTH_PASSWORD }}
NOTIFY_E2E_TEST_HTTP_AUTH_USER: ${{ secrets.NOTIFY_E2E_TEST_HTTP_AUTH_USER }}
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
NOTIFY_E2E_TEST_URI: ${{ secrets.NOTIFY_E2E_TEST_URI }}
- name: Check coverage threshold
run: pipenv run coverage report --fail-under=90

3
.gitignore vendored
View File

@@ -123,3 +123,6 @@ app/templates/vendor
secrets.auto.tfvars
terraform.tfstate
terraform.tfstate.backup
# Playwright
playwright/

View File

@@ -76,7 +76,9 @@ dead-code:
.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
rm -rf playwright
mkdir -p playwright/.auth
pipenv run pytest -vv --browser chromium --browser firefox --browser webkit tests/end_to_end
.PHONY: js-lint
js-lint: ## Run javascript linting scanners

View File

@@ -36,7 +36,6 @@ newrelic = "*"
flask-talisman = "*"
notifications-utils = {editable = true, ref = "main", git = "https://github.com/GSA/notifications-utils.git"}
coverage = "*"
pytest-playwright = "*"
vulture = "==2.7"
radon = "==6.0.1"

197
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "9fc8c5c0307fc5824eaa56ed470c0495229b902957ceeb1a1ac41fbb2d020b4e"
"sha256": "199a6207203e5f15979e65959f2521226ce0d817f9530cc1dacf688e864ded4f"
},
"pipfile-spec": 6,
"requires": {
@@ -50,19 +50,19 @@
},
"boto3": {
"hashes": [
"sha256:b505faa126db84e226f6f8d242a798fae30a725f0cac8a76c6aca9ace4e8eb28",
"sha256:ed787f250ce2562c7744395bdf32b5a7bc9184126ef50a75e97bcb66043dccf3"
"sha256:07997e299e7b87afbbb25dc9de677017eafbd96b4f1b81e931d5127716dc6dd1",
"sha256:fafc0eda7ebe7878be2ab934558ea1776cbd1bd624ce9e9b827e304d301ccd00"
],
"markers": "python_version >= '3.7'",
"version": "==1.28.32"
"version": "==1.28.33"
},
"botocore": {
"hashes": [
"sha256:7a07d8dc8cc47bf23af39409ada81f388eb78233e1bb2cde0c415756da753664",
"sha256:8992ac186988c4b4cc168e8e479e9472da1442b193c1bf7c9dcd1877ec62d23c"
"sha256:1b76549c45f712ca9734888e60a2ab9c857e6e6025b156b36c344162a7e9d0dc",
"sha256:3fd7cb89cf834b28bc7e8427cb29bb861b10652a3bebe9d0d18d9a2c1e4f3f67"
],
"markers": "python_version >= '3.7'",
"version": "==1.31.32"
"version": "==1.31.33"
},
"cachetools": {
"hashes": [
@@ -235,7 +235,7 @@
"sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac",
"sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"
],
"markers": "python_version >= '3.7'",
"markers": "python_full_version >= '3.7.0'",
"version": "==3.2.0"
},
"click": {
@@ -251,7 +251,7 @@
"sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44",
"sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"
],
"markers": "python_version >= '3.5'",
"markers": "python_version > '3.4'",
"version": "==0.4.6"
},
"coverage": {
@@ -549,14 +549,6 @@
"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",
@@ -758,24 +750,24 @@
},
"newrelic": {
"hashes": [
"sha256:39a699f88042255634c88beff92c2fc10d9d522b6c989f52a6ae098823b51a02",
"sha256:56161cbaa97f93a807db4bd61436ff7757c8a896758d325b708567cca48bfd13",
"sha256:62c558a5dbfa8728cbb0addd199038c0e75feb79282a9e07c272a2acaf250094",
"sha256:6bd507fa91175cc558c810cefe2b7ee20d1143f88a7153e255d49d7ce12cd287",
"sha256:74a7b07153aaac65cefe21183ebbedc5dee7d0cd681ec27c5258499b7a4319b1",
"sha256:7555f4bd91bea441cd2a5dc94848034e4ab2ab068d30905288a789d3214b1a03",
"sha256:885c852dc88d9612fe3de1940246e9200a510a289b0cf56459494782096cdba4",
"sha256:930457ffd0cd5696f75cd0d761e550cecbba2eadc37c0db617c25b7c4a5d19e1",
"sha256:962ac353b66f2827b337af941b86dff2d61d1c7638e5cab950e995e53e52665c",
"sha256:9eb93901fd9caebc7f965812a7dea349d43b44d45d693ffb6ee6c4c40b5de78d",
"sha256:ae9b96bad4f6b92a45fe28d7303b747d36aa2f9ad545fd3fc51f7eac2a45f011",
"sha256:c4d87e6d517f7bc8bf5f982b6260ce1efc642b4fe5b83f23d11a69a9351d83c3",
"sha256:d0c15312c6cd73559f5b01fe018feaf018a4566210338fdc5616dfb4f1ce24f0",
"sha256:e7789b0340d04bbd31c76d42f2d5d064b47f90c09c74978e6175d77bcdd9e226",
"sha256:f9361fc81911e42c272eab640569cbce1849873f5c5c9d74c9a58a3b8bd23d2f"
"sha256:16761522dde9c05146078f7729aefbf05cea7fc42c703c0b756618168939f872",
"sha256:18dae1606f79a92c21967d11cbd4f42c03eb5b04b2f6b86a954b5e6a736113d7",
"sha256:1c9463dd1b1407ade27a4c7ebb2137b77c51e23498cec7ee2671fff3fd33fd73",
"sha256:2e70371c8b5fcfca6e7f57395f205ed6b38228b4a5594f49f7aae4d978aa9eef",
"sha256:313aa30918e54bfc226229d3b4fa7f8879fa5beab98ed46218ef36265b7205e5",
"sha256:41cf36017b1b54187c5b422fff32528abc4f2043227397f7a242864c31939c37",
"sha256:59cb3da7e5dd526e7f8696a5ed704d06c43bc2d4da840897534e9a51da7266eb",
"sha256:5d5a3921c73913ea91407a6b2cda4e68aa3e52d7ceba77365d9b090b184f7e15",
"sha256:5e32b7c92ed8922df599769270dbc5a322ca78e29b5c89083b2b39af80a44361",
"sha256:68fec20006b312a3c93de82e91cb39be44ac73ba08d15552fff5206dfabda227",
"sha256:7a649068a0cf5c4a7ba0f9739e52f1ad9d2882aafcfe3361fd264cbb975c5318",
"sha256:b8954dcfa78cb5d0398490938f3a84f29d53554bb498bb50bf232715a3577a68",
"sha256:cc169ad19cb3b44ec028df08d172327b72d0137d62935c87cbbef173f710c2ed",
"sha256:d95eade883fb3aa0a2a2d04f1dd85f0df04d31bfc32f78139fe91c0362c1987e",
"sha256:fc4e0fe04ad4f1fc19cb86844d73e834d99dd613dd8a4f2e7be204193d5b8dc8"
],
"index": "pypi",
"version": "==8.10.1"
"version": "==8.11.0"
},
"notifications-python-client": {
"hashes": [
@@ -834,14 +826,6 @@
],
"version": "==2.0.3"
},
"packaging": {
"hashes": [
"sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61",
"sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"
],
"markers": "python_version >= '3.7'",
"version": "==23.1"
},
"phonenumbers": {
"hashes": [
"sha256:38180247697240ccedd74dec4bfbdbc22bb108b9c5f991f270ca3e41395e6f96",
@@ -849,27 +833,6 @@
],
"version": "==8.13.19"
},
"playwright": {
"hashes": [
"sha256:41f0280472af94c426e941f6a969ff6a7ea156dc15fd01d09ac4b8f092e2346e",
"sha256:428fdf9bfff586b73f96df53692d50d422afb93ca4650624f61e8181f548fed2",
"sha256:678b9926be2df06321d11a525d4bf08d9f4a5b151354a3b82fe2ac14476322d5",
"sha256:68d56efe5ce916bab349177e90726837a6f0cae77ebd6a5200f5333b787b25fb",
"sha256:8b5d96aae54289129ab19d3d0e2e431171ae3e5d88d49a10900dcbe569a27d43",
"sha256:b476f63251876f1625f490af8d58ec0db90b555c623b7f54105f91d33878c06d",
"sha256:b574889ef97b7f44a633aa10d72b8966a850a4354d915fd0bc7e8658e825dd63"
],
"markers": "python_version >= '3.8'",
"version": "==1.37.0"
},
"pluggy": {
"hashes": [
"sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849",
"sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"
],
"markers": "python_version >= '3.7'",
"version": "==1.2.0"
},
"prometheus-client": {
"hashes": [
"sha256:21e674f39831ae3f8acde238afd9a27a37d0d2fb5a28ea094f0ce25d2cbf2091",
@@ -885,13 +848,6 @@
],
"version": "==2.21"
},
"pyee": {
"hashes": [
"sha256:2770c4928abc721f46b705e6a72b0c59480c4a69c9a83ca0b00bb994f1ea4b32",
"sha256:9f066570130c554e9cc12de5a9d86f57c7ee47fece163bbdaa3e9c933cfbdfa5"
],
"version": "==9.0.4"
},
"pyexcel": {
"hashes": [
"sha256:ddc6904512bfa2ecda509fb3b58229bb30db14498632fd9e7a5ba7bbfb02ed1b",
@@ -978,36 +934,12 @@
"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:68dd0069e2dbf8e6024c6237d51d0277626687d94ba0dd387cea2a94ae4005b9",
"sha256:9e9622e5507f8d27a3c7fd07aadcf43122f0086b69d1b3e6728995c8dbc0e44f"
],
"index": "pypi",
"version": "==0.4.2"
},
"python-dateutil": {
"hashes": [
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
"version": "==2.8.2"
},
"python-dotenv": {
@@ -1026,14 +958,6 @@
"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",
@@ -1185,7 +1109,7 @@
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
"version": "==1.16.0"
},
"smartypants": {
@@ -1194,13 +1118,6 @@
],
"version": "==2.0.1"
},
"text-unidecode": {
"hashes": [
"sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8",
"sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"
],
"version": "==1.3"
},
"texttable": {
"hashes": [
"sha256:290348fb67f7746931bcdfd55ac7584ecd4e5b0846ab164333f0794b121760f2",
@@ -1208,21 +1125,13 @@
],
"version": "==1.6.7"
},
"tomli": {
"toml": {
"hashes": [
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"markers": "python_version < '3.11'",
"version": "==2.0.1"
},
"typing-extensions": {
"hashes": [
"sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36",
"sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"
],
"markers": "python_version >= '3.7'",
"version": "==4.7.1"
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
"version": "==0.10.2"
},
"urllib3": {
"hashes": [
@@ -1232,6 +1141,14 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.26.16"
},
"vulture": {
"hashes": [
"sha256:67fb80a014ed9fdb599dd44bb96cb54311032a104106fc2e706ef7a6dad88032",
"sha256:bccc51064ed76db15a6b58277cea8885936af047f53d2655fb5de575e93d0bca"
],
"index": "pypi",
"version": "==2.7"
},
"webencodings": {
"hashes": [
"sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
@@ -1306,19 +1223,19 @@
},
"boto3": {
"hashes": [
"sha256:b505faa126db84e226f6f8d242a798fae30a725f0cac8a76c6aca9ace4e8eb28",
"sha256:ed787f250ce2562c7744395bdf32b5a7bc9184126ef50a75e97bcb66043dccf3"
"sha256:07997e299e7b87afbbb25dc9de677017eafbd96b4f1b81e931d5127716dc6dd1",
"sha256:fafc0eda7ebe7878be2ab934558ea1776cbd1bd624ce9e9b827e304d301ccd00"
],
"markers": "python_version >= '3.7'",
"version": "==1.28.32"
"version": "==1.28.33"
},
"botocore": {
"hashes": [
"sha256:7a07d8dc8cc47bf23af39409ada81f388eb78233e1bb2cde0c415756da753664",
"sha256:8992ac186988c4b4cc168e8e479e9472da1442b193c1bf7c9dcd1877ec62d23c"
"sha256:1b76549c45f712ca9734888e60a2ab9c857e6e6025b156b36c344162a7e9d0dc",
"sha256:3fd7cb89cf834b28bc7e8427cb29bb861b10652a3bebe9d0d18d9a2c1e4f3f67"
],
"markers": "python_version >= '3.7'",
"version": "==1.31.32"
"version": "==1.31.33"
},
"cachecontrol": {
"extras": [
@@ -1486,7 +1403,7 @@
"sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac",
"sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"
],
"markers": "python_version >= '3.7'",
"markers": "python_full_version >= '3.7.0'",
"version": "==3.2.0"
},
"cryptography": {
@@ -1799,7 +1716,7 @@
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
],
"markers": "python_full_version >= '3.6.0'",
"markers": "python_version >= '3.6'",
"version": "==0.7.0"
},
"mdurl": {
@@ -1812,11 +1729,11 @@
},
"moto": {
"hashes": [
"sha256:545afeb4df94dfa730e2d7e87366dc26b4a33c2891f462cbb049f040c80ed1ec",
"sha256:7d3bd748a34641715ba469c761f72fb8ec18f349987c98f5a0f9be85a07a9911"
"sha256:00fbae396fc48c3596e47b4e3267c1a41ca01c968de023beb68e774c63910b58",
"sha256:e4835912f05627b6a53b938562b717122230fb038d023819133f8526f60ed0a7"
],
"index": "pypi",
"version": "==4.1.14"
"version": "==4.2.0"
},
"msgpack": {
"hashes": [
@@ -2022,7 +1939,7 @@
"sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32",
"sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"
],
"markers": "python_version >= '3.7'",
"index": "pypi",
"version": "==7.4.0"
},
"pytest-base-url": {
@@ -2070,7 +1987,7 @@
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
"version": "==2.8.2"
},
"python-slugify": {
@@ -2156,7 +2073,7 @@
"sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808",
"sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39"
],
"markers": "python_version >= '3.7'",
"markers": "python_full_version >= '3.7.0'",
"version": "==13.5.2"
},
"s3transfer": {
@@ -2172,7 +2089,7 @@
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
"version": "==1.16.0"
},
"smmap": {
@@ -2180,7 +2097,7 @@
"sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94",
"sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"
],
"markers": "python_full_version >= '3.6.0'",
"markers": "python_version >= '3.6'",
"version": "==5.0.0"
},
"sortedcontainers": {
@@ -2218,7 +2135,7 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
"version": "==0.10.2"
},
"tomli": {

View File

@@ -33,7 +33,8 @@ 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:
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`
@@ -56,26 +57,31 @@ your environment is set up and configured as outlined in the README.
At your shell in the project root folder, run the following commands:
```sh
pipenv install pytest-playwright
pipenv install pytest-playwright --dev
pipenv run playwright install --with-deps
```
This will install Playwright and its `pytest` plugin, then the
additional dependencies that Playwright requires.
See more details on the [Playwright for Python Installation page](https://playwright.dev/python/docs/intro).
See more details on the
[Playwright for Python Installation page](https://playwright.dev/python/docs/intro).
## Local Configuration
In order to run the E2E tests successfully on your local machine, you'll also
need to make sure you have a `.env` file in the root project folder, and that it
has at least these environment variables set in it:
In order to run the E2E tests successfully on your local machine, you'll
also need to make sure you have a `.env` file in the root project folder
and that it has at least these environment variables set in it:
```
NOTIFY_STAGING_URI
NOTIFY_STAGING_HTTP_AUTH_USER
NOTIFY_STAGING_HTTP_AUTH_PASSWORD
# E2E Test Configuration - only set for the Admin site.
NOTIFY_E2E_TEST_URI
NOTIFY_E2E_TEST_HTTP_AUTH_USER # This is optional
NOTIFY_E2E_TEST_HTTP_AUTH_PASSWORD # This is optional
NOTIFY_E2E_TEST_EMAIL
NOTIFY_E2E_TEST_PASSWORD
NOTIFY_E2E_AUTH_STATE_PATH
```
This file is **not** checked into source control and is configured to be
@@ -99,7 +105,8 @@ tests run in multiple headless browsers.
## How to Create and Maintain E2E Tests
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).
written as `pytest` scripts using
[Playwright's Python Framework](https://playwright.dev/python/docs/writing-tests).
## Maintaining E2E Tests with GitHub
@@ -117,4 +124,22 @@ This is done for a couple of reasons:
- Allows us to configure E2E tests separately
The environment variables are managed as a part of the GitHub
repository settings.
environment and repository settings.
### E2E Environment Variable Management
These are the E2E test environment variables that must be set:
```
NOTIFY_E2E_TEST_URI
NOTIFY_E2E_TEST_HTTP_AUTH_USER # This is optional
NOTIFY_E2E_TEST_HTTP_AUTH_PASSWORD # This is optional
NOTIFY_E2E_TEST_EMAIL
NOTIFY_E2E_TEST_PASSWORD
NOTIFY_E2E_AUTH_STATE_PATH
```
These are only set for the Admin site in GitHub, but must be set
for both GitHub Actions and Dependabot for the same reason as
the MFA environment variables.

View File

@@ -14,6 +14,17 @@ NODE_VERSION=16.15.1
#############################################################
# E2E Testing
NOTIFY_E2E_TEST_URI=http://localhost:6012/
#NOTIFY_E2E_TEST_HTTP_AUTH_USER="this is optional"
#NOTIFY_E2E_TEST_HTTP_AUTH_PASSWORD="this is optional - don't write secrets to the sample file"
NOTIFY_E2E_TEST_EMAIL=fake.user@example.com
NOTIFY_E2E_TEST_PASSWORD="don't write secrets to the sample file"
NOTIFY_E2E_AUTH_STATE_PATH=playwright/.auth/
#############################################################
# Local Docker setup
# API_HOST_NAME=http://dev:6011
# REDIS_URL=redis://adminredis:6379/0

View File

@@ -1,6 +1,7 @@
import copy
import json
import os
import re
from contextlib import contextmanager
from datetime import date, datetime, timedelta
from unittest.mock import Mock, PropertyMock
@@ -3347,13 +3348,94 @@ 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_auth_context(browser):
def end_to_end_context(browser):
# Create a context with HTTP Authentication credentials for Playwright E2E
# tests.
context = browser.new_context(http_credentials={
'username': os.environ.get('NOTIFY_STAGING_HTTP_AUTH_USER'),
'password': os.environ.get('NOTIFY_STAGING_HTTP_AUTH_PASSWORD'),
})
# tests, if the environment variables exist.
if os.getenv('NOTIFY_E2E_TEST_HTTP_AUTH_USER'):
context = browser.new_context(http_credentials={
'username': os.getenv('NOTIFY_E2E_TEST_HTTP_AUTH_USER'),
'password': os.getenv('NOTIFY_E2E_TEST_HTTP_AUTH_PASSWORD'),
})
else:
context = browser.new_context()
yield 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)
yield context

View File

@@ -0,0 +1,122 @@
import datetime
import os
import re
import pytest
from playwright.sync_api import expect
@pytest.mark.skip(reason='Not authenticating test users.')
def test_accounts_page(end_to_end_authenticated_context):
# Open a new page and go to the staging site.
page = end_to_end_authenticated_context.new_page()
accounts_uri = '{}accounts'.format(os.getenv('NOTIFY_E2E_TEST_URI'))
page.goto(accounts_uri)
# Check to make sure that we've arrived at the next page.
page.wait_for_load_state('domcontentloaded')
# Check to make sure that we've arrived at the next page.
# Check the page title exists and matches what we expect.
expect(page).to_have_title(re.compile('Choose service'))
# Check for the sign in heading.
sign_in_heading = page.get_by_role('heading', name='Choose service')
expect(sign_in_heading).to_be_visible()
# Retrieve some prominent elements on the page for testing.
add_service_button = page.get_by_role(
'button',
name=re.compile('Add a new service')
)
expect(add_service_button).to_be_visible()
@pytest.mark.skip(reason='Not authenticating test users.')
def test_add_new_service_workflow(end_to_end_authenticated_context):
# Prepare for adding a new service later in the test.
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_authenticated_context.browser.browser_type.name
)
# Open a new page and go to the staging site.
page = end_to_end_authenticated_context.new_page()
accounts_uri = '{}accounts'.format(os.getenv('NOTIFY_E2E_TEST_URI'))
page.goto(accounts_uri)
# Check to make sure that we've arrived at the next page.
page.wait_for_load_state('domcontentloaded')
# Check to make sure that we've arrived at the next page.
# Check the page title exists and matches what we expect.
expect(page).to_have_title(re.compile('Choose service'))
# Check for the sign in heading.
sign_in_heading = page.get_by_role('heading', name='Choose service')
expect(sign_in_heading).to_be_visible()
# Retrieve some prominent elements on the page for testing.
add_service_button = page.get_by_role(
'button',
name=re.compile('Add a new service')
)
expect(add_service_button).to_be_visible()
existing_service_link = page.get_by_role(
'link',
name=new_service_name
)
# Check to see if the service was already created - if so, we should fail.
# TODO: Figure out how to make this truly isolated, and/or work in a
# delete service workflow.
expect(existing_service_link).to_have_count(0)
# Click on add a new service.
add_service_button.click()
# Check to make sure that we've arrived at the next page.
page.wait_for_load_state('domcontentloaded')
# Check for the sign in heading.
about_heading = page.get_by_role('heading', name='About your service')
expect(about_heading).to_be_visible()
# 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)
federal_radio_button.click()
# Click on add service.
add_service_button.click()
# Check to make sure that we've arrived at the next page.
page.wait_for_load_state('domcontentloaded')
# Check for the service name title and heading.
service_heading = page.get_by_text(new_service_name)
expect(service_heading).to_be_visible()
expect(page).to_have_title(re.compile(new_service_name))

View File

@@ -1,13 +1,17 @@
import os
import re
import pytest
from playwright.sync_api import expect
def test_landing_page(end_to_end_auth_context):
def test_landing_page(end_to_end_context):
# Open a new page and go to the staging site.
page = end_to_end_auth_context.new_page()
page.goto(os.environ.get('NOTIFY_STAGING_URI'))
page = end_to_end_context.new_page()
page.goto(os.getenv('NOTIFY_E2E_TEST_URI'))
# Check to make sure that we've arrived at the next page.
page.wait_for_load_state('domcontentloaded')
# Check the page title exists and matches what we expect.
expect(page).to_have_title(re.compile('Notify.gov'))
@@ -50,22 +54,22 @@ def test_landing_page(end_to_end_auth_context):
).to_be_visible()
def test_sign_in_page(end_to_end_auth_context):
@pytest.mark.skip(reason='Not authenticating test users.')
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_auth_context.new_page()
page.goto(os.environ.get('NOTIFY_STAGING_URI'))
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.
sign_in_button.click()
# Check to make sure that we've arrived at the next page.
page.wait_for_load_state('domcontentloaded')
# Check the page title exists and matches what we expect.
# NOTE: The dash is a special character! It had to be copied from
# the template itself.
# TODO: Improve this check, or change it so no special character is
# needed. Better yet, fix the template(s) character too.
expect(page).to_have_title(re.compile('Sign in Notify.gov'))
expect(page).to_have_title(re.compile('Sign in'))
# Check for the sign in heading.
sign_in_heading = page.get_by_role('heading', name='Sign in')
@@ -81,7 +85,10 @@ def test_sign_in_page(end_to_end_auth_context):
email_address_input = page.get_by_label('Email address')
password_input = page.get_by_label('Password')
csrf_token = page.locator('xpath=//input[@name="csrf_token"]')
continue_button = page.get_by_role('button', name=re.compile('Continue'))
continue_button = page.get_by_role(
'button',
name=re.compile('Continue')
)
forgot_password_link = page.get_by_role(
'link',
name='Forgot your password?'
@@ -106,4 +113,80 @@ def test_sign_in_page(end_to_end_auth_context):
'/forgot-password'
)
# TODO: Figure out how to actually sign in...
# 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 the page title exists and matches what we expect.
expect(page).to_have_title(re.compile('Check your phone'))
# Check for the sign in heading.
sign_in_heading = page.get_by_role(
'heading',
name='Check your phone'
)
expect(sign_in_heading).to_be_visible()
# 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')
csrf_token = page.locator('xpath=//input[@name="csrf_token"]')
continue_button = page.get_by_role(
'button',
name=re.compile('Continue')
)
not_received_message_link = page.get_by_role(
'link',
name='Not received a text message?'
)
# Make sure form elements are visible and not visible as expected.
expect(mfa_input).to_be_visible()
expect(continue_button).to_be_visible()
expect(not_received_message_link).to_be_visible()
expect(csrf_token).to_be_hidden()
# Make sure form elements are configured correctly with the right
# attributes.
expect(mfa_input).to_have_attribute('type', 'tel')
expect(mfa_input).to_have_attribute('pattern', '[0-9]*')
expect(csrf_token).to_have_attribute('type', 'hidden')
expect(continue_button).to_have_attribute('type', 'submit')
expect(not_received_message_link).to_have_attribute(
'href',
'/text-not-received'
)
# 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()
# # Check to make sure that we've arrived at the next page.
# page.wait_for_load_state('domcontentloaded')
# # Check that no MFA code error happened.
# code_not_found_error = page.get_by_text('Code not found')
# expect(code_not_found_error).to_have_count(0)
# # Check the page title exists and matches what we expect.
# # This could be either the Dashboard of a service if there is only
# # one, or choosing a service if there are multiple.
# expect(page).to_have_title(re.compile('Dashboard|Choose service'))