From 9f31717ab8be3ea0fd14711eb59326cd6498152e Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Sun, 20 Nov 2022 20:03:03 +0530 Subject: [PATCH 1/3] add: use javascript executor to mark the session instead of API call remove multiple api calls, use driver to execute js to mark status for better annotation on browserstack dashboard --- src/pytest_selenium/drivers/browserstack.py | 56 +++++++++++++++------ 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/pytest_selenium/drivers/browserstack.py b/src/pytest_selenium/drivers/browserstack.py index 123e017..42a7ec8 100644 --- a/src/pytest_selenium/drivers/browserstack.py +++ b/src/pytest_selenium/drivers/browserstack.py @@ -50,11 +50,22 @@ def job_access(self): @pytest.mark.optionalhook def pytest_selenium_runtest_makereport(item, report, summary, extra): + if report.when in ["setup", "teardown"]: + return provider = BrowserStack() if not provider.uses_driver(item.config.getoption("driver")): return passed = report.passed or (report.failed and hasattr(report, "wasxfail")) + # set test failure reason if available + fail_reason = "" + if not passed: + try: + fail_reason = report.longrepr.reprcrash.message + except Exception as e: + summary.append( + "WARNING: Failed to determine {0} job URL: {1}".format(provider.name, e) + ) session_id = item._driver.session_id api_url = provider.API.format(session=session_id) @@ -75,22 +86,39 @@ def pytest_selenium_runtest_makereport(item, report, summary, extra): ) try: - # Update the job result + # Update the session status job_status = job_info["automation_session"]["status"] - status = "running" if passed else "error" - if report.when == "teardown" and passed: - status = "completed" - if job_status not in ("error", status): - # Only update the result if it's not already marked as failed - requests.put( - api_url, - headers={"Content-Type": "application/json"}, - params={"status": status}, - auth=provider.auth, - timeout=10, - ) + if job_status not in ("failed", "passed"): + # Only update the status if it's not already marked (by user via script) + if passed: + item._driver.execute_script( + 'browserstack_executor: { \ + "action": "setSessionStatus", \ + "arguments": { "status":"passed" } \ + }' + ) + else: + if fail_reason: + item._driver.execute_script( + 'browserstack_executor: {{ \ + "action": "setSessionStatus", \ + "arguments": {{ \ + "status":"failed", \ + "reason": "{}" \ + }} \ + }}'.format( + fail_reason + ) + ) + else: + item._driver.execute_script( + 'browserstack_executor: { \ + "action": "setSessionStatus", \ + "arguments": { "status":"failed" } \ + }' + ) except Exception as e: - summary.append("WARNING: Failed to update job status: {0}".format(e)) + summary.append("WARNING: Failed to update session status: {0}".format(e)) def driver_kwargs(request, test, capabilities, **kwargs): From 583a4d3720a348093cccb64c568b653444e2405c Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Sun, 20 Nov 2022 20:05:16 +0530 Subject: [PATCH 2/3] fix: add fallback check for username and key ignore username/key from config file if those are default values and pick from env variables if available --- src/pytest_selenium/drivers/browserstack.py | 24 +++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/pytest_selenium/drivers/browserstack.py b/src/pytest_selenium/drivers/browserstack.py index 42a7ec8..a5238d3 100644 --- a/src/pytest_selenium/drivers/browserstack.py +++ b/src/pytest_selenium/drivers/browserstack.py @@ -22,15 +22,35 @@ def executor(self): @property def username(self): - return self.get_credential( + user = self.get_credential( "username", ["BROWSERSTACK_USERNAME", "BROWSERSTACK_USR"] ) + if user in [ + "BROWSERSTACK_USERNAME", + "YOUR_USERNAME", + "BROWSERSTACK_USR", + ]: + return self.get_credential( + "", ["BROWSERSTACK_USERNAME", "BROWSERSTACK_USR"] + ) + else: + return user @property def key(self): - return self.get_credential( + access_key = self.get_credential( "key", ["BROWSERSTACK_ACCESS_KEY", "BROWSERSTACK_PSW"] ) + if access_key in [ + "BROWSERSTACK_ACCESS_KEY", + "YOUR_ACCESS_KEY", + "BROWSERSTACK_PSW", + ]: + return self.get_credential( + "", ["BROWSERSTACK_ACCESS_KEY", "BROWSERSTACK_PSW"] + ) + else: + return access_key @property def job_access(self): From 943551dd81c43aedd0dde7ee6e88f355772daac9 Mon Sep 17 00:00:00 2001 From: Ankit Singh <2501.ankit@gmail.com> Date: Mon, 2 Jan 2023 16:56:26 +0530 Subject: [PATCH 3/3] add: annotate on fail for long error messages, escape special characters --- src/pytest_selenium/drivers/browserstack.py | 31 +++++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/pytest_selenium/drivers/browserstack.py b/src/pytest_selenium/drivers/browserstack.py index a5238d3..55c98fe 100644 --- a/src/pytest_selenium/drivers/browserstack.py +++ b/src/pytest_selenium/drivers/browserstack.py @@ -81,7 +81,7 @@ def pytest_selenium_runtest_makereport(item, report, summary, extra): fail_reason = "" if not passed: try: - fail_reason = report.longrepr.reprcrash.message + fail_reason = report.longrepr.reprcrash except Exception as e: summary.append( "WARNING: Failed to determine {0} job URL: {1}".format(provider.name, e) @@ -118,17 +118,30 @@ def pytest_selenium_runtest_makereport(item, report, summary, extra): }' ) else: + import json + if fail_reason: item._driver.execute_script( - 'browserstack_executor: {{ \ + 'browserstack_executor: {\ + "action": "annotate", \ + "arguments": {\ + "level": "error", \ + "data": ' + + json.dumps(str(fail_reason)) + + "\ + }\ + }" + ) + item._driver.execute_script( + 'browserstack_executor: {\ "action": "setSessionStatus", \ - "arguments": {{ \ - "status":"failed", \ - "reason": "{}" \ - }} \ - }}'.format( - fail_reason - ) + "arguments": {\ + "status": "failed", \ + "reason": ' + + json.dumps(str(fail_reason)) + + "\ + }\ + }" ) else: item._driver.execute_script(