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

# Source Code for Module tlib.base.SeleniumTester

      1  import os
      2  import re
      3  import inspect
      4  # noinspection PyPackageRequirements
      5  import pytest
      6  import collections
      7  # noinspection PyPackageRequirements
      8  from selenium import webdriver
      9  from _pytest.python import FixtureRequest
     10  from _pytest.mark import MarkInfo
     11  # noinspection PyPackageRequirements
     12  import jinja2
     13  from tlib.base import TestHelper
     14  from tlib.base import FileHelper
     15  from tlib.base.PytestTester import PytestTester
     16  from selenium.webdriver.support.ui import WebDriverWait
     17  from selenium.webdriver.support import expected_conditions
     18  from selenium.common.exceptions import TimeoutException
     19  import logging
     20
     21
     22  # noinspection PyMethodParameters
     23  # noinspection PyUnresolvedReferences
     24 -class SeleniumTester(PytestTester):
     25
     26      _homepage = None
     27      _test_logger = None # Reference to test logger
     28      _tlib_logger = None # Reference to tlib logger
     29      _browser = None
     30
     31      _folder_dest = None # destination of report for the current class
     32      _test_case_id = None
     33      _test_case_name = None
     34      _test_params = None
     35
     36      _jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(TestHelper.tlib_template_folder()), trim_blocks = True)
     37
     38      _screenshot_report = None # List of tests for which there are screenshots
     39      _screenshot_report_template = _jinja_env.get_template("screenshot_report.html") # Template for generating screenshot report
     40
     41      _screenshots = {} # Screenshots generated during a test case
     42      _tc_screenshot_template = _jinja_env.get_template("testcase_screenshots.html") # Template for generating screenshots for a test case
     43
     44
     45 -    def _get_browser(self):
     46          """
     47          Returns Instance of the WebDriver browser
     48          """
     49          return self.__class__._browser
     50
     51
     52 -    def _set_browser(self, browser):
     53          """
     54          Instance of the WebDriver browser
     55          @type: webdriver.Remote
     56          """
     57          self.__class__._browser = browser
     58
     59      browser = property(_get_browser, _set_browser)
     60
     61 -    def _get_homepage(self):
     62          """
     63          Instance of the WebDriver homepage
     64          @type: webdriver.Remote
     65          """
     66          return self.__class__._homepage
     67
     68
     69 -    def _set_homepage(self, homepage):
     70          """
     71          Instance of the WebDriver homepage
     72          @type: webdriver.Remote
     73          """
     74          self.__class__._homepage = homepage
     75
     76      homepage = property(_get_homepage, _set_homepage)
     77
     78
     79      @property
     80 -    def test_logger(self):
     81          """
     82          Logger object to log test information
     83          @type: logging
     84          """
     85          return self.__class__._test_logger
     86
     87
     88      @staticmethod
     89 -    def screenshot_folder():
     90          """
     91          Returns project's screenshot folder
     92          This method must be defined in subclasses
     93          """
     94          raise NotImplementedError("Subclasses should implement this!")
     95
     96
     97      @pytest.fixture(scope='class', autouse=True)
     98 -    def initialize_class(self, test_logger, tlib_logger, browser, base_url):
     99          """
    100          @type test_logger: logging
    101          @type browser: webdriver.Remote
    102          Stores an instance of browser and test_logger
    103          """
    104          self.__class__._test_logger = test_logger
    105          self.__class__._tlib_logger = tlib_logger
    106          self.browser = browser
    107          # Only initialize homepage if it wasn't already initialized
    108          if self.homepage is None:
    109              self.homepage = base_url
    110
    111
    112 -    def get_unused_report_name(self, tc_id, tc_name):
    113          """
    114          Gets a string based on test case id and name, taking into account if test case has already been run or not
    115          @param tc_id: Test case id
    116          @type tc_id: str
    117          @param tc_name: Test case name
    118          @type tc_name: str
    119          """
    120          i = 0
    121          while True:
    122              if i == 0:
    123                  # filename = "%(tc_id)s - %(tc_name)s" % { "tc_id": tc_id, "tc_name": tc_name}
    124                  filename = "%(tc_id)s" % { "tc_id": tc_id}
    125              else:
    126                  # filename = "%(tc_id)s - %(tc_name)s[retry %(cnt)d]" % { "tc_id": tc_id, "tc_name": tc_name, "cnt": i}
    127                  filename = "%(tc_id)s [retry %(cnt)d]" % { "tc_id": tc_id, "cnt": i}
    128
    129              if not filename in self.__class__._screenshot_report:
    130                  return filename
    131
    132              i += 1
    133
    134
    135      @pytest.fixture(scope='class', autouse=True)
    136 -    def generate_class_report(self, request):
    137          """
    138          Generates HTML page with all test case results for the class
    139          @type request: FixtureRequest
    140          """
    141          self.__class__._screenshot_report = collections.OrderedDict()
    142          self.__class__._folder_dest = re.sub('\.py$', '', os.path.relpath(inspect.getfile(request.cls)))
    143
    144          def generate_report():
    145              if len(self.__class__._screenshot_report) > 0:
    146                  #Generate HTML based on template
    147                  html = self.__class__._screenshot_report_template.render(test_class=self.__class__.__name__,
    148                              files=self.__class__._screenshot_report)
    149
    150                  htm_file = "%s.htm" % self.__class__._folder_dest
    151                  full_filename = os.path.join(self.screenshot_folder(), htm_file)
    152
    153                  f = open(full_filename, "w")
    154                  try:
    155                      f.write(html)
    156                  finally:
    157                      f.close()
    158
    159          request.addfinalizer(generate_report)
    160
    161
    162      @pytest.fixture(scope='function', autouse=True)
    163 -    def generate_test_report(self, request):
    164          """
    165          Generates HTML page with all the screenshots for a test case
    166          Supports generating different When test cases are run multiple times with the same arguments
    167          @type request: FixtureRequest
    168          """
    169          # noinspection PyProtectedMember
    170          self.__class__._test_params = request.keywords.node._genid
    171          self.__class__._test_case_name = request.keywords.node.name
    172          self.__class__._screenshots = {}
    173
    174          #Get marker test case name or use a default if there is none
    175          marker = request.node.get_marker("testcasename")
    176          if marker is None:
    177              self._test_logger.warn("Test case doesn't have marker testcasename")
    178              self.__class__._test_case_id = "UNKNOWN_TEST_CASE_ID"
    179          else:
    180              self.__class__._test_case_id = marker.args[0]
    181
    182
    183          def generate_report():
    184
    185              if len(self.__class__._screenshots) > 0:
    186                  name = self.get_unused_report_name(self.__class__._test_case_id, self.__class__._test_case_name)
    187
    188                  #Generate HTML based on template
    189                  html = self.__class__._tc_screenshot_template.render(test_case_id=self.__class__._test_case_id,
    190                              test_case_name=self.__class__._test_case_name,
    191                              screenshots=self.__class__._screenshots)
    192
    193                  #Filename includes a counter in case test case is being run more than once
    194                  htm_file = FileHelper.get_filename_from_string(name) + '.htm'
    195                  relative_filename = os.path.join(self.__class__._folder_dest, htm_file)
    196                  full_filename = os.path.join(self.screenshot_folder(), relative_filename)
    197
    198                  self.__class__._screenshot_report[name] = {"tc_id": self.__class__._test_case_id,
    199                                                             "tc_name": self.__class__._test_case_name,
    200                                                             "filename": relative_filename}
    201
    202                  f = open(full_filename, "w")
    203                  try:
    204                      f.write(html)
    205                  finally:
    206                      f.close()
    207
    208
    209          request.addfinalizer(generate_report)
    210
    211
    212 -    def navigate(self, page, save_screenshot=True):
    213          """
    214          Navigate to "page"
    215          """
    216          self.test_logger.info("Navigating to %s" % page)
    217          self.browser.get(page)
    218          if save_screenshot:
    219              self.save_screenshot("Navigate to %s" % page)
    220
    221
    222 -    def go_home(self, save_screenshot=True):
    223          """
    224          Navigate to home page
    225          """
    226          self.navigate(self.homepage, save_screenshot=False)
    227          if save_screenshot:
    228              self.save_screenshot("Homepage")
    229
    230
    231 -    def save_screenshot(self, description):
    232          """
    233          Saves screen shot for the current page
    234          """
    235
    236          def get_params():
    237              if self.__class__._test_params is None:
    238                  return ""
    239              else:
    240                  return self.__class__._test_params
    241
    242          #Waits until page is loaded
    243          self._tlib_logger.debug("Saving screenshot")
    244          self.wait_for_page_loaded()
    245
    246          #Folder containing images for test case
    247          name = self.get_unused_report_name(self.__class__._test_case_id, self.__class__._test_case_name)
    248          name = FileHelper.get_filename_from_string(name)
    249
    250          test_folder = os.path.join(self.screenshot_folder(), self.__class__._folder_dest, name)
    251
    252          try:
    253              os.makedirs(test_folder)
    254          except WindowsError:
    255              pass
    256
    257          #Get number of existing images
    258          i = len(os.listdir(test_folder)) + 1
    259
    260          #Get filename
    261          img_filename = "%02d.jpg" % i
    262          full_path = os.path.join(test_folder, img_filename)
    263
    264          self.__class__._screenshots[i] = {"filename": "%s/%s" % (name, img_filename),
    265                                            "description": description}
    266
    267          self.browser.get_screenshot_as_file(full_path)
    268
    269
    270 -    def wait_for_page_loaded(self, timeout=10):
    271          """
    272          Waist until document.readyState is equal to complete
    273          @type timeout: Integer
    274          @param timeout: Number of seconds before timing out
    275          """
    276          if self.browser.execute_script("return document.readyState") == "complete":
    277              return
    278          else:
    279              self._tlib_logger.debug("Waiting for '%s' to load" % self.browser.current_url)
    280
    281          condition = lambda *args: self.browser.execute_script("return document.readyState") == "complete"
    282          try:
    283              WebDriverWait(self.browser, timeout).until(condition)
    284          except TimeoutException:
    285              self.test_logger.error('Timeout while waiting for page to load')
    286              pytest.fail('Timeout while waiting for page to load')
    287
    288          self._tlib_logger.debug("Page '%s' finished loading" % self.browser.current_url)
    289
    290      @pytest.fixture(scope='function', autouse=True)
    291 -    def setup_method(self, request):
    292          """
    293          Goes to homepage before each test, unless marker skipsetup is given
    294          """
    295          #Get all markers
    296          marks = {}
    297          for k,v in request.node.function.__dict__.iteritems():
    298              if isinstance(v, MarkInfo):
    299                  marks[k] = v
    300
    301          #Don't go to home is skipsetup marker was given
    302          if not marks.has_key('skipsetup'):
    303              #Before clearing cookies, need to go to home screen, to ensure it's the current domain
    304              self.go_home(save_screenshot=False)
    305              self.browser.delete_all_cookies()
    306              self.go_home()
    307          else:
    308              self.test_logger.info("Skipping setup")
    309
    310
    311 -    def wait_for_element_to_be_visible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    312          try:
    313              element = WebDriverWait(self.browser, timeout).until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string)))
    314              return element
    315          except TimeoutException:
    316              if error_msg is None:
    317                  error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string
    318              self.save_screenshot("[ERROR] %s" % error_msg)
    319              pytest.fail(error_msg)
    320
    321
    322 -    def wait_for_element_to_be_clickable(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    323          try:
    324              element = WebDriverWait(self.browser, timeout).until(expected_conditions.element_to_be_clickable((locator_strategy, locator_string)))
    325              return element
    326          except TimeoutException:
    327              if error_msg is None:
    328                  error_msg = "Timeout while waiting for element '%s' to be clickable" % locator_string
    329              self.save_screenshot("[ERROR] %s" % error_msg)
    330              pytest.fail(error_msg)
    331
    332
    333 -    def wait_for_element_to_be_present(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    334          try:
    335              element = WebDriverWait(self.browser, timeout).until(expected_conditions.alert_is_present((locator_strategy, locator_string)))
    336              return element
    337          except TimeoutException:
    338              if error_msg is None:
    339                  error_msg = "Timeout while waiting for element '%s' to be present" % locator_string
    340              self.save_screenshot("[ERROR] %s" % error_msg)
    341              pytest.fail(error_msg)
    342
    343
    344 -    def wait_for_element_to_be_selected(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    345          try:
    346              element = WebDriverWait(self.browser, timeout).until(expected_conditions.element_located_to_be_selected((locator_strategy, locator_string)))
    347              return element
    348          except TimeoutException:
    349              if error_msg is None:
    350                  error_msg = "Timeout while waiting for element '%s' to be selected" % locator_string
    351              self.save_screenshot("[ERROR] %s" % error_msg)
    352              pytest.fail(error_msg)
    353
    354
    355 -    def wait_for_element_to_be_invisible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    356          try:
    357              element = WebDriverWait(self.browser, timeout).until(expected_conditions.invisibility_of_element_located((locator_strategy, locator_string)))
    358              return element
    359          except TimeoutException:
    360              if error_msg is None:
    361                  error_msg = "Timeout while waiting for element '%s' to be invisible" % locator_string
    362              self.save_screenshot("[ERROR] %s" % error_msg)
    363              pytest.fail(error_msg)
    364
    365
    366 -    def wait_for_test_to_be_present_in_element(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
    367          try:
    368              WebDriverWait(self.browser, timeout).until(expected_conditions.text_to_be_present_in_element((locator_strategy, locator_string), text))
    369          except TimeoutException:
    370              if error_msg is None:
    371                  error_msg = "Timeout while waiting for text %(text)s to be present in element '%(element)s'" % {"text": text, "element":locator_string}
    372              self.save_screenshot("[ERROR] %s" % error_msg)
    373              pytest.fail(error_msg)
    374
    375
    376 -    def wait_for_test_to_be_present_in_element_value(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
    377          try:
    378              WebDriverWait(self.browser, timeout).until(expected_conditions.text_to_be_present_in_element_value((locator_strategy, locator_string), text))
    379          except TimeoutException:
    380              if error_msg is None:
    381                  error_msg = "Timeout while waiting for text %(text)s to be present in the value of element '%(element)s'" % {"text": text, "element":locator_string}
    382              self.save_screenshot("[ERROR] %s" % error_msg)
    383              pytest.fail(error_msg)
    384