def main(options): print "Setting up CertTest app to device" dm = None if options.adb_path: dm = mozdevice.DeviceManagerADB(adbPath=options.adb_path) else: dm = mozdevice.DeviceManagerADB() if dm.dirExists("/data/local/webapps/certtest-app"): print "CertTest app is already installed" return dm.pushFile("certtest_app.zip", "/data/local/certtest_app.zip") # forward the marionette port print "Forwarding marionette port" ret = dm.forward("tcp:2828", "tcp:2828") if ret != 0: #TODO: right thing here is to keep trying local ports and pass that value in our config raise Exception("Can't use localhost:2828 for port forwarding. Is something else using port 2828?") # install the app print "installing the app" f = open("app_install.js", "r") script = f.read() f.close() m = Marionette() m.start_session() m.set_context("chrome") m.set_script_timeout(5000) m.execute_async_script(script) m.delete_session()
def install_app(app_name, app_path, adb_path="adb", script_timeout=5000, marionette=None, device_serial=None): """ This installs the given application. NOTE: if a marionette session is passed, this function switches to 'content' context and will be at the top-most frame. """ if is_installed(app_name, adb_path=adb_path, device_serial=device_serial): raise Exception("%s is already installed" % app_name) sys.exit(1) app_zip = os.path.basename(app_path) dm = mozdevice.DeviceManagerADB(adbPath=adb_path, deviceSerial=device_serial) dm.pushFile("%s" % app_path, "/data/local/%s" % app_zip) # forward the marionette port dm.forward("tcp:2828", "tcp:2828") # install the app install_js = pkg_resources.resource_filename( __name__, os.path.sep.join(['app_install.js'])) with open(install_js, "r") as f: script = f.read() installed_app_name = app_name.lower().replace(" ", "-") script = script.replace("YOURAPPID", installed_app_name) script = script.replace("YOURAPPZIP", app_zip) if not marionette: m = Marionette() m.start_session() else: m = marionette m.switch_to_frame() m.set_context("chrome") m.set_script_timeout(script_timeout) m.execute_async_script(script) if not marionette: m.delete_session() else: m.set_context("content")
def install_app(app_name, app_path, adb_path="adb", script_timeout=5000, marionette=None): """ This installs the given application. NOTE: if a marionette session is passed, this function switches to 'content' context and will be at the top-most frame. """ if is_installed(app_name, adb_path=adb_path): raise Exception("%s is already installed" % app_name) sys.exit(1) app_zip = os.path.basename(app_path) dm = mozdevice.DeviceManagerADB(adbPath=adb_path) dm.pushFile("%s" % app_path, "/data/local/%s" % app_zip) # forward the marionette port if dm.forward("tcp:2828", "tcp:2828") != 0: raise Exception("Can't use localhost:2828 for port forwarding." \ "Is something else using port 2828?") # install the app install_js = pkg_resources.resource_filename(__name__, os.path.sep.join([ 'app_install.js'])) with open(install_js, "r") as f: script = f.read() installed_app_name = app_name.lower().replace(" ", "-") script = script.replace("YOURAPPID", installed_app_name) script = script.replace("YOURAPPZIP", app_zip) if not marionette: m = Marionette() m.start_session() else: m = marionette m.switch_to_frame() m.set_context("chrome") m.set_script_timeout(script_timeout) m.execute_async_script(script) if not marionette: m.delete_session() else: m.set_context("content")
def install_app(app_name, app_path, adb_path=None): dm = None if adb_path: dm = mozdevice.DeviceManagerADB(adbPath=adb_path) else: dm = mozdevice.DeviceManagerADB() #TODO: replace with app name installed_app_name = app_name.lower() installed_app_name = installed_app_name.replace(" ", "-") if dm.dirExists("/data/local/webapps/%s" % installed_app_name): raise Exception("%s is already installed" % app_name) sys.exit(1) app_zip = os.path.basename(app_path) dm.pushFile("%s" % app_path, "/data/local/%s" % app_zip) # forward the marionette port ret = dm.forward("tcp:2828", "tcp:2828") if ret != 0: raise Exception("Can't use localhost:2828 for port forwarding." \ "Is something else using port 2828?") # install the app install_js = pkg_resources.resource_filename(__name__, os.path.sep.join([ 'app_install.js'])) f = open(install_js, "r") script = f.read() f.close() script = script.replace("YOURAPPID", installed_app_name) script = script.replace("YOURAPPZIP", app_zip) m = Marionette() m.start_session() m.set_context("chrome") m.set_script_timeout(5000) m.execute_async_script(script) m.delete_session()
def getTextContentViaMarionette(url): if Marionette is None: raise Exception("sorry this method is not supported because marionette driver couldn't be installed") client = Marionette("localhost", port=2828) print("client:", client) client.start_session() print("started session") client.set_script_timeout(30000) # not sure if one script ran after another return client.execute_async_script(""" var win = window.open('""" + url + """'); setTimeout(function(){ textContent = win.document.body.textContent; win.close(); marionetteScriptFinished(textContent); }, 15000); """)
def getTextContentViaMarionette(url): if Marionette is None: raise Exception( "sorry this method is not supported because marionette driver couldn't be installed" ) client = Marionette("localhost", port=2828) print("client:", client) client.start_session() print("started session") client.set_script_timeout(30000) # not sure if one script ran after another return client.execute_async_script(""" var win = window.open('""" + url + """'); setTimeout(function(){ textContent = win.document.body.textContent; win.close(); marionetteScriptFinished(textContent); }, 15000); """)
def listapps(): marionette = Marionette(host='localhost', port=2828) marionette.start_session() marionette.set_context(marionette.CONTEXT_CONTENT) marionette.set_script_timeout(1000) apps = marionette.execute_async_script(""" let req = navigator.mozApps.mgmt.getAll(); req.onsuccess = function() { let apps = req.result; let l = [] for (let a of apps) { let data = {origin: a.origin, name: a.manifest.name}; if (a.manifest.entry_points) data.entry_points = a.manifest.entry_points; l.push(data); } marionetteScriptFinished(l); }; """) for a in apps: print a["name"]
class B2GMixin(object): profileDir = None userJS = "/data/local/user.js" marionette = None def __init__(self, host=None, marionetteHost=None, marionettePort=2828, **kwargs): # (allowing marionneteHost to be specified seems a bit # counter-intuitive since we normally get it below from the ip # address, however we currently need it to be able to connect # via adb port forwarding and localhost) if marionetteHost: self.marionetteHost = marionetteHost elif host: self.marionetteHost = host self.marionettePort = marionettePort def cleanup(self): """ If a user profile was setup on the device, restore it to the original. """ if self.profileDir: self.restoreProfile() def waitForPort(self, timeout): """Waits for the marionette server to respond, until the timeout specified. :param timeout: Timeout parameter in seconds. """ print "waiting for port" starttime = datetime.datetime.now() while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print "trying %s %s" % (self.marionettePort, self.marionetteHost) sock.connect((self.marionetteHost, self.marionettePort)) data = sock.recv(16) sock.close() if '"from"' in data: return True except socket.error: pass except Exception as e: raise DMError("Could not connect to marionette: %s" % e) time.sleep(1) raise DMError("Could not communicate with Marionette port") def setupMarionette(self, scriptTimeout=60000): """ Starts a marionette session. If no host was given at init, the ip of the device will be retrieved and networking will be established. """ if not self.marionetteHost: self.setupDHCP() self.marionetteHost = self.getIP() if not self.marionette: self.marionette = Marionette(self.marionetteHost, self.marionettePort) if not self.marionette.session: self.waitForPort(30) self.marionette.start_session() self.marionette.set_script_timeout(scriptTimeout) def restartB2G(self): """ Restarts the b2g process on the device. """ #restart b2g so we start with a clean slate if self.marionette and self.marionette.session: self.marionette.delete_session() self.shellCheckOutput(['stop', 'b2g']) # Wait for a bit to make sure B2G has completely shut down. tries = 10 while "b2g" in self.shellCheckOutput(['ps', 'b2g']) and tries > 0: tries -= 1 time.sleep(1) if tries == 0: raise DMError("Could not kill b2g process") self.shellCheckOutput(['start', 'b2g']) def setupProfile(self, prefs=None): """Sets up the user profile on the device. :param prefs: String of user_prefs to add to the profile. Defaults to a standard b2g testing profile. """ # currently we have no custom prefs to set (when bug 800138 is fixed, # we will probably want to enable marionette on an external ip by # default) if not prefs: prefs = "" #remove previous user.js if there is one if not self.profileDir: self.profileDir = tempfile.mkdtemp() our_userJS = os.path.join(self.profileDir, "user.js") if os.path.exists(our_userJS): os.remove(our_userJS) #copy profile try: self.getFile(self.userJS, our_userJS) except subprocess.CalledProcessError: pass #if we successfully copied the profile, make a backup of the file if os.path.exists(our_userJS): self.shellCheckOutput(['dd', 'if=%s' % self.userJS, 'of=%s.orig' % self.userJS]) with open(our_userJS, 'a') as user_file: user_file.write("%s" % prefs) self.pushFile(our_userJS, self.userJS) self.restartB2G() self.setupMarionette() def setupDHCP(self, interfaces=['eth0', 'wlan0']): """Sets up networking. :param interfaces: Network connection types to try. Defaults to eth0 and wlan0. """ all_interfaces = [line.split()[0] for line in \ self.shellCheckOutput(['netcfg']).splitlines()[1:]] interfaces_to_try = filter(lambda i: i in interfaces, all_interfaces) tries = 5 print "Setting up DHCP..." while tries > 0: print "attempts left: %d" % tries try: for interface in interfaces_to_try: self.shellCheckOutput(['netcfg', interface, 'dhcp'], timeout=10) if self.getIP(interfaces=[interface]): return except DMError: pass time.sleep(1) tries -= 1 raise DMError("Could not set up network connection") def restoreProfile(self): """ Restores the original user profile on the device. """ if not self.profileDir: raise DMError("There is no profile to restore") #if we successfully copied the profile, make a backup of the file our_userJS = os.path.join(self.profileDir, "user.js") if os.path.exists(our_userJS): self.shellCheckOutput(['dd', 'if=%s.orig' % self.userJS, 'of=%s' % self.userJS]) shutil.rmtree(self.profileDir) self.profileDir = None def unlock(self): """ Unlocks the device, ensuring it will never go back to the lockscreen """ if not self.marionette or not self.marionette.session: self.setupMarionette() success = self.marionette.execute_async_script(""" let setlock = window.wrappedJSObject.SettingsListener.getSettingsLock(); let obj = {'screen.timeout': 0}; setlock.set(obj); waitFor( function() { window.wrappedJSObject.LockScreen.unlock(); waitFor( function() { finish(window.wrappedJSObject.LockScreen.locked); }, function() { return !window.wrappedJSObject.LockScreen.locked; } ); }, function() { return !!window.wrappedJSObject.LockScreen; } ); """) if not success: raise DMError("Unable to unlock device") def getAppInfo(self): """ Returns the appinfo, with an additional "date" key. :rtype: dictionary """ if not self.marionette or not self.marionette.session: self.setupMarionette() self.marionette.set_context("chrome") appinfo = self.marionette.execute_script(""" var appInfo = Components.classes["@mozilla.org/xre/app-info;1"] .getService(Components.interfaces.nsIXULAppInfo); return appInfo; """) (year, month, day) = (appinfo["appBuildID"][0:4], appinfo["appBuildID"][4:6], appinfo["appBuildID"][6:8]) appinfo['date'] = "%s-%s-%s" % (year, month, day) return appinfo
waitFor( function() { window.wrappedJSObject.LockScreen.unlock(); waitFor( function() { finish(window.wrappedJSObject.LockScreen.locked); }, function() { return !window.wrappedJSObject.LockScreen.locked; } ); }, function() { return !!window.wrappedJSObject.LockScreen; } ); """ from marionette import Marionette marionette = Marionette('localhost', 2828) marionette.start_session() marionette.import_script('gaia_apps.js') marionette.set_script_timeout(60000) marionette.execute_script(unlock) result = marionette.execute_async_script("GaiaApps.launchWithName('%s')" % "NxtGenUG-QualityApps") marionette.switch_to_frame(result.get("frame")) pdb.set_trace()
class B2gExecutor(Executor): def __init__(self, app_name, app_id, device=False): self.device = device if self.device: call(['adb', 'forward', 'tcp:2828', 'tcp:2828']) self._app_name = app_name self._app_id = app_id self._marionette = Marionette() self._marionette.start_session() self._gaia_apps = GaiaApps(self._marionette) self._gaia_data = GaiaData(self._marionette) self._gaia_device = GaiaDevice(self._marionette) ''' Deprecated # https://github.com/mozilla-b2g/gaia/blob/b568b7ae8adb6ee3651bd75acbaaedff86a08912/tests/python/gaia-ui-tests/gaiatest/gaia_test.py js = os.path.abspath(os.path.join(__file__, os.path.pardir, 'atoms', "gaia_apps.js")) self._marionette.import_script(js) js = os.path.abspath(os.path.join(__file__, os.path.pardir, 'atoms', "gaia_data_layer.js")) self._marionette.set_context(self._marionette.CONTEXT_CHROME) self._marionette.import_script(js) self._marionette.set_context(self._marionette.CONTEXT_CONTENT) # C:\Users\Jun-Wei\Desktop\b2g\battery\manifest.webapp #app = GaiaApps(self._marionette).launch(self._app_name) #app = GaiaApps(self._marionette).launch('Battery', manifest_url='C:/Users/Jun-Wei/Desktop/b2g/battery/manifest.webapp', entry_point='/index.html') app = GaiaApps(self._marionette).launch('Battery') print app.frame print app.src print app.origin print app.name #print g_app.manifest_url #self._app_frame = g_app.frame self._app_frame_id = app.frame self._app_src = app.src self._app_origin = app.origin #self.app_manifest_url = g_app.manifest_url #self.gaia_apps = GaiaApps(self.__marionette) #print self.gaia_apps.displayed_app.name #print self.gaia_apps.installed_apps #print self.gaia_apps.running_apps() #js = os.path.abspath(os.path.join(__file__, os.path.pardir, 'atoms', "gaia_apps.js")) #self.__marionette.import_script(js) ''' def fire_event(self, clickable): logger.info('fire_event: id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) try: # id staring with DomAnalyzer.serial_prefix is given by our monkey and should be ignored when locating if clickable.get_id() and not clickable.get_id().startswith( DomAnalyzer.serial_prefix): self._marionette.find_element('id', clickable.get_id()).tap() elif clickable.get_xpath(): self._marionette.find_element('xpath', clickable.get_xpath()).tap() else: logger.error( 'No id nor xpath for the clickable: id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) sys.exit() except (ElementNotVisibleException, InvalidElementStateException, NoSuchElementException): logger.info( 'Element is not interactable in fire_event(): id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) except Exception as e: logger.error( 'Unknown Exception: %s in fire_event(): id: %s (xpath: %s)', str(e), clickable.get_id(), clickable.get_xpath()) sys.exit() def fill_form(self, clickable): for f in clickable.get_forms(): for input_field in f.get_inputs(): try: if input_field.get_id() and not input_field.get_id( ).startswith(DomAnalyzer.serial_prefix): self._marionette.find_element( 'id', input_field.get_id()).send_keys( input_field.get_value()) elif input_field.get_xpath(): self._marionette.find_element( 'xpath', input_field.get_xpath()).send_keys( input_field.get_value()) else: logger.error( 'No id nor xpath for an input field in the form id: %s (xpath: %s)', f.get_id(), f.get_xpath()) sys.exit() except (ElementNotVisibleException, InvalidElementStateException, NoSuchElementException): logger.info( 'Element is not interactable in fill_form(): id: %s (xpath: %s)', f.get_id(), f.get_xpath()) except Exception as e: logger.error( 'Unknown Exception: %s in fill_form(): id: %s (xpath: %s)', str(e), f.get_id(), f.get_xpath()) sys.exit() def empty_form(self, clickable): for f in clickable.get_forms(): for input_field in f.get_inputs(): try: if input_field.get_id() and not input_field.get_id( ).startswith(DomAnalyzer.serial_prefix): self._marionette.find_element( 'id', input_field.get_id()).clear() elif input_field.get_xpath(): self._marionette.find_element( 'xpath', input_field.get_xpath()).clear() else: logger.error( 'No id nor xpath for an input field in the form %s (%s)', f.get_id(), f.get_xpath()) sys.exit() except (ElementNotVisibleException, InvalidElementStateException, NoSuchElementException): logger.info( 'Element is not interactable in empty_form(): id: %s (xpath: %s)', f.get_id(), f.get_xpath()) except Exception as e: logger.error( 'Unknown Exception: %s in empty_form(): id: %s (xpath: %s)', str(e), f.get_id(), f.get_xpath()) sys.exit() def get_source(self): return self._marionette.page_source.encode(sys.stdout.encoding, 'ignore') def get_screenshot(self, clickable=None): element = None if clickable: try: if clickable.get_id() and not clickable.get_id().startswith( DomAnalyzer.serial_prefix): element = self._marionette.find_element( 'id', clickable.get_id()) elif clickable.get_xpath(): element = self._marionette.find_element( 'xpath', clickable.get_xpath()) else: logger.error( 'No id nor xpath for the clickable: id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) sys.exit() except (ElementNotVisibleException, InvalidElementStateException, NoSuchElementException): logger.info( 'Element is not interactable in get_screenshot(): id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) except Exception as e: logger.error( 'Unknown Exception: %s in get_screenshot(): id: %s (xpath: %s)', str(e), clickable.get_id(), clickable.get_xpath()) sys.exit() if not element: # set context to CHROME to capture whole screen # system frame e.g. FileNotFound cannot be captured without CONTEXT_CHROME (Don't know why) self._marionette.set_context(self._marionette.CONTEXT_CHROME) screenshot = self._marionette.screenshot(element) self._marionette.set_context(self._marionette.CONTEXT_CONTENT) return screenshot def switch_to_frame(self, by, frame_str): """ :param by: options: "id", "xpath", "link text", "partial link text", "name", "tag name", "class name", "css selector", "anon attribute" """ # self.switch_to_top_frame() frame = self._marionette.find_element(by, frame_str) self._marionette.switch_to_frame(frame) def switch_to_top_frame(self): self._marionette.switch_to_frame() # switch to the top-level frame def restart_app(self): # remember to disable screen timeout and screen lock before testing # todo: open b2g simulator, install app, # unlock_screen # self._marionette.execute_script('window.wrappedJSObject.lockScreen.unlock();') self.kill_all_apps() # kill_all_apps() will also kill the 'homescreen app' on real device # so trigger a home event to restore homescreen if self.device: self._dispatch_home_button_event() self.clear_data() self.touch_home_button() # launch the app self._gaia_apps.launch(self._app_name) ''' Deprecated if self.device: icon = self._marionette.find_element('xpath', "//li[contains(@aria-label, '" + self._app_name + "')]") else: icon = self._marionette.find_element('xpath', "//div[contains(@class, 'icon')]//span[contains(text(),'" + self._app_name + "')]") icon.tap() ''' time.sleep(5) # wait for app screen self._marionette.switch_to_frame() # this wait seems not working, need to find another useful one Wait(self._marionette).until(lambda m: m.find_element( 'css selector', "iframe[data-url*='" + self._app_id + "']"). is_displayed()) app_frame = self._marionette.find_element( 'css selector', "iframe[data-url*='" + self._app_id + "']") self._marionette.switch_to_frame(app_frame) def touch_home_button(self): # ref: https://github.com/mozilla-b2g/gaia/blob/master/tests/python/gaia-ui-tests/gaiatest/gaia_test.py#L751 apps = self._gaia_apps if apps.displayed_app.name.lower() != 'homescreen': # touching home button will return to homescreen self._dispatch_home_button_event() Wait(self._marionette).until( lambda m: apps.displayed_app.name.lower() == 'homescreen') apps.switch_to_displayed_app() else: apps.switch_to_displayed_app() mode = self._marionette.find_element(By.TAG_NAME, 'body').get_attribute('class') self._dispatch_home_button_event() apps.switch_to_displayed_app() if 'edit-mode' in mode: # touching home button will exit edit mode Wait(self._marionette).until(lambda m: m.find_element( By.TAG_NAME, 'body').get_attribute('class') != mode) else: # touching home button inside homescreen will scroll it to the top Wait(self._marionette).until(lambda m: m.execute_script( "return window.wrappedJSObject.scrollY") == 0) def _dispatch_home_button_event(self): self._gaia_device._dispatch_home_button_event() ''' Deprecated self._marionette.switch_to_frame() self._marionette.execute_script("window.wrappedJSObject.dispatchEvent(new Event('home'));") ''' time.sleep(0.5) def clear_data(self): # for now, clear contact data # https://github.com/mozilla-b2g/gaia/blob/v2.2/tests/python/gaia-ui-tests/gaiatest/gaia_test.py#L208 self._marionette.set_context(self._marionette.CONTEXT_CHROME) result = self._marionette.execute_async_script( 'return GaiaDataLayer.removeAllContacts();') assert result, 'Unable to remove all contacts' self._marionette.set_context(self._marionette.CONTEXT_CONTENT) time.sleep(0.5) def kill_all_apps(self): self._marionette.switch_to_frame() self._marionette.execute_async_script(""" // Kills all running apps, except the homescreen. function killAll() { let manager = window.wrappedJSObject.appWindowManager; let apps = manager.getApps(); for (let id in apps) { let origin = apps[id].origin; if (origin.indexOf('verticalhome') == -1) { manager.kill(origin); } } }; killAll(); // return true so execute_async_script knows the script is complete marionetteScriptFinished(true); """) time.sleep(0.5)
class B2gExecutor(Executor): def __init__(self, app_name, app_id, device=False): self.device = device if self.device: call(['adb', 'forward', 'tcp:2828', 'tcp:2828']) self._app_name = app_name self._app_id = app_id self._marionette = Marionette() self._marionette.start_session() self._gaia_apps = GaiaApps(self._marionette) self._gaia_data = GaiaData(self._marionette) self._gaia_device = GaiaDevice(self._marionette) ''' Deprecated # https://github.com/mozilla-b2g/gaia/blob/b568b7ae8adb6ee3651bd75acbaaedff86a08912/tests/python/gaia-ui-tests/gaiatest/gaia_test.py js = os.path.abspath(os.path.join(__file__, os.path.pardir, 'atoms', "gaia_apps.js")) self._marionette.import_script(js) js = os.path.abspath(os.path.join(__file__, os.path.pardir, 'atoms', "gaia_data_layer.js")) self._marionette.set_context(self._marionette.CONTEXT_CHROME) self._marionette.import_script(js) self._marionette.set_context(self._marionette.CONTEXT_CONTENT) # C:\Users\Jun-Wei\Desktop\b2g\battery\manifest.webapp #app = GaiaApps(self._marionette).launch(self._app_name) #app = GaiaApps(self._marionette).launch('Battery', manifest_url='C:/Users/Jun-Wei/Desktop/b2g/battery/manifest.webapp', entry_point='/index.html') app = GaiaApps(self._marionette).launch('Battery') print app.frame print app.src print app.origin print app.name #print g_app.manifest_url #self._app_frame = g_app.frame self._app_frame_id = app.frame self._app_src = app.src self._app_origin = app.origin #self.app_manifest_url = g_app.manifest_url #self.gaia_apps = GaiaApps(self.__marionette) #print self.gaia_apps.displayed_app.name #print self.gaia_apps.installed_apps #print self.gaia_apps.running_apps() #js = os.path.abspath(os.path.join(__file__, os.path.pardir, 'atoms', "gaia_apps.js")) #self.__marionette.import_script(js) ''' def fire_event(self, clickable): logger.info('fire_event: id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) try: # id staring with DomAnalyzer.serial_prefix is given by our monkey and should be ignored when locating if clickable.get_id() and not clickable.get_id().startswith(DomAnalyzer.serial_prefix): self._marionette.find_element('id', clickable.get_id()).tap() elif clickable.get_xpath(): self._marionette.find_element('xpath', clickable.get_xpath()).tap() else: logger.error('No id nor xpath for the clickable: id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) sys.exit() except (ElementNotVisibleException, InvalidElementStateException, NoSuchElementException): logger.info('Element is not interactable in fire_event(): id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) except Exception as e: logger.error('Unknown Exception: %s in fire_event(): id: %s (xpath: %s)', str(e), clickable.get_id(), clickable.get_xpath()) sys.exit() def fill_form(self, clickable): for f in clickable.get_forms(): for input_field in f.get_inputs(): try: if input_field.get_id() and not input_field.get_id().startswith(DomAnalyzer.serial_prefix): self._marionette.find_element('id', input_field.get_id()).send_keys(input_field.get_value()) elif input_field.get_xpath(): self._marionette.find_element('xpath', input_field.get_xpath()).send_keys(input_field.get_value()) else: logger.error('No id nor xpath for an input field in the form id: %s (xpath: %s)', f.get_id(), f.get_xpath()) sys.exit() except (ElementNotVisibleException, InvalidElementStateException, NoSuchElementException): logger.info('Element is not interactable in fill_form(): id: %s (xpath: %s)', f.get_id(), f.get_xpath()) except Exception as e: logger.error('Unknown Exception: %s in fill_form(): id: %s (xpath: %s)', str(e), f.get_id(), f.get_xpath()) sys.exit() def empty_form(self, clickable): for f in clickable.get_forms(): for input_field in f.get_inputs(): try: if input_field.get_id() and not input_field.get_id().startswith(DomAnalyzer.serial_prefix): self._marionette.find_element('id', input_field.get_id()).clear() elif input_field.get_xpath(): self._marionette.find_element('xpath', input_field.get_xpath()).clear() else: logger.error('No id nor xpath for an input field in the form %s (%s)', f.get_id(), f.get_xpath()) sys.exit() except (ElementNotVisibleException, InvalidElementStateException, NoSuchElementException): logger.info('Element is not interactable in empty_form(): id: %s (xpath: %s)', f.get_id(), f.get_xpath()) except Exception as e: logger.error('Unknown Exception: %s in empty_form(): id: %s (xpath: %s)', str(e), f.get_id(), f.get_xpath()) sys.exit() def get_source(self): return self._marionette.page_source.encode(sys.stdout.encoding, 'ignore') def get_screenshot(self, clickable=None): element = None if clickable: try: if clickable.get_id() and not clickable.get_id().startswith(DomAnalyzer.serial_prefix): element = self._marionette.find_element('id', clickable.get_id()) elif clickable.get_xpath(): element = self._marionette.find_element('xpath', clickable.get_xpath()) else: logger.error('No id nor xpath for the clickable: id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) sys.exit() except (ElementNotVisibleException, InvalidElementStateException, NoSuchElementException): logger.info('Element is not interactable in get_screenshot(): id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) except Exception as e: logger.error('Unknown Exception: %s in get_screenshot(): id: %s (xpath: %s)', str(e), clickable.get_id(), clickable.get_xpath()) sys.exit() if not element: # set context to CHROME to capture whole screen # system frame e.g. FileNotFound cannot be captured without CONTEXT_CHROME (Don't know why) self._marionette.set_context(self._marionette.CONTEXT_CHROME) screenshot = self._marionette.screenshot(element) self._marionette.set_context(self._marionette.CONTEXT_CONTENT) return screenshot def switch_to_frame(self, by, frame_str): """ :param by: options: "id", "xpath", "link text", "partial link text", "name", "tag name", "class name", "css selector", "anon attribute" """ # self.switch_to_top_frame() frame = self._marionette.find_element(by, frame_str) self._marionette.switch_to_frame(frame) def switch_to_top_frame(self): self._marionette.switch_to_frame() # switch to the top-level frame def restart_app(self): # remember to disable screen timeout and screen lock before testing # todo: open b2g simulator, install app, # unlock_screen # self._marionette.execute_script('window.wrappedJSObject.lockScreen.unlock();') self.kill_all_apps() # kill_all_apps() will also kill the 'homescreen app' on real device # so trigger a home event to restore homescreen if self.device: self._dispatch_home_button_event() self.clear_data() self.touch_home_button() # launch the app self._gaia_apps.launch(self._app_name) ''' Deprecated if self.device: icon = self._marionette.find_element('xpath', "//li[contains(@aria-label, '" + self._app_name + "')]") else: icon = self._marionette.find_element('xpath', "//div[contains(@class, 'icon')]//span[contains(text(),'" + self._app_name + "')]") icon.tap() ''' time.sleep(5) # wait for app screen self._marionette.switch_to_frame() # this wait seems not working, need to find another useful one Wait(self._marionette).until(lambda m: m.find_element('css selector', "iframe[data-url*='" + self._app_id + "']").is_displayed()) app_frame = self._marionette.find_element('css selector', "iframe[data-url*='" + self._app_id + "']") self._marionette.switch_to_frame(app_frame) def touch_home_button(self): # ref: https://github.com/mozilla-b2g/gaia/blob/master/tests/python/gaia-ui-tests/gaiatest/gaia_test.py#L751 apps = self._gaia_apps if apps.displayed_app.name.lower() != 'homescreen': # touching home button will return to homescreen self._dispatch_home_button_event() Wait(self._marionette).until( lambda m: apps.displayed_app.name.lower() == 'homescreen') apps.switch_to_displayed_app() else: apps.switch_to_displayed_app() mode = self._marionette.find_element(By.TAG_NAME, 'body').get_attribute('class') self._dispatch_home_button_event() apps.switch_to_displayed_app() if 'edit-mode' in mode: # touching home button will exit edit mode Wait(self._marionette).until(lambda m: m.find_element( By.TAG_NAME, 'body').get_attribute('class') != mode) else: # touching home button inside homescreen will scroll it to the top Wait(self._marionette).until(lambda m: m.execute_script( "return window.wrappedJSObject.scrollY") == 0) def _dispatch_home_button_event(self): self._gaia_device._dispatch_home_button_event() ''' Deprecated self._marionette.switch_to_frame() self._marionette.execute_script("window.wrappedJSObject.dispatchEvent(new Event('home'));") ''' time.sleep(0.5) def clear_data(self): # for now, clear contact data # https://github.com/mozilla-b2g/gaia/blob/v2.2/tests/python/gaia-ui-tests/gaiatest/gaia_test.py#L208 self._marionette.set_context(self._marionette.CONTEXT_CHROME) result = self._marionette.execute_async_script('return GaiaDataLayer.removeAllContacts();') assert result, 'Unable to remove all contacts' self._marionette.set_context(self._marionette.CONTEXT_CONTENT) time.sleep(0.5) def kill_all_apps(self): self._marionette.switch_to_frame() self._marionette.execute_async_script(""" // Kills all running apps, except the homescreen. function killAll() { let manager = window.wrappedJSObject.appWindowManager; let apps = manager.getApps(); for (let id in apps) { let origin = apps[id].origin; if (origin.indexOf('verticalhome') == -1) { manager.kill(origin); } } }; killAll(); // return true so execute_async_script knows the script is complete marionetteScriptFinished(true); """) time.sleep(0.5)
class TestRun(object): def __init__(self, adb="adb", serial=None): self.test_results = {} self.test_results_file = None self.m = None self.gaia_apps = None self.screenshot_path = None self.logcat_path = None self.app_name = None self.attempt = None self.num_apps = None self.device = None self.serial = serial self.port = None self.run_log = logging.getLogger('marketplace-test') if self.serial: self.dm = DeviceManagerADB(adbPath=adb, deviceSerial=serial) else: self.dm = DeviceManagerADB(adbPath=adb) def reset_marionette(self): try: self.m.delete_session() except Exception: pass self.m = None self.get_marionette() def get_marionette(self): if not self.m: self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) else: tries = 5 while tries > 0: try: self.m.get_url() break except MarionetteException as e: if "Please start a session" in str(e): time.sleep(5) self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) tries -= 1 else: raise e else: self.run_log.error("Can't connect to marionette, rebooting") self.restart_device() return self.m def write_to_file(self, data): with open("%s.tmp" % self.test_results_file, "w") as f: f.write(data) shutil.copyfile("%s.tmp" % self.test_results_file, self.test_results_file) def add_values(self, key, value): if self.serial: self.test_results["%s_%s" % (key, self.serial)] = value else: self.test_results["%s" % key] = value def add_result(self, passed=False, status=None, uninstalled_failure=False): values = {} if status: if not passed: values["status"] = "FAILED: %s" % status else: values["status"] = "PASS" if self.screenshot_path: values["screenshot"] = self.screenshot_path if self.logcat_path: values["logcat"] = self.logcat_path if uninstalled_failure: values["uninstalled_failure"] = uninstalled_failure entry = "%s_%s" % (self.app_name, self.attempt) self.test_results[entry] = values def launch_with_manifest(self, manifest): self.m.switch_to_frame() result = self.m.execute_async_script( "GaiaApps.launchWithManifestURL('%s')" % manifest, script_timeout=30000) if result == False: raise Exception("launch timed out") app = GaiaApp(frame=result.get('frame'), src=result.get('src'), name=result.get('name'), origin=result.get('origin')) if app.frame_id is None: raise Exception("failed to launch; there is no app frame") self.m.switch_to_frame(app.frame_id) return app def uninstall_with_manifest(self, manifest): self.m.switch_to_frame() script = """ GaiaApps.locateWithManifestURL('%s', null, function uninstall(app) { navigator.mozApps.mgmt.uninstall(app); marionetteScriptFinished(true); }); """ result = self.m.execute_async_script(script % manifest, script_timeout=60000) if result != True: self.add_result(status="Failed to uninstall app with url '%s'" % manifest) return app def forward_port(self): # get unused port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('localhost', 0)) addr, port = s.getsockname() s.close() dm_tries = 0 self.run_log.info("using port %s" % port) while dm_tries < 20: if self.dm.forward("tcp:%d" % port, "tcp:2828") == 0: break dm_tries += 1 time.sleep(3) else: return False self.port = port return True def restart_device(self, restart_tries=0): self.run_log.info("rebooting") # TODO restarting b2g doesn't seem to work... reboot then while restart_tries < 3: restart_tries += 1 self.dm.reboot(wait=True) self.run_log.info("forwarding") if not self.forward_port(): self.run_log.error("couldn't forward port in time, rebooting") continue self.m = Marionette(port=self.port) if not self.m.wait_for_port(180): self.run_log.error( "couldn't contact marionette in time, rebooting") continue time.sleep(1) self.m.start_session() try: Wait(self.m, timeout=240).until(lambda m: m.find_element( "id", "lockscreen-container").is_displayed()) # It retuns a little early time.sleep(2) self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.device.unlock() self.gaia_apps = GaiaApps(self.m) except (MarionetteException, IOError, socket.error) as e: self.run_log.error("got exception: %s, going to retry" % e) try: self.m.delete_session() except: # at least attempt to clear the session if possible pass continue break else: raise Exception( "Couldn't restart the device in time, even after 3 tries") def readystate_wait(self, app): try: Wait(self.get_marionette(), timeout=30).until(lambda m: m.execute_script( "return window.document.readyState;") == "complete") except ScriptTimeoutException as e: return False return True def record_icons(self): self.device.touch_home_button() icons = self.m.find_elements("class name", "icon") self.num_apps = len(icons) def check_if_app_installed(self, timeout=180): # TODO: Find a better way to do this than checking homescreen # I hope there is one... self.device.touch_home_button() icons = self.m.find_elements("class name", "icon") start = time.time() end = start + 180 found_icon = None claims_its_loaded = 0 # this is used in case 'loading' isn't applied immediately to the icon while time.time() < end: if not found_icon: icons = self.m.find_elements("class name", "icon") # We can't do set comparison b/c references change if len(icons) > self.num_apps: for icon in icons: if "loading" in icon.get_attribute("innerHTML"): found_icon = icon break else: claims_its_loaded += 1 if claims_its_loaded == 3: return True else: if "loading" not in found_icon.get_attribute("innerHTML"): return True time.sleep(2) return False
class TestCase(tornado.testing.AsyncTestCase): def __init__(self, *args, **kwargs): #self.config = kwargs.pop("config") self.handler = kwargs.pop('handler') self.io_loop = kwargs.pop('io_loop') self.cert_test_app = None super(TestCase, self).__init__(*args, **kwargs) def setUp(self): super(TestCase, self).setUp() # import environment # from environment import InProcessTestEnvironment # self.environment = environment.get(InProcessTestEnvironment) # self.server = self.environment.server self.marionette = None self.create_marionette() self.io_loop.run_sync(self.use_cert_app) def tearDown(self): super(TestCase, self).tearDown() self.io_loop.run_sync(self.close_cert_app) def create_marionette(self): if not self.marionette or not self.marionette.session: self.marionette = Marionette() self.marionette.start_session() @tornado.gen.coroutine def use_cert_app(self): # app management is done in the system app self.marionette.switch_to_frame() self.marionette.import_script("tests/app_management.js") script = "GaiaApps.launchWithName('CertTest App');" try: self.cert_test_app = self.marionette.execute_async_script(script, script_timeout=5000) self.marionette.switch_to_frame(self.cert_test_app["frame"]) self.assertTrue('certtest' in self.marionette.get_url()) except MarionetteException as e: ok = yield self.instruct("Could not launch CertTest app automatically." \ "Please launch by hand then hit OK to continue.") self.assertTrue(ok, "Could not launch CertTest app") except Exception as e: message = "Unexpected exception: %s" % e yield self.instruct(message) self.fail(message) @tornado.gen.coroutine def close_cert_app(self): self.marionette.import_script("tests/app_management.js") # app management is done in the system app self.marionette.switch_to_frame() script = "GaiaApps.kill('%s');" % self.cert_test_app["origin"] try: self.marionette.execute_async_script(script, script_timeout=5000) self.assertTrue('certtest' not in self.marionette.get_url()) except MarionetteException as e: ok = yield self.instruct("Could not close CertTest app automatically." \ "Please close by hand then hit OK to continue.") self.assertTrue(ok, "Could not close CertTest app") except Exception as e: message = "Unexpected exception: %s" % e yield self.instruct(message) self.fail(message) def get_new_ioloop(self): return self.io_loop def prompt(self, message): """Prompt the user for a reply. Returns a future which must be yielded. This will trigger an overlay in the host browser window which can be used to tell the user to perform an action or to input some manual data for us to work on. Sample usage:: answer = yield prompt("What's the meaning of life?") assert answer == 42 This function is a simple wrapper for ``tornado.gen.Task``, and is equivalent to the usage of that. :param message: The question to ask or message to give the user. :returns: A generator which must be yielded. Once yielded, the return value will be the value of the prompt, or False if the user hit 'Cancel' """ return tornado.gen.Task(self.handler.get_user_input, message) def instruct(self, message): """Presents the user with an instruction. Returns a future which must be yielded. This will trigger an overlay in the host browser window which can be used to tell the user to perform an action or to input some manual data for us to work on. Sample usage:: answer = yield prompt("What's the meaning of life?") assert answer == 42 This function is a simple wrapper for ``tornado.gen.Task``, and is equivalent to the usage of that. :param message: The instruction you want to give the user :returns: A generator which must be yielded. Once yielded, the reutrn value will be either True if they succeeded or False if they did not. """ return tornado.gen.Task(self.handler.instruct_user, message)
class Runner(object): _INPUT_NONE = '' _INPUT_EXIT_COMMAND = 'exit' _INPUT_MULTIPLE_LINE = ' \\' _INPUT_COMMAND_PREFIX = ':' _INPUT_SYSTEM_APP_KEYWORD = 'system' _KEYBOARD_FRAME_NAME = 'app://keyboard.gaiamobile.org/index.html' _is_async = False _sync_prompt = '>>> ' _async_prompt = 'a>> ' _prompt = _sync_prompt def __init__(self, **kwargs): # Added parser parser = OptionParser() parser.add_option( '-a', '--address', action='store', type='string', dest='address', default='localhost', help='The host address of b2g instance. Default=localhost') parser.add_option('-p', '--port', action='store', type='int', dest='port', default=2828, help='The port of b2g instance.') parser.add_option('-l', '--list', action='store_true', dest='enable_list', default='False', help='List all apps of b2g instance. Default=False') parser.add_option('-c', '--connect', action='store', type='string', dest='connect', default=None, help='Connect to the App iframe.' \ 'Use # ID or App URL to connect.') (options, args) = parser.parse_args() self.connect = options.connect # start marionette session self.m = Marionette(options.address, options.port) self.m.start_session() # list all iframes if options.enable_list: self.list_all_iframes() # list active iframes elif self.connect == None: self.list_active_iframes() # connect to App if self.connect == None: exit(0) else: # connect App print 'Start...' if self.open_app(self.connect): self.start_js() else: exit(-1) def switch_sync_async(self): self._is_async = not self._is_async self._prompt = self._async_prompt if self._is_async else self._sync_prompt print 'Swith to', ('Async' if self._is_async else 'Sync'), 'JS execution' def start_js(self): print 'Enter \'%s\' or Crtl+D to exit the shell.' % self._INPUT_EXIT_COMMAND print 'And enter \':h\' for more commands.' try: while True: input = raw_input(self._prompt) # if input is EXIT command, exit this program if input.lower() == self._INPUT_EXIT_COMMAND: self.goodbye() break # if input is NONE, then do nothing and keep going... elif input == self._INPUT_NONE: continue # if input start with ":", it should be the command. elif input.startswith(self._INPUT_COMMAND_PREFIX): CmdDispatcher(self, input) # if the postfix of input is MULTIPLE_LINE, then record this line and wait the next line until no input with MULTIPLE_LINE. elif input.endswith(self._INPUT_MULTIPLE_LINE): input_multiple = input[:len(input) - 1] + '; ' while True: next_input = raw_input('... ') if next_input.endswith(self._INPUT_MULTIPLE_LINE): input_multiple = input_multiple + next_input[:len( next_input) - 1] + '; ' pass else: input_multiple = input_multiple + next_input + '; ' print self.execute_script(input_multiple) break # if input is NOT above inputs, then run marionette.execute_script(INPUT)/marionette.execute_async_script(INPUT) # and print return value. else: print self.execute_script(input) except EOFError: self.goodbye() exit() except Exception as e: print e if self.open_app(-1): self.start_js() exit() def execute_script(self, script): try: if self._is_async: return self.m.execute_async_script(script) else: return self.m.execute_script(script) except Exception as ex: print str(ex.message) def goodbye(self): print 'End. Bye!!' def open_app(self, input): try: # connect App by ID app_id = int(input) if app_id < 0: print 'Connect to', self._get_system_URL() self.set_current_frame(self._get_system_URL()) self.m.switch_to_frame() else: iframes = self.get_all_iframes_id_name_pair() print 'Connect to', iframes[str(app_id)] self.set_current_frame(iframes[str(app_id)]) if iframes[str(app_id)] == self._KEYBOARD_FRAME_NAME: self.m.switch_to_frame(app_id, False) else: self.m.switch_to_frame(app_id) return True except (ValueError): # connect to System app if (input.lower() == self._INPUT_SYSTEM_APP_KEYWORD) or ( input.lower() in self._get_system_URL()): print 'Connect to', self._get_system_URL() self.set_current_frame(self._get_system_URL()) self.m.switch_to_frame() return True # connect App by substring iframes = self.get_all_iframes_id_name_pair() suitable_iframes = {} for k, v in iframes.items(): if input in v: suitable_iframes[k] = v # connect to App if there is one fit if len(suitable_iframes) == 1: target = suitable_iframes.keys()[0] print 'Connect to', suitable_iframes.values()[0] self.set_current_frame(suitable_iframes.values()[0]) if suitable_iframes.values()[0] == self._KEYBOARD_FRAME_NAME: self.m.switch_to_frame(int(target), False) else: self.m.switch_to_frame(int(target)) return True # exit if there are more than one app fit the query elif len(suitable_iframes) > 1: print 'There are more than one Apps fit the query string [', input, '].' print '{0:2s} {1:s}'.format('#', 'App URL') for k, v in sorted(suitable_iframes.items()): print '{0:2s} {1:s}'.format(k, v) return False # exit if there is no app fit the query else: print 'There is no App fit the query string [', input, '].' return False def set_current_frame(self, frame_name): self._current_frame = frame_name def get_current_frame(self): return self._current_frame def switch_to_system_frame(self): self.m.switch_to_frame() def _get_system_URL(self): self.switch_to_system_frame() system_URL = self.m.execute_script('return document.URL') return system_URL def _get_all_iframes(self): self.switch_to_system_frame() iframes = self.m.execute_script( 'return document.getElementsByTagName("iframe")') return iframes def get_all_iframes_id_name_pair(self): iframes = self._get_all_iframes() result = {} for idx in range(0, iframes['length']): iframe = iframes[str(idx)] result[str(idx)] = iframe.get_attribute('src') return result def list_all_iframes(self): iframes = self._get_all_iframes() print '{0:2s} {1:7s} {2:s}'.format('#', 'Status', 'App URL') print '{0:2s} {1:7s} {2:s}'.format('-1', '', self._get_system_URL()) for idx in range(0, iframes['length']): iframe = iframes[str(idx)] print '{0:2s} {1:7s} {2:s}'.format( str(idx), ('active' if iframe.is_displayed() else ''), iframe.get_attribute('src')) def list_active_iframes(self): iframes = self._get_all_iframes() print '{0:2s} {1:7s} {2:s}'.format('#', 'Status', 'App URL') result = {} for idx in range(0, iframes['length']): iframe = iframes[str(idx)] if iframe.is_displayed(): result[str(idx)] = iframe for idx, iframe in result.items(): print '{0:2s} {1:7s} {2:s}'.format( idx, ('active' if iframe.is_displayed() else ''), iframe.get_attribute('src')) # TODO: seems like should using Javascript to get the idx of each iframe!? def get_all_iframes_by_marionette(self): iframes = self.m.find_elements('css selector', 'iframe') return iframes def list_all_iframes_by_marionette(self): iframes = self.get_all_iframes_by_marionette() for iframe in iframes: print iframe.get_attribute('src') def list_active_iframes_by_marionette(self): iframes = self.get_all_iframes_by_marionette() result = [] for iframe in iframes: if iframe.is_displayed(): result.append(iframe) for iframe in result: print iframe.get_attribute('src')
assert(m.get_windows() == server.TEST_WINDOW_LIST) assert(m.switch_to_window('window2')) assert(m.window == 'window2') assert(m.close_window('window2')) assert(m.set_script_timeout(1000)) assert(m.set_search_timeout(500)) assert(m.get_url() == server.TEST_URL) assert(m.navigate(server.TEST_URL)) assert(m.go_back()) assert(m.go_forward()) assert(m.refresh()) assert(m.execute_script(server.TEST_EXECUTE_SCRIPT)) assert(m.execute_js_script(server.TEST_EXECUTE_SCRIPT)) assert(m.execute_js_script(server.TEST_EXECUTE_SCRIPT, server.TEST_EXECUTE_SCRIPT_ARGS)) assert(m.execute_script(server.TEST_EXECUTE_SCRIPT, server.TEST_EXECUTE_SCRIPT_ARGS)) assert(m.execute_async_script(server.TEST_EXECUTE_SCRIPT)) assert(m.execute_async_script(server.TEST_EXECUTE_SCRIPT, server.TEST_EXECUTE_SCRIPT_ARGS)) assert(str(m.find_element(HTMLElement.CLASS, 'heading')) == server.TEST_FIND_ELEMENT) assert([str(x) for x in m.find_elements(HTMLElement.TAG, 'p')] == server.TEST_FIND_ELEMENTS) assert(str(m.find_element(HTMLElement.CLASS, 'heading').find_element(HTMLElement.TAG, 'h1')) == server.TEST_FIND_ELEMENT) assert([str(x) for x in m.find_element(HTMLElement.ID, 'div1').find_elements(HTMLElement.SELECTOR, '.main')] == \ server.TEST_FIND_ELEMENTS) assert(m.find_element(HTMLElement.ID, 'id1').click()) assert(m.find_element(HTMLElement.ID, 'id2').text() == server.TEST_GET_TEXT) assert(m.find_element(HTMLElement.ID, 'id3').send_keys('Mozilla Firefox')) assert(m.find_element(HTMLElement.ID, 'id3').value() == server.TEST_GET_VALUE) assert(m.find_element(HTMLElement.ID, 'id3').clear()) assert(m.find_element(HTMLElement.ID, 'id3').selected()) assert(m.find_element(HTMLElement.ID, 'id1').equals(m.find_element(HTMLElement.TAG, 'p'))) assert(m.find_element(HTMLElement.ID, 'id3').enabled()) assert(m.find_element(HTMLElement.ID, 'id3').displayed())
class Runner(object): _INPUT_NONE = '' _INPUT_EXIT_COMMAND = 'exit' _INPUT_MULTIPLE_LINE = ' \\' _INPUT_COMMAND_PREFIX = ':' _INPUT_SYSTEM_APP_KEYWORD = 'system' _is_async = False _sync_prompt = '>>> ' _async_prompt = 'a>> ' _prompt = _sync_prompt def __init__(self, **kwargs): # Added parser parser = OptionParser() parser.add_option('-a', '--address', action='store', type='string', dest='address', default='localhost', help='The host address of b2g instance. Default=localhost') parser.add_option('-p', '--port', action='store', type='int', dest='port', default=2828, help='The port of b2g instance.') parser.add_option('-l', '--list', action='store_true', dest='enable_list', default='False', help='List all apps of b2g instance. Default=False') parser.add_option('-c', '--connect', action='store', type='string', dest='connect', default=None, help='Connect to the App iframe.' \ 'Use # ID or App URL to connect.') (options, args) = parser.parse_args() self.connect = options.connect # start marionette session self.m = Marionette(options.address, options.port) self.m.start_session() # list all iframes if options.enable_list: self.list_all_iframes() # list active iframes elif self.connect == None: self.list_active_iframes() # connect to App if self.connect == None: exit(0) else: # connect App print 'Start...' if self.open_app(self.connect): self.start_js() else: exit(-1) def switch_sync_async(self): self._is_async = not self._is_async self._prompt = self._async_prompt if self._is_async else self._sync_prompt print 'Swith to', ('Async' if self._is_async else 'Sync'), 'JS execution' def start_js(self): print 'Enter \'%s\' or Crtl+D to exit the shell.' % self._INPUT_EXIT_COMMAND print 'And enter \':h\' for more commands.' try: while True: input = raw_input(self._prompt) # if input is EXIT command, exit this program if input.lower() == self._INPUT_EXIT_COMMAND: self.goodbye() break # if input is NONE, then do nothing and keep going... elif input == self._INPUT_NONE: continue # if input start with ":", it should be the command. elif input.startswith(self._INPUT_COMMAND_PREFIX): CmdDispatcher(self, input) # if the postfix of input is MULTIPLE_LINE, then record this line and wait the next line until no input with MULTIPLE_LINE. elif input.endswith(self._INPUT_MULTIPLE_LINE): input_multiple = input[:len(input) - 1] + '; ' while True: next_input = raw_input('... ') if next_input.endswith(self._INPUT_MULTIPLE_LINE): input_multiple = input_multiple + next_input[:len(next_input) - 1] + '; ' pass else: input_multiple = input_multiple + next_input + '; ' print self.execute_script(input_multiple) break # if input is NOT above inputs, then run marionette.execute_script(INPUT)/marionette.execute_async_script(INPUT) # and print return value. else: print self.execute_script(input) except EOFError: self.goodbye() exit() except Exception as e: print e if self.open_app(-1): self.start_js() exit() def execute_script(self, script): try: if self._is_async: return self.m.execute_async_script(script) else: return self.m.execute_script(script) except Exception as ex: print str(ex.message) def goodbye(self): print 'End. Bye!!' def open_app(self, input): try: # connect App by ID app_id = int(input) if app_id < 0: print 'Connect to', self._get_system_URL() self.set_current_frame(self._get_system_URL()) self.m.switch_to_frame() else: iframes = self.get_all_iframes_id_name_pair() print 'Connect to', iframes[str(app_id)] self.set_current_frame(iframes[str(app_id)]) self.m.switch_to_frame(app_id) return True except(ValueError): # connect to System app if (input.lower() == self._INPUT_SYSTEM_APP_KEYWORD) or (input.lower() in self._get_system_URL()): print 'Connect to', self._get_system_URL() self.set_current_frame(self._get_system_URL()) self.m.switch_to_frame() return True # connect App by substring iframes = self.get_all_iframes_id_name_pair() suitable_iframes = {} for k, v in iframes.items(): if input in v: suitable_iframes[k] = v # connect to App if there is one fit if len(suitable_iframes) == 1: target = suitable_iframes.keys()[0] print 'Connect to', suitable_iframes.values()[0] self.set_current_frame(suitable_iframes.values()[0]) self.m.switch_to_frame(int(target)) return True # exit if there are more than one app fit the query elif len(suitable_iframes) > 1: print 'There are more than one Apps fit the query string [', input, '].' print '{0:2s} {1:s}'.format('#', 'App URL') for k, v in sorted(suitable_iframes.items()): print '{0:2s} {1:s}'.format(k, v) return False # exit if there is no app fit the query else: print 'There is no App fit the query string [', input, '].' return False def set_current_frame(self, frame_name): self._current_frame = frame_name def get_current_frame(self): return self._current_frame def switch_to_system_frame(self): self.m.switch_to_frame() def _get_system_URL(self): self.switch_to_system_frame() system_URL = self.m.execute_script('return document.URL') return system_URL def _get_all_iframes(self): self.switch_to_system_frame() iframes = self.m.execute_script('return document.getElementsByTagName("iframe")') return iframes def get_all_iframes_id_name_pair(self): iframes = self._get_all_iframes() result = {} for idx in range(0, iframes['length']): iframe = iframes[str(idx)] result[str(idx)] = iframe.get_attribute('src') return result def list_all_iframes(self): iframes = self._get_all_iframes() print '{0:2s} {1:7s} {2:s}'.format('#', 'Status', 'App URL') print '{0:2s} {1:7s} {2:s}'.format('-1', '', self._get_system_URL()) for idx in range(0, iframes['length']): iframe = iframes[str(idx)] print '{0:2s} {1:7s} {2:s}'.format(str(idx), ('active' if iframe.is_displayed() else ''), iframe.get_attribute('src')) def list_active_iframes(self): iframes = self._get_all_iframes() print '{0:2s} {1:7s} {2:s}'.format('#', 'Status', 'App URL') result = {} for idx in range(0, iframes['length']): iframe = iframes[str(idx)] if iframe.is_displayed(): result[str(idx)] = iframe for idx, iframe in result.items(): print '{0:2s} {1:7s} {2:s}'.format(idx, ('active' if iframe.is_displayed() else ''), iframe.get_attribute('src')) # TODO: seems like should using Javascript to get the idx of each iframe!? def get_all_iframes_by_marionette(self): iframes = self.m.find_elements('css selector', 'iframe') return iframes def list_all_iframes_by_marionette(self): iframes = self.get_all_iframes_by_marionette() for iframe in iframes: print iframe.get_attribute('src') def list_active_iframes_by_marionette(self): iframes = self.get_all_iframes_by_marionette() result = [] for iframe in iframes: if iframe.is_displayed(): result.append(iframe) for iframe in result: print iframe.get_attribute('src')
class TestRun(object): def __init__(self, adb="adb", serial=None): self.test_results = {} self.test_results_file = None self.m = None self.gaia_apps = None self.screenshot_path = None self.logcat_path = None self.app_name = None self.attempt = None self.num_apps = None self.device = None self.serial = serial self.port = None self.run_log = logging.getLogger('marketplace-test') if self.serial: self.dm = DeviceManagerADB(adbPath=adb, deviceSerial=serial) else: self.dm = DeviceManagerADB(adbPath=adb) def reset_marionette(self): try: self.m.delete_session() except Exception: pass self.m = None self.get_marionette() def get_marionette(self): if not self.m: self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) else: tries = 5 while tries > 0: try: self.m.get_url() break except MarionetteException as e: if "Please start a session" in str(e): time.sleep(5) self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) tries -= 1 else: raise e else: self.run_log.error("Can't connect to marionette, rebooting") self.restart_device() return self.m def write_to_file(self, data): with open("%s.tmp" % self.test_results_file, "w") as f: f.write(data) shutil.copyfile("%s.tmp" % self.test_results_file, self.test_results_file) def add_values(self, key, value): if self.serial: self.test_results["%s_%s" % (key, self.serial)] = value else: self.test_results["%s" % key] = value def add_result(self, passed=False, status=None, uninstalled_failure=False): values = {} if status: if not passed: values["status"] = "FAILED: %s" % status else: values["status"] = "PASS" if self.screenshot_path: values["screenshot"] = self.screenshot_path if self.logcat_path: values["logcat"] = self.logcat_path if uninstalled_failure: values["uninstalled_failure"] = uninstalled_failure entry = "%s_%s" % (self.app_name, self.attempt) self.test_results[entry] = values def launch_with_manifest(self, manifest): self.m.switch_to_frame() result = self.m.execute_async_script("GaiaApps.launchWithManifestURL('%s')" % manifest, script_timeout=30000) if result == False: raise Exception("launch timed out") app = GaiaApp(frame=result.get('frame'), src=result.get('src'), name=result.get('name'), origin=result.get('origin')) if app.frame_id is None: raise Exception("failed to launch; there is no app frame") self.m.switch_to_frame(app.frame_id) return app def uninstall_with_manifest(self, manifest): self.m.switch_to_frame() script = """ GaiaApps.locateWithManifestURL('%s', null, function uninstall(app) { navigator.mozApps.mgmt.uninstall(app); marionetteScriptFinished(true); }); """ result = self.m.execute_async_script(script % manifest, script_timeout=60000) if result != True: self.add_result(status="Failed to uninstall app with url '%s'" % manifest) return app def forward_port(self): # get unused port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('localhost', 0)) addr, port = s.getsockname() s.close() dm_tries = 0 self.run_log.info("using port %s" % port) while dm_tries < 20: if self.dm.forward("tcp:%d" % port, "tcp:2828") == 0: break dm_tries += 1 time.sleep(3) else: return False self.port = port return True def restart_device(self, restart_tries=0): self.run_log.info("rebooting") # TODO restarting b2g doesn't seem to work... reboot then while restart_tries < 3: restart_tries += 1 self.dm.reboot(wait=True) self.run_log.info("forwarding") if not self.forward_port(): self.run_log.error("couldn't forward port in time, rebooting") continue self.m = Marionette(port=self.port) if not self.m.wait_for_port(180): self.run_log.error("couldn't contact marionette in time, rebooting") continue time.sleep(1) self.m.start_session() try: Wait(self.m, timeout=240).until(lambda m: m.find_element("id", "lockscreen-container").is_displayed()) # It retuns a little early time.sleep(2) self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.device.unlock() self.gaia_apps = GaiaApps(self.m) except (MarionetteException, IOError, socket.error) as e: self.run_log.error("got exception: %s, going to retry" % e) try: self.m.delete_session() except: # at least attempt to clear the session if possible pass continue break else: raise Exception("Couldn't restart the device in time, even after 3 tries") def readystate_wait(self, app): try: Wait(self.get_marionette(), timeout=30).until(lambda m: m.execute_script("return window.document.readyState;") == "complete") except ScriptTimeoutException as e: return False return True def record_icons(self): self.device.touch_home_button() icons = self.m.find_elements("class name", "icon") self.num_apps = len(icons) def check_if_app_installed(self, timeout=180): # TODO: Find a better way to do this than checking homescreen # I hope there is one... self.device.touch_home_button() icons = self.m.find_elements("class name", "icon") start = time.time() end = start + 180 found_icon = None claims_its_loaded = 0 # this is used in case 'loading' isn't applied immediately to the icon while time.time() < end: if not found_icon: icons = self.m.find_elements("class name", "icon") # We can't do set comparison b/c references change if len(icons) > self.num_apps: for icon in icons: if "loading" in icon.get_attribute("innerHTML"): found_icon = icon break else: claims_its_loaded += 1 if claims_its_loaded == 3: return True else: if "loading" not in found_icon.get_attribute("innerHTML"): return True time.sleep(2) return False
waitFor( function() { window.wrappedJSObject.LockScreen.unlock(); waitFor( function() { finish(window.wrappedJSObject.LockScreen.locked); }, function() { return !window.wrappedJSObject.LockScreen.locked; } ); }, function() { return !!window.wrappedJSObject.LockScreen; } ); """ from marionette import Marionette marionette = Marionette('localhost', 2828) marionette.start_session() marionette.import_script('gaia_apps.js') marionette.set_script_timeout(60000) marionette.execute_script(unlock) result = marionette.execute_async_script("GaiaApps.launchWithName('%s')" % "MozCampAsia-QualityApps") marionette.switch_to_frame(result.get("frame")) pdb.set_trace()
import SimpleHTTPServer import SocketServer from marionette import Marionette m = Marionette('localhost', 2828) m.start_session() script = """ var request = navigator.mozApps.install('http://localhost:3000/package.manifest'); request.onsuccess = function () { marionetteScriptFinished('YAY'); }; request.onerror = function(e) { marionetteScriptFinished('Boooo :( ' + this.error.name); };""" print m.execute_async_script(script) # Out[69]: u'Boooo :( INVALID_MANIFEST'