Bug 1280947 - Dispatch DOM change event on appending file to input; r=automatedtester

MozReview-Commit-ID: 6SC01AEkuTs

--HG--
extra : rebase_source : ee4ed323e024bf6fdcf9a28ab7f44763c7475c8c
This commit is contained in:
Andreas Tolfsen 2016-08-25 14:18:04 +01:00
parent e213d0d9c5
commit 0f34b42611
5 changed files with 172 additions and 4 deletions

View File

@ -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();
});
});
};

View File

@ -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)
"""

View File

@ -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")

View File

@ -221,6 +221,33 @@ interaction.selectOption = function(el) {
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.
*

View File

@ -595,20 +595,21 @@ function setTestName(msg) {
* 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);
}