Merge pull request #358 from dod-ccpo/end-to-end-test-#160690740
End to end test #160690740
This commit is contained in:
commit
5dd55dea55
3
.gitignore
vendored
3
.gitignore
vendored
@ -44,3 +44,6 @@ config/dev.ini
|
||||
|
||||
# coverage output
|
||||
.coverage
|
||||
|
||||
# selenium testing
|
||||
browserstacklocal
|
||||
|
1
Pipfile
1
Pipfile
@ -34,6 +34,7 @@ factory-boy = "*"
|
||||
pytest-flask = "*"
|
||||
pytest-env = "*"
|
||||
pytest-cov = "*"
|
||||
selenium = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.6.6"
|
||||
|
55
Pipfile.lock
generated
55
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "c95e31f2315762631fcae7253275e46cbca22bbfd4467cf454e74163743c6ae7"
|
||||
"sha256": "1e5e6a695229166aaa5e6c427fed07a903766e9b3d24981a19cc8e5ada8db978"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@ -440,10 +440,10 @@
|
||||
},
|
||||
"colorama": {
|
||||
"hashes": [
|
||||
"sha256:463f8483208e921368c9f306094eb6f725c6ca42b0f97e313cb5d5512459feda",
|
||||
"sha256:48eb22f4f8461b1df5734a074b57042430fb06e1d61bd1e11b078c0fe6d7a1f1"
|
||||
"sha256:a3d89af5db9e9806a779a50296b5fdb466e281147c2c235e8225ecc6dbf7bbf3",
|
||||
"sha256:c9b54bebe91a6a803e0772c8561d53f2926bfeb17cd141fbabcb08424086595c"
|
||||
],
|
||||
"version": "==0.3.9"
|
||||
"version": "==0.4.0"
|
||||
},
|
||||
"coverage": {
|
||||
"hashes": [
|
||||
@ -508,10 +508,10 @@
|
||||
},
|
||||
"faker": {
|
||||
"hashes": [
|
||||
"sha256:74b32991f8e08e4f2f84858b919eca253becfaec4b3fa5fcff7fdbd70d5d78b1",
|
||||
"sha256:c2ce42dd8361e6d392276006d757532562463c8642b1086709584200b7fd7758"
|
||||
"sha256:2621643b80a10b91999925cfd20f64d2b36f20bf22136bbdc749bb57d6ffe124",
|
||||
"sha256:5ed822d31bd2d6edf10944d176d30dc9c886afdd381eefb7ba8b7aad86171646"
|
||||
],
|
||||
"version": "==0.9.1"
|
||||
"version": "==0.9.2"
|
||||
},
|
||||
"flask": {
|
||||
"hashes": [
|
||||
@ -523,10 +523,10 @@
|
||||
},
|
||||
"gitdb2": {
|
||||
"hashes": [
|
||||
"sha256:87783b7f4a8f6b71c7fe81d32179b3c8781c1a7d6fa0c69bff2f315b00aff4f8",
|
||||
"sha256:bb4c85b8a58531c51373c89f92163b92f30f81369605a67cd52d1fc21246c044"
|
||||
"sha256:83361131a1836661a155172932a13c08bda2db3674e4caa32368aa6eb02f38c2",
|
||||
"sha256:e3a0141c5f2a3f635c7209d56c496ebe1ad35da82fe4d3ec4aaa36278d70648a"
|
||||
],
|
||||
"version": "==2.0.4"
|
||||
"version": "==2.0.5"
|
||||
},
|
||||
"gitpython": {
|
||||
"hashes": [
|
||||
@ -684,11 +684,11 @@
|
||||
},
|
||||
"prompt-toolkit": {
|
||||
"hashes": [
|
||||
"sha256:5eff0c9fd652384ecfe730bbcdf3658868725c6928fbf608d9338834d7a974b6",
|
||||
"sha256:81da9ecf6ca6806a549697529af8ec3ac5b739c13ac14607218e650db1b53131",
|
||||
"sha256:c67c1c264d8a0d9e1070e9272bacee00f76c81daab7bc4bf09ff991bd1e224a7"
|
||||
"sha256:646b3401b3b0bb7752100bc9b7aeecb36cb09cdfc63652b5856708b5ba8db7da",
|
||||
"sha256:82766ffd7397e6661465e20bd1390db0781ca4fbbab4cf6c2578cacdd8b09754",
|
||||
"sha256:ccad8461b5d912782726af17122113e196085e7e11d57cf0c9b982bf1ab2c7be"
|
||||
],
|
||||
"version": "==2.0.5"
|
||||
"version": "==2.0.6"
|
||||
},
|
||||
"ptyprocess": {
|
||||
"hashes": [
|
||||
@ -699,10 +699,10 @@
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
"sha256:06a30435d058473046be836d3fc4f27167fd84c45b99704f2fb5509ef61f9af1",
|
||||
"sha256:50402e9d1c9005d759426988a492e0edaadb7f4e68bcddfea586bc7432d009c6"
|
||||
"sha256:bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694",
|
||||
"sha256:e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6"
|
||||
],
|
||||
"version": "==1.6.0"
|
||||
"version": "==1.7.0"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
@ -778,6 +778,14 @@
|
||||
],
|
||||
"version": "==4.2b4"
|
||||
},
|
||||
"selenium": {
|
||||
"hashes": [
|
||||
"sha256:ab192cd046164c40fabcf44b47c66c8b12495142f4a69dcc55ea6eeef096e614",
|
||||
"sha256:fdb6b1143d8899e8a32e358ad05bf5d89a480dbac359dbbd341592aa8696dcd1"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.14.1"
|
||||
},
|
||||
"simplegeneric": {
|
||||
"hashes": [
|
||||
"sha256:dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173"
|
||||
@ -793,10 +801,10 @@
|
||||
},
|
||||
"smmap2": {
|
||||
"hashes": [
|
||||
"sha256:0dd53d991af487f9b22774fa89451358da3607c02b9b886a54736c6a313ece0b",
|
||||
"sha256:dc216005e529d57007ace27048eb336dcecb7fc413cfb3b2f402bb25972b69c6"
|
||||
"sha256:0555a7bf4df71d1ef4218e4807bbf9b201f910174e6e08af2e138d4e517b4dde",
|
||||
"sha256:29a9ffa0497e7f2be94ca0ed1ca1aa3cd4cf25a1f6b4f5f87f74b46ed91d609a"
|
||||
],
|
||||
"version": "==2.0.4"
|
||||
"version": "==2.0.5"
|
||||
},
|
||||
"stevedore": {
|
||||
"hashes": [
|
||||
@ -856,6 +864,13 @@
|
||||
"markers": "python_version < '3.7' and implementation_name == 'cpython'",
|
||||
"version": "==1.1.0"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
|
||||
"sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
|
||||
],
|
||||
"version": "==1.23"
|
||||
},
|
||||
"watchdog": {
|
||||
"hashes": [
|
||||
"sha256:965f658d0732de3188211932aeb0bb457587f04f63ab4c1e33eab878e9de961d"
|
||||
|
25
README.md
25
README.md
@ -131,6 +131,31 @@ To re-run tests each time a file is changed:
|
||||
|
||||
pipenv run ptw
|
||||
|
||||
### Selenium Tests
|
||||
|
||||
Selenium tests rely on BrowserStack. In order to run the Selenium tests
|
||||
locally, you need BrowserStack credentials. The user email and key can
|
||||
be found on the account settings page. To run the selenium tests:
|
||||
|
||||
```
|
||||
BROWSERSTACK_TOKEN=<token> BROWSERSTACK_EMAIL=<email> ./script/selenium_test
|
||||
```
|
||||
|
||||
The selenium tests are in `tests/acceptance`. This directory is ignored by
|
||||
pytest for normal test runs.
|
||||
|
||||
The `selenium_test` script manages the setup of a separate database and
|
||||
launching the BrowserStackLocal client. If you already have the client running
|
||||
locally, you can run the selenium tests with:
|
||||
|
||||
```
|
||||
BROWSERSTACK_TOKEN=<token> BROWSERSTACK_EMAIL=<email> pipenv run pytest tests/acceptance
|
||||
```
|
||||
|
||||
The BrowserStack email is the one associated with the account. The token is
|
||||
available in the BrowserStack profile information page. Go to the dashboard,
|
||||
then "Account" > "Settings", then the token is under "Local Testing".
|
||||
|
||||
## Notes
|
||||
|
||||
Jinja templates are like mustache templates -- add the
|
||||
|
@ -17,4 +17,13 @@ def make_error_pages(app):
|
||||
app.logger.error(e.message)
|
||||
return render_template("error.html", message="Log in Failed"), 401
|
||||
|
||||
@app.errorhandler(Exception)
|
||||
# pylint: disable=unused-variable
|
||||
def exception(e):
|
||||
app.logger.error(e.message)
|
||||
return (
|
||||
render_template("error.html", message="An Unexpected Error Occurred"),
|
||||
500,
|
||||
)
|
||||
|
||||
return app
|
||||
|
3
config/selenium.ini
Normal file
3
config/selenium.ini
Normal file
@ -0,0 +1,3 @@
|
||||
[default]
|
||||
PGDATABASE = atat_selenium
|
||||
CRL_DIRECTORY = tests/fixtures/crl
|
@ -2,4 +2,4 @@
|
||||
norecursedirs = .venv .git node_modules
|
||||
env =
|
||||
D:FLASK_ENV=test
|
||||
addopts = --cov=atst --cov-report term --cov-fail-under 90
|
||||
addopts = --ignore=tests/acceptance/ --cov=atst --cov-report term --cov-fail-under 90
|
||||
|
53
script/selenium_test
Executable file
53
script/selenium_test
Executable file
@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
|
||||
# script/selenium_test: Run selenium tests via BrowserStack
|
||||
|
||||
source "$(dirname "${0}")"/../script/include/global_header.inc.sh
|
||||
|
||||
export FLASK_ENV=selenium
|
||||
|
||||
# create upload directory for app
|
||||
mkdir uploads | true
|
||||
|
||||
# Fetch postgres settings and set them as ENV vars
|
||||
source ./script/get_db_settings
|
||||
|
||||
if [ -n "${PGDATABASE}" ]; then
|
||||
echo "Resetting database ${PGDATABASE}..."
|
||||
# Reset the db
|
||||
reset_db "${PGDATABASE}"
|
||||
else
|
||||
echo "ERROR: RESET_DB is set, but PGDATABASE is not!"
|
||||
echo "Skipping database reset..."
|
||||
fi
|
||||
|
||||
BSL_FILE=BrowserStackLocal
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
BSL_DOWNLOAD="https://www.browserstack.com/browserstack-local/BrowserStackLocal-darwin-x64.zip"
|
||||
else
|
||||
BSL_DOWNLOAD="https://www.browserstack.com/browserstack-local/BrowserStackLocal-linux-x64.zip"
|
||||
fi
|
||||
|
||||
# Fetch BrowserStackLocal script
|
||||
if [ -e "${BSL_FILE}" ]; then
|
||||
echo "BrowserStack file already exists"
|
||||
else
|
||||
echo "downloading BrowserStack file"
|
||||
curl $BSL_DOWNLOAD --output $BSL_FILE.zip
|
||||
unzip $BSL_FILE.zip -d .
|
||||
rm $BSL_FILE.zip
|
||||
chmod u+x $BSL_FILE
|
||||
fi
|
||||
|
||||
# run BrowserStackLocal in the background
|
||||
echo "starting BrowserStack local client..."
|
||||
./$BSL_FILE --key $BROWSERSTACK_TOKEN &
|
||||
BSL_ID=$!
|
||||
trap "kill $BSL_ID" SIGTERM SIGINT EXIT
|
||||
|
||||
# run example selenium script that fetches the home page
|
||||
echo "running selenium tests"
|
||||
pipenv run pytest tests/acceptance -s --no-cov
|
||||
|
||||
# kill BrowserStackLocal
|
||||
kill $BSL_ID
|
0
tests/acceptance/__init__.py
Normal file
0
tests/acceptance/__init__.py
Normal file
18
tests/acceptance/browsers.py
Normal file
18
tests/acceptance/browsers.py
Normal file
@ -0,0 +1,18 @@
|
||||
BROWSERSTACK_CONFIG = {
|
||||
"win7_ie10": {
|
||||
"browser": "IE",
|
||||
"browser_version": "10.0",
|
||||
"os": "Windows",
|
||||
"os_version": "7",
|
||||
"resolution": "1024x768",
|
||||
"browserstack.local": True,
|
||||
},
|
||||
"win10_chrome62": {
|
||||
"browser": "Chrome",
|
||||
"browser_version": "62.0",
|
||||
"os": "Windows",
|
||||
"os_version": "10",
|
||||
"resolution": "1024x768",
|
||||
"browserstack.local": True,
|
||||
},
|
||||
}
|
65
tests/acceptance/conftest.py
Normal file
65
tests/acceptance/conftest.py
Normal file
@ -0,0 +1,65 @@
|
||||
import os
|
||||
import pytest
|
||||
import logging
|
||||
from collections import Mapping
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
|
||||
from .browsers import BROWSERSTACK_CONFIG
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=True)
|
||||
def session(db, request):
|
||||
"""
|
||||
Override base test session
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class DriverCollection(Mapping):
|
||||
"""
|
||||
Allows access to drivers with dictionary syntax. Keeps track of which ones
|
||||
have already been initialized. Allows teardown of all existing drivers.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._drivers = {}
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._drivers)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._drivers)
|
||||
|
||||
def __getitem__(self, name):
|
||||
if name in self._drivers:
|
||||
return self._drivers[name]
|
||||
|
||||
elif name in BROWSERSTACK_CONFIG:
|
||||
self._drivers[name] = self._build_driver(name)
|
||||
return self._drivers[name]
|
||||
|
||||
else:
|
||||
raise AttributeError("Driver {} not found".format(name))
|
||||
|
||||
def _build_driver(self, config_key):
|
||||
return webdriver.Remote(
|
||||
command_executor="http://{}:{}@hub.browserstack.com:80/wd/hub".format(
|
||||
os.getenv("BROWSERSTACK_EMAIL"), os.getenv("BROWSERSTACK_TOKEN")
|
||||
),
|
||||
desired_capabilities=BROWSERSTACK_CONFIG.get(config_key),
|
||||
)
|
||||
|
||||
def teardown(self):
|
||||
for driver in self._drivers.values():
|
||||
driver.quit()
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def drivers():
|
||||
driver_collection = DriverCollection()
|
||||
|
||||
yield driver_collection
|
||||
|
||||
driver_collection.teardown()
|
50
tests/acceptance/test_basic.py
Normal file
50
tests/acceptance/test_basic.py
Normal file
@ -0,0 +1,50 @@
|
||||
import pytest
|
||||
import requests
|
||||
from flask import url_for
|
||||
from urllib.parse import urljoin
|
||||
from .browsers import BROWSERSTACK_CONFIG
|
||||
from atst.domain.users import Users
|
||||
import atst.domain.exceptions as exceptions
|
||||
from atst.routes.dev import _DEV_USERS as DEV_USERS
|
||||
from tests.test_auth import _login
|
||||
|
||||
import cryptography.x509 as x509
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
||||
|
||||
USER_CERT = "ssl/client-certs/atat.mil.crt"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("browser_type", BROWSERSTACK_CONFIG.keys())
|
||||
@pytest.mark.usefixtures("live_server")
|
||||
def test_can_get_title(browser_type, app, drivers):
|
||||
driver = drivers[browser_type]
|
||||
driver.get(url_for("atst.root", _external=True))
|
||||
assert "JEDI" in driver.title
|
||||
|
||||
|
||||
def _get_common_name(cert_path):
|
||||
with open(USER_CERT, "rb") as cert_file:
|
||||
cert = x509.load_pem_x509_certificate(cert_file.read(), default_backend())
|
||||
common_names = cert.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
|
||||
return common_names[0].value
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def valid_user_from_cert():
|
||||
cn = _get_common_name(USER_CERT)
|
||||
cn_parts = cn.split(".")
|
||||
user_info = {
|
||||
"last_name": cn_parts[0],
|
||||
"first_name": cn_parts[1],
|
||||
"dod_id": cn_parts[-1],
|
||||
"atat_role_name": "developer",
|
||||
}
|
||||
return Users.get_or_create_by_dod_id(**user_info)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("live_server")
|
||||
def test_login(drivers, client, app, valid_user_from_cert):
|
||||
driver = drivers["win7_ie10"]
|
||||
driver.get(url_for("dev.login_dev", _external=True))
|
||||
assert "Sign in" not in driver.title
|
Loading…
x
Reference in New Issue
Block a user