1 import os
2 import inspect
3
4 import pytest
5 import collections
6 from time import sleep
7
8 from _pytest.python import FixtureRequest
9
10 import jinja2
11 from tlib.base import TestHelper
12 from tlib.base import FileHelper
13 from tlib.base.PytestTester import PytestTester
14 from selenium.webdriver.common.by import By
15 from selenium.webdriver.support.ui import WebDriverWait
16 from selenium.webdriver.support import expected_conditions
17 from selenium.common.exceptions import TimeoutException, NoAlertPresentException, NoSuchElementException
18 from selenium.webdriver.remote.webelement import WebElement
19 from TestHelper import Singleton
25
26 __metaclass__ = Singleton
27
28 _driver = None
29
30 _folder_dest = None
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
39
40
41 _screenshot_report_template = _jinja_env.get_template("screenshot_report.html")
42
43 _screenshots = {}
44
45
46 _tc_screenshot_template = _jinja_env.get_template("testcase_screenshots.html")
47
48 _request = None
49
63
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
72 curr_folder = os.path.dirname(self._request.fspath.strpath)
73
74
75 for i in range(1, 4):
76 curr_folder = os.path.abspath(os.path.join(curr_folder, os.pardir))
77
78
79 screenshot_folder = os.path.join(curr_folder, 'screenshots')
80 if os.path.exists(screenshot_folder):
81 return screenshot_folder
82
84 """
85 Gets a string based on test case id and name, taking into account if test case has already been run or not
86 @param tc_id: Test case id
87 @type tc_id: str
88 @type tc_name: str
89 """
90 i = 0
91 while True:
92 if i == 0:
93 filename = "%(tc_id)s_%(tc_params)s" % {"tc_id": tc_id, "tc_params": tc_params}
94 else:
95 filename = "%(tc_id)s_%(tc_params)s [retry %(cnt)d]" % \
96 {"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 @pytest.fixture(scope='class', autouse=True)
105 """
106 @type request: FixtureRequest
107 """
108
109
110 self._screenshot_report = collections.OrderedDict()
111 self._folder_dest = os.path.basename(os.path.dirname(os.path.abspath(inspect.getfile(request.cls))))
112
113 def generate_report():
114 if len(self._screenshot_report) > 0:
115
116 html = self._screenshot_report_template.render(test_class=request.cls.__name__,
117 files=self._screenshot_report)
118
119 htm_file = "%s.htm" % self._folder_dest
120 full_filename = os.path.join(self.screenshot_folder(), htm_file)
121
122 f = open(full_filename, "w")
123 try:
124 f.write(html)
125 finally:
126 f.close()
127
128 request.addfinalizer(generate_report)
129
130 @pytest.fixture(scope='function', autouse=True)
187
188 request.addfinalizer(generate_report)
189
221
223 """
224 Removes non-ascii characters from a string
225 @type str: string
226 @param str: String with non-ascii characters
227 @rtype: string
228 """
229 return ''.join([i if ord(i) < 128 else '' for i in str])
230
232 """
233 Compare two images, if they are equal return True else False, if empty then None
234 @type ref: ImageFile
235 @type new: ImageFile
236 @param ref: Reference Image
237 @param new: Image captured at run time
238 @rtype: bool
239 """
240 sleep(0.2)
241 return ImageChops.difference(ref, new).getbbox() is None
242
243
245 """
246 Takes a screenshot of the entire view as a PIL.Image
247 @rtype: Image
248 """
249 data = self._driver.get_screenshot_as_base64()
250 return Image.open(StringIO(base64.decodestring(data)))
251
253 """
254 Get the dimensions of the element
255 @type element: WebElement
256 @param element: Element whose dimensions need to calculated
257 @rtype: tuple (element dimensions)
258 """
259 location = element.location
260 size = element.size
261 return (location['x'],
262 location['y'],
263 location['x'] + size['width'],
264 location['y'] + size['height']
265 )
266
268 """
269 Get page screenshot cropped to only include the given element
270 @type element: WebElement
271 @param element: WebElement to be extracted
272 @rtype: Extracted/Cropped WebElement
273 """
274 dimensions = self._get_dimensions(element)
275 original_image = self.take_screenshot()
276 cropped_image = original_image.crop(dimensions)
277
278 test_folder = os.path.join(self.screenshot_folder(), self._folder_dest)
279 cropped_image.save("%s\Captured-%s.png" % (test_folder, self._test_params))
280
281 return cropped_image
282
283 - def wait_for_page_loaded(self, timeout=10):
284 raise NotImplementedError("This method should be implemented by derived classes")
285
287 """
288 Waist until an alert is visible
289 @type timeout: Integer
290 @param timeout: Number of seconds before timing out
291 @rtype: bool
292 """
293 def is_alert_visible():
294 try:
295
296 alert = self._driver.switch_to_alert().text
297 return True
298 except NoAlertPresentException as e:
299 return False
300
301 condition = lambda *args: is_alert_visible()
302 try:
303 WebDriverWait(self._driver, timeout).until(condition)
304 return self._driver.switch_to_alert()
305 except TimeoutException:
306 self.test_logger.error('Timeout while waiting for alert to appear')
307 raise RuntimeError('Timeout while waiting for alert to appear')
308
310 """
311 Wait until an element becomes visible
312 @param locator_strategy: Location strategy to use
313 @type locator_strategy: By
314 @param locator_string: String used to locate element
315 @type locator_string: str
316 @param error_msg: Error string to show if element is not found
317 @type error_msg: str
318 @param timeout: Maximum time in seconds to wait for the element to be visible
319 @type timeout: int
320 @rtype: WebElement
321 """
322 try:
323 element = WebDriverWait(self._driver, timeout).\
324 until(expected_conditions.visibility_of_element_located((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 visible" % locator_string
329 self.save_screenshot("[ERROR] %s" % error_msg)
330 raise RuntimeError(error_msg)
331
333 """
334 Wait until an element cna be clicked
335 @param locator_strategy: Location strategy to use
336 @type locator_strategy: By
337 @param locator_string: String used to locate element
338 @type locator_string: str
339 @param error_msg: Error string to show if element is not found
340 @type error_msg: str
341 @param timeout: Maximum time in seconds to wait for the element to be clickable
342 @type timeout: int
343 @rtype: WebElement
344 """
345 try:
346 element = WebDriverWait(self._driver, timeout).\
347 until(expected_conditions.element_to_be_clickable((locator_strategy, locator_string)))
348 return element
349 except TimeoutException:
350 if error_msg is None:
351 error_msg = "Timeout while waiting for element '%s' to be clickable" % locator_string
352 self.save_screenshot("[ERROR] %s" % error_msg)
353 raise RuntimeError(error_msg)
354
356 """
357 Wait until an element is present
358 @param locator_strategy: Location strategy to use
359 @type locator_strategy: By
360 @param locator_string: String used to locate element
361 @type locator_string: str
362 @param error_msg: Error string to show if element is not found
363 @type error_msg: str
364 @param timeout: Maximum time in seconds to wait for the element to be present
365 @type timeout: int
366 @rtype: WebElement
367 """
368 try:
369 element = WebDriverWait(self._driver, timeout).\
370 until(expected_conditions.presence_of_element_located((locator_strategy, locator_string)))
371 return element
372 except TimeoutException:
373 if error_msg is None:
374 error_msg = "Timeout while waiting for element '%s' to be present" % locator_string
375 self.save_screenshot("[ERROR] %s" % error_msg)
376 raise RuntimeError(error_msg)
377
379 """
380 Wait until an element is selected
381 @param locator_strategy: Location strategy to use
382 @type locator_strategy: By
383 @param locator_string: String used to locate element
384 @type locator_string: str
385 @param error_msg: Error string to show if element is not found
386 @type error_msg: str
387 @param timeout: Maximum time in seconds to wait for the element to be selected
388 @type timeout: int
389 @rtype: WebElement
390 """
391 try:
392 element = WebDriverWait(self._driver, timeout).\
393 until(expected_conditions.element_located_to_be_selected((locator_strategy, locator_string)))
394 return element
395 except TimeoutException:
396 if error_msg is None:
397 error_msg = "Timeout while waiting for element '%s' to be selected" % locator_string
398 self.save_screenshot("[ERROR] %s" % error_msg)
399 raise RuntimeError(error_msg)
400
402 """
403 Wait until an element becomes invisible
404 @param locator_strategy: Location strategy to use
405 @type locator_strategy: By
406 @param locator_string: String used to locate element
407 @type locator_string: str
408 @param error_msg: Error string to show if element is not found
409 @type error_msg: str
410 @param timeout: Maximum time in seconds to wait for the element to be hidden
411 @type timeout: int
412 @rtype: WebElement
413 """
414 try:
415 element = WebDriverWait(self._driver, timeout).\
416 until(expected_conditions.invisibility_of_element_located((locator_strategy, locator_string)))
417 return element
418 except TimeoutException:
419 if error_msg is None:
420 error_msg = "Timeout while waiting for element '%s' to be invisible" % locator_string
421 self.save_screenshot("[ERROR] %s" % error_msg)
422 raise RuntimeError(error_msg)
423
425 """
426 Wait until an element that moves on the screen stops moving
427 @param locator_strategy: Location strategy to use
428 @type locator_strategy: By
429 @param locator_string: String used to locate element
430 @type locator_string: str
431 @param error_msg: Error string to show if element is not found
432 @type error_msg: str
433 @param timeout: Maximum time in seconds to wait for the element to be visible
434 @type timeout: int
435 @rtype: WebElement
436 """
437 try:
438 element = WebDriverWait(self._driver, timeout).\
439 until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string)))
440
441
442 old_location = {'x': 0, 'y': 0}
443 while old_location != element.location:
444 self.tlib_logger.debug("Pop-up is still moving. Previous position: %s, current position: %s" %
445 (old_location, element.location))
446 old_location = element.location
447 sleep(0.1)
448 element = self._driver.find_element(locator_strategy, locator_string)
449
450 return element
451 except TimeoutException:
452 if error_msg is None:
453 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string
454 self.save_screenshot("[ERROR] %s" % error_msg)
455 raise RuntimeError(error_msg)
456
457 - def wait_for_text_to_be_present_in_element(self, locator_strategy, locator_string, text,
458 error_msg=None, timeout=10):
459 """
460 Wait for an element that contains specified text
461 @param locator_strategy: Location strategy to use
462 @type locator_strategy: By
463 @param locator_string: String used to locate element
464 @type locator_string: str
465 @param error_msg: Error string to show if element is not found
466 @type error_msg: str
467 @param timeout: Maximum time in seconds to wait
468 @type timeout: int
469 """
470 try:
471 WebDriverWait(self._driver, timeout).\
472 until(expected_conditions.text_to_be_present_in_element((locator_strategy, locator_string), text))
473 except TimeoutException:
474 if error_msg is None:
475 error_msg = "Timeout while waiting for text %(text)s to be present in element '%(element)s'" % \
476 {"text": text, "element": locator_string}
477 self.save_screenshot("[ERROR] %s" % error_msg)
478 raise RuntimeError(error_msg)
479
480 - def wait_for_text_to_be_present_in_element_value(self, locator_strategy, locator_string, text,
481 error_msg=None, timeout=10):
482 """
483 Wait for an element's value to contain some test
484 @param locator_strategy: Location strategy to use
485 @type locator_strategy: By
486 @param locator_string: String used to locate element
487 @type locator_string: str
488 @param error_msg: Error string to show if element is not found
489 @type error_msg: str
490 @param timeout: Maximum time in seconds to wait
491 @type timeout: int
492 """
493 try:
494 WebDriverWait(self._driver, timeout).\
495 until(expected_conditions.text_to_be_present_in_element_value((locator_strategy, locator_string), text))
496 except TimeoutException:
497 if error_msg is None:
498 error_msg = "Timeout while waiting for text %(text)s to be present " \
499 "in the value of element '%(element)s'" % {"text": text, "element": locator_string}
500 self.save_screenshot("[ERROR] %s" % error_msg)
501 raise RuntimeError(error_msg)
502
503
504 - def get_webelement_by_link_text(self, locator_string):
505 """
506 Get the webelement by link text
507 @param locator_string: String used to locate element
508 @type locator_string: str
509 @param error_msg: Error string to show if element is not found
510 @type error_msg: str
511 """
512 try:
513 return self.browser.find_element_by_link_text(locator_string)
514 except NoSuchElementException:
515 error_msg="Could not find the link: '%s'"
516 self.save_screenshot(error_msg % locator_string)
517 raise RuntimeError(error_msg % locator_string)
518
520 """
521 Get the webelement by xpath
522 @param locator_string: String used to locate element
523 @type locator_string: str
524 @param error_msg: Error string to show if element is not found
525 @type error_msg: str
526 """
527 try:
528 self.wait_for_element_to_be_visible(By.XPATH, locator_string)
529 return self.browser.find_element_by_xpath(locator_string)
530 except NoSuchElementException:
531 error_msg="Could not find the xpath: '%s'"
532 self.save_screenshot(error_msg % locator_string)
533 raise RuntimeError(error_msg % locator_string)
534
536 """
537 Get the webelement by CSS
538 @param locator_string: String used to locate element
539 @type locator_string: str
540 @param error_msg: Error string to show if element is not found
541 @type error_msg: str
542 """
543 try:
544 self.wait_for_element_to_be_visible(By.CSS_SELECTOR, locator_string)
545 return self.browser.find_element_by_css_selector(locator_string)
546 except NoSuchElementException:
547 error_msg="Could not find css: '%s'"
548 self.save_screenshot(error_msg % locator_string)
549 raise RuntimeError(error_msg %locator_string)
550
552 """
553 Get the webelement list by xpath
554 @param locator_string: String used to locate element
555 @type locator_string: str
556 @param error_msg: Error string to show if element is not found
557 @type error_msg: str
558 """
559 try:
560 return self.browser.find_elements_by_xpath(locator_string)
561 except NoSuchElementException:
562 error_msg="Could not find the link: '%s'"
563 self.save_screenshot(error_msg % locator_string)
564 raise RuntimeError(error_msg+ " '%s'" % locator_string)
565
567 """
568 Get the webelement list by CSS
569 @param locator_string: String used to locate element
570 @type locator_string: str
571 @param error_msg: Error string to show if element is not found
572 @type error_msg: str
573 """
574 try:
575 return self.browser.find_elements_by_css_selector(locator_string)
576 except NoSuchElementException:
577 error_msg="Could not find css: '%s'"
578 self.save_screenshot(error_msg % locator_string)
579 raise RuntimeError(error_msg % locator_string)
580