1 import os
2 import inspect
3
4 import pytest
5 import collections
6 from time import sleep
7
8 from selenium import webdriver
9 from _pytest.python import FixtureRequest
10
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 import logging
26
27 _homepage = None
28 _browser = 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
51 """
52 Returns Instance of the WebDriver browser
53 @rtype: webdriver.Remote
54 """
55 return SeleniumTester._browser
56
63
64 browser = property(_get_browser, _set_browser)
65
66 - def _get_homepage(self):
67 """
68 Instance of the WebDriver homepage
69 """
70 return SeleniumTester._homepage
71
72 - def _set_homepage(self, homepage):
73 """
74 Instance of the WebDriver homepage
75 @type homepage: webdriver.Remote
76 """
77 SeleniumTester._homepage = homepage
78
79 homepage = property(_get_homepage, _set_homepage)
80
94
96 """
97 Will try to find a folder named "screenshot" starting from the file being executed and up to
98 three levels up
99
100 @return: str
101 """
102
103 curr_folder = os.path.dirname(self._request.fspath.strpath)
104
105
106 for i in range(1, 4):
107 curr_folder = os.path.abspath(os.path.join(curr_folder, os.pardir))
108
109
110 screenshot_folder = os.path.join(curr_folder, 'screenshots')
111 if os.path.exists(screenshot_folder):
112 return screenshot_folder
113
114 @pytest.fixture(scope='class', autouse=True)
115 - def initialize_class(self, request, test_logger, tlib_logger, browser, base_url):
116 """
117 @type test_logger: logging
118 @type browser: webdriver.Remote
119 @type request: FixtureRequest
120 """
121
122 request.cls.test_logger = test_logger
123 request.cls.tlib_logger = tlib_logger
124 self.browser = browser
125
126
127 if self.homepage is None:
128 self.homepage = base_url
129
130
131 request.cls._screenshot_report = collections.OrderedDict()
132 request.cls._folder_dest = os.path.basename(os.path.dirname(os.path.abspath(inspect.getfile(request.cls))))
133
134 def generate_report():
135 if len(request.cls._screenshot_report) > 0:
136
137 html = request.cls._screenshot_report_template.render(test_class=request.cls.__name__,
138 files=request.cls._screenshot_report)
139
140 htm_file = "%s.htm" % request.cls._folder_dest
141 full_filename = os.path.join(self.screenshot_folder(), htm_file)
142
143 f = open(full_filename, "w")
144 try:
145 f.write(html)
146 finally:
147 f.close()
148
149 request.addfinalizer(generate_report)
150
152 """
153 Gets a string based on test case id and name, taking into account if test case has already been run or not
154 @param tc_id: Test case id
155 @type tc_id: str
156 @type tc_name: str
157 """
158 i = 0
159 while True:
160 if i == 0:
161 filename = "%(tc_id)s_%(tc_params)s" % {"tc_id": tc_id, "tc_params": tc_params}
162 else:
163 filename = "%(tc_id)s_%(tc_params)s [retry %(cnt)d]" % {"tc_id": tc_id, "tc_params": tc_params, "cnt": i}
164
165 if not filename in self._request.cls._screenshot_report:
166 return filename
167
168 i += 1
169
170 - def navigate(self, page, save_screenshot=True):
171 """
172 Navigate to "page"
173 """
174 self.test_logger.info(u"Navigating to %s" % page)
175 self.browser.get(page)
176 if save_screenshot:
177 self.save_screenshot(u"Navigate to %s" % page)
178
179 - def go_home(self, save_screenshot=True):
186
217
218 - def wait_for_page_loaded(self, timeout=10):
219 """
220 Waist until document.readyState is equal to complete
221 @type timeout: Integer
222 @param timeout: Number of seconds before timing out
223 """
224 if self.browser.execute_script("return document.readyState") == "complete":
225 return
226 else:
227 self.tlib_logger.debug("Waiting for '%s' to load" % self.browser.current_url)
228
229 condition = lambda *args: self.browser.execute_script("return document.readyState") == "complete"
230 try:
231 WebDriverWait(self.browser, timeout).until(condition)
232 except TimeoutException:
233 self.test_logger.error('Timeout while waiting for page to load')
234 pytest.fail('Timeout while waiting for page to load')
235
236 self.tlib_logger.debug("Page '%s' finished loading" % self.browser.current_url)
237
239 """
240 Waist until an alert is visible
241 @type timeout: Integer
242 @param timeout: Number of seconds before timing out
243 @rtype: bool
244 """
245 def is_alert_visible():
246 try:
247
248 alert = self.browser.switch_to_alert().text
249 return True
250 except NoAlertPresentException:
251 return False
252
253 condition = lambda *args: is_alert_visible()
254 try:
255 WebDriverWait(self.browser, timeout).until(condition)
256 return self.browser.switch_to_alert()
257 except TimeoutException:
258 self.test_logger.error('Timeout while waiting for alert to appear')
259 pytest.fail('Timeout while waiting for alert to appear')
260
261 @pytest.fixture(scope='function', autouse=True)
262 - def setup_test(self, request, test_logger, browser):
263 """
264 Goes to homepage before each test, unless marker skipsetup is given
265
266 @type request: FixtureRequest
267 @type test_logger: logging
268 @type browser: webdriver.Remote
269 """
270
271 self._request = request
272
273
274
275 request.cls._test_params = request.keywords.node._genid
276 request.cls._test_case_name = request.keywords.node.name
277 request.cls._screenshots = {}
278
279
280 marker = request.node.get_marker("testcasename")
281 if marker is None:
282 test_logger.warn("Test case doesn't have marker testcasename")
283 request.cls._test_case_id = "UNKNOWN_TEST_CASE_ID"
284 else:
285 request.cls._test_case_id = marker.args[0]
286
287 def generate_report():
288 """
289 Generates HTML page with all the screenshots for a test case
290 Supports generating different files when test cases are run multiple times with the same arguments
291 """
292
293 if len(request.cls._screenshots) > 0:
294 name = self.get_unused_report_name(request.cls._test_case_id, self._request.cls._test_params)
295
296
297 html = request.cls._tc_screenshot_template.render(test_case_id=request.cls._test_case_id,
298 test_case_name=request.cls._test_case_name,
299 screenshots=request.cls._screenshots)
300
301
302 htm_file = FileHelper.get_filename_from_string(name) + '.htm'
303 relative_filename = os.path.join(request.cls._folder_dest, htm_file)
304 full_filename = os.path.join(self.screenshot_folder(), relative_filename)
305
306 request.cls._screenshot_report[name] = {"tc_id": request.cls._test_case_id,
307 "tc_name": request.cls._test_case_name,
308 "filename": relative_filename}
309
310 f = open(full_filename, "w")
311 try:
312 f.write(html)
313 finally:
314 f.close()
315
316 request.addfinalizer(generate_report)
317
318
319 if "skipsetup" not in request.node.function.__dict__:
320
321 self.go_home(save_screenshot=False)
322 browser.delete_all_cookies()
323 self.go_home()
324 else:
325 test_logger.info("Skipping setup")
326
328 """
329 Wait until an element becomes visible
330 @param locator_strategy: Location strategy to use
331 @type locator_strategy: By
332 @param locator_string: String used to locate element
333 @type locator_string: str
334 @param error_msg: Error string to show if element is not found
335 @type error_msg: str
336 @param timeout: Maximum time in seconds to wait for the element to be visible
337 @type timeout: int
338 @rtype: WebElement
339 """
340 try:
341 element = WebDriverWait(self.browser, timeout).\
342 until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string)))
343 return element
344 except TimeoutException:
345 if error_msg is None:
346 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string
347 self.save_screenshot("[ERROR] %s" % error_msg)
348 pytest.fail(error_msg)
349
351 """
352 Wait until an element cna be clicked
353 @param locator_strategy: Location strategy to use
354 @type locator_strategy: By
355 @param locator_string: String used to locate element
356 @type locator_string: str
357 @param error_msg: Error string to show if element is not found
358 @type error_msg: str
359 @param timeout: Maximum time in seconds to wait for the element to be clickable
360 @type timeout: int
361 @rtype: WebElement
362 """
363 try:
364 element = WebDriverWait(self.browser, timeout).\
365 until(expected_conditions.element_to_be_clickable((locator_strategy, locator_string)))
366 return element
367 except TimeoutException:
368 if error_msg is None:
369 error_msg = "Timeout while waiting for element '%s' to be clickable" % locator_string
370 self.save_screenshot("[ERROR] %s" % error_msg)
371 pytest.fail(error_msg)
372
374 """
375 Wait until an element is present
376 @param locator_strategy: Location strategy to use
377 @type locator_strategy: By
378 @param locator_string: String used to locate element
379 @type locator_string: str
380 @param error_msg: Error string to show if element is not found
381 @type error_msg: str
382 @param timeout: Maximum time in seconds to wait for the element to be present
383 @type timeout: int
384 @rtype: WebElement
385 """
386 try:
387 element = WebDriverWait(self.browser, timeout).\
388 until(expected_conditions.alert_is_present((locator_strategy, locator_string)))
389 return element
390 except TimeoutException:
391 if error_msg is None:
392 error_msg = "Timeout while waiting for element '%s' to be present" % locator_string
393 self.save_screenshot("[ERROR] %s" % error_msg)
394 pytest.fail(error_msg)
395
397 """
398 Wait until an element is selected
399 @param locator_strategy: Location strategy to use
400 @type locator_strategy: By
401 @param locator_string: String used to locate element
402 @type locator_string: str
403 @param error_msg: Error string to show if element is not found
404 @type error_msg: str
405 @param timeout: Maximum time in seconds to wait for the element to be selected
406 @type timeout: int
407 @rtype: WebElement
408 """
409 try:
410 element = WebDriverWait(self.browser, timeout).\
411 until(expected_conditions.element_located_to_be_selected((locator_strategy, locator_string)))
412 return element
413 except TimeoutException:
414 if error_msg is None:
415 error_msg = "Timeout while waiting for element '%s' to be selected" % locator_string
416 self.save_screenshot("[ERROR] %s" % error_msg)
417 pytest.fail(error_msg)
418
420 """
421 Wait until an element becomes invisible
422 @param locator_strategy: Location strategy to use
423 @type locator_strategy: By
424 @param locator_string: String used to locate element
425 @type locator_string: str
426 @param error_msg: Error string to show if element is not found
427 @type error_msg: str
428 @param timeout: Maximum time in seconds to wait for the element to be hidden
429 @type timeout: int
430 @rtype: WebElement
431 """
432 try:
433 element = WebDriverWait(self.browser, timeout).\
434 until(expected_conditions.invisibility_of_element_located((locator_strategy, locator_string)))
435 return element
436 except TimeoutException:
437 if error_msg is None:
438 error_msg = "Timeout while waiting for element '%s' to be invisible" % locator_string
439 self.save_screenshot("[ERROR] %s" % error_msg)
440 pytest.fail(error_msg)
441
443 """
444 Wait until an element that moves on the screen stops moving
445 @param locator_strategy: Location strategy to use
446 @type locator_strategy: By
447 @param locator_string: String used to locate element
448 @type locator_string: str
449 @param error_msg: Error string to show if element is not found
450 @type error_msg: str
451 @param timeout: Maximum time in seconds to wait for the element to be visible
452 @type timeout: int
453 @rtype: WebElement
454 """
455 try:
456 element = WebDriverWait(self.browser, timeout).\
457 until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string)))
458
459
460 old_location = {'x': 0, 'y': 0}
461 while old_location != element.location:
462 self.tlib_logger.debug("Pop-up is still moving. Previous position: %s, current position: %s" %
463 (old_location, element.location))
464 old_location = element.location
465 sleep(0.1)
466 element = self.browser.find_element(locator_strategy, locator_string)
467
468 return element
469 except TimeoutException:
470 if error_msg is None:
471 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string
472 self.save_screenshot("[ERROR] %s" % error_msg)
473 pytest.fail(error_msg)
474
475 - def wait_for_text_to_be_present_in_element(self, locator_strategy, locator_string, text,
476 error_msg=None, timeout=10):
477 """
478 Wait for an element that contains specified text
479 @param locator_strategy: Location strategy to use
480 @type locator_strategy: By
481 @param locator_string: String used to locate element
482 @type locator_string: str
483 @param error_msg: Error string to show if element is not found
484 @type error_msg: str
485 @param timeout: Maximum time in seconds to wait
486 @type timeout: int
487 """
488 try:
489 WebDriverWait(self.browser, timeout).\
490 until(expected_conditions.text_to_be_present_in_element((locator_strategy, locator_string), text))
491 except TimeoutException:
492 if error_msg is None:
493 error_msg = "Timeout while waiting for text %(text)s to be present in element '%(element)s'" % \
494 {"text": text, "element": locator_string}
495 self.save_screenshot("[ERROR] %s" % error_msg)
496 pytest.fail(error_msg)
497
498 - def wait_for_text_to_be_present_in_element_value(self, locator_strategy, locator_string,
499 text, error_msg=None, timeout=10):
500 """
501 Wait for an element's value to contain some test
502 @param locator_strategy: Location strategy to use
503 @type locator_strategy: By
504 @param locator_string: String used to locate element
505 @type locator_string: str
506 @param error_msg: Error string to show if element is not found
507 @type error_msg: str
508 @param timeout: Maximum time in seconds to wait
509 @type timeout: int
510 """
511 try:
512 WebDriverWait(self.browser, timeout).\
513 until(expected_conditions.text_to_be_present_in_element_value((locator_strategy, locator_string), text))
514 except TimeoutException:
515 if error_msg is None:
516 error_msg = "Timeout while waiting for text %(text)s to be present in the value of " \
517 "element '%(element)s'" % {"text": text, "element": locator_string}
518 self.save_screenshot("[ERROR] %s" % error_msg)
519 pytest.fail(error_msg)
520
521
523 try:
524 return self.browser.find_element_by_link_text(text)
525 except NoSuchElementException:
526 error_msg="Could not find the link: '%s'"
527 self.save_screenshot(error_msg % text)
528 pytest.fail(error_msg % text)
529
531 try:
532 self.wait_for_element_to_be_visible(By.XPATH, locator_xpath)
533 return self.browser.find_element_by_xpath(locator_xpath)
534 except NoSuchElementException:
535 error_msg="Could not find the xpath: '%s'"
536 self.save_screenshot(error_msg % locator_xpath)
537 pytest.fail(error_msg % locator_xpath)
538
540 try:
541 self.wait_for_element_to_be_visible(By.CSS_SELECTOR, locator_css)
542 return self.browser.find_element_by_css_selector(locator_css)
543 except NoSuchElementException:
544 error_msg="Could not find css: '%s'"
545 self.save_screenshot(error_msg % locator_css)
546 pytest.fail(error_msg %locator_css)
547
549 try:
550 return self.browser.find_elements_by_xpath(text)
551 except NoSuchElementException:
552 error_msg="Could not find the link: '%s'"
553 self.save_screenshot(error_msg % text)
554 pytest.fail(error_msg+ " '%s'" % text)
555
557 try:
558 return self.browser.find_elements_by_css_selector(locator_css)
559 except NoSuchElementException:
560 error_msg="Could not find css: '%s'"
561 self.save_screenshot(error_msg % locator_css)
562 pytest.fail(error_msg % locator_css)
563