Skip to content

Commit

Permalink
Merge pull request #5 from kieras/develop
Browse files Browse the repository at this point in the history
Gapi2 login, Headless mode, Local config file support, debug messages and other improvements
  • Loading branch information
kieras authored Mar 16, 2018
2 parents 16bb17e + 451be1c commit dec654a
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 24 deletions.
2 changes: 1 addition & 1 deletion parakeet/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
# |_| \__,_|_| \__,_|_|\_\___|\___|\__|


__version__ = '0.0.11'
__version__ = '0.0.12'
45 changes: 41 additions & 4 deletions parakeet/auth.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# -*- coding: utf-8 -*-
import base64

from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec

from parakeet.lettuce_logger import LOG


def decode(password):
"""
Expand Down Expand Up @@ -31,24 +35,57 @@ def __init__(self, browser, home_title):
self.browser = browser
self.home_title = home_title

self.window = ''

def fill_email(self, email):
LOG.debug('fill_email')
self.browser.find_element_by_id('identifierId').type(email)
return self

def click_next(self):
LOG.debug('click_next')
self.browser.find_element_by_id('identifierNext').click()
return self

def fill_password(self, password):
self.browser.splinter.is_element_visible_by_css('#password input', self.browser.waiting_time)
LOG.debug('fill_password')
self.browser.splinter.\
is_element_visible_by_css('#password input', self.browser.waiting_time)
self.browser.splinter.type('password', decode(password))
return self

def login(self):
self.browser.find_element_by_id('passwordNext').click()
LOG.debug('login')
password_next_element = self.browser\
.find_element_by_id('passwordNext')\
.click()
return self

def redirect_to_home(self):
WebDriverWait(self.browser.selenium, 10).until(ec.title_contains(self.home_title))
WebDriverWait(self.browser.selenium, 10).until(ec.invisibility_of_element_located((By.CLASS_NAME, 'main-loading')))
LOG.debug('redirect_to_home')
WebDriverWait(self.browser.selenium, self.browser.waiting_time, self.browser.poll_frequency)\
.until(ec.title_contains(self.home_title))
WebDriverWait(self.browser.selenium, self.browser.waiting_time, self.browser.poll_frequency)\
.until(ec.invisibility_of_element_located((By.CLASS_NAME, 'main-loading')))
return self

def click_sign_in(self):
LOG.debug('click_sign_in')
self.browser.find_element_by_xpath('//md-card-actions/button').click()
return self

def switch_windows_before(self):
LOG.debug('switch_windows_before')
self.browser.selenium.switch_to_window(self.window)
return self

def switch_windows_after(self):
LOG.debug('switch_windows_after')
popup = self.browser.selenium.window_handles[1]
self.browser.selenium.switch_to_window(popup)
return self

def set_window(self):
LOG.debug('set_window')
self.window = self.browser.selenium.window_handles[0]
return self
65 changes: 58 additions & 7 deletions parakeet/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from random import randint
from time import sleep
from parakeet.lettuce_logger import LOG


class ParakeetElement(object):
Expand All @@ -29,6 +32,10 @@ def clear(self):
return self

def click(self):
self.parakeet.retry(method=self.click_once_time_only)
return self

def click_once_time_only(self):
self.element = self.wait_element_to_be_clickable()
self.element.click()
return self
Expand All @@ -48,17 +55,17 @@ def get_attribute(self, name):
return self.element.get_attribute(name)

def wait_visibility_of_element_located(self):
return WebDriverWait(self.parakeet.selenium, self.parakeet.waiting_time).until(
return WebDriverWait(self.parakeet.selenium, self.parakeet.waiting_time, self.parakeet.poll_frequency).until(
ec.visibility_of_element_located(self.locator)
)

def wait_invisibility_of_element_located(self):
return WebDriverWait(self.parakeet.selenium, self.parakeet.waiting_time).until(
return WebDriverWait(self.parakeet.selenium, self.parakeet.waiting_time, self.parakeet.poll_frequency).until(
ec.invisibility_of_element_located(self.locator)
)

def wait_element_to_be_clickable(self):
return WebDriverWait(self.parakeet.selenium, self.parakeet.waiting_time).until(
return WebDriverWait(self.parakeet.selenium, self.parakeet.waiting_time, self.parakeet.poll_frequency).until(
ec.element_to_be_clickable(self.locator)
)

Expand Down Expand Up @@ -103,43 +110,87 @@ class ParakeetBrowser(object):

def __init__(self, config):
self.config = config
self.splinter = Browser(config['browser'])
self.splinter = Browser(config.get('browser'), headless=config.get('headless'))
self.selenium = self.splinter.driver
self.waiting_time = int(config['default_implicitly_wait_seconds'])
self.waiting_time = int(config.get('default_implicitly_wait_seconds'))
self.poll_frequency = int(config.get('default_poll_frequency_seconds'))
self.retry_get_element = int(config.get('retry', 1))
self.selenium.implicitly_wait(self.waiting_time)
self.selenium.set_window_size(int(config['window_size']['width']), int(config['window_size']['height']))
self.selenium.set_window_size(int(config['window_size']['width']),
int(config['window_size']['height']))

def find_element_by_id(self, element_id):
LOG.debug('find_element_by_id({})'
.format(element_id))
locator = (By.ID, element_id)
element = self.get_element_waiting_for_its_presence(locator)
return ParakeetElement(element, locator, self)

def find_element_by_xpath(self, element_xpath):
LOG.debug('find_element_by_xpath({})'
.format(element_xpath))
locator = (By.XPATH, element_xpath)
element = self.get_element_waiting_for_its_presence(locator)
return ParakeetElement(element, locator, self)

def is_element_present_by_id(self, element_id):
LOG.debug('is_element_present_by_id({})'
.format(element_id))
return self.splinter.is_element_present_by_id(element_id, self.waiting_time)

def is_element_present_by_xpath(self, element_xpath):
LOG.debug('is_element_present_by_xpath({})'
.format(element_xpath))
return self.splinter.is_element_present_by_xpath(element_xpath, self.waiting_time)

def is_text_present(self, text):
LOG.debug('is_text_present({})'
.format(text))
return self.splinter.is_text_present(text)

def quit(self):
LOG.debug('quit')
self.splinter.quit()

def visit(self, url):
LOG.debug('visit')
self.splinter.visit(url)

def visit_home(self):
LOG.debug('visit_home')
self.visit(self.config['home_url'])

def get_element_waiting_for_its_presence(self, locator):
element = WebDriverWait(self.selenium, self.waiting_time).until(
LOG.debug('get_element_waiting_for_its_presence({}, {}, {})'
.format(locator, self.waiting_time, self.poll_frequency))
element = WebDriverWait(self.selenium, self.waiting_time, self.poll_frequency).until(
ec.presence_of_element_located(locator)
)
return element

# noinspection PyBroadException
def retry(self, method=None, **kwargs):
"""
Method retry the execution
:param method:
:param kwargs:
:return:
"""
_next = 'next_iterator'
_retry = self.retry_get_element
_next_iterator = kwargs.get(_next, 1)

try:
LOG.debug('Trying {}/{} to perform method {}'
.format(_next_iterator, _retry, method.__name__))

kwargs.pop(_next, None)

return method(**kwargs)
except Exception as ex:
LOG.error('Exception: {}'.format(ex.message))
if _next_iterator < _retry:
kwargs[_next] = _next_iterator + 1
sleep(randint(1, 3))
return self.retry(method=method, **kwargs)
raise ex
51 changes: 42 additions & 9 deletions parakeet/common_steps.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,50 @@
# -*- coding: utf-8 -*-
from lettuce import step, world
from auth import LoginPage
from parakeet.lettuce_logger import LOG


@step(u'.* the logged user is "([^"]*)"')
def login(step, user_name):
# If it is already logged do not attempt to login
"""
If it is already logged do not attempt to login
:param step:
:param user_name:
:return:
"""
if world.cfg['system_page_title'] not in world.browser.selenium.title:
email = world.users[user_name]['email']
password = world.users[user_name]['password']
LoginPage(world.browser, world.cfg['system_page_title'])\
.fill_email(email)\
.click_next()\
.fill_password(password)\
.login()\
.redirect_to_home()
LOG.debug(world.cfg.get('login_provider','missing login provider'))
if world.cfg['login_provider'] == 'google_oauth':
google_oauth(user_name)
elif world.cfg['login_provider'] == 'google_oauth_gapi2':
google_oauth_gapi2(user_name)
LoginPage(world.browser, world.cfg['system_page_title']).redirect_to_home()


def google_oauth(user_name):
"""
Default login with old GAPI Google Oauth version
:param user_name:
:return:
"""
email = world.users[user_name]['email']
password = world.users[user_name]['password']
LoginPage(world.browser, world.cfg['system_page_title'])\
.fill_email(email)\
.click_next()\
.fill_password(password)\
.login()


def google_oauth_gapi2(user_name):
"""
Login with personalized GAPI2 oauth version
:return:
"""
LoginPage(world.browser, world.cfg['system_page_title'])\
.set_window()\
.click_sign_in()\
.switch_windows_after()
google_oauth(user_name)
LoginPage(world.browser, world.cfg['system_page_title'])\
.switch_windows_before()
51 changes: 51 additions & 0 deletions parakeet/lettuce_logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import logging
import sys
from colorlog import ColoredFormatter

log_level = {'INFO': logging.INFO,
'WARNING': logging.WARNING,
'DEBUG': logging.DEBUG,
'ERROR': logging.ERROR}

APP_LOGGER = 'google.tests.e2e'

formatter = ColoredFormatter(
"%(green)s%(asctime)s - %(name)s -%(reset)s %(log_color)s%(levelname)-8s%(reset)s"
" - %(white)s%(message)s",
datefmt=None,
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red',
}
)


def init_logs(level='INFO', logger=None):
"""
Setup the logs inside of the tests.
:param level:
:param logger:
:return:
"""
handler = logging.getLogger(logger)
_level = log_level.get(level, logging.INFO)
handler.setLevel(_level)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(_level)
ch.setFormatter(formatter)
handler.addHandler(ch)


def get_logger():
"""
Return the default logger.
:return:
"""
return logging.getLogger(APP_LOGGER)


LOG = get_logger()
9 changes: 6 additions & 3 deletions parakeet/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
import yaml

import os

def load_yaml(yaml_file):
"""
Expand All @@ -10,7 +10,10 @@ def load_yaml(yaml_file):
:type yaml_file: str
:return: a dict.
"""
print('Loading file: {}.'.format(yaml_file))
with open(yaml_file, 'r') as f:
_local_file = os.path.join(os.path.expanduser('~'), yaml_file)
_local_file = _local_file if os.path.exists(_local_file) else yaml_file

print('Loading file: {}.'.format(_local_file))
with open(_local_file, 'r') as f:
yaml_content = yaml.load(f)
return yaml_content

0 comments on commit dec654a

Please sign in to comment.