1 import os
2 import re
3 import inspect
4
5 import pytest
6 import collections
7 from time import sleep
8
9 from selenium import webdriver
10 from _pytest.python import FixtureRequest
11
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.common.by import By
17 from selenium.webdriver.support.ui import WebDriverWait
18 from selenium.webdriver.support import expected_conditions
19 from selenium.common.exceptions import TimeoutException, NoAlertPresentException
20 from selenium.webdriver.remote.webelement import WebElement
21 import logging
27
28 _homepage = None
29 _test_logger = None
30 _tlib_logger = None
31 _browser = None
32
33 _folder_dest = None
34 _test_case_id = None
35 _test_case_name = None
36 _test_params = None
37
38 _jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(TestHelper.tlib_template_folder()), trim_blocks = True)
39
40 _screenshot_report = None
41 _screenshot_report_template = _jinja_env.get_template("screenshot_report.html")
42
43 _screenshots = {}
44 _tc_screenshot_template = _jinja_env.get_template("testcase_screenshots.html")
45
46
48 """
49 Returns Instance of the WebDriver browser
50 @rtype: webdriver.Remote
51 """
52 return self.__class__._browser
53
54
56 """
57 Instance of the WebDriver browser
58 @type browser: webdriver.Remote
59 """
60 self.__class__._browser = browser
61
62 browser = property(_get_browser, _set_browser)
63
64 - def _get_homepage(self):
65 """
66 Instance of the WebDriver homepage
67 """
68 return self.__class__._homepage
69
70
71 - def _set_homepage(self, homepage):
72 """
73 Instance of the WebDriver homepage
74 @type homepage: webdriver.Remote
75 """
76 self.__class__._homepage = homepage
77
78 homepage = property(_get_homepage, _set_homepage)
79
80
81 @property
83 """
84 Logger object to log test information
85 """
86 return self.__class__._test_logger
87
88
89 @staticmethod
91 """
92 Returns project's screenshot folder
93 Will try to find a folder names "screenshot" on the subclasses parent directory and will raise
94 exception NotImplementedError if folder is not found
95 """
96 for sc in SeleniumTester.__subclasses__():
97
98 filename = inspect.getfile(sc)
99
100
101 module_folder = os.path.dirname(filename)
102 parent_folder = os.path.abspath(os.path.join(module_folder, os.pardir))
103
104
105 screenshots = os.path.join(parent_folder, 'screenshots')
106 if os.path.exists(screenshots):
107 return screenshots
108
109
110 raise NotImplementedError("Could not find screenshot folder. Subclass should implement this method")
111
112
113 @pytest.fixture(scope='class', autouse=True)
126
127
129 """
130 Gets a string based on test case id and name, taking into account if test case has already been run or not
131 @param tc_id: Test case id
132 @type tc_id: str
133 @param tc_name: Test case name
134 @type tc_name: str
135 """
136 i = 0
137 while True:
138 if i == 0:
139
140 filename = "%(tc_id)s" % { "tc_id": tc_id}
141 else:
142
143 filename = "%(tc_id)s [retry %(cnt)d]" % { "tc_id": tc_id, "cnt": i}
144
145 if not filename in self.__class__._screenshot_report:
146 return filename
147
148 i += 1
149
150
151 @pytest.fixture(scope='class', autouse=True)
153 """
154 Generates HTML page with all test case results for the class
155 @type request: FixtureRequest
156 """
157 self.__class__._screenshot_report = collections.OrderedDict()
158 self.__class__._folder_dest = re.sub('\.py$', '', os.path.relpath(inspect.getfile(request.cls)))
159
160 def generate_report():
161 if len(self.__class__._screenshot_report) > 0:
162
163 html = self.__class__._screenshot_report_template.render(test_class=self.__class__.__name__,
164 files=self.__class__._screenshot_report)
165
166 htm_file = "%s.htm" % self.__class__._folder_dest
167 full_filename = os.path.join(self.screenshot_folder(), htm_file)
168
169 f = open(full_filename, "w")
170 try:
171 f.write(html)
172 finally:
173 f.close()
174
175 request.addfinalizer(generate_report)
176
177
178 @pytest.fixture(scope='function', autouse=True)
223
224
225 request.addfinalizer(generate_report)
226
227
228 - def navigate(self, page, save_screenshot=True):
236
237
238 - def go_home(self, save_screenshot=True):
245
246
248 """
249 Saves screen shot for the current page
250 """
251
252 def get_params():
253 if self.__class__._test_params is None:
254 return ""
255 else:
256 return self.__class__._test_params
257
258
259 self._tlib_logger.debug("Saving screenshot")
260 self.wait_for_page_loaded()
261
262
263 name = self.get_unused_report_name(self.__class__._test_case_id, self.__class__._test_case_name)
264 name = FileHelper.get_filename_from_string(name)
265
266 test_folder = os.path.join(self.screenshot_folder(), self.__class__._folder_dest, name)
267
268 try:
269 os.makedirs(test_folder)
270 except WindowsError:
271 pass
272
273
274 i = len(os.listdir(test_folder)) + 1
275
276
277 img_filename = "%02d.png" % i
278 full_path = os.path.join(test_folder, img_filename)
279
280 self.__class__._screenshots[i] = {"filename": "%s/%s" % (name, img_filename),
281 u"description": description}
282
283 self.browser.get_screenshot_as_file(full_path)
284
285
286 - def wait_for_page_loaded(self, timeout=10):
287 """
288 Waist until document.readyState is equal to complete
289 @type timeout: Integer
290 @param timeout: Number of seconds before timing out
291 """
292 if self.browser.execute_script("return document.readyState") == "complete":
293 return
294 else:
295 self._tlib_logger.debug("Waiting for '%s' to load" % self.browser.current_url)
296
297 condition = lambda *args: self.browser.execute_script("return document.readyState") == "complete"
298 try:
299 WebDriverWait(self.browser, timeout).until(condition)
300 except TimeoutException:
301 self.test_logger.error('Timeout while waiting for page to load')
302 pytest.fail('Timeout while waiting for page to load')
303
304 self._tlib_logger.debug("Page '%s' finished loading" % self.browser.current_url)
305
307 """
308 Waist until an alert is visible
309 @type timeout: Integer
310 @param timeout: Number of seconds before timing out
311 @rtype: bool
312 """
313 def is_alert_visible():
314 try:
315
316 alert = self.browser.switch_to_alert().text
317 return True
318 except NoAlertPresentException as e:
319 return False
320
321 condition = lambda *args: is_alert_visible()
322 try:
323 WebDriverWait(self.browser, timeout).until(condition)
324 return self.browser.switch_to_alert()
325 except TimeoutException:
326 self.test_logger.error('Timeout while waiting for alert to appear')
327 pytest.fail('Timeout while waiting for alert to appear')
328
329 @pytest.fixture(scope='function', autouse=True)
330 - def setup_test(self, request, test_logger, browser):
331 """
332 Goes to homepage before each test, unless marker skipsetup is given
333 """
334
335 if not request.node.function.__dict__.has_key('skipsetup'):
336
337 self.go_home(save_screenshot=False)
338 browser.delete_all_cookies()
339 self.go_home()
340 else:
341 test_logger.info("Skipping setup")
342
343
345 """
346 Wait until an element becomes visible
347 @param locator_strategy: Location strategy to use
348 @type locator_strategy: By
349 @param locator_string: String used to locate element
350 @type locator_string: str
351 @param error_msg: Error string to show if element is not found
352 @type error_msg: str
353 @param timeout: Maximum time in seconds to wait for the element to be visible
354 @type timeout: int
355 @rtype: WebElement
356 """
357 try:
358 element = WebDriverWait(self.browser, timeout).until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string)))
359 return element
360 except TimeoutException:
361 if error_msg is None:
362 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string
363 self.save_screenshot("[ERROR] %s" % error_msg)
364 pytest.fail(error_msg)
365
366
368 """
369 Wait until an element cna be clicked
370 @param locator_strategy: Location strategy to use
371 @type locator_strategy: By
372 @param locator_string: String used to locate element
373 @type locator_string: str
374 @param error_msg: Error string to show if element is not found
375 @type error_msg: str
376 @param timeout: Maximum time in seconds to wait for the element to be clickable
377 @type timeout: int
378 @rtype: WebElement
379 """
380 try:
381 element = WebDriverWait(self.browser, timeout).until(expected_conditions.element_to_be_clickable((locator_strategy, locator_string)))
382 return element
383 except TimeoutException:
384 if error_msg is None:
385 error_msg = "Timeout while waiting for element '%s' to be clickable" % locator_string
386 self.save_screenshot("[ERROR] %s" % error_msg)
387 pytest.fail(error_msg)
388
389
391 """
392 Wait until an element is present
393 @param locator_strategy: Location strategy to use
394 @type locator_strategy: By
395 @param locator_string: String used to locate element
396 @type locator_string: str
397 @param error_msg: Error string to show if element is not found
398 @type error_msg: str
399 @param timeout: Maximum time in seconds to wait for the element to be present
400 @type timeout: int
401 @rtype: WebElement
402 """
403 try:
404 element = WebDriverWait(self.browser, timeout).until(expected_conditions.alert_is_present((locator_strategy, locator_string)))
405 return element
406 except TimeoutException:
407 if error_msg is None:
408 error_msg = "Timeout while waiting for element '%s' to be present" % locator_string
409 self.save_screenshot("[ERROR] %s" % error_msg)
410 pytest.fail(error_msg)
411
412
414 """
415 Wait until an element is selected
416 @param locator_strategy: Location strategy to use
417 @type locator_strategy: By
418 @param locator_string: String used to locate element
419 @type locator_string: str
420 @param error_msg: Error string to show if element is not found
421 @type error_msg: str
422 @param timeout: Maximum time in seconds to wait for the element to be selected
423 @type timeout: int
424 @rtype: WebElement
425 """
426 try:
427 element = WebDriverWait(self.browser, timeout).until(expected_conditions.element_located_to_be_selected((locator_strategy, locator_string)))
428 return element
429 except TimeoutException:
430 if error_msg is None:
431 error_msg = "Timeout while waiting for element '%s' to be selected" % locator_string
432 self.save_screenshot("[ERROR] %s" % error_msg)
433 pytest.fail(error_msg)
434
435
437 """
438 Wait until an element becomes invisible
439 @param locator_strategy: Location strategy to use
440 @type locator_strategy: By
441 @param locator_string: String used to locate element
442 @type locator_string: str
443 @param error_msg: Error string to show if element is not found
444 @type error_msg: str
445 @param timeout: Maximum time in seconds to wait for the element to be hidden
446 @type timeout: int
447 @rtype: WebElement
448 """
449 try:
450 element = WebDriverWait(self.browser, timeout).until(expected_conditions.invisibility_of_element_located((locator_strategy, locator_string)))
451 return element
452 except TimeoutException:
453 if error_msg is None:
454 error_msg = "Timeout while waiting for element '%s' to be invisible" % locator_string
455 self.save_screenshot("[ERROR] %s" % error_msg)
456 pytest.fail(error_msg)
457
458
460 """
461 Wait until an element that moves on the screen stops moving
462 @param locator_strategy: Location strategy to use
463 @type locator_strategy: By
464 @param locator_string: String used to locate element
465 @type locator_string: str
466 @param error_msg: Error string to show if element is not found
467 @type error_msg: str
468 @param timeout: Maximum time in seconds to wait for the element to be visible
469 @type timeout: int
470 @rtype: WebElement
471 """
472 try:
473 element = WebDriverWait(self.browser, timeout).until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string)))
474
475
476 old_location = {'x':0, 'y':0}
477 while old_location != element.location:
478 self._tlib_logger.debug("Pop-up is still moving. Previous position: %s, current position: %s" % (old_location, element.location))
479 old_location = element.location
480 sleep(0.1)
481 element = self.browser.find_element(locator_strategy, locator_string)
482
483 return element
484 except TimeoutException:
485 if error_msg is None:
486 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string
487 self.save_screenshot("[ERROR] %s" % error_msg)
488 pytest.fail(error_msg)
489
490
491 - def wait_for_text_to_be_present_in_element(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
492 """
493 Wait for an element that contains specified text
494 @param locator_strategy: Location strategy to use
495 @type locator_strategy: By
496 @param locator_string: String used to locate element
497 @type locator_string: str
498 @param error_msg: Error string to show if element is not found
499 @type error_msg: str
500 @param timeout: Maximum time in seconds to wait
501 @type timeout: int
502 """
503 try:
504 WebDriverWait(self.browser, timeout).until(expected_conditions.text_to_be_present_in_element((locator_strategy, locator_string), text))
505 except TimeoutException:
506 if error_msg is None:
507 error_msg = "Timeout while waiting for text %(text)s to be present in element '%(element)s'" % {"text": text, "element":locator_string}
508 self.save_screenshot("[ERROR] %s" % error_msg)
509 pytest.fail(error_msg)
510
511
512 - def wait_for_text_to_be_present_in_element_value(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
513 """
514 Wait for an element's value to contain some test
515 @param locator_strategy: Location strategy to use
516 @type locator_strategy: By
517 @param locator_string: String used to locate element
518 @type locator_string: str
519 @param error_msg: Error string to show if element is not found
520 @type error_msg: str
521 @param timeout: Maximum time in seconds to wait
522 @type timeout: int
523 """
524 try:
525 WebDriverWait(self.browser, timeout).until(expected_conditions.text_to_be_present_in_element_value((locator_strategy, locator_string), text))
526 except TimeoutException:
527 if error_msg is None:
528 error_msg = "Timeout while waiting for text %(text)s to be present in the value of element '%(element)s'" % {"text": text, "element":locator_string}
529 self.save_screenshot("[ERROR] %s" % error_msg)
530 pytest.fail(error_msg)
531
532
534 try:
535 return self.browser.find_element_by_link_text(text)
536 except NoSuchElementException:
537 pytest.fail("Could not find the link: '%s'" % text)
538
540 try:
541 self.wait_for_element_to_be_visible(By.XPATH, locator_xpath)
542 return self.browser.find_element_by_xpath(locator_xpath)
543 except NoSuchElementException:
544 pytest.fail("Could not find the xpath: '%s'" % locator_xpath)
545
547 try:
548 self.wait_for_element_to_be_visible(By.CSS_SELECTOR, locator_css)
549 return self.browser.find_element_by_css_selector(locator_css)
550 except NoSuchElementException:
551 pytest.fail("Could not find css: '%s'" %locator_css)
552
554 try:
555 return self.browser.find_elements_by_xpath(text)
556 except NoSuchElementException:
557 pytest.fail("Could not find the xpath: '%s'" %text)
558
560 try:
561 return self.browser.find_elements_by_css_selector(locator_css)
562 except NoSuchElementException:
563 pytest.fail("Could not find css: '%s'" %locator_css)
564