{"id":2623,"date":"2015-03-24T12:30:41","date_gmt":"2015-03-24T16:30:41","guid":{"rendered":"http:\/\/qxf2.com\/blog\/?p=2623"},"modified":"2018-03-15T10:38:09","modified_gmt":"2018-03-15T14:38:09","slug":"python-appium-scroll-through-search-result-table","status":"publish","type":"post","link":"https:\/\/qxf2.com\/blog\/python-appium-scroll-through-search-result-table\/","title":{"rendered":"Python and Appium: Scroll through search result table"},"content":{"rendered":"<p>In this post we show you how to scroll through a table and identify elements. We also tackle the case where the table spans multiple pages. This is a natural and common workflow on most mobile devices. A typical use case involves the user performing a search within a mobile application. The app returns a table of results. The user then scrolls through the results and clicks on the result she likes. <\/p>\n<hr>\n<h3>Why this post?<\/h3>\n<p>Qxf2\u2019s <a href=\"http:\/\/www.qxf2.com\/goals?utm_source=blog&#038;utm_medium=click&#038;utm_campaign=From%20blog\">top goal<\/a> is to help testers. We identified mobile automation as one area where we could help testers. Over the past few months, we have written several basic tutorials that help testers with mobile automation. We feel like the next stage is to address some common intermediate level problems you may face as you implement mobile automation at your workplace. This post outlines a technical challenge testers are likely to face and our approach to solve it.<\/p>\n<hr>\n<h3>New to mobile automation?<\/h3>\n<p>If you are new to mobile automation, we suggest the following posts to get started:<br \/>\na) <a href=\"https:\/\/qxf2.com\/blog\/appium-mobile-automation\/\">Run Appium on an emulator<\/a><br \/>\nb) <a href=\"https:\/\/qxf2.com\/blog\/appium-tutorial-python-physical-device\/\">Run Appium on mobile devices<\/a><br \/>\nc) <a href=\"https:\/\/qxf2.com\/blog\/identify-ui-elements-mobile-apps\/\">Identify UI elements on mobile platforms<\/a><br \/>\nd) <a href=\"https:\/\/qxf2.com\/blog\/automating-pinch-zoom-swipe-appium\/\">Execute different mobile gestures<\/a><\/p>\n<hr>\n<h3>Overview<\/h3>\n<p>We are going to be using <a href=\"http:\/\/bigbasket.com\/\">BigBasket<\/a> as the example mobile application. We will login, search for a product, scroll through all the search results and print out the products we encounter in the search results. In previous posts, we have shown you how to <a href=\"https:\/\/qxf2.com\/blog\/running-mobile-automation-on-multiple-devices\/\">login to BigBasket<\/a> and <a href=\"https:\/\/qxf2.com\/blog\/android-appium-press-enter-soft-keyboard\/\">perform a search<\/a>. Here is a short video (with no audio) showing you what actions our automated script intends to perform. We used the excellent <a href=\"http:\/\/recordable.mobi\/\">Recordable<\/a> app to create this video.<br \/>\n<div style=\"width: 320px;\" class=\"wp-video\"><!--[if lt IE 9]><script>document.createElement('video');<\/script><![endif]-->\n<video class=\"wp-video-shortcode\" id=\"video-2623-1\" width=\"320\" height=\"480\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2015\/03\/10-03-2015-12-071.mp4?_=1\" \/><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2015\/03\/10-03-2015-12-071.mp4\">https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2015\/03\/10-03-2015-12-071.mp4<\/a><\/video><\/div><\/p>\n<p>We are searching for &#8216;<em>Real juice<\/em>&#8216; using the BigBasket application. Once we click on the search icon we note that 20 items out of the total 24 items are displayed on page 1. We need a way to iterate through these first 20 search results, print out each one of them and then click on the &#8216;Next&#8217; button to view the remaining 4 items. We are choosing to print out the title of every product we notice while scrolling. This is to show you how to pick a specific element\/column from a table!<\/p>\n<hr>\n<h3>Algorithm for scrolling through tables<\/h3>\n<p>The logic for collecting each search result deserves some explanation. Search results are usually multi-page, which means it is not enough to just collect the results on the first page. We need to be able to paginate intelligently too. Further, given that the scroll size is arbitrary, we have to ensure we collect each search result exactly once and miss no search result. If your table does not paginate, simply skip the pagination portion of the logic.<\/p>\n<figure id=\"attachment_2728\" aria-describedby=\"caption-attachment-2728\" style=\"width: 722px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2015\/03\/scroll_logic_1.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\/2015\/03\/scroll_logic_1.jpg\" alt=\"Flowchart for how to scroll through tables.\" width=\"722\" height=\"877\" class=\"size-full wp-image-2728\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2015\/03\/scroll_logic_1.jpg 722w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2015\/03\/scroll_logic_1-247x300.jpg 247w\" sizes=\"auto, (max-width: 722px) 100vw, 722px\" \/><\/a><figcaption id=\"caption-attachment-2728\" class=\"wp-caption-text\">Flowchart for how to scroll through tables using Appium<\/figcaption><\/figure>\n<hr>\n<h3>Code to implement scrolling through tables<\/h3>\n<p>Here is the code snippet for implementing the flow chart above. We use a counter to keep track of the number of elements traversed. The counter is helpful while debugging. <\/p>\n<pre lang='python'>\r\ndef get_search_result(self,results_count=0):\r\n        \"Get and scroll through the search results of the query\"\r\n        elm = self.get_element(self.first_element_tablerow)\r\n        elm_prod_desc = elm.find_element_by_id(self.search_result_text)\r\n        new_prod_desc = self.get_dom_text(elm_prod_desc)\r\n        #Case when we cant scroll further,we get all elements present on the screen\r\n        if (new_prod_desc == self.current_prod_desc):\r\n            results = self.get_elements(self.search_result_text)\r\n            for result in results:\r\n                search_result = self.get_dom_text(result)\r\n                #To avoid counting the top element twice\r\n                if (search_result != new_prod_desc):\r\n                    results_count = results_count + 1\r\n                    self.write(\"Item number %d is %s\"%(results_count,search_result))\r\n            #Click 'Next' button if present\r\n            if (self.check_element_present(self.search_next_button)):\r\n                self.click_element(self.search_next_button)\r\n                self.wait(3)\r\n                self.get_search_result(results_count)\r\n                self.wait(3)\r\n            else:\r\n                self.write (\"Reached end of search results\")\r\n        else:            \r\n            self.current_prod_desc = new_prod_desc\r\n\r\n            #######################\r\n            #TO BE FILLED IN THE SWIPE, MOVE_TO SECTIONS OF THIS TUTORIAL\r\n            #option 1 : swipe, option 2:move_to\r\n            #######################\r\n\r\n            results_count = results_count + 1\r\n            self.write(\"Item number %d is %s\"%(results_count,self.current_prod_desc))\r\n            self.get_search_result(results_count)\r\n<\/pre>\n<hr>\n<h3>Approaches<\/h3>\n<p>While all this seems handy and neat, a big problem happens to be how much to scroll. If you scroll too much, you run the risk of missing some search results because they were never displayed. If you scroll too little, this GUI test could take forever! So we experimented with two approaches:<br \/>\n1. Swipe<br \/>\n2. move_to<br \/>\nIt turned out that we have more control with move_to. Use our <a href=\"https:\/\/qxf2.com\/blog\/running-mobile-automation-on-multiple-devices\/\">previous tutorial<\/a> to move out the amount you want move_to to move into a configuration file. For the completeness of this post, we will show you both methods.<\/p>\n<p><strong>1.Using swipe:<\/strong><br \/>\nInitially we went ahead implementing the scroll functionality using the swipe method (bottom-up), but we realized the results were not consistent and few elements were getting missed while iterating over the search results.<\/p>\n<pre lang='python'>\r\n#swipe from the bottom of the screen to the top of the screen\r\nself.driver.swipe(470, 800, 470, 50, 400) \r\n#swipe the top element \r\nself.driver.swipe(470, 420, 470,180, 400) \r\n#swipe the last element swipe  \r\nself.driver.swipe(470, 800, 470, 600, 400) \r\n# swipe from middle of top element to the top of screen\r\nself.driver.swipe(470, 280, 470, 80, 400) \r\n<\/pre>\n<p>While our first instinct was to use swipe, Appium can only &#8216;see&#8217; elements visible on the screen. So if you swipe hard, you run the risk of overshooting and having some elements not display at all.<\/p>\n<p><strong>2.Using move_to:<\/strong><br \/>\nNext we tried the move_to method where we identify the element using the tablerow xpath and move it to the top of the screen so that the next item is the first item on the screen. We called it recursively till all the elements are traversed successfully. This approach gave us accurate results.<\/p>\n<pre lang='python'>\r\nfrom appium.webdriver.common.touch_action import TouchAction\r\n\r\naction = TouchAction(self.driver) \r\naction.press(elm).perform()\r\naction.move_to(x=0, y=50).perform() #We highly recommend moving the '50' into a conf file\r\nself.wait(2)            \r\n<\/pre>\n<p>Here is the final test script for test run<\/p>\n<pre lang='python'>\r\n\"\"\"\r\nQxf2 test case to Search for a item in BigBasket app. This class imports BigBasket_Base_appium \r\n\"\"\"\r\n\r\nfrom BigBasket_Base_Appium import BigBasket_Base_Appium\r\nfrom optparse import OptionParser\r\nfrom ConfigParser import SafeConfigParser\r\n\r\ndef run_search_test(config_file,test_run):\r\n    \" Perform search for the value supplied and list the search results\"\r\n    #Create a test obj with parameters\r\n    test_obj = BigBasket_Base_Appium(config_file=config_file,test_run=test_run)\r\n    test_obj.wait(10)\r\n    test_obj.click_search_home()\r\n    test_obj.perform_search(\"real juice\")\r\n    test_obj.get_search_result()\r\n    test_obj.wait(5)\r\n    \r\n    \" TearDown Test\"\r\n    test_obj.tearDown()\r\n    \r\n     \r\n#---START OF SCRIPT\r\nif __name__=='__main__':\r\n    #Accept command line options from the user\r\n    #Python module optparse \r\n    usage = \"usage: %prog -c  -t  \\nE.g.1: %prog -c D:\\\\Git_Qxf2\\\\qxf2\\\\samples\\\\clients\\\\BigBasket\\\\configuration.ini -t \\\"Android_4.2\\\"\\n---\"\r\n    parser = OptionParser(usage=usage)\r\n    parser.add_option(\"-c\",\"--config\",dest=\"config_file\",help=\"The full path of the configuration file\")\r\n    parser.add_option(\"-t\",\"--test_run\",dest=\"test_run\",help=\"The name of the test run\")\r\n    (options,args) = parser.parse_args()\r\n\r\n    run_search_test(config_file=options.config_file, test_run=options.test_run)\r\n<\/pre>\n<p>Now you can run the test with the command line parameters mentioned <a href=\"https:\/\/qxf2.com\/blog\/running-mobile-automation-on-multiple-devices\/\">here<\/a>. <\/p>\n<p><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2015\/03\/run_scroll_test.gif\" data-rel=\"lightbox-image-1\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2015\/03\/run_scroll_test-300x162.gif\" alt=\"run appium scroll table\" width=\"300\" height=\"162\" class=\"alignnone size-medium wp-image-2753\" \/><\/a><\/p>\n<p>And finally, here is a recording of the entire automated test.<br \/>\n<div style=\"width: 320px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-2623-2\" width=\"320\" height=\"480\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2015\/03\/10-03-2015-11-561.mp4?_=2\" \/><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2015\/03\/10-03-2015-11-561.mp4\">https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2015\/03\/10-03-2015-11-561.mp4<\/a><\/video><\/div><\/p>\n<hr>\n<p>Phew! We needed video, diagrams, code snippets and a whole lot of text to write up this tutorial. We also had to leave out a lot of code and share only the snippets. We plan on sharing our code on GitHub sometime this year. Until then, if you need help with some of the missing code, feel free to ask us below. <\/p>\n<p><strong>Note: <\/strong>We think you would be interested in our open-sourced <a href=\"https:\/\/github.com\/qxf2\/qxf2-page-object-model\">Appium + Python test automation framework<\/a> based on the page object pattern.<\/p>\n<p>P.S.: Yes. We are taking baby steps to publishing these tutorials on Youtube.<\/p>\n<hr>\n<script>(function() {\n\twindow.mc4wp = window.mc4wp || {\n\t\tlisteners: [],\n\t\tforms: {\n\t\t\ton: function(evt, cb) {\n\t\t\t\twindow.mc4wp.listeners.push(\n\t\t\t\t\t{\n\t\t\t\t\t\tevent   : evt,\n\t\t\t\t\t\tcallback: cb\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n})();\n<\/script><!-- Mailchimp for WordPress v4.10.1 - https:\/\/wordpress.org\/plugins\/mailchimp-for-wp\/ --><form id=\"mc4wp-form-1\" class=\"mc4wp-form mc4wp-form-6165 mc4wp-form-theme mc4wp-form-theme-blue\" method=\"post\" data-id=\"6165\" data-name=\"Newsletter\" ><div class=\"mc4wp-form-fields\"><div style=\"border:3px; border-style:dashed;border-color:#56d1e1;padding:1.2em;\">\r\n  <h1 style=\"text-align: center; padding-top: 20px; padding-bottom: 20px; color: #592b1b;\">Subscribe to our weekly Newsletter<\/h1>\r\n  <input style=\"margin: auto;\" type=\"email\" name=\"EMAIL\" placeholder=\"Your email address\" required \/>\r\n  <br>\r\n  <p style=\"text-align: center;\">\r\n    <input style=\"background-color: #890c06 !important; border-color: #890c06;\" type=\"submit\" value=\"Sign up\" \/>\r\n    \r\n  <\/p>\r\n  <p style=\"text-align: center;\">\r\n    <a href=\"http:\/\/mailchi.mp\/c9c7b81ddf13\/the-informed-testers-newsletter-20-oct-2017\"><small>View a sample<\/small><\/a>\r\n  <\/p>\r\n  <br>\r\n<\/div><\/div><label style=\"display: none !important;\">Leave this field empty if you're human: <input type=\"text\" name=\"_mc4wp_honeypot\" value=\"\" tabindex=\"-1\" autocomplete=\"off\" \/><\/label><input type=\"hidden\" name=\"_mc4wp_timestamp\" value=\"1776201235\" \/><input type=\"hidden\" name=\"_mc4wp_form_id\" value=\"6165\" \/><input type=\"hidden\" name=\"_mc4wp_form_element_id\" value=\"mc4wp-form-1\" \/><div class=\"mc4wp-response\"><\/div><\/form><!-- \/ Mailchimp for WordPress Plugin -->\n<hr>\n","protected":false},"excerpt":{"rendered":"<p>In this post we show you how to scroll through a table and identify elements. We also tackle the case where the table spans multiple pages. This is a natural and common workflow on most mobile devices. A typical use case involves the user performing a search within a mobile application. The app returns a table of results. The user [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[48,50,38,47,71,18],"tags":[],"class_list":["post-2623","post","type-post","status-publish","format-standard","hentry","category-android","category-appium","category-automation","category-mobile","category-mobile-automation","category-python"],"_links":{"self":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/2623","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\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/comments?post=2623"}],"version-history":[{"count":66,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/2623\/revisions"}],"predecessor-version":[{"id":6218,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/2623\/revisions\/6218"}],"wp:attachment":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/media?parent=2623"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/categories?post=2623"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/tags?post=2623"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}