1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2026-01-09 21:34:12 +01:00
Files
pentadactyl-pm/common/tests/functional/dactyl.jsm
Kris Maglione f3e510837d Manually run Ex completers rather than pushing them through the
command line machinery. It's orders of magnitude faster, on the one
hand, but also runs the display machinery for all results, which may
catch more errors. Errors in the command line completion machinery
should be tested elsewhere.

Fix some sidebar test bugs.

Move dactyl.js and utils.js files out of sandboxes and into modules
so we can pass XML objects in and out of them.

--HG--
rename : common/tests/functional/dactyl.js => common/tests/functional/dactyl.jsm
rename : common/tests/functional/utils.js => common/tests/functional/utils.jsm
2011-02-02 22:01:49 -05:00

496 lines
18 KiB
JavaScript

function module(uri) {
if (!/^[a-z-]+:/.exec(uri))
uri = /([^ ]+\/)[^\/]+$/.exec(Components.stack.filename)[1] + uri + ".jsm";
let obj = {};
Components.utils.import(uri, obj);
return obj;
}
var EXPORTED_SYMBOLS = ["Controller"];
var utils = module("utils");
const { 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.modules = controller.window.dactyl.modules;
this.errorCount = 0;
this._countBeep = function countBeep() {
self.beepCount++;
}
this.errors = [];
this._countError = function countError(message, highlight) {
if (/\bErrorMsg\b/.test(highlight))
self.errors.push(String(message));
}
this.modules.dactyl.registerObserver("beep", this._countBeep);
this.modules.dactyl.registerObserver("echoLine", this._countError);
this.modules.dactyl.registerObserver("echoMultiline", this._countError);
this.resetErrorCount();
}
Controller.prototype = {
teardown: function () {
this.modules.dactyl.unregisterObserver("beep", this._countBeep);
this.modules.dactyl.unregisterObserver("echoLine", this._countError);
this.modules.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.errors.length;
this.assertNoErrors(func, self, args, message);
// dump("assertMessageError " + errorCount + " " + this.errorMessageCount + "\n");
return utils.assert('dactyl.assertMessageError', this.errors.length > 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
*/
assertNoErrors: function (func, self, args, message) {
let msg = message ? ": " + message : "";
let beepCount = this.beepCount;
let errorCount = this.errorCount;
if (func) {
errorCount = this.modules.util.errorCount;
try {
var returnVal = func.apply(self || this, args || []);
}
catch (e) {
this.modules.util.reportError(e);
}
}
if (this.beepCount > beepCount)
frame.log({
function: "dactyl.beepMonitor",
want: beepCount, got: this.beepCount,
comment: "Got " + (this.beepCount - beepCount) + " beeps during execution" + msg
});
if (errorCount != this.modules.util.errorCount)
var errors = this.modules.util.errors.slice(errorCount - this.modules.util.errorCount)
.join("\n");
var res = utils.assertEqual('dactyl.assertNoErrors',
errorCount, this.modules.util.errorCount,
"Errors were reported during the execution of this test" + msg + "\n" + errors);
return returnVal === undefined ? res : returnVal;
},
/**
* Asserts that the no error messages are reported during the call
* of *func*.
*
* @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
*/
assertNoErrorMessages: function (func, self, args, message) {
let msg = message ? ": " + message : "";
let count = this.errors.length;
try {
func.apply(self || this, args || []);
}
catch (e) {
this.modules.util.reportError(e);
}
return utils.assertEqual('dactyl.assertNoErrorMessages', count, this.errors.length,
"Error messsages were reported" + msg + ":\n\t" +
this.errors.slice(count).join("\n\t"));
},
/**
* Resets the error count used to determine whether new errors were
* reported during the execution of a test.
*/
resetErrorCount: function () {
this.errorCount = this.modules.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.modules.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.modules.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.modules.commandline.widgets.active.message,
/**
* @property {Node} The multi-line output window.
*/
get multiline() self.modules.commandline.widgets.multilineOutput,
/**
* @property {Node} The multi-line output container.
*/
get multilineContainer() self.modules.commandline.widgets.mowContainer,
}),
/**
* Returns dactyl to normal mode.
*/
setNormalMode: wrapAssertNoErrors(function () {
// XXX: Normal mode test
for (let i = 0; i < 15 && this.modules.modes.stack.length > 1; i++)
this.controller.keypress(null, "VK_ESCAPE", {});
this.controller.keypress(null, "l", { ctrlKey: true });
utils.assert("dactyl.setNormalMode", this.modules.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.modules.commandline.savingOutput = true;
if (args)
this.modules.ex[cmd](args);
else if (true)
this.modules.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.modules.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.
* @param {boolean} longWay Whether to test the completion by
* entering it into the command line and dispatching a <Tab> key
* press.
*/
runExCompletion: wrapAssertNoErrors(function (cmd, longWay) {
// dump("runExCompletion " + cmd + "\n");
if (!longWay) {
var context = this.modules.CompletionContext(cmd);
context.tabPressed = true;
context.fork("ex", 0, this.modules.completion, "ex");
utils.assert("dactyl.runCompletions", context.wait(5000),
"Completion failed: " + cmd.quote());
for (var [, ctxt] in Iterator(context.contextList))
for (var [, item] in Iterator(ctxt.items))
ctxt.createRow(item);
return context;
}
else {
this.setExMode();
utils.assertEqual("dactyl.assertCommandLineFocused",
this.elements.commandInput,
this.elements.focused,
"Running Ex Completion: The command line is not focused");
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.modules.commandline._tabTimer)
this.modules.commandline._tabTimer.flush();
else if (this.modules.commandline.commandSession && this.modules.commandline.commandSession.completions)
this.modules.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.modules.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.modules.config.appName // XXX
};
// vim: sw=4 ts=8 et ft=javascript: