Bug 1219504 - BrowserTestUtils changes (r=mconley)

This commit is contained in:
Bill McCloskey 2015-10-28 16:50:12 -07:00
parent 3e6bd6f644
commit 549f9af669
4 changed files with 90 additions and 17 deletions

View File

@ -139,17 +139,33 @@ this.BrowserTestUtils = {
* A xul:browser.
* @param {Boolean} includeSubFrames
* A boolean indicating if loads from subframes should be included.
* @param {optional string or function} wantLoad
* If a function, takes a URL and returns true if that's the load we're
* interested in. If a string, gives the URL of the load we're interested
* in. If not present, the first load resolves the promise.
*
* @return {Promise}
* @resolves When a load event is triggered for the browser.
*/
browserLoaded(browser, includeSubFrames=false) {
browserLoaded(browser, includeSubFrames=false, wantLoad=null) {
function isWanted(url) {
if (!wantLoad) {
return true;
} else if (typeof(wantLoad) == "function") {
return wantLoad(url);
} else {
// It's a string.
return wantLoad == url;
}
}
return new Promise(resolve => {
let mm = browser.ownerDocument.defaultView.messageManager;
mm.addMessageListener("browser-test-utils:loadEvent", function onLoad(msg) {
if (msg.target == browser && (!msg.data.subframe || includeSubFrames)) {
if (msg.target == browser && (!msg.data.subframe || includeSubFrames) &&
isWanted(msg.data.url)) {
mm.removeMessageListener("browser-test-utils:loadEvent", onLoad);
resolve();
resolve(msg.data.url);
}
});
});

View File

@ -12,12 +12,10 @@ this.EXPORTED_SYMBOLS = [
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const FRAME_SCRIPT = "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js";
const globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIMessageListenerManager);
/**
* Keeps track of whether the frame script was already loaded.
*/
@ -37,6 +35,16 @@ var gMessageID = 1;
* This object provides the public module functions.
*/
this.ContentTask = {
/**
* _testScope saves the current testScope from
* browser-test.js. This is used to implement SimpleTest functions
* like ok() and is() in the content process. The scope is only
* valid for tasks spawned in the current test, so we keep track of
* the ID of the first task spawned in this test (_scopeValidId).
*/
_testScope: null,
_scopeValidId: 0,
/**
* Creates and starts a new task in a browser's content.
*
@ -57,7 +65,7 @@ this.ContentTask = {
spawn: function ContentTask_spawn(browser, arg, task) {
// Load the frame script if needed.
if (!gFrameScriptLoaded) {
globalMM.loadFrameScript(FRAME_SCRIPT, true);
Services.mm.loadFrameScript(FRAME_SCRIPT, true);
gFrameScriptLoaded = true;
}
@ -80,20 +88,39 @@ this.ContentTask = {
return deferred.promise;
},
setTestScope(scope) {
this._testScope = scope;
this._scopeValidId = gMessageID;
},
};
var ContentMessageListener = {
receiveMessage(aMessage) {
let id = aMessage.data.id
let deferred = gPromises.get(id);
gPromises.delete(id);
let id = aMessage.data.id;
if (aMessage.data.error) {
deferred.reject(aMessage.data.error);
} else {
deferred.resolve(aMessage.data.result);
if (id < ContentTask._scopeValidId) {
throw new Error("test result returned after test finished");
}
if (aMessage.name == "content-task:complete") {
let deferred = gPromises.get(id);
gPromises.delete(id);
if (aMessage.data.error) {
deferred.reject(aMessage.data.error);
} else {
deferred.resolve(aMessage.data.result);
}
} else if (aMessage.name == "content-task:test-result") {
let data = aMessage.data;
ContentTask._testScope.ok(data.condition, data.name, data.diag, data.stack);
} else if (aMessage.name == "content-task:test-info") {
ContentTask._testScope.info(aMessage.data.name);
}
},
};
Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager)
.addMessageListener("content-task:complete", ContentMessageListener);
Services.mm.addMessageListener("content-task:complete", ContentMessageListener);
Services.mm.addMessageListener("content-task:test-result", ContentMessageListener);
Services.mm.addMessageListener("content-task:test-info", ContentMessageListener);

View File

@ -13,6 +13,33 @@ addMessageListener("content-task:spawn", function (msg) {
let id = msg.data.id;
let source = msg.data.runnable || "()=>{}";
function getStack(aStack) {
let frames = [];
for (let frame = aStack; frame; frame = frame.caller) {
frames.push(frame.filename + ":" + frame.name + ":" + frame.lineNumber);
}
return frames.join("\n");
}
function ok(condition, name, diag) {
let stack = getStack(Components.stack.caller);
sendAsyncMessage("content-task:test-result", {
id, condition, name, diag, stack
});
}
function is(a, b, name) {
ok(Object.is(a, b), name, "Got " + a + ", expected " + b);
}
function isnot(a, b, name) {
ok(!Object.is(a, b), name, "Didn't expect " + a + ", but got it");
}
function info(name) {
sendAsyncMessage("content-task:test-info", {id, name});
}
try {
let runnablestr = `
(() => {
@ -20,7 +47,7 @@ addMessageListener("content-task:spawn", function (msg) {
})();`
let runnable = eval(runnablestr);
let iterator = runnable(msg.data.arg);
let iterator = runnable.call(this, msg.data.arg);
Task.spawn(iterator).then((val) => {
sendAsyncMessage("content-task:complete", {
id: id,

View File

@ -552,6 +552,7 @@ Tester.prototype = {
this.SimpleTest[m] = this.SimpleTestOriginal[m];
});
this.ContentTask.setTestScope(null);
testScope.destroy();
this.currentTest.scope = null;
}
@ -706,6 +707,8 @@ Tester.prototype = {
currentTest.addResult(res);
});
this.ContentTask.setTestScope(currentScope);
// Allow Assert.jsm methods to be tacked to the current scope.
this.currentTest.scope.export_assertions = function() {
for (let func in this.Assert) {