Initial set up for Ghost Inspector integration tests.
Adds a CircleCI integration for Ghost Inspector (https://ghostinspector.com), a headless browser testing SaaS. The README is updated with details about how to run GI locally. Removes the bootstrap setup for Selenium testing with BrowserStack.
This commit is contained in:
@@ -135,6 +135,67 @@ jobs:
|
|||||||
- run: "docker tag ${AZURE_SERVER_NAME}/atat:atat-${CIRCLE_SHA1} ${AZURE_SERVER_NAME}/atat:latest"
|
- run: "docker tag ${AZURE_SERVER_NAME}/atat:atat-${CIRCLE_SHA1} ${AZURE_SERVER_NAME}/atat:latest"
|
||||||
- run: "docker push ${AZURE_SERVER_NAME}/atat:latest"
|
- run: "docker push ${AZURE_SERVER_NAME}/atat:latest"
|
||||||
|
|
||||||
|
integration-tests:
|
||||||
|
docker:
|
||||||
|
- image: docker:17.05.0-ce-git
|
||||||
|
steps:
|
||||||
|
- setup_remote_docker:
|
||||||
|
version: 18.06.0-ce
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Set up temporary docker network
|
||||||
|
command: docker network create atat
|
||||||
|
- run:
|
||||||
|
name: Build image
|
||||||
|
command: docker build . -t atat:latest
|
||||||
|
- run:
|
||||||
|
name: Get storage containers
|
||||||
|
command: docker pull postgres:latest && docker pull redis:latest
|
||||||
|
- run:
|
||||||
|
name: Start redis
|
||||||
|
command: docker run -d --network atat --link redis:redis -p 6379:6379 --name redis redis:latest
|
||||||
|
- run:
|
||||||
|
name: Start postgres
|
||||||
|
command: docker run -d --network atat --link postgres:postgres -p 5432:5432 --name postgres postgres:latest
|
||||||
|
- run:
|
||||||
|
name: Start application container
|
||||||
|
command: |
|
||||||
|
docker run -d \
|
||||||
|
-e DISABLE_CRL_CHECK=true \
|
||||||
|
-e PGHOST=postgres \
|
||||||
|
-e REDIS_URI=redis://redis:6379 \
|
||||||
|
-p 8000:8000 \
|
||||||
|
--network atat \
|
||||||
|
--name test-atat \
|
||||||
|
atat:latest \
|
||||||
|
uwsgi \
|
||||||
|
--callable app \
|
||||||
|
--module app \
|
||||||
|
--plugin python3 \
|
||||||
|
--virtualenv /opt/atat/atst/.venv \
|
||||||
|
--http-socket :8000
|
||||||
|
- run:
|
||||||
|
name: Wait for containers
|
||||||
|
command: sleep 3
|
||||||
|
- run:
|
||||||
|
name: Create database
|
||||||
|
command: docker exec postgres createdb -U postgres atat
|
||||||
|
- run:
|
||||||
|
name: Apply migrations
|
||||||
|
command: docker exec test-atat .venv/bin/python .venv/bin/alembic upgrade head
|
||||||
|
- run:
|
||||||
|
name: Execute Ghost Inspector test suite
|
||||||
|
command: |
|
||||||
|
docker pull ghostinspector/test-runner-standalone:latest
|
||||||
|
docker run \
|
||||||
|
-e NGROK_TOKEN=$NGROK_TOKEN \
|
||||||
|
-e GI_API_KEY=$GI_API_KEY \
|
||||||
|
-e GI_SUITE=$GI_SUITE \
|
||||||
|
-e GI_PARAMS_JSON='{}' \
|
||||||
|
-e APP_PORT="test-atat:8000" \
|
||||||
|
--network atat \
|
||||||
|
ghostinspector/test-runner-standalone:latest
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
run-tests:
|
run-tests:
|
||||||
@@ -143,9 +204,12 @@ workflows:
|
|||||||
- test:
|
- test:
|
||||||
requires:
|
requires:
|
||||||
- app_setup
|
- app_setup
|
||||||
- azure-build-and-push-image:
|
- integration-tests:
|
||||||
requires:
|
requires:
|
||||||
- test
|
- test
|
||||||
|
- azure-build-and-push-image:
|
||||||
|
requires:
|
||||||
|
- integration-tests
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
@@ -204,7 +268,7 @@ workflows:
|
|||||||
repo: atat
|
repo: atat
|
||||||
tag: "atat-${CIRCLE_SHA1},latest"
|
tag: "atat-${CIRCLE_SHA1},latest"
|
||||||
requires:
|
requires:
|
||||||
- test
|
- integration-tests
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@@ -52,10 +52,12 @@ ssl/client-certs/*.srl
|
|||||||
# je coverage output
|
# je coverage output
|
||||||
coverage
|
coverage
|
||||||
|
|
||||||
|
# BrowserStack
|
||||||
# selenium testing
|
|
||||||
browserstacklocal
|
browserstacklocal
|
||||||
|
|
||||||
|
# decompiled CircleCI yaml
|
||||||
|
local-ci.yml
|
||||||
|
|
||||||
# python config
|
# python config
|
||||||
.python-version
|
.python-version
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
"files": "^.secrets.baseline$",
|
"files": "^.secrets.baseline$",
|
||||||
"lines": null
|
"lines": null
|
||||||
},
|
},
|
||||||
"generated_at": "2019-10-03T14:34:50Z",
|
"generated_at": "2019-10-14T19:14:26Z",
|
||||||
"plugins_used": [
|
"plugins_used": [
|
||||||
{
|
{
|
||||||
"base64_limit": 4.5,
|
"base64_limit": 4.5,
|
||||||
@@ -40,6 +40,13 @@
|
|||||||
"is_verified": false,
|
"is_verified": false,
|
||||||
"line_number": 156,
|
"line_number": 156,
|
||||||
"type": "Secret Keyword"
|
"type": "Secret Keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hashed_secret": "81b127e2222d9bfc4609053faec85300f7525463",
|
||||||
|
"is_secret": false,
|
||||||
|
"is_verified": false,
|
||||||
|
"line_number": 244,
|
||||||
|
"type": "Secret Keyword"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"alembic.ini": [
|
"alembic.ini": [
|
||||||
@@ -153,24 +160,6 @@
|
|||||||
"type": "Private Key"
|
"type": "Private Key"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tests/acceptance/conftest.py": [
|
|
||||||
{
|
|
||||||
"hashed_secret": "bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f",
|
|
||||||
"is_secret": false,
|
|
||||||
"is_verified": false,
|
|
||||||
"line_number": 48,
|
|
||||||
"type": "Basic Auth Credentials"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tests/fixtures/chain/make-chain.sh": [
|
|
||||||
{
|
|
||||||
"hashed_secret": "bad2e396920ce37fe53fc291f90b130d915375fb",
|
|
||||||
"is_secret": false,
|
|
||||||
"is_verified": false,
|
|
||||||
"line_number": 35,
|
|
||||||
"type": "Secret Keyword"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tests/forms/test_validators.py": [
|
"tests/forms/test_validators.py": [
|
||||||
{
|
{
|
||||||
"hashed_secret": "260408f687da9094705a841acda9b029563053ee",
|
"hashed_secret": "260408f687da9094705a841acda9b029563053ee",
|
||||||
@@ -194,7 +183,7 @@
|
|||||||
"hashed_secret": "e4f14805dfd1e6af030359090c535e149e6b4207",
|
"hashed_secret": "e4f14805dfd1e6af030359090c535e149e6b4207",
|
||||||
"is_secret": false,
|
"is_secret": false,
|
||||||
"is_verified": false,
|
"is_verified": false,
|
||||||
"line_number": 651,
|
"line_number": 656,
|
||||||
"type": "Hex High Entropy String"
|
"type": "Hex High Entropy String"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
37
README.md
37
README.md
@@ -220,30 +220,37 @@ To generate coverage reports for the Javascript tests:
|
|||||||
|
|
||||||
yarn test:coverage
|
yarn test:coverage
|
||||||
|
|
||||||
### Selenium Tests
|
### Ghost Inspector Tests
|
||||||
|
|
||||||
Selenium tests rely on BrowserStack. In order to run the Selenium tests
|
AT-AT uses [Ghost Inpsector](https://app.ghostinspector.com/) for
|
||||||
locally, you need BrowserStack credentials. The user email and key can
|
integration testing. These tests do not run locally as part of the
|
||||||
be found on the account settings page. To run the selenium tests:
|
regular test suite but do run in CI. To run them locally, you will
|
||||||
|
need the following:
|
||||||
|
|
||||||
|
- [docker](https://docs.docker.com/v17.12/install/)
|
||||||
|
- [circleci CLI tool](https://circleci.com/docs/2.0/local-cli/#installation)
|
||||||
|
- the prerequisite variable information listed [here](https://ghostinspector.com/docs/integration/circle-ci/)
|
||||||
|
|
||||||
|
The version of our CircleCI config (2.1) is incompatible with the
|
||||||
|
`circleci` tool. First run:
|
||||||
|
|
||||||
```
|
```
|
||||||
BROWSERSTACK_TOKEN=<token> BROWSERSTACK_EMAIL=<email> ./script/selenium_test
|
circleci config process .circleci/config.yml > local-ci.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
The selenium tests are in `tests/acceptance`. This directory is ignored by
|
Then run the job:
|
||||||
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
|
circleci local execute -e GI_SUITE=<SUITE_ID> -e GI_API_KEY=<API KEY> -e NGROK_TOKEN=<NGROK TOKEN> --job integration-tests -c local-ci.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
The BrowserStack email is the one associated with the account. The token is
|
If the job fails and you want to re-run it, you may receive errors
|
||||||
available in the BrowserStack profile information page. Go to the dashboard,
|
about running docker containers or the network already existing.
|
||||||
then "Account" > "Settings", then the token is under "Local Testing".
|
Some version of the following should reset your local docker state:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker container stop redis postgres test-atat; docker container rm redis postgres test-atat ; docker network rm atat
|
||||||
|
```
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
@@ -1,18 +0,0 @@
|
|||||||
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,
|
|
||||||
},
|
|
||||||
}
|
|
@@ -1,65 +0,0 @@
|
|||||||
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()
|
|
@@ -1,50 +0,0 @@
|
|||||||
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
|
|
Reference in New Issue
Block a user