Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add E2E Tests for Wok #292

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions tests/run_tests.sh.in
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,14 @@ HAVE_UNITTEST=@HAVE_PYMOD_UNITTEST@
PYTHON_VER=@PYTHON_VERSION@

if [ "$1" = "-v" ]; then
OPTS="-v"
# p ./test*.py means the pattern for
# discover is local file starting with test
OPTS="-v -p ./test*.py"
shift
else
OPTS=""
fi

if [ $# -ne 0 ]; then
ARGS="$@"
else
ARGS=`find -name "test_*.py" | xargs -I @ basename @ .py`
fi

CMD="python3 -m unittest"

PYTHONPATH=../src:../ $CMD $OPTS $ARGS
PYTHONPATH=../src:../ $CMD $OPTS
63 changes: 63 additions & 0 deletions tests/ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Wok E2E Tests

The tests are located in `tests/ui`. You should go to the directory to start them
```
$ cd tests/ui
```

## How to run

First you need to install all dependencies, start Wok server and run the tests

### Optional: install a virtual environment

```
$ python3 -m venv .env
ramonmedeiros marked this conversation as resolved.
Show resolved Hide resolved
$ source .env/bin/activate
```

### Install deps
```
$ pip install -r requirements.txt
```

### Install Browser

This tests expect Google Chrome installed. Visit https://www.google.com/chrome/ for info

### Start wok server

```
$ python src/wokd
```

### Parameters for run
The script expect some environment variables to run wok tests, which are:

```
Expect environment variables:
USERNAME: username for the host default: root
PASSWORD: password for the host
HOST: host for wok default: localhost
PORT: port for wok default: 8001
BROWSER: browser to run default: CHROME possible: [CHROME, FIREFOX]
```

So, if you are running against a remote host:

```
$ HOST=<HOST> ./run_tests.sh
Type password for host USER@HOST

```

### Run in debug mode
If you use the command above, the browser will no be visible for you.

To see the browser action, add the variable `DEBUG`

```
$ HOST=<HOST> DEBUG=true ./run_tests.sh
Type password for host USER@HOST

```
72 changes: 72 additions & 0 deletions tests/ui/pages/login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import logging as log
import os
import utils
import pytest
from selenium.common.exceptions import TimeoutException

logging = log.getLogger(__name__)

# locators by ID
USERNAME = "username"
PASSWORD = "password"
LOGIN_BUTTON = "btn-login"
LOGIN_BAR = "user-login"

# environment variables
ENV_USER = "USERNAME"
ENV_PASS = "PASSWORD"
ENV_PORT = "PORT"
ENV_HOST = "HOST"


class WokLoginPage():
"""
Page object to Login

Expect environment variables:
USERNAME: username for the host
PASSWORD: password for the host
HOST: host for Wok
PORT: port for Wok
"""

def __init__(self, browser):
self.browser = browser

# assert envs
for var in [ENV_USER, ENV_PASS, ENV_PORT, ENV_HOST]:
assert var in os.environ, f"{var} is a required environment var"

# get values
self.host = os.environ.get(ENV_HOST)
self.port = os.environ.get(ENV_PORT)
self.user = os.environ.get(ENV_USER)
self.password = os.environ.get(ENV_PASS)

def login(self):
try:
url = f"https://{self.host}:{self.port}/login.html"
self.browser.get(url)
except TimeoutException as e:
logging.error(f"Cannot reach wok server at {url}")
return False

# fill user and password
logging.info(f"Loging in {url}")
utils.fillTextIfElementIsVisibleById(self.browser,
USERNAME,
self.user)
utils.fillTextIfElementIsVisibleById(self.browser,
PASSWORD,
self.password)

# press login
utils.clickIfElementIsVisibleById(self.browser, LOGIN_BUTTON)

# login bar not found: return error
if utils.waitElementIsVisibleById(self.browser, LOGIN_BAR) == False:
logging.error(f"Invalid credentials")
return False

logging.info(f"Logged in {url}")
return True
4 changes: 4 additions & 0 deletions tests/ui/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[pytest]
verbose = True
log_cli = True
log_cli_level = INFO
29 changes: 29 additions & 0 deletions tests/ui/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
appnope==0.1.0
attrs==19.3.0
backcall==0.1.0
chromedriver-binary==78.0.3904.105.0
decorator==4.4.1
importlib-metadata==1.3.0
ipdb==0.12.3
ipython==7.11.0
ipython-genutils==0.2.0
jedi==0.15.2
more-itertools==8.0.2
packaging==19.2
parso==0.5.2
pexpect==4.7.0
pickleshare==0.7.5
pluggy==0.13.1
prompt-toolkit==3.0.2
ptyprocess==0.6.0
py==1.8.1
pygeckodriver==0.26.0
Pygments==2.5.2
pyparsing==2.4.6
pytest==5.3.2
selenium==3.141.0
six==1.13.0
traitlets==4.3.3
urllib3==1.25.7
wcwidth==0.1.7
zipp==0.6.0
15 changes: 15 additions & 0 deletions tests/ui/run_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash

HOST=${HOST:-localhost}
PORT=${PORT:-8001}
USERNAME=${USERNAME:-root}
BROWSER=${BROWSER:-CHROME}

# ask for password if not passed
if [ -z $PASSWORD ]; then
echo "Type password for host ${USERNAME}@${HOST}"
read -s PASSWORD
fi

echo "Running on browser ${BROWSER}"
HOST=${HOST} PASSWORD=${PASSWORD} USERNAME=${USERNAME} PORT=${PORT} BROWSER=${BROWSER} python3 -m pytest
13 changes: 13 additions & 0 deletions tests/ui/test_login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import pytest

from pages.login import WokLoginPage
from utils import getBrowser

@pytest.fixture
def browser():
browser = getBrowser()
yield browser
browser.quit()

def test_login(browser):
assert WokLoginPage(browser).login(), "Cannot login to Wok"
123 changes: 123 additions & 0 deletions tests/ui/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
from selenium import webdriver
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver.firefox.options import Options as FirefoxOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.wait import TimeoutException
from selenium.webdriver.support import expected_conditions as EC
from pygeckodriver import geckodriver_path
from chromedriver_binary import chromedriver_filename

import logging as log
import os

logging = log.getLogger(__name__)

WAIT = 10

def getBrowser(headless=True):
# chrome: set browser class and options
if os.environ.get("BROWSER").upper() == "CHROME":
options = ChromeOptions()
options.add_argument('--no-sandbox')
options.add_argument('--disable-gpu')
browser = webdriver.Chrome
path = chromedriver_filename

# firefox: set browser class and options
elif os.environ.get("BROWSER").upper() == "FIREFOX":
options = FirefoxOptions()
browser = webdriver.Firefox
path = geckodriver_path

# headless
if "DEBUG" not in os.environ:
options.add_argument('--headless')
#options.headless = True
else:
logging.info("Headless mode deactivated")

# error: browser not found
try:
driver = browser(options=options, executable_path=path)
except Exception as e:
logging.info(f"Browser or driver not found: {e}")

driver.set_page_load_timeout(WAIT * 2)
return driver

def waitElementByCondition(browser, condition, searchMethod, searchString, errorMessage, time=WAIT):
try:
element = WebDriverWait(browser, time).until(
condition((searchMethod, searchString))
)
except TimeoutException as e:
logging.error(f"Element {searchString} {errorMessage}")
return False
return True


def waitElementIsVisibleById(browser, elementId, time=WAIT):
return waitElementByCondition(browser,
EC.visibility_of_element_located,
By.ID,
elementId,
"is not visibile",
time)

def waitElementIsVisibleByXpath(browser, xpath):
return waitElementByCondition(browser,
EC.visibility_of_element_located,
By.XPATH,
xpath,
"is not visibile")

def waitElementIsClickableById(browser, elementId):
return waitElementByCondition(browser,
EC.element_to_be_clickable,
By.ID,
elementId,
"is not clickable")

def waitElementIsClickableByXpath(browser, xpath):
return waitElementByCondition(browser,
EC.element_to_be_clickable,
By.XPATH,
xpath,
"is not clickable")

def clickIfElementIsVisibleByXpath(browser, xpath):
try:
assert(waitElementIsVisibleByXpath(browser, xpath))
assert(waitElementIsClickableByXpath(browser, xpath))
browser.find_element_by_xpath(xpath).click()

except Exception as e:
logging.error(f"Cannot click on element {xpath}: {e}")
return False

return True

def clickIfElementIsVisibleById(browser, elementId):
try:
assert(waitElementIsVisibleById(browser, elementId))
assert(waitElementIsClickableById(browser, elementId))
browser.find_element_by_id(elementId).click()

except Exception as e:
logging.error(f"Cannot click on element {elementId}: {e}")
return False

return True

def fillTextIfElementIsVisibleById(browser, elementId, text):
try:
assert(waitElementIsVisibleById(browser, elementId))
browser.find_element_by_id(elementId).send_keys(text)

except Exception as e:
logging.error(f"Cannot type {text} on element {elementId}: {e}")
return False

return True