Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Landing Honza's overhaul of the FireUnit UI (and a number of tests fo…

…r various bugs).
  • Loading branch information...
commit 06188755eec5cc516abafd800bfb3898addae228 1 parent d1ea916
@jeresig jeresig authored
Showing with 1,786 additions and 239 deletions.
  1. +5 −1 chrome.manifest
  2. +920 −215 chrome/content/fireunit/fireunit.js
  3. +5 −0 chrome/content/fireunit/fireunit.xul
  4. +161 −0 chrome/content/fireunit/jsdiff.js
  5. +23 −18 chrome/content/fireunit/test/commandline.html
  6. +40 −0 chrome/content/fireunit/test/inspect.html
  7. +6 −0 chrome/content/fireunit/test/net/env.js
  8. +63 −0 chrome/content/fireunit/test/net/issue1256.html
  9. +1 −0  chrome/content/fireunit/test/net/issue1256.txt
  10. +95 −0 chrome/content/fireunit/test/net/issue176.html
  11. +63 −0 chrome/content/fireunit/test/net/issue372.html
  12. +1 −0  chrome/content/fireunit/test/net/issue372.txt
  13. +70 −0 chrome/content/fireunit/test/net/issue601.html
  14. +1 −0  chrome/content/fireunit/test/net/issue601.txt
  15. +79 −0 chrome/content/fireunit/test/net/issue700.html
  16. +2 −0  chrome/content/fireunit/test/net/issue700.txt
  17. +2 −2 chrome/content/fireunit/test/net/net1.html
  18. +7 −1 chrome/content/fireunit/test/net/start.html
  19. +50 −0 chrome/content/fireunit/test/net/utils.js
  20. +11 −0 chrome/locale/en-US/fireunit.properties
  21. +176 −0 chrome/skin/classic/fireunit.css
  22. +3 −0  defaults/preferences/prefs.js
  23. +2 −2 install.rdf
