{"id":3657,"date":"2016-02-12T01:05:37","date_gmt":"2016-02-12T06:05:37","guid":{"rendered":"http:\/\/qxf2.com\/blog\/?p=3657"},"modified":"2019-06-17T04:11:52","modified_gmt":"2019-06-17T08:11:52","slug":"browserstack-screenshots-video","status":"publish","type":"post","link":"https:\/\/qxf2.com\/blog\/browserstack-screenshots-video\/","title":{"rendered":"Obtaining BrowserStack screenshots and video links"},"content":{"rendered":"<p>At <a href=\"http:\/\/www.qxf2.com\/?utm_source=browserstack-screenshot&amp;utm_medium=click&amp;utm_campaign=From%20blog\">Qxf2<\/a> we use <a href=\"http:\/\/browserstack.com\/\">BrowserStack<\/a> to run our automated checks against different browsers. BrowserStack is a cloud-based, cross-browser, testing tool that takes away the pain of maintaining a physical lab and saves us a lot of time.<\/p>\n<hr \/>\n<h3>Why this post?<\/h3>\n<p>BrowserStack is a great tool to <u>execute<\/u> your automated tests. However BrowserStack is not a good tool to <u>report<\/u> the test results. At our clients, we usually execute our tests on BrowserStack and then report our automated test results to a test case management tool like <a href=\"http:\/\/www.gurock.com\/testrail\/\">TestRail<\/a> or rely on the CI server as the test reporting tool. BrowserStack has useful debug artifacts that we want to share with whatever test reporting system that we integrate with. We especially find the BrowserStack screenshot URLs and the BrowserStack URL for the video replay to be helpful in investigating and reporting failures. We created a BrowserStack library using Python and BrowserStack&#8217;s API to help us share these BrowserStack artifacts across our automation framework. We wanted to share it with the testing community.<\/p>\n<hr \/>\n<h3>Overview<\/h3>\n<p>We will run a test similar to what we ran in our <a href=\"https:\/\/qxf2.com\/blog\/browserstack-part1\/\">previous post<\/a> on BrowserStack. If you need more details on BrowserStack set up and access key please refer to the previous post. We will take one screenshot during the test. BrowserStack saves the screenshot and stores a video recording of the test. We will write a library that will help us access these artifacts and print them out in our test&#8217;s log.<\/p>\n<p><strong>Note 1:<\/strong> BrowserStack screenshots are available on Amazon&#8217;s S3 for 90 days.<br \/>\n<strong>Note 2:<\/strong> BrowserStack&#8217;s video recording of your test run is available for 30 days.<br \/>\nSo if you are working in a highly risk conscious industry with the chance of being audited, you may want to store these artifacts somewhere else.<\/p>\n<hr \/>\n<h3>A step by step guide<\/h3>\n<p>Here are the six steps we will perform:<br \/>\n1. Authenticate<br \/>\n2. Get replay url<br \/>\n3. Get latest screenshot<br \/>\n4. Putting it all together<br \/>\n5. Example usage<br \/>\n6. Running the test<\/p>\n<p>You can use REST API to access information about your tests like build, sessions, session logs etc. We will use Python <a href=\"https:\/\/pypi.python.org\/pypi\/requests\">requests<\/a> module to achieve this.<\/p>\n<p><strong>Step1. Authenticate<\/strong><br \/>\nUse your BrowserStack username and access key for authentication<\/p>\n<pre lang=\"python\">self.auth = ('USERNAME','ACCESSKEY')\r\n<\/pre>\n<p><strong>Step2. Get replay url<\/strong><br \/>\nThe replay URL is of form: \/builds\/$build_id\/session\/$session_id\/. To get the replay URL you need the build id and session id. Let us write methods to get the build id as well as the active session id.<\/p>\n<pre lang=\"python\">    def get_build_id(self):\r\n        \"Get the build ID\"\r\n        self.build_url = self.browserstack_url + \"builds.json\"\r\n        builds = requests.get(self.build_url, auth=self.auth).json()\r\n        build_id =  builds[0]['automation_build']['hashed_id']\r\n        \r\n        return build_id\r\n\r\n\r\n    def get_sessions(self):\r\n        \"Get a JSON object with all the sessions\"\r\n        build_id = self.get_build_id()\r\n        sessions= requests.get(self.browserstack_url + 'builds\/%s\/sessions.json'%build_id, auth=self.auth).json()\r\n\r\n        return sessions\r\n\r\n\r\n    def get_active_session_id(self):\r\n        \"Return the session ID of the first active session\"\r\n        session_id = None\r\n        sessions = self.get_sessions()\r\n        for session in sessions:\r\n            #Get session id of the first session with status = running \r\n            if session['automation_session']['status']=='running':\r\n                session_id = session['automation_session']['hashed_id']\r\n                break\r\n                \r\n        return session_id\r\n\r\n\r\n     def get_session_url(self,session_id):\r\n        \"Get the video URL from build and session details. Needs to be called after session is completed\"\r\n        build_id = self.get_build_id()\r\n        self.build_session_url = self.browserstack_url + \"builds\/\"+build_id+\"\/sessions\/\"+session_id\r\n        build_session_details = requests.get(self.build_session_url, auth=self.auth).json()\r\n\r\n        #Get the video url from session details\r\n        video_url = build_session_details[u'automation_session'][u'video_url']\r\n        session_url= video_url.encode(\"utf-8\")\r\n        \r\n        return session_url\r\n<\/pre>\n<p><strong>Step3. Get latest screenshot<\/strong><br \/>\nThis step is a bit of a hack. There is no direct API call to get a list of screenshots saved by BrowserStack. There is no way to explicitly name the image BrowserStack saves on Amazon&#8217;s S3. To get past this, we end up parsing the session logs and find the latest screenshot. Then in our test, we can explicitly name and store the screenshot URL.<\/p>\n<pre lang=\"python\">    def get_session_logs(self):\r\n        \"Return the session log in text format\"\r\n        build_id = self.get_build_id()\r\n        session_id = self.get_active_session_id()\r\n        session_log = requests.get(self.browserstack_url + 'builds\/%s\/sessions\/%s\/logs'%(build_id,session_id),auth=self.auth).text\r\n\r\n        return session_log\r\n\r\n\r\n    def get_latest_screenshot_url(self):\r\n        \"Get the URL of the latest screenshot\"\r\n        session_log = self.get_session_logs()\r\n        \r\n        #Process the text to locate the URL of the last screenshot\r\n        screenshot_request = session_log.split('screenshot {}')[-1]\r\n        response_result = screenshot_request.split('REQUEST')[0]\r\n        image_url = response_result.split('https:\/\/')[-1]\r\n        image_url = image_url.split('.png')[0]\r\n        screenshot_url = 'https:\/\/' + image_url + '.png'\r\n        \r\n        return screenshot_url\r\n<\/pre>\n<p><strong>Step4. Putting it all together Library<\/strong><br \/>\nThis is how our library looks.<\/p>\n<pre lang=\"python\">\"\"\"\r\nQxf2 BrowserStack library to interact with BrowserStack's artifacts.\r\nFor now, this is useful for:\r\na) Obtaining the session URL\r\nb) Obtaining URLs of screenshots\r\n\"\"\"\r\n\r\nimport os,requests\r\n\r\n\r\nclass BrowserStack_Library():\r\n    \"BrowserStack library to interact with BrowserStack artifacts\"\r\n    def __init__(self,credentials_file=None):\r\n        \"Constructor for the BrowserStack library\"\r\n        self.browserstack_url = \"https:\/\/www.browserstack.com\/automate\/\"\r\n        self.auth = ('USERNAME','ACCESS_KEY')\r\n\r\n\r\n    def get_build_id(self):\r\n        \"Get the build ID\"\r\n        self.build_url = self.browserstack_url + \"builds.json\"\r\n        builds = requests.get(self.build_url, auth=self.auth).json()\r\n        build_id =  builds[0]['automation_build']['hashed_id']\r\n        \r\n        return build_id\r\n\r\n\r\n    def get_sessions(self):\r\n        \"Get a JSON object with all the sessions\"\r\n        build_id = self.get_build_id()\r\n        sessions= requests.get(self.browserstack_url + 'builds\/%s\/sessions.json'%build_id, auth=self.auth).json()\r\n\r\n        return sessions\r\n\r\n\r\n    def get_active_session_id(self):\r\n        \"Return the session ID of the first active session\"\r\n        session_id = None\r\n        sessions = self.get_sessions()\r\n        for session in sessions:\r\n            #Get session id of the first session with status = running \r\n            if session['automation_session']['status']=='running':\r\n                session_id = session['automation_session']['hashed_id']\r\n                break\r\n                \r\n        return session_id\r\n\r\n\r\n     def get_session_url(self,session_id):\r\n        \"Get the video URL from build and session details. Needs to be called after session is completed\"\r\n        build_id = self.get_build_id()\r\n        self.build_session_url = self.browserstack_url + \"builds\/\"+build_id+\"\/sessions\/\"+session_id\r\n        build_session_details = requests.get(self.build_session_url, auth=self.auth).json()\r\n\r\n        #Get the video url from session details\r\n        video_url = build_session_details[u'automation_session'][u'video_url']\r\n        session_url= video_url.encode(\"utf-8\")\r\n        \r\n        return session_url\r\n\r\n\r\n    def get_session_logs(self):\r\n        \"Return the session log in text format\"\r\n        build_id = self.get_build_id()\r\n        session_id = self.get_active_session_id()\r\n        session_log = requests.get(self.browserstack_url + 'builds\/%s\/sessions\/%s\/logs'%(build_id,session_id),auth=self.auth).text\r\n\r\n        return session_log\r\n\r\n\r\n    def get_latest_screenshot_url(self):\r\n        \"Get the URL of the latest screenshot\"\r\n        session_log = self.get_session_logs()\r\n        \r\n        #Process the text to locate the URL of the last screenshot\r\n        \r\n        screenshot_request = session_log.split('screenshot {}')[-1]\r\n        response_result = screenshot_request.split('REQUEST')[0]\r\n        image_url = response_result.split('https:\/\/')[-1]\r\n        image_url = image_url.split('.png')[0]\r\n        screenshot_url = 'https:\/\/' + image_url + '.png'\r\n        \r\n        return screenshot_url\r\n<\/pre>\n<p><strong>Step5. Example usage<\/strong><br \/>\nWe are going to execute a Selenium test that visits http:\/\/www.chess.com\/, asserts the title page and then clicks on the Sign up button and then take a screenshot. We will get and print the BrowserStack video replay URL and screenshot URL using the BrowserStack Library we just created.<\/p>\n<pre lang=\"python\">import unittest, time\r\nfrom selenium import webdriver\r\nfrom selenium.webdriver.common.desired_capabilities import DesiredCapabilities\r\nfrom BrowserStack_Library import BrowserStack_Library\r\n\r\nclass SeleniumOnBrowserStack(unittest.TestCase):\r\n    \"Example class written to run Selenium tests on BrowserStack\"\r\n    def setUp(self):\r\n        desired_cap = {'platform': 'Windows', 'browserName': 'Firefox', 'browser_version':'65', 'browserstack.debug': 'true' }\r\n        self.driver = webdriver.Remote(command_executor='http:\/\/USERNAME:ACCESS_KEY@hub.browserstack.com:80\/wd\/hub',desired_capabilities=desired_cap)\r\n        self.browserstack_obj = BrowserStack_Library()\r\n        \r\n        \r\n     def test_chess(self):\r\n        \"An example test: Visit chess.com and click on sign up link\"        \r\n        # Go to the URL \r\n        self.driver.get(\"http:\/\/www.chess.com\")\r\n        # Assert that the Home Page has title \"Chess.com - Play Chess Online - Free Games\"\r\n        self.assertIn(\"Chess.com - Play Chess Online - Free Games\", self.driver.title)\r\n        # Identify the xpath for Play Now button which will take you to the sign up page\r\n        elem = self.driver.find_element_by_xpath(\"\/\/a[@title='Play Now']\")\r\n        elem.click()\r\n        self.driver.save_screenshot(\"test_chess.png\")\r\n        # Print the title\r\n        print self.driver.title\r\n        # Get the Browserstack Screenshot url\r\n        print \"BrowserStack Screenshot url :\",self.browserstack_obj.get_latest_screenshot_url()\r\n        # Get the Browserstack active session id\r\n        active_session_id = self.browserstack_obj.get_active_session_id()\r\n        print \"BrowserStack session id :\",active_session_id\r\n        # Complete the session\r\n        self.driver.quit()\r\n        # Get the Browserstack Session\/Video url\r\n        print \"BrowserStack Session url :\",self.browserstack_obj.get_session_url(active_session_id)\r\n \r\nif __name__ == '__main__':\r\n    unittest.main()\r\n<\/pre>\n<p><strong>Step6. Run the Test<\/strong><br \/>\nYou can run the test script the normal way you do. We run it via the command prompt.<\/p>\n<p><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2016\/02\/Running_Browserstack_Test.jpg\" data-rel=\"lightbox-image-0\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2016\/02\/Running_Browserstack_Test.jpg\" alt=\"Running Browserstack Test\" width=\"962\" height=\"220\" class=\"aligncenter size-full wp-image-9363\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2016\/02\/Running_Browserstack_Test.jpg 962w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2016\/02\/Running_Browserstack_Test-300x69.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2016\/02\/Running_Browserstack_Test-768x176.jpg 768w\" sizes=\"auto, (max-width: 962px) 100vw, 962px\" \/><\/a><\/p>\n<hr \/>\n<p>We&#8217;ll leave you with a screenshot of how we end up hyperlinking these artifacts when reporting to TestRail.<br \/>\n<a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2016\/02\/example_report_to_testrail.png\" data-rel=\"lightbox-image-1\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-3669\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2016\/02\/example_report_to_testrail.png\" alt=\"Example BrowserStack report to TestRail\" width=\"692\" height=\"315\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2016\/02\/example_report_to_testrail.png 692w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2016\/02\/example_report_to_testrail-300x137.png 300w\" sizes=\"auto, (max-width: 692px) 100vw, 692px\" \/><\/a><br \/>\nWe plan to write a post (within the next 6 months) showing you how to take screenshots automatically, numbers them intelligently, integrate screenshot naming with BrowserStack and then hyperlinking the artifacts to TestRail. If you are in a hurry to get this information, please post a comment below and we will directly share code with you.<\/p>\n<hr \/>\n","protected":false},"excerpt":{"rendered":"<p>At Qxf2 we use BrowserStack to run our automated checks against different browsers. BrowserStack is a cloud-based, cross-browser, testing tool that takes away the pain of maintaining a physical lab and saves us a lot of time. Why this post? BrowserStack is a great tool to execute your automated tests. However BrowserStack is not a good tool to report the [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[38,40,18],"tags":[],"class_list":["post-3657","post","type-post","status-publish","format-standard","hentry","category-automation","category-browserstack","category-python"],"_links":{"self":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/3657","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/comments?post=3657"}],"version-history":[{"count":34,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/3657\/revisions"}],"predecessor-version":[{"id":11026,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/3657\/revisions\/11026"}],"wp:attachment":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/media?parent=3657"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/categories?post=3657"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/tags?post=3657"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}