Home | History | Annotate | Download | only in remoting
      1 #!/usr/bin/python
      2 # Copyright 2014 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """A Chromedriver smoke-test that installs and launches a web-app.
      7 
      8   Args:
      9     driver_dir: Location of Chromedriver binary on local machine.
     10     profile_dir: A user-data-dir containing login token for the app-user.
     11     app_id: App ID of web-app in Chrome web-store.
     12     app_window_title: The title of the window that should come up on app launch.
     13 
     14     TODO(anandc): Reduce the # of parameters required from the command-line.
     15     Maybe read from a JSON file. Also, map appID to expected app window title.
     16 
     17   This script navigates to the app-detail page on Chrome Web Store for the
     18   specified app-id. From there, it then installs the app and launches it. It
     19   then checks if the resulting new window has the expected title.
     20 """
     21 
     22 import argparse
     23 import os
     24 import shutil
     25 import tempfile
     26 import time
     27 
     28 from selenium import webdriver
     29 from selenium.webdriver.chrome.options import Options
     30 
     31 CWS_URL = 'https://chrome.google.com/webstore/detail'
     32 WEBSTORE_BUTTON_LABEL = 'webstore-test-button-label'
     33 FREE_BUTTON_XPATH = (
     34     '//div[contains(@class, \"%s\") and text() = \"Free\"]' %
     35     (WEBSTORE_BUTTON_LABEL))
     36 LAUNCH_BUTTON_XPATH = (
     37     '//div[contains(@class, \"%s\") and text() = \"Launch app\"]' %
     38     (WEBSTORE_BUTTON_LABEL))
     39 WAIT_TIME = 2
     40 
     41 
     42 def CreateTempProfileDir(source_dir):
     43   """Creates a temporary profile directory, for use by the test.
     44 
     45      This avoids modifying the input user-data-dir by actions that the test
     46      performs.
     47 
     48   Args:
     49     source_dir: The directory to copy and place in a temp folder.
     50 
     51   Returns:
     52     tmp_dir: Name of the temporary folder that was created.
     53     profile_dir: Name of the profile-dir under the tmp_dir.
     54   """
     55 
     56   tmp_dir = tempfile.mkdtemp()
     57   print 'Created folder %s' % (tmp_dir)
     58   profile_dir = os.path.join(tmp_dir, 'testuser')
     59   # Copy over previous created profile for this execution of Chrome Driver.
     60   shutil.copytree(source_dir, profile_dir)
     61   return tmp_dir, profile_dir
     62 
     63 
     64 def ParseCmdLineArgs():
     65   """Parses command line arguments and returns them.
     66 
     67   Returns:
     68     args: Parse command line arguments.
     69   """
     70   parser = argparse.ArgumentParser()
     71   parser.add_argument(
     72       '-d', '--driver_dir', required=True,
     73       help='path to folder where Chromedriver has been installed.')
     74   parser.add_argument(
     75       '-p', '--profile_dir', required=True,
     76       help='path to user-data-dir with trusted-tester signed in.')
     77   parser.add_argument(
     78       '-a', '--app_id', required=True,
     79       help='app-id of web-store app being tested.')
     80   parser.add_argument(
     81       '-e', '--app_window_title', required=True,
     82       help='Title of the app window that we expect to come up.')
     83 
     84   # Use input json file if specified on command line.
     85   args = parser.parse_args()
     86   return args
     87 
     88 
     89 def GetLinkAndWait(driver, link_to_get):
     90   """Navigates to the specified link.
     91 
     92   Args:
     93     driver: Active window for this Chromedriver instance.
     94     link_to_get: URL of the destination.
     95   """
     96   driver.get(link_to_get)
     97   # TODO(anandc): Is there any event or state we could wait on? For now,
     98   # we have hard-coded sleeps.
     99   time.sleep(WAIT_TIME)
    100 
    101 
    102 def ClickAndWait(driver, button_xpath):
    103   """Clicks button at the specified XPath of the current document.
    104 
    105   Args:
    106     driver: Active window for this Chromedriver instance.
    107     button_xpath: XPath in this document to button we want to click.
    108   """
    109   button = driver.find_element_by_xpath(button_xpath)
    110   button.click()
    111   time.sleep(WAIT_TIME)
    112 
    113 
    114 def WindowWithTitleExists(driver, title):
    115   """Verifies if one of the open windows has the specified title.
    116 
    117   Args:
    118     driver: Active window for this Chromedriver instance.
    119     title: Title of the window we are looking for.
    120 
    121   Returns:
    122     True if an open window in this session with the specified title was found.
    123     False otherwise.
    124   """
    125   for handle in driver.window_handles:
    126     driver.switch_to_window(handle)
    127     if driver.title == title:
    128       return True
    129   return False
    130 
    131 
    132 def main():
    133 
    134   args = ParseCmdLineArgs()
    135 
    136   org_profile_dir = args.profile_dir
    137   print 'Creating temp-dir using profile-dir %s' % org_profile_dir
    138   tmp_dir, profile_dir = CreateTempProfileDir(org_profile_dir)
    139 
    140   options = Options()
    141   options.add_argument('--user-data-dir=' + profile_dir)
    142   # Suppress the confirmation dialog that comes up.
    143   # With M39, this flag will no longer work. See https://crbug/357774.
    144   # TODO(anandc): Work with a profile-dir that already has extension downloaded,
    145   # and also add support for loading extension from a local directory.
    146   options.add_argument('--apps-gallery-install-auto-confirm-for-tests=accept')
    147   driver = webdriver.Chrome(args.driver_dir, chrome_options=options)
    148 
    149   try:
    150 
    151     chrome_apps_link = 'chrome://apps'
    152     cws_app_detail_link = '%s/%s' % (CWS_URL, args.app_id)
    153 
    154     # Navigate to chrome:apps first.
    155     # TODO(anandc): Add check to make sure the app we are testing isn't already
    156     # added for this user.
    157     GetLinkAndWait(driver, chrome_apps_link)
    158 
    159     # Navigate to the app detail page at the Chrome Web Store.
    160     GetLinkAndWait(driver, cws_app_detail_link)
    161     # Get the page again, to get all controls. This seems to be a bug, either
    162     # in ChromeDriver, or the app-page. Without this additional GET, we don't
    163     # get all controls. Even sleeping for 5 seconds doesn't suffice.
    164     # TODO(anandc): Investigate why the page doesn't work with just 1 call.
    165     GetLinkAndWait(driver, cws_app_detail_link)
    166 
    167     # Install the app by clicking the button that says "Free".
    168     ClickAndWait(driver, FREE_BUTTON_XPATH)
    169 
    170     # We should now be at a new tab. Get its handle.
    171     current_tab = driver.window_handles[-1]
    172     # And switch to it.
    173     driver.switch_to_window(current_tab)
    174 
    175     # From this new tab, go to Chrome Apps
    176     # TODO(anandc): Add check to make sure the app we are testing is now added.
    177     GetLinkAndWait(driver, chrome_apps_link)
    178 
    179     # Back to the app detail page.
    180     GetLinkAndWait(driver, cws_app_detail_link)
    181     # Again, do this twice, for reasons noted above.
    182     GetLinkAndWait(driver, cws_app_detail_link)
    183 
    184     # Click to launch the newly installed app.
    185     ClickAndWait(driver, LAUNCH_BUTTON_XPATH)
    186 
    187     # For now, make sure the "connecting" dialog comes up.
    188     # TODO(anandc): Add more validation; ideally, wait for the separate app
    189     # window to appear.
    190     if WindowWithTitleExists(driver, args.app_window_title):
    191       print 'Web-App %s launched successfully.' % args.app_window_title
    192     else:
    193       print 'Web-app %s did not launch successfully.' % args.app_window_title
    194 
    195   except Exception, e:
    196     raise e
    197   finally:
    198     # Cleanup.
    199     print 'Deleting %s' % tmp_dir
    200     shutil.rmtree(profile_dir)
    201     os.rmdir(tmp_dir)
    202     driver.quit()
    203 
    204 
    205 if __name__ == '__main__':
    206   main()
    207