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 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
52 changes: 52 additions & 0 deletions tests/ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# 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 to 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
```

### Run in headless mode
The script expect some environment variables to run kimchi-project tests, which are:
ramonmedeiros marked this conversation as resolved.
Show resolved Hide resolved

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

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 KimchiLoginPage():
ramonmedeiros marked this conversation as resolved.
Show resolved Hide resolved
"""
Page object to Login

Expect environment variables:
KIMCHI_USERNAME: username for the host
ramonmedeiros marked this conversation as resolved.
Show resolved Hide resolved
KIMCHI_PASSWORD: password for the host
ramonmedeiros marked this conversation as resolved.
Show resolved Hide resolved
KIMCHI_HOST: host for kimchi
ramonmedeiros marked this conversation as resolved.
Show resolved Hide resolved
KIMCHI_PORT: port for kimchi
ramonmedeiros marked this conversation as resolved.
Show resolved Hide resolved
"""

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 kimchi at {url}")
ramonmedeiros marked this conversation as resolved.
Show resolved Hide resolved
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
28 changes: 28 additions & 0 deletions tests/ui/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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
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
13 changes: 13 additions & 0 deletions tests/ui/run_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

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

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

HOST=${HOST} PASSWORD=${PASSWORD} USERNAME=${USERNAME} PORT=${PORT} python3 -m pytest
15 changes: 15 additions & 0 deletions tests/ui/test_login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import utils
from pages.login import KimchiLoginPage
ramonmedeiros marked this conversation as resolved.
Show resolved Hide resolved

import logging as log

class TestWokLogin():

def setup(self):
self.browser = utils.getBrowser()

def test_login(self):
assert KimchiLoginPage(self.browser).login(), "Cannot login to Kimchi"

def tearDown(self):
self.browser.close()
105 changes: 105 additions & 0 deletions tests/ui/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
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

import chromedriver_binary
import logging as log
import os

logging = log.getLogger(__name__)

WAIT = 10

def getBrowser(headless=True):
if os.environ.get("DEBUG") is not None:
logging.info("Headless mode deactivated")
headless = False

options = Options()
if headless is True:
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-gpu')

driver = webdriver.Chrome(options=options)
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