Best Python code snippet using SeleniumBase
webdriver_test.py
Source:webdriver_test.py
...5102 """ Same as wait_for_and_switch_to_alert(), but smaller default T_O """5103 self.__check_scope__()5104 timeout = self.get_timeout(timeout, constants.SMALL_TIMEOUT)5105 return page_actions.wait_for_and_switch_to_alert(self.driver, timeout)5106 def __process_visual_baseline_logs(self):5107 """ Save copies of baseline PNGs in "./latest_logs" during failures.5108 Also create a side_by_side.html file for visual comparisons. """5109 test_logpath = os.path.join(self.log_path, self.__get_test_id())5110 for baseline_copy_tuple in self.__visual_baseline_copies:5111 baseline_path = baseline_copy_tuple[0]5112 baseline_copy_name = baseline_copy_tuple[1]5113 b_c_alt_name = baseline_copy_tuple[2]5114 latest_png_path = baseline_copy_tuple[3]5115 latest_copy_name = baseline_copy_tuple[4]5116 l_c_alt_name = baseline_copy_tuple[5]5117 baseline_copy_path = os.path.join(test_logpath, baseline_copy_name)5118 b_c_alt_path = os.path.join(test_logpath, b_c_alt_name)5119 latest_copy_path = os.path.join(test_logpath, latest_copy_name)5120 l_c_alt_path = os.path.join(test_logpath, l_c_alt_name)5121 if len(self.__visual_baseline_copies) == 1:5122 baseline_copy_path = b_c_alt_path5123 latest_copy_path = l_c_alt_path5124 if (5125 os.path.exists(baseline_path)5126 and not os.path.exists(baseline_copy_path)5127 ):5128 self.__create_log_path_as_needed(test_logpath)5129 shutil.copy(baseline_path, baseline_copy_path)5130 if (5131 os.path.exists(latest_png_path)5132 and not os.path.exists(latest_copy_path)5133 ):5134 self.__create_log_path_as_needed(test_logpath)5135 shutil.copy(latest_png_path, latest_copy_path)5136 if len(self.__visual_baseline_copies) != 1:5137 return # Only possible when deferred visual asserts are used5138 head = (5139 '<head><meta charset="utf-8">'5140 '<meta name="viewport" content="shrink-to-fit=no">'5141 '<link rel="shortcut icon" href="%s">'5142 "<title>Visual Comparison</title>"5143 "</head>"5144 % (constants.SideBySide.SIDE_BY_SIDE_PNG)5145 )5146 table_html = (5147 '<table border="3px solid #E6E6E6;" width="100%;" padding: 12px;'5148 ' font-size="16px;" text-align="left;" id="results-table"'5149 ' style="background-color: #FAFAFA;">'5150 '<thead id="results-table-head">'5151 '<tr>'5152 '<th style="background-color: rgba(0, 128, 0, 0.25);"'5153 ' col="baseline">Baseline Screenshot</th>'5154 '<th style="background-color: rgba(128, 0, 0, 0.25);"'5155 ' col="failure">Visual Diff Failure Screenshot</th>'5156 "</tr></thead>"5157 )5158 row = (5159 '<tbody class="compare results-table-row">'5160 '<tr style="background-color: #F4F4FE;">'5161 '<td><img src="%s" width="100%%" /></td>'5162 '<td><img src="%s" width="100%%" /></td>'5163 "</tr></tbody>"5164 "" % ("baseline.png", "baseline_diff.png")5165 )5166 header_text = "SeleniumBase Visual Comparison"5167 header = '<h3 align="center">%s</h3>' % header_text5168 table_html += row5169 table_html += "</table>"5170 footer = "<br /><b>Last updated:</b> "5171 timestamp, the_date, the_time = log_helper.get_master_time()5172 last_updated = "%s at %s" % (the_date, the_time)5173 footer = footer + "%s" % last_updated5174 gen_by = (5175 '<p><div>Generated by: <b><a href="https://seleniumbase.io/">'5176 "SeleniumBase</a></b></div></p><p></p>"5177 )5178 footer = footer + gen_by5179 the_html = (5180 '<html lang="en">'5181 + head5182 + '<body style="background-color: #FCFCF4;">'5183 + header5184 + table_html5185 + footer5186 + "</body>"5187 )5188 file_path = os.path.join(test_logpath, constants.SideBySide.HTML_FILE)5189 out_file = codecs.open(file_path, "w+", encoding="utf-8")5190 out_file.writelines(the_html)5191 out_file.close()5192 def check_window(5193 self,5194 name="default",5195 level=0,5196 baseline=False,5197 check_domain=True,5198 full_diff=False,5199 ):5200 """*** Automated Visual Testing with SeleniumBase ***5201 The first time a test calls self.check_window() for a unique "name"5202 parameter provided, it will set a visual baseline, meaning that it5203 creates a folder, saves the URL to a file, saves the current window5204 screenshot to a file, and creates the following three files5205 with the listed data saved:5206 tags_level1.txt -> HTML tags from the window5207 tags_level2.txt -> HTML tags + attributes from the window5208 tags_level3.txt -> HTML tags + attributes/values from the window5209 Baseline folders are named based on the test name and the name5210 parameter passed to self.check_window(). The same test can store5211 multiple baseline folders.5212 If the baseline is being set/reset, the "level" doesn't matter.5213 After the first run of self.check_window(), it will compare the5214 HTML tags of the latest window to the one from the initial run.5215 Here's how the level system works:5216 * level=0 ->5217 DRY RUN ONLY - Will perform comparisons to the baseline (and5218 print out any differences that are found) but5219 won't fail the test even if differences exist.5220 * level=1 ->5221 HTML tags are compared to tags_level1.txt5222 * level=2 ->5223 HTML tags are compared to tags_level1.txt and5224 HTML tags/attributes are compared to tags_level2.txt5225 * level=3 ->5226 HTML tags are compared to tags_level1.txt and5227 HTML tags + attributes are compared to tags_level2.txt and5228 HTML tags + attributes/values are compared to tags_level3.txt5229 As shown, Level-3 is the most strict, Level-1 is the least strict.5230 If the comparisons from the latest window to the existing baseline5231 don't match, the current test will fail, except for Level-0 tests.5232 You can reset the visual baseline on the command line by using:5233 --visual_baseline5234 As long as "--visual_baseline" is used on the command line while5235 running tests, the self.check_window() method cannot fail because5236 it will rebuild the visual baseline rather than comparing the html5237 tags of the latest run to the existing baseline. If there are any5238 expected layout changes to a website that you're testing, you'll5239 need to reset the baseline to prevent unnecessary failures.5240 self.check_window() will fail with "Page Domain Mismatch Failure"5241 if the page domain doesn't match the domain of the baseline,5242 unless "check_domain" is set to False when calling check_window().5243 If you want to use self.check_window() to compare a web page to5244 a later version of itself from within the same test run, you can5245 add the parameter "baseline=True" to the first time you call5246 self.check_window() in a test to use that as the baseline. This5247 only makes sense if you're calling self.check_window() more than5248 once with the same name parameter in the same test.5249 If "full_diff" is set to False, the error output will only5250 include the first differing element in the list comparison.5251 Set "full_diff" to True if you want to see the full output.5252 Automated Visual Testing with self.check_window() is not very5253 effective for websites that have dynamic content that changes5254 the layout and structure of web pages. For those, you're much5255 better off using regular SeleniumBase functional testing.5256 Example usage:5257 self.check_window(name="testing", level=0)5258 self.check_window(name="xkcd_home", level=1)5259 self.check_window(name="github_page", level=2)5260 self.check_window(name="wikipedia_page", level=3)5261 """5262 self.wait_for_ready_state_complete()5263 if level == "0":5264 level = 05265 if level == "1":5266 level = 15267 if level == "2":5268 level = 25269 if level == "3":5270 level = 35271 if level != 0 and level != 1 and level != 2 and level != 3:5272 raise Exception('Parameter "level" must be set to 0, 1, 2, or 3!')5273 if self.demo_mode:5274 message = (5275 "WARNING: Using check_window() from Demo Mode may lead "5276 "to unexpected results caused by Demo Mode HTML changes."5277 )5278 logging.info(message)5279 test_id = self.__get_display_id().split("::")[-1]5280 if not name or len(name) < 1:5281 name = "default"5282 name = str(name)5283 from seleniumbase.core import visual_helper5284 visual_helper.visual_baseline_folder_setup()5285 baseline_dir = constants.VisualBaseline.STORAGE_FOLDER5286 visual_baseline_path = baseline_dir + "/" + test_id + "/" + name5287 page_url_file = visual_baseline_path + "/page_url.txt"5288 baseline_png = "baseline.png"5289 baseline_png_path = visual_baseline_path + "/%s" % baseline_png5290 latest_png = "latest.png"5291 latest_png_path = visual_baseline_path + "/%s" % latest_png5292 level_1_file = visual_baseline_path + "/tags_level_1.txt"5293 level_2_file = visual_baseline_path + "/tags_level_2.txt"5294 level_3_file = visual_baseline_path + "/tags_level_3.txt"5295 set_baseline = False5296 if baseline or self.visual_baseline:5297 set_baseline = True5298 if not os.path.exists(visual_baseline_path):5299 set_baseline = True5300 try:5301 os.makedirs(visual_baseline_path)5302 except Exception:5303 pass # Only reachable during multi-threaded test runs5304 if not os.path.exists(page_url_file):5305 set_baseline = True5306 if not os.path.exists(baseline_png_path):5307 set_baseline = True5308 if not os.path.exists(level_1_file):5309 set_baseline = True5310 if not os.path.exists(level_2_file):5311 set_baseline = True5312 if not os.path.exists(level_3_file):5313 set_baseline = True5314 page_url = self.get_current_url()5315 soup = self.get_beautiful_soup()5316 html_tags = soup.body.find_all()5317 level_1 = [[tag.name] for tag in html_tags]5318 level_1 = json.loads(json.dumps(level_1)) # Tuples become lists5319 level_2 = [[tag.name, sorted(tag.attrs.keys())] for tag in html_tags]5320 level_2 = json.loads(json.dumps(level_2)) # Tuples become lists5321 level_3 = [[tag.name, sorted(tag.attrs.items())] for tag in html_tags]5322 level_3 = json.loads(json.dumps(level_3)) # Tuples become lists5323 if set_baseline:5324 self.save_screenshot(5325 baseline_png, visual_baseline_path, selector="body"5326 )5327 out_file = codecs.open(page_url_file, "w+", encoding="utf-8")5328 out_file.writelines(page_url)5329 out_file.close()5330 out_file = codecs.open(level_1_file, "w+", encoding="utf-8")5331 out_file.writelines(json.dumps(level_1))5332 out_file.close()5333 out_file = codecs.open(level_2_file, "w+", encoding="utf-8")5334 out_file.writelines(json.dumps(level_2))5335 out_file.close()5336 out_file = codecs.open(level_3_file, "w+", encoding="utf-8")5337 out_file.writelines(json.dumps(level_3))5338 out_file.close()5339 baseline_path = os.path.join(visual_baseline_path, baseline_png)5340 baseline_copy_name = "baseline_%s.png" % name5341 b_c_alt_name = "baseline.png"5342 latest_copy_name = "baseline_diff_%s.png" % name5343 l_c_alt_name = "baseline_diff.png"5344 baseline_copy_tuple = (5345 baseline_path, baseline_copy_name, b_c_alt_name,5346 latest_png_path, latest_copy_name, l_c_alt_name,5347 )5348 self.__visual_baseline_copies.append(baseline_copy_tuple)5349 if not set_baseline:5350 self.save_screenshot(5351 latest_png, visual_baseline_path, selector="body"5352 )5353 f = open(page_url_file, "r")5354 page_url_data = f.read().strip()5355 f.close()5356 f = open(level_1_file, "r")5357 level_1_data = json.loads(f.read())5358 f.close()5359 f = open(level_2_file, "r")5360 level_2_data = json.loads(f.read())5361 f.close()5362 f = open(level_3_file, "r")5363 level_3_data = json.loads(f.read())5364 f.close()5365 domain_fail = (5366 "\n*\nPage Domain Mismatch Failure: "5367 "Current Page Domain doesn't match the Page Domain of the "5368 "Baseline! Can't compare two completely different sites! "5369 "Run with --visual_baseline to reset the baseline!"5370 )5371 level_1_failure = (5372 "\n*\n*** Exception: <Level 1> Visual Diff Failure:\n"5373 "* HTML tags don't match the baseline!"5374 )5375 level_2_failure = (5376 "\n*\n*** Exception: <Level 2> Visual Diff Failure:\n"5377 "* HTML tag attribute names don't match the baseline!"5378 )5379 level_3_failure = (5380 "\n*\n*** Exception: <Level 3> Visual Diff Failure:\n"5381 "* HTML tag attribute values don't match the baseline!"5382 )5383 page_domain = self.get_domain_url(page_url)5384 page_data_domain = self.get_domain_url(page_url_data)5385 unittest.TestCase.maxDiff = 32005386 if level != 0 and check_domain:5387 self.assertEqual(page_data_domain, page_domain, domain_fail)5388 unittest.TestCase.maxDiff = 6400 # Use `None` for no limit5389 if level == 3:5390 if not full_diff:5391 self.__assert_eq(level_3_data, level_3, level_3_failure)5392 else:5393 self.assertEqual(level_3_data, level_3, level_3_failure)5394 unittest.TestCase.maxDiff = 32005395 if level == 2:5396 if not full_diff:5397 self.__assert_eq(level_2_data, level_2, level_2_failure)5398 else:5399 self.assertEqual(level_2_data, level_2, level_2_failure)5400 if level == 1:5401 if not full_diff:5402 self.__assert_eq(level_1_data, level_1, level_1_failure)5403 else:5404 self.assertEqual(level_1_data, level_1, level_1_failure)5405 unittest.TestCase.maxDiff = 6400 # Use `None` for no limit5406 if level == 0:5407 try:5408 unittest.TestCase.maxDiff = 32005409 if check_domain:5410 self.assertEqual(5411 page_domain, page_data_domain, domain_fail5412 )5413 try:5414 if not full_diff:5415 self.__assert_eq(5416 level_1_data, level_1, level_1_failure5417 )5418 else:5419 self.assertEqual(5420 level_1_data, level_1, level_1_failure5421 )5422 except Exception as e:5423 print(e)5424 try:5425 if not full_diff:5426 self.__assert_eq(5427 level_2_data, level_2, level_2_failure5428 )5429 else:5430 self.assertEqual(5431 level_2_data, level_2, level_2_failure5432 )5433 except Exception as e:5434 print(e)5435 unittest.TestCase.maxDiff = 6400 # Use `None` for no limit5436 if not full_diff:5437 self.__assert_eq(5438 level_3_data, level_3, level_3_failure5439 )5440 else:5441 self.assertEqual(5442 level_3_data, level_3, level_3_failure5443 )5444 except Exception as e:5445 print(e) # Level-0 Dry Run (Only print the differences)5446 unittest.TestCase.maxDiff = None # Reset unittest.TestCase.maxDiff5447 # Since the check passed, do not save an extra copy of the baseline5448 del self.__visual_baseline_copies[-1] # .pop() returns the element5449 def __get_exception_message(self):5450 """This method extracts the message from an exception if there5451 was an exception that occurred during the test, assuming5452 that the exception was in a try/except block and not thrown."""5453 exception_info = sys.exc_info()[1]5454 if hasattr(exception_info, "msg"):5455 exc_message = exception_info.msg5456 elif hasattr(exception_info, "message"):5457 exc_message = exception_info.message5458 else:5459 exc_message = sys.exc_info()5460 return exc_message5461 def __add_deferred_assert_failure(self):5462 """ Add a deferred_assert failure to a list for future processing. """5463 self.__check_scope__()5464 current_url = self.driver.current_url5465 message = self.__get_exception_message()5466 self.__deferred_assert_failures.append(5467 "CHECK #%s: (%s) %s\n"5468 % (self.__deferred_assert_count, current_url, message)5469 )5470 ############5471 def deferred_assert_element(5472 self, selector, by=By.CSS_SELECTOR, timeout=None5473 ):5474 """A non-terminating assertion for an element on a page.5475 Failures will be saved until the process_deferred_asserts()5476 method is called from inside a test, likely at the end of it."""5477 self.__check_scope__()5478 timeout = self.get_timeout(timeout, constants.MINI_TIMEOUT)5479 self.__deferred_assert_count += 15480 try:5481 url = self.get_current_url()5482 if url == self.__last_url_of_deferred_assert:5483 timeout = 1 # Was already on page (full wait not needed)5484 else:5485 self.__last_url_of_deferred_assert = url5486 except Exception:5487 pass5488 try:5489 self.wait_for_element_visible(selector, by=by, timeout=timeout)5490 return True5491 except Exception:5492 self.__add_deferred_assert_failure()5493 return False5494 def deferred_assert_text(5495 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5496 ):5497 """A non-terminating assertion for text from an element on a page.5498 Failures will be saved until the process_deferred_asserts()5499 method is called from inside a test, likely at the end of it."""5500 self.__check_scope__()5501 timeout = self.get_timeout(timeout, constants.MINI_TIMEOUT)5502 self.__deferred_assert_count += 15503 try:5504 url = self.get_current_url()5505 if url == self.__last_url_of_deferred_assert:5506 timeout = 1 # Was already on page (full wait not needed)5507 else:5508 self.__last_url_of_deferred_assert = url5509 except Exception:5510 pass5511 try:5512 self.wait_for_text_visible(text, selector, by=by, timeout=timeout)5513 return True5514 except Exception:5515 self.__add_deferred_assert_failure()5516 return False5517 def deferred_assert_exact_text(5518 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5519 ):5520 """A non-terminating assertion for exact text from an element.5521 Failures will be saved until the process_deferred_asserts()5522 method is called from inside a test, likely at the end of it."""5523 self.__check_scope__()5524 timeout = self.get_timeout(timeout, constants.MINI_TIMEOUT)5525 self.__deferred_assert_count += 15526 try:5527 url = self.get_current_url()5528 if url == self.__last_url_of_deferred_assert:5529 timeout = 1 # Was already on page (full wait not needed)5530 else:5531 self.__last_url_of_deferred_assert = url5532 except Exception:5533 pass5534 try:5535 self.wait_for_exact_text_visible(5536 text, selector, by=by, timeout=timeout5537 )5538 return True5539 except Exception:5540 self.__add_deferred_assert_failure()5541 return False5542 def deferred_check_window(5543 self,5544 name="default",5545 level=0,5546 baseline=False,5547 check_domain=True,5548 full_diff=False,5549 ):5550 """A non-terminating assertion for the check_window() method.5551 Failures will be saved until the process_deferred_asserts()5552 method is called from inside a test, likely at the end of it."""5553 self.__check_scope__()5554 self.__deferred_assert_count += 15555 try:5556 self.check_window(5557 name=name,5558 level=level,5559 baseline=baseline,5560 check_domain=check_domain,5561 full_diff=full_diff,5562 )5563 return True5564 except Exception:5565 self.__add_deferred_assert_failure()5566 return False5567 def process_deferred_asserts(self, print_only=False):5568 """To be used with any test that uses deferred_asserts, which are5569 non-terminating verifications that only raise exceptions5570 after this method is called.5571 This is useful for pages with multiple elements to be checked when5572 you want to find as many bugs as possible in a single test run5573 before having all the exceptions get raised simultaneously.5574 Might be more useful if this method is called after processing all5575 the deferred asserts on a single html page so that the failure5576 screenshot matches the location of the deferred asserts.5577 If "print_only" is set to True, the exception won't get raised."""5578 if self.__deferred_assert_failures:5579 exception_output = ""5580 exception_output += "\n***** DEFERRED ASSERTION FAILURES:\n"5581 exception_output += "TEST: %s\n\n" % self.id()5582 all_failing_checks = self.__deferred_assert_failures5583 self.__deferred_assert_failures = []5584 for tb in all_failing_checks:5585 exception_output += "%s\n" % tb5586 if print_only:5587 print(exception_output)5588 else:5589 raise Exception(exception_output.replace("\\n", "\n"))5590 ############5591 # Alternate naming scheme for the "deferred_assert" methods.5592 def delayed_assert_element(5593 self, selector, by=By.CSS_SELECTOR, timeout=None5594 ):5595 """ Same as self.deferred_assert_element() """5596 return self.deferred_assert_element(5597 selector=selector, by=by, timeout=timeout5598 )5599 def delayed_assert_text(5600 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5601 ):5602 """ Same as self.deferred_assert_text() """5603 return self.deferred_assert_text(5604 text=text, selector=selector, by=by, timeout=timeout5605 )5606 def delayed_assert_exact_text(5607 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5608 ):5609 """ Same as self.deferred_assert_exact_text() """5610 return self.deferred_assert_exact_text(5611 text=text, selector=selector, by=by, timeout=timeout5612 )5613 def delayed_check_window(5614 self,5615 name="default",5616 level=0,5617 baseline=False,5618 check_domain=True,5619 full_diff=False5620 ):5621 """ Same as self.deferred_check_window() """5622 return self.deferred_check_window(5623 name=name,5624 level=level,5625 baseline=baseline,5626 check_domain=check_domain,5627 full_diff=full_diff,5628 )5629 def process_delayed_asserts(self, print_only=False):5630 """ Same as self.process_deferred_asserts() """5631 self.process_deferred_asserts(print_only=print_only)5632 ############5633 def __js_click(self, selector, by=By.CSS_SELECTOR):5634 """ Clicks an element using pure JS. Does not use jQuery. """5635 selector, by = self.__recalculate_selector(selector, by)5636 css_selector = self.convert_to_css_selector(selector, by=by)5637 css_selector = re.escape(css_selector) # Add "\\" to special chars5638 css_selector = self.__escape_quotes_if_needed(css_selector)5639 script = (5640 """var simulateClick = function (elem) {5641 var evt = new MouseEvent('click', {5642 bubbles: true,5643 cancelable: true,5644 view: window5645 });5646 var canceled = !elem.dispatchEvent(evt);5647 };5648 var someLink = document.querySelector('%s');5649 simulateClick(someLink);"""5650 % css_selector5651 )5652 self.execute_script(script)5653 def __js_click_all(self, selector, by=By.CSS_SELECTOR):5654 """ Clicks all matching elements using pure JS. (No jQuery) """5655 selector, by = self.__recalculate_selector(selector, by)5656 css_selector = self.convert_to_css_selector(selector, by=by)5657 css_selector = re.escape(css_selector) # Add "\\" to special chars5658 css_selector = self.__escape_quotes_if_needed(css_selector)5659 script = (5660 """var simulateClick = function (elem) {5661 var evt = new MouseEvent('click', {5662 bubbles: true,5663 cancelable: true,5664 view: window5665 });5666 var canceled = !elem.dispatchEvent(evt);5667 };5668 var $elements = document.querySelectorAll('%s');5669 var index = 0, length = $elements.length;5670 for(; index < length; index++){5671 simulateClick($elements[index]);}"""5672 % css_selector5673 )5674 self.execute_script(script)5675 def __jquery_slow_scroll_to(self, selector, by=By.CSS_SELECTOR):5676 selector, by = self.__recalculate_selector(selector, by)5677 element = self.wait_for_element_present(5678 selector, by=by, timeout=settings.SMALL_TIMEOUT5679 )5680 dist = js_utils.get_scroll_distance_to_element(self.driver, element)5681 time_offset = 05682 try:5683 if dist and abs(dist) > constants.Values.SSMD:5684 time_offset = int(5685 float(abs(dist) - constants.Values.SSMD) / 12.55686 )5687 if time_offset > 950:5688 time_offset = 9505689 except Exception:5690 time_offset = 05691 scroll_time_ms = 550 + time_offset5692 sleep_time = 0.625 + (float(time_offset) / 1000.0)5693 selector = self.convert_to_css_selector(selector, by=by)5694 selector = self.__make_css_match_first_element_only(selector)5695 scroll_script = (5696 """jQuery([document.documentElement, document.body]).animate({"""5697 """scrollTop: jQuery('%s').offset().top - 130}, %s);"""5698 % (selector, scroll_time_ms)5699 )5700 if js_utils.is_jquery_activated(self.driver):5701 self.execute_script(scroll_script)5702 else:5703 self.__slow_scroll_to_element(element)5704 self.sleep(sleep_time)5705 def __jquery_click(self, selector, by=By.CSS_SELECTOR):5706 """ Clicks an element using jQuery. Different from using pure JS. """5707 self.wait_for_element_present(5708 selector, by=by, timeout=settings.SMALL_TIMEOUT5709 )5710 selector = self.convert_to_css_selector(selector, by=by)5711 selector = self.__make_css_match_first_element_only(selector)5712 click_script = """jQuery('%s')[0].click();""" % selector5713 self.safe_execute_script(click_script)5714 def __get_href_from_link_text(self, link_text, hard_fail=True):5715 href = self.get_link_attribute(link_text, "href", hard_fail)5716 if not href:5717 return None5718 if href.startswith("//"):5719 link = "http:" + href5720 elif href.startswith("/"):5721 url = self.driver.current_url5722 domain_url = self.get_domain_url(url)5723 link = domain_url + href5724 else:5725 link = href5726 return link5727 def __click_dropdown_link_text(self, link_text, link_css):5728 """ When a link may be hidden under a dropdown menu, use this. """5729 soup = self.get_beautiful_soup()5730 drop_down_list = []5731 for item in soup.select("li[class]"):5732 drop_down_list.append(item)5733 csstype = link_css.split("[")[1].split("=")[0]5734 for item in drop_down_list:5735 item_text_list = item.text.split("\n")5736 if link_text in item_text_list and csstype in item.decode():5737 dropdown_css = ""5738 try:5739 for css_class in item["class"]:5740 dropdown_css += "."5741 dropdown_css += css_class5742 except Exception:5743 continue5744 dropdown_css = item.name + dropdown_css5745 matching_dropdowns = self.find_visible_elements(dropdown_css)5746 for dropdown in matching_dropdowns:5747 # The same class names might be used for multiple dropdowns5748 if dropdown.is_displayed():5749 try:5750 try:5751 page_actions.hover_element(5752 self.driver,5753 dropdown,5754 )5755 except Exception:5756 # If hovering fails, driver is likely outdated5757 # Time to go directly to the hidden link text5758 self.open(5759 self.__get_href_from_link_text(link_text)5760 )5761 return True5762 page_actions.hover_element_and_click(5763 self.driver,5764 dropdown,5765 link_text,5766 click_by=By.LINK_TEXT,5767 timeout=0.12,5768 )5769 return True5770 except Exception:5771 pass5772 return False5773 def __get_href_from_partial_link_text(self, link_text, hard_fail=True):5774 href = self.get_partial_link_text_attribute(5775 link_text, "href", hard_fail5776 )5777 if not href:5778 return None5779 if href.startswith("//"):5780 link = "http:" + href5781 elif href.startswith("/"):5782 url = self.driver.current_url5783 domain_url = self.get_domain_url(url)5784 link = domain_url + href5785 else:5786 link = href5787 return link5788 def __click_dropdown_partial_link_text(self, link_text, link_css):5789 """ When a partial link may be hidden under a dropdown, use this. """5790 soup = self.get_beautiful_soup()5791 drop_down_list = []5792 for item in soup.select("li[class]"):5793 drop_down_list.append(item)5794 csstype = link_css.split("[")[1].split("=")[0]5795 for item in drop_down_list:5796 item_text_list = item.text.split("\n")5797 if link_text in item_text_list and csstype in item.decode():5798 dropdown_css = ""5799 try:5800 for css_class in item["class"]:5801 dropdown_css += "."5802 dropdown_css += css_class5803 except Exception:5804 continue5805 dropdown_css = item.name + dropdown_css5806 matching_dropdowns = self.find_visible_elements(dropdown_css)5807 for dropdown in matching_dropdowns:5808 # The same class names might be used for multiple dropdowns5809 if dropdown.is_displayed():5810 try:5811 try:5812 page_actions.hover_element(5813 self.driver, dropdown5814 )5815 except Exception:5816 # If hovering fails, driver is likely outdated5817 # Time to go directly to the hidden link text5818 self.open(5819 self.__get_href_from_partial_link_text(5820 link_text5821 )5822 )5823 return True5824 page_actions.hover_element_and_click(5825 self.driver,5826 dropdown,5827 link_text,5828 click_by=By.LINK_TEXT,5829 timeout=0.12,5830 )5831 return True5832 except Exception:5833 pass5834 return False5835 def __looks_like_a_page_url(self, url):5836 """Returns True if the url parameter looks like a URL. This method5837 is slightly more lenient than page_utils.is_valid_url(url) due to5838 possible typos when calling self.get(url), which will try to5839 navigate to the page if a URL is detected, but will instead call5840 self.get_element(URL_AS_A_SELECTOR) if the input in not a URL."""5841 if (5842 url.startswith("http:")5843 or url.startswith("https:")5844 or url.startswith("://")5845 or url.startswith("chrome:")5846 or url.startswith("about:")5847 or url.startswith("data:")5848 or url.startswith("file:")5849 or url.startswith("edge:")5850 or url.startswith("opera:")5851 or url.startswith("view-source:")5852 ):5853 return True5854 else:5855 return False5856 def __make_css_match_first_element_only(self, selector):5857 # Only get the first match5858 return page_utils.make_css_match_first_element_only(selector)5859 def __demo_mode_scroll_if_active(self, selector, by):5860 if self.demo_mode:5861 self.slow_scroll_to(selector, by=by)5862 def __demo_mode_highlight_if_active(self, selector, by):5863 if self.demo_mode:5864 # Includes self.slow_scroll_to(selector, by=by) by default5865 self.highlight(selector, by=by)5866 elif self.slow_mode:5867 # Just do the slow scroll part of the highlight() method5868 time.sleep(0.08)5869 element = self.wait_for_element_visible(5870 selector, by=by, timeout=settings.SMALL_TIMEOUT5871 )5872 try:5873 scroll_distance = js_utils.get_scroll_distance_to_element(5874 self.driver, element5875 )5876 if abs(scroll_distance) > settings.SSMD:5877 self.__jquery_slow_scroll_to(selector, by)5878 else:5879 self.__slow_scroll_to_element(element)5880 except (StaleElementReferenceException, ElementNotInteractableException):5881 self.wait_for_ready_state_complete()5882 time.sleep(0.12)5883 element = self.wait_for_element_visible(5884 selector, by=by, timeout=settings.SMALL_TIMEOUT5885 )5886 self.__slow_scroll_to_element(element)5887 time.sleep(0.12)5888 def __scroll_to_element(self, element, selector=None, by=By.CSS_SELECTOR):5889 success = js_utils.scroll_to_element(self.driver, element)5890 if not success and selector:5891 self.wait_for_ready_state_complete()5892 element = element_actions.wait_for_element_visible(5893 self.driver, selector, by, timeout=settings.SMALL_TIMEOUT5894 )5895 self._demo_mode_pause_if_active(tiny=True)5896 def __slow_scroll_to_element(self, element):5897 try:5898 js_utils.slow_scroll_to_element(self.driver, element, self.browser)5899 except Exception:5900 # Scroll to the element instantly if the slow scroll fails5901 js_utils.scroll_to_element(self.driver, element)5902 def __highlight_with_assert_success(5903 self, message, selector, by=By.CSS_SELECTOR5904 ):5905 selector, by = self.__recalculate_selector(selector, by, xp_ok=False)5906 element = self.wait_for_element_visible(5907 selector, by=by, timeout=settings.SMALL_TIMEOUT5908 )5909 try:5910 scroll_distance = js_utils.get_scroll_distance_to_element(5911 self.driver, element5912 )5913 if abs(scroll_distance) > constants.Values.SSMD:5914 self.__jquery_slow_scroll_to(selector, by)5915 else:5916 self.__slow_scroll_to_element(element)5917 except Exception:5918 self.wait_for_ready_state_complete()5919 time.sleep(0.12)5920 element = self.wait_for_element_visible(5921 selector, by=by, timeout=settings.SMALL_TIMEOUT5922 )5923 self.__slow_scroll_to_element(element)5924 try:5925 selector = self.convert_to_css_selector(selector, by=by)5926 except Exception:5927 # Don't highlight if can't convert to CSS_SELECTOR5928 return5929 o_bs = "" # original_box_shadow5930 try:5931 style = element.get_attribute("style")5932 except Exception:5933 self.wait_for_ready_state_complete()5934 time.sleep(0.12)5935 element = self.wait_for_element_visible(5936 selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT5937 )5938 style = element.get_attribute("style")5939 if style:5940 if "box-shadow: " in style:5941 box_start = style.find("box-shadow: ")5942 box_end = style.find(";", box_start) + 15943 original_box_shadow = style[box_start:box_end]5944 o_bs = original_box_shadow5945 if ":contains" not in selector and ":first" not in selector:5946 selector = re.escape(selector)5947 selector = self.__escape_quotes_if_needed(selector)5948 self.__highlight_with_js_2(message, selector, o_bs)5949 else:5950 selector = self.__make_css_match_first_element_only(selector)5951 selector = re.escape(selector)5952 selector = self.__escape_quotes_if_needed(selector)5953 try:5954 self.__highlight_with_jquery_2(message, selector, o_bs)5955 except Exception:5956 pass # JQuery probably couldn't load. Skip highlighting.5957 time.sleep(0.065)5958 def __highlight_with_js_2(self, message, selector, o_bs):5959 duration = self.message_duration5960 if not duration:5961 duration = settings.DEFAULT_MESSAGE_DURATION5962 if (self.headless or self.xvfb) and float(duration) > 0.75:5963 duration = 0.755964 js_utils.highlight_with_js_2(5965 self.driver, message, selector, o_bs, duration5966 )5967 def __highlight_with_jquery_2(self, message, selector, o_bs):5968 duration = self.message_duration5969 if not duration:5970 duration = settings.DEFAULT_MESSAGE_DURATION5971 if (self.headless or self.xvfb) and float(duration) > 0.75:5972 duration = 0.755973 js_utils.highlight_with_jquery_2(5974 self.driver, message, selector, o_bs, duration5975 )5976 ############5977 from seleniumbase.common import decorators5978 @decorators.deprecated("You should use re.escape() instead.")5979 def jq_format(self, code):5980 # DEPRECATED - re.escape() already performs the intended action.5981 return js_utils._jq_format(code)5982 ############5983 def setup(self):5984 """5985 Be careful if a subclass of BaseCase overrides setUp()5986 You'll need to add the following line to the subclass setUp() method:5987 super(SubClassOfBaseCase, self).setUp()5988 """5989 if self._called_setup:5990 # This test already called setUp()5991 return5992 self._called_setup = True5993 self._called_teardown = False5994 if self.is_pytest:5995 self.headless_active = False5996 self.mobile_emulator = sb_config.mobile_emulator5997 self.device_metrics = sb_config.device_metrics5998 self.cap_file = sb_config.cap_file5999 self.cap_string = sb_config.cap_string6000 self.settings_file = sb_config.settings_file6001 self.database_env = sb_config.database_env6002 self.message_duration = sb_config.message_duration6003 self.js_checking_on = sb_config.js_checking_on6004 self.ad_block_on = sb_config.ad_block_on6005 self.block_images = sb_config.block_images6006 self.chromium_arg = sb_config.chromium_arg6007 self.firefox_arg = sb_config.firefox_arg6008 self.firefox_pref = sb_config.firefox_pref6009 self.verify_delay = sb_config.verify_delay6010 self.recorder_mode = sb_config.recorder_mode6011 self.recorder_ext = sb_config.recorder_mode6012 self.disable_csp = sb_config.disable_csp6013 self.disable_ws = sb_config.disable_ws6014 self.enable_ws = sb_config.enable_ws6015 if not self.config.getoption("disable_ws", False):6016 self.config.option.enable_ws = True6017 self.enable_sync = sb_config.enable_sync6018 self.use_auto_ext = sb_config.use_auto_ext6019 self.no_sandbox = sb_config.no_sandbox6020 self.disable_gpu = sb_config.disable_gpu6021 self.incognito = sb_config.incognito6022 self.guest_mode = sb_config.guest_mode6023 self.devtools = sb_config.devtools6024 self.remote_debug = sb_config.remote_debug6025 self._multithreaded = sb_config._multithreaded6026 self._reuse_session = sb_config.reuse_session6027 self._crumbs = sb_config.crumbs6028 self.dashboard = sb_config.dashboard6029 self._dash_initialized = sb_config._dashboard_initialized6030 if self.dashboard and self._multithreaded:6031 import fasteners6032 self.dash_lock = fasteners.InterProcessLock(6033 constants.Dashboard.LOCKFILE6034 )6035 self.swiftshader = sb_config.swiftshader6036 self.user_data_dir = sb_config.user_data_dir6037 self.extension_zip = sb_config.extension_zip6038 self.extension_dir = sb_config.extension_dir6039 self.external_pdf = sb_config.external_pdf6040 self.maximize_option = sb_config.maximize_option6041 self.save_screenshot_after_test = sb_config.save_screenshot6042 self.visual_baseline = sb_config.visual_baseline6043 self.timeout_multiplier = sb_config.timeout_multiplier6044 self.pytest_html_report = sb_config.pytest_html_report6045 self.report_on = False6046 if self.pytest_html_report:6047 self.report_on = True6048 self.use_grid = False6049 if self.servername != "localhost":6050 # Use Selenium Grid (Use --server="127.0.0.1" for a local Grid)6051 self.use_grid = True6052 if self.with_db_reporting:6053 import getpass6054 import uuid6055 from seleniumbase.core.application_manager import (6056 ApplicationManager,6057 )6058 from seleniumbase.core.testcase_manager import (6059 ExecutionQueryPayload,6060 )6061 from seleniumbase.core.testcase_manager import (6062 TestcaseDataPayload,6063 )6064 from seleniumbase.core.testcase_manager import TestcaseManager6065 self.execution_guid = str(uuid.uuid4())6066 self.testcase_guid = None6067 self.execution_start_time = 06068 self.case_start_time = 06069 self.application = None6070 self.testcase_manager = None6071 self.error_handled = False6072 self.testcase_manager = TestcaseManager(self.database_env)6073 #6074 exec_payload = ExecutionQueryPayload()6075 exec_payload.execution_start_time = int(time.time() * 1000)6076 self.execution_start_time = exec_payload.execution_start_time6077 exec_payload.guid = self.execution_guid6078 exec_payload.username = getpass.getuser()6079 self.testcase_manager.insert_execution_data(exec_payload)6080 #6081 data_payload = TestcaseDataPayload()6082 self.testcase_guid = str(uuid.uuid4())6083 data_payload.guid = self.testcase_guid6084 data_payload.execution_guid = self.execution_guid6085 if self.with_selenium:6086 data_payload.browser = self.browser6087 else:6088 data_payload.browser = "N/A"6089 data_payload.test_address = test_id6090 application = ApplicationManager.generate_application_string(6091 self._testMethodName6092 )6093 data_payload.env = application.split(".")[0]6094 data_payload.start_time = application.split(".")[1]6095 data_payload.state = constants.State.UNTESTED6096 self.__skip_reason = None6097 self.testcase_manager.insert_testcase_data(data_payload)6098 self.case_start_time = int(time.time() * 1000)6099 if self.headless or self.xvfb:6100 width = settings.HEADLESS_START_WIDTH6101 height = settings.HEADLESS_START_HEIGHT6102 try:6103 # from pyvirtualdisplay import Display # Skip for own lib6104 from sbvirtualdisplay import Display6105 self.display = Display(visible=0, size=(width, height))6106 self.display.start()6107 self.headless_active = True6108 except Exception:6109 # pyvirtualdisplay might not be necessary anymore because6110 # Chrome and Firefox now have built-in headless displays6111 pass6112 # Verify that SeleniumBase is installed successfully6113 if not hasattr(self, "browser"):6114 raise Exception(6115 'SeleniumBase plugins DID NOT load! * Please REINSTALL!\n'6116 '*** Either install SeleniumBase in Dev Mode from a clone:\n'6117 ' >>> "pip install -e ." (Run in DIR with setup.py)\n'6118 '*** Or install the latest SeleniumBase version from PyPI:\n'6119 ' >>> "pip install -U seleniumbase" (Run in any DIR)'6120 )6121 if not hasattr(sb_config, "_is_timeout_changed"):6122 # Should only be reachable from pure Python runs6123 sb_config._is_timeout_changed = False6124 sb_config._SMALL_TIMEOUT = settings.SMALL_TIMEOUT6125 sb_config._LARGE_TIMEOUT = settings.LARGE_TIMEOUT6126 if sb_config._is_timeout_changed:6127 if sb_config._SMALL_TIMEOUT and sb_config._LARGE_TIMEOUT:6128 settings.SMALL_TIMEOUT = sb_config._SMALL_TIMEOUT6129 settings.LARGE_TIMEOUT = sb_config._LARGE_TIMEOUT6130 if not hasattr(sb_config, "_recorded_actions"):6131 # Only filled when Recorder Mode is enabled6132 sb_config._recorded_actions = {}6133 if not hasattr(settings, "SWITCH_TO_NEW_TABS_ON_CLICK"):6134 # If using an older settings file, set the new definitions manually6135 settings.SWITCH_TO_NEW_TABS_ON_CLICK = True6136 # Parse the settings file6137 if self.settings_file:6138 from seleniumbase.core import settings_parser6139 settings_parser.set_settings(self.settings_file)6140 # Set variables that may be useful to developers6141 self.log_abspath = os.path.abspath(self.log_path)6142 self.data_path = os.path.join(self.log_path, self.__get_test_id())6143 self.data_abspath = os.path.abspath(self.data_path)6144 # Mobile Emulator device metrics: CSS Width, CSS Height, & Pixel-Ratio6145 if self.device_metrics:6146 metrics_string = self.device_metrics6147 metrics_string = metrics_string.replace(" ", "")6148 metrics_list = metrics_string.split(",")6149 exception_string = (6150 "Invalid input for Mobile Emulator device metrics!\n"6151 "Expecting a comma-separated string with three\n"6152 "integer values for Width, Height, and Pixel-Ratio.\n"6153 'Example: --metrics="411,731,3" '6154 )6155 if len(metrics_list) != 3:6156 raise Exception(exception_string)6157 try:6158 self.__device_width = int(metrics_list[0])6159 self.__device_height = int(metrics_list[1])6160 self.__device_pixel_ratio = int(metrics_list[2])6161 self.mobile_emulator = True6162 except Exception:6163 raise Exception(exception_string)6164 if self.mobile_emulator:6165 if not self.user_agent:6166 # Use the Pixel 4 user agent by default if not specified6167 self.user_agent = (6168 "Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) "6169 "AppleWebKit/537.36 (KHTML, like Gecko) "6170 "Chrome/89.0.4389.105 Mobile Safari/537.36"6171 )6172 # Dashboard pre-processing:6173 if self.dashboard:6174 if self._multithreaded:6175 with self.dash_lock:6176 sb_config._sbase_detected = True6177 sb_config._only_unittest = False6178 if not self._dash_initialized:6179 sb_config._dashboard_initialized = True6180 self._dash_initialized = True6181 self.__process_dashboard(False, init=True)6182 else:6183 sb_config._sbase_detected = True6184 sb_config._only_unittest = False6185 if not self._dash_initialized:6186 sb_config._dashboard_initialized = True6187 self._dash_initialized = True6188 self.__process_dashboard(False, init=True)6189 has_url = False6190 if self._reuse_session:6191 if not hasattr(sb_config, "shared_driver"):6192 sb_config.shared_driver = None6193 if sb_config.shared_driver:6194 try:6195 self._default_driver = sb_config.shared_driver6196 self.driver = sb_config.shared_driver6197 self._drivers_list = [sb_config.shared_driver]6198 url = self.get_current_url()6199 if url is not None:6200 has_url = True6201 if len(self.driver.window_handles) > 1:6202 while len(self.driver.window_handles) > 1:6203 self.switch_to_window(6204 len(self.driver.window_handles) - 16205 )6206 self.driver.close()6207 self.switch_to_window(0)6208 if self._crumbs:6209 self.driver.delete_all_cookies()6210 except Exception:6211 pass6212 if self._reuse_session and sb_config.shared_driver and has_url:6213 good_start_page = False6214 if self.start_page and len(self.start_page) >= 4:6215 if page_utils.is_valid_url(self.start_page):6216 good_start_page = True6217 self.__new_window_on_rec_open = False6218 self.open(self.start_page)6219 self.__new_window_on_rec_open = True6220 else:6221 new_start_page = "https://" + self.start_page6222 if page_utils.is_valid_url(new_start_page):6223 good_start_page = True6224 self.__dont_record_open = True6225 self.open(new_start_page)6226 self.__dont_record_open = False6227 else:6228 # Launch WebDriver for both Pytest and Nosetests6229 self.driver = self.get_new_driver(6230 browser=self.browser,6231 headless=self.headless,6232 locale_code=self.locale_code,6233 protocol=self.protocol,6234 servername=self.servername,6235 port=self.port,6236 proxy=self.proxy_string,6237 proxy_bypass_list=self.proxy_bypass_list,6238 agent=self.user_agent,6239 switch_to=True,6240 cap_file=self.cap_file,6241 cap_string=self.cap_string,6242 recorder_ext=self.recorder_ext,6243 disable_csp=self.disable_csp,6244 enable_ws=self.enable_ws,6245 enable_sync=self.enable_sync,6246 use_auto_ext=self.use_auto_ext,6247 no_sandbox=self.no_sandbox,6248 disable_gpu=self.disable_gpu,6249 incognito=self.incognito,6250 guest_mode=self.guest_mode,6251 devtools=self.devtools,6252 remote_debug=self.remote_debug,6253 swiftshader=self.swiftshader,6254 ad_block_on=self.ad_block_on,6255 block_images=self.block_images,6256 chromium_arg=self.chromium_arg,6257 firefox_arg=self.firefox_arg,6258 firefox_pref=self.firefox_pref,6259 user_data_dir=self.user_data_dir,6260 extension_zip=self.extension_zip,6261 extension_dir=self.extension_dir,6262 external_pdf=self.external_pdf,6263 is_mobile=self.mobile_emulator,6264 d_width=self.__device_width,6265 d_height=self.__device_height,6266 d_p_r=self.__device_pixel_ratio,6267 )6268 self._default_driver = self.driver6269 if self._reuse_session:6270 sb_config.shared_driver = self.driver6271 if self.browser in ["firefox", "ie", "safari", "opera"]:6272 # Only Chrome and Edge browsers have the mobile emulator.6273 # Some actions such as hover-clicking are different on mobile.6274 self.mobile_emulator = False6275 # Configure the test time limit (if used).6276 self.set_time_limit(self.time_limit)6277 # Set the start time for the test (in ms).6278 # Although the pytest clock starts before setUp() begins,6279 # the time-limit clock starts at the end of the setUp() method.6280 sb_config.start_time_ms = int(time.time() * 1000.0)6281 if not self.__start_time_ms:6282 # Call this once in case of multiple setUp() calls in the same test6283 self.__start_time_ms = sb_config.start_time_ms6284 def __set_last_page_screenshot(self):6285 """self.__last_page_screenshot is only for pytest html report logs.6286 self.__last_page_screenshot_png is for all screenshot log files."""6287 if not self.__last_page_screenshot and (6288 not self.__last_page_screenshot_png6289 ):6290 try:6291 element = self.driver.find_element(6292 by=By.TAG_NAME, value="body"6293 )6294 if self.is_pytest and self.report_on:6295 self.__last_page_screenshot_png = (6296 self.driver.get_screenshot_as_png()6297 )6298 self.__last_page_screenshot = element.screenshot_as_base646299 else:6300 self.__last_page_screenshot_png = element.screenshot_as_png6301 except Exception:6302 if not self.__last_page_screenshot:6303 if self.is_pytest and self.report_on:6304 try:6305 self.__last_page_screenshot = (6306 self.driver.get_screenshot_as_base64()6307 )6308 except Exception:6309 self.__last_page_screenshot = (6310 constants.Warnings.SCREENSHOT_UNDEFINED6311 )6312 if not self.__last_page_screenshot_png:6313 try:6314 self.__last_page_screenshot_png = (6315 self.driver.get_screenshot_as_png()6316 )6317 except Exception:6318 self.__last_page_screenshot_png = (6319 constants.Warnings.SCREENSHOT_UNDEFINED6320 )6321 def __set_last_page_url(self):6322 if not self.__last_page_url:6323 try:6324 self.__last_page_url = log_helper.get_last_page(self.driver)6325 except Exception:6326 self.__last_page_url = None6327 def __set_last_page_source(self):6328 if not self.__last_page_source:6329 try:6330 self.__last_page_source = (6331 log_helper.get_html_source_with_base_href(6332 self.driver, self.driver.page_source6333 )6334 )6335 except Exception:6336 self.__last_page_source = (6337 constants.Warnings.PAGE_SOURCE_UNDEFINED6338 )6339 def __get_exception_info(self):6340 exc_message = None6341 if (6342 python36343 and hasattr(self, "_outcome")6344 and (hasattr(self._outcome, "errors") and self._outcome.errors)6345 ):6346 try:6347 exc_message = self._outcome.errors[0][1][1]6348 except Exception:6349 exc_message = "(Unknown Exception)"6350 else:6351 try:6352 exc_message = sys.last_value6353 except Exception:6354 exc_message = "(Unknown Exception)"6355 return str(exc_message)6356 def __insert_test_result(self, state, err):6357 from seleniumbase.core.testcase_manager import TestcaseDataPayload6358 data_payload = TestcaseDataPayload()6359 data_payload.runtime = int(time.time() * 1000) - self.case_start_time6360 data_payload.guid = self.testcase_guid6361 data_payload.execution_guid = self.execution_guid6362 data_payload.state = state6363 if err:6364 import traceback6365 tb_string = traceback.format_exc()6366 if "Message: " in tb_string:6367 data_payload.message = (6368 "Message: " + tb_string.split("Message: ")[-1]6369 )6370 elif "Exception: " in tb_string:6371 data_payload.message = tb_string.split("Exception: ")[-1]6372 elif "Error: " in tb_string:6373 data_payload.message = tb_string.split("Error: ")[-1]6374 else:6375 data_payload.message = self.__get_exception_info()6376 else:6377 test_id = self.__get_test_id_2()6378 if (6379 self.is_pytest6380 and test_id in sb_config._results.keys()6381 and (sb_config._results[test_id] == "Skipped")6382 ):6383 if self.__skip_reason:6384 data_payload.message = "Skipped: " + self.__skip_reason6385 else:6386 data_payload.message = "Skipped: (no reason given)"6387 self.testcase_manager.update_testcase_data(data_payload)6388 def __add_pytest_html_extra(self):6389 if not self.__added_pytest_html_extra:6390 try:6391 if self.with_selenium:6392 if not self.__last_page_screenshot:6393 self.__set_last_page_screenshot()6394 self.__set_last_page_url()6395 self.__set_last_page_source()6396 if self.report_on:6397 extra_url = {}6398 extra_url["name"] = "URL"6399 extra_url["format"] = "url"6400 extra_url["content"] = self.get_current_url()6401 extra_url["mime_type"] = None6402 extra_url["extension"] = None6403 extra_image = {}6404 extra_image["name"] = "Screenshot"6405 extra_image["format"] = "image"6406 extra_image["content"] = self.__last_page_screenshot6407 extra_image["mime_type"] = "image/png"6408 extra_image["extension"] = "png"6409 self.__added_pytest_html_extra = True6410 if self.__last_page_screenshot != (6411 constants.Warnings.SCREENSHOT_UNDEFINED6412 ):6413 self._html_report_extra.append(extra_url)6414 self._html_report_extra.append(extra_image)6415 except Exception:6416 pass6417 def __has_exception(self):6418 has_exception = False6419 if hasattr(sys, "last_traceback") and sys.last_traceback is not None:6420 has_exception = True6421 elif python3 and hasattr(self, "_outcome"):6422 if hasattr(self._outcome, "errors") and self._outcome.errors:6423 has_exception = True6424 else:6425 if python3:6426 has_exception = sys.exc_info()[1] is not None6427 else:6428 if not hasattr(self, "_using_sb_fixture_class") and (6429 not hasattr(self, "_using_sb_fixture_no_class")6430 ):6431 has_exception = sys.exc_info()[1] is not None6432 else:6433 has_exception = len(str(sys.exc_info()[1]).strip()) > 06434 if (6435 self.__will_be_skipped6436 and (hasattr(self, "_using_sb_fixture") or not python3)6437 ):6438 has_exception = False6439 return has_exception6440 def __get_test_id(self):6441 """ The id used in various places such as the test log path. """6442 test_id = "%s.%s.%s" % (6443 self.__class__.__module__,6444 self.__class__.__name__,6445 self._testMethodName,6446 )6447 if self._sb_test_identifier and len(str(self._sb_test_identifier)) > 6:6448 test_id = self._sb_test_identifier6449 test_id = test_id.replace(".py::", ".").replace("::", ".")6450 return test_id6451 def __get_test_id_2(self):6452 """ The id for SeleniumBase Dashboard entries. """6453 if "PYTEST_CURRENT_TEST" in os.environ:6454 return os.environ["PYTEST_CURRENT_TEST"].split(" ")[0]6455 test_id = "%s.%s.%s" % (6456 self.__class__.__module__.split(".")[-1],6457 self.__class__.__name__,6458 self._testMethodName,6459 )6460 if self._sb_test_identifier and len(str(self._sb_test_identifier)) > 6:6461 test_id = self._sb_test_identifier6462 if test_id.count(".") > 1:6463 test_id = ".".join(test_id.split(".")[1:])6464 return test_id6465 def __get_display_id(self):6466 """ The id for running a test from pytest. (Displayed on Dashboard) """6467 if "PYTEST_CURRENT_TEST" in os.environ:6468 return os.environ["PYTEST_CURRENT_TEST"].split(" ")[0]6469 test_id = "%s.py::%s::%s" % (6470 self.__class__.__module__.replace(".", "/"),6471 self.__class__.__name__,6472 self._testMethodName,6473 )6474 if self._sb_test_identifier and len(str(self._sb_test_identifier)) > 6:6475 test_id = self._sb_test_identifier6476 if hasattr(self, "_using_sb_fixture_class"):6477 if test_id.count(".") >= 2:6478 parts = test_id.split(".")6479 full = parts[-3] + ".py::" + parts[-2] + "::" + parts[-1]6480 test_id = full6481 elif hasattr(self, "_using_sb_fixture_no_class"):6482 if test_id.count(".") >= 1:6483 parts = test_id.split(".")6484 full = parts[-2] + ".py::" + parts[-1]6485 test_id = full6486 return test_id6487 def __get_filename(self):6488 """ The filename of the current SeleniumBase test. (NOT Path) """6489 filename = None6490 if "PYTEST_CURRENT_TEST" in os.environ:6491 test_id = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0]6492 filename = test_id.split("::")[0].split("/")[-1]6493 else:6494 filename = self.__class__.__module__.split(".")[-1] + ".py"6495 return filename6496 def __create_log_path_as_needed(self, test_logpath):6497 if not os.path.exists(test_logpath):6498 try:6499 os.makedirs(test_logpath)6500 except Exception:6501 pass # Only reachable during multi-threaded runs6502 # def __process_dashboard(self, has_exception, init=False):6503 # """ SeleniumBase Dashboard Processing """6504 # if self._multithreaded:6505 # existing_res = sb_config._results # For recording "Skipped" tests6506 # abs_path = os.path.abspath(".")6507 # dash_json_loc = constants.Dashboard.DASH_JSON6508 # dash_jsonpath = os.path.join(abs_path, dash_json_loc)6509 # if not init and os.path.exists(dash_jsonpath):6510 # with open(dash_jsonpath, "r") as f:6511 # dash_json = f.read().strip()6512 # dash_data, d_id, dash_rt, tlp, d_stats = json.loads(dash_json)6513 # num_passed, num_failed, num_skipped, num_untested = d_stats6514 # sb_config._results = dash_data6515 # sb_config._display_id = d_id6516 # sb_config._duration = dash_rt # Dashboard Run Time6517 # sb_config._d_t_log_path = tlp # Test Log Path6518 # sb_config.item_count_passed = num_passed6519 # sb_config.item_count_failed = num_failed6520 # sb_config.item_count_skipped = num_skipped6521 # sb_config.item_count_untested = num_untested6522 # if len(sb_config._extra_dash_entries) > 0:6523 # # First take care of existing entries from non-SeleniumBase tests6524 # for test_id in sb_config._extra_dash_entries:6525 # if test_id in sb_config._results.keys():6526 # if sb_config._results[test_id] == "Skipped":6527 # sb_config.item_count_skipped += 16528 # sb_config.item_count_untested -= 16529 # elif sb_config._results[test_id] == "Failed":6530 # sb_config.item_count_failed += 16531 # sb_config.item_count_untested -= 16532 # elif sb_config._results[test_id] == "Passed":6533 # sb_config.item_count_passed += 16534 # sb_config.item_count_untested -= 16535 # else: # Mark "Skipped" if unknown6536 # sb_config.item_count_skipped += 16537 # sb_config.item_count_untested -= 16538 # sb_config._extra_dash_entries = [] # Reset the list to empty6539 # # Process new entries6540 # log_dir = self.log_path6541 # ft_id = self.__get_test_id() # Full test id with path to log files6542 # test_id = self.__get_test_id_2() # The test id used by the DashBoard6543 # dud = "seleniumbase/plugins/pytest_plugin.py::BaseClass::base_method"6544 # dud2 = "pytest_plugin.BaseClass.base_method"6545 # if hasattr(self, "_using_sb_fixture") and self.__will_be_skipped:6546 # test_id = sb_config._test_id6547 # if not init:6548 # duration_ms = int(time.time() * 1000) - self.__start_time_ms6549 # duration = float(duration_ms) / 1000.06550 # duration = "{:.2f}".format(duration)6551 # sb_config._duration[test_id] = duration6552 # if (6553 # has_exception6554 # or self.save_screenshot_after_test6555 # or self.__screenshot_count > 06556 # or self.__will_be_skipped6557 # ):6558 # sb_config._d_t_log_path[test_id] = os.path.join(log_dir, ft_id)6559 # else:6560 # sb_config._d_t_log_path[test_id] = None6561 # if test_id not in sb_config._display_id.keys():6562 # sb_config._display_id[test_id] = self.__get_display_id()6563 # if sb_config._display_id[test_id] == dud:6564 # return6565 # if (6566 # hasattr(self, "_using_sb_fixture")6567 # and test_id not in sb_config._results.keys()6568 # ):6569 # if test_id.count(".") > 1:6570 # alt_test_id = ".".join(test_id.split(".")[1:])6571 # if alt_test_id in sb_config._results.keys():6572 # sb_config._results.pop(alt_test_id)6573 # elif test_id.count(".") == 1:6574 # alt_test_id = sb_config._display_id[test_id]6575 # alt_test_id = alt_test_id.replace(".py::", ".")6576 # alt_test_id = alt_test_id.replace("::", ".")6577 # if alt_test_id in sb_config._results.keys():6578 # sb_config._results.pop(alt_test_id)6579 # if test_id in sb_config._results.keys() and (6580 # sb_config._results[test_id] == "Skipped"6581 # ):6582 # if self.__passed_then_skipped:6583 # # Multiple calls of setUp() and tearDown() in the same test6584 # sb_config.item_count_passed -= 16585 # sb_config.item_count_untested += 16586 # self.__passed_then_skipped = False6587 # sb_config._results[test_id] = "Skipped"6588 # sb_config.item_count_skipped += 16589 # sb_config.item_count_untested -= 16590 # elif (6591 # self._multithreaded6592 # and test_id in existing_res.keys()6593 # and existing_res[test_id] == "Skipped"6594 # ):6595 # sb_config._results[test_id] = "Skipped"6596 # sb_config.item_count_skipped += 16597 # sb_config.item_count_untested -= 16598 # elif has_exception:6599 # if test_id not in sb_config._results.keys():6600 # sb_config._results[test_id] = "Failed"6601 # sb_config.item_count_failed += 16602 # sb_config.item_count_untested -= 16603 # elif not sb_config._results[test_id] == "Failed":6604 # # tearDown() was called more than once in the test6605 # if sb_config._results[test_id] == "Passed":6606 # # Passed earlier, but last run failed6607 # sb_config._results[test_id] = "Failed"6608 # sb_config.item_count_failed += 16609 # sb_config.item_count_passed -= 16610 # else:6611 # sb_config._results[test_id] = "Failed"6612 # sb_config.item_count_failed += 16613 # sb_config.item_count_untested -= 16614 # else:6615 # # pytest-rerunfailures caused a duplicate failure6616 # sb_config._results[test_id] = "Failed"6617 # else:6618 # if (6619 # test_id in sb_config._results.keys()6620 # and sb_config._results[test_id] == "Failed"6621 # ):6622 # # pytest-rerunfailures reran a test that failed6623 # sb_config._d_t_log_path[test_id] = os.path.join(6624 # log_dir, ft_id6625 # )6626 # sb_config.item_count_failed -= 16627 # sb_config.item_count_untested += 16628 # elif (6629 # test_id in sb_config._results.keys()6630 # and sb_config._results[test_id] == "Passed"6631 # ):6632 # # tearDown() was called more than once in the test6633 # sb_config.item_count_passed -= 16634 # sb_config.item_count_untested += 16635 # sb_config._results[test_id] = "Passed"6636 # sb_config.item_count_passed += 16637 # sb_config.item_count_untested -= 16638 # else:6639 # pass # Only initialize the Dashboard on the first processing6640 # num_passed = sb_config.item_count_passed6641 # num_failed = sb_config.item_count_failed6642 # num_skipped = sb_config.item_count_skipped6643 # num_untested = sb_config.item_count_untested6644 # self.create_pie_chart(title=constants.Dashboard.TITLE)6645 # self.add_data_point("Passed", num_passed, color="#84d474")6646 # self.add_data_point("Untested", num_untested, color="#eaeaea")6647 # self.add_data_point("Skipped", num_skipped, color="#efd8b4")6648 # self.add_data_point("Failed", num_failed, color="#f17476")6649 # style = (6650 # '<link rel="stylesheet" charset="utf-8" '6651 # 'href="%s">' % constants.Dashboard.STYLE_CSS6652 # )6653 # auto_refresh_html = ""6654 # if num_untested > 0:6655 # # Refresh every X seconds when waiting for more test results6656 # auto_refresh_html = constants.Dashboard.META_REFRESH_HTML6657 # else:6658 # # The tests are complete6659 # if sb_config._using_html_report:6660 # # Add the pie chart to the pytest html report6661 # sb_config._saved_dashboard_pie = self.extract_chart()6662 # if self._multithreaded:6663 # abs_path = os.path.abspath(".")6664 # dash_pie = json.dumps(sb_config._saved_dashboard_pie)6665 # dash_pie_loc = constants.Dashboard.DASH_PIE6666 # pie_path = os.path.join(abs_path, dash_pie_loc)6667 # pie_file = codecs.open(pie_path, "w+", encoding="utf-8")6668 # pie_file.writelines(dash_pie)6669 # pie_file.close()6670 # head = (6671 # '<head><meta charset="utf-8">'6672 # '<meta name="viewport" content="shrink-to-fit=no">'6673 # '<link rel="shortcut icon" href="%s">'6674 # "%s"6675 # "<title>Dashboard</title>"6676 # "%s</head>"6677 # % (constants.Dashboard.DASH_PIE_PNG_1, auto_refresh_html, style)6678 # )6679 # table_html = (6680 # "<div></div>"6681 # '<table border="1px solid #e6e6e6;" width="100%;" padding: 5px;'6682 # ' font-size="12px;" text-align="left;" id="results-table">'6683 # '<thead id="results-table-head">'6684 # '<tr style="background-color: #F7F7FD;">'6685 # '<th col="result">Result</th><th col="name">Test</th>'6686 # '<th col="duration">Duration</th><th col="links">Links</th>'6687 # "</tr></thead>"6688 # )6689 # the_failed = []6690 # the_skipped = []6691 # the_passed_hl = [] # Passed and has logs6692 # the_passed_nl = [] # Passed and no logs6693 # the_untested = []6694 # if dud2 in sb_config._results.keys():6695 # sb_config._results.pop(dud2)6696 # for key in sb_config._results.keys():6697 # t_res = sb_config._results[key]6698 # t_dur = sb_config._duration[key]6699 # t_d_id = sb_config._display_id[key]6700 # t_l_path = sb_config._d_t_log_path[key]6701 # res_low = t_res.lower()6702 # if sb_config._results[key] == "Failed":6703 # if not sb_config._d_t_log_path[key]:6704 # sb_config._d_t_log_path[key] = os.path.join(log_dir, ft_id)6705 # the_failed.append([res_low, t_res, t_d_id, t_dur, t_l_path])6706 # elif sb_config._results[key] == "Skipped":6707 # the_skipped.append([res_low, t_res, t_d_id, t_dur, t_l_path])6708 # elif sb_config._results[key] == "Passed" and t_l_path:6709 # the_passed_hl.append([res_low, t_res, t_d_id, t_dur, t_l_path])6710 # elif sb_config._results[key] == "Passed" and not t_l_path:6711 # the_passed_nl.append([res_low, t_res, t_d_id, t_dur, t_l_path])6712 # elif sb_config._results[key] == "Untested":6713 # the_untested.append([res_low, t_res, t_d_id, t_dur, t_l_path])6714 # for row in the_failed:6715 # row = (6716 # '<tbody class="%s results-table-row">'6717 # '<tr style="background-color: #FFF8F8;">'6718 # '<td class="col-result">%s</td><td>%s</td><td>%s</td>'6719 # '<td><a href="%s">Logs</a> / <a href="%s/">Data</a>'6720 # "</td></tr></tbody>"6721 # "" % (row[0], row[1], row[2], row[3], log_dir, row[4])6722 # )6723 # table_html += row6724 # for row in the_skipped:6725 # if not row[4]:6726 # row = (6727 # '<tbody class="%s results-table-row">'6728 # '<tr style="background-color: #FEFEF9;">'6729 # '<td class="col-result">%s</td><td>%s</td><td>%s</td>'6730 # "<td>-</td></tr></tbody>"6731 # % (row[0], row[1], row[2], row[3])6732 # )6733 # else:6734 # row = (6735 # '<tbody class="%s results-table-row">'6736 # '<tr style="background-color: #FEFEF9;">'6737 # '<td class="col-result">%s</td><td>%s</td><td>%s</td>'6738 # '<td><a href="%s">Logs</a> / <a href="%s/">Data</a>'6739 # "</td></tr></tbody>"6740 # "" % (row[0], row[1], row[2], row[3], log_dir, row[4])6741 # )6742 # table_html += row6743 # for row in the_passed_hl:6744 # # Passed and has logs6745 # row = (6746 # '<tbody class="%s results-table-row">'6747 # '<tr style="background-color: #F8FFF8;">'6748 # '<td class="col-result">%s</td><td>%s</td><td>%s</td>'6749 # '<td><a href="%s">Logs</a> / <a href="%s/">Data</a>'6750 # "</td></tr></tbody>"6751 # "" % (row[0], row[1], row[2], row[3], log_dir, row[4])6752 # )6753 # table_html += row6754 # for row in the_passed_nl:6755 # # Passed and no logs6756 # row = (6757 # '<tbody class="%s results-table-row">'6758 # '<tr style="background-color: #F8FFF8;">'6759 # '<td class="col-result">%s</td><td>%s</td><td>%s</td>'6760 # "<td>-</td></tr></tbody>" % (row[0], row[1], row[2], row[3])6761 # )6762 # table_html += row6763 # for row in the_untested:6764 # row = (6765 # '<tbody class="%s results-table-row"><tr>'6766 # '<td class="col-result">%s</td><td>%s</td><td>%s</td>'6767 # "<td>-</td></tr></tbody>" % (row[0], row[1], row[2], row[3])6768 # )6769 # table_html += row6770 # table_html += "</table>"6771 # add_more = "<br /><b>Last updated:</b> "6772 # timestamp, the_date, the_time = log_helper.get_master_time()6773 # last_updated = "%s at %s" % (the_date, the_time)6774 # add_more = add_more + "%s" % last_updated6775 # status = "<p></p><div><b>Status:</b> Awaiting results..."6776 # status += " (Refresh the page for updates)"6777 # if num_untested == 0:6778 # status = "<p></p><div><b>Status:</b> Test Run Complete:"6779 # if num_failed == 0:6780 # if num_passed > 0:6781 # if num_skipped == 0:6782 # status += " <b>Success!</b> (All tests passed)"6783 # else:6784 # status += " <b>Success!</b> (No failing tests)"6785 # else:6786 # status += " All tests were skipped!"6787 # else:6788 # latest_logs_dir = "latest_logs/"6789 # log_msg = "See latest logs for details"6790 # if num_failed == 1:6791 # status += (6792 # " <b>1 test failed!</b> --- "6793 # '(<b><a href="%s">%s</a></b>)'6794 # "" % (latest_logs_dir, log_msg)6795 # )6796 # else:6797 # status += (6798 # " <b>%s tests failed!</b> --- "6799 # '(<b><a href="%s">%s</a></b>)'6800 # "" % (num_failed, latest_logs_dir, log_msg)6801 # )6802 # status += "</div><p></p>"6803 # add_more = add_more + status6804 # gen_by = (6805 # '<p><div>Generated by: <b><a href="https://seleniumbase.io/">'6806 # "SeleniumBase</a></b></div></p><p></p>"6807 # )6808 # add_more = add_more + gen_by6809 # # Have dashboard auto-refresh on updates when using an http server6810 # refresh_line = (6811 # '<script type="text/javascript" src="%s">'6812 # "</script>" % constants.Dashboard.LIVE_JS6813 # )6814 # if num_untested == 0 and sb_config._using_html_report:6815 # sb_config._dash_final_summary = status6816 # add_more = add_more + refresh_line6817 # the_html = (6818 # '<html lang="en">'6819 # + head6820 # + self.extract_chart()6821 # + table_html6822 # + add_more6823 # )6824 # abs_path = os.path.abspath(".")6825 # file_path = os.path.join(abs_path, "dashboard.html")6826 # out_file = codecs.open(file_path, "w+", encoding="utf-8")6827 # out_file.writelines(the_html)6828 # out_file.close()6829 # sb_config._dash_html = the_html6830 # if self._multithreaded:6831 # d_stats = (num_passed, num_failed, num_skipped, num_untested)6832 # _results = sb_config._results6833 # _display_id = sb_config._display_id6834 # _rt = sb_config._duration # Run Time (RT)6835 # _tlp = sb_config._d_t_log_path # Test Log Path (TLP)6836 # dash_json = json.dumps((_results, _display_id, _rt, _tlp, d_stats))6837 # dash_json_loc = constants.Dashboard.DASH_JSON6838 # dash_jsonpath = os.path.join(abs_path, dash_json_loc)6839 # dash_json_file = codecs.open(dash_jsonpath, "w+", encoding="utf-8")6840 # dash_json_file.writelines(dash_json)6841 # dash_json_file.close()6842 def has_exception(self):6843 """(This method should ONLY be used in custom tearDown() methods.)6844 This method returns True if the test failed or raised an exception.6845 This is useful for performing additional steps in your tearDown()6846 method (based on whether or not the test passed or failed).6847 Example use cases:6848 * Performing cleanup steps if a test didn't complete.6849 * Sending test data and/or results to a dashboard service.6850 """6851 return self.__has_exception()6852 def save_teardown_screenshot(self):6853 """(Should ONLY be used at the start of custom tearDown() methods.)6854 This method takes a screenshot of the current web page for a6855 failing test (or when running your tests with --save-screenshot).6856 That way your tearDown() method can navigate away from the last6857 page where the test failed, and still get the correct screenshot6858 before performing tearDown() steps on other pages. If this method6859 is not included in your custom tearDown() method, a screenshot6860 will still be taken after the last step of your tearDown(), where6861 you should be calling "super(SubClassOfBaseCase, self).tearDown()"6862 """6863 try:6864 self.__check_scope__()6865 except Exception:6866 return6867 if self.__has_exception() or self.save_screenshot_after_test:6868 test_logpath = os.path.join(self.log_path, self.__get_test_id())6869 self.__create_log_path_as_needed(test_logpath)6870 self.__set_last_page_screenshot()6871 self.__set_last_page_url()6872 self.__set_last_page_source()6873 self.__add_pytest_html_extra()6874 def teardown(self):6875 """6876 Be careful if a subclass of BaseCase overrides setUp()6877 You'll need to add the following line to the subclass's tearDown():6878 super(SubClassOfBaseCase, self).tearDown()6879 """6880 if self._called_teardown:6881 logger.info("This test already called teardown()")6882 return6883 self._called_teardown = True6884 self._called_setup = False6885 try:6886 is_pytest = self.is_pytest # This fails if overriding setUp()6887 if is_pytest:6888 with_selenium = self.with_selenium6889 except Exception:6890 sub_class_name = (6891 str(self.__class__.__bases__[0]).split(".")[-1].split("'")[0]6892 )6893 sub_file_name = str(self.__class__.__bases__[0]).split(".")[-2]6894 sub_file_name = sub_file_name + ".py"6895 class_name = str(self.__class__).split(".")[-1].split("'")[0]6896 file_name = str(self.__class__).split(".")[-2] + ".py"6897 class_name_used = sub_class_name6898 file_name_used = sub_file_name6899 if sub_class_name == "BaseCase":6900 class_name_used = class_name6901 file_name_used = file_name6902 fix_setup = "super(%s, self).setUp()" % class_name_used6903 fix_teardown = "super(%s, self).tearDown()" % class_name_used6904 message = (6905 "You're overriding SeleniumBase's BaseCase setUp() "6906 "method with your own setUp() method, which breaks "6907 "SeleniumBase. You can fix this by going to your "6908 "%s class located in your %s file and adding the "6909 "following line of code AT THE BEGINNING of your "6910 "setUp() method:\n%s\n\nAlso make sure "6911 "you have added the following line of code AT THE "6912 "END of your tearDown() method:\n%s\n"6913 % (class_name_used, file_name_used, fix_setup, fix_teardown)6914 )6915 raise Exception(message)6916 # *** Start tearDown() officially ***6917 self._slow_mode_pause_if_active()6918 has_exception = self.__has_exception()6919 if self.__overrided_default_timeouts:6920 # Reset default timeouts in case there are more tests6921 # These were changed in set_default_timeout()6922 if sb_config._SMALL_TIMEOUT and sb_config._LARGE_TIMEOUT:6923 settings.SMALL_TIMEOUT = sb_config._SMALL_TIMEOUT6924 settings.LARGE_TIMEOUT = sb_config._LARGE_TIMEOUT6925 sb_config._is_timeout_changed = False6926 self.__overrided_default_timeouts = False6927 deferred_exception = None6928 if self.__deferred_assert_failures:6929 print(6930 "\nWhen using self.deferred_assert_*() methods in your tests, "6931 "remember to call self.process_deferred_asserts() afterwards. "6932 "Now calling in tearDown()...\nFailures Detected:"6933 )6934 if not has_exception:6935 try:6936 self.process_deferred_asserts()6937 except Exception as e:6938 deferred_exception = e6939 else:6940 self.process_deferred_asserts(print_only=True)6941 test_id = self.__get_test_id()6942 if with_selenium:6943 # Save a screenshot if logging is on when an exception occurs6944 if has_exception:6945 self.__add_pytest_html_extra()6946 sb_config._has_exception = True6947 if (6948 self.with_testing_base6949 and not has_exception6950 and self.save_screenshot_after_test6951 ):6952 test_logpath = os.path.join(self.log_path, test_id)6953 self.__create_log_path_as_needed(test_logpath)6954 if not self.__last_page_screenshot_png:6955 self.__set_last_page_screenshot()6956 self.__set_last_page_url()6957 self.__set_last_page_source()6958 log_helper.log_screenshot(6959 test_logpath,6960 self.driver,6961 self.__last_page_screenshot_png,6962 )6963 self.__add_pytest_html_extra()6964 if self.with_testing_base and has_exception:6965 test_logpath = os.path.join(self.log_path, test_id)6966 self.__create_log_path_as_needed(test_logpath)6967 if (6968 not self.with_screen_shots6969 and not self.with_basic_test_info6970 and not self.with_page_source6971 ):6972 # Log everything if nothing specified (if testing_base)6973 if not self.__last_page_screenshot_png:6974 self.__set_last_page_screenshot()6975 self.__set_last_page_url()6976 self.__set_last_page_source()6977 log_helper.log_screenshot(6978 test_logpath,6979 self.driver,6980 self.__last_page_screenshot_png,6981 )6982 log_helper.log_test_failure_data(6983 self,6984 test_logpath,6985 self.driver,6986 self.browser,6987 self.__last_page_url,6988 )6989 log_helper.log_page_source(6990 test_logpath, self.driver, self.__last_page_source6991 )6992 else:6993 if self.with_screen_shots:6994 if not self.__last_page_screenshot_png:6995 self.__set_last_page_screenshot()6996 self.__set_last_page_url()6997 self.__set_last_page_source()6998 log_helper.log_screenshot(6999 test_logpath,7000 self.driver,7001 self.__last_page_screenshot_png,7002 )7003 if self.with_basic_test_info:7004 log_helper.log_test_failure_data(7005 self,7006 test_logpath,7007 self.driver,7008 self.browser,7009 self.__last_page_url,7010 )7011 if self.with_page_source:7012 log_helper.log_page_source(7013 test_logpath,7014 self.driver,7015 self.__last_page_source,7016 )7017 if self.dashboard:7018 if self._multithreaded:7019 with self.dash_lock:7020 self.__process_dashboard(has_exception)7021 else:7022 self.__process_dashboard(has_exception)7023 # (Pytest) Finally close all open browser windows7024 self.__quit_all_drivers()7025 if self.config.getoption("headless") or self.config.getoption("xvfb"):7026 if self.headless_active:7027 try:7028 self.display.stop()7029 except AttributeError:7030 pass7031 except Exception:7032 pass7033 self.display = None7034 if self.config.getoption("with_db_reporting", False):7035 if has_exception:7036 self.__insert_test_result(constants.State.FAILED, True)7037 else:7038 test_id = self.__get_test_id_2()7039 if test_id in sb_config._results.keys() and (7040 sb_config._results[test_id] == "Skipped"7041 ):7042 self.__insert_test_result(7043 constants.State.SKIPPED, False7044 )7045 else:7046 self.__insert_test_result(7047 constants.State.PASSED, False7048 )7049 runtime = int(time.time() * 1000) - self.execution_start_time7050 self.testcase_manager.update_execution_data(7051 self.execution_guid, runtime7052 )7053 if self.config.getoption("with_s3_logging and has_exception", False):7054 """ If enabled, upload logs to S3 during test exceptions. """7055 import uuid7056 from seleniumbase.core.s3_manager import S3LoggingBucket7057 s3_bucket = S3LoggingBucket()7058 guid = str(uuid.uuid4().hex)7059 path = "%s/%s" % (self.log_path, test_id)7060 uploaded_files = []7061 for logfile in os.listdir(path):7062 logfile_name = "%s/%s/%s" % (7063 guid,7064 test_id,7065 logfile.split(path)[-1],7066 )7067 s3_bucket.upload_file(7068 logfile_name, "%s/%s" % (path, logfile)7069 )7070 uploaded_files.append(logfile_name)7071 s3_bucket.save_uploaded_file_names(uploaded_files)7072 index_file = s3_bucket.upload_index_file(test_id, guid)7073 print("\n\n*** Log files uploaded: ***\n%s\n" % index_file)7074 logging.info(7075 "\n\n*** Log files uploaded: ***\n%s\n" % index_file7076 )7077 if self.with_db_reporting:7078 from seleniumbase.core.testcase_manager import (7079 TestcaseDataPayload,7080 )7081 from seleniumbase.core.testcase_manager import (7082 TestcaseManager,7083 )7084 self.testcase_manager = TestcaseManager(self.database_env)7085 data_payload = TestcaseDataPayload()7086 data_payload.guid = self.testcase_guid7087 data_payload.logURL = index_file7088 self.testcase_manager.update_testcase_log_url(data_payload)7089 # Resume tearDown() for both Pytest and Nosetests7090 if has_exception and self.__visual_baseline_copies:7091 self.__process_visual_baseline_logs()7092 if deferred_exception:7093 # User forgot to call "self.process_deferred_asserts()" in test...
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!