1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/devtools/netmonitor/test/head.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,365 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 +"use strict"; 1.7 + 1.8 +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; 1.9 + 1.10 +let { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); 1.11 +let { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); 1.12 +let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {}); 1.13 +let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); 1.14 +let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); 1.15 +let { CurlUtils } = Cu.import("resource:///modules/devtools/Curl.jsm", {}); 1.16 +let TargetFactory = devtools.TargetFactory; 1.17 +let Toolbox = devtools.Toolbox; 1.18 + 1.19 +const EXAMPLE_URL = "http://example.com/browser/browser/devtools/netmonitor/test/"; 1.20 + 1.21 +const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html"; 1.22 +const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html"; 1.23 +const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html"; 1.24 +const CONTENT_TYPE_WITHOUT_CACHE_URL = EXAMPLE_URL + "html_content-type-without-cache-test-page.html"; 1.25 +const CYRILLIC_URL = EXAMPLE_URL + "html_cyrillic-test-page.html"; 1.26 +const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html"; 1.27 +const POST_DATA_URL = EXAMPLE_URL + "html_post-data-test-page.html"; 1.28 +const POST_RAW_URL = EXAMPLE_URL + "html_post-raw-test-page.html"; 1.29 +const POST_RAW_WITH_HEADERS_URL = EXAMPLE_URL + "html_post-raw-with-headers-test-page.html"; 1.30 +const PARAMS_URL = EXAMPLE_URL + "html_params-test-page.html"; 1.31 +const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html"; 1.32 +const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html"; 1.33 +const JSON_MALFORMED_URL = EXAMPLE_URL + "html_json-malformed-test-page.html"; 1.34 +const JSON_CUSTOM_MIME_URL = EXAMPLE_URL + "html_json-custom-mime-test-page.html"; 1.35 +const JSON_TEXT_MIME_URL = EXAMPLE_URL + "html_json-text-mime-test-page.html"; 1.36 +const SORTING_URL = EXAMPLE_URL + "html_sorting-test-page.html"; 1.37 +const FILTERING_URL = EXAMPLE_URL + "html_filter-test-page.html"; 1.38 +const INFINITE_GET_URL = EXAMPLE_URL + "html_infinite-get-page.html"; 1.39 +const CUSTOM_GET_URL = EXAMPLE_URL + "html_custom-get-page.html"; 1.40 +const SINGLE_GET_URL = EXAMPLE_URL + "html_single-get-page.html"; 1.41 +const STATISTICS_URL = EXAMPLE_URL + "html_statistics-test-page.html"; 1.42 +const CURL_URL = EXAMPLE_URL + "html_copy-as-curl.html"; 1.43 +const CURL_UTILS_URL = EXAMPLE_URL + "html_curl-utils.html"; 1.44 + 1.45 +const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs"; 1.46 +const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs"; 1.47 +const STATUS_CODES_SJS = EXAMPLE_URL + "sjs_status-codes-test-server.sjs"; 1.48 +const SORTING_SJS = EXAMPLE_URL + "sjs_sorting-test-server.sjs"; 1.49 + 1.50 +const TEST_IMAGE = EXAMPLE_URL + "test-image.png"; 1.51 +const TEST_IMAGE_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg=="; 1.52 + 1.53 +gDevTools.testing = true; 1.54 +SimpleTest.registerCleanupFunction(() => { 1.55 + gDevTools.testing = false; 1.56 +}); 1.57 + 1.58 +// All tests are asynchronous. 1.59 +waitForExplicitFinish(); 1.60 + 1.61 +const gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log"); 1.62 +// To enable logging for try runs, just set the pref to true. 1.63 +Services.prefs.setBoolPref("devtools.debugger.log", false); 1.64 + 1.65 +// Always reset some prefs to their original values after the test finishes. 1.66 +const gDefaultFilters = Services.prefs.getCharPref("devtools.netmonitor.filters"); 1.67 + 1.68 +registerCleanupFunction(() => { 1.69 + info("finish() was called, cleaning up..."); 1.70 + 1.71 + Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging); 1.72 + Services.prefs.setCharPref("devtools.netmonitor.filters", gDefaultFilters); 1.73 +}); 1.74 + 1.75 +function addTab(aUrl, aWindow) { 1.76 + info("Adding tab: " + aUrl); 1.77 + 1.78 + let deferred = promise.defer(); 1.79 + let targetWindow = aWindow || window; 1.80 + let targetBrowser = targetWindow.gBrowser; 1.81 + 1.82 + targetWindow.focus(); 1.83 + let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl); 1.84 + let browser = tab.linkedBrowser; 1.85 + 1.86 + browser.addEventListener("load", function onLoad() { 1.87 + browser.removeEventListener("load", onLoad, true); 1.88 + deferred.resolve(tab); 1.89 + }, true); 1.90 + 1.91 + return deferred.promise; 1.92 +} 1.93 + 1.94 +function removeTab(aTab, aWindow) { 1.95 + info("Removing tab."); 1.96 + 1.97 + let targetWindow = aWindow || window; 1.98 + let targetBrowser = targetWindow.gBrowser; 1.99 + 1.100 + targetBrowser.removeTab(aTab); 1.101 +} 1.102 + 1.103 +function initNetMonitor(aUrl, aWindow) { 1.104 + info("Initializing a network monitor pane."); 1.105 + 1.106 + return addTab(aUrl).then((aTab) => { 1.107 + info("Net tab added successfully: " + aUrl); 1.108 + 1.109 + let deferred = promise.defer(); 1.110 + let debuggee = aTab.linkedBrowser.contentWindow.wrappedJSObject; 1.111 + let target = TargetFactory.forTab(aTab); 1.112 + 1.113 + gDevTools.showToolbox(target, "netmonitor").then((aToolbox) => { 1.114 + info("Netork monitor pane shown successfully."); 1.115 + 1.116 + let monitor = aToolbox.getCurrentPanel(); 1.117 + deferred.resolve([aTab, debuggee, monitor]); 1.118 + }); 1.119 + 1.120 + return deferred.promise; 1.121 + }); 1.122 +} 1.123 + 1.124 +function restartNetMonitor(aMonitor, aNewUrl) { 1.125 + info("Restarting the specified network monitor."); 1.126 + 1.127 + let deferred = promise.defer(); 1.128 + let tab = aMonitor.target.tab; 1.129 + let url = aNewUrl || tab.linkedBrowser.contentWindow.wrappedJSObject.location.href; 1.130 + 1.131 + aMonitor.once("destroyed", () => initNetMonitor(url).then(deferred.resolve)); 1.132 + removeTab(tab); 1.133 + 1.134 + return deferred.promise; 1.135 +} 1.136 + 1.137 +function teardown(aMonitor) { 1.138 + info("Destroying the specified network monitor."); 1.139 + 1.140 + let deferred = promise.defer(); 1.141 + let tab = aMonitor.target.tab; 1.142 + 1.143 + aMonitor.once("destroyed", () => executeSoon(deferred.resolve)); 1.144 + removeTab(tab); 1.145 + 1.146 + return deferred.promise; 1.147 +} 1.148 + 1.149 +function waitForNetworkEvents(aMonitor, aGetRequests, aPostRequests = 0) { 1.150 + let deferred = promise.defer(); 1.151 + 1.152 + let panel = aMonitor.panelWin; 1.153 + let genericEvents = 0; 1.154 + let postEvents = 0; 1.155 + 1.156 + function onGenericEvent() { 1.157 + genericEvents++; 1.158 + maybeResolve(); 1.159 + } 1.160 + 1.161 + function onPostEvent() { 1.162 + postEvents++; 1.163 + maybeResolve(); 1.164 + } 1.165 + 1.166 + function maybeResolve() { 1.167 + info("> Network events progress: " + 1.168 + genericEvents + "/" + ((aGetRequests + aPostRequests) * 13) + ", " + 1.169 + postEvents + "/" + (aPostRequests * 2)); 1.170 + 1.171 + // There are 15 updates which need to be fired for a request to be 1.172 + // considered finished. RequestPostData isn't fired for non-POST requests. 1.173 + if (genericEvents == (aGetRequests + aPostRequests) * 13 && 1.174 + postEvents == aPostRequests * 2) { 1.175 + 1.176 + panel.off(panel.EVENTS.UPDATING_REQUEST_HEADERS, onGenericEvent); 1.177 + panel.off(panel.EVENTS.RECEIVED_REQUEST_HEADERS, onGenericEvent); 1.178 + panel.off(panel.EVENTS.UPDATING_REQUEST_COOKIES, onGenericEvent); 1.179 + panel.off(panel.EVENTS.RECEIVED_REQUEST_COOKIES, onGenericEvent); 1.180 + panel.off(panel.EVENTS.UPDATING_REQUEST_POST_DATA, onPostEvent); 1.181 + panel.off(panel.EVENTS.RECEIVED_REQUEST_POST_DATA, onPostEvent); 1.182 + panel.off(panel.EVENTS.UPDATING_RESPONSE_HEADERS, onGenericEvent); 1.183 + panel.off(panel.EVENTS.RECEIVED_RESPONSE_HEADERS, onGenericEvent); 1.184 + panel.off(panel.EVENTS.UPDATING_RESPONSE_COOKIES, onGenericEvent); 1.185 + panel.off(panel.EVENTS.RECEIVED_RESPONSE_COOKIES, onGenericEvent); 1.186 + panel.off(panel.EVENTS.STARTED_RECEIVING_RESPONSE, onGenericEvent); 1.187 + panel.off(panel.EVENTS.UPDATING_RESPONSE_CONTENT, onGenericEvent); 1.188 + panel.off(panel.EVENTS.RECEIVED_RESPONSE_CONTENT, onGenericEvent); 1.189 + panel.off(panel.EVENTS.UPDATING_EVENT_TIMINGS, onGenericEvent); 1.190 + panel.off(panel.EVENTS.RECEIVED_EVENT_TIMINGS, onGenericEvent); 1.191 + 1.192 + executeSoon(deferred.resolve); 1.193 + } 1.194 + } 1.195 + 1.196 + panel.on(panel.EVENTS.UPDATING_REQUEST_HEADERS, onGenericEvent); 1.197 + panel.on(panel.EVENTS.RECEIVED_REQUEST_HEADERS, onGenericEvent); 1.198 + panel.on(panel.EVENTS.UPDATING_REQUEST_COOKIES, onGenericEvent); 1.199 + panel.on(panel.EVENTS.RECEIVED_REQUEST_COOKIES, onGenericEvent); 1.200 + panel.on(panel.EVENTS.UPDATING_REQUEST_POST_DATA, onPostEvent); 1.201 + panel.on(panel.EVENTS.RECEIVED_REQUEST_POST_DATA, onPostEvent); 1.202 + panel.on(panel.EVENTS.UPDATING_RESPONSE_HEADERS, onGenericEvent); 1.203 + panel.on(panel.EVENTS.RECEIVED_RESPONSE_HEADERS, onGenericEvent); 1.204 + panel.on(panel.EVENTS.UPDATING_RESPONSE_COOKIES, onGenericEvent); 1.205 + panel.on(panel.EVENTS.RECEIVED_RESPONSE_COOKIES, onGenericEvent); 1.206 + panel.on(panel.EVENTS.STARTED_RECEIVING_RESPONSE, onGenericEvent); 1.207 + panel.on(panel.EVENTS.UPDATING_RESPONSE_CONTENT, onGenericEvent); 1.208 + panel.on(panel.EVENTS.RECEIVED_RESPONSE_CONTENT, onGenericEvent); 1.209 + panel.on(panel.EVENTS.UPDATING_EVENT_TIMINGS, onGenericEvent); 1.210 + panel.on(panel.EVENTS.RECEIVED_EVENT_TIMINGS, onGenericEvent); 1.211 + 1.212 + return deferred.promise; 1.213 +} 1.214 + 1.215 +function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) { 1.216 + info("> Verifying: " + aMethod + " " + aUrl + " " + aData.toSource()); 1.217 + // This bloats log sizes significantly in automation (bug 992485) 1.218 + //info("> Request: " + aRequestItem.attachment.toSource()); 1.219 + 1.220 + let requestsMenu = aRequestItem.ownerView; 1.221 + let widgetIndex = requestsMenu.indexOfItem(aRequestItem); 1.222 + let visibleIndex = requestsMenu.visibleItems.indexOf(aRequestItem); 1.223 + 1.224 + info("Widget index of item: " + widgetIndex); 1.225 + info("Visible index of item: " + visibleIndex); 1.226 + 1.227 + let { fuzzyUrl, status, statusText, type, fullMimeType, size, time } = aData; 1.228 + let { attachment, target } = aRequestItem 1.229 + 1.230 + let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL); 1.231 + let name = uri.fileName || "/"; 1.232 + let query = uri.query; 1.233 + let hostPort = uri.hostPort; 1.234 + 1.235 + if (fuzzyUrl) { 1.236 + ok(attachment.method.startsWith(aMethod), "The attached method is incorrect."); 1.237 + ok(attachment.url.startsWith(aUrl), "The attached url is incorrect."); 1.238 + } else { 1.239 + is(attachment.method, aMethod, "The attached method is incorrect."); 1.240 + is(attachment.url, aUrl, "The attached url is incorrect."); 1.241 + } 1.242 + 1.243 + is(target.querySelector(".requests-menu-method").getAttribute("value"), 1.244 + aMethod, "The displayed method is incorrect."); 1.245 + 1.246 + if (fuzzyUrl) { 1.247 + ok(target.querySelector(".requests-menu-file").getAttribute("value").startsWith( 1.248 + name + (query ? "?" + query : "")), "The displayed file is incorrect."); 1.249 + ok(target.querySelector(".requests-menu-file").getAttribute("tooltiptext").startsWith( 1.250 + name + (query ? "?" + query : "")), "The tooltip file is incorrect."); 1.251 + } else { 1.252 + is(target.querySelector(".requests-menu-file").getAttribute("value"), 1.253 + name + (query ? "?" + query : ""), "The displayed file is incorrect."); 1.254 + is(target.querySelector(".requests-menu-file").getAttribute("tooltiptext"), 1.255 + name + (query ? "?" + query : ""), "The tooltip file is incorrect."); 1.256 + } 1.257 + 1.258 + is(target.querySelector(".requests-menu-domain").getAttribute("value"), 1.259 + hostPort, "The displayed domain is incorrect."); 1.260 + is(target.querySelector(".requests-menu-domain").getAttribute("tooltiptext"), 1.261 + hostPort, "The tooltip domain is incorrect."); 1.262 + 1.263 + if (status !== undefined) { 1.264 + let value = target.querySelector(".requests-menu-status").getAttribute("code"); 1.265 + let codeValue = target.querySelector(".requests-menu-status-code").getAttribute("value"); 1.266 + let tooltip = target.querySelector(".requests-menu-status-and-method").getAttribute("tooltiptext"); 1.267 + info("Displayed status: " + value); 1.268 + info("Displayed code: " + codeValue); 1.269 + info("Tooltip status: " + tooltip); 1.270 + is(value, status, "The displayed status is incorrect."); 1.271 + is(codeValue, status, "The displayed status code is incorrect."); 1.272 + is(tooltip, status + " " + statusText, "The tooltip status is incorrect."); 1.273 + } 1.274 + if (type !== undefined) { 1.275 + let value = target.querySelector(".requests-menu-type").getAttribute("value"); 1.276 + let tooltip = target.querySelector(".requests-menu-type").getAttribute("tooltiptext"); 1.277 + info("Displayed type: " + value); 1.278 + info("Tooltip type: " + tooltip); 1.279 + is(value, type, "The displayed type is incorrect."); 1.280 + is(tooltip, fullMimeType, "The tooltip type is incorrect."); 1.281 + } 1.282 + if (size !== undefined) { 1.283 + let value = target.querySelector(".requests-menu-size").getAttribute("value"); 1.284 + let tooltip = target.querySelector(".requests-menu-size").getAttribute("tooltiptext"); 1.285 + info("Displayed size: " + value); 1.286 + info("Tooltip size: " + tooltip); 1.287 + is(value, size, "The displayed size is incorrect."); 1.288 + is(tooltip, size, "The tooltip size is incorrect."); 1.289 + } 1.290 + if (time !== undefined) { 1.291 + let value = target.querySelector(".requests-menu-timings-total").getAttribute("value"); 1.292 + let tooltip = target.querySelector(".requests-menu-timings-total").getAttribute("tooltiptext"); 1.293 + info("Displayed time: " + value); 1.294 + info("Tooltip time: " + tooltip); 1.295 + ok(~~(value.match(/[0-9]+/)) >= 0, "The displayed time is incorrect."); 1.296 + ok(~~(tooltip.match(/[0-9]+/)) >= 0, "The tooltip time is incorrect."); 1.297 + } 1.298 + 1.299 + if (visibleIndex != -1) { 1.300 + if (visibleIndex % 2 == 0) { 1.301 + ok(aRequestItem.target.hasAttribute("even"), 1.302 + "Unexpected 'even' attribute for " + aRequestItem.value); 1.303 + ok(!aRequestItem.target.hasAttribute("odd"), 1.304 + "Unexpected 'odd' attribute for " + aRequestItem.value); 1.305 + } else { 1.306 + ok(!aRequestItem.target.hasAttribute("even"), 1.307 + "Unexpected 'even' attribute for " + aRequestItem.value); 1.308 + ok(aRequestItem.target.hasAttribute("odd"), 1.309 + "Unexpected 'odd' attribute for " + aRequestItem.value); 1.310 + } 1.311 + } 1.312 +} 1.313 + 1.314 +/** 1.315 + * Helper function for waiting for an event to fire before resolving a promise. 1.316 + * Example: waitFor(aMonitor.panelWin, aMonitor.panelWin.EVENTS.TAB_UPDATED); 1.317 + * 1.318 + * @param object subject 1.319 + * The event emitter object that is being listened to. 1.320 + * @param string eventName 1.321 + * The name of the event to listen to. 1.322 + * @return object 1.323 + * Returns a promise that resolves upon firing of the event. 1.324 + */ 1.325 +function waitFor (subject, eventName) { 1.326 + let deferred = promise.defer(); 1.327 + subject.once(eventName, deferred.resolve); 1.328 + return deferred.promise; 1.329 +} 1.330 + 1.331 +/** 1.332 + * Tests if a button for a filter of given type is the only one checked. 1.333 + * 1.334 + * @param string aFilterType 1.335 + * The type of the filter that should be the only one checked. 1.336 + */ 1.337 +function testFilterButtons(aMonitor, aFilterType) { 1.338 + let doc = aMonitor.panelWin.document; 1.339 + let target = doc.querySelector("#requests-menu-filter-" + aFilterType + "-button"); 1.340 + let buttons = doc.querySelectorAll(".requests-menu-footer-button"); 1.341 + 1.342 + // Only target should be checked. 1.343 + let checkStatus = [(button == target) ? 1 : 0 for (button of buttons)] 1.344 + testFilterButtonsCustom(aMonitor, checkStatus); 1.345 +} 1.346 + 1.347 +/** 1.348 + * Tests if filter buttons have 'checked' attributes set correctly. 1.349 + * 1.350 + * @param array aIsChecked 1.351 + * An array specifying if a button at given index should have a 1.352 + * 'checked' attribute. For example, if the third item of the array 1.353 + * evaluates to true, the third button should be checked. 1.354 + */ 1.355 +function testFilterButtonsCustom(aMonitor, aIsChecked) { 1.356 + let doc = aMonitor.panelWin.document; 1.357 + let buttons = doc.querySelectorAll(".requests-menu-footer-button"); 1.358 + for (let i = 0; i < aIsChecked.length; i++) { 1.359 + let button = buttons[i]; 1.360 + if (aIsChecked[i]) { 1.361 + is(button.hasAttribute("checked"), true, 1.362 + "The " + button.id + " button should have a 'checked' attribute."); 1.363 + } else { 1.364 + is(button.hasAttribute("checked"), false, 1.365 + "The " + button.id + " button should not have a 'checked' attribute."); 1.366 + } 1.367 + } 1.368 +}