diff --git a/testing/marionette/addon.js b/testing/marionette/addon.js new file mode 100644 index 000000000000..0c0749ddcc35 --- /dev/null +++ b/testing/marionette/addon.js @@ -0,0 +1,93 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.import("resource://gre/modules/AddonManager.jsm"); +Cu.import("resource://gre/modules/FileUtils.jsm"); + +Cu.import("chrome://marionette/content/error.js"); + +this.EXPORTED_SYMBOLS = ["addon"]; + +this.addon = {}; + +/** + * Installs Firefox addon. + * + * If the addon is restartless, it can be used right away. Otherwise a + * restart is needed. + * + * Temporary addons will automatically be unisntalled on shutdown and + * do not need to be signed, though they must be restartless. + * + * @param {string} path + * Full path to the extension package archive to be installed. + * @param {boolean=} temporary + * Install the add-on temporarily if true. + * + * @return {Promise.} + * Addon ID string of the newly installed addon. + * + * @throws {AddonError} + * if installation fails + */ +addon.install = function(path, temporary = false) { + return new Promise((resolve, reject) => { + let listener = { + onInstallEnded: function(install, addon) { + resolve(addon.id); + }, + + onInstallFailed: function(install) { + reject(new AddonError(install.error)); + }, + + onInstalled: function(addon) { + AddonManager.removeAddonListener(listener); + resolve(addon.id); + } + }; + + let file = new FileUtils.File(path); + + // temporary addons + if (temp) { + AddonManager.addAddonListener(listener); + AddonManager.installTemporaryAddon(file); + } + + // addons that require restart + else { + AddonManager.getInstallForFile(file, function(aInstall) { + if (aInstall.error != 0) { + reject(new AddonError(aInstall.error)); + } + aInstall.addListener(listener); + aInstall.install(); + }); + } + }); +}; + +/** + * Uninstall a Firefox addon. + * + * If the addon is restartless, it will be uninstalled right + * away. Otherwise a restart is necessary. + * + * @param {string} id + * Addon ID to uninstall. + * + * @return {Promise} + */ +addon.uninstall = function(id) { + return new Promise(resolve => { + AddonManager.getAddonByID(arguments[0], function(addon) { + addon.uninstall(); + }); + }); +}; diff --git a/testing/marionette/harness/marionette/tests/unit/test_certificates.py b/testing/marionette/harness/marionette/tests/unit/test_certificates.py new file mode 100644 index 000000000000..c3f90743cfa9 --- /dev/null +++ b/testing/marionette/harness/marionette/tests/unit/test_certificates.py @@ -0,0 +1,31 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from marionette import MarionetteTestCase +from marionette_driver.errors import UnknownException + + +class TestCertificates(MarionetteTestCase): + def test_block_insecure_sites(self): + self.marionette.delete_session() + self.marionette.start_session() + + self.marionette.navigate(self.fixtures.where_is("test.html", on="http")) + self.assertIn("http://", self.marionette.get_url()) + with self.assertRaises(UnknownException): + self.marionette.navigate(self.fixtures.where_is("test.html", on="https")) + + def test_accept_all_insecure(self): + self.marionette.delete_session() + self.marionette.start_session({"desiredCapability": {"acceptSslCerts": ["*"]}}) + self.marionette.navigate(self.fixtures.where_is("test.html", on="https")) + self.assertIn("https://", self.marionette.url) + + """ + def test_accept_some_insecure(self): + self.marionette.delete_session() + self.marionette.start_session({"requiredCapabilities": {"acceptSslCerts": ["127.0.0.1"]}}) + self.marionette.navigate(self.fixtures.where_is("test.html", on="https")) + self.assertIn("https://", self.marionette.url) + """ \ No newline at end of file diff --git a/testing/marionette/harness/marionette/tests/unit/test_file_upload.py b/testing/marionette/harness/marionette/tests/unit/test_file_upload.py index 2ed2c531cc33..1e82e17f82c1 100644 --- a/testing/marionette/harness/marionette/tests/unit/test_file_upload.py +++ b/testing/marionette/harness/marionette/tests/unit/test_file_upload.py @@ -105,6 +105,22 @@ class TestFileUpload(MarionetteTestCase): Wait(self.marionette).until(lambda m: m.get_url() != url) self.assertIn("multipart/form-data", self.body.text) + def test_change_event(self): + self.marionette.navigate(single) + self.marionette.execute_script(""" + window.changeEvs = []; + let el = arguments[arguments.length - 1]; + el.addEventListener("change", ev => window.changeEvs.push(ev)); + console.log(window.changeEvs.length); + """, script_args=(self.input,), sandbox=None) + + with tempfile() as f: + self.input.send_keys(f.name) + + nevs = self.marionette.execute_script( + "return window.changeEvs.length", sandbox=None) + self.assertEqual(1, nevs) + def find_inputs(self): return self.marionette.find_elements(By.TAG_NAME, "input") diff --git a/testing/marionette/interaction.js b/testing/marionette/interaction.js index 1080a42183a2..52ca196e11e4 100644 --- a/testing/marionette/interaction.js +++ b/testing/marionette/interaction.js @@ -221,6 +221,33 @@ interaction.selectOption = function(el) { event.click(parent); }; +/** + * Appends |path| to an 's file list. + * + * @param {HTMLInputElement} el + * An element. + * @param {File} file + * File object to assign to |el|. + */ +interaction.uploadFile = function(el, file) { + let fs = Array.prototype.slice.call(el.files); + fs.push(file); + + // opens OS widget dialogue + // which means the mousedown/focus/mouseup/click events + // occur before the change event + event.mouseover(el); + event.mousemove(el); + event.mousedown(el); + event.focus(el); + event.mouseup(el); + event.click(el); + + el.mozSetFileArray(fs); + + event.change(el); +}; + /** * Locate the