Chapter9 Extending Selenium
9.1 Creating an extension class for web tables
Create a new class WebTable
, which we will use to implement support for the table elements
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
class WebTable:
def __init__(self,webTable):
self._webTable = webTable
# retrieve rows
def getRowCount(self):
self.tableRows = self._webTable.find_elements_by_tag_name('tr')
return len(self.tableRows)
#retrieve columns
def getColumnCount(self):
self.tableRows = self._webTable.find_elements_by_tag_name('tr')
self.headerRow = self.tableRows[0]
self.tableCols = self.headerRow.find_elements_by_tag_name('td')
return len(self.tableCols)
# retrieve data from a specific cell of the table
def getCellData(self,rowIdx,colIdx):
self.tableRows = self._webTable.find_elements_by_tag_name('tr')
self.currentRow = self.tableRows[rowIdx-1]
self.tableCols = self.currentRow.find_elements_by_tag_name('td')
self.cell = self.tableCols[colIdx-1]
return self.cell.text
# retrieve the cell editor element
def getCellEditor(self,rowIdx,colIdx,editorIdx):
try:
self.tableRows = self._webTable.find_elements_by_tag_name('tr')
self.currentRow = self.tableRows[rowIdx-1]
self.tableCols = self.currentRow.find_elements_by_tag_name('td')
self.cell = self.tableCols[colIdx-1]
self.cellEditor = self.cell.find_elements_by_tag_name('input')[editorIdx]
return self.cellEditor
except NoSuchElementException:
raise NoSuchElementException('Failed to get cell editor')
Create a Test Class:
import unittest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from WebTable import WebTable
class WebTableTests(unittest.TestCase):
def setUp(self):
chrome_options = Options()
chrome_options.add_argument('--no-sandbox') #让chrome在root权限下跑
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_experimental_option('useAutomationExtension',False)
self.driver = webdriver.Chrome(executable_path='D:/elf_private/test/chromedriver',options=chrome_options)
self.driver.implicitly_wait(30)
self.driver.maximize_window()
self.driver.get("file:///D:/elf_private/test/sele/table_example.html")
def testWebTableTest(self):
self.table = WebTable(self.driver.find_element_by_tag_name('table'))
print('Table Row Count: ' + str(self.table.getRowCount()))
print('Table Column Count: ' + str(self.table.getColumnCount()))
print('Row3 Col1 is ' + self.table.getCellData(2,1))
def tearDown(self):
self.driver.quit()
if __name__ == '__main__':
unittest.main()
9.2 Creating an extension for the jQueryUI tab widget
Example jQuery UI tabs widget: http://jqueryui.com/demos/tabs/
Create a JQueryUITab
class to represent the tab widget
from selenium import webdriver
class JQueryUITab:
def __init__(self,jQueryUITab):
self._jQueryUITab = jQueryUITab
def getTabCount(self):
self.tabs = self._jQueryUITab.find_elements_by_css_selector(".ui-tabs-nav > li")
return len(self.tabs)
def getSelectedTab(self):
self.tab = self._jQueryUITab.\
find_element_by_css_selector(".ui-tabs-nav > li[class*='ui-state-active']")
return self.tab.text
def selectTab(self,driver,tabName):
self.idx = 0
self.found = False
self.tabs = self._jQueryUITab.find_elements_by_css_selector(".ui-tabs-nav > li")
for tab in self.tabs:
if tab.text == tabName:
driver.execute_script(\
"jQuery(arguments[0]).tabs().tabs('option','active',arguments[1]);",\
self._jQueryUITab,self.idx)
self.found = True
break
idx += 1
if self.found == False:
raise IllegalArgumentException("Could not find tab '" + tabName +"'")
test code:
import unittest
from selenium import webdriver
from JQueryUITab import JQueryUITab
class WebTableTests(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox(executable_path='/usr/local/bin/geckodriver')
self.driver.implicitly_wait(30)
self.driver.maximize_window()
self.driver.get("file:///Users/elf/selenium/jquery/index.html")
def testJianShu(self):
self.tab = JQueryUITab(self.driver.find_element_by_css_selector("div#tabs"))
print(str(self.tab.getTabCount()))
self.tab.selectTab(self.driver,"First")
print(self.tab.getSelectedTab())
def tearDown(self):
self.driver.quit()
if __name__ == '__main__':
unittest.main()
9.3 Implementing an extension for the WebElement object to set the element attribute values
Setting an element's attribute can be useful in various situations where the test needs to manipulate properties of an element. For example, for a masked textbox, the send_keys()
method may not work well, and setting the value of the textbox will help to overcome these issues.
setAttribute()
method:
class WebElementExtender:
@staticmethod
def setAttribute(driver,element,attributeName,value):
driver.execute_script("arguments[0].setAttribute(arguments[1],arguments[2])",\
element,attributeName,value)
An example on using this method:
In this case, this can be done by clear()
and send_keys()
search = driver.find_element_by_id("q")
WebElementExtender.setAttribute(driver,search,"placeholder","abc")
9.4 Implementing an extension for the WebElement object to highlight elements
highlightElement
method
@staticmethod
def highlightElement(driver,element):
driver.execute_script("arguments[0].setAttribute('style',arguments[1]);",\
element,"background:green ;border:2px solid red;")
An example on using this method:
search = driver.find_element_by_id("q")
WebElementExtender.highlightElement(driver,search)
# wait 3s to see the highlight effect
time.sleep(3)
search.send_keys("abc")
9.5 Creating an object map for Selenium tests
Create a .xml
file to store the elements
SignUp
id
sign_up
SignIn
id
sign_in
Download
classname
app-download-btn
SearchArea
name
q
SearchButton
classname
search-btn
Implement the ObjectMap
class to read the xml file and provide the locator information to the test
from selenium import webdriver
from xml.dom.minidom import parse
class ObjectMap:
def __init__(self,mapFile):
self.domTree = parse(mapFile)
self.rootNode = self.domTree.documentElement
def getLocator(self,driver,logicalElementName):
self.locators = self.rootNode.getElementsByTagName("element")
for locator in self.locators:
#print(locator.getElementsByTagName("name")[0].childNodes[0].data)
if locator.getElementsByTagName("name")[0].childNodes[0].data == logicalElementName:
try:
self.locatorType = locator.getElementsByTagName("type")[0].childNodes[0].data
self.locatorValue = locator.getElementsByTagName("value")[0].childNodes[0].data
return self.getLocatorByType(driver)
except Exception:
raise Exception("Failed to generate locator for '" + logicalElementName + "'")
def getLocatorByType(self,driver):
if self.locatorType.lower() == "id":
return driver.find_element_by_id(self.locatorValue)
elif self.locatorType.lower() == "name":
return driver.find_element_by_name(self.locatorValue)
elif self.locatorType.lower() == "classname":
return driver.find_element_by_class_name(self.locatorValue)
elif self.locatorType.lower() == "linktext":
return driver.find_element_by_link_text(self.locatorValue)
elif self.locatorType.lower() == "partiallinktext":
return driver.find_element_by_partial_link_text(self.locatorValue)
elif self.locatorType.lower() == "css":
return driver.find_element_by_css_selector(self.locatorValue)
elif self.locatorType.lower() == "xpath":
return driver.find_element_by_xpath(self.locatorValue)
elif self.locatorType.lower() == "tagname":
return driver.find_element_by_tag_name(self.locatorValue)
else:
raise Exception("Locator Type '" + self.locatorType +"' not supported!")
Create a test code:
import unittest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from ObjectMap import ObjectMap
class WebTableTests(unittest.TestCase):
def setUp(self):
chrome_options = Options()
chrome_options.add_argument('--no-sandbox') #让chrome在root权限下跑
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_experimental_option('useAutomationExtension',False)
self.driver = webdriver.Chrome(executable_path='D:/elf_private/test/chromedriver',options=chrome_options)
self.driver.implicitly_wait(30)
self.driver.maximize_window()
#self.driver.get("file:///D:/elf_private/test/sele/table_example.html")
self.driver.get("https://www.jianshu.com")
def testJianShu(self):
self.map = ObjectMap('mapFile.xml')
print(self.map.getLocator(self.driver,"SignUp").text)
print(self.map.getLocator(self.driver,"SignIn").text)
print(self.map.getLocator(self.driver,"Download").text)
Search_Area = self.map.getLocator(self.driver,"SearchArea")
Search_Area.send_keys("abc")
Button = self.map.getLocator(self.driver,"SearchButton")
Button.click()
def tearDown(self):
self.driver.quit()
if __name__ == '__main__':
unittest.main()
9.6 Capturing screenshots of elements in the Selenium WebDriver
There are screenshot APIs in selenium for Python. So no need to create interface ourselves ^_^
- selenium.webdriver.remote.webdriver
- get_screenshot_as_base64()
- get_screenshot_as_file(filename)
- get_screenshot_as_png()
- selenium.webdriver.remote.webelement
- screenshot(filename)
- screenshot_as_base64
- screenshot_as_png
button = driver.find_element_by_id("sign_up")
# get the screenshot of the button
button.screenshot('signup.png')
# get the screenshot of the current window
driver.get_screenshot_as_file('currentwindow.png')
9.7 Comparing images in Selenium
use PIL
module for python
Implement Compare
class to compare two images
from PIL import Image
from PIL import ImageChops
class Compare:
@staticmethod
def CompareImage(PathOne,PathTwo,DiffPath):
ImgOne = Image.open(PathOne)
ImgTwo = Image.open(PathTwo)
try:
DiffImg = ImageChops.difference(ImgOne,ImgTwo)
if DiffImg.getbbox() is None:
print("Images are same!")
else:
DiffImg.save(DiffPath)
except ValueError as e:
raise ValueError("Paste another image into this image.")
test code:
self.driver.get_screenshot_as_file("New.png")
Compare.CompareImage("Base.png","New.png","Diff.png")
9.8 Measuring performance with the Navigation Timing API
Navigation Timing is a W3C Standard JavaScript API to measure performance on the Web. The API provides a simple way to get accurate and detailed timing statistics natively for page navigation and load events. It is available on IE9, Google Chrome, Firefox and WebKit-based browsers.
The API is accessed via the properties of the timing interface of the window.performance object using JavaScript
driver.get("https://www.jianshu.com")
# get the Load Event End
loadEventEnd = driver.execute_script("return window.performance.timing.loadEventEnd")
# get the Navigation Event Start
navigationStart = driver.execute_script("return window.performance.timing.navigationStart")
# print Page Load Time
print("Page Load Time is " + str((loadEventEnd - navigationStart)/1000) + " seconds.")