End to end test #160690740
This commit is contained in:
dandds 2018-10-15 11:00:28 -04:00 committed by GitHub
commit 5dd55dea55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 263 additions and 21 deletions

3
.gitignore vendored
View File

@ -44,3 +44,6 @@ config/dev.ini
# coverage output
.coverage
# selenium testing
browserstacklocal

View File

@ -34,6 +34,7 @@ factory-boy = "*"
pytest-flask = "*"
pytest-env = "*"
pytest-cov = "*"
selenium = "*"
[requires]
python_version = "3.6.6"

55
Pipfile.lock generated
View File

@ -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"

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,3 @@
[default]
PGDATABASE = atat_selenium
CRL_DIRECTORY = tests/fixtures/crl

View File

@ -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
View 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

View File

View 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,
},
}

View 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()

View 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