зеркало из https://github.com/mozilla/pjs.git
Bug 501235 - "add new mochitest sub-harness for docshell testing." r=bzbarsky
This commit is contained in:
Родитель
86f5431cb8
Коммит
142e987d3c
|
@ -78,8 +78,16 @@ _TEST_FILES = \
|
|||
test_bug456980.xul \
|
||||
$(NULL)
|
||||
|
||||
_DOCSHELL_SUBHARNESS = \
|
||||
docshell_helpers.js \
|
||||
generic.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_HTTP_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
|
||||
|
||||
libs:: $(_DOCSHELL_SUBHARNESS)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
|
||||
|
|
|
@ -0,0 +1,380 @@
|
|||
/**
|
||||
* Import common SimpleTest methods so that they're usable in this window.
|
||||
*/
|
||||
var imports = [ "SimpleTest", "is", "isnot", "ok", "onerror", "todo",
|
||||
"todo_is", "todo_isnot" ];
|
||||
for each (var import in imports) {
|
||||
window[import] = window.opener.wrappedJSObject[import];
|
||||
}
|
||||
|
||||
/**
|
||||
* Define global constants and variables.
|
||||
*/
|
||||
const NAV_NONE = 0;
|
||||
const NAV_BACK = 1;
|
||||
const NAV_FORWARD = 2;
|
||||
const NAV_URI = 3;
|
||||
|
||||
var gExpectedEvents; // an array of events which are expected to be
|
||||
// triggered by this navigation
|
||||
var gFinalEvent; // true if the last expected event has occurred
|
||||
var gUrisNotInBFCache = []; // an array of uri's which shouldn't be stored
|
||||
// in the bfcache
|
||||
var gNavType = NAV_NONE; // defines the most recent navigation type
|
||||
// executed by doPageNavigation
|
||||
|
||||
|
||||
/**
|
||||
* The doPageNavigation() function performs page navigations asynchronously,
|
||||
* listens for specified events, and compares actual events with a list of
|
||||
* expected events. When all expected events have occurred, an optional
|
||||
* callback can be notified. The parameter passed to this function is an
|
||||
* object with the following properties:
|
||||
*
|
||||
* uri: if !undefined, the browser will navigate to this uri
|
||||
*
|
||||
* back: if true, the browser will execute goBack()
|
||||
*
|
||||
* forward: if true, the browser will execute goForward()
|
||||
*
|
||||
* eventsToListenFor: an array containing one or more of the following event
|
||||
* types to listen for: "pageshow", "pagehide", "onload",
|
||||
* "onunload". If this property is undefined, only a
|
||||
* single "pageshow" events will be listened for. If this
|
||||
* property is explicitly empty, [], then no events will
|
||||
* be listened for.
|
||||
*
|
||||
* expectedEvents: an array of one or more expectedEvent objects,
|
||||
* corresponding to the events which are expected to be
|
||||
* fired for this navigation. Each object has the
|
||||
* following properties:
|
||||
*
|
||||
* type: one of the event type strings
|
||||
* title (optional): the title of the window the
|
||||
* event belongs to
|
||||
* persisted (optional): the event's expected
|
||||
* .persisted attribute
|
||||
*
|
||||
* This function will verify that events with the
|
||||
* specified properties are fired in the same order as
|
||||
* specified in the array. If .title or .persisted
|
||||
* properties for an expectedEvent are undefined, those
|
||||
* properties will not be verified for that particular
|
||||
* event.
|
||||
*
|
||||
* This property is ignored if eventsToListenFor is
|
||||
* undefined or [].
|
||||
*
|
||||
* preventBFCache: if true, an unload handler will be added to the loaded
|
||||
* page to prevent it from being bfcached. This property
|
||||
* has no effect when eventsToListenFor is [].
|
||||
*
|
||||
* onNavComplete: a callback which is notified after all expected events
|
||||
* have occurred, or after a timeout has elapsed. This
|
||||
* callback is not notified if eventsToListenFor is [].
|
||||
*
|
||||
* There must be an expectedEvent object for each event of the types in
|
||||
* eventsToListenFor which is triggered by this navigation. For example, if
|
||||
* eventsToListenFor = [ "pagehide", "pageshow" ], then expectedEvents
|
||||
* must contain an object for each pagehide and pageshow event which occurs as
|
||||
* a result of this navigation.
|
||||
*/
|
||||
function doPageNavigation(params) {
|
||||
// Parse the parameters.
|
||||
let back = params.back ? params.back : false;
|
||||
let forward = params.forward ? params.forward : false;
|
||||
let uri = params.uri ? params.uri : false;
|
||||
let eventsToListenFor = typeof(params.eventsToListenFor) != "undefined" ?
|
||||
params.eventsToListenFor : ["pageshow"];
|
||||
gExpectedEvents = typeof(params.eventsToListenFor) == "undefined" ||
|
||||
eventsToListenFor.length == 0 ? undefined : params.expectedEvents;
|
||||
let preventBFCache = (typeof[params.preventBFCache] == "undefined") ?
|
||||
false : params.preventBFCache;
|
||||
|
||||
// Do some sanity checking on arguments.
|
||||
if (back && forward)
|
||||
throw "Can't specify both back and forward";
|
||||
if (back && uri)
|
||||
throw "Can't specify both back and a uri";
|
||||
if (forward && uri)
|
||||
throw "Can't specify both forward and a uri";
|
||||
if (!back && !forward && !uri)
|
||||
throw "Must specify back or foward or uri";
|
||||
if (params.onNavComplete && eventsToListenFor.length == 0)
|
||||
throw "Can't use onNavComplete when eventsToListenFor == []";
|
||||
if (params.preventBFCache && eventsToListenFor.length == 0)
|
||||
throw "Can't use preventBFCache when eventsToListenFor == []";
|
||||
for each (let anEventType in eventsToListenFor) {
|
||||
let eventFound = false;
|
||||
if ( (anEventType == "pageshow") && (!gExpectedEvents) )
|
||||
eventFound = true;
|
||||
for each (let anExpectedEvent in gExpectedEvents) {
|
||||
if (anExpectedEvent.type == anEventType)
|
||||
eventFound = true;
|
||||
}
|
||||
if (!eventFound)
|
||||
throw "Event type " + anEventType + " is specified in " +
|
||||
"eventsToListenFor, but not in expectedEvents";
|
||||
}
|
||||
|
||||
// If the test explicitly sets .eventsToListenFor to [], don't wait for any
|
||||
// events.
|
||||
gFinalEvent = eventsToListenFor.length == 0 ? true : false;
|
||||
|
||||
// Add an event listener for each type of event in the .eventsToListenFor
|
||||
// property of the input parameters.
|
||||
for each (let eventType in eventsToListenFor) {
|
||||
dump("TEST: registering a listener for " + eventType + " events\n");
|
||||
TestWindow.getBrowser().addEventListener(eventType, pageEventListener,
|
||||
true);
|
||||
}
|
||||
|
||||
// Perform the specified navigation.
|
||||
if (back) {
|
||||
gNavType = NAV_BACK;
|
||||
TestWindow.getBrowser().goBack();
|
||||
}
|
||||
else if (forward) {
|
||||
gNavType = NAV_FORWARD;
|
||||
TestWindow.getBrowser().goForward();
|
||||
}
|
||||
else if (uri) {
|
||||
gNavType = NAV_URI;
|
||||
TestWindow.getBrowser().loadURI(uri);
|
||||
}
|
||||
else {
|
||||
throw "No valid navigation type passed to doPageNavigation!";
|
||||
}
|
||||
|
||||
// If we're listening for events and there is an .onNavComplete callback,
|
||||
// wait for all events to occur, and then call doPageNavigation_complete().
|
||||
if (eventsToListenFor.length > 0 && params.onNavComplete)
|
||||
{
|
||||
waitForTrue(
|
||||
function() { return gFinalEvent; },
|
||||
function() {
|
||||
doPageNavigation_complete(eventsToListenFor, params.onNavComplete,
|
||||
preventBFCache);
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish doPageNavigation(), by removing event listeners, adding an unload
|
||||
* handler if appropriate, and calling the onNavComplete callback. This
|
||||
* function is called after all the expected events for this navigation have
|
||||
* occurred.
|
||||
*/
|
||||
function doPageNavigation_complete(eventsToListenFor, onNavComplete,
|
||||
preventBFCache) {
|
||||
// Unregister our event listeners.
|
||||
dump("TEST: removing event listeners\n");
|
||||
for each (let eventType in eventsToListenFor) {
|
||||
TestWindow.getBrowser().removeEventListener(eventType, pageEventListener,
|
||||
true);
|
||||
}
|
||||
|
||||
// If the .preventBFCache property was set, add an empty unload handler to
|
||||
// prevent the page from being bfcached.
|
||||
let uri = TestWindow.getBrowser().currentURI.spec;
|
||||
if (preventBFCache) {
|
||||
TestWindow.getWindow().addEventListener("unload", function() {
|
||||
dump("TEST: Called dummy unload function to prevent page from " +
|
||||
"being bfcached.\n");
|
||||
}, true);
|
||||
|
||||
// Save the current uri in an array of uri's which shouldn't be
|
||||
// stored in the bfcache, for later verification.
|
||||
if (!(uri in gUrisNotInBFCache)) {
|
||||
gUrisNotInBFCache.push(uri);
|
||||
}
|
||||
} else if (gNavType == NAV_URI) {
|
||||
// If we're navigating to a uri and .preventBFCache was not
|
||||
// specified, splice it out of gUrisNotInBFCache if it's there.
|
||||
gUrisNotInBFCache.forEach(
|
||||
function(element, index, array) {
|
||||
if (element == uri) {
|
||||
array.splice(index, 1);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
// Notify the callback now that we're done.
|
||||
onNavComplete.call();
|
||||
}
|
||||
|
||||
/**
|
||||
* The event listener which listens for expectedEvents.
|
||||
*/
|
||||
function pageEventListener(event) {
|
||||
try {
|
||||
dump("TEST: eventListener received a " + event.type + " event for page " +
|
||||
event.originalTarget.title + ", persisted=" + event.persisted + "\n");
|
||||
}catch(e) {
|
||||
// Ignore any exception.
|
||||
}
|
||||
|
||||
// If this page shouldn't be in the bfcache because it was previously
|
||||
// loaded with .preventBFCache, make sure that its pageshow event
|
||||
// has .persisted = false, even if the test doesn't explicitly test
|
||||
// for .persisted.
|
||||
if ( (event.type == "pageshow") &&
|
||||
(gNavType == NAV_BACK || gNavType == NAV_FORWARD) ) {
|
||||
let uri = TestWindow.getBrowser().currentURI.spec;
|
||||
if (uri in gUrisNotInBFCache) {
|
||||
ok(!event.persisted, "pageshow event has .persisted = false, even " +
|
||||
"though it was loaded with .preventBFCache previously\n");
|
||||
}
|
||||
}
|
||||
|
||||
// If no expected events were specified, mark the final event as having been
|
||||
// triggered when a pageshow event is fired; this will allow
|
||||
// doPageNavigation() to return.
|
||||
if ((typeof(gExpectedEvents) == "undefined") && event.type == "pageshow")
|
||||
{
|
||||
setTimeout(function() { gFinalEvent = true; }, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are explicitly no expected events, but we receive one, it's an
|
||||
// error.
|
||||
if (gExpectedEvents.length == 0) {
|
||||
ok(false, "Unexpected event (" + event.type + ") occurred");
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab the next expected event, and compare its attributes against the
|
||||
// actual event.
|
||||
let expected = gExpectedEvents.shift();
|
||||
|
||||
is(event.type, expected.type,
|
||||
"A " + expected.type + " event was expected, but a " +
|
||||
event.type + " event occurred");
|
||||
|
||||
if (typeof(expected.title) != "undefined") {
|
||||
ok(event.originalTarget instanceof HTMLDocument,
|
||||
"originalTarget for last " + event.type +
|
||||
" event not an HTMLDocument");
|
||||
is(event.originalTarget.title, expected.title,
|
||||
"A " + event.type + " event was expected for page " +
|
||||
expected.title + ", but was fired for page " +
|
||||
event.originalTarget.title);
|
||||
}
|
||||
|
||||
if (typeof(expected.persisted) != "undefined") {
|
||||
is(event.persisted, expected.persisted,
|
||||
"The persisted property of the " + event.type + "event on page " +
|
||||
event.originalTarget.title + " had an unexpected value");
|
||||
}
|
||||
|
||||
// If we're out of expected events, let doPageNavigation() return.
|
||||
if (gExpectedEvents.length == 0)
|
||||
setTimeout(function() { gFinalEvent = true; }, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* End a test.
|
||||
*/
|
||||
function finish() {
|
||||
// Work around bug 467960.
|
||||
var history = TestWindow.getBrowser().webNavigation.sessionHistory;
|
||||
history.PurgeHistory(history.count);
|
||||
|
||||
// Close the test window and signal the framework that the test is done.
|
||||
window.close();
|
||||
window.opener.wrappedJSObject.SimpleTest.finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function which waits until another function returns true, or until a
|
||||
* timeout occurs, and then notifies a callback.
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* fn: a function which is evaluated repeatedly, and when it turns true,
|
||||
* the onWaitComplete callback is notified.
|
||||
*
|
||||
* onWaitComplete: a callback which will be notified when fn() returns
|
||||
* true, or when a timeout occurs.
|
||||
*
|
||||
* timeout: a timeout, in seconds or ms, after which waitForTrue() will
|
||||
* fail an assertion and then return, even if the fn function never
|
||||
* returns true. If timeout is undefined, waitForTrue() will never
|
||||
* time out.
|
||||
*/
|
||||
function waitForTrue(fn, onWaitComplete, timeout) {
|
||||
var start = new Date().valueOf();
|
||||
if (typeof(timeout) != "undefined") {
|
||||
// If timeoutWait is less than 500, assume it represents seconds, and
|
||||
// convert to ms.
|
||||
if (timeout < 500)
|
||||
timeout *= 1000;
|
||||
}
|
||||
|
||||
// Loop until the test function returns true, or until a timeout occurs,
|
||||
// if a timeout is defined.
|
||||
var intervalid;
|
||||
intervalid =
|
||||
setInterval(
|
||||
function() {
|
||||
var timeoutHit = false;
|
||||
if (typeof(timeout) != "undefined") {
|
||||
timeoutHit = new Date().valueOf() - start >=
|
||||
timeout ? true : false;
|
||||
if (timeoutHit) {
|
||||
ok(false, "Timed out waiting for condition");
|
||||
}
|
||||
}
|
||||
if (timeoutHit || fn.call()) {
|
||||
// Stop calling the test function and notify the callback.
|
||||
clearInterval(intervalid);
|
||||
onWaitComplete.call();
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the bfcache.
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* enable: if true, set max_total_viewers to -1 (the default); if false, set
|
||||
* to 0 (disabled), if a number, set it to that specific number
|
||||
*/
|
||||
function enableBFCache(enable) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefBranch);
|
||||
if (typeof(enable) == "boolean") {
|
||||
if (enable)
|
||||
prefs.setIntPref("browser.sessionhistory.max_total_viewers", -1);
|
||||
else
|
||||
prefs.setIntPref("browser.sessionhistory.max_total_viewers", 0);
|
||||
}
|
||||
else if (typeof(enable) == "number") {
|
||||
prefs.setIntPref("browser.sessionhistory.max_total_viewers", enable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full HTTP url for a file in the mochitest docshell test
|
||||
* directory.
|
||||
*/
|
||||
function getHttpUrl(filename) {
|
||||
return "http://localhost:8888/chrome/docshell/test/chrome/" + filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience object with methods that return the current test window,
|
||||
* browser, and document.
|
||||
*/
|
||||
var TestWindow = {};
|
||||
TestWindow.getWindow = function () {
|
||||
return document.getElementById("content").contentWindow;
|
||||
}
|
||||
TestWindow.getBrowser = function () {
|
||||
return document.getElementById("content");
|
||||
}
|
||||
TestWindow.getDocument = function () {
|
||||
return document.getElementById("content").contentDocument;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/perl
|
||||
#
|
||||
# gen_template.pl
|
||||
# Makes docshell test case templates.
|
||||
# Takes one argument:
|
||||
#
|
||||
# -b : a bugnumber
|
||||
#
|
||||
# e.g.: perl gen_template.pl -b 303267
|
||||
#
|
||||
# Writes test case template files for bug 303267 to the current directory.
|
||||
use FindBin;
|
||||
use Getopt::Long;
|
||||
GetOptions("b=i"=> \$bug_number);
|
||||
|
||||
$template = "$FindBin::RealBin/test.template.txt";
|
||||
|
||||
open(IN,$template) or die("Failed to open input file for reading.");
|
||||
open(OUT, ">>test_bug" . $bug_number . ".xul") or die("Failed to open output file for appending.");
|
||||
while((defined(IN)) && ($line = <IN>)) {
|
||||
$line =~ s/{BUGNUMBER}/$bug_number/g;
|
||||
print OUT $line;
|
||||
}
|
||||
close(IN);
|
||||
close(OUT);
|
||||
|
||||
$template = "$FindBin::RealBin/window.template.txt";
|
||||
|
||||
open(IN,$template) or die("Failed to open input file for reading.");
|
||||
open(OUT, ">>bug" . $bug_number . "_window.xul") or die("Failed to open output file for appending.");
|
||||
while((defined(IN)) && ($line = <IN>)) {
|
||||
$line =~ s/{BUGNUMBER}/$bug_number/g;
|
||||
print OUT $line;
|
||||
}
|
||||
close(IN);
|
||||
close(OUT);
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>
|
||||
generic page
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="div1" style="height: 1000px; border: thin solid black;">
|
||||
A generic page which can be used any time a test needs to load an arbitrary page via http.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}.xul
|
||||
-->
|
||||
<window title="Mozilla Bug {BUGNUMBER}"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<title>Test for Bug {BUGNUMBER}</title>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}">
|
||||
Mozilla Bug {BUGNUMBER}</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
/** Test for Bug {BUGNUMBER} **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.open("bug{BUGNUMBER}_window.xul", "bug{BUGNUMBER}",
|
||||
"chrome,width=600,height=600");
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
</window>
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
|
||||
<window id="303267Test"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
width="600"
|
||||
height="600"
|
||||
onload="setTimeout(nextTest,0);"
|
||||
title="bug {BUGNUMBER} test">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/chrome/docshell/test/chrome/docshell_helpers.js">
|
||||
</script>
|
||||
|
||||
<script type="application/javascript"><![CDATA[
|
||||
|
||||
// Define the generator-iterator for the tests.
|
||||
var tests = testIterator();
|
||||
|
||||
////
|
||||
// Execute the next test in the generator function.
|
||||
//
|
||||
function nextTest() {
|
||||
tests.next();
|
||||
}
|
||||
|
||||
////
|
||||
// Generator function for test steps for bug {BUGNUMBER}:
|
||||
// Description goes here.
|
||||
//
|
||||
function testIterator()
|
||||
{
|
||||
// Test steps go here. See bug303267_window.xul for an example.
|
||||
|
||||
// Tell the framework the test is finished. Include the final 'yield'
|
||||
// statement to prevent a StopIteration exception from being thrown.
|
||||
finish();
|
||||
yield;
|
||||
}
|
||||
|
||||
]]></script>
|
||||
|
||||
<browser type="content-primary" flex="1" id="content" src="about:blank"/>
|
||||
</window>
|
Загрузка…
Ссылка в новой задаче