View
6 chrome.manifest
@@ -1,2 +1,6 @@
-content fireunit chrome/content/fireunit/ xpcnativewrappers=no
+content fireunit chrome/content/fireunit/ xpcnativewrappers=no
+skin fireunit classic/1.0 chrome/skin/classic/
+
+locale fireunit en-US chrome/locale/en-US/
+
overlay chrome://firebug/content/firebugOverlay.xul chrome://fireunit/content/fireunit.xul
View
1,135 chrome/content/fireunit/fireunit.js
@@ -5,274 +5,979 @@
FBL.ns(function() { with (FBL) {
-var panelName = "Test";
+// Constants
+//-----------------------------------------------------------------------------
-var testQueue;
-var queueResults = "";
-var server;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+var panelName = "test"; // uniques ID of the Test panel
+var testQueue; // tests to be executed
+var queueResults = []; // test results collected
+var server; // HTTP local server
var uuid = 1;
var serverPort = 7080;
-var cache = Components.classes["@mozilla.org/network/cache-service;1"].getService(Components.interfaces.nsICacheService);
-
-/**
- * Module implementation.
- */
-Firebug.FireUnitModule = extend(Firebug.Module, {
- showPanel: function(browser, panel) {
- var isHwPanel = panel && panel.name == panelName;
- var hwButtons = browser.chrome.$("fbFireUnitButtons");
- collapse(hwButtons, !isHwPanel);
+// Services
+var cache = Cc["@mozilla.org/network/cache-service;1"].getService(Ci.nsICacheService);
+
+// Module implementation.
+//-----------------------------------------------------------------------------
+
+/**
+ * This objects represents a module of Fireunit extension. This object is
+ * responsible for injecting the "fireunit" object into a web page.
+ */
+Firebug.FireUnitModule = extend(Firebug.Module,
+{
+ initialize: function()
+ {
+ if (FBTrace.DBG_FIREUNIT)
+ FBTrace.sysout("fireunit.FireUnitModule.initialize");
+
+ // Add listener for log customization
+ Firebug.TraceModule.addListener(this);
+ },
+
+ shutdown: function()
+ {
+ Firebug.TraceModule.removeListener(this);
+ },
+
+ reattachContext: function(browser, context)
+ {
+ if (browser.detached)
+ {
+ // If Firebug is opened in a new window, the stylesheet must be
+ // appended again.
+ this.addStyleSheets(context.window.document);
+ }
+ },
+
+ showPanel: function(browser, panel)
+ {
+ // xxxHonza: there is one button in toolbar that isn't working yet.
+ var isHwPanel = panel && panel.name == panelName;
+ var hwButtons = browser.chrome.$("fbFireUnitButtons");
+ collapse(hwButtons, !isHwPanel);
},
- watchWindow: function(context, win){
+ watchWindow: function(context, win)
+ {
if (win.wrappedJSObject && win.wrappedJSObject.fireunit)
return;
- function clean( str ) {
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
- }
+ // Inject "fireunit" object into the test page. This object
+ // provides all necessary APIs to write a unit test.
+ win.wrappedJSObject.fireunit = new this.Fireunit(context, win);
+ },
+
+ unWatchWindow: function()
+ {
+ delete win.wrappedJSObject.fireunit;
+ },
+
+ addStyleSheets: function(panel)
+ {
+ this.addStyleSheet(panel.document, "chrome://fireunit/skin/tabView.css", "tabViewCss");
+ this.addStyleSheet(panel.document, "chrome://fireunit/skin/fireunit.css", "fireUnitCss");
+ },
+
+ // xxxHonza: There should be APIs in lib.js to easily append a new stylesheet.
+ addStyleSheet: function(doc, uri, id)
+ {
+ // Make sure the stylesheet isn't appended twice.
+ if ($(id, doc))
+ return;
+
+ var styleSheet = createStyleSheet(doc, uri);
+ styleSheet.setAttribute("id", id);
+ addStyleSheet(doc, styleSheet);
+ },
+
+ /**
+ * Trace console support
+ */
+ onLoadConsole: function(win, rootNode) // Called when console window is loaded.
+ {
+ this.addStyleSheet(rootNode.ownerDocument,
+ "chrome://fireunit/skin/fireunit.css",
+ "fireUnitCss");
+ },
+
+ // Called when a new message is logged in to the trace-console window.
+ onDump: function(message)
+ {
+ var index = message.text.indexOf("fireunit.");
+ if (index == 0) {
+ message.text = message.text.substr("fireunit.".length);
+ message.type = "DBG_FIREUNIT";
+ }
+ }
+});
- var queue = [];
+// Fireunit object implementation.
+//-----------------------------------------------------------------------------
- function addToQueue(fn){}
+/**
+ * This object is injected into the test page as "fireunit" in order to
+ * provider necessary APIs for test implementation.
+ */
+Firebug.FireUnitModule.Fireunit = function(context, win)
+{
+ this.context = context;
+ this.win = win;
+}
- function removeFromQueue(){}
+Firebug.FireUnitModule.Fireunit.prototype = function()
+{
+ var queue = [];
+
+ function addToQueue(fn) {
+ }
- function getServer(){
- if ( !server ) {
+ function removeFromQueue() {
+ }
+
+ function getServer() {
+ if ( !server ) {
server = new nsHttpServer();
server.start( serverPort );
- }
- return server;
- }
-
- function chromeToPath(aPath){
- if (!aPath || !(/^chrome:/.test(aPath)))
- return urlToPath( aPath );
+ }
+ return server;
+ }
- var ios = Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces["nsIIOService"]);
- var uri = ios.newURI(aPath, "UTF-8", null);
- var cr = Components.classes['@mozilla.org/chrome/chrome-registry;1'].getService(Components.interfaces["nsIChromeRegistry"]);
- var rv = cr.convertChromeURL(uri).spec;
+ function chromeToPath(aPath) {
+ if (!aPath || !(/^chrome:/.test(aPath)))
+ return urlToPath( aPath );
- if (/^file:/.test(rv))
- rv = urlToPath(rv);
- else
- rv = urlToPath("file://"+rv);
+ var ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci["nsIIOService"]);
+ var uri = ios.newURI(aPath, "UTF-8", null);
+ var cr = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci["nsIChromeRegistry"]);
+ var rv = cr.convertChromeURL(uri).spec;
- return rv;
- }
+ if (/^file:/.test(rv))
+ rv = urlToPath(rv);
+ else
+ rv = urlToPath("file://"+rv);
- function urlToPath(aPath){
- if (!aPath || !/^file:/.test(aPath)) return;
-
- return Components.classes["@mozilla.org/network/protocol;1?name=file"]
- .createInstance(Components.interfaces.nsIFileProtocolHandler)
- .getFileFromURLSpec(aPath);
- }
+ return rv;
+ }
- var winID = uuid++;
+ function urlToPath(aPath) {
+ if (!aPath || !/^file:/.test(aPath))
+ return;
+
+ return Cc["@mozilla.org/network/protocol;1?name=file"]
+ .createInstance(Ci.nsIFileProtocolHandler)
+ .getFileFromURLSpec(aPath);
+ }
- win.wrappedJSObject.fireunit = {
- forceHttp: function(){
- cache.evictEntries(Components.interfaces.nsICache.STORE_ON_DISK);
- cache.evictEntries(Components.interfaces.nsICache.STORE_IN_MEMORY);
+ var winID = uuid++;
- if ( win.wrappedJSObject.location.protocol !== "http:" ) {
- var file = chromeToPath( win.wrappedJSObject.location + "" );
- var dir = file.parent;
+ // Define fireunit APIs.
+ var fireunit = {
+ forceHttp: function() {
+ cache.evictEntries(Ci.nsICache.STORE_ON_DISK);
+ cache.evictEntries(Ci.nsICache.STORE_IN_MEMORY);
- getServer().registerDirectory( "/test" + winID + "/", dir );
+ if ( this.win.wrappedJSObject.location.protocol !== "http:" ) {
+ var file = chromeToPath( this.win.wrappedJSObject.location + "" );
+ var dir = file.parent;
- win.wrappedJSObject.location = "http://localhost:" + serverPort + "/test" + winID + "/" + file.leafName;
+ getServer().registerDirectory( "/test" + winID + "/", dir );
- return false;
- }
+ this.win.wrappedJSObject.location = "http://localhost:" + serverPort + "/test" +
+ winID + "/" + file.leafName;
- return true;
- },
- runTests: function() {
- testQueue = Array.prototype.slice.call( arguments );
- queueResults = "";
+ return false;
+ }
- this.testDone();
- },
- testDone: function() {
- if ( testQueue ) {
- if ( testQueue.length ) {
- win.wrappedJSObject.location = testQueue.shift();
- } else {
- var panel = context.getPanel(panelName).panelNode;
- panel.innerHTML += queueResults;
- queueResults = testQueue = null;
- }
- }
- },
- id: function( id ) {
- if ( typeof id == "string" ) {
- if ( win.location.toString().indexOf("chrome:") == 0 ) {
- return document.getElementById( id );
- } else {
- return win.document.getElementById( id );
- }
- }
- return id;
- },
- ok: function( pass, msg ){
- var results = "<li><span style='color:" +
- (pass ? "green" : "red") + ";'>" +
- (pass ? "PASS" : "FAIL") + "</span> " +
- clean( msg ) + "</li>";
-
- if ( testQueue ) {
- queueResults += results;
- } else {
- var panel = context.getPanel(panelName).panelNode;
- panel.innerHTML += results;
- }
- },
- test: function( name, fn ) {
- addToQueue( fn );
- },
- compare: function( expected, result, msg ) {
- var pass = expected == result;
- var panel = context.getPanel(panelName).panelNode;
- panel.innerHTML += "<li><span style='color:" +
- (pass ? "green" : "red") + ";'>" +
- (pass ? "PASS" : "FAIL") + "</span> " +
- clean( msg ) +
- (pass ? "" : "<br/><pre style='font-family:Courier;'> Expected: " + clean( expected ) +
- "\n Result: " + clean( result ) + "</pre>") + "</li>";
- },
- reCompare: function( expected, result, msg ) {
- if ( RegExp( expected ).test( result ) ) {
- return this.compare( expected, expected, msg );
- } else {
- return this.compare( expected, result, msg );
- }
- },
- click: function( node ){
- node = this.id( node );
-
- if ( node.click ) {
- return node.click();
- }
-
- var doc = node.ownerDocument, event = doc.createEvent("MouseEvents");
- event.initMouseEvent("click", true, true, doc.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
- return node.dispatchEvent( event );
- },
- focus: function( node ){
- node = this.id( node );
-
- if ( node.focus ) {
- return node.focus();
- }
-
- var doc = node.ownerDocument, event = doc.createEvent("UIEvents");
- event.initUIEvent("DOMFocusIn", true, true, doc.defaultView, 1);
- return node.dispatchEvent( event );
- },
- value: function( node, text ){
- node = this.id( node );
-
- node.value = text;
- },
- key: function( node, letter ){
- node = this.id( node );
-
- var keyCode = letter, charCode = 0;
-
- if ( typeof keyCode == "string" ) {
- charCode = keyCode.charCodeAt(0);
- keyCode = 0;
- }
-
- var doc = node.ownerDocument, event = doc.createEvent("KeyEvents");
- event.initKeyEvent("keypress", true, true, doc.defaultView, false, false, false, false, keyCode, charCode);
- return node.dispatchEvent( event );
- },
- panel: function( name ) {
- // xxxHonza: in case of net panel tests the URL doesn't have to come from chrome.
- //if ( win.location.toString().indexOf("chrome:") == 0 )
- return FirebugContext.getPanel( name ).panelNode;
- },
- // HTTP Server
- registerPathHandler: function(path, handler)
- {
- return getServer().registerPathHandler(path, function(metadata, response) {
- try {
- handler.apply(null, [metadata, response]);
- }
- catch (err) {
- FBTrace.dumpProperties("fireunit.registerPathHandler EXCEPTION", err);
- }
- });
+ return true;
+ },
+ runTests: function() {
+ testQueue = Array.prototype.slice.call( arguments );
+ queueResults = [];
+
+ this.testDone();
+ },
+ testDone: function() {
+ if (FBTrace.DBG_FIREUNIT)
+ FBTrace.sysout("fireunit.testDone: " + this.win.wrappedJSObject.location);
+
+ var panel = this.context.getPanel(panelName);
+ if ( testQueue ) {
+ if ( testQueue.length ) {
+ this.win.wrappedJSObject.location = testQueue.shift();
+ } else {
+ panel.appendResults(queueResults);
+ panel.appendSummary();
+ queueResults = testQueue = null;
}
- };
+ }
+ else {
+ panel.appendSummary();
+ }
+ },
+ id: function( id ) {
+ if ( typeof id == "string" ) {
+ if ( this.win.location.toString().indexOf("chrome:") == 0 ) {
+ return document.getElementById( id );
+ } else {
+ return this.win.document.getElementById( id );
+ }
+ }
+ return id;
+ },
+ ok: function( pass, msg ) {
+ var result = new Firebug.FireUnitModule.TestResult(this.win, pass, msg);
+ if ( testQueue ) {
+ queueResults.push(result);
+ } else {
+ var panel = this.context.getPanel(panelName);
+ panel.appendResults([result]);
+ }
+ },
+ test: function( name, fn ) {
+ addToQueue( fn );
+ },
+ compare: function( expected, actuall, msg ) {
+ var pass = expected == actuall;
+ var result = new Firebug.FireUnitModule.TestResult(this.win, pass, msg,
+ expected, actuall);
+ if ( testQueue ) {
+ queueResults.push(result);
+ } else {
+ var panel = this.context.getPanel(panelName);
+ panel.appendResults([result]);
+ }
+ },
+ reCompare: function( expected, result, msg ) {
+ if ( RegExp( expected ).test( result ) ) {
+ return this.compare( expected, expected, msg );
+ } else {
+ return this.compare( expected, result, msg );
+ }
+ },
+ click: function( node ){
+ node = this.id( node );
- win.wrappedJSObject.fireunit.__defineGetter__("Firebug", function() {
- return Firebug;
- });
+ if ( node.click ) {
+ return node.click();
+ }
- win.wrappedJSObject.fireunit.__defineGetter__("FBL", function() {
- return FBL;
- });
+ var doc = node.ownerDocument, event = doc.createEvent("MouseEvents");
+ event.initMouseEvent("click", true, true, doc.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ return node.dispatchEvent( event );
+ },
+ focus: function( node ){
+ node = this.id( node );
- win.wrappedJSObject.fireunit.__defineGetter__("FirebugChrome", function() {
- return FirebugChrome;
- });
+ if ( node.focus ) {
+ return node.focus();
+ }
- win.wrappedJSObject.fireunit.__defineGetter__("FBTrace", function() {
- return FBTrace;
- });
+ var doc = node.ownerDocument, event = doc.createEvent("UIEvents");
+ event.initUIEvent("DOMFocusIn", true, true, doc.defaultView, 1);
+ return node.dispatchEvent( event );
+ },
+ value: function( node, text ){
+ node = this.id( node );
- win.wrappedJSObject.fireunit.__defineGetter__("FirebugContext", function() {
- return FirebugContext;
- });
- },
-
- unWatchWindow: function(){
- delete win.wrappedJSObject.fireunit;
- }
-});
+ node.value = text;
+ },
+ key: function( node, letter ){
+ node = this.id( node );
+
+ var keyCode = letter, charCode = 0;
+
+ if ( typeof keyCode == "string" ) {
+ charCode = keyCode.charCodeAt(0);
+ keyCode = 0;
+ }
+
+ var doc = node.ownerDocument, event = doc.createEvent("KeyEvents");
+ event.initKeyEvent("keypress", true, true, doc.defaultView, false, false, false,
+ false, keyCode, charCode);
+ return node.dispatchEvent( event );
+ },
+ panel: function( name ) {
+ // xxxHonza: in case of net panel tests the URL doesn't have to come from chrome,
+ // but also from local host.
+ if ( this.win.location.toString().indexOf("chrome:") == 0 ||
+ this.win.location.toString().indexOf("http://localhost:" + serverPort) == 0)
+ return this.context.getPanel( name ).panelNode;
+ },
+ // HTTP Server
+ registerPathHandler: function(path, handler) {
+ return getServer().registerPathHandler(path, function(metadata, response) {
+ try {
+ handler.apply(null, [metadata, response]);
+ }
+ catch (err) {
+ FBTrace.dumpProperties("fireunit.registerPathHandler EXCEPTION", err);
+ }
+ });
+ }
+ };
+
+ // Getters for access to Firebug's internal APIs.
+ fireunit.__defineGetter__("Firebug", function() {
+ return Firebug;
+ });
+
+ fireunit.__defineGetter__("FBL", function() {
+ return FBL;
+ });
+
+ fireunit.__defineGetter__("FirebugChrome", function() {
+ return FirebugChrome;
+ });
+
+ fireunit.__defineGetter__("FBTrace", function() {
+ return FBTrace;
+ });
+
+ fireunit.__defineGetter__("FirebugContext", function() {
+ return FirebugContext;
+ });
+
+ return fireunit;
+}();
+
+// Localization
+//-----------------------------------------------------------------------------
+
+// xxxHonza: There should be APIs in lib.js to easily get localized strings
+// from custom bundle.
+function $FU_STR(name)
+{
+ try
+ {
+ return document.getElementById("strings_fireUnit").getString(name.replace(' ', '_', "g"));
+ }
+ catch (err)
+ {
+ if (FBTrace.DBG_FIREUNIT)
+ {
+ FBTrace.sysout("fireunit.Missing translation for: " + name + "\n");
+ FBTrace.dumpProperties("fireunit.getString FAILS ", err);
+ }
+ }
+
+ // Use only the label after last dot.
+ var index = name.lastIndexOf(".");
+ if (index > 0)
+ name = name.substr(index + 1);
+
+ return name;
+}
+
+// Panel implementation
+//-----------------------------------------------------------------------------
/**
- * Panel implementation
+ * This object representes a new Firebug panel that displyas list of logs
+ * (test results) coming from executed tests. The panel also implements two
+ * options "Passing Tests" and "Failing Tests" that can be used to filter
+ * the content.
*/
function FireUnitPanel() {}
-FireUnitPanel.prototype = extend(Firebug.Panel, {
- name: panelName,
- title: panelName,
+FireUnitPanel.prototype = extend(Firebug.Panel,
+{
+ name: panelName,
+ title: $FU_STR("fireunit.panel.Test"),
- initialize: function() {
+ initialize: function()
+ {
Firebug.Panel.initialize.apply(this, arguments);
+
+ // Append custom stylesheet.
+ Firebug.FireUnitModule.addStyleSheets(this);
+
+ // Create basic content for the panel.
+ var rep = Firebug.FireUnitModule.TestResultRep;
+ this.table = rep.tableTag.replace({}, this.panelNode, rep);
},
- getOptionsMenuItems: function(context){
+ show: function(state)
+ {
+ this.updatePanelFilter();
+ },
+
+ // Called automatically by Firebug framework when a preference (from Firebug.prefDomain)
+ // is changed.
+ updateOption: function(name, value)
+ {
+ if (FBTrace.DBG_FIREUNIT)
+ FBTrace.sysout("fireunit.FireUnitPanel.updateOption: " + name + ": " + value);
+
+ if (name == "fireunit.showPass" || name == "fireunit.showFail")
+ this.updatePanelFilter();
+ },
+
+ updatePanelFilter: function()
+ {
+ var showPass = Firebug.getPref(Firebug.prefDomain, "fireunit.showPass");
+ var showFail = Firebug.getPref(Firebug.prefDomain, "fireunit.showFail");
+
+ // Update styles on the root table (contains the list of results).
+ // These styles ensure proper visibility of pass and fail tests according
+ // to the preferences.
+ var panelNode = this.context.getPanel(panelName).panelNode;
+ var table = getElementByClass(panelNode, "testTable");
+ showPass ? setClass(table, "showPass") : removeClass(table, "showPass");
+ showFail ? setClass(table, "showFail") : removeClass(table, "showFail");
+ },
+
+ getOptionsMenuItems: function(context)
+ {
return [
- this.optionMenu("Passing Tests", "fireunit.showPass"),
- this.optionMenu("Failing Tests", "fireunit.showFail")
+ this.optionMenu($FU_STR("fireunit.option.Passing_Tests"), "fireunit.showPass"),
+ this.optionMenu($FU_STR("fireunit.option.Failing_Tests"), "fireunit.showFail")
];
},
- optionMenu: function(label, option){
+ optionMenu: function(label, option)
+ {
var value = Firebug.getPref(Firebug.prefDomain, option);
- return {
- label: label,
- nol10n: true,
- type: "checkbox",
- checked: value,
- command: bindFixed(Firebug.setPref, this, Firebug.prefDomain, option, !value)
+ return {
+ label: label,
+ nol10n: true,
+ type: "checkbox",
+ checked: value,
+ command: bindFixed(Firebug.setPref, this, Firebug.prefDomain, option, !value)
};
+ },
+
+ appendResults: function(queueResults)
+ {
+ // Append new test results.
+ var tbody = this.table.firstChild;
+ var row = Firebug.FireUnitModule.TestResultRep.resultTag.insertRows(
+ {results: queueResults}, tbody.lastChild ? tbody.lastChild : tbody)[0];
+
+ scrollToBottom(this.panelNode);
+ },
+
+ appendSummary: function()
+ {
+ var tbody = this.table.firstChild;
+
+ // Count number of passing and failing tests.
+ var summary = { passing: 0, failing: 0 };
+ for (var row = tbody.firstChild; row; row = row.nextSibling) {
+ if (hasClass(row, "testResultRow"))
+ hasClass(row, "testError") ? summary.failing++ : summary.passing++;
+ }
+
+ // Append summary row.
+ var summaryRow = Firebug.FireUnitModule.TestResultRep.summaryTag.insertRows(
+ {summary: summary}, tbody.lastChild ? tbody.lastChild : tbody)[0];
+
+ // Activate our panel since this is what the user wants to see now.
+ this.context.chrome.selectPanel(panelName);
+ scrollToBottom(this.panelNode);
}
});
+// Domplate Repository
+//-----------------------------------------------------------------------------
+
+/**
+ * This template represents a "test-result" that is beening displayed within
+ * Fireunit's panel. Expandable and collapsible logic associated with each
+ * result is also implemented by this object.
+ */
+Firebug.FireUnitModule.TestResultRep = domplate(Firebug.Rep,
+{
+ tableTag:
+ TABLE({class: "testTable", cellpadding: 0, cellspacing: 0, onclick: "$onClick"},
+ TBODY()
+ ),
+
+ resultTag:
+ FOR("result", "$results",
+ TR({class: "testResultRow", _repObject: "$result",
+ $testError: "$result|isError",
+ $testOK: "$result|isOK"},
+ TD({class: "testResultCol", width: "100%"},
+ DIV({class: "testResultMessage testResultLabel"},
+ "$result|getMessage"
+ )
+ ),
+ TD({class: "testResultCol"},
+ DIV({class: "testResultFileName testResultLabel"},
+ "$result.fileName"
+ )
+ )
+ )
+ ),
+
+ resultInfoTag:
+ TR({class: "testResultInfoRow", _repObject: "$result",
+ $testError: "$result|isError"},
+ TD({class: "testResultInfoCol", colspan: 2})
+ ),
+
+ summaryTag:
+ TR({class: "testResultSummaryRow testResultRow"},
+ TD({class: "testResultCol", colspan: 2},
+ SPAN({class: "testResultSummaryLabel",
+ $summaryPass: "$summary|summaryPassed"},
+ $FU_STR("fireunit.option.Passing_Tests"),
+ ": $summary.passing"
+ ),
+ SPAN({class: "testResultSummaryLabel",
+ $collapsed: "$summary|summaryPassed",
+ $testError: "$summary.failing"},
+ $FU_STR("fireunit.option.Failing_Tests"),
+ ": $summary.failing"
+ )
+ )
+ ),
+
+ getMessage: function(result)
+ {
+ return result.msg;
+ },
+
+ isError: function(result)
+ {
+ return !result.pass;
+ },
+
+ isOK: function(result)
+ {
+ return result.pass;
+ },
+
+ summaryPassed: function(summary)
+ {
+ return !summary.failing;
+ },
+
+ onClick: function(event)
+ {
+ if (isLeftClick(event))
+ {
+ var row = getAncestorByClass(event.target, "testResultRow");
+ if (row)
+ {
+ this.toggleResultRow(row);
+ cancelEvent(event);
+ }
+ }
+ },
+
+ toggleResultRow: function(row)
+ {
+ var result = row.repObject;
+
+ toggleClass(row, "opened");
+ if (hasClass(row, "opened"))
+ {
+ var infoBodyRow = this.resultInfoTag.insertRows({result: result}, row)[0];
+ infoBodyRow.repObject = result;
+ this.initInfoBody(infoBodyRow);
+ }
+ else
+ {
+ var infoBodyRow = row.nextSibling;
+ var netInfoBox = getElementByClass(infoBodyRow, "testResultInfoBody");
+ row.parentNode.removeChild(infoBodyRow);
+ }
+ },
+
+ initInfoBody: function(infoBodyRow)
+ {
+ var result = infoBodyRow.repObject;
+ var TabView = Firebug.FireUnitModule.TestResultTabView;
+ var tabViewNode = TabView.viewTag.replace({result: result}, infoBodyRow.firstChild, TabView);
+
+ // Select default tab.
+ TabView.selectTabByName(tabViewNode, "Stack");
+ },
+
+ // Firebug rep support
+ supportsObject: function(testResult)
+ {
+ return testResult instanceof Firebug.FireUnitModule.TestResult;
+ },
+
+ browseObject: function(testResult, context)
+ {
+ return false;
+ },
+
+ getRealObject: function(testResult, context)
+ {
+ return testResult;
+ },
+
+ getContextMenuItems: function(testResult, target, context)
+ {
+ // xxxHonza: The "copy" command shouldn't be there for now.
+ var popup = $("fbContextMenu");
+ FBL.eraseNode(popup);
+
+ var items = [];
+
+ if (testResult.stack)
+ {
+ items.push({
+ label: $FU_STR("fireunit.item.View_Source"),
+ nol10n: true,
+ command: bindFixed(this.onViewSource, this, testResult)
+ });
+ }
+
+ return items;
+ },
+
+ // Context menu commands
+ onViewSource: function(testResult)
+ {
+ var stackFrame = testResult.stack[0];
+ FirebugContext.chrome.select(new SourceLink(stackFrame.fileName,
+ stackFrame.lineNumber, "js"));
+ },
+});
+
+//-----------------------------------------------------------------------------
+
+/**
+ * This template represents an "info-body" for expanded test-result. This
+ * object also implements logic related to a tab view.
+ *
+ * xxxHonza: since the tab view is used already used several times, it would
+ * be very useful to have a TabView widget defined in Firebug's Domplate
+ * repository.
+ */
+Firebug.FireUnitModule.TestResultTabView = domplate(Firebug.Rep,
+{
+ listeners: [],
+
+ viewTag:
+ TABLE({"class": "tabView", cellpadding: 0, cellspacing: 0},
+ TBODY(
+ TR({"class": "tabViewRow"},
+ TD({"class": "tabViewCol", valign: "top"},
+ TAG("$tabList", {result: "$result"})
+ )
+ )
+ )
+ ),
+
+ tabList:
+ DIV({"class": "tabViewBody"},
+ TAG("$tabBar", {result: "$result"}),
+ TAG("$tabBodies")
+ ),
+
+ // List of tabs
+ tabBar:
+ DIV({"class": "tabBar"},
+ A({class: "StackTab tab", onclick: "$onClickTab",
+ view: "Stack", $collapsed: "$result|hideStackTab"},
+ $FU_STR("fireunit.tab.Stack")
+ ),
+ A({class: "CompareTab tab", onclick: "$onClickTab",
+ view: "Compare", $collapsed: "$result|hideCompareTab"},
+ $FU_STR("fireunit.tab.Compare")
+ )
+ ),
+
+ // List of tab bodies
+ tabBodies:
+ DIV({"class": "tabBodies"},
+ DIV({class: "tabStackBody tabBody"}),
+ DIV({class: "tabCompareBody tabBody"})
+ ),
+
+ // Stack tab displayed within resultInfoRow
+ stackTag:
+ TABLE({class: "testResultStackInfoBody", cellpadding: 0, cellspacing: 0},
+ TBODY(
+ FOR("stack", "$result.stack",
+ TR(
+ TD(
+ A({class: "stackFrameLink", onclick: "$onClickStackFrame",
+ lineNumber: "$stack.lineNumber"},
+ "$stack.fileName"),
+ SPAN("&nbsp;"),
+ SPAN("(", "$stack.lineNumber", ")")
+ )
+ )
+ )
+ )
+ ),
+
+ // Compare tab displayed within resultInfoRow
+ compareTag:
+ TABLE({class: "testResultCompareInfoBody", cellpadding: 0, cellspacing: 0},
+ TBODY(
+ TR({class: "testResultCompareTitle expected"},
+ TD(
+ $FU_STR("fireunit.title.Expected")
+ ),
+ TD({class: "testResultCompareSwitch expected",
+ onclick: "$onSwitchView"},
+ $FU_STR("fireunit.switch.view_source")
+ )
+ ),
+ TR(
+ TD({class: "testResultExpected", colspan: 2})
+ ),
+ TR({class: "testResultCompareTitle result"},
+ TD(
+ $FU_STR("fireunit.title.Result")
+ ),
+ TD({class: "testResultCompareSwitch result",
+ onclick: "$onSwitchView"},
+ $FU_STR("fireunit.switch.view_source")
+ )
+ ),
+ TR(
+ TD({class: "testResultResult", colspan: 2})
+ ),
+ TR({class: "testResultCompareTitle diff",
+ $collapsed: "$result|hideDiffGroup"},
+ TD({colspan: 2},
+ $FU_STR("fireunit.title.Difference")
+ )
+ ),
+ TR(
+ TD({class: "testResultDiff", colspan: 2})
+ )
+ )
+ ),
+
+ hideStackTab: function(result)
+ {
+ return false;
+ },
+
+ hideCompareTab: function(result)
+ {
+ return !(result.expected && result.result);
+ },
+
+ hideDiffGroup: function(result)
+ {
+ return (result.expected == result.result);
+ },
+
+ onClickTab: function(event)
+ {
+ this.selectTab(event.target);
+ },
+
+ selectTabByName: function(tabView, tabName)
+ {
+ var tab = getElementByClass(tabView, tabName + "Tab");
+ if (tab)
+ this.selectTab(tab);
+ },
+
+ selectTab: function(tab)
+ {
+ var view = tab.getAttribute("view");
+ var viewBody = getAncestorByClass(tab, "tabViewBody");
+
+ // Deactivate current tab.
+ if (viewBody.selectedTab)
+ {
+ viewBody.selectedTab.removeAttribute("selected");
+ viewBody.selectedBody.removeAttribute("selected");
+ }
+
+ // Store info about new active tab. Each tab has to have a body,
+ // which is identified by class.
+ var tabBody = getElementByClass(viewBody, "tab" + view + "Body");
+ viewBody.selectedTab = tab;
+ viewBody.selectedBody = tabBody;
+
+ // Activate new tab.
+ viewBody.selectedTab.setAttribute("selected", "true");
+ viewBody.selectedBody.setAttribute("selected", "true");
+
+ this.updateTabBody(viewBody, view);
+ },
+
+ updateTabBody: function(viewBody, tabName)
+ {
+ if (FBTrace.DBG_FIREUNIT)
+ FBTrace.sysout("fireunit.TestResultRep.onUpdateTabBody: " + tabName);
+
+ var tab = viewBody.selectedTab;
+ var infoRow = getAncestorByClass(viewBody, "testResultInfoRow");
+ var result = infoRow.repObject;
+
+ // Update Stack tab content
+ var tabStackBody = getElementByClass(viewBody, "tabStackBody");
+ if (tabName == "Stack" && !tabStackBody.updated)
+ {
+ tabStackBody.updated = true;
+ this.stackTag.replace({result: result}, tabStackBody, this);
+ }
+
+ // Update Compare tab content
+ var tabCompareBody = getElementByClass(viewBody, "tabCompareBody");
+ if (tabName == "Compare" && !tabCompareBody.updated)
+ {
+ tabCompareBody.updated = true;
+ this.compareTag.replace({result: result}, tabCompareBody, this);
+
+ this.insertXml(result.expected, getElementByClass(viewBody, "testResultExpected"));
+ this.insertXml(result.result, getElementByClass(viewBody, "testResultResult"));
+
+ // The diff is generated only if there are any differences.
+ if (result.expected != result.result) {
+ var diffNode = getElementByClass(viewBody, "testResultDiff");
+ var diffText = diffString(clean(result.expected), clean(result.result));
+ insertWrappedText(diffText, diffNode, true);
+ }
+ }
+ },
+
+ onSwitchView: function(event)
+ {
+ var target = event.target;
+ var expected = hasClass(target, "expected");
+ var infoRow = getAncestorByClass(target, "testResultInfoRow");
+ var result = infoRow.repObject;
+ var sourceBody = getElementByClass(infoRow, expected ? "testResultExpected" : "testResultResult");
+
+ clearNode(sourceBody);
+
+ if (target.sourceView)
+ this.insertXml(result.expected, sourceBody);
+ else
+ insertWrappedText(expected ? result.expected : result.result, sourceBody);
+
+ target.innerHTML = $FU_STR("fireunit.switch." + (target.sourceView ? "view_source" : "pretty_print"));
+ target.sourceView = !target.sourceView;
+ },
+
+ onClickStackFrame: function(event)
+ {
+ FirebugContext.chrome.select(new SourceLink(event.target.innerHTML,
+ event.target.getAttribute("lineNumber"), "js" ))
+ },
+
+ insertXml: function(xml, parentNode)
+ {
+ var parser = CCIN("@mozilla.org/xmlextras/domparser;1", "nsIDOMParser");
+
+ // Create helper root element (for the case where there is no signle root).
+ var tempXml = "<wrapper>" + xml + "</wrapper>";
+ var doc = parser.parseFromString(tempXml, "text/xml");
+ var docElem = doc.documentElement;
+
+ // Error handling
+ var nsURI = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
+ if (docElem.namespaceURI == nsURI && docElem.nodeName == "parsererror")
+ {
+ var errorNode = Firebug.FireUnitModule.ParseErrorRep.tag.replace({error: {
+ message: docElem.firstChild.nodeValue,
+ source: docElem.lastChild.textContent
+ }}, parentNode);
+
+ var xmlSource = getElementByClass(errorNode, "xmlInfoSource");
+ insertWrappedText(xml, xmlSource);
+ return;
+ }
+
+ // Generate UI. Get appropriate domplate tag for every element that is found
+ // within the helper <wrapper> and append it into the parent container.
+ for (var i=0; i<docElem.childNodes.length; i++)
+ Firebug.HTMLPanel.CompleteElement.getNodeTag(docElem.childNodes[i]).
+ append({object: docElem.childNodes[i]}, parentNode);
+ }
+});
+
+//-----------------------------------------------------------------------------
+
+/**
+ * This template displays a parse-erros that can occurs when parsing
+ * expected and acuall results (see fireunit.compare method).
+ */
+Firebug.FireUnitModule.ParseErrorRep = domplate(Firebug.Rep,
+{
+ tag:
+ DIV({class: "xmlInfoError"},
+ DIV({class: "xmlInfoErrorMsg"}, "$error.message"),
+ PRE({class: "xmlInfoErrorSource"}, "$error|getSource"),
+ BR(),
+ PRE({class: "xmlInfoSource"})
+ ),
+
+ getSource: function(error)
+ {
+ var parts = error.source.split("\n");
+ if (parts.length != 2)
+ return error.source;
+
+ var limit = 50;
+ var column = parts[1].length;
+ if (column >= limit) {
+ parts[0] = "..." + parts[0].substr(column - limit);
+ parts[1] = "..." + parts[1].substr(column - limit);
+ }
+
+ if (parts[0].length > 80)
+ parts[0] = parts[0].substr(0, 80) + "...";
+
+ return parts.join("\n");
+ }
+});
+
+// Helper Objects
+//-----------------------------------------------------------------------------
+
/**
- * Registration
+ * This object represents a test-result.
*/
+Firebug.FireUnitModule.TestResult = function(win, pass, msg, expected, result)
+{
+ var location = win.wrappedJSObject.location.href;
+ this.fileName = location.substr(location.lastIndexOf("/") + 1);
+
+ this.pass = pass ? true : false;
+ this.msg = clean(msg);
+ this.expected = expected;
+ this.result = result;
+
+ // xxxHonza: there should be perhaps simple API in lib.js to get the stack trace.
+ this.stack = [];
+ for (var frame = Components.stack, i=0; frame; frame = frame.caller, i++)
+ {
+ var fileName = unescape(frame.filename ? frame.filename : "");
+ if (fileName == "chrome://fireunit/content/fireunit.js")
+ continue;
+
+ var lineNumber = frame.lineNumber ? frame.lineNumber : "";
+ this.stack.push({fileName:fileName, lineNumber:lineNumber});
+ }
+}
+
+// Utils
+//-----------------------------------------------------------------------------
+
+function clean( str )
+{
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
+}
+
+// Registration
+//-----------------------------------------------------------------------------
+
Firebug.registerPanel(FireUnitPanel);
Firebug.registerModule(Firebug.FireUnitModule);
+Firebug.registerRep(Firebug.FireUnitModule.TestResultRep);
+
+//-----------------------------------------------------------------------------
}});
View
5 chrome/content/fireunit/fireunit.xul
@@ -3,7 +3,12 @@
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="chrome://fireunit/content/httpd.js" type="application/x-javascript"/>
<script src="chrome://fireunit/content/fireunit.js" type="application/x-javascript"/>
+ <script src="chrome://fireunit/content/jsdiff.js" type="application/x-javascript"/>
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="strings_fireUnit" src="chrome://fireunit/locale/fireunit.properties"/>
+ </stringbundleset>
+
<commandset id="mainCommandSet">
<command id="cmd_hwReRunTests" oncommand="Firebug.FireUnitModel.onReRunTests(FirebugContext)"/>
</commandset>
View
161 chrome/content/fireunit/jsdiff.js
@@ -0,0 +1,161 @@
+/*
+ * Javascript Diff Algorithm
+ * By John Resig (http://ejohn.org/)
+ * Modified by Chu Alan "sprite"
+ *
+ * More Info:
+ * http://ejohn.org/projects/javascript-diff-algorithm/
+ */
+
+function escape(s) {
+ return s;
+
+/* var n = s;
+ n = n.replace(/&/g, "&amp;");
+ n = n.replace(/</g, "&lt;");
+ n = n.replace(/>/g, "&gt;");
+ n = n.replace(/"/g, "&quot;");
+
+ return n;*/
+}
+
+function diffString( o, n ) {
+ o = o.replace(/\s+$/, '');
+ n = n.replace(/\s+$/, '');
+
+ var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) );
+ var str = "";
+
+ var oSpace = o.match(/\s+/g);
+ if (oSpace == null) {
+ oSpace = ["\n"];
+ } else {
+ oSpace.push("\n");
+ }
+ var nSpace = n.match(/\s+/g);
+ if (nSpace == null) {
+ nSpace = ["\n"];
+ } else {
+ nSpace.push("\n");
+ }
+
+ if (out.n.length == 0) {
+ for (var i = 0; i < out.o.length; i++) {
+ str += '<del>' + escape(out.o[i]) + oSpace[i] + "</del>";
+ }
+ } else {
+ if (out.n[0].text == null) {
+ for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
+ str += '<del>' + escape(out.o[n]) + oSpace[n] + "</del>";
+ }
+ }
+
+ for ( var i = 0; i < out.n.length; i++ ) {
+ if (out.n[i].text == null) {
+ str += '<ins>' + escape(out.n[i]) + nSpace[i] + "</ins>";
+ } else {
+ var pre = "";
+
+ for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
+ pre += '<del>' + escape(out.o[n]) + oSpace[n] + "</del>";
+ }
+ str += " " + out.n[i].text + nSpace[i] + pre;
+ }
+ }
+ }
+
+ return str;
+}
+
+function randomColor() {
+ return "rgb(" + (Math.random() * 100) + "%, " +
+ (Math.random() * 100) + "%, " +
+ (Math.random() * 100) + "%)";
+}
+function diffString2( o, n ) {
+ o = o.replace(/\s+$/, '');
+ n = n.replace(/\s+$/, '');
+
+ var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) );
+
+ var oSpace = o.match(/\s+/g);
+ if (oSpace == null) {
+ oSpace = ["\n"];
+ } else {
+ oSpace.push("\n");
+ }
+ var nSpace = n.match(/\s+/g);
+ if (nSpace == null) {
+ nSpace = ["\n"];
+ } else {
+ nSpace.push("\n");
+ }
+
+ var os = "";
+ var colors = new Array();
+ for (var i = 0; i < out.o.length; i++) {
+ colors[i] = randomColor();
+
+ if (out.o[i].text != null) {
+ os += '<span style="background-color: ' +colors[i]+ '">' +
+ escape(out.o[i].text) + oSpace[i] + "</span>";
+ } else {
+ os += "<del>" + escape(out.o[i]) + oSpace[i] + "</del>";
+ }
+ }
+
+ var ns = "";
+ for (var i = 0; i < out.n.length; i++) {
+ if (out.n[i].text != null) {
+ ns += '<span style="background-color: ' +colors[out.n[i].row]+ '">' +
+ escape(out.n[i].text) + nSpace[i] + "</span>";
+ } else {
+ ns += "<ins>" + escape(out.n[i]) + nSpace[i] + "</ins>";
+ }
+ }
+
+ return { o : os , n : ns };
+}
+
+function diff( o, n ) {
+ var ns = new Object();
+ var os = new Object();
+
+ for ( var i = 0; i < n.length; i++ ) {
+ if ( ns[ n[i] ] == null )
+ ns[ n[i] ] = { rows: new Array(), o: null };
+ ns[ n[i] ].rows.push( i );
+ }
+
+ for ( var i = 0; i < o.length; i++ ) {
+ if ( os[ o[i] ] == null )
+ os[ o[i] ] = { rows: new Array(), n: null };
+ os[ o[i] ].rows.push( i );
+ }
+
+ for ( var i in ns ) {
+ if ( ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1 ) {
+ n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] };
+ o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] };
+ }
+ }
+
+ for ( var i = 0; i < n.length - 1; i++ ) {
+ if ( n[i].text != null && n[i+1].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
+ n[i+1] == o[ n[i].row + 1 ] ) {
+ n[i+1] = { text: n[i+1], row: n[i].row + 1 };
+ o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 };
+ }
+ }
+
+ for ( var i = n.length - 1; i > 0; i-- ) {
+ if ( n[i].text != null && n[i-1].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
+ n[i-1] == o[ n[i].row - 1 ] ) {
+ n[i-1] = { text: n[i-1], row: n[i].row - 1 };
+ o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 };
+ }
+ }
+
+ return { o: o, n: n };
+}
+
View
41 chrome/content/fireunit/test/commandline.html
@@ -23,8 +23,11 @@
function checkText( str, msg ) {
var divs = fireunit.panel("console").getElementsByTagName("div");
- var result = divs[ divs.length - 1 ].innerHTML;
- fireunit.reCompare( str, result, msg );
+ fireunit.ok( divs.length > 0, msg + " " + str);
+ if (divs.length) {
+ var result = divs[ divs.length - 1 ].innerHTML;
+ fireunit.reCompare( str, result, msg );
+ }
}
function clear(){
@@ -116,30 +119,32 @@
clear();
// DOM Elements
- fireunit.value( "fbCommandLine", "document.getElementsByTagName('div')[0]" );
- fireunit.key( "fbCommandLine", 13 );
- checkText( '<a class="objectLink objectLink-element">&lt;<span class="nodeTag">div</span>&gt;</a>', "DOM Element verified." );
+ fireunit.test("DOM Elements", function(){
+ fireunit.value( "fbCommandLine", "document.getElementsByTagName('div')[0]" );
+ fireunit.key( "fbCommandLine", 13 );
+ checkText( '<a class="objectLink objectLink-element">&lt;<span class="nodeTag">div</span>&gt;</a>', "DOM Element verified." );
- fireunit.value( "fbCommandLine", "document.getElementsByTagName('div')[1]" );
- fireunit.key( "fbCommandLine", 13 );
- checkText( '<a class="objectLink objectLink-element">&lt;<span class="nodeTag">div</span>&nbsp;id="<span class="nodeValue">first</span>"&nbsp;classname="<span class="nodeValue">test</span>"&nbsp;style="<span class="nodeValue">color: red;</span>"&gt;</a>', "DOM Element verified." );
+ fireunit.value( "fbCommandLine", "document.getElementsByTagName('div')[1]" );
+ fireunit.key( "fbCommandLine", 13 );
+ checkText( '<a class="objectLink objectLink-element">&lt;<span class="nodeTag">div</span>&nbsp;id="<span class="nodeValue">first</span>"&nbsp;classname="<span class="nodeValue">test</span>"&nbsp;style="<span class="nodeValue">color: red;</span>"&gt;</a>', "DOM Element verified." );
- fireunit.value( "fbCommandLine", "document.getElementsByTagName('div')[1]" );
- fireunit.key( "fbCommandLine", 13 );
- checkText( '<a class="objectLink objectLink-element">&lt;<span class="nodeTag">div</span>&nbsp;id="<span class="nodeValue">first</span>"&nbsp;classname="<span class="nodeValue">test</span>"&nbsp;style="<span class="nodeValue">color: red;</span>"&gt;</a>', "DOM Element verified." );
+ fireunit.value( "fbCommandLine", "document.getElementsByTagName('div')[1]" );
+ fireunit.key( "fbCommandLine", 13 );
+ checkText( '<a class="objectLink objectLink-element">&lt;<span class="nodeTag">div</span>&nbsp;id="<span class="nodeValue">first</span>"&nbsp;classname="<span class="nodeValue">test</span>"&nbsp;style="<span class="nodeValue">color: red;</span>"&gt;</a>', "DOM Element verified." );
- fireunit.value( "fbCommandLine", "document.getElementsByTagName('div')[2]" );
- fireunit.key( "fbCommandLine", 13 );
-setTimeout(function(){
- checkText( '<a class="objectLink objectLink-element">&lt;<span class="nodeTag">div</span>&nbsp;id="<span class="nodeValue">last</span>"&gt;</a>', "DOM Element verified." );
+ fireunit.value( "fbCommandLine", "document.getElementsByTagName('div')[2]" );
+ fireunit.key( "fbCommandLine", 13 );
+ checkText( '<a class="objectLink objectLink-element">&lt;<span class="nodeTag">div</span>&nbsp;id="<span class="nodeValue">last</span>"&gt;</a>', "DOM Element verified." );
-}, 100);
- clear();
+ clear();
+ });
// Trigger large console
//fireunit.type( "fbCommandLine", "alert('test');\n" );
- Application.prefs.setValue("extensions.firebug.throttleMessages", oldThrottle);
+ fireunit.queue(function(){
+ Application.prefs.setValue("extensions.firebug.throttleMessages", oldThrottle);
+ });
}, 500);
};
</script>
View
40 chrome/content/fireunit/test/inspect.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<script src="chrome://firebug/content/xpcom.js"></script>
+<script src="chrome://firebug/content/lib.js"></script>
+<script src="chrome://firebug/content/chrome.js"></script>
+<script src="chrome://firebug/content/domplate.js"></script>
+<script>
+var state = false;
+
+// URL Redirection
+// URL Reloading
+// Async tests
+
+fireunit.click( "fbStatusBar" );
+fireunit.focus( "fbCommandLine" );
+
+setTimeout(function(){
+ // Verify basic command execution
+ state = false;
+ fireunit.value( "fbCommandLine", "state = true;" );
+ fireunit.key( "fbCommandLine", 13 );
+ fireunit.ok( state, "Console command executed." );
+
+ // Trigger large console
+ //fireunit.type( "fbCommandLine", "alert('test');\n" );
+}, 500);
+</script>
+
+</head>
+
+<body>
+ <div id="first">===</div>
+ <div id="last">---</div>
+ <textarea id="text">test</textarea>
+</body>
+</html>
View
6 chrome/content/fireunit/test/net/env.js
@@ -0,0 +1,6 @@
+/*
+ * Author: Jan Odvarko, www.janodvarko.cz
+ */
+
+var layoutTimeout = 300;
+var prefDomain = "extensions.firebug";
View
63 chrome/content/fireunit/test/net/issue1256.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script src="utils.js" type="text/javascript"></script>
+<script src="env.js" type="text/javascript"></script>
+<script>
+netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+var request = new XMLHttpRequest();
+request.onreadystatechange = function () {
+ if (request.readyState == 4 && request.status == 200)
+ setTimeout(function() {
+ checkParamsTab(request);
+ }, layoutTimeout);
+};
+request.open("POST", "issue1256.txt", true);
+request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+request.send("param1=1 %2B 2");
+
+function checkParamsTab(request)
+{
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ fireunit.FBTrace.sysout("fireunit.issue1256.checkParamsTab", request);
+
+ // Open Firebug UI and activate Net panel.
+ fireunit.Firebug.showBar(true);
+ fireunit.FirebugChrome.selectPanel("net");
+
+ // Get Net panel content
+ var panelNode = fireunit.panel("net");
+ fireunit.ok(panelNode, "Net panel must exist.");
+
+ // Expand the test request with params
+ var netRow = fireunit.FBL.getElementByClass(panelNode, "netRow", "category-xhr",
+ "hasHeaders", "loaded");
+ fireunit.ok(netRow, "There must be just one xhr request.");
+ fireunit.click(netRow);
+
+ // Activate Params tab.
+ var netInfoRow = netRow.nextSibling;
+ expandNetTabs(netInfoRow, "netInfoPostTab");
+
+ var postTable = fireunit.FBL.getElementByClass(netInfoRow, "netInfoPostTable");
+ fireunit.ok(postTable, "Info body must be created at this moment.");
+ if (postTable) {
+ var paramName = fireunit.FBL.getElementByClass(postTable, "netInfoParamName").textContent;
+ var paramValue = fireunit.FBL.getElementByClass(postTable, "netInfoParamValue").textContent;
+
+ fireunit.compare("param1", paramName, "The parameter name must be 'param1'.");
+ fireunit.compare("1 + 2", paramValue, "The parameter value must be '1 + 2'");
+ }
+
+ // Finish test
+ fireunit.FBTrace.sysout("fireunit.issue1256.DONE");
+ fireunit.testDone();
+}
+</script>
+</head>
+<body>
+<h1>Test Case for Issue #1256</h1>
+</body>
+</html>
View
1  chrome/content/fireunit/test/net/issue1256.txt
@@ -0,0 +1 @@
+Response for issue1256.html
View
95 chrome/content/fireunit/test/net/issue176.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script src="utils.js" type="text/javascript"></script>
+<script src="env.js" type="text/javascript"></script>
+<script>
+netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+// Get current request URI without the file extension.
+var index = location.pathname.lastIndexOf(".");
+var requestUri = location.pathname.substr(0, index);
+
+// Implementation of a request handler (serve side)
+function requestHandler(metadata, response) {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+ var path = metadata.path;
+ fireunit.FBTrace.sysout("fireunit.issue176.pathHandler executed: " + path,
+ [metadata, response]);
+
+ var extension = path.substr(path.lastIndexOf(".") + 1);
+ response.setHeader("Content-Type", (extension == "txt" ? "video/x-flv" : ""), false);
+ response.write("counter++;");
+}
+
+// Register request handler for two different URLs
+fireunit.registerPathHandler(requestUri + ".flv", requestHandler);
+fireunit.registerPathHandler(requestUri + ".txt", requestHandler);
+
+var counter = 0;
+var uris = ["issue176.txt", "issue176.flv"];
+
+// Embed two files into the page as scripts.
+var head = document.getElementsByTagName("head")[0];
+for (var i=0; i<uris.length; i++) {
+ script = document.createElement("script");
+ script.setAttribute("type", "text/javascript");
+ script.setAttribute("src", uris[i]);
+ head.appendChild(script);
+}
+
+// Wait till the response is returned from the server.
+var maxPendingCheck = 10;
+function waitForResponse() {
+ setTimeout(function() {
+ if (counter > 1) {
+ checkNetPanelUI();
+ }
+ else {
+ if (--maxPendingCheck > 0)
+ waitForResponse();
+ }
+ }, layoutTimeout);
+};
+waitForResponse();
+
+// Make sure the Net panel's UI is properly filtered.
+function checkNetPanelUI()
+{
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ fireunit.FBTrace.sysout("fireunit.issue176.checkNetPanelUI " + counter);
+
+ // Open Firebug UI and activate Net panel.
+ fireunit.Firebug.showBar(true);
+ fireunit.FirebugChrome.selectPanel("net");
+
+ // Set "Flash" filter
+ fireunit.Firebug.NetMonitor.onToggleFilter(fireunit.FirebugContext, "flash");
+
+ // Wait for relayout.
+ setTimeout(function()
+ {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+ // Get Net panel content
+ var panelNode = fireunit.panel("net");
+ fireunit.ok(panelNode, "Net panel must exist.");
+
+ // Check number of requests. Must be exactly two.
+ var netRows = getElementsByClass(panelNode, "netRow", "category-flash", "hasHeaders", "loaded");
+ fireunit.ok(netRows.length == 2, "There must be exactly two requests displayed!");
+
+ // Finish test
+ fireunit.Firebug.NetMonitor.onToggleFilter(fireunit.FirebugContext, "all");
+ fireunit.FBTrace.sysout("fireunit.issue176.DONE");
+ fireunit.testDone();
+ }, layoutTimeout);
+}
+</script>
+</head>
+<body>
+<h1>Test Case for Issue #176</h1>
+</body>
+</html>
View
63 chrome/content/fireunit/test/net/issue372.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script src="utils.js" type="text/javascript"></script>
+<script src="env.js" type="text/javascript"></script>
+<script>
+netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+// Prepare test data.
+var xml = "<root><test id=\"1\"/></root>";
+var parser = new DOMParser();
+var xmlDoc = parser.parseFromString(xml, "text/xml");
+
+// Send data through XHR POST.
+var request = new XMLHttpRequest();
+request.onreadystatechange = function () {
+ if (request.readyState == 4 && request.status == 200)
+ setTimeout(function() {
+ checkPostTab(request);
+ }, layoutTimeout);
+};
+request.open("POST", "issue372.txt", true);
+request.setRequestHeader("Content-Type", "text/xml;charset=utf-8;");
+request.send(xmlDoc);
+
+// Check net panel's UI
+function checkPostTab(request)
+{
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+ // Open Firebug UI and activate Net panel.
+ fireunit.Firebug.showBar(true);
+ fireunit.FirebugChrome.selectPanel("net");
+
+ // Get Net panel content
+ var panelNode = fireunit.panel("net");
+ expandNetRows(panelNode, "netRow", "category-xhr");
+ expandNetTabs(panelNode, "netInfoPostTab");
+
+ var infoBodyRow = fireunit.FBL.getElementByClass(panelNode, "netInfoBody", "category-xhr");
+ fireunit.ok(infoBodyRow, "Info body verified");
+ if (!infoBodyRow)
+ return;
+
+ var postBody = fireunit.FBL.getElementByClass(infoBodyRow, "netInfoPostText");
+ fireunit.ok(postBody, "Post tab body must exist now.");
+ if (!postBody)
+ return;
+
+ var result = unEscapeHTML(postBody.firstChild.innerHTML);
+ fireunit.compare(xml, result, "Post tab body content verified");
+
+ // Finish test
+ fireunit.FBTrace.sysout("fireunit.issue372.DONE");
+ fireunit.testDone();
+}
+</script>
+</head>
+<body>
+<h1>Test Case for Issue #372</h1>
+</body>
+</html>
View
1  chrome/content/fireunit/test/net/issue372.txt
@@ -0,0 +1 @@
+Response for issue372.html
View
70 chrome/content/fireunit/test/net/issue601.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script src="utils.js" type="text/javascript"></script>
+<script src="env.js" type="text/javascript"></script>
+<script>
+netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+// Get current value of the showXMLHttpRequests preference. This one
+// must be set to false in this test, byt the original value is reverted.
+var prefOrigValue = fireunit.Firebug.getPref(prefDomain, "showXMLHttpRequests");
+fireunit.Firebug.setPref(prefDomain, "showXMLHttpRequests", false);
+
+var request = new XMLHttpRequest();
+request.onreadystatechange = function () {
+ if (request.readyState == 4 && request.status == 200)
+ setTimeout(function() {
+ checkResponseTab(request);
+ }, layoutTimeout);
+};
+
+request.open("GET", "issue601.txt", true);
+request.send(null);
+
+function checkResponseTab(request)
+{
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ fireunit.FBTrace.sysout("fireunit.issue601.checkResponseTab", request);
+
+ // Open Firebug UI and activate Net panel.
+ fireunit.Firebug.showBar(true);
+ fireunit.FirebugChrome.selectPanel("net");
+
+ // Get Net panel content
+ var panelNode = fireunit.panel("net");
+ fireunit.ok(panelNode, "Net panel must exist.");
+
+ // Expand Net's panel UI so, it's populated with data.
+ expandNetRows(panelNode, "netRow");
+ expandNetTabs(panelNode, "netInfoResponseTab");
+
+ var infoBodyRow = fireunit.FBL.getElementByClass(panelNode, "netInfoBody", "category-xhr");
+ fireunit.ok(infoBodyRow, "Info body must be created at this moment.");
+ if (infoBodyRow) {
+ var responseBody = fireunit.FBL.getElementByClass(infoBodyRow, "netInfoResponseText");
+ fireunit.ok(responseBody, "Response tab must exist.");
+ fireunit.ok(responseBody.firstChild.firstChild, "Response text node must exist.");
+
+ fireunit.FBTrace.sysout("fireunit.issue601.node with response text",
+ responseBody.firstChild.firstChild);
+
+ if (responseBody.firstChild.firstChild) {
+ fireunit.compare("Response for test_issue601.htm",
+ responseBody.firstChild.firstChild.nodeValue,
+ "Test response must match.");
+ }
+ }
+
+ // Finish test
+ fireunit.Firebug.setPref(prefDomain, "showXMLHttpRequests", prefOrigValue);
+ fireunit.FBTrace.sysout("fireunit.issue601.DONE");
+ fireunit.testDone();
+}
+</script>
+</head>
+<body>
+<h1>Test Case for Issue #601</h1>
+</body>
+</html>
View
1  chrome/content/fireunit/test/net/issue601.txt
@@ -0,0 +1 @@
+Response for test_issue601.htm
View
79 chrome/content/fireunit/test/net/issue700.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script src="utils.js" type="text/javascript"></script>
+<script src="env.js" type="text/javascript"></script>
+<script>
+netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+// Test reponse.
+var bodyContent = "<h1>Response for Issue700.html</h1>";
+var htmlResponse = "<html><head/><body>" + bodyContent + "</body></html>";
+
+// Implementation of a request handler (serve side)
+var index = location.pathname.lastIndexOf(".");
+var requestUri = location.pathname.substr(0, index) + ".txt";
+function requestHandler(metadata, response) {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ fireunit.FBTrace.sysout("fireunit.issue700.pathHandler executed: " + metadata.path, [metadata, response]);
+ response.setHeader("Content-Type", "text/html", false);
+ response.write(htmlResponse);
+}
+fireunit.registerPathHandler(requestUri, requestHandler);
+
+// Generate XHR
+var request = new XMLHttpRequest();
+request.onreadystatechange = function () {
+ if (request.readyState == 4 && request.status == 200)
+ setTimeout(function() {
+ checkHtmlTabPhase1(request);
+ }, layoutTimeout);
+};
+request.open("GET", "issue700.txt", true);
+request.send(null);
+
+// Check UI
+function checkHtmlTabPhase1(request)
+{
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ fireunit.FBTrace.sysout("fireunit.issue700.checkHtmlTab", request);
+
+ // Open Firebug UI and activate Net panel.
+ fireunit.Firebug.showBar(true);
+ fireunit.FirebugChrome.selectPanel("net");
+
+ // Expand Net's panel UI so, it's populated with data.
+ var panelNode = fireunit.panel("net");
+ expandNetRows(panelNode, "netRow", "category-xhr");
+
+ // Wait till iframe for preview is fully loaded.
+ var htmlPreview = fireunit.FBL.getElementByClass(panelNode, "netInfoHtmlPreview");
+ htmlPreview.contentWindow.addEventListener("load", checkHtmlTabPhase2, true);
+}
+
+function checkHtmlTabPhase2()
+{
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+ var panelNode = fireunit.panel("net");
+ expandNetTabs(panelNode, "netInfoHtmlTab");
+ var htmlPreview = fireunit.FBL.getElementByClass(panelNode, "netInfoHtmlPreview");
+ fireunit.ok(htmlPreview, "Html preview must exist.");
+ if (!htmlPreview)
+ return;
+
+ // Compare content with expected result.
+ var body = htmlPreview.contentDocument.getElementsByTagName("body")[0];
+ fireunit.compare(bodyContent, body.innerHTML, "HTML preview verified.");
+
+ // Finish
+ fireunit.FBTrace.sysout("fireunit.issue700.DONE");
+ fireunit.testDone();
+}
+</script>
+</head>
+<body>
+<h1>Test Case for Issue #700</h1>
+</body>
+</html>
View
2  chrome/content/fireunit/test/net/issue700.txt
@@ -0,0 +1,2 @@
+<html><head/><body><h1>Response for Issue700.html</h2></body</html>
+
View
4 chrome/content/fireunit/test/net/net1.html
@@ -3,8 +3,8 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
-fireunit.ok( true, "A true test, on page 1." );
-fireunit.ok( false, "A false test, on page 1." );
+//fireunit.ok( true, "A true test, on page 1." );
+//fireunit.ok( false, "A false test, on page 1." );
setTimeout(function(){
fireunit.testDone();
}, 5000);
View
8 chrome/content/fireunit/test/net/start.html
@@ -4,7 +4,13 @@
<head>
<script>
if ( fireunit.forceHttp() ) {
- fireunit.runTests( "net1.html", "net2.html" );
+ fireunit.runTests(
+ "issue176.html", // Filter for Flash requests
+ "issue601.html", // Response Tab
+ "issue1256.html", // Params Tab
+ "issue372.html", // Post Tab
+ "issue700.html" // HTML Tab
+ );
}
</script>
</head>
View
50 chrome/content/fireunit/test/net/utils.js
@@ -0,0 +1,50 @@
+/*
+ * Author: Jan Odvarko, www.janodvarko.cz
+ */
+
+function expandNetRows(panelNode, className) // className, className, ...
+{
+ var rows = getElementsByClass.apply(null, arguments);
+ for (var i=0; i<rows.length; i++)
+ {
+ var row = rows[i];
+ if (!fireunit.FBL.hasClass(row, "opened"))
+ fireunit.click(row);
+ }
+}
+
+function expandNetTabs(panelNode, tabClass)
+{
+ var tabs = fireunit.FBL.getElementsByClass(panelNode, tabClass);
+ for (var i=0; i<tabs.length; i++)
+ {
+ var tab = tabs[i];
+ if (!fireunit.FBL.hasClass(tab, "collapsed"))
+ fireunit.click(tab);
+ }
+}
+
+function getElementsByClass(node, className) // className, className, ...
+{
+ var result = [];
+ var args = fireunit.FBL.cloneArray(arguments); args.shift();
+ getElementsByClassInternal(node, args, result);
+ return result;
+}
+
+function getElementsByClassInternal(node, classNames, result)
+{
+ for (var child = node.firstChild; child; child = child.nextSibling)
+ {
+ var args1 = fireunit.FBL.cloneArray(classNames); args1.unshift(child);
+ if (fireunit.FBL.hasClass.apply(null, args1))
+ result.push(child);
+
+ getElementsByClassInternal(child, classNames, result);
+ }
+}
+
+function unEscapeHTML(str)
+{
+ return str.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">");
+}
View
11 chrome/locale/en-US/fireunit.properties
@@ -0,0 +1,11 @@
+fireunit.panel.Test=Test
+fireunit.tab.Stack=Stack
+fireunit.tab.Compare=Compare
+fireunit.title.Expected=Expected Result
+fireunit.title.Result=Actual Result
+fireunit.title.Difference=Difference
+fireunit.switch.view_source=view source
+fireunit.switch.pretty_print=pretty print
+fireunit.option.Passing_Tests=Passing Tests
+fireunit.option.Failing_Tests=Failing Tests
+fireunit.item.View_Source=View Source
View
176 chrome/skin/classic/fireunit.css
@@ -0,0 +1,176 @@
+
+/************************************************************************************************/
+
+/* Styles for list of test results */
+.panelNode-test {
+ overflow-x: hidden;
+}
+
+.testTable {
+ width: 100%;
+}
+
+.testResultRow {
+ background: white;
+}
+
+.testResultRow:hover {
+ background: #EFEFEF;
+}
+
+.testResultCol {
+ padding: 0;
+ vertical-align: top;
+ border-bottom: 1px solid #EFEFEF;
+ white-space: nowrap;
+}
+
+.testResultLabel {
+ overflow: hidden;
+ min-width: 70px;