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