Skip to content

Commit

Permalink
Merge pull request #2 from kieras/develop
Browse files Browse the repository at this point in the history
New version 0.0.11
  • Loading branch information
kieras authored Jan 23, 2018
2 parents 0e5e179 + be845c4 commit 16bb17e
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 30 deletions.
4 changes: 3 additions & 1 deletion parakeet/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
from .config import *
from .utils import *
from .auth import *
from .browser import *
from .page_objects import *
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.2'
__version__ = '0.0.11'
22 changes: 9 additions & 13 deletions parakeet/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import base64
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support import expected_conditions as ec


def decode(password):
Expand All @@ -23,7 +23,7 @@ def __init__(self, browser, home_title):
"""
:param browser: the browser.
:type browser: splinter.Browser
:type browser: ParakeetBrowser
:param home_title: the home page title you expect after logging in.
:type home_title: str
Expand All @@ -32,27 +32,23 @@ def __init__(self, browser, home_title):
self.home_title = home_title

def fill_email(self, email):
self.browser.is_element_present_by_id('identifierId', wait_time=10)
self.browser.fill('identifier', email)
self.browser.find_element_by_id('identifierId').type(email)
return self

def click_next(self):
self.browser.is_element_present_by_id('identifierNext', wait_time=10)
self.browser.find_by_id('identifierNext').click()
self.browser.find_element_by_id('identifierNext').click()
return self

def fill_password(self, password):
self.browser.is_element_visible_by_css('#password input', wait_time=10)
self.browser.type('password', decode(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.is_element_present_by_id('passwordNext', wait_time=10)
WebDriverWait(self.browser.driver, 10).until(EC.element_to_be_clickable((By.ID, 'passwordNext')))
self.browser.find_by_id('passwordNext').click()
self.browser.find_element_by_id('passwordNext').click()
return self

def redirect_to_home(self):
WebDriverWait(self.browser.driver, 10).until(EC.title_contains(self.home_title))
WebDriverWait(self.browser.driver, 10).until(EC.invisibility_of_element_located((By.CLASS_NAME, 'main-loading')))
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')))
return self
145 changes: 145 additions & 0 deletions parakeet/browser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# -*- coding: utf-8 -*-
from __future__ import division
import time
import re
from splinter import Browser
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec


class ParakeetElement(object):
"""
A wrapper around Selenium WebElement for AngularJS and AngularJS Material projects.
Attributes:
element: The Selenium WebElement object.
locator: A tuple of (by, path) from Selenium API.
parakeet: The instance of ParakeetBrowser.
"""
def __init__(self, element, locator, parakeet):
self.element = element
self.locator = locator
self.parakeet = parakeet

def clear(self):
self.element = self.wait_visibility_of_element_located()
self.element.clear()
return self

def click(self):
self.element = self.wait_element_to_be_clickable()
self.element.click()
return self

def click_and_wait_disappear(self):
self.click()
self.wait_invisibility_of_element_located()
return self

def type(self, value):
self.element = self.wait_visibility_of_element_located()
self.element.send_keys(value)
self.debounce()
return self

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(
ec.visibility_of_element_located(self.locator)
)

def wait_invisibility_of_element_located(self):
return WebDriverWait(self.parakeet.selenium, self.parakeet.waiting_time).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(
ec.element_to_be_clickable(self.locator)
)

def debounce(self):
"""
If the element has an AngularJS debounce set, it sleeps for 1.5x the debounce value.
"""
ng_model_options_value = self.get_attribute('ng-model-options')

if ng_model_options_value is not None:
debounce_value = ParakeetElement.extract_debounce_value(ng_model_options_value)
if debounce_value > 0:
time.sleep(1.5 * debounce_value/1000)

@staticmethod
def extract_debounce_value(attribute_value):
"""
Try to extract the AngularJS debounce value from ng-model-options value.
Usually that attribute value is something like this: '{ debounce: 300 }'.
:param attribute_value: The string representing the ng-model-options attribute value.
:return: debounce value, or 0 if does not have one.
"""
debounce_value = "0"
result_debounce = re.search("""debounce"*'*:"*'*\s*"*'*(\d*)"*'*,*\s*""", attribute_value, re.IGNORECASE)
if result_debounce:
debounce_value = result_debounce.group(1)
return int(debounce_value)


class ParakeetBrowser(object):
"""
A wrapper around Splinter / Selenium for AngularJS and AngularJS Material projects.
Attributes:
config: The Parakeet/project config dictionary.
splinter: The Splinter browser/driver instance.
selenium: The Selenium driver instance.
waiting_time: Maximum time in seconds to wait for an action.
"""

def __init__(self, config):
self.config = config
self.splinter = Browser(config['browser'])
self.selenium = self.splinter.driver
self.waiting_time = int(config['default_implicitly_wait_seconds'])
self.selenium.implicitly_wait(self.waiting_time)
self.selenium.set_window_size(int(config['window_size']['width']), int(config['window_size']['height']))

def find_element_by_id(self, 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):
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):
return self.splinter.is_element_present_by_id(element_id, self.waiting_time)

def is_element_present_by_xpath(self, element_xpath):
return self.splinter.is_element_present_by_xpath(element_xpath, self.waiting_time)

def is_text_present(self, text):
return self.splinter.is_text_present(text)

def quit(self):
self.splinter.quit()

def visit(self, url):
self.splinter.visit(url)

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

def get_element_waiting_for_its_presence(self, locator):
element = WebDriverWait(self.selenium, self.waiting_time).until(
ec.presence_of_element_located(locator)
)
return element

17 changes: 17 additions & 0 deletions parakeet/common_steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from lettuce import step, world
from auth import LoginPage


@step(u'.* the logged user is "([^"]*)"')
def login(step, user_name):
# If it is already logged do not attempt to login
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()
15 changes: 0 additions & 15 deletions parakeet/config.py

This file was deleted.

14 changes: 14 additions & 0 deletions parakeet/page_objects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-


class BasePageObject(object):
"""A base class for all page objects.
Attributes:
browser: The ParakeetBrowser instance.
"""

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


16 changes: 16 additions & 0 deletions parakeet/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
import yaml


def load_yaml(yaml_file):
"""
Load a YAML file and return it as a dict.
:param yaml_file: path to the yaml file.
:type yaml_file: str
:return: a dict.
"""
print('Loading file: {}.'.format(yaml_file))
with open(yaml_file, 'r') as f:
yaml_content = yaml.load(f)
return yaml_content
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
selenium==3.7.0
splinter==0.7.7
pyyaml==3.12
lettuce==0.2.23

0 comments on commit 16bb17e

Please sign in to comment.