mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-21 10:17:58 +01:00
Merge default.
--HG-- branch : key-processing
This commit is contained in:
@@ -36,7 +36,7 @@ XPI_BINS = $(JAR_BINS)
|
|||||||
|
|
||||||
XPI_NAME = $(NAME)-$(VERSION)
|
XPI_NAME = $(NAME)-$(VERSION)
|
||||||
XPI = ../downloads/$(XPI_NAME).xpi
|
XPI = ../downloads/$(XPI_NAME).xpi
|
||||||
XPI_PATH = $(TOP)$(XPI:%.xpi=%)
|
XPI_PATH = $(TOP)/$(XPI:%.xpi=%)
|
||||||
|
|
||||||
RDF = ../downloads/update.rdf
|
RDF = ../downloads/update.rdf
|
||||||
RDF_IN = $(RDF).in
|
RDF_IN = $(RDF).in
|
||||||
@@ -162,7 +162,7 @@ distclean:
|
|||||||
rm -rf $(BUILD_DIR)
|
rm -rf $(BUILD_DIR)
|
||||||
|
|
||||||
# TODO: generalize log path
|
# TODO: generalize log path
|
||||||
test: $(XPI)
|
test: xpi
|
||||||
@echo "Running functional tests..."
|
@echo "Running functional tests..."
|
||||||
$(MOZMILL) --show-all -l /tmp/dactyl-test.log -b $(HOSTAPP_PATH) --addons $(XPI) -t $(TEST_DIR)
|
$(MOZMILL) --show-all -l /tmp/dactyl-test.log -b $(HOSTAPP_PATH) --addons $(XPI) -t $(TEST_DIR)
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,9 @@ var CommandWidgets = Class("CommandWidgets", {
|
|||||||
|
|
||||||
[this.commandbar, this.statusbar].forEach(function (nodeSet) {
|
[this.commandbar, this.statusbar].forEach(function (nodeSet) {
|
||||||
let elem = nodeSet[obj.name];
|
let elem = nodeSet[obj.name];
|
||||||
if (val != null) {
|
if (val == null)
|
||||||
|
elem.value = "";
|
||||||
|
else {
|
||||||
highlight.highlightNode(elem,
|
highlight.highlightNode(elem,
|
||||||
(val[0] != null ? val[0] : obj.defaultGroup)
|
(val[0] != null ? val[0] : obj.defaultGroup)
|
||||||
.split(/\s/).filter(util.identity)
|
.split(/\s/).filter(util.identity)
|
||||||
|
|||||||
@@ -159,7 +159,18 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
if (type in this._observers)
|
if (type in this._observers)
|
||||||
this._observers[type] = this._observers[type].filter(function (callback) {
|
this._observers[type] = this._observers[type].filter(function (callback) {
|
||||||
if (callback.get()) {
|
if (callback.get()) {
|
||||||
callback.get().apply(null, args);
|
try {
|
||||||
|
try {
|
||||||
|
callback.get().apply(null, args);
|
||||||
|
}
|
||||||
|
catch (e if e.message == "can't wrap XML objects") {
|
||||||
|
// Horrible kludge.
|
||||||
|
callback.get().apply(null, [String(args[0])].concat(args.slice(1)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
dactyl.reportError(e);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -211,6 +222,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
* 'visualbell' option.
|
* 'visualbell' option.
|
||||||
*/
|
*/
|
||||||
beep: function () {
|
beep: function () {
|
||||||
|
this.triggerObserver("beep");
|
||||||
if (options["visualbell"]) {
|
if (options["visualbell"]) {
|
||||||
let elems = {
|
let elems = {
|
||||||
bell: document.getElementById("dactyl-bell"),
|
bell: document.getElementById("dactyl-bell"),
|
||||||
@@ -1091,6 +1103,12 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onExecute: function onExecute(event) {
|
||||||
|
let cmd = event.originalTarget.getAttribute("dactyl-execute");
|
||||||
|
commands.execute(cmd, null, false, null,
|
||||||
|
{ file: "[Command Line]", line: 1 });
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens one or more URLs. Returns true when load was initiated, or
|
* Opens one or more URLs. Returns true when load was initiated, or
|
||||||
* false on error.
|
* false on error.
|
||||||
@@ -1337,9 +1355,13 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
*/
|
*/
|
||||||
reportError: function reportError(error, echo) {
|
reportError: function reportError(error, echo) {
|
||||||
if (error instanceof FailedAssertion || error.message === "Interrupted") {
|
if (error instanceof FailedAssertion || error.message === "Interrupted") {
|
||||||
|
|
||||||
let prefix = io.sourcing ? io.sourcing.file + ":" + io.sourcing.line + ": " : "";
|
let prefix = io.sourcing ? io.sourcing.file + ":" + io.sourcing.line + ": " : "";
|
||||||
|
if (error.message && error.message.indexOf(prefix) !== 0)
|
||||||
|
error.message = prefix + error.message;
|
||||||
|
|
||||||
if (error.message)
|
if (error.message)
|
||||||
dactyl.echoerr(template.linkifyHelp(prefix + error.message));
|
dactyl.echoerr(template.linkifyHelp(error.message));
|
||||||
else
|
else
|
||||||
dactyl.beep();
|
dactyl.beep();
|
||||||
return;
|
return;
|
||||||
@@ -1434,6 +1456,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
}, {
|
}, {
|
||||||
events: function () {
|
events: function () {
|
||||||
events.addSessionListener(window, "click", dactyl.closure.onClick, true);
|
events.addSessionListener(window, "click", dactyl.closure.onClick, true);
|
||||||
|
events.addSessionListener(window, "dactyl.execute", dactyl.closure.onExecute, true);
|
||||||
},
|
},
|
||||||
// Only general options are added here, which are valid for all Dactyl extensions
|
// Only general options are added here, which are valid for all Dactyl extensions
|
||||||
options: function () {
|
options: function () {
|
||||||
@@ -1676,7 +1699,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
function (args) {
|
function (args) {
|
||||||
try {
|
try {
|
||||||
let cmd = dactyl.userEval(args[0] || "");
|
let cmd = dactyl.userEval(args[0] || "");
|
||||||
dactyl.execute(cmd, null, true);
|
dactyl.execute(cmd || "", null, true);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
dactyl.echoerr(e);
|
dactyl.echoerr(e);
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ var AddonList = Class("AddonList", {
|
|||||||
if (addon && addon.id in this.addons)
|
if (addon && addon.id in this.addons)
|
||||||
this.addons[addon.id].update();
|
this.addons[addon.id].update();
|
||||||
if (this.ready)
|
if (this.ready)
|
||||||
this.modules.mow.resize(false);
|
this.modules.commandline.updateOutputHeight(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
onDisabled: function (addon) { this.update(addon); },
|
onDisabled: function (addon) { this.update(addon); },
|
||||||
|
|||||||
@@ -658,7 +658,7 @@ function Class() {
|
|||||||
|
|
||||||
var Constructor = eval(String.replace(<![CDATA[
|
var Constructor = eval(String.replace(<![CDATA[
|
||||||
(function constructor() {
|
(function constructor() {
|
||||||
let self = Object.create(Constructor.prototype, {
|
var self = Object.create(Constructor.prototype, {
|
||||||
constructor: { value: Constructor },
|
constructor: { value: Constructor },
|
||||||
});
|
});
|
||||||
self.instance = self;
|
self.instance = self;
|
||||||
|
|||||||
@@ -868,10 +868,15 @@ var Completion = Module("completion", {
|
|||||||
context = context.contexts["/list"];
|
context = context.contexts["/list"];
|
||||||
context.wait();
|
context.wait();
|
||||||
|
|
||||||
|
let contexts = context.contextList.filter(function (c) c.hasItems && c.items.length);
|
||||||
|
if (!contexts.length)
|
||||||
|
contexts = context.contextList.filter(function (c) c.hasItems).slice(0, 1);
|
||||||
|
if (!contexts.length)
|
||||||
|
contexts = context.contextList.slice(-1);
|
||||||
|
|
||||||
modules.commandline.commandOutput(
|
modules.commandline.commandOutput(
|
||||||
<div highlight="Completions">
|
<div highlight="Completions">
|
||||||
{ template.map(context.contextList.filter(function (c) c.hasItems && c.items.length),
|
{ template.map(contexts, function (context)
|
||||||
function (context)
|
|
||||||
template.completionRow(context.title, "CompTitle") +
|
template.completionRow(context.title, "CompTitle") +
|
||||||
template.map(context.items, function (item) context.createRow(item), null, 100)) }
|
template.map(context.items, function (item) context.createRow(item), null, 100)) }
|
||||||
</div>);
|
</div>);
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ var DownloadList = Class("DownloadList",
|
|||||||
else {
|
else {
|
||||||
this.addDownload(download.id);
|
this.addDownload(download.id);
|
||||||
|
|
||||||
this.modules.mow.resize(false);
|
this.modules.commandline.updateOutputHeight(false);
|
||||||
this.nodes.list.scrollIntoView(false);
|
this.nodes.list.scrollIntoView(false);
|
||||||
}
|
}
|
||||||
this.update();
|
this.update();
|
||||||
|
|||||||
@@ -1344,13 +1344,16 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
maxErrors: 15,
|
errorCount: 0,
|
||||||
errors: Class.memoize(function () []),
|
errors: Class.memoize(function () []),
|
||||||
|
maxErrors: 15,
|
||||||
reportError: function (error) {
|
reportError: function (error) {
|
||||||
if (Cu.reportError)
|
if (Cu.reportError)
|
||||||
Cu.reportError(error);
|
Cu.reportError(error);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.errorCount++;
|
||||||
|
|
||||||
let obj = update({}, error, {
|
let obj = update({}, error, {
|
||||||
toString: function () String(error),
|
toString: function () String(error),
|
||||||
stack: <>{util.stackLines(String(error.stack || Error().stack)).join("\n").replace(/^/mg, "\t")}</>
|
stack: <>{util.stackLines(String(error.stack || Error().stack)).join("\n").replace(/^/mg, "\t")}</>
|
||||||
|
|||||||
437
common/tests/functional/dactyl.js
Normal file
437
common/tests/functional/dactyl.js
Normal file
@@ -0,0 +1,437 @@
|
|||||||
|
|
||||||
|
var utils = require("utils");
|
||||||
|
const { module, NS } = utils;
|
||||||
|
|
||||||
|
var elementslib = module("resource://mozmill/modules/elementslib.js");
|
||||||
|
var frame = module("resource://mozmill/modules/frame.js");
|
||||||
|
var jumlib = module("resource://mozmill/modules/jum.js");
|
||||||
|
|
||||||
|
function wrapAssertNoErrors(func, message) {
|
||||||
|
return function wrapped(arg) this.assertNoErrors(func, this, arguments, message || arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertMessage(funcName, want, got, message) {
|
||||||
|
if (typeof want === "string")
|
||||||
|
return utils.assertEqual(funcName, want, got, message);
|
||||||
|
else if (typeof want === "function")
|
||||||
|
return utils.test(want(got), {
|
||||||
|
function: funcName,
|
||||||
|
want: want, got: got,
|
||||||
|
comment: message
|
||||||
|
});
|
||||||
|
else
|
||||||
|
return utils.test(want.test(got), {
|
||||||
|
function: funcName,
|
||||||
|
want: want, got: got,
|
||||||
|
comment: message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A controller for simulating Dactyl related user actions and for making
|
||||||
|
* assertions about the expected outcomes of such actions.
|
||||||
|
*
|
||||||
|
* @param {MozMillController} controller The browser's MozMill controller.
|
||||||
|
*/
|
||||||
|
function Controller(controller) {
|
||||||
|
var self = this;
|
||||||
|
this.controller = controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property {object} The dactyl modules namespace, to be used
|
||||||
|
* sparingly in tests.
|
||||||
|
*/
|
||||||
|
this.dactyl = controller.window.dactyl.modules;
|
||||||
|
|
||||||
|
this.errorCount = 0;
|
||||||
|
|
||||||
|
this._counBeep = function countBeep() {
|
||||||
|
self.beepCount++;
|
||||||
|
}
|
||||||
|
this._countError = function countError(message, highlight) {
|
||||||
|
if (/\bErrorMsg\b/.test(highlight))
|
||||||
|
self.errorCount++;
|
||||||
|
}
|
||||||
|
this.dactyl.dactyl.registerObserver("beep", this._countBeep);
|
||||||
|
this.dactyl.dactyl.registerObserver("echoLine", this._countError);
|
||||||
|
this.dactyl.dactyl.registerObserver("echoMultiline", this._countError);
|
||||||
|
|
||||||
|
this.resetErrorCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.prototype = {
|
||||||
|
|
||||||
|
teardown: function () {
|
||||||
|
this.dactyl.dactyl.unregisterObserver("beep", this._countBeep);
|
||||||
|
this.dactyl.dactyl.unregisterObserver("echoLine", this._countError);
|
||||||
|
this.dactyl.dactyl.unregisterObserver("echoMultiline", this._countError);
|
||||||
|
},
|
||||||
|
|
||||||
|
beepCount: 0,
|
||||||
|
errorCount: 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that an error message is displayed during the execution
|
||||||
|
* of *func*.
|
||||||
|
*
|
||||||
|
* @param {function} func A function to call during before the
|
||||||
|
* assertion takes place.
|
||||||
|
* @param {object} self The 'this' object to be used during the call
|
||||||
|
* of *func*. @optional
|
||||||
|
* @param {Array} args Arguments to be passed to *func*. @optional
|
||||||
|
* @param {string} message The message to display upon assertion failure. @optional
|
||||||
|
*/
|
||||||
|
assertMessageError: function (func, self, args, message) {
|
||||||
|
let errorCount = this.errorCount;
|
||||||
|
this.assertNoErrors(func, self, args, message);
|
||||||
|
// dump("assertMessageError " + errorCount + " " + this.errorCount + "\n");
|
||||||
|
return utils.assert('dactyl.assertMessageError', this.errorCount > errorCount,
|
||||||
|
"Expected error but got none" + (message ? ": " + message : ""));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that any output message text content matches *text*.
|
||||||
|
*
|
||||||
|
* @param {string|RegExp|function} want The expected text of the expected message line.
|
||||||
|
* @param {string} message The message to display upon assertion failure.
|
||||||
|
*/
|
||||||
|
assertMessage: function (want, message) {
|
||||||
|
return assertMessage('dactyl.assertMessage', want,
|
||||||
|
this.readMessageWindow() || this.readMessageLine(),
|
||||||
|
message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the output message line text content matches *text*.
|
||||||
|
*
|
||||||
|
* @param {string|RegExp|function} want The expected text of the expected message line.
|
||||||
|
* @param {string} message The message to display upon assertion failure.
|
||||||
|
*/
|
||||||
|
assertMessageLine: function (want, message) {
|
||||||
|
return assertMessage('dactyl.assertMessageLine', want,
|
||||||
|
this.readMessageLine(),
|
||||||
|
message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the output message window text content matches *text*.
|
||||||
|
*
|
||||||
|
* @param {string|RegExp|function} want The expected text of the message window.
|
||||||
|
* @param {string} message The message to display upon assertion failure.
|
||||||
|
*/
|
||||||
|
assertMessageWindow: function (want, message) {
|
||||||
|
return assertMessage('dactyl.assertMessageWindow', want,
|
||||||
|
this.readMessageWindow(),
|
||||||
|
message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the output message line text is an error and content matches *text*.
|
||||||
|
*
|
||||||
|
* @param {string|RegExp|function} want The expected text of the expected message line.
|
||||||
|
* @param {string} message The message to display upon assertion failure.
|
||||||
|
*/
|
||||||
|
assertErrorMessage: function (want, message) {
|
||||||
|
return assertMessage('dactyl.assertMessageError', want,
|
||||||
|
this.readMessageLine(),
|
||||||
|
message) &&
|
||||||
|
assertMessage('dactyl.assertMessageError', /\bErrorMsg\b/,
|
||||||
|
this.elements.message.getAttributeNS(NS, "highlight"),
|
||||||
|
message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the multi-line output window is in the given state.
|
||||||
|
*
|
||||||
|
* @param {boolean} open True if the window is expected to be open.
|
||||||
|
* @param {string} message The message to display upon assertion failure. @optional
|
||||||
|
*/
|
||||||
|
assertMessageWindowOpen: function (open, message) {
|
||||||
|
return utils.assertEqual('dactyl.assertMessageWindowOpen', open,
|
||||||
|
!this.elements.multilineContainer.collapsed,
|
||||||
|
message || "Multi-line output not in the expected state");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the no errors have been reported since the last call
|
||||||
|
* to resetErrorCount.
|
||||||
|
*
|
||||||
|
* @param {function} func A function to call during before the
|
||||||
|
* assertion takes place. When present, the current error count
|
||||||
|
* is reset before execution.
|
||||||
|
* @optional
|
||||||
|
* @param {object} self The 'this' object to be used during the call
|
||||||
|
* of *func*. @optional
|
||||||
|
* @param {Array} args Arguments to be passed to *func*. @optional
|
||||||
|
* @param {string} message The message to display upon assertion failure. @optional
|
||||||
|
* @param {string} message The message to display upon assertion failure. @optional
|
||||||
|
*/
|
||||||
|
assertNoErrors: function (func, self, args, message) {
|
||||||
|
let msg = message ? ": " + message : "";
|
||||||
|
let beepCount = this.beepCount;
|
||||||
|
let errorCount = this.errorCount;
|
||||||
|
|
||||||
|
if (func) {
|
||||||
|
errorCount = this.dactyl.util.errorCount;
|
||||||
|
|
||||||
|
try {
|
||||||
|
func.apply(self || this, args || []);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.dactyl.util.reportError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.beepCount > beepCount)
|
||||||
|
this.frame.log({
|
||||||
|
function: "dactyl.beepMonitor",
|
||||||
|
want: beepCount, got: this.beepCount,
|
||||||
|
comment: "Got " + (this.beepCount - beepCount) + " beeps during execution" + msg
|
||||||
|
});
|
||||||
|
|
||||||
|
if (errorCount != this.dactyl.util.errorCount)
|
||||||
|
var errors = this.dactyl.util.errors.slice(errorCount - this.dactyl.util.errorCount)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
return utils.assertEqual('dactyl.assertNoErrors',
|
||||||
|
errorCount, this.dactyl.util.errorCount,
|
||||||
|
"Errors were reported during the execution of this test" + msg + "\n" + errors);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the error count used to determine whether new errors were
|
||||||
|
* reported during the execution of a test.
|
||||||
|
*/
|
||||||
|
resetErrorCount: function () {
|
||||||
|
this.errorCount = this.dactyl.util.errorCount;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the given function such that any errors triggered during
|
||||||
|
* its execution will trigger a failed assertion.
|
||||||
|
*
|
||||||
|
* @param {function} func The function to wrap.
|
||||||
|
* @param {string} message The message to display upon assertion failure. @optional
|
||||||
|
*/
|
||||||
|
wrapAssertNoErrors: function (func, message) {
|
||||||
|
let self = this;
|
||||||
|
return function wrapped() self.assertNoErrors(func, this, arguments, message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the current window selection matches *text*.
|
||||||
|
*
|
||||||
|
* @param {string|RegExp|function} text The expected text of the current selection.
|
||||||
|
* @param {string} message The message to display upon assertion failure.
|
||||||
|
*/
|
||||||
|
assertSelection: function (want, message) {
|
||||||
|
return assertMessage('dactyl.assertSelection', want,
|
||||||
|
String(this.controller.window.content.getSelection()),
|
||||||
|
message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property {string} The name of dactyl's current key handling
|
||||||
|
* mode.
|
||||||
|
*/
|
||||||
|
get currentMode() this.dactyl.modes.main.name,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property {object} A map of dactyl widgets to be used sparingly
|
||||||
|
* for focus assertions.
|
||||||
|
*/
|
||||||
|
get elements() let (self = this) ({
|
||||||
|
/**
|
||||||
|
* @property {HTMLInputElement} The command line's command input box
|
||||||
|
*/
|
||||||
|
get commandInput() self.dactyl.commandline.widgets.active.command.inputField,
|
||||||
|
/**
|
||||||
|
* @property {Node|null} The currently focused node.
|
||||||
|
*/
|
||||||
|
get focused() self.controller.window.document.commandDispatcher.focusedElement,
|
||||||
|
/**
|
||||||
|
* @property {HTMLInputElement} The message bar's command input box
|
||||||
|
*/
|
||||||
|
get message() self.dactyl.commandline.widgets.active.message,
|
||||||
|
/**
|
||||||
|
* @property {Node} The multi-line output window.
|
||||||
|
*/
|
||||||
|
get multiline() self.dactyl.commandline.widgets.multilineOutput,
|
||||||
|
/**
|
||||||
|
* @property {Node} The multi-line output container.
|
||||||
|
*/
|
||||||
|
get multilineContainer() self.dactyl.commandline.widgets.mowContainer,
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns dactyl to normal mode.
|
||||||
|
*/
|
||||||
|
setNormalMode: wrapAssertNoErrors(function () {
|
||||||
|
// XXX: Normal mode test
|
||||||
|
for (let i = 0; i < 15 && this.dactyl.modes.stack.length > 1; i++)
|
||||||
|
this.controller.keypress(null, "VK_ESCAPE", {});
|
||||||
|
|
||||||
|
this.controller.keypress(null, "l", { ctrlKey: true });
|
||||||
|
|
||||||
|
utils.assert("dactyl.setNormalMode", this.dactyl.modes.stack.length == 1,
|
||||||
|
"Failed to return to Normal mode");
|
||||||
|
|
||||||
|
this.assertMessageWindowOpen(false, "Returning to normal mode: Multi-line output not closed");
|
||||||
|
this.assertMessageLine(function (msg) !msg, "Returning to normal mode: Message not cleared");
|
||||||
|
}, "Returning to normal mode"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns dactyl to Ex mode.
|
||||||
|
*/
|
||||||
|
setExMode: wrapAssertNoErrors(function () {
|
||||||
|
if (this.currentMode !== "EX") {
|
||||||
|
this.setNormalMode();
|
||||||
|
this.controller.keypress(null, ":", {});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.elements.commandInput.value = "";
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a Vi command.
|
||||||
|
*
|
||||||
|
* @param {string|Array} keys Either a string of simple keys suitable for
|
||||||
|
* {@link MozMillController#type} or an array of keysym - modifier
|
||||||
|
* pairs suitable for {@link MozMillController#keypress}.
|
||||||
|
*/
|
||||||
|
runViCommand: wrapAssertNoErrors(function (keys) {
|
||||||
|
if (typeof keys == "string")
|
||||||
|
keys = [[k] for each (k in keys)];
|
||||||
|
keys.forEach(function ([key, modifiers]) { this.controller.keypress(null, key, modifiers || {}); }, this);
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs an Ex command.
|
||||||
|
*
|
||||||
|
* @param {string} cmd The Ex command string as entered on the command
|
||||||
|
* line.
|
||||||
|
* @param {object} args An args object by means of which to execute
|
||||||
|
* the command. If absent *cmd* is parsed as a complete
|
||||||
|
* arguments string. @optional
|
||||||
|
*/
|
||||||
|
// TODO: Use execution code from commandline.js to catch more
|
||||||
|
// possible errors without being insanely inefficient after the
|
||||||
|
// merge.
|
||||||
|
runExCommand: wrapAssertNoErrors(function (cmd, args) {
|
||||||
|
this.setNormalMode();
|
||||||
|
try {
|
||||||
|
// Force async commands to wait for their output to be ready
|
||||||
|
// before returning.
|
||||||
|
this.dactyl.commandline.savingOutput = true;
|
||||||
|
if (args)
|
||||||
|
this.dactyl.ex[cmd](args);
|
||||||
|
else if (true)
|
||||||
|
this.dactyl.commands.execute(cmd, null, false, null,
|
||||||
|
{ file: "[Command Line]", line: 1 });
|
||||||
|
else {
|
||||||
|
var doc = this.controller.window.document;
|
||||||
|
var event = doc.createEvent("Events");
|
||||||
|
event.initEvent("dactyl.execute", false, false);
|
||||||
|
doc.documentElement.setAttribute("dactyl-execute", cmd);
|
||||||
|
doc.documentElement.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
this.dactyl.commandline.savingOutput = false;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers Ex completion for the given command string and ensures
|
||||||
|
* that no errors have occurred during the process.
|
||||||
|
*
|
||||||
|
* @param {string} cmd The Ex command string as entered on the command
|
||||||
|
* line.
|
||||||
|
*/
|
||||||
|
runExCompletion: wrapAssertNoErrors(function (cmd) {
|
||||||
|
this.setExMode();
|
||||||
|
|
||||||
|
utils.assertEqual("dactyl.runExCompletion",
|
||||||
|
this.elements.commandInput,
|
||||||
|
this.elements.focused,
|
||||||
|
"Running Ex Completion: The command line is not focused");
|
||||||
|
|
||||||
|
// dump("runExCompletion " + cmd + "\n");
|
||||||
|
if (true) {
|
||||||
|
let input = this.elements.commandInput;
|
||||||
|
input.value = cmd;
|
||||||
|
|
||||||
|
var event = input.ownerDocument.createEvent("Events");
|
||||||
|
event.initEvent("change", true, false);
|
||||||
|
input.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.controller.type(null, cmd);
|
||||||
|
|
||||||
|
utils.assertEqual("dactyl.runExCompletion", cmd,
|
||||||
|
this.elements.commandInput.editor.rootElement.firstChild.textContent,
|
||||||
|
"Command line does not have the expected value: " + cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.controller.keypress(null, "VK_TAB", {});
|
||||||
|
|
||||||
|
// XXX
|
||||||
|
if (this.dactyl.commandline._tabTimer)
|
||||||
|
this.dactyl.commandline._tabTimer.flush();
|
||||||
|
else if (this.dactyl.commandline.commandSession && this.dactyl.commandline.commandSession.completions)
|
||||||
|
this.dactyl.commandline.commandSession.completions.tabTimer.flush();
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the text content of the output message line.
|
||||||
|
*
|
||||||
|
* @returns {string} The message line text content.
|
||||||
|
*/
|
||||||
|
readMessageLine: function () {
|
||||||
|
return this.elements.message.value;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the text content of the output message window.
|
||||||
|
*
|
||||||
|
* @returns {string} The message window text content.
|
||||||
|
*/
|
||||||
|
readMessageWindow: function () {
|
||||||
|
if (!this.elements.multilineContainer.collapsed)
|
||||||
|
return this.elements.multiline.contentDocument.body.textContent;
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the output message window by echoing a single newline character.
|
||||||
|
*/
|
||||||
|
openMessageWindow: wrapAssertNoErrors(function() {
|
||||||
|
this.dactyl.dactyl.echo("\n");
|
||||||
|
}, "Opening message window"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the current message.
|
||||||
|
*/
|
||||||
|
clearMessage: function() {
|
||||||
|
this.elements.message.value = ""; // XXX
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the output message window if open.
|
||||||
|
*/
|
||||||
|
closeMessageWindow: wrapAssertNoErrors(function() {
|
||||||
|
for (let i = 0; i < 15 && !this.elements.multilineContainer.collapsed; i++)
|
||||||
|
this.controller.keypress(null, "VK_ESCAPE", {});
|
||||||
|
this.assertMessageWindowOpen(false, "Clearing message window failed");
|
||||||
|
}, "Clearing message window"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property {string} The specific Dactyl application. Eg. Pentadactyl
|
||||||
|
*/
|
||||||
|
get applicationName() this.dactyl.config.appName // XXX
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Controller = Controller;
|
||||||
|
|
||||||
|
// vim: sw=4 ts=8 et:
|
||||||
78
common/tests/functional/testEchoCommands.js
Normal file
78
common/tests/functional/testEchoCommands.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
var dactyllib = require("dactyl");
|
||||||
|
|
||||||
|
var setupModule = function (module) {
|
||||||
|
controller = mozmill.getBrowserController();
|
||||||
|
dactyl = new dactyllib.Controller(controller);
|
||||||
|
};
|
||||||
|
|
||||||
|
var teardownModule = function (module) {
|
||||||
|
dactyl.teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
var teardownTest = function (test) {
|
||||||
|
dactyl.closeMessageWindow();
|
||||||
|
};
|
||||||
|
|
||||||
|
var testEchoCommand_SingleLineMessageAndClosedMOW_MessageDisplayedInMessageLine = function () {
|
||||||
|
const output = "foobar";
|
||||||
|
|
||||||
|
assertEchoGeneratesLineOutput({
|
||||||
|
ECHO_COMMAND: "echo " + output.quote(),
|
||||||
|
EXPECTED_OUTPUT: output
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var testEchoCommand_SingleLineMessageAndOpenMOW_MessageAppendedToMOW = function () {
|
||||||
|
const output = "foobar";
|
||||||
|
|
||||||
|
dactyl.openMessageWindow();
|
||||||
|
|
||||||
|
assertEchoGeneratesWindowOutput({
|
||||||
|
ECHO_COMMAND: "echo " + output.quote(),
|
||||||
|
EXPECTED_OUTPUT: RegExp(output)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var testEchoCommand_MultilineMessageAndClosedMOW_MessageDisplayedInMOW = function () {
|
||||||
|
const output = "foo\nbar";
|
||||||
|
|
||||||
|
assertEchoGeneratesWindowOutput({
|
||||||
|
ECHO_COMMAND: "echo " + output.quote(),
|
||||||
|
EXPECTED_OUTPUT: output
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var testEchoCommand_MultilineMessageAndOpenMOW_MessageAppendedToMOW = function () {
|
||||||
|
const output = "foo\nbar";
|
||||||
|
|
||||||
|
dactyl.openMessageWindow();
|
||||||
|
|
||||||
|
assertEchoGeneratesWindowOutput({
|
||||||
|
ECHO_COMMAND: "echo " + output.quote(),
|
||||||
|
EXPECTED_OUTPUT: RegExp(output)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var testEchoCommand_ObjectArgumentAndClosedMOW_MessageDisplayedInMOW = function () {
|
||||||
|
assertEchoGeneratesWindowOutput({
|
||||||
|
ECHO_COMMAND: "echo var obj = { x: 1, y: 2 }; obj;",
|
||||||
|
EXPECTED_OUTPUT: "[object\u00A0Object]::\nx: 1\ny: 2\n"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function executeCommand(command) {
|
||||||
|
dactyl.runViCommand(":" + command);
|
||||||
|
dactyl.runViCommand([["VK_RETURN"]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertEchoGeneratesWindowOutput({ ECHO_COMMAND, EXPECTED_OUTPUT }) {
|
||||||
|
executeCommand(ECHO_COMMAND);
|
||||||
|
dactyl.assertMessageWindow(EXPECTED_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertEchoGeneratesLineOutput({ ECHO_COMMAND, EXPECTED_OUTPUT }) {
|
||||||
|
executeCommand(ECHO_COMMAND);
|
||||||
|
dactyl.assertMessageLine(EXPECTED_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: sw=4 ts=8 et:
|
||||||
49
common/tests/functional/testFindCommands.js
Normal file
49
common/tests/functional/testFindCommands.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
var dactyllib = require("dactyl");
|
||||||
|
|
||||||
|
const FIND_TEST_PAGE = collector.addHttpResource("./data/") + "find.html";
|
||||||
|
|
||||||
|
var setupModule = function (module) {
|
||||||
|
controller = mozmill.getBrowserController();
|
||||||
|
dactyl = new dactyllib.Controller(controller);
|
||||||
|
};
|
||||||
|
|
||||||
|
var teardownModule = function (module) {
|
||||||
|
dactyl.teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
var setupTest = function (test) {
|
||||||
|
controller.open(FIND_TEST_PAGE);
|
||||||
|
controller.waitForPageLoad(controller.tabs.activeTab);
|
||||||
|
controller.sleep(1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
var testFindCommand_PresentAlphabeticText_TextSelected = function () {
|
||||||
|
assertTextFoundInPage("letter")
|
||||||
|
};
|
||||||
|
|
||||||
|
var testFindCommand_PresentNumericText_TextSelected = function () {
|
||||||
|
assertTextFoundInPage("3.141")
|
||||||
|
};
|
||||||
|
|
||||||
|
var testFindCommand_MissingText_ErrorMessageDisplayed = function () {
|
||||||
|
const MISSING_TEXT = "8c307545a017f60add90ef08955e148e";
|
||||||
|
const PATTERN_NOT_FOUND_ERROR = "E486: Pattern not found: " + MISSING_TEXT;
|
||||||
|
|
||||||
|
runTextSearchCommand(MISSING_TEXT);
|
||||||
|
|
||||||
|
dactyl.assertErrorMessage(PATTERN_NOT_FOUND_ERROR);
|
||||||
|
};
|
||||||
|
|
||||||
|
function runTextSearchCommand(str) {
|
||||||
|
dactyl.runViCommand("/" + str);
|
||||||
|
dactyl.runViCommand([["VK_RETURN"]]);
|
||||||
|
|
||||||
|
controller.sleep(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertTextFoundInPage(text) {
|
||||||
|
runTextSearchCommand(text);
|
||||||
|
dactyl.assertSelection(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: sw=4 ts=8 et:
|
||||||
66
common/tests/functional/testHelpCommands.js
Normal file
66
common/tests/functional/testHelpCommands.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
var jumlib = {}; Components.utils.import("resource://mozmill/modules/jum.js", jumlib);
|
||||||
|
var dactyllib = require("dactyl");
|
||||||
|
|
||||||
|
var setupModule = function (module) {
|
||||||
|
controller = mozmill.getBrowserController();
|
||||||
|
dactyl = new dactyllib.Controller(controller);
|
||||||
|
};
|
||||||
|
|
||||||
|
var teardownModule = function (module) {
|
||||||
|
dactyl.teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
var setupTest = function (test) {
|
||||||
|
dactyl.runViCommand([["VK_ESCAPE"]]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const HELP_FILES = ["all", "tutorial", "intro", "starting", "browsing",
|
||||||
|
"buffer", "cmdline", "insert", "options", "pattern", "tabs", "hints",
|
||||||
|
"map", "eval", "marks", "repeat", "autocommands", "print", "gui",
|
||||||
|
"styling", "message", "developer", "various", "faq", "index", "plugins"];
|
||||||
|
|
||||||
|
var testViHelpCommand_OpensIntroHelpPage = function () {
|
||||||
|
assertHelpOpensPageWithTag({
|
||||||
|
HELP_COMMAND: function () { dactyl.runViCommand([["VK_F1"]]); },
|
||||||
|
EXPECTED_HELP_TAG: "intro.xml"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var testViHelpAllCommand_OpensAllHelpPage = function () {
|
||||||
|
assertHelpOpensPageWithTag({
|
||||||
|
HELP_COMMAND: function () { dactyl.runViCommand([["VK_F1", { altKey: true }]]); },
|
||||||
|
EXPECTED_HELP_TAG: "all.xml"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var testExHelpCommand_NoArgs_OpensIntroHelpPage = function () {
|
||||||
|
assertHelpOpensPageWithTag({
|
||||||
|
HELP_COMMAND: function () { dactyl.runExCommand("help"); },
|
||||||
|
EXPECTED_HELP_TAG: "intro.xml"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var testExHelpAllCommand_NoArgs_OpensAllHelpPage = function () {
|
||||||
|
assertHelpOpensPageWithTag({
|
||||||
|
HELP_COMMAND: function () { dactyl.runExCommand("helpall"); },
|
||||||
|
EXPECTED_HELP_TAG: "all.xml"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var testExHelpCommand_PageTagArg_OpensHelpPageContainingTag = function () {
|
||||||
|
for (let [, file] in Iterator(HELP_FILES)) {
|
||||||
|
let tag = file + ".xml";
|
||||||
|
assertHelpOpensPageWithTag({
|
||||||
|
HELP_COMMAND: function () { dactyl.runExCommand("help " + tag); },
|
||||||
|
EXPECTED_HELP_TAG: tag
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function assertHelpOpensPageWithTag({ HELP_COMMAND, EXPECTED_HELP_TAG }) {
|
||||||
|
HELP_COMMAND();
|
||||||
|
controller.waitForPageLoad(controller.tabs.activeTab);
|
||||||
|
controller.assertNode(new elementslib.ID(controller.tabs.activeTab, EXPECTED_HELP_TAG));
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: sw=4 ts=8 et:
|
||||||
37
common/tests/functional/testShellCommands.js
Normal file
37
common/tests/functional/testShellCommands.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
var dactyllib = require("dactyl");
|
||||||
|
|
||||||
|
var setupModule = function (module) {
|
||||||
|
controller = mozmill.getBrowserController();
|
||||||
|
dactyl = new dactyllib.Controller(controller);
|
||||||
|
};
|
||||||
|
|
||||||
|
var teardownModule = function (module) {
|
||||||
|
dactyl.teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
var teardownTest = function (test) {
|
||||||
|
dactyl.closeMessageWindow();
|
||||||
|
};
|
||||||
|
|
||||||
|
var testRunCommand_ExecutingOutputCommand_OutputDisplayed = function () {
|
||||||
|
const EXPECTED_OUTPUT = "foobar";
|
||||||
|
const COMMAND = "run echo " + EXPECTED_OUTPUT;
|
||||||
|
|
||||||
|
dactyl.runExCommand(COMMAND);
|
||||||
|
|
||||||
|
dactyl.assertMessageWindow(RegExp(EXPECTED_OUTPUT));
|
||||||
|
};
|
||||||
|
|
||||||
|
var testRunCommand_RepeatArg_LastCommandRepeated = function () {
|
||||||
|
const EXPECTED_OUTPUT = /foobar$/; // XXX
|
||||||
|
const COMMAND = "run echo 'foobar'";
|
||||||
|
const REPEAT_COMMAND = "run!";
|
||||||
|
|
||||||
|
dactyl.runExCommand(COMMAND);
|
||||||
|
dactyl.closeMessageWindow();
|
||||||
|
dactyl.runExCommand(REPEAT_COMMAND);
|
||||||
|
|
||||||
|
dactyl.assertMessageWindow(EXPECTED_OUTPUT);
|
||||||
|
};
|
||||||
|
|
||||||
|
// vim: sw=4 ts=8 et:
|
||||||
38
common/tests/functional/testVersionCommand.js
Normal file
38
common/tests/functional/testVersionCommand.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
var dactyllib = require("dactyl");
|
||||||
|
|
||||||
|
var setupModule = function (module) {
|
||||||
|
controller = mozmill.getBrowserController();
|
||||||
|
dactyl = new dactyllib.Controller(controller);
|
||||||
|
};
|
||||||
|
|
||||||
|
var teardownModule = function (module) {
|
||||||
|
dactyl.teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
var setupTest = function (test) {
|
||||||
|
dactyl.closeMessageWindow();
|
||||||
|
};
|
||||||
|
|
||||||
|
var testVersionCommand_NoArg_VersionStringDisplayed = function () {
|
||||||
|
const EXPECTED_OUTPUT = RegExp(dactyl.applicationName + ".+ (.+) running on:.+"); // XXX
|
||||||
|
|
||||||
|
dactyl.runExCommand("version");
|
||||||
|
|
||||||
|
dactyl.assertMessageWindow(EXPECTED_OUTPUT);
|
||||||
|
};
|
||||||
|
|
||||||
|
var testVersionCommand_BangArg_HostAppVersionPageDisplayed = function () {
|
||||||
|
const EXPECTED_URL = "about:";
|
||||||
|
const EXPECTED_TITLE = "About:";
|
||||||
|
const BLANK_PAGE_URL = "about:blank";
|
||||||
|
|
||||||
|
controller.open(BLANK_PAGE_URL);
|
||||||
|
controller.waitForPageLoad(controller.tabs.activeTab);
|
||||||
|
dactyl.runExCommand("version!");
|
||||||
|
controller.waitForPageLoad(controller.tabs.activeTab);
|
||||||
|
|
||||||
|
controller.assert(function () controller.tabs.activeTab.location.href === EXPECTED_URL);
|
||||||
|
controller.assert(function () controller.tabs.activeTab.title === EXPECTED_TITLE);
|
||||||
|
};
|
||||||
|
|
||||||
|
// vim: sw=4 ts=8 et:
|
||||||
49
common/tests/functional/utils.js
Normal file
49
common/tests/functional/utils.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
function module(uri) {
|
||||||
|
let obj = {};
|
||||||
|
Components.utils.import(uri, obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
var elementslib = module("resource://mozmill/modules/elementslib.js");
|
||||||
|
var frame = module("resource://mozmill/modules/frame.js");
|
||||||
|
var jumlib = module("resource://mozmill/modules/jum.js");
|
||||||
|
|
||||||
|
function toJSON(val) {
|
||||||
|
if (typeof val == "function")
|
||||||
|
return val.toSource();
|
||||||
|
else
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test(val, params) {
|
||||||
|
frame.events[val ? "pass" : "fail"](params);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var [k, v] in Iterator({
|
||||||
|
|
||||||
|
NS: Namespace("dactyl", "http://vimperator.org/namespaces/liberator"),
|
||||||
|
|
||||||
|
module: module,
|
||||||
|
|
||||||
|
toJSON: toJSON,
|
||||||
|
|
||||||
|
test: test,
|
||||||
|
|
||||||
|
assert: function (funcName, value, comment)
|
||||||
|
test(value, {
|
||||||
|
function: funcName,
|
||||||
|
value: toJSON(value),
|
||||||
|
comment: toJSON(comment)
|
||||||
|
}),
|
||||||
|
|
||||||
|
assertEqual: function (funcName, want, got, comment)
|
||||||
|
test(want == got, {
|
||||||
|
function: funcName,
|
||||||
|
want: toJSON(want), got: toJSON(got),
|
||||||
|
comment: toJSON(comment)
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
exports[k] = v;
|
||||||
|
|
||||||
|
// vim: sw=4 ts=8 et:
|
||||||
Reference in New Issue
Block a user