Bug 1280947 - Dispatch DOM change event on appending file to input; r?automatedtester
MozReview-Commit-ID: 6SC01AEkuTs
new file mode 100644
--- /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.<string>}
+ * 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();
+ });
+ });
+};
new file mode 100644
--- /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
--- a/testing/marionette/harness/marionette/tests/unit/test_file_upload.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_file_upload.py
@@ -100,16 +100,32 @@ class TestFileUpload(MarionetteTestCase)
f.write("camembert")
f.flush()
self.input.send_keys(f.name)
self.submit.click()
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")
@property
def input(self):
return self.find_inputs()[0]
@property
--- a/testing/marionette/interaction.js
+++ b/testing/marionette/interaction.js
@@ -217,16 +217,43 @@ interaction.selectOption = function(el)
el.selected = !el.selected;
event.change(parent);
event.mouseup(parent);
event.click(parent);
};
/**
+ * Appends |path| to an <input type=file>'s file list.
+ *
+ * @param {HTMLInputElement} el
+ * An <input type=file> 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);
+
+ // <input type=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 <select> element that encapsulate an <option> element.
*
* @param {HTMLOptionElement} optionEl
* Option element.
*
* @return {HTMLSelectElement}
* Select element wrapping |optionEl|.
*
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -590,30 +590,31 @@ function setTestName(msg) {
sendOk(msg.json.command_id);
}
/**
* Receive file objects from chrome in order to complete a
* sendKeysToElement action on a file input element.
*/
function receiveFiles(msg) {
- if ('error' in msg.json) {
+ if ("error" in msg.json) {
let err = new InvalidArgumentError(msg.json.error);
sendError(err, msg.json.command_id);
return;
}
+
if (!fileInputElement) {
let err = new InvalidElementStateError("receiveFiles called with no valid fileInputElement");
sendError(err, msg.json.command_id);
return;
}
- let fs = Array.prototype.slice.call(fileInputElement.files);
- fs.push(msg.json.file);
- fileInputElement.mozSetFileArray(fs);
+
+ interaction.uploadFile(fileInputElement, msg.json.file);
fileInputElement = null;
+
sendOk(msg.json.command_id);
}
/**
* This function creates a touch event given a touch type and a touch
*/
function emitTouchEvent(type, touch) {
if (!wasInterrupted()) {