Package tlib :: Package base :: Module WebDriverTester |
[hide private]
[frames] | no frames]

# Source Code for Module tlib.base.WebDriverTester

      1  import os
      2  import re
      3  import inspect
      4  # noinspection PyPackageRequirements
      5  import pytest
      6  import collections
      7  from time import sleep
      8  # noinspection PyPackageRequirements
      9  from _pytest.python import FixtureRequest
     10  # noinspection PyPackageRequirements
     11  import jinja2
     12  from tlib.base import TestHelper
     13  from tlib.base import FileHelper
     14  from tlib.base.PytestTester import PytestTester
     15  from selenium.webdriver.common.by import By
     16  from selenium.webdriver.support.ui import WebDriverWait
     17  from selenium.webdriver.support import expected_conditions
     18  from selenium.common.exceptions import TimeoutException, NoAlertPresentException
     19  from selenium.webdriver.remote.webelement import WebElement
     20  from TestHelper import Singleton
     21
     22  # noinspection PyMethodParameters
     23  # noinspection PyUnresolvedReferences
     24 -class WebDriverTester(PytestTester):
     25
     26      __metaclass__ = Singleton
     27
     28      _driver = None
     29
     30      _folder_dest = None # destination of report for the current class
     31      _test_case_id = None
     32      _test_case_name = None
     33      _test_params = None
     34
     35      _jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(TestHelper.tlib_template_folder()),
     36                                      trim_blocks=True)
     37
     38      _screenshot_report = None  # List of tests for which there are screenshots
     39
     40      # Template for generating screenshot report
     41      _screenshot_report_template = _jinja_env.get_template("screenshot_report.html")
     42
     43      _screenshots = {}  # Screenshots generated during a test case
     44
     45      # Template for generating screenshots for a test case
     46      _tc_screenshot_template = _jinja_env.get_template("testcase_screenshots.html")
     47
     48      _request = None
     49
     50 -    def screenshot_folder(self):
     51          """
     52          Returns location of screenshot folder
     53
     54          @return: str
     55          """
     56          screenshot_folder = self._find_screenshot_folder()
     57          if screenshot_folder is None:
     58          #Couldn't find screenshot folder
     59              raise NotImplementedError("Could not find screenshot folder. "
     60                                        "Class should implement method screenshot_folder")
     61          else:
     62              return screenshot_folder
     63
     64 -    def _find_screenshot_folder(self):
     65          """
     66          Will try to find a folder named "screenshot" starting from the file being executed and up to
     67          three levels up
     68
     69          @return: str
     70          """
     71          #Get current folder
     72          curr_folder = os.path.dirname(self._request.fspath.strpath)
     73
     74          #Go up to three levels to find screenshot folder
     75          for i in range(1, 4):
     76              curr_folder = os.path.abspath(os.path.join(curr_folder, os.pardir))
     77
     78              #Check if there is a folder 'screenshots'
     79              screenshot_folder = os.path.join(curr_folder, 'screenshots')
     80              if os.path.exists(screenshot_folder):
     81                  return screenshot_folder
     82
     83
     84 -    def _get_unused_report_name(self, tc_id, tc_params):
     85          """
     86          Gets a string based on test case id and name, taking into account if test case has already been run or not
     87          @param tc_id: Test case id
     88          @type tc_id: str
     89          @type tc_name: str
     90          """
     91          i = 0
     92          while True:
     93              if i == 0:
     94                  filename = "%(tc_id)s_%(tc_params)s" % {"tc_id": tc_id, "tc_params": tc_params}
     95              else:
     96                  filename = "%(tc_id)s_%(tc_params)s [retry %(cnt)d]" % {"tc_id": tc_id, "tc_params": tc_params, "cnt": i}
     97
     98              if not filename in self._screenshot_report:
     99                  return filename
    100
    101              i += 1
    102
    103
    104      @pytest.fixture(scope='class', autouse=True)
    105 -    def initialize_webdriver_class(self, request):
    106          """
    107          @type request: FixtureRequest
    108          """
    109
    110          #Generates initial HTML page with all test case results for the class
    111          self._screenshot_report = collections.OrderedDict()
    112          self._folder_dest = os.path.basename(os.path.dirname(os.path.abspath(inspect.getfile(request.cls))))
    113
    114          def generate_report():
    115              if len(self._screenshot_report) > 0:
    116                  #Generate HTML based on template
    117                  html = self._screenshot_report_template.render(test_class=request.cls.__name__,
    118                                                                  files=self._screenshot_report)
    119
    120                  htm_file = "%s.htm" % self._folder_dest
    121                  full_filename = os.path.join(self.screenshot_folder(), htm_file)
    122
    123                  f = open(full_filename, "w")
    124                  try:
    125                      f.write(html)
    126                  finally:
    127                      f.close()
    128
    129          request.addfinalizer(generate_report)
    130
    131
    132      @pytest.fixture(scope='function', autouse=True)
    133 -    def setup_webdriver_test(self, request):
    134          """
    135          Goes to homepage before each test, unless marker skipsetup is given
    136
    137          @type request: FixtureRequest
    138          """
    139          #Store an instance of the request object. Required for the generation of screenshots
    140          self._request = request
    141
    142          #Initialization required for screenshot generation
    143          #  noinspection PyProtectedMember
    144          self._test_params = request.keywords.node._genid
    145          self._test_case_name = request.keywords.node.name
    146          self._screenshots = {}
    147
    148          #Get marker test case name or use a default if there is none
    149          marker = request.node.get_marker("testcasename")
    150          if marker is None:
    151              self.test_logger.warn("Test case doesn't have marker testcasename")
    152              self._test_case_id = "UNKNOWN_TEST_CASE_ID"
    153          else:
    154              self._test_case_id = marker.args[0]
    155
    156          def generate_report():
    157              """
    158              Generates HTML page with all the screenshots for a test case
    159              Supports generating different files when test cases are run multiple times with the same arguments
    160              """
    161
    162              if len(self._screenshots) > 0:
    163                  name = self._get_unused_report_name(self._test_case_id, self._test_params)
    164
    165                  #Generate HTML based on template
    166                  html = self._tc_screenshot_template.render(test_case_id=self._test_case_id,
    167                                                                    test_case_name=self._test_case_name,
    168                                                                    screenshots=self._screenshots)
    169
    170                  #Filename includes a counter in case test case is being run more than once
    171                  htm_file = FileHelper.get_filename_from_string(name) + '.htm'
    172                  relative_filename = os.path.join(self._folder_dest, htm_file)
    173                  full_filename = os.path.join(self.screenshot_folder(), relative_filename)
    174
    175                  self._screenshot_report[name] = {"tc_id": self._test_case_id,
    176                                                          "tc_name": self._test_case_name,
    177                                                          "filename": relative_filename}
    178
    179                  f = open(full_filename, "w")
    180                  try:
    181                      f.write(html)
    182                  finally:
    183                      f.close()
    184
    185          request.addfinalizer(generate_report)
    186
    187
    188 -    def save_screenshot(self, description):
    189          """
    190          Saves screen shot for the current page
    191          """
    192          #Waits until page is loaded
    193          self.tlib_logger.debug("Saving screenshot")
    194          self.wait_for_page_loaded()
    195
    196          #Folder containing images for test case
    197          name = self._get_unused_report_name(self._test_case_id, self._test_params)
    198          name = FileHelper.get_filename_from_string(name)
    199
    200          test_folder = os.path.join(self.screenshot_folder(), self._folder_dest, name)
    201
    202          try:
    203              os.makedirs(test_folder)
    204          except WindowsError:
    205              pass
    206
    207          #Get number of existing images
    208          i = len(os.listdir(test_folder)) + 1
    209
    210          #Get filename
    211          img_filename = "%02d.png" % i
    212          full_path = os.path.join(test_folder, img_filename)
    213
    214          self._screenshots[i] = {"filename": "%s/%s" % (name, img_filename),
    215                                         u"description": description}
    216
    217          self._driver.get_screenshot_as_file(full_path)
    218
    219
    220 -    def wait_for_page_loaded(self, timeout=10):
    221          raise NotImplementedError("This method should be implemented by derived classes")
    222
    223
    224 -    def wait_for_alert(self, timeout=10):
    225          """
    226          Waist until an alert is visible
    227          @type timeout: Integer
    228          @param timeout: Number of seconds before timing out
    229          @rtype: bool
    230          """
    231          def is_alert_visible():
    232              try:
    233                  #Try to get alert text to trigger exception if alert is not visible
    234                  alert = self._driver.switch_to_alert().text
    235                  return True
    236              except NoAlertPresentException as e:
    237                  return False
    238
    239          condition = lambda *args: is_alert_visible()
    240          try:
    241              WebDriverWait(self._driver, timeout).until(condition)
    242              return self._driver.switch_to_alert()
    243          except TimeoutException:
    244              self.test_logger.error('Timeout while waiting for alert to appear')
    245              pytest.fail('Timeout while waiting for alert to appear')
    246
    247
    248 -    def wait_for_element_to_be_visible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    249          """
    250          Wait until an element becomes visible
    251          @param locator_strategy: Location strategy to use
    252          @type locator_strategy: By
    253          @param locator_string: String used to locate element
    254          @type locator_string: str
    255          @param error_msg: Error string to show if element is not found
    256          @type error_msg: str
    257          @param timeout: Maximum time in seconds to wait for the element to be visible
    258          @type timeout: int
    259          @rtype: WebElement
    260          """
    261          try:
    262              element = WebDriverWait(self._driver, timeout).until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string)))
    263              return element
    264          except TimeoutException:
    265              if error_msg is None:
    266                  error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string
    267              self.save_screenshot("[ERROR] %s" % error_msg)
    268              pytest.fail(error_msg)
    269
    270
    271 -    def wait_for_element_to_be_clickable(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    272          """
    273          Wait until an element cna be clicked
    274          @param locator_strategy: Location strategy to use
    275          @type locator_strategy: By
    276          @param locator_string: String used to locate element
    277          @type locator_string: str
    278          @param error_msg: Error string to show if element is not found
    279          @type error_msg: str
    280          @param timeout: Maximum time in seconds to wait for the element to be clickable
    281          @type timeout: int
    282          @rtype: WebElement
    283          """
    284          try:
    285              element = WebDriverWait(self._driver, timeout).until(expected_conditions.element_to_be_clickable((locator_strategy, locator_string)))
    286              return element
    287          except TimeoutException:
    288              if error_msg is None:
    289                  error_msg = "Timeout while waiting for element '%s' to be clickable" % locator_string
    290              self.save_screenshot("[ERROR] %s" % error_msg)
    291              pytest.fail(error_msg)
    292
    293
    294 -    def wait_for_element_to_be_present(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    295          """
    296          Wait until an element is present
    297          @param locator_strategy: Location strategy to use
    298          @type locator_strategy: By
    299          @param locator_string: String used to locate element
    300          @type locator_string: str
    301          @param error_msg: Error string to show if element is not found
    302          @type error_msg: str
    303          @param timeout: Maximum time in seconds to wait for the element to be present
    304          @type timeout: int
    305          @rtype: WebElement
    306          """
    307          try:
    308              element = WebDriverWait(self._driver, timeout).until(expected_conditions.alert_is_present((locator_strategy, locator_string)))
    309              return element
    310          except TimeoutException:
    311              if error_msg is None:
    312                  error_msg = "Timeout while waiting for element '%s' to be present" % locator_string
    313              self.save_screenshot("[ERROR] %s" % error_msg)
    314              pytest.fail(error_msg)
    315
    316
    317 -    def wait_for_element_to_be_selected(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    318          """
    319          Wait until an element is selected
    320          @param locator_strategy: Location strategy to use
    321          @type locator_strategy: By
    322          @param locator_string: String used to locate element
    323          @type locator_string: str
    324          @param error_msg: Error string to show if element is not found
    325          @type error_msg: str
    326          @param timeout: Maximum time in seconds to wait for the element to be selected
    327          @type timeout: int
    328          @rtype: WebElement
    329          """
    330          try:
    331              element = WebDriverWait(self._driver, timeout).until(expected_conditions.element_located_to_be_selected((locator_strategy, locator_string)))
    332              return element
    333          except TimeoutException:
    334              if error_msg is None:
    335                  error_msg = "Timeout while waiting for element '%s' to be selected" % locator_string
    336              self.save_screenshot("[ERROR] %s" % error_msg)
    337              pytest.fail(error_msg)
    338
    339
    340 -    def wait_for_element_to_be_invisible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    341          """
    342          Wait until an element becomes invisible
    343          @param locator_strategy: Location strategy to use
    344          @type locator_strategy: By
    345          @param locator_string: String used to locate element
    346          @type locator_string: str
    347          @param error_msg: Error string to show if element is not found
    348          @type error_msg: str
    349          @param timeout: Maximum time in seconds to wait for the element to be hidden
    350          @type timeout: int
    351          @rtype: WebElement
    352          """
    353          try:
    354              element = WebDriverWait(self._driver, timeout).until(expected_conditions.invisibility_of_element_located((locator_strategy, locator_string)))
    355              return element
    356          except TimeoutException:
    357              if error_msg is None:
    358                  error_msg = "Timeout while waiting for element '%s' to be invisible" % locator_string
    359              self.save_screenshot("[ERROR] %s" % error_msg)
    360              pytest.fail(error_msg)
    361
    362
    363 -    def wait_for_element_to_be_static(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    364          """
    365          Wait until an element that moves on the screen stops moving
    366          @param locator_strategy: Location strategy to use
    367          @type locator_strategy: By
    368          @param locator_string: String used to locate element
    369          @type locator_string: str
    370          @param error_msg: Error string to show if element is not found
    371          @type error_msg: str
    372          @param timeout: Maximum time in seconds to wait for the element to be visible
    373          @type timeout: int
    374          @rtype: WebElement
    375          """
    376          try:
    377              element = WebDriverWait(self._driver, timeout).until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string)))
    378
    379              #wait until element is not moving or click will fail
    380              old_location = {'x':0, 'y':0}
    381              while old_location != element.location:
    382                  self.tlib_logger.debug("Pop-up is still moving. Previous position: %s, current position: %s" % (old_location, element.location))
    383                  old_location = element.location
    384                  sleep(0.1)
    385                  element = self._driver.find_element(locator_strategy, locator_string)
    386
    387              return element
    388          except TimeoutException:
    389              if error_msg is None:
    390                  error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string
    391              self.save_screenshot("[ERROR] %s" % error_msg)
    392              pytest.fail(error_msg)
    393
    394
    395 -    def wait_for_text_to_be_present_in_element(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
    396          """
    397          Wait for an element that contains specified text
    398          @param locator_strategy: Location strategy to use
    399          @type locator_strategy: By
    400          @param locator_string: String used to locate element
    401          @type locator_string: str
    402          @param error_msg: Error string to show if element is not found
    403          @type error_msg: str
    404          @param timeout: Maximum time in seconds to wait
    405          @type timeout: int
    406          """
    407          try:
    408              WebDriverWait(self._driver, timeout).until(expected_conditions.text_to_be_present_in_element((locator_strategy, locator_string), text))
    409          except TimeoutException:
    410              if error_msg is None:
    411                  error_msg = "Timeout while waiting for text %(text)s to be present in element '%(element)s'" % {"text": text, "element":locator_string}
    412              self.save_screenshot("[ERROR] %s" % error_msg)
    413              pytest.fail(error_msg)
    414
    415
    416 -    def wait_for_text_to_be_present_in_element_value(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
    417          """
    418          Wait for an element's value to contain some test
    419          @param locator_strategy: Location strategy to use
    420          @type locator_strategy: By
    421          @param locator_string: String used to locate element
    422          @type locator_string: str
    423          @param error_msg: Error string to show if element is not found
    424          @type error_msg: str
    425          @param timeout: Maximum time in seconds to wait
    426          @type timeout: int
    427          """
    428          try:
    429              WebDriverWait(self._driver, timeout).until(expected_conditions.text_to_be_present_in_element_value((locator_strategy, locator_string), text))
    430          except TimeoutException:
    431              if error_msg is None:
    432                  error_msg = "Timeout while waiting for text %(text)s to be present in the value of element '%(element)s'" % {"text": text, "element":locator_string}
    433              self.save_screenshot("[ERROR] %s" % error_msg)
    434              pytest.fail(error_msg)
    435