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()),
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 self.__class__._browser
59
61 """
62 Instance of the WebDriver browser
63 @type browser: webdriver.Remote
64 """
65 self.__class__._browser = browser
66
67 browser = property(_get_browser, _set_browser)
68
69 - def _get_homepage(self):
70 """
71 Instance of the WebDriver homepage
72 """
73 return self.__class__._homepage
74
75 - def _set_homepage(self, homepage):
76 """
77 Instance of the WebDriver homepage
78 @type homepage: webdriver.Remote
79 """
80 self.__class__._homepage = homepage
81
82 homepage = property(_get_homepage, _set_homepage)
83
84 @property
86 """
87 Logger object to log test information
88 """
89 return self.__class__._test_logger
90
104
106 """
107 Will try to find a folder named "screenshot" starting from the file being executed and up to
108 three levels up
109
110 @return: str
111 """
112
113 curr_folder = os.path.dirname(self._request.fspath.strpath)
114
115
116 for i in range(1, 4):
117 curr_folder = os.path.abspath(os.path.join(curr_folder, os.pardir))
118
119
120 screenshot_folder = os.path.join(curr_folder, 'screenshots')
121 if os.path.exists(screenshot_folder):
122 return screenshot_folder
123
124 pass
125
126 @pytest.fixture(scope='class', autouse=True)
139
141 """
142 Gets a string based on test case id and name, taking into account if test case has already been run or not
143 @param tc_id: Test case id
144 @type tc_id: str
145 @type tc_name: str
146 """
147 i = 0
148 while True:
149 if i == 0:
150 filename = "%(tc_id)s" % {"tc_id": tc_id}
151 else:
152 filename = "%(tc_id)s [retry %(cnt)d]" % {"tc_id": tc_id, "cnt": i}
153
154 if not filename in self.__class__._screenshot_report:
155 return filename
156
157 i += 1
158
159 @pytest.fixture(scope='class', autouse=True)
161 """
162 Generates HTML page with all test case results for the class
163 @type request: FixtureRequest
164 """
165 self.__class__._screenshot_report = collections.OrderedDict()
166 self.__class__._folder_dest = re.sub('\.py$', '', os.path.relpath(inspect.getfile(request.cls)))
167
168 def generate_report():
169 if len(self.__class__._screenshot_report) > 0:
170
171 html = self.__class__._screenshot_report_template.render(test_class=self.__class__.__name__,
172 files=self.__class__._screenshot_report)
173
174 htm_file = "%s.htm" % self.__class__._folder_dest
175 full_filename = os.path.join(self.screenshot_folder(), htm_file)
176
177 f = open(full_filename, "w")
178 try:
179 f.write(html)
180 finally:
181 f.close()
182
183 request.addfinalizer(generate_report)
184
185 @pytest.fixture(scope='function', autouse=True)
187 """
188 Generates HTML page with all the screenshots for a test case
189 Supports generating different When test cases are run multiple times with the same arguments
190 @type request: FixtureRequest
191 """
192
193 self.__class__._test_params = request.keywords.node._genid
194 self.__class__._test_case_name = request.keywords.node.name
195 self.__class__._screenshots = {}
196
197
198 marker = request.node.get_marker("testcasename")
199 if marker is None:
200 self._test_logger.warn("Test case doesn't have marker testcasename")
201 self.__class__._test_case_id = "UNKNOWN_TEST_CASE_ID"
202 else:
203 self.__class__._test_case_id = marker.args[0]
204
205 def generate_report():
206
207 if len(self.__class__._screenshots) > 0:
208 name = self.get_unused_report_name(self.__class__._test_case_id)
209
210
211 html = self.__class__._tc_screenshot_template.render(test_case_id=self.__class__._test_case_id,
212 test_case_name=self.__class__._test_case_name,
213 screenshots=self.__class__._screenshots)
214
215
216 htm_file = FileHelper.get_filename_from_string(name) + '.htm'
217 relative_filename = os.path.join(self.__class__._folder_dest, htm_file)
218 full_filename = os.path.join(self.screenshot_folder(), relative_filename)
219
220 self.__class__._screenshot_report[name] = {"tc_id": self.__class__._test_case_id,
221 "tc_name": self.__class__._test_case_name,
222 "filename": relative_filename}
223
224 f = open(full_filename, "w")
225 try:
226 f.write(html)
227 finally:
228 f.close()
229
230 request.addfinalizer(generate_report)
231
232 - def navigate(self, page, save_screenshot=True):
240
241 - def go_home(self, save_screenshot=True):
248
250 """
251 Saves screen shot for the current page
252 """
253
254 self._tlib_logger.debug("Saving screenshot")
255 self.wait_for_page_loaded()
256
257
258 name = self.get_unused_report_name(self.__class__._test_case_id)
259 name = FileHelper.get_filename_from_string(name)
260
261 test_folder = os.path.join(self.screenshot_folder(), self.__class__._folder_dest, name)
262
263 try:
264 os.makedirs(test_folder)
265 except WindowsError:
266 pass
267
268
269 i = len(os.listdir(test_folder)) + 1
270
271
272 img_filename = "%02d.png" % i
273 full_path = os.path.join(test_folder, img_filename)
274
275 self.__class__._screenshots[i] = {"filename": "%s/%s" % (name, img_filename),
276 u"description": description}
277
278 self.browser.get_screenshot_as_file(full_path)
279
280 - def wait_for_page_loaded(self, timeout=10):
281 """
282 Waist until document.readyState is equal to complete
283 @type timeout: Integer
284 @param timeout: Number of seconds before timing out
285 """
286 if self.browser.execute_script("return document.readyState") == "complete":
287 return
288 else:
289 self._tlib_logger.debug("Waiting for '%s' to load" % self.browser.current_url)
290
291 condition = lambda *args: self.browser.execute_script("return document.readyState") == "complete"
292 try:
293 WebDriverWait(self.browser, timeout).until(condition)
294 except TimeoutException:
295 self.test_logger.error('Timeout while waiting for page to load')
296 pytest.fail('Timeout while waiting for page to load')
297
298 self._tlib_logger.debug("Page '%s' finished loading" % self.browser.current_url)
299
301 """
302 Waist until an alert is visible
303 @type timeout: Integer
304 @param timeout: Number of seconds before timing out
305 @rtype: bool
306 """
307 def is_alert_visible():
308 try:
309
310 alert = self.browser.switch_to_alert().text
311 return True
312 except NoAlertPresentException:
313 return False
314
315 condition = lambda *args: is_alert_visible()
316 try:
317 WebDriverWait(self.browser, timeout).until(condition)
318 return self.browser.switch_to_alert()
319 except TimeoutException:
320 self.test_logger.error('Timeout while waiting for alert to appear')
321 pytest.fail('Timeout while waiting for alert to appear')
322
323 @pytest.fixture(scope='function', autouse=True)
324 - def setup_test(self, request, test_logger, browser):
325 """
326 Goes to homepage before each test, unless marker skipsetup is given
327 """
328
329 self._request = request
330
331
332 if "skipsetup" not in request.node.function.__dict__:
333
334 self.go_home(save_screenshot=False)
335 browser.delete_all_cookies()
336 self.go_home()
337 else:
338 test_logger.info("Skipping setup")
339
341 """
342 Wait until an element becomes visible
343 @param locator_strategy: Location strategy to use
344 @type locator_strategy: By
345 @param locator_string: String used to locate element
346 @type locator_string: str
347 @param error_msg: Error string to show if element is not found
348 @type error_msg: str
349 @param timeout: Maximum time in seconds to wait for the element to be visible
350 @type timeout: int
351 @rtype: WebElement
352 """
353 try:
354 element = WebDriverWait(self.browser, timeout).\
355 until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string)))
356 return element
357 except TimeoutException:
358 if error_msg is None:
359 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string
360 self.save_screenshot("[ERROR] %s" % error_msg)
361 pytest.fail(error_msg)
362
364 """
365 Wait until an element cna be clicked
366 @param locator_strategy: Location strategy to use
367 @type locator_strategy: By
368 @param locator_string: String used to locate element
369 @type locator_string: str
370 @param error_msg: Error string to show if element is not found
371 @type error_msg: str
372 @param timeout: Maximum time in seconds to wait for the element to be clickable
373 @type timeout: int
374 @rtype: WebElement
375 """
376 try:
377 element = WebDriverWait(self.browser, timeout).\
378 until(expected_conditions.element_to_be_clickable((locator_strategy, locator_string)))
379 return element
380 except TimeoutException:
381 if error_msg is None:
382 error_msg = "Timeout while waiting for element '%s' to be clickable" % locator_string
383 self.save_screenshot("[ERROR] %s" % error_msg)
384 pytest.fail(error_msg)
385
387 """
388 Wait until an element is present
389 @param locator_strategy: Location strategy to use
390 @type locator_strategy: By
391 @param locator_string: String used to locate element
392 @type locator_string: str
393 @param error_msg: Error string to show if element is not found
394 @type error_msg: str
395 @param timeout: Maximum time in seconds to wait for the element to be present
396 @type timeout: int
397 @rtype: WebElement
398 """
399 try:
400 element = WebDriverWait(self.browser, timeout).\
401 until(expected_conditions.alert_is_present((locator_strategy, locator_string)))
402 return element
403 except TimeoutException:
404 if error_msg is None:
405 error_msg = "Timeout while waiting for element '%s' to be present" % locator_string
406 self.save_screenshot("[ERROR] %s" % error_msg)
407 pytest.fail(error_msg)
408
410 """
411 Wait until an element is selected
412 @param locator_strategy: Location strategy to use
413 @type locator_strategy: By
414 @param locator_string: String used to locate element
415 @type locator_string: str
416 @param error_msg: Error string to show if element is not found
417 @type error_msg: str
418 @param timeout: Maximum time in seconds to wait for the element to be selected
419 @type timeout: int
420 @rtype: WebElement
421 """
422 try:
423 element = WebDriverWait(self.browser, timeout).\
424 until(expected_conditions.element_located_to_be_selected((locator_strategy, locator_string)))
425 return element
426 except TimeoutException:
427 if error_msg is None:
428 error_msg = "Timeout while waiting for element '%s' to be selected" % locator_string
429 self.save_screenshot("[ERROR] %s" % error_msg)
430 pytest.fail(error_msg)
431
433 """
434 Wait until an element becomes invisible
435 @param locator_strategy: Location strategy to use
436 @type locator_strategy: By
437 @param locator_string: String used to locate element
438 @type locator_string: str
439 @param error_msg: Error string to show if element is not found
440 @type error_msg: str
441 @param timeout: Maximum time in seconds to wait for the element to be hidden
442 @type timeout: int
443 @rtype: WebElement
444 """
445 try:
446 element = WebDriverWait(self.browser, timeout).\
447 until(expected_conditions.invisibility_of_element_located((locator_strategy, locator_string)))
448 return element
449 except TimeoutException:
450 if error_msg is None:
451 error_msg = "Timeout while waiting for element '%s' to be invisible" % locator_string
452 self.save_screenshot("[ERROR] %s" % error_msg)
453 pytest.fail(error_msg)
454
456 """
457 Wait until an element that moves on the screen stops moving
458 @param locator_strategy: Location strategy to use
459 @type locator_strategy: By
460 @param locator_string: String used to locate element
461 @type locator_string: str
462 @param error_msg: Error string to show if element is not found
463 @type error_msg: str
464 @param timeout: Maximum time in seconds to wait for the element to be visible
465 @type timeout: int
466 @rtype: WebElement
467 """
468 try:
469 element = WebDriverWait(self.browser, timeout).\
470 until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string)))
471
472
473 old_location = {'x': 0, 'y': 0}
474 while old_location != element.location:
475 self._tlib_logger.debug("Pop-up is still moving. Previous position: %s, current position: %s" %
476 (old_location, element.location))
477 old_location = element.location
478 sleep(0.1)
479 element = self.browser.find_element(locator_strategy, locator_string)
480
481 return element
482 except TimeoutException:
483 if error_msg is None:
484 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string
485 self.save_screenshot("[ERROR] %s" % error_msg)
486 pytest.fail(error_msg)
487
488 - def wait_for_text_to_be_present_in_element(self, locator_strategy, locator_string, text,
489 error_msg=None, timeout=10):
490 """
491 Wait for an element that contains specified text
492 @param locator_strategy: Location strategy to use
493 @type locator_strategy: By
494 @param locator_string: String used to locate element
495 @type locator_string: str
496 @param error_msg: Error string to show if element is not found
497 @type error_msg: str
498 @param timeout: Maximum time in seconds to wait
499 @type timeout: int
500 """
501 try:
502 WebDriverWait(self.browser, timeout).\
503 until(expected_conditions.text_to_be_present_in_element((locator_strategy, locator_string), text))
504 except TimeoutException:
505 if error_msg is None:
506 error_msg = "Timeout while waiting for text %(text)s to be present in element '%(element)s'" % \
507 {"text": text, "element": locator_string}
508 self.save_screenshot("[ERROR] %s" % error_msg)
509 pytest.fail(error_msg)
510
511 - def wait_for_text_to_be_present_in_element_value(self, locator_strategy, locator_string,
512 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).\
526 until(expected_conditions.text_to_be_present_in_element_value((locator_strategy, locator_string), text))
527 except TimeoutException:
528 if error_msg is None:
529 error_msg = "Timeout while waiting for text %(text)s to be present in the value of " \
530 "element '%(element)s'" % {"text": text, "element": locator_string}
531 self.save_screenshot("[ERROR] %s" % error_msg)
532 pytest.fail(error_msg)
533
534
536 try:
537 return self.browser.find_element_by_link_text(text)
538 except NoSuchElementException:
539 pytest.fail("Could not find the link: '%s'" % text)
540
542 try:
543 self.wait_for_element_to_be_visible(By.XPATH, locator_xpath)
544 return self.browser.find_element_by_xpath(locator_xpath)
545 except NoSuchElementException:
546 pytest.fail("Could not find the xpath: '%s'" % locator_xpath)
547
549 try:
550 self.wait_for_element_to_be_visible(By.CSS_SELECTOR, locator_css)
551 return self.browser.find_element_by_css_selector(locator_css)
552 except NoSuchElementException:
553 pytest.fail("Could not find css: '%s'" % locator_css)
554
556 try:
557 return self.browser.find_elements_by_xpath(text)
558 except NoSuchElementException:
559 pytest.fail("Could not find the xpath: '%s'" % text)
560
562 try:
563 return self.browser.find_elements_by_css_selector(locator_css)
564 except NoSuchElementException:
565 pytest.fail("Could not find css: '%s'" % locator_css)
566