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