diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /devtools/client/netmonitor/test | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'devtools/client/netmonitor/test')
139 files changed, 11425 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/test/.eslintrc.js b/devtools/client/netmonitor/test/.eslintrc.js new file mode 100644 index 000000000..8d15a76d9 --- /dev/null +++ b/devtools/client/netmonitor/test/.eslintrc.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = { + // Extend from the shared list of defined globals for mochitests. + "extends": "../../../.eslintrc.mochitests.js" +}; diff --git a/devtools/client/netmonitor/test/browser.ini b/devtools/client/netmonitor/test/browser.ini new file mode 100644 index 000000000..5dfe9012d --- /dev/null +++ b/devtools/client/netmonitor/test/browser.ini @@ -0,0 +1,156 @@ +[DEFAULT] +tags = devtools +subsuite = devtools +support-files = + dropmarker.svg + head.js + html_cause-test-page.html + html_content-type-test-page.html + html_content-type-without-cache-test-page.html + html_brotli-test-page.html + html_image-tooltip-test-page.html + html_cors-test-page.html + html_custom-get-page.html + html_cyrillic-test-page.html + html_frame-test-page.html + html_frame-subdocument.html + html_filter-test-page.html + html_infinite-get-page.html + html_json-custom-mime-test-page.html + html_json-long-test-page.html + html_json-malformed-test-page.html + html_json-text-mime-test-page.html + html_jsonp-test-page.html + html_navigate-test-page.html + html_params-test-page.html + html_post-data-test-page.html + html_post-json-test-page.html + html_post-raw-test-page.html + html_post-raw-with-headers-test-page.html + html_simple-test-page.html + html_single-get-page.html + html_send-beacon.html + html_sorting-test-page.html + html_statistics-test-page.html + html_status-codes-test-page.html + html_api-calls-test-page.html + html_copy-as-curl.html + html_curl-utils.html + sjs_content-type-test-server.sjs + sjs_cors-test-server.sjs + sjs_https-redirect-test-server.sjs + sjs_hsts-test-server.sjs + sjs_simple-test-server.sjs + sjs_sorting-test-server.sjs + sjs_status-codes-test-server.sjs + sjs_truncate-test-server.sjs + test-image.png + service-workers/status-codes.html + service-workers/status-codes-service-worker.js + !/devtools/client/framework/test/shared-head.js + +[browser_net_aaa_leaktest.js] +[browser_net_accessibility-01.js] +[browser_net_accessibility-02.js] +skip-if = (toolkit == "cocoa" && e10s) # bug 1252254 +[browser_net_api-calls.js] +[browser_net_autoscroll.js] +skip-if = true # Bug 1309191 - replace with rewritten version in React +[browser_net_cached-status.js] +[browser_net_cause.js] +[browser_net_cause_redirect.js] +[browser_net_service-worker-status.js] +[browser_net_charts-01.js] +[browser_net_charts-02.js] +[browser_net_charts-03.js] +[browser_net_charts-04.js] +[browser_net_charts-05.js] +[browser_net_charts-06.js] +[browser_net_charts-07.js] +[browser_net_clear.js] +[browser_net_complex-params.js] +[browser_net_content-type.js] +[browser_net_brotli.js] +[browser_net_curl-utils.js] +[browser_net_copy_image_as_data_uri.js] +subsuite = clipboard +[browser_net_copy_svg_image_as_data_uri.js] +subsuite = clipboard +[browser_net_copy_url.js] +subsuite = clipboard +[browser_net_copy_params.js] +subsuite = clipboard +[browser_net_copy_response.js] +subsuite = clipboard +[browser_net_copy_headers.js] +subsuite = clipboard +[browser_net_copy_as_curl.js] +subsuite = clipboard +[browser_net_cors_requests.js] +[browser_net_cyrillic-01.js] +[browser_net_cyrillic-02.js] +[browser_net_details-no-duplicated-content.js] +skip-if = (os == 'linux' && e10s && debug) # Bug 1242204 +[browser_net_frame.js] +[browser_net_filter-01.js] +[browser_net_filter-02.js] +[browser_net_filter-03.js] +[browser_net_filter-04.js] +[browser_net_footer-summary.js] +[browser_net_html-preview.js] +[browser_net_icon-preview.js] +[browser_net_image-tooltip.js] +[browser_net_json-long.js] +[browser_net_json-malformed.js] +[browser_net_json_custom_mime.js] +[browser_net_json_text_mime.js] +[browser_net_jsonp.js] +[browser_net_large-response.js] +[browser_net_leak_on_tab_close.js] +[browser_net_open_request_in_tab.js] +[browser_net_page-nav.js] +[browser_net_pane-collapse.js] +[browser_net_pane-toggle.js] +[browser_net_post-data-01.js] +[browser_net_post-data-02.js] +[browser_net_post-data-03.js] +[browser_net_post-data-04.js] +[browser_net_prefs-and-l10n.js] +[browser_net_prefs-reload.js] +[browser_net_raw_headers.js] +[browser_net_reload-button.js] +[browser_net_reload-markers.js] +[browser_net_req-resp-bodies.js] +[browser_net_resend_cors.js] +[browser_net_resend_headers.js] +[browser_net_resend.js] +[browser_net_security-details.js] +[browser_net_security-error.js] +[browser_net_security-icon-click.js] +[browser_net_security-redirect.js] +[browser_net_security-state.js] +[browser_net_security-tab-deselect.js] +[browser_net_security-tab-visibility.js] +[browser_net_security-warnings.js] +[browser_net_send-beacon.js] +[browser_net_send-beacon-other-tab.js] +[browser_net_simple-init.js] +[browser_net_simple-request-data.js] +skip-if = true # Bug 1258809 +[browser_net_simple-request-details.js] +skip-if = true # Bug 1258809 +[browser_net_simple-request.js] +[browser_net_sort-01.js] +skip-if = (e10s && debug && os == 'mac') # Bug 1253037 +[browser_net_sort-02.js] +[browser_net_sort-03.js] +[browser_net_statistics-01.js] +[browser_net_statistics-02.js] +[browser_net_statistics-03.js] +[browser_net_status-codes.js] +[browser_net_streaming-response.js] +[browser_net_throttle.js] +[browser_net_timeline_ticks.js] +[browser_net_timing-division.js] +[browser_net_truncate.js] +[browser_net_persistent_logs.js] diff --git a/devtools/client/netmonitor/test/browser_net_aaa_leaktest.js b/devtools/client/netmonitor/test/browser_net_aaa_leaktest.js new file mode 100644 index 000000000..31c1e03ad --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_aaa_leaktest.js @@ -0,0 +1,28 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the network monitor leaks on initialization and sudden destruction. + * You can also use this initialization format as a template for other tests. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { document, NetMonitorView, NetMonitorController } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + + ok(tab, "Should have a tab available."); + ok(monitor, "Should have a network monitor pane available."); + + ok(document, "Should have a document available."); + ok(NetMonitorView, "Should have a NetMonitorView object available."); + ok(NetMonitorController, "Should have a NetMonitorController object available."); + ok(RequestsMenu, "Should have a RequestsMenu object available."); + ok(NetworkDetails, "Should have a NetworkDetails object available."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_accessibility-01.js b/devtools/client/netmonitor/test/browser_net_accessibility-01.js new file mode 100644 index 000000000..c0832064f --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_accessibility-01.js @@ -0,0 +1,87 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if focus modifiers work for the SideMenuWidget. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + info("Starting test... "); + + // It seems that this test may be slow on Ubuntu builds running on ec2. + requestLongerTimeout(2); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let count = 0; + function check(selectedIndex, paneVisibility) { + info("Performing check " + (count++) + "."); + + is(RequestsMenu.selectedIndex, selectedIndex, + "The selected item in the requests menu was incorrect."); + is(NetMonitorView.detailsPaneHidden, !paneVisibility, + "The network requests details pane visibility state was incorrect."); + } + + let wait = waitForNetworkEvents(monitor, 2); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(2); + }); + yield wait; + + check(-1, false); + + RequestsMenu.focusLastVisibleItem(); + check(1, true); + RequestsMenu.focusFirstVisibleItem(); + check(0, true); + + RequestsMenu.focusNextItem(); + check(1, true); + RequestsMenu.focusPrevItem(); + check(0, true); + + RequestsMenu.focusItemAtDelta(+1); + check(1, true); + RequestsMenu.focusItemAtDelta(-1); + check(0, true); + + RequestsMenu.focusItemAtDelta(+10); + check(1, true); + RequestsMenu.focusItemAtDelta(-10); + check(0, true); + + wait = waitForNetworkEvents(monitor, 18); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(18); + }); + yield wait; + + RequestsMenu.focusLastVisibleItem(); + check(19, true); + RequestsMenu.focusFirstVisibleItem(); + check(0, true); + + RequestsMenu.focusNextItem(); + check(1, true); + RequestsMenu.focusPrevItem(); + check(0, true); + + RequestsMenu.focusItemAtDelta(+10); + check(10, true); + RequestsMenu.focusItemAtDelta(-10); + check(0, true); + + RequestsMenu.focusItemAtDelta(+100); + check(19, true); + RequestsMenu.focusItemAtDelta(-100); + check(0, true); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_accessibility-02.js b/devtools/client/netmonitor/test/browser_net_accessibility-02.js new file mode 100644 index 000000000..33420a440 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_accessibility-02.js @@ -0,0 +1,130 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if keyboard and mouse navigation works in the network requests menu. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + info("Starting test... "); + + // It seems that this test may be slow on Ubuntu builds running on ec2. + requestLongerTimeout(2); + + let { window, $, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let count = 0; + function check(selectedIndex, paneVisibility) { + info("Performing check " + (count++) + "."); + + is(RequestsMenu.selectedIndex, selectedIndex, + "The selected item in the requests menu was incorrect."); + is(NetMonitorView.detailsPaneHidden, !paneVisibility, + "The network requests details pane visibility state was incorrect."); + } + + let wait = waitForNetworkEvents(monitor, 2); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(2); + }); + yield wait; + + check(-1, false); + + EventUtils.sendKey("DOWN", window); + check(0, true); + EventUtils.sendKey("UP", window); + check(0, true); + + EventUtils.sendKey("PAGE_DOWN", window); + check(1, true); + EventUtils.sendKey("PAGE_UP", window); + check(0, true); + + EventUtils.sendKey("END", window); + check(1, true); + EventUtils.sendKey("HOME", window); + check(0, true); + + wait = waitForNetworkEvents(monitor, 18); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(18); + }); + yield wait; + + EventUtils.sendKey("DOWN", window); + check(1, true); + EventUtils.sendKey("DOWN", window); + check(2, true); + EventUtils.sendKey("UP", window); + check(1, true); + EventUtils.sendKey("UP", window); + check(0, true); + + EventUtils.sendKey("PAGE_DOWN", window); + check(4, true); + EventUtils.sendKey("PAGE_DOWN", window); + check(8, true); + EventUtils.sendKey("PAGE_UP", window); + check(4, true); + EventUtils.sendKey("PAGE_UP", window); + check(0, true); + + EventUtils.sendKey("HOME", window); + check(0, true); + EventUtils.sendKey("HOME", window); + check(0, true); + EventUtils.sendKey("PAGE_UP", window); + check(0, true); + EventUtils.sendKey("HOME", window); + check(0, true); + + EventUtils.sendKey("END", window); + check(19, true); + EventUtils.sendKey("END", window); + check(19, true); + EventUtils.sendKey("PAGE_DOWN", window); + check(19, true); + EventUtils.sendKey("END", window); + check(19, true); + + EventUtils.sendKey("PAGE_UP", window); + check(15, true); + EventUtils.sendKey("PAGE_UP", window); + check(11, true); + EventUtils.sendKey("UP", window); + check(10, true); + EventUtils.sendKey("UP", window); + check(9, true); + EventUtils.sendKey("PAGE_DOWN", window); + check(13, true); + EventUtils.sendKey("PAGE_DOWN", window); + check(17, true); + EventUtils.sendKey("PAGE_DOWN", window); + check(19, true); + EventUtils.sendKey("PAGE_DOWN", window); + check(19, true); + + EventUtils.sendKey("HOME", window); + check(0, true); + EventUtils.sendKey("DOWN", window); + check(1, true); + EventUtils.sendKey("END", window); + check(19, true); + EventUtils.sendKey("DOWN", window); + check(19, true); + + EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle")); + check(-1, false); + + EventUtils.sendMouseEvent({ type: "mousedown" }, $(".side-menu-widget-item")); + check(0, true); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_api-calls.js b/devtools/client/netmonitor/test/browser_net_api-calls.js new file mode 100644 index 000000000..994dc0354 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_api-calls.js @@ -0,0 +1,39 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests whether API call URLs (without a filename) are correctly displayed + * (including Unicode) + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(API_CALLS_URL); + info("Starting test... "); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + const REQUEST_URIS = [ + "http://example.com/api/fileName.xml", + "http://example.com/api/file%E2%98%A2.xml", + "http://example.com/api/ascii/get/", + "http://example.com/api/unicode/%E2%98%A2/", + "http://example.com/api/search/?q=search%E2%98%A2" + ]; + + let wait = waitForNetworkEvents(monitor, 5); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + REQUEST_URIS.forEach(function (uri, index) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(index), "GET", uri); + }); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_autoscroll.js b/devtools/client/netmonitor/test/browser_net_autoscroll.js new file mode 100644 index 000000000..9abb3fd17 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_autoscroll.js @@ -0,0 +1,75 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Bug 863102 - Automatically scroll down upon new network requests. + */ +add_task(function* () { + requestLongerTimeout(2); + + let { monitor } = yield initNetMonitor(INFINITE_GET_URL); + let win = monitor.panelWin; + let topNode = win.document.getElementById("requests-menu-contents"); + let requestsContainer = topNode.getElementsByTagName("scrollbox")[0]; + ok(!!requestsContainer, "Container element exists as expected."); + + // (1) Check that the scroll position is maintained at the bottom + // when the requests overflow the vertical size of the container. + yield waitForRequestsToOverflowContainer(); + yield waitForScroll(); + ok(scrolledToBottom(requestsContainer), "Scrolled to bottom on overflow."); + + // (2) Now set the scroll position somewhere in the middle and check + // that additional requests do not change the scroll position. + let children = requestsContainer.childNodes; + let middleNode = children.item(children.length / 2); + middleNode.scrollIntoView(); + ok(!scrolledToBottom(requestsContainer), "Not scrolled to bottom."); + // save for comparison later + let scrollTop = requestsContainer.scrollTop; + yield waitForNetworkEvents(monitor, 8); + yield waitSomeTime(); + is(requestsContainer.scrollTop, scrollTop, "Did not scroll."); + + // (3) Now set the scroll position back at the bottom and check that + // additional requests *do* cause the container to scroll down. + requestsContainer.scrollTop = requestsContainer.scrollHeight; + ok(scrolledToBottom(requestsContainer), "Set scroll position to bottom."); + yield waitForNetworkEvents(monitor, 8); + yield waitForScroll(); + ok(scrolledToBottom(requestsContainer), "Still scrolled to bottom."); + + // (4) Now select an item in the list and check that additional requests + // do not change the scroll position. + monitor.panelWin.NetMonitorView.RequestsMenu.selectedIndex = 0; + yield waitForNetworkEvents(monitor, 8); + yield waitSomeTime(); + is(requestsContainer.scrollTop, 0, "Did not scroll."); + + // Done: clean up. + yield teardown(monitor); + + function* waitForRequestsToOverflowContainer() { + while (true) { + yield waitForNetworkEvents(monitor, 1); + if (requestsContainer.scrollHeight > requestsContainer.clientHeight) { + return; + } + } + } + + function scrolledToBottom(element) { + return element.scrollTop + element.clientHeight >= element.scrollHeight; + } + + function waitSomeTime() { + // Wait to make sure no scrolls happen + return wait(50); + } + + function waitForScroll() { + return monitor._view.RequestsMenu.widget.once("scroll-to-bottom"); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_brotli.js b/devtools/client/netmonitor/test/browser_net_brotli.js new file mode 100644 index 000000000..cc6908d68 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_brotli.js @@ -0,0 +1,91 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const BROTLI_URL = HTTPS_EXAMPLE_URL + "html_brotli-test-page.html"; +const BROTLI_REQUESTS = 1; + +/** + * Test brotli encoded response is handled correctly on HTTPS. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(BROTLI_URL); + info("Starting test... "); + + let { document, Editor, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, BROTLI_REQUESTS); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "GET", HTTPS_CONTENT_TYPE_SJS + "?fmt=br", { + status: 200, + statusText: "Connected", + type: "plain", + fullMimeType: "text/plain", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 10), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 64), + time: true + }); + + let onEvent = waitForResponseBodyDisplayed(); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[3]); + yield onEvent; + yield testResponseTab("br"); + + yield teardown(monitor); + + function* testResponseTab(type) { + let tabEl = document.querySelectorAll("#details-pane tab")[3]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3]; + + is(tabEl.getAttribute("selected"), "true", + "The response tab in the network details pane should be selected."); + + function checkVisibility(box) { + is(tabpanel.querySelector("#response-content-info-header") + .hasAttribute("hidden"), true, + "The response info header doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-json-box") + .hasAttribute("hidden"), box != "json", + "The response content json box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-textarea-box") + .hasAttribute("hidden"), box != "textarea", + "The response content textarea box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-image-box") + .hasAttribute("hidden"), box != "image", + "The response content image box doesn't have the intended visibility."); + } + + switch (type) { + case "br": { + checkVisibility("textarea"); + + let expected = "X".repeat(64); + let editor = yield NetMonitorView.editor("#response-content-textarea"); + is(editor.getText(), expected, + "The text shown in the source editor is incorrect for the brotli request."); + is(editor.getMode(), Editor.modes.text, + "The mode active in the source editor is incorrect for the brotli request."); + break; + } + } + } + + function waitForResponseBodyDisplayed() { + return monitor.panelWin.once(monitor.panelWin.EVENTS.RESPONSE_BODY_DISPLAYED); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_cached-status.js b/devtools/client/netmonitor/test/browser_net_cached-status.js new file mode 100644 index 000000000..66b926bea --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_cached-status.js @@ -0,0 +1,111 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if cached requests have the correct status code + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(STATUS_CODES_URL, null, true); + info("Starting test... "); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + NetworkDetails._params.lazyEmpty = false; + + const REQUEST_DATA = [ + { + method: "GET", + uri: STATUS_CODES_SJS + "?sts=ok&cached", + details: { + status: 200, + statusText: "OK", + type: "plain", + fullMimeType: "text/plain; charset=utf-8" + } + }, + { + method: "GET", + uri: STATUS_CODES_SJS + "?sts=redirect&cached", + details: { + status: 301, + statusText: "Moved Permanently", + type: "html", + fullMimeType: "text/html; charset=utf-8" + } + }, + { + method: "GET", + uri: "http://example.com/redirected", + details: { + status: 404, + statusText: "Not Found", + type: "html", + fullMimeType: "text/html; charset=utf-8" + } + }, + { + method: "GET", + uri: STATUS_CODES_SJS + "?sts=ok&cached", + details: { + status: 200, + statusText: "OK (cached)", + displayedStatus: "cached", + type: "plain", + fullMimeType: "text/plain; charset=utf-8" + } + }, + { + method: "GET", + uri: STATUS_CODES_SJS + "?sts=redirect&cached", + details: { + status: 301, + statusText: "Moved Permanently (cached)", + displayedStatus: "cached", + type: "html", + fullMimeType: "text/html; charset=utf-8" + } + }, + { + method: "GET", + uri: "http://example.com/redirected", + details: { + status: 404, + statusText: "Not Found", + type: "html", + fullMimeType: "text/html; charset=utf-8" + } + } + ]; + + info("Performing requests #1..."); + yield performRequestsAndWait(); + + info("Performing requests #2..."); + yield performRequestsAndWait(); + + let index = 0; + for (let request of REQUEST_DATA) { + let item = RequestsMenu.getItemAtIndex(index); + + info("Verifying request #" + index); + yield verifyRequestItemTarget(item, request.method, request.uri, request.details); + + index++; + } + + yield teardown(monitor); + + function* performRequestsAndWait() { + let wait = waitForNetworkEvents(monitor, 3); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performCachedRequests(); + }); + yield wait; + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_cause.js b/devtools/client/netmonitor/test/browser_net_cause.js new file mode 100644 index 000000000..2e73965d0 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_cause.js @@ -0,0 +1,147 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if request cause is reported correctly. + */ + +const CAUSE_FILE_NAME = "html_cause-test-page.html"; +const CAUSE_URL = EXAMPLE_URL + CAUSE_FILE_NAME; + +const EXPECTED_REQUESTS = [ + { + method: "GET", + url: CAUSE_URL, + causeType: "document", + causeUri: "", + // The document load has internal privileged JS code on the stack + stack: true + }, + { + method: "GET", + url: EXAMPLE_URL + "stylesheet_request", + causeType: "stylesheet", + causeUri: CAUSE_URL, + stack: false + }, + { + method: "GET", + url: EXAMPLE_URL + "img_request", + causeType: "img", + causeUri: CAUSE_URL, + stack: false + }, + { + method: "GET", + url: EXAMPLE_URL + "xhr_request", + causeType: "xhr", + causeUri: CAUSE_URL, + stack: [{ fn: "performXhrRequest", file: CAUSE_FILE_NAME, line: 22 }] + }, + { + method: "GET", + url: EXAMPLE_URL + "fetch_request", + causeType: "fetch", + causeUri: CAUSE_URL, + stack: [{ fn: "performFetchRequest", file: CAUSE_FILE_NAME, line: 26 }] + }, + { + method: "GET", + url: EXAMPLE_URL + "promise_fetch_request", + causeType: "fetch", + causeUri: CAUSE_URL, + stack: [ + { fn: "performPromiseFetchRequest", file: CAUSE_FILE_NAME, line: 38 }, + { fn: null, file: CAUSE_FILE_NAME, line: 37, asyncCause: "promise callback" }, + ] + }, + { + method: "GET", + url: EXAMPLE_URL + "timeout_fetch_request", + causeType: "fetch", + causeUri: CAUSE_URL, + stack: [ + { fn: "performTimeoutFetchRequest", file: CAUSE_FILE_NAME, line: 40 }, + { fn: "performPromiseFetchRequest", file: CAUSE_FILE_NAME, line: 39, + asyncCause: "setTimeout handler" }, + ] + }, + { + method: "POST", + url: EXAMPLE_URL + "beacon_request", + causeType: "beacon", + causeUri: CAUSE_URL, + stack: [{ fn: "performBeaconRequest", file: CAUSE_FILE_NAME, line: 30 }] + }, +]; + +add_task(function* () { + // Async stacks aren't on by default in all builds + yield SpecialPowers.pushPrefEnv({ set: [["javascript.options.asyncstack", true]] }); + + // the initNetMonitor function clears the network request list after the + // page is loaded. That's why we first load a bogus page from SIMPLE_URL, + // and only then load the real thing from CAUSE_URL - we want to catch + // all the requests the page is making, not only the XHRs. + // We can't use about:blank here, because initNetMonitor checks that the + // page has actually made at least one request. + let { tab, monitor } = yield initNetMonitor(SIMPLE_URL); + let { $, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length); + tab.linkedBrowser.loadURI(CAUSE_URL); + yield wait; + + is(RequestsMenu.itemCount, EXPECTED_REQUESTS.length, + "All the page events should be recorded."); + + EXPECTED_REQUESTS.forEach((spec, i) => { + let { method, url, causeType, causeUri, stack } = spec; + + let requestItem = RequestsMenu.getItemAtIndex(i); + verifyRequestItemTarget(requestItem, + method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } } + ); + + let { stacktrace } = requestItem.attachment.cause; + let stackLen = stacktrace ? stacktrace.length : 0; + + if (stack) { + ok(stacktrace, `Request #${i} has a stacktrace`); + ok(stackLen > 0, + `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`); + + // if "stack" is array, check the details about the top stack frames + if (Array.isArray(stack)) { + stack.forEach((frame, j) => { + is(stacktrace[j].functionName, frame.fn, + `Request #${i} has the correct function on JS stack frame #${j}`); + is(stacktrace[j].filename.split("/").pop(), frame.file, + `Request #${i} has the correct file on JS stack frame #${j}`); + is(stacktrace[j].lineNumber, frame.line, + `Request #${i} has the correct line number on JS stack frame #${j}`); + is(stacktrace[j].asyncCause, frame.asyncCause, + `Request #${i} has the correct async cause on JS stack frame #${j}`); + }); + } + } else { + is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`); + } + }); + + // Sort the requests by cause and check the order + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-cause-button")); + let expectedOrder = EXPECTED_REQUESTS.map(r => r.causeType).sort(); + expectedOrder.forEach((expectedCause, i) => { + let { target } = RequestsMenu.getItemAtIndex(i); + let causeLabel = target.querySelector(".requests-menu-cause-label"); + let cause = causeLabel.getAttribute("value"); + is(cause, expectedCause, `The request #${i} has the expected cause after sorting`); + }); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_cause_redirect.js b/devtools/client/netmonitor/test/browser_net_cause_redirect.js new file mode 100644 index 000000000..ace6390ab --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_cause_redirect.js @@ -0,0 +1,57 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if request JS stack is property reported if the request is internally + * redirected without hitting the network (HSTS is one of such cases) + */ + +add_task(function* () { + const EXPECTED_REQUESTS = [ + // Request to HTTP URL, redirects to HTTPS, has callstack + { status: 302, hasStack: true }, + // Serves HTTPS, sets the Strict-Transport-Security header, no stack + { status: 200, hasStack: false }, + // Second request to HTTP redirects to HTTPS internally + { status: 200, hasStack: true }, + ]; + + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + let { RequestsMenu } = monitor.panelWin.NetMonitorView; + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length); + yield performRequests(2, HSTS_SJS); + yield wait; + + EXPECTED_REQUESTS.forEach(({status, hasStack}, i) => { + let { attachment } = RequestsMenu.getItemAtIndex(i); + + is(attachment.status, status, `Request #${i} has the expected status`); + + let { stacktrace } = attachment.cause; + let stackLen = stacktrace ? stacktrace.length : 0; + + if (hasStack) { + ok(stacktrace, `Request #${i} has a stacktrace`); + ok(stackLen > 0, `Request #${i} has a stacktrace with ${stackLen} items`); + } else { + is(stackLen, 0, `Request #${i} has an empty stacktrace`); + } + }); + + // Send a request to reset the HSTS policy to state before the test + wait = waitForNetworkEvents(monitor, 1); + yield performRequests(1, HSTS_SJS + "?reset"); + yield wait; + + yield teardown(monitor); + + function performRequests(count, url) { + return ContentTask.spawn(tab.linkedBrowser, { count, url }, function* (args) { + content.wrappedJSObject.performRequests(args.count, args.url); + }); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_charts-01.js b/devtools/client/netmonitor/test/browser_net_charts-01.js new file mode 100644 index 000000000..987881836 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_charts-01.js @@ -0,0 +1,73 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Makes sure Pie Charts have the right internal structure. + */ + +add_task(function* () { + let { monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { document, Chart } = monitor.panelWin; + + let pie = Chart.Pie(document, { + width: 100, + height: 100, + data: [{ + size: 1, + label: "foo" + }, { + size: 2, + label: "bar" + }, { + size: 3, + label: "baz" + }] + }); + + let node = pie.node; + let slices = node.querySelectorAll(".pie-chart-slice.chart-colored-blob"); + let labels = node.querySelectorAll(".pie-chart-label"); + + ok(node.classList.contains("pie-chart-container") && + node.classList.contains("generic-chart-container"), + "A pie chart container was created successfully."); + + is(slices.length, 3, + "There should be 3 pie chart slices created."); + ok(slices[0].getAttribute("d").match( + /\s*M 50,50 L 49\.\d+,97\.\d+ A 47\.5,47\.5 0 0 1 49\.\d+,2\.5\d* Z/), + "The first slice has the correct data."); + ok(slices[1].getAttribute("d").match( + /\s*M 50,50 L 91\.\d+,26\.\d+ A 47\.5,47\.5 0 0 1 49\.\d+,97\.\d+ Z/), + "The second slice has the correct data."); + ok(slices[2].getAttribute("d").match( + /\s*M 50,50 L 50\.\d+,2\.5\d* A 47\.5,47\.5 0 0 1 91\.\d+,26\.\d+ Z/), + "The third slice has the correct data."); + + ok(slices[0].hasAttribute("largest"), + "The first slice should be the largest one."); + ok(slices[2].hasAttribute("smallest"), + "The third slice should be the smallest one."); + + ok(slices[0].getAttribute("name"), "baz", + "The first slice's name is correct."); + ok(slices[1].getAttribute("name"), "bar", + "The first slice's name is correct."); + ok(slices[2].getAttribute("name"), "foo", + "The first slice's name is correct."); + + is(labels.length, 3, + "There should be 3 pie chart labels created."); + is(labels[0].textContent, "baz", + "The first label's text is correct."); + is(labels[1].textContent, "bar", + "The first label's text is correct."); + is(labels[2].textContent, "foo", + "The first label's text is correct."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_charts-02.js b/devtools/client/netmonitor/test/browser_net_charts-02.js new file mode 100644 index 000000000..ae53147f0 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_charts-02.js @@ -0,0 +1,49 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Makes sure Pie Charts have the right internal structure when + * initialized with empty data. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { document, Chart } = monitor.panelWin; + + let pie = Chart.Pie(document, { + data: null, + width: 100, + height: 100 + }); + + let node = pie.node; + let slices = node.querySelectorAll(".pie-chart-slice.chart-colored-blob"); + let labels = node.querySelectorAll(".pie-chart-label"); + + ok(node.classList.contains("pie-chart-container") && + node.classList.contains("generic-chart-container"), + "A pie chart container was created successfully."); + + is(slices.length, 1, "There should be 1 pie chart slice created."); + ok(slices[0].getAttribute("d").match( + /\s*M 50,50 L 50\.\d+,2\.5\d* A 47\.5,47\.5 0 1 1 49\.\d+,2\.5\d* Z/), + "The first slice has the correct data."); + + ok(slices[0].hasAttribute("largest"), + "The first slice should be the largest one."); + ok(slices[0].hasAttribute("smallest"), + "The first slice should also be the smallest one."); + ok(slices[0].getAttribute("name"), L10N.getStr("pieChart.loading"), + "The first slice's name is correct."); + + is(labels.length, 1, "There should be 1 pie chart label created."); + is(labels[0].textContent, "Loading", "The first label's text is correct."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_charts-03.js b/devtools/client/netmonitor/test/browser_net_charts-03.js new file mode 100644 index 000000000..c7d9b0c1a --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_charts-03.js @@ -0,0 +1,106 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Makes sure Table Charts have the right internal structure. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { document, Chart } = monitor.panelWin; + + let table = Chart.Table(document, { + title: "Table title", + data: [{ + label1: 1, + label2: 11.1 + }, { + label1: 2, + label2: 12.2 + }, { + label1: 3, + label2: 13.3 + }], + strings: { + label2: (value, index) => value + ["foo", "bar", "baz"][index] + }, + totals: { + label1: value => "Hello " + L10N.numberWithDecimals(value, 2), + label2: value => "World " + L10N.numberWithDecimals(value, 2) + } + }); + + let node = table.node; + let title = node.querySelector(".table-chart-title"); + let grid = node.querySelector(".table-chart-grid"); + let totals = node.querySelector(".table-chart-totals"); + let rows = grid.querySelectorAll(".table-chart-row"); + let sums = node.querySelectorAll(".table-chart-summary-label"); + + ok(node.classList.contains("table-chart-container") && + node.classList.contains("generic-chart-container"), + "A table chart container was created successfully."); + + ok(title, "A title node was created successfully."); + is(title.getAttribute("value"), "Table title", + "The title node displays the correct text."); + + is(rows.length, 3, "There should be 3 table chart rows created."); + + ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"), + "A colored blob exists for the firt row."); + is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "label1", + "The first column of the first row exists."); + is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label2", + "The second column of the first row exists."); + is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "1", + "The first column of the first row displays the correct text."); + is(rows[0].querySelectorAll("label")[1].getAttribute("value"), "11.1foo", + "The second column of the first row displays the correct text."); + + ok(rows[1].querySelector(".table-chart-row-box.chart-colored-blob"), + "A colored blob exists for the second row."); + is(rows[1].querySelectorAll("label")[0].getAttribute("name"), "label1", + "The first column of the second row exists."); + is(rows[1].querySelectorAll("label")[1].getAttribute("name"), "label2", + "The second column of the second row exists."); + is(rows[1].querySelectorAll("label")[0].getAttribute("value"), "2", + "The first column of the second row displays the correct text."); + is(rows[1].querySelectorAll("label")[1].getAttribute("value"), "12.2bar", + "The second column of the first row displays the correct text."); + + ok(rows[2].querySelector(".table-chart-row-box.chart-colored-blob"), + "A colored blob exists for the third row."); + is(rows[2].querySelectorAll("label")[0].getAttribute("name"), "label1", + "The first column of the third row exists."); + is(rows[2].querySelectorAll("label")[1].getAttribute("name"), "label2", + "The second column of the third row exists."); + is(rows[2].querySelectorAll("label")[0].getAttribute("value"), "3", + "The first column of the third row displays the correct text."); + is(rows[2].querySelectorAll("label")[1].getAttribute("value"), "13.3baz", + "The second column of the third row displays the correct text."); + + is(sums.length, 2, "There should be 2 total summaries created."); + + is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("name"), + "label1", + "The first sum's type is correct."); + is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("value"), + "Hello 6", + "The first sum's value is correct."); + + is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("name"), + "label2", + "The second sum's type is correct."); + is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("value"), + "World 36.60", + "The second sum's value is correct."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_charts-04.js b/devtools/client/netmonitor/test/browser_net_charts-04.js new file mode 100644 index 000000000..0d150c409 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_charts-04.js @@ -0,0 +1,75 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Makes sure Pie Charts have the right internal structure when + * initialized with empty data. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { document, Chart } = monitor.panelWin; + + let table = Chart.Table(document, { + title: "Table title", + data: null, + totals: { + label1: value => "Hello " + L10N.numberWithDecimals(value, 2), + label2: value => "World " + L10N.numberWithDecimals(value, 2) + } + }); + + let node = table.node; + let title = node.querySelector(".table-chart-title"); + let grid = node.querySelector(".table-chart-grid"); + let totals = node.querySelector(".table-chart-totals"); + let rows = grid.querySelectorAll(".table-chart-row"); + let sums = node.querySelectorAll(".table-chart-summary-label"); + + ok(node.classList.contains("table-chart-container") && + node.classList.contains("generic-chart-container"), + "A table chart container was created successfully."); + + ok(title, "A title node was created successfully."); + is(title.getAttribute("value"), "Table title", + "The title node displays the correct text."); + + is(rows.length, 1, "There should be 1 table chart row created."); + + ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"), + "A colored blob exists for the firt row."); + is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "size", + "The first column of the first row exists."); + is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label", + "The second column of the first row exists."); + is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "", + "The first column of the first row displays the correct text."); + is(rows[0].querySelectorAll("label")[1].getAttribute("value"), + L10N.getStr("tableChart.loading"), + "The second column of the first row displays the correct text."); + + is(sums.length, 2, + "There should be 2 total summaries created."); + + is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("name"), + "label1", + "The first sum's type is correct."); + is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("value"), + "Hello 0", + "The first sum's value is correct."); + + is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("name"), + "label2", + "The second sum's type is correct."); + is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("value"), + "World 0", + "The second sum's value is correct."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_charts-05.js b/devtools/client/netmonitor/test/browser_net_charts-05.js new file mode 100644 index 000000000..00445b132 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_charts-05.js @@ -0,0 +1,61 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Makes sure Pie+Table Charts have the right internal structure. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { document, Chart } = monitor.panelWin; + + let chart = Chart.PieTable(document, { + title: "Table title", + data: [{ + size: 1, + label: 11.1 + }, { + size: 2, + label: 12.2 + }, { + size: 3, + label: 13.3 + }], + strings: { + label2: (value, index) => value + ["foo", "bar", "baz"][index] + }, + totals: { + size: value => "Hello " + L10N.numberWithDecimals(value, 2), + label: value => "World " + L10N.numberWithDecimals(value, 2) + } + }); + + ok(chart.pie, "The pie chart proxy is accessible."); + ok(chart.table, "The table chart proxy is accessible."); + + let node = chart.node; + let rows = node.querySelectorAll(".table-chart-row"); + let sums = node.querySelectorAll(".table-chart-summary-label"); + + ok(node.classList.contains("pie-table-chart-container"), + "A pie+table chart container was created successfully."); + + ok(node.querySelector(".table-chart-title"), + "A title node was created successfully."); + ok(node.querySelector(".pie-chart-container"), + "A pie chart was created successfully."); + ok(node.querySelector(".table-chart-container"), + "A table chart was created successfully."); + + is(rows.length, 3, "There should be 3 pie chart slices created."); + is(rows.length, 3, "There should be 3 table chart rows created."); + is(sums.length, 2, "There should be 2 total summaries created."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_charts-06.js b/devtools/client/netmonitor/test/browser_net_charts-06.js new file mode 100644 index 000000000..4bb70e53e --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_charts-06.js @@ -0,0 +1,47 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Makes sure Pie Charts correctly handle empty source data. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { document, Chart } = monitor.panelWin; + + let pie = Chart.Pie(document, { + data: [], + width: 100, + height: 100 + }); + + let node = pie.node; + let slices = node.querySelectorAll(".pie-chart-slice.chart-colored-blob"); + let labels = node.querySelectorAll(".pie-chart-label"); + + is(slices.length, 1, + "There should be 1 pie chart slice created."); + ok(slices[0].getAttribute("d").match( + /\s*M 50,50 L 50\.\d+,2\.5\d* A 47\.5,47\.5 0 1 1 49\.\d+,2\.5\d* Z/), + "The slice has the correct data."); + + ok(slices[0].hasAttribute("largest"), + "The slice should be the largest one."); + ok(slices[0].hasAttribute("smallest"), + "The slice should also be the smallest one."); + ok(slices[0].getAttribute("name"), L10N.getStr("pieChart.unavailable"), + "The slice's name is correct."); + + is(labels.length, 1, + "There should be 1 pie chart label created."); + is(labels[0].textContent, "Empty", + "The label's text is correct."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_charts-07.js b/devtools/client/netmonitor/test/browser_net_charts-07.js new file mode 100644 index 000000000..bb992e4eb --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_charts-07.js @@ -0,0 +1,63 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Makes sure Table Charts correctly handle empty source data. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { document, Chart } = monitor.panelWin; + + let table = Chart.Table(document, { + data: [], + totals: { + label1: value => "Hello " + L10N.numberWithDecimals(value, 2), + label2: value => "World " + L10N.numberWithDecimals(value, 2) + } + }); + + let node = table.node; + let grid = node.querySelector(".table-chart-grid"); + let totals = node.querySelector(".table-chart-totals"); + let rows = grid.querySelectorAll(".table-chart-row"); + let sums = node.querySelectorAll(".table-chart-summary-label"); + + is(rows.length, 1, "There should be 1 table chart row created."); + + ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"), + "A colored blob exists for the firt row."); + is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "size", + "The first column of the first row exists."); + is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label", + "The second column of the first row exists."); + is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "", + "The first column of the first row displays the correct text."); + is(rows[0].querySelectorAll("label")[1].getAttribute("value"), + L10N.getStr("tableChart.unavailable"), + "The second column of the first row displays the correct text."); + + is(sums.length, 2, "There should be 2 total summaries created."); + + is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("name"), + "label1", + "The first sum's type is correct."); + is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("value"), + "Hello 0", + "The first sum's value is correct."); + + is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("name"), + "label2", + "The second sum's type is correct."); + is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("value"), + "World 0", + "The second sum's value is correct."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_clear.js b/devtools/client/netmonitor/test/browser_net_clear.js new file mode 100644 index 000000000..94a60cd39 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_clear.js @@ -0,0 +1,77 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the clear button empties the request menu. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { $, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + let detailsPane = $("#details-pane"); + let detailsPaneToggleButton = $("#details-pane-toggle"); + let clearButton = $("#requests-menu-clear-button"); + + RequestsMenu.lazyUpdate = false; + + // Make sure we start in a sane state + assertNoRequestState(RequestsMenu, detailsPaneToggleButton); + + // Load one request and assert it shows up in the list + let networkEvent = monitor.panelWin.once(monitor.panelWin.EVENTS.NETWORK_EVENT); + tab.linkedBrowser.reload(); + yield networkEvent; + + assertSingleRequestState(); + + // Click clear and make sure the requests are gone + EventUtils.sendMouseEvent({ type: "click" }, clearButton); + assertNoRequestState(); + + // Load a second request and make sure they still show up + networkEvent = monitor.panelWin.once(monitor.panelWin.EVENTS.NETWORK_EVENT); + tab.linkedBrowser.reload(); + yield networkEvent; + + assertSingleRequestState(); + + // Make sure we can now open the details pane + NetMonitorView.toggleDetailsPane({ visible: true, animated: false }); + ok(!detailsPane.classList.contains("pane-collapsed") && + !detailsPaneToggleButton.classList.contains("pane-collapsed"), + "The details pane should be visible after clicking the toggle button."); + + // Click clear and make sure the details pane closes + EventUtils.sendMouseEvent({ type: "click" }, clearButton); + assertNoRequestState(); + ok(detailsPane.classList.contains("pane-collapsed") && + detailsPaneToggleButton.classList.contains("pane-collapsed"), + "The details pane should not be visible clicking 'clear'."); + + return teardown(monitor); + + /** + * Asserts the state of the network monitor when one request has loaded + */ + function assertSingleRequestState() { + is(RequestsMenu.itemCount, 1, + "The request menu should have one item at this point."); + is(detailsPaneToggleButton.hasAttribute("disabled"), false, + "The pane toggle button should be enabled after a request is made."); + } + + /** + * Asserts the state of the network monitor when no requests have loaded + */ + function assertNoRequestState() { + is(RequestsMenu.itemCount, 0, + "The request menu should be empty at this point."); + is(detailsPaneToggleButton.hasAttribute("disabled"), true, + "The pane toggle button should be disabled when the request menu is cleared."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_complex-params.js b/devtools/client/netmonitor/test/browser_net_complex-params.js new file mode 100644 index 000000000..103c644bb --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_complex-params.js @@ -0,0 +1,195 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests whether complex request params and payload sent via POST are + * displayed correctly. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(PARAMS_URL); + info("Starting test... "); + + let { document, EVENTS, Editor, NetMonitorView } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + NetworkDetails._params.lazyEmpty = false; + + let wait = waitForNetworkEvents(monitor, 1, 6); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + let onEvent = monitor.panelWin.once(EVENTS.REQUEST_POST_PARAMS_DISPLAYED); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[2]); + yield onEvent; + yield testParamsTab1("a", '""', '{ "foo": "bar" }', '""'); + + onEvent = monitor.panelWin.once(EVENTS.REQUEST_POST_PARAMS_DISPLAYED); + RequestsMenu.selectedIndex = 1; + yield onEvent; + yield testParamsTab1("a", '"b"', '{ "foo": "bar" }', '""'); + + onEvent = monitor.panelWin.once(EVENTS.REQUEST_POST_PARAMS_DISPLAYED); + RequestsMenu.selectedIndex = 2; + yield onEvent; + yield testParamsTab1("a", '"b"', "foo", '"bar"'); + + onEvent = monitor.panelWin.once(EVENTS.REQUEST_POST_PARAMS_DISPLAYED); + RequestsMenu.selectedIndex = 3; + yield onEvent; + yield testParamsTab2("a", '""', '{ "foo": "bar" }', "js"); + + onEvent = monitor.panelWin.once(EVENTS.REQUEST_POST_PARAMS_DISPLAYED); + RequestsMenu.selectedIndex = 4; + yield onEvent; + yield testParamsTab2("a", '"b"', '{ "foo": "bar" }', "js"); + + onEvent = monitor.panelWin.once(EVENTS.REQUEST_POST_PARAMS_DISPLAYED); + RequestsMenu.selectedIndex = 5; + yield onEvent; + yield testParamsTab2("a", '"b"', "?foo=bar", "text"); + + onEvent = monitor.panelWin.once(EVENTS.SIDEBAR_POPULATED); + RequestsMenu.selectedIndex = 6; + yield onEvent; + yield testParamsTab3("a", '"b"'); + + yield teardown(monitor); + + function testParamsTab1(queryStringParamName, queryStringParamValue, + formDataParamName, formDataParamValue) { + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2]; + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 2, + "The number of param scopes displayed in this tabpanel is incorrect."); + is(tabpanel.querySelectorAll(".variable-or-property").length, 2, + "The number of param values displayed in this tabpanel is incorrect."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0, + "The empty notice should not be displayed in this tabpanel."); + + is(tabpanel.querySelector("#request-params-box") + .hasAttribute("hidden"), false, + "The request params box should not be hidden."); + is(tabpanel.querySelector("#request-post-data-textarea-box") + .hasAttribute("hidden"), true, + "The request post data textarea box should be hidden."); + + let paramsScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + let formDataScope = tabpanel.querySelectorAll(".variables-view-scope")[1]; + + is(paramsScope.querySelector(".name").getAttribute("value"), + L10N.getStr("paramsQueryString"), + "The params scope doesn't have the correct title."); + is(formDataScope.querySelector(".name").getAttribute("value"), + L10N.getStr("paramsFormData"), + "The form data scope doesn't have the correct title."); + + is(paramsScope.querySelectorAll(".variables-view-variable .name")[0] + .getAttribute("value"), + queryStringParamName, + "The first query string param name was incorrect."); + is(paramsScope.querySelectorAll(".variables-view-variable .value")[0] + .getAttribute("value"), + queryStringParamValue, + "The first query string param value was incorrect."); + + is(formDataScope.querySelectorAll(".variables-view-variable .name")[0] + .getAttribute("value"), + formDataParamName, + "The first form data param name was incorrect."); + is(formDataScope.querySelectorAll(".variables-view-variable .value")[0] + .getAttribute("value"), + formDataParamValue, + "The first form data param value was incorrect."); + } + + function* testParamsTab2(queryStringParamName, queryStringParamValue, + requestPayload, editorMode) { + let isJSON = editorMode == "js"; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2]; + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 2, + "The number of param scopes displayed in this tabpanel is incorrect."); + is(tabpanel.querySelectorAll(".variable-or-property").length, isJSON ? 4 : 1, + "The number of param values displayed in this tabpanel is incorrect."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0, + "The empty notice should not be displayed in this tabpanel."); + + is(tabpanel.querySelector("#request-params-box") + .hasAttribute("hidden"), false, + "The request params box should not be hidden."); + is(tabpanel.querySelector("#request-post-data-textarea-box") + .hasAttribute("hidden"), isJSON, + "The request post data textarea box should be hidden."); + + let paramsScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + let payloadScope = tabpanel.querySelectorAll(".variables-view-scope")[1]; + + is(paramsScope.querySelector(".name").getAttribute("value"), + L10N.getStr("paramsQueryString"), + "The params scope doesn't have the correct title."); + is(payloadScope.querySelector(".name").getAttribute("value"), + isJSON ? L10N.getStr("jsonScopeName") : L10N.getStr("paramsPostPayload"), + "The request payload scope doesn't have the correct title."); + + is(paramsScope.querySelectorAll(".variables-view-variable .name")[0] + .getAttribute("value"), + queryStringParamName, + "The first query string param name was incorrect."); + is(paramsScope.querySelectorAll(".variables-view-variable .value")[0] + .getAttribute("value"), + queryStringParamValue, + "The first query string param value was incorrect."); + + if (isJSON) { + let requestPayloadObject = JSON.parse(requestPayload); + let requestPairs = Object.keys(requestPayloadObject) + .map(k => [k, requestPayloadObject[k]]); + let displayedNames = payloadScope.querySelectorAll( + ".variables-view-property.variable-or-property .name"); + let displayedValues = payloadScope.querySelectorAll( + ".variables-view-property.variable-or-property .value"); + for (let i = 0; i < requestPairs.length; i++) { + let [requestPayloadName, requestPayloadValue] = requestPairs[i]; + is(requestPayloadName, displayedNames[i].getAttribute("value"), + "JSON property name " + i + " should be displayed correctly"); + is('"' + requestPayloadValue + '"', displayedValues[i].getAttribute("value"), + "JSON property value " + i + " should be displayed correctly"); + } + } else { + let editor = yield NetMonitorView.editor("#request-post-data-textarea"); + is(editor.getText(), requestPayload, + "The text shown in the source editor is incorrect."); + is(editor.getMode(), Editor.modes[editorMode], + "The mode active in the source editor is incorrect."); + } + } + + function testParamsTab3(queryStringParamName, queryStringParamValue) { + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2]; + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 0, + "The number of param scopes displayed in this tabpanel is incorrect."); + is(tabpanel.querySelectorAll(".variable-or-property").length, 0, + "The number of param values displayed in this tabpanel is incorrect."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 1, + "The empty notice should be displayed in this tabpanel."); + + is(tabpanel.querySelector("#request-params-box") + .hasAttribute("hidden"), false, + "The request params box should not be hidden."); + is(tabpanel.querySelector("#request-post-data-textarea-box") + .hasAttribute("hidden"), true, + "The request post data textarea box should be hidden."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_content-type.js b/devtools/client/netmonitor/test/browser_net_content-type.js new file mode 100644 index 000000000..1951bc69d --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_content-type.js @@ -0,0 +1,255 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if different response content types are handled correctly. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL); + info("Starting test... "); + + let { document, Editor, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "GET", CONTENT_TYPE_SJS + "?fmt=xml", { + status: 200, + statusText: "OK", + type: "xml", + fullMimeType: "text/xml; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 42), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1), + "GET", CONTENT_TYPE_SJS + "?fmt=css", { + status: 200, + statusText: "OK", + type: "css", + fullMimeType: "text/css; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(2), + "GET", CONTENT_TYPE_SJS + "?fmt=js", { + status: 200, + statusText: "OK", + type: "js", + fullMimeType: "application/javascript; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(3), + "GET", CONTENT_TYPE_SJS + "?fmt=json", { + status: 200, + statusText: "OK", + type: "json", + fullMimeType: "application/json; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4), + "GET", CONTENT_TYPE_SJS + "?fmt=bogus", { + status: 404, + statusText: "Not Found", + type: "html", + fullMimeType: "text/html; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 24), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(5), + "GET", TEST_IMAGE, { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "png", + fullMimeType: "image/png", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 580), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(6), + "GET", CONTENT_TYPE_SJS + "?fmt=gzip", { + status: 200, + statusText: "OK", + type: "plain", + fullMimeType: "text/plain", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 73), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 10.73), + time: true + }); + + let onEvent = waitForResponseBodyDisplayed(); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[3]); + yield onEvent; + yield testResponseTab("xml"); + + yield selectIndexAndWaitForTabUpdated(1); + yield testResponseTab("css"); + + yield selectIndexAndWaitForTabUpdated(2); + yield testResponseTab("js"); + + yield selectIndexAndWaitForTabUpdated(3); + yield testResponseTab("json"); + + yield selectIndexAndWaitForTabUpdated(4); + yield testResponseTab("html"); + + yield selectIndexAndWaitForTabUpdated(5); + yield testResponseTab("png"); + + yield selectIndexAndWaitForTabUpdated(6); + yield testResponseTab("gzip"); + + yield teardown(monitor); + + function* testResponseTab(type) { + let tabEl = document.querySelectorAll("#details-pane tab")[3]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3]; + + is(tabEl.getAttribute("selected"), "true", + "The response tab in the network details pane should be selected."); + + function checkVisibility(box) { + is(tabpanel.querySelector("#response-content-info-header") + .hasAttribute("hidden"), true, + "The response info header doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-json-box") + .hasAttribute("hidden"), box != "json", + "The response content json box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-textarea-box") + .hasAttribute("hidden"), box != "textarea", + "The response content textarea box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-image-box") + .hasAttribute("hidden"), box != "image", + "The response content image box doesn't have the intended visibility."); + } + + switch (type) { + case "xml": { + checkVisibility("textarea"); + + let editor = yield NetMonitorView.editor("#response-content-textarea"); + is(editor.getText(), "<label value='greeting'>Hello XML!</label>", + "The text shown in the source editor is incorrect for the xml request."); + is(editor.getMode(), Editor.modes.html, + "The mode active in the source editor is incorrect for the xml request."); + break; + } + case "css": { + checkVisibility("textarea"); + + let editor = yield NetMonitorView.editor("#response-content-textarea"); + is(editor.getText(), "body:pre { content: 'Hello CSS!' }", + "The text shown in the source editor is incorrect for the xml request."); + is(editor.getMode(), Editor.modes.css, + "The mode active in the source editor is incorrect for the xml request."); + break; + } + case "js": { + checkVisibility("textarea"); + + let editor = yield NetMonitorView.editor("#response-content-textarea"); + is(editor.getText(), "function() { return 'Hello JS!'; }", + "The text shown in the source editor is incorrect for the xml request."); + is(editor.getMode(), Editor.modes.js, + "The mode active in the source editor is incorrect for the xml request."); + break; + } + case "json": { + checkVisibility("json"); + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 1, + "There should be 1 json scope displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-property").length, 2, + "There should be 2 json properties displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0, + "The empty notice should not be displayed in this tabpanel."); + + let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + + is(jsonScope.querySelector(".name").getAttribute("value"), + L10N.getStr("jsonScopeName"), + "The json scope doesn't have the correct title."); + + is(jsonScope.querySelectorAll(".variables-view-property .name")[0] + .getAttribute("value"), + "greeting", "The first json property name was incorrect."); + is(jsonScope.querySelectorAll(".variables-view-property .value")[0] + .getAttribute("value"), + "\"Hello JSON!\"", "The first json property value was incorrect."); + + is(jsonScope.querySelectorAll(".variables-view-property .name")[1] + .getAttribute("value"), + "__proto__", "The second json property name was incorrect."); + is(jsonScope.querySelectorAll(".variables-view-property .value")[1] + .getAttribute("value"), + "Object", "The second json property value was incorrect."); + break; + } + case "html": { + checkVisibility("textarea"); + + let editor = yield NetMonitorView.editor("#response-content-textarea"); + is(editor.getText(), "<blink>Not Found</blink>", + "The text shown in the source editor is incorrect for the xml request."); + is(editor.getMode(), Editor.modes.html, + "The mode active in the source editor is incorrect for the xml request."); + break; + } + case "png": { + checkVisibility("image"); + + let imageNode = tabpanel.querySelector("#response-content-image"); + yield once(imageNode, "load"); + + is(tabpanel.querySelector("#response-content-image-name-value") + .getAttribute("value"), "test-image.png", + "The image name info isn't correct."); + is(tabpanel.querySelector("#response-content-image-mime-value") + .getAttribute("value"), "image/png", + "The image mime info isn't correct."); + is(tabpanel.querySelector("#response-content-image-dimensions-value") + .getAttribute("value"), "16" + " \u00D7 " + "16", + "The image dimensions info isn't correct."); + break; + } + case "gzip": { + checkVisibility("textarea"); + + let expected = new Array(1000).join("Hello gzip!"); + let editor = yield NetMonitorView.editor("#response-content-textarea"); + is(editor.getText(), expected, + "The text shown in the source editor is incorrect for the gzip request."); + is(editor.getMode(), Editor.modes.text, + "The mode active in the source editor is incorrect for the gzip request."); + break; + } + } + } + + function selectIndexAndWaitForTabUpdated(index) { + let onTabUpdated = monitor.panelWin.once(monitor.panelWin.EVENTS.TAB_UPDATED); + RequestsMenu.selectedIndex = index; + return onTabUpdated; + } + + function waitForResponseBodyDisplayed() { + return monitor.panelWin.once(monitor.panelWin.EVENTS.RESPONSE_BODY_DISPLAYED); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_copy_as_curl.js b/devtools/client/netmonitor/test/browser_net_copy_as_curl.js new file mode 100644 index 000000000..9cf66aa4f --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_copy_as_curl.js @@ -0,0 +1,88 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if Copy as cURL works. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CURL_URL); + info("Starting test... "); + + // Different quote chars are used for Windows and POSIX + const QUOTE = Services.appinfo.OS == "WINNT" ? "\"" : "'"; + + // Quote a string, escape the quotes inside the string + function quote(str) { + return QUOTE + str.replace(new RegExp(QUOTE, "g"), `\\${QUOTE}`) + QUOTE; + } + + // Header param is formatted as -H "Header: value" or -H 'Header: value' + function header(h) { + return "-H " + quote(h); + } + + // Construct the expected command + const EXPECTED_RESULT = [ + "curl " + quote(SIMPLE_SJS), + "--compressed", + header("Host: example.com"), + header("User-Agent: " + navigator.userAgent), + header("Accept: */*"), + header("Accept-Language: " + navigator.language), + header("X-Custom-Header-1: Custom value"), + header("X-Custom-Header-2: 8.8.8.8"), + header("X-Custom-Header-3: Mon, 3 Mar 2014 11:11:11 GMT"), + header("Referer: " + CURL_URL), + header("Connection: keep-alive"), + header("Pragma: no-cache"), + header("Cache-Control: no-cache") + ]; + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, SIMPLE_SJS, function* (url) { + content.wrappedJSObject.performRequest(url); + }); + yield wait; + + let requestItem = RequestsMenu.getItemAtIndex(0); + RequestsMenu.selectedItem = requestItem; + + yield waitForClipboardPromise(function setup() { + RequestsMenu.contextMenu.copyAsCurl(); + }, function validate(result) { + if (typeof result !== "string") { + return false; + } + + // Different setups may produce the same command, but with the + // parameters in a different order in the commandline (which is fine). + // Here we confirm that the commands are the same even in that case. + + // This monster regexp parses the command line into an array of arguments, + // recognizing quoted args with matching quotes and escaped quotes inside: + // [ "curl 'url'", "--standalone-arg", "-arg-with-quoted-string 'value\'s'" ] + let matchRe = /[-A-Za-z1-9]+(?: ([\"'])(?:\\\1|.)*?\1)?/g; + + let actual = result.match(matchRe); + + // Must begin with the same "curl 'URL'" segment + if (!actual || EXPECTED_RESULT[0] != actual[0]) { + return false; + } + + // Must match each of the params in the middle (headers and --compressed) + return EXPECTED_RESULT.length === actual.length && + EXPECTED_RESULT.every(param => actual.includes(param)); + }); + + info("Clipboard contains a cURL command for the currently selected item's url."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_copy_headers.js b/devtools/client/netmonitor/test/browser_net_copy_headers.js new file mode 100644 index 000000000..36ce2fb34 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_copy_headers.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if copying a request's request/response headers works. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1); + tab.linkedBrowser.reload(); + yield wait; + + let requestItem = RequestsMenu.getItemAtIndex(0); + RequestsMenu.selectedItem = requestItem; + + let { method, httpVersion, status, statusText } = requestItem.attachment; + + const EXPECTED_REQUEST_HEADERS = [ + `${method} ${SIMPLE_URL} ${httpVersion}`, + "Host: example.com", + "User-Agent: " + navigator.userAgent + "", + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + "Accept-Language: " + navigator.languages.join(",") + ";q=0.5", + "Accept-Encoding: gzip, deflate", + "Connection: keep-alive", + "Upgrade-Insecure-Requests: 1", + "Pragma: no-cache", + "Cache-Control: no-cache" + ].join("\n"); + + yield waitForClipboardPromise(function setup() { + RequestsMenu.contextMenu.copyRequestHeaders(); + }, function validate(result) { + // Sometimes, a "Cookie" header is left over from other tests. Remove it: + result = String(result).replace(/Cookie: [^\n]+\n/, ""); + return result === EXPECTED_REQUEST_HEADERS; + }); + info("Clipboard contains the currently selected item's request headers."); + + const EXPECTED_RESPONSE_HEADERS = [ + `${httpVersion} ${status} ${statusText}`, + "Last-Modified: Sun, 3 May 2015 11:11:11 GMT", + "Content-Type: text/html", + "Content-Length: 465", + "Connection: close", + "Server: httpd.js", + "Date: Sun, 3 May 2015 11:11:11 GMT" + ].join("\n"); + + yield waitForClipboardPromise(function setup() { + RequestsMenu.contextMenu.copyResponseHeaders(); + }, function validate(result) { + // Fake the "Last-Modified" and "Date" headers because they will vary: + result = String(result) + .replace(/Last-Modified: [^\n]+ GMT/, "Last-Modified: Sun, 3 May 2015 11:11:11 GMT") + .replace(/Date: [^\n]+ GMT/, "Date: Sun, 3 May 2015 11:11:11 GMT"); + return result === EXPECTED_RESPONSE_HEADERS; + }); + info("Clipboard contains the currently selected item's response headers."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_copy_image_as_data_uri.js b/devtools/client/netmonitor/test/browser_net_copy_image_as_data_uri.js new file mode 100644 index 000000000..144ced80d --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_copy_image_as_data_uri.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if copying an image as data uri works. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL); + info("Starting test... "); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + let requestItem = RequestsMenu.getItemAtIndex(5); + RequestsMenu.selectedItem = requestItem; + + yield waitForClipboardPromise(function setup() { + RequestsMenu.contextMenu.copyImageAsDataUri(); + }, TEST_IMAGE_DATA_URI); + + ok(true, "Clipboard contains the currently selected image as data uri."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_copy_params.js b/devtools/client/netmonitor/test/browser_net_copy_params.js new file mode 100644 index 000000000..1cb6f6620 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_copy_params.js @@ -0,0 +1,98 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests whether copying a request item's parameters works. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(PARAMS_URL); + info("Starting test... "); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1, 6); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + RequestsMenu.selectedItem = RequestsMenu.getItemAtIndex(0); + yield testCopyUrlParamsHidden(false); + yield testCopyUrlParams("a"); + yield testCopyPostDataHidden(false); + yield testCopyPostData("{ \"foo\": \"bar\" }"); + + RequestsMenu.selectedItem = RequestsMenu.getItemAtIndex(1); + yield testCopyUrlParamsHidden(false); + yield testCopyUrlParams("a=b"); + yield testCopyPostDataHidden(false); + yield testCopyPostData("{ \"foo\": \"bar\" }"); + + RequestsMenu.selectedItem = RequestsMenu.getItemAtIndex(2); + yield testCopyUrlParamsHidden(false); + yield testCopyUrlParams("a=b"); + yield testCopyPostDataHidden(false); + yield testCopyPostData("foo=bar"); + + RequestsMenu.selectedItem = RequestsMenu.getItemAtIndex(3); + yield testCopyUrlParamsHidden(false); + yield testCopyUrlParams("a"); + yield testCopyPostDataHidden(false); + yield testCopyPostData("{ \"foo\": \"bar\" }"); + + RequestsMenu.selectedItem = RequestsMenu.getItemAtIndex(4); + yield testCopyUrlParamsHidden(false); + yield testCopyUrlParams("a=b"); + yield testCopyPostDataHidden(false); + yield testCopyPostData("{ \"foo\": \"bar\" }"); + + RequestsMenu.selectedItem = RequestsMenu.getItemAtIndex(5); + yield testCopyUrlParamsHidden(false); + yield testCopyUrlParams("a=b"); + yield testCopyPostDataHidden(false); + yield testCopyPostData("?foo=bar"); + + RequestsMenu.selectedItem = RequestsMenu.getItemAtIndex(6); + yield testCopyUrlParamsHidden(true); + yield testCopyPostDataHidden(true); + + return teardown(monitor); + + function testCopyUrlParamsHidden(hidden) { + let allMenuItems = openContextMenuAndGetAllItems(NetMonitorView); + let copyUrlParamsNode = allMenuItems.find(item => + item.id === "request-menu-context-copy-url-params"); + is(copyUrlParamsNode.visible, !hidden, + "The \"Copy URL Parameters\" context menu item should" + (hidden ? " " : " not ") + + "be hidden."); + } + + function* testCopyUrlParams(queryString) { + yield waitForClipboardPromise(function setup() { + RequestsMenu.contextMenu.copyUrlParams(); + }, queryString); + ok(true, "The url query string copied from the selected item is correct."); + } + + function testCopyPostDataHidden(hidden) { + let allMenuItems = openContextMenuAndGetAllItems(NetMonitorView); + let copyPostDataNode = allMenuItems.find(item => + item.id === "request-menu-context-copy-post-data"); + is(copyPostDataNode.visible, !hidden, + "The \"Copy POST Data\" context menu item should" + (hidden ? " " : " not ") + + "be hidden."); + } + + function* testCopyPostData(postData) { + yield waitForClipboardPromise(function setup() { + RequestsMenu.contextMenu.copyPostData(); + }, postData); + ok(true, "The post data string copied from the selected item is correct."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_copy_response.js b/devtools/client/netmonitor/test/browser_net_copy_response.js new file mode 100644 index 000000000..411fe5cf0 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_copy_response.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if copying a request's response works. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL); + info("Starting test... "); + + const EXPECTED_RESULT = '{ "greeting": "Hello JSON!" }'; + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + let requestItem = RequestsMenu.getItemAtIndex(3); + RequestsMenu.selectedItem = requestItem; + + yield waitForClipboardPromise(function setup() { + RequestsMenu.contextMenu.copyResponse(); + }, EXPECTED_RESULT); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js b/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js new file mode 100644 index 000000000..252ce92bd --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if copying an image as data uri works. + */ + +const SVG_URL = EXAMPLE_URL + "dropmarker.svg"; + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CURL_URL); + info("Starting test... "); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, SVG_URL, function* (url) { + content.wrappedJSObject.performRequest(url); + }); + yield wait; + + let requestItem = RequestsMenu.getItemAtIndex(0); + RequestsMenu.selectedItem = requestItem; + + yield waitForClipboardPromise(function setup() { + RequestsMenu.contextMenu.copyImageAsDataUri(); + }, function check(text) { + return text.startsWith("data:") && !/undefined/.test(text); + }); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_copy_url.js b/devtools/client/netmonitor/test/browser_net_copy_url.js new file mode 100644 index 000000000..660f5fe79 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_copy_url.js @@ -0,0 +1,31 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if copying a request's url works. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + info("Starting test... "); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(1); + }); + yield wait; + + let requestItem = RequestsMenu.getItemAtIndex(0); + RequestsMenu.selectedItem = requestItem; + + yield waitForClipboardPromise(function setup() { + RequestsMenu.contextMenu.copyUrl(); + }, requestItem.attachment.url); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_cors_requests.js b/devtools/client/netmonitor/test/browser_net_cors_requests.js new file mode 100644 index 000000000..d61b8e2f0 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_cors_requests.js @@ -0,0 +1,33 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test that CORS preflight requests are displayed by network monitor + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CORS_URL); + let { RequestsMenu } = monitor.panelWin.NetMonitorView; + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1, 1); + + info("Performing a CORS request"); + let requestUrl = "http://test1.example.com" + CORS_SJS_PATH; + yield ContentTask.spawn(tab.linkedBrowser, requestUrl, function* (url) { + content.wrappedJSObject.performRequests(url, "triggering/preflight", "post-data"); + }); + + info("Waiting until the requests appear in netmonitor"); + yield wait; + + info("Checking the preflight and flight methods"); + ["OPTIONS", "POST"].forEach((method, i) => { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), method, requestUrl); + }); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_curl-utils.js b/devtools/client/netmonitor/test/browser_net_curl-utils.js new file mode 100644 index 000000000..7a5fc7926 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_curl-utils.js @@ -0,0 +1,228 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests Curl Utils functionality. + */ + +const { CurlUtils } = require("devtools/client/shared/curl"); + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CURL_UTILS_URL); + info("Starting test... "); + + let { NetMonitorView, gNetwork } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1, 3); + yield ContentTask.spawn(tab.linkedBrowser, SIMPLE_SJS, function* (url) { + content.wrappedJSObject.performRequests(url); + }); + yield wait; + + let requests = { + get: RequestsMenu.getItemAtIndex(0), + post: RequestsMenu.getItemAtIndex(1), + multipart: RequestsMenu.getItemAtIndex(2), + multipartForm: RequestsMenu.getItemAtIndex(3) + }; + + let data = yield createCurlData(requests.get.attachment, gNetwork); + testFindHeader(data); + + data = yield createCurlData(requests.post.attachment, gNetwork); + testIsUrlEncodedRequest(data); + testWritePostDataTextParams(data); + + data = yield createCurlData(requests.multipart.attachment, gNetwork); + testIsMultipartRequest(data); + testGetMultipartBoundary(data); + testRemoveBinaryDataFromMultipartText(data); + + data = yield createCurlData(requests.multipartForm.attachment, gNetwork); + testGetHeadersFromMultipartText(data); + + if (Services.appinfo.OS != "WINNT") { + testEscapeStringPosix(); + } else { + testEscapeStringWin(); + } + + yield teardown(monitor); +}); + +function testIsUrlEncodedRequest(data) { + let isUrlEncoded = CurlUtils.isUrlEncodedRequest(data); + ok(isUrlEncoded, "Should return true for url encoded requests."); +} + +function testIsMultipartRequest(data) { + let isMultipart = CurlUtils.isMultipartRequest(data); + ok(isMultipart, "Should return true for multipart/form-data requests."); +} + +function testFindHeader(data) { + let headers = data.headers; + let hostName = CurlUtils.findHeader(headers, "Host"); + let requestedWithLowerCased = CurlUtils.findHeader(headers, "x-requested-with"); + let doesNotExist = CurlUtils.findHeader(headers, "X-Does-Not-Exist"); + + is(hostName, "example.com", + "Header with name 'Host' should be found in the request array."); + is(requestedWithLowerCased, "XMLHttpRequest", + "The search should be case insensitive."); + is(doesNotExist, null, + "Should return null when a header is not found."); +} + +function testWritePostDataTextParams(data) { + let params = CurlUtils.writePostDataTextParams(data.postDataText); + is(params, "param1=value1¶m2=value2¶m3=value3", + "Should return a serialized representation of the request parameters"); +} + +function testGetMultipartBoundary(data) { + let boundary = CurlUtils.getMultipartBoundary(data); + ok(/-{3,}\w+/.test(boundary), + "A boundary string should be found in a multipart request."); +} + +function testRemoveBinaryDataFromMultipartText(data) { + let generatedBoundary = CurlUtils.getMultipartBoundary(data); + let text = data.postDataText; + let binaryRemoved = + CurlUtils.removeBinaryDataFromMultipartText(text, generatedBoundary); + let boundary = "--" + generatedBoundary; + + const EXPECTED_POSIX_RESULT = [ + "$'", + boundary, + "\\r\\n\\r\\n", + "Content-Disposition: form-data; name=\"param1\"", + "\\r\\n\\r\\n", + "value1", + "\\r\\n", + boundary, + "\\r\\n\\r\\n", + "Content-Disposition: form-data; name=\"file\"; filename=\"filename.png\"", + "\\r\\n", + "Content-Type: image/png", + "\\r\\n\\r\\n", + boundary + "--", + "\\r\\n", + "'" + ].join(""); + + const EXPECTED_WIN_RESULT = [ + '"' + boundary + '"^', + "\u000d\u000A\u000d\u000A", + '"Content-Disposition: form-data; name=""param1"""^', + "\u000d\u000A\u000d\u000A", + '"value1"^', + "\u000d\u000A", + '"' + boundary + '"^', + "\u000d\u000A\u000d\u000A", + '"Content-Disposition: form-data; name=""file""; filename=""filename.png"""^', + "\u000d\u000A", + '"Content-Type: image/png"^', + "\u000d\u000A\u000d\u000A", + '"' + boundary + '--"^', + "\u000d\u000A", + '""' + ].join(""); + + if (Services.appinfo.OS != "WINNT") { + is(CurlUtils.escapeStringPosix(binaryRemoved), EXPECTED_POSIX_RESULT, + "The mulitpart request payload should not contain binary data."); + } else { + is(CurlUtils.escapeStringWin(binaryRemoved), EXPECTED_WIN_RESULT, + "WinNT: The mulitpart request payload should not contain binary data."); + } +} + +function testGetHeadersFromMultipartText(data) { + let headers = CurlUtils.getHeadersFromMultipartText(data.postDataText); + + ok(Array.isArray(headers), "Should return an array."); + ok(headers.length > 0, "There should exist at least one request header."); + is(headers[0].name, "Content-Type", "The first header name should be 'Content-Type'."); +} + +function testEscapeStringPosix() { + let surroundedWithQuotes = "A simple string"; + is(CurlUtils.escapeStringPosix(surroundedWithQuotes), "'A simple string'", + "The string should be surrounded with single quotes."); + + let singleQuotes = "It's unusual to put crickets in your coffee."; + is(CurlUtils.escapeStringPosix(singleQuotes), + "$'It\\'s unusual to put crickets in your coffee.'", + "Single quotes should be escaped."); + + let newLines = "Line 1\r\nLine 2\u000d\u000ALine3"; + is(CurlUtils.escapeStringPosix(newLines), "$'Line 1\\r\\nLine 2\\r\\nLine3'", + "Newlines should be escaped."); + + let controlChars = "\u0007 \u0009 \u000C \u001B"; + is(CurlUtils.escapeStringPosix(controlChars), "$'\\x07 \\x09 \\x0c \\x1b'", + "Control characters should be escaped."); + + let extendedAsciiChars = "æ ø ü ß ö é"; + is(CurlUtils.escapeStringPosix(extendedAsciiChars), + "$'\\xc3\\xa6 \\xc3\\xb8 \\xc3\\xbc \\xc3\\x9f \\xc3\\xb6 \\xc3\\xa9'", + "Character codes outside of the decimal range 32 - 126 should be escaped."); +} + +function testEscapeStringWin() { + let surroundedWithDoubleQuotes = "A simple string"; + is(CurlUtils.escapeStringWin(surroundedWithDoubleQuotes), '"A simple string"', + "The string should be surrounded with double quotes."); + + let doubleQuotes = "Quote: \"Time is an illusion. Lunchtime doubly so.\""; + is(CurlUtils.escapeStringWin(doubleQuotes), + '"Quote: ""Time is an illusion. Lunchtime doubly so."""', + "Double quotes should be escaped."); + + let percentSigns = "%AppData%"; + is(CurlUtils.escapeStringWin(percentSigns), '""%"AppData"%""', + "Percent signs should be escaped."); + + let backslashes = "\\A simple string\\"; + is(CurlUtils.escapeStringWin(backslashes), '"\\\\A simple string\\\\"', + "Backslashes should be escaped."); + + let newLines = "line1\r\nline2\r\nline3"; + is(CurlUtils.escapeStringWin(newLines), + '"line1"^\u000d\u000A"line2"^\u000d\u000A"line3"', + "Newlines should be escaped."); +} + +function* createCurlData(selected, network, controller) { + let { url, method, httpVersion } = selected; + + // Create a sanitized object for the Curl command generator. + let data = { + url, + method, + headers: [], + httpVersion, + postDataText: null + }; + + // Fetch header values. + for (let { name, value } of selected.requestHeaders.headers) { + let text = yield network.getString(value); + data.headers.push({ name: name, value: text }); + } + + // Fetch the request payload. + if (selected.requestPostData) { + let postData = selected.requestPostData.postData.text; + data.postDataText = yield network.getString(postData); + } + + return data; +} diff --git a/devtools/client/netmonitor/test/browser_net_cyrillic-01.js b/devtools/client/netmonitor/test/browser_net_cyrillic-01.js new file mode 100644 index 000000000..43d6f522e --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_cyrillic-01.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if cyrillic text is rendered correctly in the source editor. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CYRILLIC_URL); + info("Starting test... "); + + let { document, EVENTS, Editor, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "GET", CONTENT_TYPE_SJS + "?fmt=txt", { + status: 200, + statusText: "DA DA DA" + }); + + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[3]); + + yield monitor.panelWin.once(EVENTS.RESPONSE_BODY_DISPLAYED); + let editor = yield NetMonitorView.editor("#response-content-textarea"); + // u044F = я + is(editor.getText().indexOf("\u044F"), 26, + "The text shown in the source editor is correct."); + is(editor.getMode(), Editor.modes.text, + "The mode active in the source editor is correct."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_cyrillic-02.js b/devtools/client/netmonitor/test/browser_net_cyrillic-02.js new file mode 100644 index 000000000..cd6b2000e --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_cyrillic-02.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if cyrillic text is rendered correctly in the source editor + * when loaded directly from an HTML page. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CYRILLIC_URL); + info("Starting test... "); + + let { document, EVENTS, Editor, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1); + tab.linkedBrowser.reload(); + yield wait; + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "GET", CYRILLIC_URL, { + status: 200, + statusText: "OK" + }); + + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[3]); + + yield monitor.panelWin.once(EVENTS.RESPONSE_BODY_DISPLAYED); + let editor = yield NetMonitorView.editor("#response-content-textarea"); + // u044F = я + is(editor.getText().indexOf("\u044F"), 486, + "The text shown in the source editor is correct."); + is(editor.getMode(), Editor.modes.html, + "The mode active in the source editor is correct."); + + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_details-no-duplicated-content.js b/devtools/client/netmonitor/test/browser_net_details-no-duplicated-content.js new file mode 100644 index 000000000..c3df51ced --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_details-no-duplicated-content.js @@ -0,0 +1,172 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// A test to ensure that the content in details pane is not duplicated. + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + let panel = monitor.panelWin; + let { NetMonitorView, EVENTS } = panel; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + + const COOKIE_UNIQUE_PATH = "/do-not-use-in-other-tests-using-cookies"; + + let TEST_CASES = [ + { + desc: "Test headers tab", + pageURI: CUSTOM_GET_URL, + requestURI: null, + isPost: false, + tabIndex: 0, + variablesView: NetworkDetails._headers, + expectedScopeLength: 2, + }, + { + desc: "Test cookies tab", + pageURI: CUSTOM_GET_URL, + requestURI: COOKIE_UNIQUE_PATH, + isPost: false, + tabIndex: 1, + variablesView: NetworkDetails._cookies, + expectedScopeLength: 1, + }, + { + desc: "Test params tab", + pageURI: POST_RAW_URL, + requestURI: null, + isPost: true, + tabIndex: 2, + variablesView: NetworkDetails._params, + expectedScopeLength: 1, + }, + ]; + + info("Adding a cookie for the \"Cookie\" tab test"); + yield setDocCookie("a=b; path=" + COOKIE_UNIQUE_PATH); + + info("Running tests"); + for (let spec of TEST_CASES) { + yield runTestCase(spec); + } + + // Remove the cookie. If an error occurs the path of the cookie ensures it + // doesn't mess with the other tests. + info("Removing the added cookie."); + yield setDocCookie( + "a=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=" + COOKIE_UNIQUE_PATH); + + yield teardown(monitor); + + /** + * Set a content document cookie + */ + function setDocCookie(cookie) { + return ContentTask.spawn(tab.linkedBrowser, cookie, function* (cookieArg) { + content.document.cookie = cookieArg; + }); + } + + /** + * A helper that handles the execution of each case. + */ + function* runTestCase(spec) { + info("Running case: " + spec.desc); + let wait = waitForNetworkEvents(monitor, 1); + tab.linkedBrowser.loadURI(spec.pageURI); + yield wait; + + RequestsMenu.clear(); + yield waitForFinalDetailTabUpdate(spec.tabIndex, spec.isPost, spec.requestURI); + + is(spec.variablesView._store.length, spec.expectedScopeLength, + "View contains " + spec.expectedScopeLength + " scope headers"); + } + + /** + * A helper that prepares the variables view for the actual testing. It + * - selects the correct tab + * - performs the specified request to specified URI + * - opens the details view + * - waits for the final update to happen + */ + function* waitForFinalDetailTabUpdate(tabIndex, isPost, uri) { + let onNetworkEvent = panel.once(EVENTS.NETWORK_EVENT); + let onDetailsPopulated = panel.once(EVENTS.NETWORKDETAILSVIEW_POPULATED); + let onRequestFinished = isPost ? + waitForNetworkEvents(monitor, 0, 1) : + waitForNetworkEvents(monitor, 1); + + info("Performing a request"); + yield ContentTask.spawn(tab.linkedBrowser, uri, function* (url) { + content.wrappedJSObject.performRequests(1, url); + }); + + info("Waiting for NETWORK_EVENT"); + yield onNetworkEvent; + + if (!RequestsMenu.getItemAtIndex(0)) { + info("Waiting for the request to be added to the view"); + yield monitor.panelWin.once(EVENTS.REQUEST_ADDED); + } + + ok(true, "Received NETWORK_EVENT. Selecting the item."); + let item = RequestsMenu.getItemAtIndex(0); + RequestsMenu.selectedItem = item; + + info("Item selected. Waiting for NETWORKDETAILSVIEW_POPULATED"); + yield onDetailsPopulated; + + info("Received populated event. Selecting tab at index " + tabIndex); + NetworkDetails.widget.selectedIndex = tabIndex; + + info("Waiting for request to finish."); + yield onRequestFinished; + + ok(true, "Request finished."); + + /** + * Because this test uses lazy updates there's four scenarios to consider: + * #1: Everything is updated and test is ready to continue. + * #2: There's updates that are waiting to be flushed. + * #3: Updates are flushed but the tab update is still running. + * #4: There's pending updates and a tab update is still running. + * + * For case #1 there's not going to be a TAB_UPDATED event so don't wait for + * it (bug 1106181). + * + * For cases #2 and #3 it's enough to wait for one TAB_UPDATED event as for + * - case #2 the next flush will perform the final update and single + * TAB_UPDATED event is emitted. + * - case #3 the running update is the final update that'll emit one + * TAB_UPDATED event. + * + * For case #4 we must wait for the updates to be flushed before we can + * start waiting for TAB_UPDATED event or we'll continue the test right + * after the pending update finishes. + */ + let hasQueuedUpdates = RequestsMenu._updateQueue.length !== 0; + let hasRunningTabUpdate = NetworkDetails._viewState.updating[tabIndex]; + + if (hasQueuedUpdates || hasRunningTabUpdate) { + info("There's pending updates - waiting for them to finish."); + info(" hasQueuedUpdates: " + hasQueuedUpdates); + info(" hasRunningTabUpdate: " + hasRunningTabUpdate); + + if (hasQueuedUpdates && hasRunningTabUpdate) { + info("Waiting for updates to be flushed."); + // _flushRequests calls .populate which emits the following event + yield panel.once(EVENTS.NETWORKDETAILSVIEW_POPULATED); + + info("Requests flushed."); + } + + info("Waiting for final tab update."); + yield waitFor(panel, EVENTS.TAB_UPDATED); + } + + info("All updates completed."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_filter-01.js b/devtools/client/netmonitor/test/browser_net_filter-01.js new file mode 100644 index 000000000..b0d76c629 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_filter-01.js @@ -0,0 +1,264 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test if filtering items in the network table works correctly. + */ +const BASIC_REQUESTS = [ + { url: "sjs_content-type-test-server.sjs?fmt=html&res=undefined&text=Sample" }, + { url: "sjs_content-type-test-server.sjs?fmt=css&text=sample" }, + { url: "sjs_content-type-test-server.sjs?fmt=js&text=sample" }, +]; + +const REQUESTS_WITH_MEDIA = BASIC_REQUESTS.concat([ + { url: "sjs_content-type-test-server.sjs?fmt=font" }, + { url: "sjs_content-type-test-server.sjs?fmt=image" }, + { url: "sjs_content-type-test-server.sjs?fmt=audio" }, + { url: "sjs_content-type-test-server.sjs?fmt=video" }, +]); + +const REQUESTS_WITH_MEDIA_AND_FLASH = REQUESTS_WITH_MEDIA.concat([ + { url: "sjs_content-type-test-server.sjs?fmt=flash" }, +]); + +const REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS = REQUESTS_WITH_MEDIA_AND_FLASH.concat([ + /* "Upgrade" is a reserved header and can not be set on XMLHttpRequest */ + { url: "sjs_content-type-test-server.sjs?fmt=ws" }, +]); + +add_task(function* () { + let Actions = require("devtools/client/netmonitor/actions/index"); + let { monitor } = yield initNetMonitor(FILTERING_URL); + let { gStore } = monitor.panelWin; + + function setFreetextFilter(value) { + gStore.dispatch(Actions.setFilterText(value)); + } + + info("Starting test... "); + + let { $, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 9); + loadCommonFrameScript(); + yield performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS); + yield wait; + + EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle")); + + isnot(RequestsMenu.selectedItem, null, + "There should be a selected item in the requests menu."); + is(RequestsMenu.selectedIndex, 0, + "The first item should be selected in the requests menu."); + is(NetMonitorView.detailsPaneHidden, false, + "The details pane should not be hidden after toggle button was pressed."); + + // First test with single filters... + testFilterButtons(monitor, "all"); + testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]); + + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button")); + testFilterButtons(monitor, "html"); + testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]); + + // Reset filters + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-css-button")); + testFilterButtons(monitor, "css"); + testContents([0, 1, 0, 0, 0, 0, 0, 0, 0]); + + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-js-button")); + testFilterButtons(monitor, "js"); + testContents([0, 0, 1, 0, 0, 0, 0, 0, 0]); + + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-xhr-button")); + testFilterButtons(monitor, "xhr"); + testContents([1, 1, 1, 1, 1, 1, 1, 1, 0]); + + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-fonts-button")); + testFilterButtons(monitor, "fonts"); + testContents([0, 0, 0, 1, 0, 0, 0, 0, 0]); + + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-images-button")); + testFilterButtons(monitor, "images"); + testContents([0, 0, 0, 0, 1, 0, 0, 0, 0]); + + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-media-button")); + testFilterButtons(monitor, "media"); + testContents([0, 0, 0, 0, 0, 1, 1, 0, 0]); + + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-flash-button")); + testFilterButtons(monitor, "flash"); + testContents([0, 0, 0, 0, 0, 0, 0, 1, 0]); + + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-ws-button")); + testFilterButtons(monitor, "ws"); + testContents([0, 0, 0, 0, 0, 0, 0, 0, 1]); + + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + testFilterButtons(monitor, "all"); + testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]); + + // Text in filter box that matches nothing should hide all. + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + setFreetextFilter("foobar"); + testContents([0, 0, 0, 0, 0, 0, 0, 0, 0]); + + // Text in filter box that matches should filter out everything else. + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + setFreetextFilter("sample"); + testContents([1, 1, 1, 0, 0, 0, 0, 0, 0]); + + // Text in filter box that matches should filter out everything else. + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + setFreetextFilter("SAMPLE"); + testContents([1, 1, 1, 0, 0, 0, 0, 0, 0]); + + // Test negative filtering (only show unmatched items) + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + setFreetextFilter("-sample"); + testContents([0, 0, 0, 1, 1, 1, 1, 1, 1]); + + // ...then combine multiple filters together. + + // Enable filtering for html and css; should show request of both type. + setFreetextFilter(""); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-css-button")); + testFilterButtonsCustom(monitor, [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]); + testContents([1, 1, 0, 0, 0, 0, 0, 0, 0]); + + // Html and css filter enabled and text filter should show just the html and css match. + // Should not show both the items matching the button plus the items matching the text. + setFreetextFilter("sample"); + testContents([1, 1, 0, 0, 0, 0, 0, 0, 0]); + + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-flash-button")); + setFreetextFilter(""); + testFilterButtonsCustom(monitor, [0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0]); + testContents([1, 1, 0, 0, 0, 0, 0, 1, 0]); + + // Disable some filters. Only one left active. + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-css-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-flash-button")); + testFilterButtons(monitor, "html"); + testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]); + + // Disable last active filter. Should toggle to all. + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button")); + testFilterButtons(monitor, "all"); + testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]); + + // Enable few filters and click on all. Only "all" should be checked. + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-css-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-ws-button")); + testFilterButtonsCustom(monitor, [0, 1, 1, 0, 0, 0, 0, 0, 0, 1]); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + testFilterButtons(monitor, "all"); + testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]); + + yield teardown(monitor); + + function testContents(visibility) { + isnot(RequestsMenu.selectedItem, null, + "There should still be a selected item after filtering."); + is(RequestsMenu.selectedIndex, 0, + "The first item should be still selected after filtering."); + is(NetMonitorView.detailsPaneHidden, false, + "The details pane should still be visible after filtering."); + + is(RequestsMenu.items.length, visibility.length, + "There should be a specific amount of items in the requests menu."); + is(RequestsMenu.visibleItems.length, visibility.filter(e => e).length, + "There should be a specific amount of visbile items in the requests menu."); + + for (let i = 0; i < visibility.length; i++) { + is(RequestsMenu.getItemAtIndex(i).target.hidden, !visibility[i], + "The item at index " + i + " doesn't have the correct hidden state."); + } + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "GET", CONTENT_TYPE_SJS + "?fmt=html", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "html", + fullMimeType: "text/html; charset=utf-8" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1), + "GET", CONTENT_TYPE_SJS + "?fmt=css", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "css", + fullMimeType: "text/css; charset=utf-8" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(2), + "GET", CONTENT_TYPE_SJS + "?fmt=js", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "js", + fullMimeType: "application/javascript; charset=utf-8" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(3), + "GET", CONTENT_TYPE_SJS + "?fmt=font", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "woff", + fullMimeType: "font/woff" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4), + "GET", CONTENT_TYPE_SJS + "?fmt=image", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "png", + fullMimeType: "image/png" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(5), + "GET", CONTENT_TYPE_SJS + "?fmt=audio", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "ogg", + fullMimeType: "audio/ogg" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(6), + "GET", CONTENT_TYPE_SJS + "?fmt=video", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "webm", + fullMimeType: "video/webm" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(7), + "GET", CONTENT_TYPE_SJS + "?fmt=flash", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "x-shockwave-flash", + fullMimeType: "application/x-shockwave-flash" + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(8), + "GET", CONTENT_TYPE_SJS + "?fmt=ws", { + fuzzyUrl: true, + status: 101, + statusText: "Switching Protocols", + }); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_filter-02.js b/devtools/client/netmonitor/test/browser_net_filter-02.js new file mode 100644 index 000000000..70a051b6d --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_filter-02.js @@ -0,0 +1,200 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test if filtering items in the network table works correctly with new requests. + */ + +const BASIC_REQUESTS = [ + { url: "sjs_content-type-test-server.sjs?fmt=html&res=undefined" }, + { url: "sjs_content-type-test-server.sjs?fmt=css" }, + { url: "sjs_content-type-test-server.sjs?fmt=js" }, +]; + +const REQUESTS_WITH_MEDIA = BASIC_REQUESTS.concat([ + { url: "sjs_content-type-test-server.sjs?fmt=font" }, + { url: "sjs_content-type-test-server.sjs?fmt=image" }, + { url: "sjs_content-type-test-server.sjs?fmt=audio" }, + { url: "sjs_content-type-test-server.sjs?fmt=video" }, +]); + +const REQUESTS_WITH_MEDIA_AND_FLASH = REQUESTS_WITH_MEDIA.concat([ + { url: "sjs_content-type-test-server.sjs?fmt=flash" }, +]); + +const REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS = REQUESTS_WITH_MEDIA_AND_FLASH.concat([ + /* "Upgrade" is a reserved header and can not be set on XMLHttpRequest */ + { url: "sjs_content-type-test-server.sjs?fmt=ws" }, +]); + +add_task(function* () { + let { monitor } = yield initNetMonitor(FILTERING_URL); + info("Starting test... "); + + // It seems that this test may be slow on Ubuntu builds running on ec2. + requestLongerTimeout(2); + + let { $, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 9); + loadCommonFrameScript(); + yield performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS); + yield wait; + + EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle")); + + isnot(RequestsMenu.selectedItem, null, + "There should be a selected item in the requests menu."); + is(RequestsMenu.selectedIndex, 0, + "The first item should be selected in the requests menu."); + is(NetMonitorView.detailsPaneHidden, false, + "The details pane should not be hidden after toggle button was pressed."); + + testFilterButtons(monitor, "all"); + testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]); + + info("Testing html filtering."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button")); + testFilterButtons(monitor, "html"); + testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]); + + info("Performing more requests."); + wait = waitForNetworkEvents(monitor, 9); + yield performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS); + yield wait; + + info("Testing html filtering again."); + testFilterButtons(monitor, "html"); + testContents([1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]); + + info("Performing more requests."); + wait = waitForNetworkEvents(monitor, 9); + yield performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS); + yield wait; + + info("Testing html filtering again."); + testFilterButtons(monitor, "html"); + testContents([1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]); + + info("Resetting filters."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button")); + testFilterButtons(monitor, "all"); + testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); + + yield teardown(monitor); + + function testContents(visibility) { + isnot(RequestsMenu.selectedItem, null, + "There should still be a selected item after filtering."); + is(RequestsMenu.selectedIndex, 0, + "The first item should be still selected after filtering."); + is(NetMonitorView.detailsPaneHidden, false, + "The details pane should still be visible after filtering."); + + is(RequestsMenu.items.length, visibility.length, + "There should be a specific amount of items in the requests menu."); + is(RequestsMenu.visibleItems.length, visibility.filter(e => e).length, + "There should be a specific amount of visbile items in the requests menu."); + + for (let i = 0; i < visibility.length; i++) { + is(RequestsMenu.getItemAtIndex(i).target.hidden, !visibility[i], + "The item at index " + i + " doesn't have the correct hidden state."); + } + + for (let i = 0; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=html", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "html", + fullMimeType: "text/html; charset=utf-8" + }); + } + for (let i = 1; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=css", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "css", + fullMimeType: "text/css; charset=utf-8" + }); + } + for (let i = 2; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=js", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "js", + fullMimeType: "application/javascript; charset=utf-8" + }); + } + for (let i = 3; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=font", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "woff", + fullMimeType: "font/woff" + }); + } + for (let i = 4; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=image", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "png", + fullMimeType: "image/png" + }); + } + for (let i = 5; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=audio", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "ogg", + fullMimeType: "audio/ogg" + }); + } + for (let i = 6; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=video", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "webm", + fullMimeType: "video/webm" + }); + } + for (let i = 7; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=flash", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "x-shockwave-flash", + fullMimeType: "application/x-shockwave-flash" + }); + } + for (let i = 8; i < visibility.length; i += 9) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=ws", { + fuzzyUrl: true, + status: 101, + statusText: "Switching Protocols" + }); + } + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_filter-03.js b/devtools/client/netmonitor/test/browser_net_filter-03.js new file mode 100644 index 000000000..2babdaab3 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_filter-03.js @@ -0,0 +1,185 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test if filtering items in the network table works correctly with new requests + * and while sorting is enabled. + */ +const BASIC_REQUESTS = [ + { url: "sjs_content-type-test-server.sjs?fmt=html&res=undefined" }, + { url: "sjs_content-type-test-server.sjs?fmt=css" }, + { url: "sjs_content-type-test-server.sjs?fmt=js" }, +]; + +const REQUESTS_WITH_MEDIA = BASIC_REQUESTS.concat([ + { url: "sjs_content-type-test-server.sjs?fmt=font" }, + { url: "sjs_content-type-test-server.sjs?fmt=image" }, + { url: "sjs_content-type-test-server.sjs?fmt=audio" }, + { url: "sjs_content-type-test-server.sjs?fmt=video" }, +]); + +add_task(function* () { + let { monitor } = yield initNetMonitor(FILTERING_URL); + info("Starting test... "); + + // It seems that this test may be slow on Ubuntu builds running on ec2. + requestLongerTimeout(2); + + let { $, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + // The test assumes that the first HTML request here has a longer response + // body than the other HTML requests performed later during the test. + let requests = Cu.cloneInto(REQUESTS_WITH_MEDIA, {}); + let newres = "res=<p>" + new Array(10).join(Math.random(10)) + "</p>"; + requests[0].url = requests[0].url.replace("res=undefined", newres); + + loadCommonFrameScript(); + + let wait = waitForNetworkEvents(monitor, 7); + yield performRequestsInContent(requests); + yield wait; + + EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle")); + + isnot(RequestsMenu.selectedItem, null, + "There should be a selected item in the requests menu."); + is(RequestsMenu.selectedIndex, 0, + "The first item should be selected in the requests menu."); + is(NetMonitorView.detailsPaneHidden, false, + "The details pane should not be hidden after toggle button was pressed."); + + testFilterButtons(monitor, "all"); + testContents([0, 1, 2, 3, 4, 5, 6], 7, 0); + + info("Sorting by size, ascending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button")); + testFilterButtons(monitor, "all"); + testContents([6, 4, 5, 0, 1, 2, 3], 7, 6); + + info("Testing html filtering."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button")); + testFilterButtons(monitor, "html"); + testContents([6, 4, 5, 0, 1, 2, 3], 1, 6); + + info("Performing more requests."); + wait = waitForNetworkEvents(monitor, 7); + performRequestsInContent(REQUESTS_WITH_MEDIA); + yield wait; + + info("Testing html filtering again."); + resetSorting(); + testFilterButtons(monitor, "html"); + testContents([8, 13, 9, 11, 10, 12, 0, 4, 1, 5, 2, 6, 3, 7], 2, 13); + + info("Performing more requests."); + performRequestsInContent(REQUESTS_WITH_MEDIA); + yield waitForNetworkEvents(monitor, 7); + + info("Testing html filtering again."); + resetSorting(); + testFilterButtons(monitor, "html"); + testContents([12, 13, 20, 14, 16, 18, 15, 17, 19, 0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11], + 3, 20); + + yield teardown(monitor); + + function resetSorting() { + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-waterfall-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button")); + } + + function testContents(order, visible, selection) { + isnot(RequestsMenu.selectedItem, null, + "There should still be a selected item after filtering."); + is(RequestsMenu.selectedIndex, selection, + "The first item should be still selected after filtering."); + is(NetMonitorView.detailsPaneHidden, false, + "The details pane should still be visible after filtering."); + + is(RequestsMenu.items.length, order.length, + "There should be a specific amount of items in the requests menu."); + is(RequestsMenu.visibleItems.length, visible, + "There should be a specific amount of visbile items in the requests menu."); + + for (let i = 0; i < order.length; i++) { + is(RequestsMenu.getItemAtIndex(i), RequestsMenu.items[i], + "The requests menu items aren't ordered correctly. Misplaced item " + i + "."); + } + + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i]), + "GET", CONTENT_TYPE_SJS + "?fmt=html", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "html", + fullMimeType: "text/html; charset=utf-8" + }); + } + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len]), + "GET", CONTENT_TYPE_SJS + "?fmt=css", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "css", + fullMimeType: "text/css; charset=utf-8" + }); + } + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 2]), + "GET", CONTENT_TYPE_SJS + "?fmt=js", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "js", + fullMimeType: "application/javascript; charset=utf-8" + }); + } + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 3]), + "GET", CONTENT_TYPE_SJS + "?fmt=font", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "woff", + fullMimeType: "font/woff" + }); + } + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 4]), + "GET", CONTENT_TYPE_SJS + "?fmt=image", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "png", + fullMimeType: "image/png" + }); + } + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 5]), + "GET", CONTENT_TYPE_SJS + "?fmt=audio", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "ogg", + fullMimeType: "audio/ogg" + }); + } + for (let i = 0, len = order.length / 7; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 6]), + "GET", CONTENT_TYPE_SJS + "?fmt=video", { + fuzzyUrl: true, + status: 200, + statusText: "OK", + type: "webm", + fullMimeType: "video/webm" + }); + } + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_filter-04.js b/devtools/client/netmonitor/test/browser_net_filter-04.js new file mode 100644 index 000000000..e617dbaa9 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_filter-04.js @@ -0,0 +1,63 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if invalid filter types are sanitized when loaded from the preferences. + */ + +const BASIC_REQUESTS = [ + { url: "sjs_content-type-test-server.sjs?fmt=html&res=undefined" }, + { url: "sjs_content-type-test-server.sjs?fmt=css" }, + { url: "sjs_content-type-test-server.sjs?fmt=js" }, +]; + +const REQUESTS_WITH_MEDIA = BASIC_REQUESTS.concat([ + { url: "sjs_content-type-test-server.sjs?fmt=font" }, + { url: "sjs_content-type-test-server.sjs?fmt=image" }, + { url: "sjs_content-type-test-server.sjs?fmt=audio" }, + { url: "sjs_content-type-test-server.sjs?fmt=video" }, +]); + +const REQUESTS_WITH_MEDIA_AND_FLASH = REQUESTS_WITH_MEDIA.concat([ + { url: "sjs_content-type-test-server.sjs?fmt=flash" }, +]); + +const REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS = REQUESTS_WITH_MEDIA_AND_FLASH.concat([ + /* "Upgrade" is a reserved header and can not be set on XMLHttpRequest */ + { url: "sjs_content-type-test-server.sjs?fmt=ws" }, +]); + +add_task(function* () { + Services.prefs.setCharPref("devtools.netmonitor.filters", '["js", "bogus"]'); + + let { monitor } = yield initNetMonitor(FILTERING_URL); + info("Starting test... "); + + let { Prefs, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + is(Prefs.filters.length, 2, + "All filter types were loaded as an array from the preferences."); + is(Prefs.filters[0], "js", + "The first filter type is correct."); + is(Prefs.filters[1], "bogus", + "The second filter type is invalid, but loaded anyway."); + + let wait = waitForNetworkEvents(monitor, 9); + loadCommonFrameScript(); + yield performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS); + yield wait; + + testFilterButtons(monitor, "js"); + ok(true, "Only the correct filter type was taken into consideration."); + + yield teardown(monitor); + + let filters = Services.prefs.getCharPref("devtools.netmonitor.filters"); + is(filters, '["js"]', + "The bogus filter type was ignored and removed from the preferences."); +}); diff --git a/devtools/client/netmonitor/test/browser_net_footer-summary.js b/devtools/client/netmonitor/test/browser_net_footer-summary.js new file mode 100644 index 000000000..e484b2097 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_footer-summary.js @@ -0,0 +1,75 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test if the summary text displayed in the network requests menu footer + * is correct. + */ + +add_task(function* () { + requestLongerTimeout(2); + let { L10N } = require("devtools/client/netmonitor/l10n"); + let { PluralForm } = require("devtools/shared/plural-form"); + + let { tab, monitor } = yield initNetMonitor(FILTERING_URL); + info("Starting test... "); + + let { $, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + testStatus(); + + for (let i = 0; i < 2; i++) { + info(`Performing requests in batch #${i}`); + let wait = waitForNetworkEvents(monitor, 8); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests('{ "getMedia": true, "getFlash": true }'); + }); + yield wait; + + testStatus(); + + let buttons = ["html", "css", "js", "xhr", "fonts", "images", "media", "flash"]; + for (let button of buttons) { + let buttonEl = $(`#requests-menu-filter-${button}-button`); + EventUtils.sendMouseEvent({ type: "click" }, buttonEl); + testStatus(); + } + } + + yield teardown(monitor); + + function testStatus() { + let summary = $("#requests-menu-network-summary-button"); + let value = summary.getAttribute("label"); + info("Current summary: " + value); + + let visibleItems = RequestsMenu.visibleItems; + let visibleRequestsCount = visibleItems.length; + let totalRequestsCount = RequestsMenu.itemCount; + info("Current requests: " + visibleRequestsCount + " of " + totalRequestsCount + "."); + + if (!totalRequestsCount || !visibleRequestsCount) { + is(value, L10N.getStr("networkMenu.empty"), + "The current summary text is incorrect, expected an 'empty' label."); + return; + } + + let totalBytes = RequestsMenu._getTotalBytesOfRequests(visibleItems); + let totalMillis = + RequestsMenu._getNewestRequest(visibleItems).attachment.endedMillis - + RequestsMenu._getOldestRequest(visibleItems).attachment.startedMillis; + + info("Computed total bytes: " + totalBytes); + info("Computed total millis: " + totalMillis); + + is(value, PluralForm.get(visibleRequestsCount, L10N.getStr("networkMenu.summary")) + .replace("#1", visibleRequestsCount) + .replace("#2", L10N.numberWithDecimals((totalBytes || 0) / 1024, 2)) + .replace("#3", L10N.numberWithDecimals((totalMillis || 0) / 1000, 2)) + , "The current summary text is incorrect."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_frame.js b/devtools/client/netmonitor/test/browser_net_frame.js new file mode 100644 index 000000000..eeded652b --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_frame.js @@ -0,0 +1,221 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests for all expected requests when an iframe is loading a subdocument. + */ + +const TOP_FILE_NAME = "html_frame-test-page.html"; +const SUB_FILE_NAME = "html_frame-subdocument.html"; +const TOP_URL = EXAMPLE_URL + TOP_FILE_NAME; +const SUB_URL = EXAMPLE_URL + SUB_FILE_NAME; + +const EXPECTED_REQUESTS_TOP = [ + { + method: "GET", + url: TOP_URL, + causeType: "document", + causeUri: "", + stack: true + }, + { + method: "GET", + url: EXAMPLE_URL + "stylesheet_request", + causeType: "stylesheet", + causeUri: TOP_URL, + stack: false + }, + { + method: "GET", + url: EXAMPLE_URL + "img_request", + causeType: "img", + causeUri: TOP_URL, + stack: false + }, + { + method: "GET", + url: EXAMPLE_URL + "xhr_request", + causeType: "xhr", + causeUri: TOP_URL, + stack: [{ fn: "performXhrRequest", file: TOP_FILE_NAME, line: 23 }] + }, + { + method: "GET", + url: EXAMPLE_URL + "fetch_request", + causeType: "fetch", + causeUri: TOP_URL, + stack: [{ fn: "performFetchRequest", file: TOP_FILE_NAME, line: 27 }] + }, + { + method: "GET", + url: EXAMPLE_URL + "promise_fetch_request", + causeType: "fetch", + causeUri: TOP_URL, + stack: [ + { fn: "performPromiseFetchRequest", file: TOP_FILE_NAME, line: 39 }, + { fn: null, file: TOP_FILE_NAME, line: 38, asyncCause: "promise callback" }, + ] + }, + { + method: "GET", + url: EXAMPLE_URL + "timeout_fetch_request", + causeType: "fetch", + causeUri: TOP_URL, + stack: [ + { fn: "performTimeoutFetchRequest", file: TOP_FILE_NAME, line: 41 }, + { fn: "performPromiseFetchRequest", file: TOP_FILE_NAME, line: 40, + asyncCause: "setTimeout handler" }, + ] + }, + { + method: "POST", + url: EXAMPLE_URL + "beacon_request", + causeType: "beacon", + causeUri: TOP_URL, + stack: [{ fn: "performBeaconRequest", file: TOP_FILE_NAME, line: 31 }] + }, +]; + +const EXPECTED_REQUESTS_SUB = [ + { + method: "GET", + url: SUB_URL, + causeType: "subdocument", + causeUri: TOP_URL, + stack: false + }, + { + method: "GET", + url: EXAMPLE_URL + "stylesheet_request", + causeType: "stylesheet", + causeUri: SUB_URL, + stack: false + }, + { + method: "GET", + url: EXAMPLE_URL + "img_request", + causeType: "img", + causeUri: SUB_URL, + stack: false + }, + { + method: "GET", + url: EXAMPLE_URL + "xhr_request", + causeType: "xhr", + causeUri: SUB_URL, + stack: [{ fn: "performXhrRequest", file: SUB_FILE_NAME, line: 22 }] + }, + { + method: "GET", + url: EXAMPLE_URL + "fetch_request", + causeType: "fetch", + causeUri: SUB_URL, + stack: [{ fn: "performFetchRequest", file: SUB_FILE_NAME, line: 26 }] + }, + { + method: "GET", + url: EXAMPLE_URL + "promise_fetch_request", + causeType: "fetch", + causeUri: SUB_URL, + stack: [ + { fn: "performPromiseFetchRequest", file: SUB_FILE_NAME, line: 38 }, + { fn: null, file: SUB_FILE_NAME, line: 37, asyncCause: "promise callback" }, + ] + }, + { + method: "GET", + url: EXAMPLE_URL + "timeout_fetch_request", + causeType: "fetch", + causeUri: SUB_URL, + stack: [ + { fn: "performTimeoutFetchRequest", file: SUB_FILE_NAME, line: 40 }, + { fn: "performPromiseFetchRequest", file: SUB_FILE_NAME, line: 39, + asyncCause: "setTimeout handler" }, + ] + }, + { + method: "POST", + url: EXAMPLE_URL + "beacon_request", + causeType: "beacon", + causeUri: SUB_URL, + stack: [{ fn: "performBeaconRequest", file: SUB_FILE_NAME, line: 30 }] + }, +]; + +const REQUEST_COUNT = EXPECTED_REQUESTS_TOP.length + EXPECTED_REQUESTS_SUB.length; + +add_task(function* () { + // Async stacks aren't on by default in all builds + yield SpecialPowers.pushPrefEnv({ set: [["javascript.options.asyncstack", true]] }); + + // the initNetMonitor function clears the network request list after the + // page is loaded. That's why we first load a bogus page from SIMPLE_URL, + // and only then load the real thing from TOP_URL - we want to catch + // all the requests the page is making, not only the XHRs. + // We can't use about:blank here, because initNetMonitor checks that the + // page has actually made at least one request. + let { tab, monitor } = yield initNetMonitor(SIMPLE_URL); + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + RequestsMenu.lazyUpdate = false; + + tab.linkedBrowser.loadURI(TOP_URL, null, null); + + yield waitForNetworkEvents(monitor, REQUEST_COUNT); + + is(RequestsMenu.itemCount, REQUEST_COUNT, + "All the page events should be recorded."); + + // While there is a defined order for requests in each document separately, the requests + // from different documents may interleave in various ways that change per test run, so + // there is not a single order when considering all the requests together. + let currentTop = 0; + let currentSub = 0; + for (let i = 0; i < REQUEST_COUNT; i++) { + let requestItem = RequestsMenu.getItemAtIndex(i); + + let itemUrl = requestItem.attachment.url; + let itemCauseUri = requestItem.target.querySelector(".requests-menu-cause-label") + .getAttribute("tooltiptext"); + let spec; + if (itemUrl == SUB_URL || itemCauseUri == SUB_URL) { + spec = EXPECTED_REQUESTS_SUB[currentSub++]; + } else { + spec = EXPECTED_REQUESTS_TOP[currentTop++]; + } + let { method, url, causeType, causeUri, stack } = spec; + + verifyRequestItemTarget(requestItem, + method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } } + ); + + let { stacktrace } = requestItem.attachment.cause; + let stackLen = stacktrace ? stacktrace.length : 0; + + if (stack) { + ok(stacktrace, `Request #${i} has a stacktrace`); + ok(stackLen > 0, + `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`); + + // if "stack" is array, check the details about the top stack frames + if (Array.isArray(stack)) { + stack.forEach((frame, j) => { + is(stacktrace[j].functionName, frame.fn, + `Request #${i} has the correct function on JS stack frame #${j}`); + is(stacktrace[j].filename.split("/").pop(), frame.file, + `Request #${i} has the correct file on JS stack frame #${j}`); + is(stacktrace[j].lineNumber, frame.line, + `Request #${i} has the correct line number on JS stack frame #${j}`); + is(stacktrace[j].asyncCause, frame.asyncCause, + `Request #${i} has the correct async cause on JS stack frame #${j}`); + }); + } + } else { + is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`); + } + } + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_html-preview.js b/devtools/client/netmonitor/test/browser_net_html-preview.js new file mode 100644 index 000000000..351009de5 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_html-preview.js @@ -0,0 +1,62 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if html responses show and properly populate a "Preview" tab. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_URL); + info("Starting test... "); + + let { $, document, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 6); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + + is($("#event-details-pane").selectedIndex, 0, + "The first tab in the details pane should be selected."); + is($("#preview-tab").hidden, true, + "The preview tab should be hidden for non html responses."); + is($("#preview-tabpanel").hidden, false, + "The preview tabpanel is not hidden for non html responses."); + + RequestsMenu.selectedIndex = 4; + NetMonitorView.toggleDetailsPane({ visible: true, animated: false }, 6); + + is($("#event-details-pane").selectedIndex, 6, + "The sixth tab in the details pane should be selected."); + is($("#preview-tab").hidden, false, + "The preview tab should be visible now."); + + yield monitor.panelWin.once(EVENTS.RESPONSE_HTML_PREVIEW_DISPLAYED); + let iframe = $("#response-preview"); + ok(iframe, + "There should be a response preview iframe available."); + ok(iframe.contentDocument, + "The iframe's content document should be available."); + is(iframe.contentDocument.querySelector("blink").textContent, "Not Found", + "The iframe's content document should be loaded and correct."); + + RequestsMenu.selectedIndex = 5; + + is($("#event-details-pane").selectedIndex, 0, + "The first tab in the details pane should be selected again."); + is($("#preview-tab").hidden, true, + "The preview tab should be hidden again for non html responses."); + is($("#preview-tabpanel").hidden, false, + "The preview tabpanel is not hidden again for non html responses."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_icon-preview.js b/devtools/client/netmonitor/test/browser_net_icon-preview.js new file mode 100644 index 000000000..e3c5bde4e --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_icon-preview.js @@ -0,0 +1,71 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if image responses show a thumbnail in the requests menu. + */ + +add_task(function* () { + let Actions = require("devtools/client/netmonitor/actions/index"); + + let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL); + info("Starting test... "); + + let { $, $all, EVENTS, ACTIVITY_TYPE, NetMonitorView, NetMonitorController, + gStore } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + let wait = waitForEvents(); + yield performRequests(); + yield wait; + + info("Checking the image thumbnail when all items are shown."); + checkImageThumbnail(); + + RequestsMenu.sortBy("size"); + info("Checking the image thumbnail when all items are sorted."); + checkImageThumbnail(); + + gStore.dispatch(Actions.toggleFilterType("images")); + info("Checking the image thumbnail when only images are shown."); + checkImageThumbnail(); + + info("Reloading the debuggee and performing all requests again..."); + wait = waitForEvents(); + yield reloadAndPerformRequests(); + yield wait; + + info("Checking the image thumbnail after a reload."); + checkImageThumbnail(); + + yield teardown(monitor); + + function waitForEvents() { + return promise.all([ + waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS), + monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED) + ]); + } + + function performRequests() { + return ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + } + + function* reloadAndPerformRequests() { + yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED); + yield performRequests(); + } + + function checkImageThumbnail() { + is($all(".requests-menu-icon[type=thumbnail]").length, 1, + "There should be only one image request with a thumbnail displayed."); + is($(".requests-menu-icon[type=thumbnail]").src, TEST_IMAGE_DATA_URI, + "The image requests-menu-icon thumbnail is displayed correctly."); + is($(".requests-menu-icon[type=thumbnail]").hidden, false, + "The image requests-menu-icon thumbnail should not be hidden."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_image-tooltip.js b/devtools/client/netmonitor/test/browser_net_image-tooltip.js new file mode 100644 index 000000000..04cd26959 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_image-tooltip.js @@ -0,0 +1,101 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const IMAGE_TOOLTIP_URL = EXAMPLE_URL + "html_image-tooltip-test-page.html"; +const IMAGE_TOOLTIP_REQUESTS = 1; + +/** + * Tests if image responses show a popup in the requests menu when hovered. + */ +add_task(function* test() { + let { tab, monitor } = yield initNetMonitor(IMAGE_TOOLTIP_URL); + info("Starting test... "); + + let { $, EVENTS, ACTIVITY_TYPE, NetMonitorView, NetMonitorController } = + monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + RequestsMenu.lazyUpdate = true; + + let onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS); + let onThumbnail = monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED); + + yield performRequests(); + yield onEvents; + yield onThumbnail; + + info("Checking the image thumbnail after a few requests were made..."); + yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[0]); + + // Hide tooltip before next test, to avoid the situation that tooltip covers + // the icon for the request of the next test. + info("Checking the image thumbnail gets hidden..."); + yield hideTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[0]); + + // +1 extra document reload + onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS + 1); + onThumbnail = monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED); + + info("Reloading the debuggee and performing all requests again..."); + yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED); + yield performRequests(); + yield onEvents; + yield onThumbnail; + + info("Checking the image thumbnail after a reload."); + yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[1]); + + info("Checking if the image thumbnail is hidden when mouse leaves the menu widget"); + let requestsMenuEl = $("#requests-menu-contents"); + let onHidden = RequestsMenu.tooltip.once("hidden"); + EventUtils.synthesizeMouse(requestsMenuEl, 0, 0, {type: "mouseout"}, monitor.panelWin); + yield onHidden; + + yield teardown(monitor); + + function performRequests() { + return ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + } + + /** + * Show a tooltip on the {requestItem} and verify that it was displayed + * with the expected content. + */ + function* showTooltipAndVerify(tooltip, requestItem) { + let anchor = $(".requests-menu-file", requestItem.target); + yield showTooltipOn(tooltip, anchor); + + info("Tooltip was successfully opened for the image request."); + is(tooltip.panel.querySelector("img").src, TEST_IMAGE_DATA_URI, + "The tooltip's image content is displayed correctly."); + } + + /** + * Trigger a tooltip over an element by sending mousemove event. + * @return a promise that resolves when the tooltip is shown + */ + function showTooltipOn(tooltip, element) { + let onShown = tooltip.once("shown"); + let win = element.ownerDocument.defaultView; + EventUtils.synthesizeMouseAtCenter(element, {type: "mousemove"}, win); + return onShown; + } + + /** + * Hide a tooltip on the {requestItem} and verify that it was closed. + */ + function* hideTooltipAndVerify(tooltip, requestItem) { + // Hovering method hides tooltip. + let anchor = $(".requests-menu-method", requestItem.target); + + let onHidden = tooltip.once("hidden"); + let win = anchor.ownerDocument.defaultView; + EventUtils.synthesizeMouseAtCenter(anchor, {type: "mousemove"}, win); + yield onHidden; + + info("Tooltip was successfully closed."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_json-long.js b/devtools/client/netmonitor/test/browser_net_json-long.js new file mode 100644 index 000000000..2347d26c4 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_json-long.js @@ -0,0 +1,98 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if very long JSON responses are handled correctly. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(JSON_LONG_URL); + info("Starting test... "); + + // This is receiving over 80 KB of json and will populate over 6000 items + // in a variables view instance. Debug builds are slow. + requestLongerTimeout(4); + + let { document, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "GET", CONTENT_TYPE_SJS + "?fmt=json-long", { + status: 200, + statusText: "OK", + type: "json", + fullMimeType: "text/json; charset=utf-8", + size: L10N.getFormatStr("networkMenu.sizeKB", + L10N.numberWithDecimals(85975 / 1024, 2)), + time: true + }); + + let onEvent = monitor.panelWin.once(EVENTS.RESPONSE_BODY_DISPLAYED); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[3]); + yield onEvent; + + testResponseTab(); + + yield teardown(monitor); + + function testResponseTab() { + let tabEl = document.querySelectorAll("#details-pane tab")[3]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3]; + + is(tabEl.getAttribute("selected"), "true", + "The response tab in the network details pane should be selected."); + + is(tabpanel.querySelector("#response-content-info-header") + .hasAttribute("hidden"), true, + "The response info header doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-json-box") + .hasAttribute("hidden"), false, + "The response content json box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-textarea-box") + .hasAttribute("hidden"), true, + "The response content textarea box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-image-box") + .hasAttribute("hidden"), true, + "The response content image box doesn't have the intended visibility."); + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 1, + "There should be 1 json scope displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-property").length, 6143, + "There should be 6143 json properties displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0, + "The empty notice should not be displayed in this tabpanel."); + + let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + let names = ".variables-view-property > .title > .name"; + let values = ".variables-view-property > .title > .value"; + + is(jsonScope.querySelector(".name").getAttribute("value"), + L10N.getStr("jsonScopeName"), + "The json scope doesn't have the correct title."); + + is(jsonScope.querySelectorAll(names)[0].getAttribute("value"), + "0", "The first json property name was incorrect."); + is(jsonScope.querySelectorAll(values)[0].getAttribute("value"), + "Object", "The first json property value was incorrect."); + + is(jsonScope.querySelectorAll(names)[1].getAttribute("value"), + "greeting", "The second json property name was incorrect."); + is(jsonScope.querySelectorAll(values)[1].getAttribute("value"), + "\"Hello long string JSON!\"", "The second json property value was incorrect."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_json-malformed.js b/devtools/client/netmonitor/test/browser_net_json-malformed.js new file mode 100644 index 000000000..6bed60480 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_json-malformed.js @@ -0,0 +1,77 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if malformed JSON responses are handled correctly. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(JSON_MALFORMED_URL); + info("Starting test... "); + + let { document, EVENTS, Editor, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "GET", CONTENT_TYPE_SJS + "?fmt=json-malformed", { + status: 200, + statusText: "OK", + type: "json", + fullMimeType: "text/json; charset=utf-8" + }); + + let onEvent = monitor.panelWin.once(EVENTS.RESPONSE_BODY_DISPLAYED); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[3]); + yield onEvent; + + let tabEl = document.querySelectorAll("#details-pane tab")[3]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3]; + + is(tabEl.getAttribute("selected"), "true", + "The response tab in the network details pane should be selected."); + + is(tabpanel.querySelector("#response-content-info-header") + .hasAttribute("hidden"), false, + "The response info header doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-info-header") + .getAttribute("value"), + "SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data" + + " at line 1 column 40 of the JSON data", + "The response info header doesn't have the intended value attribute."); + is(tabpanel.querySelector("#response-content-info-header") + .getAttribute("tooltiptext"), + "SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data" + + " at line 1 column 40 of the JSON data", + "The response info header doesn't have the intended tooltiptext attribute."); + + is(tabpanel.querySelector("#response-content-json-box") + .hasAttribute("hidden"), true, + "The response content json box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-textarea-box") + .hasAttribute("hidden"), false, + "The response content textarea box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-image-box") + .hasAttribute("hidden"), true, + "The response content image box doesn't have the intended visibility."); + + let editor = yield NetMonitorView.editor("#response-content-textarea"); + is(editor.getText(), "{ \"greeting\": \"Hello malformed JSON!\" },", + "The text shown in the source editor is incorrect."); + is(editor.getMode(), Editor.modes.js, + "The mode active in the source editor is incorrect."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_json_custom_mime.js b/devtools/client/netmonitor/test/browser_net_json_custom_mime.js new file mode 100644 index 000000000..210ffbbe8 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_json_custom_mime.js @@ -0,0 +1,90 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if JSON responses with unusal/custom MIME types are handled correctly. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(JSON_CUSTOM_MIME_URL); + info("Starting test... "); + + let { document, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "GET", CONTENT_TYPE_SJS + "?fmt=json-custom-mime", { + status: 200, + statusText: "OK", + type: "x-bigcorp-json", + fullMimeType: "text/x-bigcorp-json; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41), + time: true + }); + + let onEvent = monitor.panelWin.once(EVENTS.RESPONSE_BODY_DISPLAYED); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[3]); + yield onEvent; + + testResponseTab(); + + yield teardown(monitor); + + function testResponseTab() { + let tabEl = document.querySelectorAll("#details-pane tab")[3]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3]; + + is(tabEl.getAttribute("selected"), "true", + "The response tab in the network details pane should be selected."); + + is(tabpanel.querySelector("#response-content-info-header") + .hasAttribute("hidden"), true, + "The response info header doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-json-box") + .hasAttribute("hidden"), false, + "The response content json box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-textarea-box") + .hasAttribute("hidden"), true, + "The response content textarea box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-image-box") + .hasAttribute("hidden"), true, + "The response content image box doesn't have the intended visibility."); + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 1, + "There should be 1 json scope displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-property").length, 2, + "There should be 2 json properties displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0, + "The empty notice should not be displayed in this tabpanel."); + + let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + is(jsonScope.querySelectorAll(".variables-view-property .name")[0] + .getAttribute("value"), + "greeting", "The first json property name was incorrect."); + is(jsonScope.querySelectorAll(".variables-view-property .value")[0] + .getAttribute("value"), + "\"Hello oddly-named JSON!\"", "The first json property value was incorrect."); + + is(jsonScope.querySelectorAll(".variables-view-property .name")[1] + .getAttribute("value"), + "__proto__", "The second json property name was incorrect."); + is(jsonScope.querySelectorAll(".variables-view-property .value")[1] + .getAttribute("value"), + "Object", "The second json property value was incorrect."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_json_text_mime.js b/devtools/client/netmonitor/test/browser_net_json_text_mime.js new file mode 100644 index 000000000..edc98a5c9 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_json_text_mime.js @@ -0,0 +1,90 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if JSON responses with unusal/custom MIME types are handled correctly. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(JSON_TEXT_MIME_URL); + info("Starting test... "); + + let { document, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "GET", CONTENT_TYPE_SJS + "?fmt=json-text-mime", { + status: 200, + statusText: "OK", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41), + time: true + }); + + let onEvent = monitor.panelWin.once(EVENTS.RESPONSE_BODY_DISPLAYED); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[3]); + yield onEvent; + + testResponseTab(); + + yield teardown(monitor); + + function testResponseTab() { + let tabEl = document.querySelectorAll("#details-pane tab")[3]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3]; + + is(tabEl.getAttribute("selected"), "true", + "The response tab in the network details pane should be selected."); + + is(tabpanel.querySelector("#response-content-info-header") + .hasAttribute("hidden"), true, + "The response info header doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-json-box") + .hasAttribute("hidden"), false, + "The response content json box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-textarea-box") + .hasAttribute("hidden"), true, + "The response content textarea box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-image-box") + .hasAttribute("hidden"), true, + "The response content image box doesn't have the intended visibility."); + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 1, + "There should be 1 json scope displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-property").length, 2, + "There should be 2 json properties displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0, + "The empty notice should not be displayed in this tabpanel."); + + let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + is(jsonScope.querySelectorAll(".variables-view-property .name")[0] + .getAttribute("value"), + "greeting", "The first json property name was incorrect."); + is(jsonScope.querySelectorAll(".variables-view-property .value")[0] + .getAttribute("value"), + "\"Hello third-party JSON!\"", "The first json property value was incorrect."); + + is(jsonScope.querySelectorAll(".variables-view-property .name")[1] + .getAttribute("value"), + "__proto__", "The second json property name was incorrect."); + is(jsonScope.querySelectorAll(".variables-view-property .value")[1] + .getAttribute("value"), + "Object", "The second json property value was incorrect."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_jsonp.js b/devtools/client/netmonitor/test/browser_net_jsonp.js new file mode 100644 index 000000000..3007d8c4d --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_jsonp.js @@ -0,0 +1,111 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if JSONP responses are handled correctly. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(JSONP_URL); + info("Starting test... "); + + let { document, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + NetworkDetails._json.lazyEmpty = false; + + let wait = waitForNetworkEvents(monitor, 2); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "GET", CONTENT_TYPE_SJS + "?fmt=jsonp&jsonp=$_0123Fun", { + status: 200, + statusText: "OK", + type: "json", + fullMimeType: "text/json; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1), + "GET", CONTENT_TYPE_SJS + "?fmt=jsonp2&jsonp=$_4567Sad", { + status: 200, + statusText: "OK", + type: "json", + fullMimeType: "text/json; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 54), + time: true + }); + + let onEvent = monitor.panelWin.once(EVENTS.RESPONSE_BODY_DISPLAYED); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[3]); + yield onEvent; + + testResponseTab("$_0123Fun", "\"Hello JSONP!\""); + + onEvent = monitor.panelWin.once(EVENTS.RESPONSE_BODY_DISPLAYED); + RequestsMenu.selectedIndex = 1; + yield onEvent; + + testResponseTab("$_4567Sad", "\"Hello weird JSONP!\""); + + yield teardown(monitor); + + function testResponseTab(func, greeting) { + let tabEl = document.querySelectorAll("#details-pane tab")[3]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3]; + + is(tabEl.getAttribute("selected"), "true", + "The response tab in the network details pane should be selected."); + + is(tabpanel.querySelector("#response-content-info-header") + .hasAttribute("hidden"), true, + "The response info header doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-json-box") + .hasAttribute("hidden"), false, + "The response content json box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-textarea-box") + .hasAttribute("hidden"), true, + "The response content textarea box doesn't have the intended visibility."); + is(tabpanel.querySelector("#response-content-image-box") + .hasAttribute("hidden"), true, + "The response content image box doesn't have the intended visibility."); + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 1, + "There should be 1 json scope displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-property").length, 2, + "There should be 2 json properties displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0, + "The empty notice should not be displayed in this tabpanel."); + + let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + + is(jsonScope.querySelector(".name").getAttribute("value"), + L10N.getFormatStr("jsonpScopeName", func), + "The json scope doesn't have the correct title."); + + is(jsonScope.querySelectorAll(".variables-view-property .name")[0] + .getAttribute("value"), + "greeting", "The first json property name was incorrect."); + is(jsonScope.querySelectorAll(".variables-view-property .value")[0] + .getAttribute("value"), + greeting, "The first json property value was incorrect."); + + is(jsonScope.querySelectorAll(".variables-view-property .name")[1] + .getAttribute("value"), + "__proto__", "The second json property name was incorrect."); + is(jsonScope.querySelectorAll(".variables-view-property .value")[1] + .getAttribute("value"), + "Object", "The second json property value was incorrect."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_large-response.js b/devtools/client/netmonitor/test/browser_net_large-response.js new file mode 100644 index 000000000..98d67b46d --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_large-response.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if very large response contents are just displayed as plain text. + */ + +const HTML_LONG_URL = CONTENT_TYPE_SJS + "?fmt=html-long"; + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + info("Starting test... "); + + // This test could potentially be slow because over 100 KB of stuff + // is going to be requested and displayed in the source editor. + requestLongerTimeout(2); + + let { document, EVENTS, Editor, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, HTML_LONG_URL, function* (url) { + content.wrappedJSObject.performRequests(1, url); + }); + yield wait; + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "GET", CONTENT_TYPE_SJS + "?fmt=html-long", { + status: 200, + statusText: "OK" + }); + + let onEvent = monitor.panelWin.once(EVENTS.RESPONSE_BODY_DISPLAYED); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[3]); + yield onEvent; + + let editor = yield NetMonitorView.editor("#response-content-textarea"); + ok(editor.getText().match(/^<p>/), + "The text shown in the source editor is incorrect."); + is(editor.getMode(), Editor.modes.text, + "The mode active in the source editor is incorrect."); + + yield teardown(monitor); + + // This test uses a lot of memory, so force a GC to help fragmentation. + info("Forcing GC after netmonitor test."); + Cu.forceGC(); +}); diff --git a/devtools/client/netmonitor/test/browser_net_leak_on_tab_close.js b/devtools/client/netmonitor/test/browser_net_leak_on_tab_close.js new file mode 100644 index 000000000..9e788f36c --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_leak_on_tab_close.js @@ -0,0 +1,17 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests that netmonitor doesn't leak windows on parent-side pages (bug 1285638) + */ + +add_task(function* () { + // Tell initNetMonitor to enable cache. Otherwise it will assert that there were more + // than zero network requests during the page load. But when loading about:config, + // there are none. + let { monitor } = yield initNetMonitor("about:config", null, true); + ok(monitor, "The network monitor was opened"); + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js b/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js new file mode 100644 index 000000000..8e7ffcc84 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if Open in new tab works. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + info("Starting test..."); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(1); + }); + yield wait; + + let requestItem = RequestsMenu.getItemAtIndex(0); + RequestsMenu.selectedItem = requestItem; + + let onTabOpen = once(gBrowser.tabContainer, "TabOpen", false); + RequestsMenu.contextMenu.openRequestInTab(); + yield onTabOpen; + + ok(true, "A new tab has been opened"); + + yield teardown(monitor); + + gBrowser.removeCurrentTab(); +}); diff --git a/devtools/client/netmonitor/test/browser_net_page-nav.js b/devtools/client/netmonitor/test/browser_net_page-nav.js new file mode 100644 index 000000000..6ac18297c --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_page-nav.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if page navigation ("close", "navigate", etc.) triggers an appropriate + * action in the network monitor. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { EVENTS } = monitor.panelWin; + + yield testNavigate(); + yield testNavigateBack(); + yield testClose(); + + function* testNavigate() { + info("Navigating forward..."); + + let onWillNav = monitor.panelWin.once(EVENTS.TARGET_WILL_NAVIGATE); + let onDidNav = monitor.panelWin.once(EVENTS.TARGET_DID_NAVIGATE); + + tab.linkedBrowser.loadURI(NAVIGATE_URL); + yield onWillNav; + + is(tab.linkedBrowser.currentURI.spec, SIMPLE_URL, + "Target started navigating to the correct location."); + + yield onDidNav; + is(tab.linkedBrowser.currentURI.spec, NAVIGATE_URL, + "Target finished navigating to the correct location."); + } + + function* testNavigateBack() { + info("Navigating backward..."); + + let onWillNav = monitor.panelWin.once(EVENTS.TARGET_WILL_NAVIGATE); + let onDidNav = monitor.panelWin.once(EVENTS.TARGET_DID_NAVIGATE); + + tab.linkedBrowser.loadURI(SIMPLE_URL); + yield onWillNav; + + is(tab.linkedBrowser.currentURI.spec, NAVIGATE_URL, + "Target started navigating back to the previous location."); + + yield onDidNav; + is(tab.linkedBrowser.currentURI.spec, SIMPLE_URL, + "Target finished navigating back to the previous location."); + } + + function* testClose() { + info("Closing..."); + + let onDestroyed = monitor.once("destroyed"); + removeTab(tab); + yield onDestroyed; + + ok(!monitor._controller.client, + "There shouldn't be a client available after destruction."); + ok(!monitor._controller.tabClient, + "There shouldn't be a tabClient available after destruction."); + ok(!monitor._controller.webConsoleClient, + "There shouldn't be a webConsoleClient available after destruction."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_pane-collapse.js b/devtools/client/netmonitor/test/browser_net_pane-collapse.js new file mode 100644 index 000000000..2760ec000 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_pane-collapse.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the network monitor panes collapse properly. + */ + +add_task(function* () { + let { monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { document, Prefs, NetMonitorView } = monitor.panelWin; + let detailsPane = document.getElementById("details-pane"); + let detailsPaneToggleButton = document.getElementById("details-pane-toggle"); + + ok(detailsPane.classList.contains("pane-collapsed") && + detailsPaneToggleButton.classList.contains("pane-collapsed"), + "The details pane should initially be hidden."); + + NetMonitorView.toggleDetailsPane({ visible: true, animated: false }); + + let width = ~~(detailsPane.getAttribute("width")); + is(width, Prefs.networkDetailsWidth, + "The details pane has an incorrect width."); + is(detailsPane.style.marginLeft, "0px", + "The details pane has an incorrect left margin."); + is(detailsPane.style.marginRight, "0px", + "The details pane has an incorrect right margin."); + ok(!detailsPane.hasAttribute("animated"), + "The details pane has an incorrect animated attribute."); + ok(!detailsPane.classList.contains("pane-collapsed") && + !detailsPaneToggleButton.classList.contains("pane-collapsed"), + "The details pane should at this point be visible."); + + // Trigger reflow to make sure the UI is in required state. + document.documentElement.getBoundingClientRect(); + + NetMonitorView.toggleDetailsPane({ visible: false, animated: true }); + + yield once(detailsPane, "transitionend"); + + let margin = -(width + 1) + "px"; + is(width, Prefs.networkDetailsWidth, + "The details pane has an incorrect width after collapsing."); + is(detailsPane.style.marginLeft, margin, + "The details pane has an incorrect left margin after collapsing."); + is(detailsPane.style.marginRight, margin, + "The details pane has an incorrect right margin after collapsing."); + ok(!detailsPane.hasAttribute("animated"), + "The details pane has an incorrect attribute after an animated collapsing."); + ok(detailsPane.classList.contains("pane-collapsed") && + detailsPaneToggleButton.classList.contains("pane-collapsed"), + "The details pane should not be visible after collapsing."); + + NetMonitorView.toggleDetailsPane({ visible: true, animated: false }); + + is(width, Prefs.networkDetailsWidth, + "The details pane has an incorrect width after uncollapsing."); + is(detailsPane.style.marginLeft, "0px", + "The details pane has an incorrect left margin after uncollapsing."); + is(detailsPane.style.marginRight, "0px", + "The details pane has an incorrect right margin after uncollapsing."); + ok(!detailsPane.hasAttribute("animated"), + "The details pane has an incorrect attribute after an unanimated uncollapsing."); + ok(!detailsPane.classList.contains("pane-collapsed") && + !detailsPaneToggleButton.classList.contains("pane-collapsed"), + "The details pane should be visible again after uncollapsing."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_pane-toggle.js b/devtools/client/netmonitor/test/browser_net_pane-toggle.js new file mode 100644 index 000000000..87b71019c --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_pane-toggle.js @@ -0,0 +1,74 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if toggling the details pane works as expected. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { $, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + let { NETWORK_EVENT, TAB_UPDATED } = monitor.panelWin.EVENTS; + RequestsMenu.lazyUpdate = false; + + let toggleButton = $("#details-pane-toggle"); + + is(toggleButton.hasAttribute("disabled"), true, + "The pane toggle button should be disabled when the frontend is opened."); + is(toggleButton.classList.contains("pane-collapsed"), true, + "The pane toggle button should indicate that the details pane is " + + "collapsed when the frontend is opened."); + is(NetMonitorView.detailsPaneHidden, true, + "The details pane should be hidden when the frontend is opened."); + is(RequestsMenu.selectedItem, null, + "There should be no selected item in the requests menu."); + + let networkEvent = monitor.panelWin.once(NETWORK_EVENT); + tab.linkedBrowser.reload(); + yield networkEvent; + + is(toggleButton.hasAttribute("disabled"), false, + "The pane toggle button should be enabled after the first request."); + is(toggleButton.classList.contains("pane-collapsed"), true, + "The pane toggle button should still indicate that the details pane is " + + "collapsed after the first request."); + is(NetMonitorView.detailsPaneHidden, true, + "The details pane should still be hidden after the first request."); + is(RequestsMenu.selectedItem, null, + "There should still be no selected item in the requests menu."); + + EventUtils.sendMouseEvent({ type: "mousedown" }, toggleButton); + + yield monitor.panelWin.once(TAB_UPDATED); + + is(toggleButton.hasAttribute("disabled"), false, + "The pane toggle button should still be enabled after being pressed."); + is(toggleButton.classList.contains("pane-collapsed"), false, + "The pane toggle button should now indicate that the details pane is " + + "not collapsed anymore after being pressed."); + is(NetMonitorView.detailsPaneHidden, false, + "The details pane should not be hidden after toggle button was pressed."); + isnot(RequestsMenu.selectedItem, null, + "There should be a selected item in the requests menu."); + is(RequestsMenu.selectedIndex, 0, + "The first item should be selected in the requests menu."); + + EventUtils.sendMouseEvent({ type: "mousedown" }, toggleButton); + + is(toggleButton.hasAttribute("disabled"), false, + "The pane toggle button should still be enabled after being pressed again."); + is(toggleButton.classList.contains("pane-collapsed"), true, + "The pane toggle button should now indicate that the details pane is " + + "collapsed after being pressed again."); + is(NetMonitorView.detailsPaneHidden, true, + "The details pane should now be hidden after the toggle button was pressed again."); + is(RequestsMenu.selectedItem, null, + "There should now be no selected item in the requests menu."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_persistent_logs.js b/devtools/client/netmonitor/test/browser_net_persistent_logs.js new file mode 100644 index 000000000..ac2371e1f --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_persistent_logs.js @@ -0,0 +1,51 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the network monitor leaks on initialization and sudden destruction. + * You can also use this initialization format as a template for other tests. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(SINGLE_GET_URL); + info("Starting test... "); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + Services.prefs.setBoolPref("devtools.webconsole.persistlog", false); + + yield reloadAndWait(); + + is(RequestsMenu.itemCount, 2, + "The request menu should have two items at this point."); + + yield reloadAndWait(); + + // Since the reload clears the log, we still expect two requests in the log + is(RequestsMenu.itemCount, 2, + "The request menu should still have two items at this point."); + + // Now we toggle the persistence logs on + Services.prefs.setBoolPref("devtools.webconsole.persistlog", true); + + yield reloadAndWait(); + + // Since we togged the persistence logs, we expect four items after the reload + is(RequestsMenu.itemCount, 4, + "The request menu should now have four items at this point."); + + Services.prefs.setBoolPref("devtools.webconsole.persistlog", false); + return teardown(monitor); + + /** + * Reload the page and wait for 2 GET requests. Race-free. + */ + function reloadAndWait() { + let wait = waitForNetworkEvents(monitor, 2); + tab.linkedBrowser.reload(); + return wait; + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_post-data-01.js b/devtools/client/netmonitor/test/browser_net_post-data-01.js new file mode 100644 index 000000000..6d5f8dc1b --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_post-data-01.js @@ -0,0 +1,166 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the POST requests display the correct information in the UI. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(POST_DATA_URL); + info("Starting test... "); + + let { document, EVENTS, Editor, NetMonitorView } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + NetworkDetails._params.lazyEmpty = false; + + let wait = waitForNetworkEvents(monitor, 0, 2); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), + "POST", SIMPLE_SJS + "?foo=bar&baz=42&type=urlencoded", { + status: 200, + statusText: "Och Aye", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1), + "POST", SIMPLE_SJS + "?foo=bar&baz=42&type=multipart", { + status: 200, + statusText: "Och Aye", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12), + time: true + }); + + let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[2]); + yield onEvent; + yield testParamsTab("urlencoded"); + + onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED); + RequestsMenu.selectedIndex = 1; + yield onEvent; + yield testParamsTab("multipart"); + + return teardown(monitor); + + function* testParamsTab(type) { + let tabEl = document.querySelectorAll("#details-pane tab")[2]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2]; + + is(tabEl.getAttribute("selected"), "true", + "The params tab in the network details pane should be selected."); + + function checkVisibility(box) { + is(tabpanel.querySelector("#request-params-box") + .hasAttribute("hidden"), !box.includes("params"), + "The request params box doesn't have the indended visibility."); + is(tabpanel.querySelector("#request-post-data-textarea-box") + .hasAttribute("hidden"), !box.includes("textarea"), + "The request post data textarea box doesn't have the indended visibility."); + } + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 2, + "There should be 2 param scopes displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0, + "The empty notice should not be displayed in this tabpanel."); + + let queryScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + let postScope = tabpanel.querySelectorAll(".variables-view-scope")[1]; + + is(queryScope.querySelector(".name").getAttribute("value"), + L10N.getStr("paramsQueryString"), + "The query scope doesn't have the correct title."); + + is(postScope.querySelector(".name").getAttribute("value"), + L10N.getStr(type == "urlencoded" ? "paramsFormData" : "paramsPostPayload"), + "The post scope doesn't have the correct title."); + + is(queryScope.querySelectorAll(".variables-view-variable .name")[0] + .getAttribute("value"), + "foo", "The first query param name was incorrect."); + is(queryScope.querySelectorAll(".variables-view-variable .value")[0] + .getAttribute("value"), + "\"bar\"", "The first query param value was incorrect."); + is(queryScope.querySelectorAll(".variables-view-variable .name")[1] + .getAttribute("value"), + "baz", "The second query param name was incorrect."); + is(queryScope.querySelectorAll(".variables-view-variable .value")[1] + .getAttribute("value"), + "\"42\"", "The second query param value was incorrect."); + is(queryScope.querySelectorAll(".variables-view-variable .name")[2] + .getAttribute("value"), + "type", "The third query param name was incorrect."); + is(queryScope.querySelectorAll(".variables-view-variable .value")[2] + .getAttribute("value"), + "\"" + type + "\"", "The third query param value was incorrect."); + + if (type == "urlencoded") { + checkVisibility("params"); + + is(tabpanel.querySelectorAll(".variables-view-variable").length, 5, + "There should be 5 param values displayed in this tabpanel."); + is(queryScope.querySelectorAll(".variables-view-variable").length, 3, + "There should be 3 param values displayed in the query scope."); + is(postScope.querySelectorAll(".variables-view-variable").length, 2, + "There should be 2 param values displayed in the post scope."); + + is(postScope.querySelectorAll(".variables-view-variable .name")[0] + .getAttribute("value"), + "foo", "The first post param name was incorrect."); + is(postScope.querySelectorAll(".variables-view-variable .value")[0] + .getAttribute("value"), + "\"bar\"", "The first post param value was incorrect."); + is(postScope.querySelectorAll(".variables-view-variable .name")[1] + .getAttribute("value"), + "baz", "The second post param name was incorrect."); + is(postScope.querySelectorAll(".variables-view-variable .value")[1] + .getAttribute("value"), + "\"123\"", "The second post param value was incorrect."); + } else { + checkVisibility("params textarea"); + + is(tabpanel.querySelectorAll(".variables-view-variable").length, 3, + "There should be 3 param values displayed in this tabpanel."); + is(queryScope.querySelectorAll(".variables-view-variable").length, 3, + "There should be 3 param values displayed in the query scope."); + is(postScope.querySelectorAll(".variables-view-variable").length, 0, + "There should be 0 param values displayed in the post scope."); + + let editor = yield NetMonitorView.editor("#request-post-data-textarea"); + let text = editor.getText(); + + ok(text.includes("Content-Disposition: form-data; name=\"text\""), + "The text shown in the source editor is incorrect (1.1)."); + ok(text.includes("Content-Disposition: form-data; name=\"email\""), + "The text shown in the source editor is incorrect (2.1)."); + ok(text.includes("Content-Disposition: form-data; name=\"range\""), + "The text shown in the source editor is incorrect (3.1)."); + ok(text.includes("Content-Disposition: form-data; name=\"Custom field\""), + "The text shown in the source editor is incorrect (4.1)."); + ok(text.includes("Some text..."), + "The text shown in the source editor is incorrect (2.2)."); + ok(text.includes("42"), + "The text shown in the source editor is incorrect (3.2)."); + ok(text.includes("Extra data"), + "The text shown in the source editor is incorrect (4.2)."); + is(editor.getMode(), Editor.modes.text, + "The mode active in the source editor is incorrect."); + } + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_post-data-02.js b/devtools/client/netmonitor/test/browser_net_post-data-02.js new file mode 100644 index 000000000..3cdd2f14a --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_post-data-02.js @@ -0,0 +1,73 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the POST requests display the correct information in the UI, + * for raw payloads with attached content-type headers. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(POST_RAW_URL); + info("Starting test... "); + + let { document, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + NetworkDetails._params.lazyEmpty = false; + + let wait = waitForNetworkEvents(monitor, 0, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED); + NetMonitorView.toggleDetailsPane({ visible: true }, 2); + RequestsMenu.selectedIndex = 0; + yield onEvent; + + let tabEl = document.querySelectorAll("#event-details-pane tab")[2]; + let tabpanel = document.querySelectorAll("#event-details-pane tabpanel")[2]; + + is(tabEl.getAttribute("selected"), "true", + "The params tab in the network details pane should be selected."); + + is(tabpanel.querySelector("#request-params-box") + .hasAttribute("hidden"), false, + "The request params box doesn't have the indended visibility."); + is(tabpanel.querySelector("#request-post-data-textarea-box") + .hasAttribute("hidden"), true, + "The request post data textarea box doesn't have the indended visibility."); + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 1, + "There should be 1 param scopes displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0, + "The empty notice should not be displayed in this tabpanel."); + + let postScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + is(postScope.querySelector(".name").getAttribute("value"), + L10N.getStr("paramsFormData"), + "The post scope doesn't have the correct title."); + + is(postScope.querySelectorAll(".variables-view-variable").length, 2, + "There should be 2 param values displayed in the post scope."); + is(postScope.querySelectorAll(".variables-view-variable .name")[0] + .getAttribute("value"), + "foo", "The first query param name was incorrect."); + is(postScope.querySelectorAll(".variables-view-variable .value")[0] + .getAttribute("value"), + "\"bar\"", "The first query param value was incorrect."); + is(postScope.querySelectorAll(".variables-view-variable .name")[1] + .getAttribute("value"), + "baz", "The second query param name was incorrect."); + is(postScope.querySelectorAll(".variables-view-variable .value")[1] + .getAttribute("value"), + "\"123\"", "The second query param value was incorrect."); + + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_post-data-03.js b/devtools/client/netmonitor/test/browser_net_post-data-03.js new file mode 100644 index 000000000..3433f89ce --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_post-data-03.js @@ -0,0 +1,98 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the POST requests display the correct information in the UI, + * for raw payloads with content-type headers attached to the upload stream. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(POST_RAW_WITH_HEADERS_URL); + info("Starting test... "); + + let { document, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 0, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED); + NetMonitorView.toggleDetailsPane({ visible: true }); + RequestsMenu.selectedIndex = 0; + yield onEvent; + + let tabEl = document.querySelectorAll("#details-pane tab")[0]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[0]; + let requestFromUploadScope = tabpanel.querySelectorAll(".variables-view-scope")[2]; + + is(tabEl.getAttribute("selected"), "true", + "The headers tab in the network details pane should be selected."); + is(tabpanel.querySelectorAll(".variables-view-scope").length, 3, + "There should be 3 header scopes displayed in this tabpanel."); + + is(requestFromUploadScope.querySelector(".name").getAttribute("value"), + L10N.getStr("requestHeadersFromUpload") + " (" + + L10N.getFormatStr("networkMenu.sizeKB", L10N.numberWithDecimals(74 / 1024, 3)) + ")", + "The request headers from upload scope doesn't have the correct title."); + + is(requestFromUploadScope.querySelectorAll(".variables-view-variable").length, 2, + "There should be 2 headers displayed in the request headers from upload scope."); + + is(requestFromUploadScope.querySelectorAll(".variables-view-variable .name")[0] + .getAttribute("value"), + "content-type", "The first request header name was incorrect."); + is(requestFromUploadScope.querySelectorAll(".variables-view-variable .value")[0] + .getAttribute("value"), "\"application/x-www-form-urlencoded\"", + "The first request header value was incorrect."); + is(requestFromUploadScope.querySelectorAll(".variables-view-variable .name")[1] + .getAttribute("value"), + "custom-header", "The second request header name was incorrect."); + is(requestFromUploadScope.querySelectorAll(".variables-view-variable .value")[1] + .getAttribute("value"), + "\"hello world!\"", "The second request header value was incorrect."); + + onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[2]); + yield onEvent; + + tabEl = document.querySelectorAll("#details-pane tab")[2]; + tabpanel = document.querySelectorAll("#details-pane tabpanel")[2]; + let formDataScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + + is(tab.getAttribute("selected"), "true", + "The response tab in the network details pane should be selected."); + is(tabpanel.querySelectorAll(".variables-view-scope").length, 1, + "There should be 1 header scope displayed in this tabpanel."); + + is(formDataScope.querySelector(".name").getAttribute("value"), + L10N.getStr("paramsFormData"), + "The form data scope doesn't have the correct title."); + + is(formDataScope.querySelectorAll(".variables-view-variable").length, 2, + "There should be 2 payload values displayed in the form data scope."); + + is(formDataScope.querySelectorAll(".variables-view-variable .name")[0] + .getAttribute("value"), + "foo", "The first payload param name was incorrect."); + is(formDataScope.querySelectorAll(".variables-view-variable .value")[0] + .getAttribute("value"), + "\"bar\"", "The first payload param value was incorrect."); + is(formDataScope.querySelectorAll(".variables-view-variable .name")[1] + .getAttribute("value"), + "baz", "The second payload param name was incorrect."); + is(formDataScope.querySelectorAll(".variables-view-variable .value")[1] + .getAttribute("value"), + "\"123\"", "The second payload param value was incorrect."); + + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_post-data-04.js b/devtools/client/netmonitor/test/browser_net_post-data-04.js new file mode 100644 index 000000000..565792287 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_post-data-04.js @@ -0,0 +1,74 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the POST requests display the correct information in the UI, + * for JSON payloads. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(POST_JSON_URL); + info("Starting test... "); + + let { document, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + NetworkDetails._params.lazyEmpty = false; + + let wait = waitForNetworkEvents(monitor, 0, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED); + NetMonitorView.toggleDetailsPane({ visible: true }, 2); + RequestsMenu.selectedIndex = 0; + yield onEvent; + + let tabEl = document.querySelectorAll("#event-details-pane tab")[2]; + let tabpanel = document.querySelectorAll("#event-details-pane tabpanel")[2]; + + is(tabEl.getAttribute("selected"), "true", + "The params tab in the network details pane should be selected."); + + is(tabpanel.querySelector("#request-params-box") + .hasAttribute("hidden"), false, + "The request params box doesn't have the intended visibility."); + is(tabpanel.querySelector("#request-post-data-textarea-box") + .hasAttribute("hidden"), true, + "The request post data textarea box doesn't have the intended visibility."); + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 1, + "There should be 1 param scopes displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0, + "The empty notice should not be displayed in this tabpanel."); + + let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + is(jsonScope.querySelector(".name").getAttribute("value"), + L10N.getStr("jsonScopeName"), + "The JSON scope doesn't have the correct title."); + + let valueScope = tabpanel.querySelector( + ".variables-view-scope > .variables-view-element-details"); + + is(valueScope.querySelectorAll(".variables-view-variable").length, 1, + "There should be 1 value displayed in the JSON scope."); + is(valueScope.querySelector(".variables-view-property .name") + .getAttribute("value"), + "a", "The JSON var name was incorrect."); + is(valueScope.querySelector(".variables-view-property .value") + .getAttribute("value"), + "1", "The JSON var value was incorrect."); + + let detailsParent = valueScope.querySelector(".variables-view-property .name") + .closest(".variables-view-element-details"); + is(detailsParent.hasAttribute("open"), true, "The JSON value must be visible"); + + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_prefs-and-l10n.js b/devtools/client/netmonitor/test/browser_net_prefs-and-l10n.js new file mode 100644 index 000000000..e73f94d6d --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_prefs-and-l10n.js @@ -0,0 +1,54 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the preferences and localization objects work correctly. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + ok(monitor.panelWin.Prefs, + "Should have a preferences object available on the panel window."); + + testL10N(); + testPrefs(); + + return teardown(monitor); + + function testL10N() { + is(typeof L10N.getStr("netmonitor.security.enabled"), "string", + "The getStr() method didn't return a valid string."); + is(typeof L10N.getFormatStr("networkMenu.totalMS", "foo"), "string", + "The getFormatStr() method didn't return a valid string."); + } + + function testPrefs() { + let { Prefs } = monitor.panelWin; + + is(Prefs.networkDetailsWidth, + Services.prefs.getIntPref("devtools.netmonitor.panes-network-details-width"), + "Getting a pref should work correctly."); + + let previousValue = Prefs.networkDetailsWidth; + let bogusValue = ~~(Math.random() * 100); + Prefs.networkDetailsWidth = bogusValue; + is(Prefs.networkDetailsWidth, + Services.prefs.getIntPref("devtools.netmonitor.panes-network-details-width"), + "Getting a pref after it has been modified should work correctly."); + is(Prefs.networkDetailsWidth, bogusValue, + "The pref wasn't updated correctly in the preferences object."); + + Prefs.networkDetailsWidth = previousValue; + is(Prefs.networkDetailsWidth, + Services.prefs.getIntPref("devtools.netmonitor.panes-network-details-width"), + "Getting a pref after it has been modified again should work correctly."); + is(Prefs.networkDetailsWidth, previousValue, + "The pref wasn't updated correctly again in the preferences object."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_prefs-reload.js b/devtools/client/netmonitor/test/browser_net_prefs-reload.js new file mode 100644 index 000000000..ee56ee446 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_prefs-reload.js @@ -0,0 +1,215 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the prefs that should survive across tool reloads work. + */ + +add_task(function* () { + let Actions = require("devtools/client/netmonitor/actions/index"); + let { monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + // This test reopens the network monitor a bunch of times, for different + // hosts (bottom, side, window). This seems to be slow on debug builds. + requestLongerTimeout(3); + + // Use these getters instead of caching instances inside the panel win, + // since the tool is reopened a bunch of times during this test + // and the instances will differ. + let getView = () => monitor.panelWin.NetMonitorView; + let getStore = () => monitor.panelWin.gStore; + + let prefsToCheck = { + filters: { + // A custom new value to be used for the verified preference. + newValue: ["html", "css"], + // Getter used to retrieve the current value from the frontend, in order + // to verify that the pref was applied properly. + validateValue: ($) => getView().RequestsMenu._activeFilters, + // Predicate used to modify the frontend when setting the new pref value, + // before trying to validate the changes. + modifyFrontend: ($, value) => value.forEach(e => + getStore().dispatch(Actions.toggleFilterType(e))) + }, + networkDetailsWidth: { + newValue: ~~(Math.random() * 200 + 100), + validateValue: ($) => ~~$("#details-pane").getAttribute("width"), + modifyFrontend: ($, value) => $("#details-pane").setAttribute("width", value) + }, + networkDetailsHeight: { + newValue: ~~(Math.random() * 300 + 100), + validateValue: ($) => ~~$("#details-pane").getAttribute("height"), + modifyFrontend: ($, value) => $("#details-pane").setAttribute("height", value) + } + /* add more prefs here... */ + }; + + yield testBottom(); + yield testSide(); + yield testWindow(); + + info("Moving toolbox back to the bottom..."); + yield monitor._toolbox.switchHost(Toolbox.HostType.BOTTOM); + return teardown(monitor); + + function storeFirstPrefValues() { + info("Caching initial pref values."); + + for (let name in prefsToCheck) { + let currentValue = monitor.panelWin.Prefs[name]; + prefsToCheck[name].firstValue = currentValue; + } + } + + function validateFirstPrefValues() { + info("Validating current pref values to the UI elements."); + + for (let name in prefsToCheck) { + let currentValue = monitor.panelWin.Prefs[name]; + let firstValue = prefsToCheck[name].firstValue; + let validateValue = prefsToCheck[name].validateValue; + + is(currentValue.toSource(), firstValue.toSource(), + "Pref " + name + " should be equal to first value: " + firstValue); + is(currentValue.toSource(), validateValue(monitor.panelWin.$).toSource(), + "Pref " + name + " should validate: " + currentValue); + } + } + + function modifyFrontend() { + info("Modifying UI elements to the specified new values."); + + for (let name in prefsToCheck) { + let currentValue = monitor.panelWin.Prefs[name]; + let firstValue = prefsToCheck[name].firstValue; + let newValue = prefsToCheck[name].newValue; + let validateValue = prefsToCheck[name].validateValue; + let modFrontend = prefsToCheck[name].modifyFrontend; + + modFrontend(monitor.panelWin.$, newValue); + info("Modified UI element affecting " + name + " to: " + newValue); + + is(currentValue.toSource(), firstValue.toSource(), + "Pref " + name + " should still be equal to first value: " + firstValue); + isnot(currentValue.toSource(), newValue.toSource(), + "Pref " + name + " should't yet be equal to second value: " + newValue); + is(newValue.toSource(), validateValue(monitor.panelWin.$).toSource(), + "The UI element affecting " + name + " should validate: " + newValue); + } + } + + function validateNewPrefValues() { + info("Invalidating old pref values to the modified UI elements."); + + for (let name in prefsToCheck) { + let currentValue = monitor.panelWin.Prefs[name]; + let firstValue = prefsToCheck[name].firstValue; + let newValue = prefsToCheck[name].newValue; + let validateValue = prefsToCheck[name].validateValue; + + isnot(currentValue.toSource(), firstValue.toSource(), + "Pref " + name + " should't be equal to first value: " + firstValue); + is(currentValue.toSource(), newValue.toSource(), + "Pref " + name + " should now be equal to second value: " + newValue); + is(newValue.toSource(), validateValue(monitor.panelWin.$).toSource(), + "The UI element affecting " + name + " should validate: " + newValue); + } + } + + function resetFrontend() { + info("Resetting UI elements to the cached initial pref values."); + + for (let name in prefsToCheck) { + let currentValue = monitor.panelWin.Prefs[name]; + let firstValue = prefsToCheck[name].firstValue; + let newValue = prefsToCheck[name].newValue; + let validateValue = prefsToCheck[name].validateValue; + let modFrontend = prefsToCheck[name].modifyFrontend; + + modFrontend(monitor.panelWin.$, firstValue); + info("Modified UI element affecting " + name + " to: " + firstValue); + + isnot(currentValue.toSource(), firstValue.toSource(), + "Pref " + name + " should't yet be equal to first value: " + firstValue); + is(currentValue.toSource(), newValue.toSource(), + "Pref " + name + " should still be equal to second value: " + newValue); + is(firstValue.toSource(), validateValue(monitor.panelWin.$).toSource(), + "The UI element affecting " + name + " should validate: " + firstValue); + } + } + + function* testBottom() { + info("Testing prefs reload for a bottom host."); + storeFirstPrefValues(); + + // Validate and modify while toolbox is on the bottom. + validateFirstPrefValues(); + modifyFrontend(); + + let newMonitor = yield restartNetMonitor(monitor); + monitor = newMonitor.monitor; + + // Revalidate and reset frontend while toolbox is on the bottom. + validateNewPrefValues(); + resetFrontend(); + + newMonitor = yield restartNetMonitor(monitor); + monitor = newMonitor.monitor; + + // Revalidate. + validateFirstPrefValues(); + } + + function* testSide() { + info("Moving toolbox to the side..."); + + yield monitor._toolbox.switchHost(Toolbox.HostType.SIDE); + info("Testing prefs reload for a side host."); + storeFirstPrefValues(); + + // Validate and modify frontend while toolbox is on the side. + validateFirstPrefValues(); + modifyFrontend(); + + let newMonitor = yield restartNetMonitor(monitor); + monitor = newMonitor.monitor; + + // Revalidate and reset frontend while toolbox is on the side. + validateNewPrefValues(); + resetFrontend(); + + newMonitor = yield restartNetMonitor(monitor); + monitor = newMonitor.monitor; + + // Revalidate. + validateFirstPrefValues(); + } + + function* testWindow() { + info("Moving toolbox into a window..."); + + yield monitor._toolbox.switchHost(Toolbox.HostType.WINDOW); + info("Testing prefs reload for a window host."); + storeFirstPrefValues(); + + // Validate and modify frontend while toolbox is in a window. + validateFirstPrefValues(); + modifyFrontend(); + + let newMonitor = yield restartNetMonitor(monitor); + monitor = newMonitor.monitor; + + // Revalidate and reset frontend while toolbox is in a window. + validateNewPrefValues(); + resetFrontend(); + + newMonitor = yield restartNetMonitor(monitor); + monitor = newMonitor.monitor; + + // Revalidate. + validateFirstPrefValues(); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_raw_headers.js b/devtools/client/netmonitor/test/browser_net_raw_headers.js new file mode 100644 index 000000000..2cb734745 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_raw_headers.js @@ -0,0 +1,70 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if showing raw headers works. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(POST_DATA_URL); + info("Starting test... "); + + let { document, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 0, 2); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + let origItem = RequestsMenu.getItemAtIndex(0); + + let onTabEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED); + RequestsMenu.selectedItem = origItem; + yield onTabEvent; + + EventUtils.sendMouseEvent({ type: "click" }, + document.getElementById("toggle-raw-headers")); + + testShowRawHeaders(origItem.attachment); + + EventUtils.sendMouseEvent({ type: "click" }, + document.getElementById("toggle-raw-headers")); + + testHideRawHeaders(document); + + return teardown(monitor); + + /* + * Tests that raw headers were displayed correctly + */ + function testShowRawHeaders(data) { + let requestHeaders = document.getElementById("raw-request-headers-textarea").value; + for (let header of data.requestHeaders.headers) { + ok(requestHeaders.indexOf(header.name + ": " + header.value) >= 0, + "textarea contains request headers"); + } + let responseHeaders = document.getElementById("raw-response-headers-textarea").value; + for (let header of data.responseHeaders.headers) { + ok(responseHeaders.indexOf(header.name + ": " + header.value) >= 0, + "textarea contains response headers"); + } + } + + /* + * Tests that raw headers textareas are hidden and empty + */ + function testHideRawHeaders() { + let rawHeadersHidden = document.getElementById("raw-headers").getAttribute("hidden"); + let requestTextarea = document.getElementById("raw-request-headers-textarea"); + let responseTextarea = document.getElementById("raw-response-headers-textarea"); + ok(rawHeadersHidden, "raw headers textareas are hidden"); + ok(requestTextarea.value == "", "raw request headers textarea is empty"); + ok(responseTextarea.value == "", "raw response headers textarea is empty"); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_reload-button.js b/devtools/client/netmonitor/test/browser_net_reload-button.js new file mode 100644 index 000000000..e91de8302 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_reload-button.js @@ -0,0 +1,25 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the empty-requests reload button works. + */ + +add_task(function* () { + let { monitor } = yield initNetMonitor(SINGLE_GET_URL); + info("Starting test... "); + + let { document, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + let wait = waitForNetworkEvents(monitor, 2); + let button = document.querySelector("#requests-menu-reload-notice-button"); + button.click(); + yield wait; + + is(RequestsMenu.itemCount, 2, "The request menu should have two items after reloading"); + + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_reload-markers.js b/devtools/client/netmonitor/test/browser_net_reload-markers.js new file mode 100644 index 000000000..26866830f --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_reload-markers.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the empty-requests reload button works. + */ + +add_task(function* () { + let { monitor } = yield initNetMonitor(SINGLE_GET_URL); + info("Starting test... "); + + let { document, EVENTS } = monitor.panelWin; + let button = document.querySelector("#requests-menu-reload-notice-button"); + button.click(); + + let markers = []; + + monitor.panelWin.on(EVENTS.TIMELINE_EVENT, (_, marker) => { + markers.push(marker); + }); + + yield waitForNetworkEvents(monitor, 2); + yield waitUntil(() => markers.length == 2); + + ok(true, "Reloading finished"); + + is(markers[0].name, "document::DOMContentLoaded", + "The first received marker is correct."); + is(markers[1].name, "document::Load", + "The second received marker is correct."); + + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js b/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js new file mode 100644 index 000000000..71a913501 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js @@ -0,0 +1,68 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test if request and response body logging stays on after opening the console. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(JSON_LONG_URL); + info("Starting test... "); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + // Perform first batch of requests. + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + verifyRequest(0); + + // Switch to the webconsole. + let onWebConsole = monitor._toolbox.once("webconsole-selected"); + monitor._toolbox.selectTool("webconsole"); + yield onWebConsole; + + // Switch back to the netmonitor. + let onNetMonitor = monitor._toolbox.once("netmonitor-selected"); + monitor._toolbox.selectTool("netmonitor"); + yield onNetMonitor; + + // Reload debugee. + wait = waitForNetworkEvents(monitor, 1); + tab.linkedBrowser.reload(); + yield wait; + + // Perform another batch of requests. + wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + verifyRequest(1); + + return teardown(monitor); + + function verifyRequest(offset) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(offset), + "GET", CONTENT_TYPE_SJS + "?fmt=json-long", { + status: 200, + statusText: "OK", + type: "json", + fullMimeType: "text/json; charset=utf-8", + size: L10N.getFormatStr("networkMenu.sizeKB", + L10N.numberWithDecimals(85975 / 1024, 2)), + time: true + }); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_resend.js b/devtools/client/netmonitor/test/browser_net_resend.js new file mode 100644 index 000000000..7b540ec50 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_resend.js @@ -0,0 +1,174 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if resending a request works. + */ + +const ADD_QUERY = "t1=t2"; +const ADD_HEADER = "Test-header: true"; +const ADD_UA_HEADER = "User-Agent: Custom-Agent"; +const ADD_POSTDATA = "&t3=t4"; + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(POST_DATA_URL); + info("Starting test... "); + + let { panelWin } = monitor; + let { document, EVENTS, NetMonitorView } = panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 0, 2); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + let origItem = RequestsMenu.getItemAtIndex(0); + + let onTabUpdated = panelWin.once(EVENTS.TAB_UPDATED); + RequestsMenu.selectedItem = origItem; + yield onTabUpdated; + + // add a new custom request cloned from selected request + let onPopulated = panelWin.once(EVENTS.CUSTOMREQUESTVIEW_POPULATED); + RequestsMenu.cloneSelectedRequest(); + yield onPopulated; + + testCustomForm(origItem.attachment); + + let customItem = RequestsMenu.selectedItem; + testCustomItem(customItem, origItem); + + // edit the custom request + yield editCustomForm(); + testCustomItemChanged(customItem, origItem); + + // send the new request + wait = waitForNetworkEvents(monitor, 0, 1); + RequestsMenu.sendCustomRequest(); + yield wait; + + let sentItem = RequestsMenu.selectedItem; + testSentRequest(sentItem.attachment, origItem.attachment); + + return teardown(monitor); + + function testCustomItem(item, orig) { + let method = item.target.querySelector(".requests-menu-method").value; + let origMethod = orig.target.querySelector(".requests-menu-method").value; + is(method, origMethod, "menu item is showing the same method as original request"); + + let file = item.target.querySelector(".requests-menu-file").value; + let origFile = orig.target.querySelector(".requests-menu-file").value; + is(file, origFile, "menu item is showing the same file name as original request"); + + let domain = item.target.querySelector(".requests-menu-domain").value; + let origDomain = orig.target.querySelector(".requests-menu-domain").value; + is(domain, origDomain, "menu item is showing the same domain as original request"); + } + + function testCustomItemChanged(item, orig) { + let file = item.target.querySelector(".requests-menu-file").value; + let expectedFile = orig.target.querySelector(".requests-menu-file").value + + "&" + ADD_QUERY; + + is(file, expectedFile, "menu item is updated to reflect url entered in form"); + } + + /* + * Test that the New Request form was populated correctly + */ + function testCustomForm(data) { + is(document.getElementById("custom-method-value").value, data.method, + "new request form showing correct method"); + + is(document.getElementById("custom-url-value").value, data.url, + "new request form showing correct url"); + + let query = document.getElementById("custom-query-value"); + is(query.value, "foo=bar\nbaz=42\ntype=urlencoded", + "new request form showing correct query string"); + + let headers = document.getElementById("custom-headers-value").value.split("\n"); + for (let {name, value} of data.requestHeaders.headers) { + ok(headers.indexOf(name + ": " + value) >= 0, "form contains header from request"); + } + + let postData = document.getElementById("custom-postdata-value"); + is(postData.value, data.requestPostData.postData.text, + "new request form showing correct post data"); + } + + /* + * Add some params and headers to the request form + */ + function* editCustomForm() { + panelWin.focus(); + + let query = document.getElementById("custom-query-value"); + let queryFocus = once(query, "focus", false); + // Bug 1195825: Due to some unexplained dark-matter with promise, + // focus only works if delayed by one tick. + query.setSelectionRange(query.value.length, query.value.length); + executeSoon(() => query.focus()); + yield queryFocus; + + // add params to url query string field + type(["VK_RETURN"]); + type(ADD_QUERY); + + let headers = document.getElementById("custom-headers-value"); + let headersFocus = once(headers, "focus", false); + headers.setSelectionRange(headers.value.length, headers.value.length); + headers.focus(); + yield headersFocus; + + // add a header + type(["VK_RETURN"]); + type(ADD_HEADER); + + // add a User-Agent header, to check if default headers can be modified + // (there will be two of them, first gets overwritten by the second) + type(["VK_RETURN"]); + type(ADD_UA_HEADER); + + let postData = document.getElementById("custom-postdata-value"); + let postFocus = once(postData, "focus", false); + postData.setSelectionRange(postData.value.length, postData.value.length); + postData.focus(); + yield postFocus; + + // add to POST data + type(ADD_POSTDATA); + } + + /* + * Make sure newly created event matches expected request + */ + function testSentRequest(data, origData) { + is(data.method, origData.method, "correct method in sent request"); + is(data.url, origData.url + "&" + ADD_QUERY, "correct url in sent request"); + + let { headers } = data.requestHeaders; + let hasHeader = headers.some(h => `${h.name}: ${h.value}` == ADD_HEADER); + ok(hasHeader, "new header added to sent request"); + + let hasUAHeader = headers.some(h => `${h.name}: ${h.value}` == ADD_UA_HEADER); + ok(hasUAHeader, "User-Agent header added to sent request"); + + is(data.requestPostData.postData.text, + origData.requestPostData.postData.text + ADD_POSTDATA, + "post data added to sent request"); + } + + function type(string) { + for (let ch of string) { + EventUtils.synthesizeKey(ch, {}, panelWin); + } + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_resend_cors.js b/devtools/client/netmonitor/test/browser_net_resend_cors.js new file mode 100644 index 000000000..d63c3b54e --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_resend_cors.js @@ -0,0 +1,80 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if resending a CORS request avoids the security checks and doesn't send + * a preflight OPTIONS request (bug 1270096 and friends) + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CORS_URL); + info("Starting test... "); + + let { EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let requestUrl = "http://test1.example.com" + CORS_SJS_PATH; + + info("Waiting for OPTIONS, then POST"); + let wait = waitForNetworkEvents(monitor, 1, 1); + yield ContentTask.spawn(tab.linkedBrowser, requestUrl, function* (url) { + content.wrappedJSObject.performRequests(url, "triggering/preflight", "post-data"); + }); + yield wait; + + const METHODS = ["OPTIONS", "POST"]; + + // Check the requests that were sent + for (let [i, method] of METHODS.entries()) { + let { attachment } = RequestsMenu.getItemAtIndex(i); + is(attachment.method, method, `The ${method} request has the right method`); + is(attachment.url, requestUrl, `The ${method} request has the right URL`); + } + + // Resend both requests without modification. Wait for resent OPTIONS, then POST. + // POST is supposed to have no preflight OPTIONS request this time (CORS is disabled) + let onRequests = waitForNetworkEvents(monitor, 1, 1); + for (let [i, method] of METHODS.entries()) { + let item = RequestsMenu.getItemAtIndex(i); + + info(`Selecting the ${method} request (at index ${i})`); + let onUpdate = monitor.panelWin.once(EVENTS.TAB_UPDATED); + RequestsMenu.selectedItem = item; + yield onUpdate; + + info("Cloning the selected request into a custom clone"); + let onPopulate = monitor.panelWin.once(EVENTS.CUSTOMREQUESTVIEW_POPULATED); + RequestsMenu.cloneSelectedRequest(); + yield onPopulate; + + info("Sending the cloned request (without change)"); + RequestsMenu.sendCustomRequest(); + } + + info("Waiting for both resent requests"); + yield onRequests; + + // Check the resent requests + for (let [i, method] of METHODS.entries()) { + let index = i + 2; + let item = RequestsMenu.getItemAtIndex(index).attachment; + is(item.method, method, `The ${method} request has the right method`); + is(item.url, requestUrl, `The ${method} request has the right URL`); + is(item.status, 200, `The ${method} response has the right status`); + + if (method === "POST") { + is(item.requestPostData.postData.text, "post-data", + "The POST request has the right POST data"); + // eslint-disable-next-line mozilla/no-cpows-in-tests + is(item.responseContent.content.text, "Access-Control-Allow-Origin: *", + "The POST response has the right content"); + } + } + + info("Finishing the test"); + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_resend_headers.js b/devtools/client/netmonitor/test/browser_net_resend_headers.js new file mode 100644 index 000000000..0503817e3 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_resend_headers.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test if custom request headers are not ignored (bug 1270096 and friends) + */ + +add_task(function* () { + let { monitor } = yield initNetMonitor(SIMPLE_SJS); + info("Starting test... "); + + let { NetMonitorView, NetMonitorController } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let requestUrl = SIMPLE_SJS; + let requestHeaders = [ + { name: "Host", value: "fakehost.example.com" }, + { name: "User-Agent", value: "Testzilla" }, + { name: "Referer", value: "http://example.com/referrer" }, + { name: "Accept", value: "application/jarda"}, + { name: "Accept-Encoding", value: "compress, identity, funcoding" }, + { name: "Accept-Language", value: "cs-CZ" } + ]; + + let wait = waitForNetworkEvents(monitor, 0, 1); + NetMonitorController.webConsoleClient.sendHTTPRequest({ + url: requestUrl, + method: "POST", + headers: requestHeaders, + body: "Hello" + }); + yield wait; + + let { attachment } = RequestsMenu.getItemAtIndex(0); + is(attachment.method, "POST", "The request has the right method"); + is(attachment.url, requestUrl, "The request has the right URL"); + + for (let { name, value } of attachment.requestHeaders.headers) { + info(`Request header: ${name}: ${value}`); + } + + function hasRequestHeader(name, value) { + let { headers } = attachment.requestHeaders; + return headers.some(h => h.name === name && h.value === value); + } + + function hasNotRequestHeader(name) { + let { headers } = attachment.requestHeaders; + return headers.every(h => h.name !== name); + } + + for (let { name, value } of requestHeaders) { + ok(hasRequestHeader(name, value), `The ${name} header has the right value`); + } + + // Check that the Cookie header was not added silently (i.e., that the request is + // anonymous. + for (let name of ["Cookie"]) { + ok(hasNotRequestHeader(name), `The ${name} header is not present`); + } + + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_security-details.js b/devtools/client/netmonitor/test/browser_net_security-details.js new file mode 100644 index 000000000..0a83b3ed9 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_security-details.js @@ -0,0 +1,102 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Test that Security details tab contains the expected data. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + let { $, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + RequestsMenu.lazyUpdate = false; + + info("Performing a secure request."); + const REQUESTS_URL = "https://example.com" + CORS_SJS_PATH; + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, REQUESTS_URL, function* (url) { + content.wrappedJSObject.performRequests(1, url); + }); + yield wait; + + info("Selecting the request."); + RequestsMenu.selectedIndex = 0; + + info("Waiting for details pane to be updated."); + yield monitor.panelWin.once(EVENTS.TAB_UPDATED); + + info("Selecting security tab."); + NetworkDetails.widget.selectedIndex = 5; + + info("Waiting for security tab to be updated."); + yield monitor.panelWin.once(EVENTS.TAB_UPDATED); + + let errorbox = $("#security-error"); + let infobox = $("#security-information"); + + is(errorbox.hidden, true, "Error box is hidden."); + is(infobox.hidden, false, "Information box visible."); + + // Connection + + // The protocol will be TLS but the exact version depends on which protocol + // the test server example.com supports. + let protocol = $("#security-protocol-version-value").value; + ok(protocol.startsWith("TLS"), "The protocol " + protocol + " seems valid."); + + // The cipher suite used by the test server example.com might change at any + // moment but all of them should start with "TLS_". + // http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml + let suite = $("#security-ciphersuite-value").value; + ok(suite.startsWith("TLS_"), "The suite " + suite + " seems valid."); + + // Host + checkLabel("#security-info-host-header", "Host example.com:"); + checkLabel("#security-http-strict-transport-security-value", "Disabled"); + checkLabel("#security-public-key-pinning-value", "Disabled"); + + // Cert + checkLabel("#security-cert-subject-cn", "example.com"); + checkLabel("#security-cert-subject-o", "<Not Available>"); + checkLabel("#security-cert-subject-ou", "<Not Available>"); + + checkLabel("#security-cert-issuer-cn", "Temporary Certificate Authority"); + checkLabel("#security-cert-issuer-o", "Mozilla Testing"); + checkLabel("#security-cert-issuer-ou", "<Not Available>"); + + // Locale sensitive and varies between timezones. Cant't compare equality or + // the test fails depending on which part of the world the test is executed. + checkLabelNotEmpty("#security-cert-validity-begins"); + checkLabelNotEmpty("#security-cert-validity-expires"); + + checkLabelNotEmpty("#security-cert-sha1-fingerprint"); + checkLabelNotEmpty("#security-cert-sha256-fingerprint"); + yield teardown(monitor); + + /** + * A helper that compares value attribute of a label with given selector to the + * expected value. + */ + function checkLabel(selector, expected) { + info("Checking label " + selector); + + let element = $(selector); + + ok(element, "Selector matched an element."); + is(element.value, expected, "Label has the expected value."); + } + + /** + * A helper that checks the label with given selector is not an empty string. + */ + function checkLabelNotEmpty(selector) { + info("Checking that label " + selector + " is non-empty."); + + let element = $(selector); + + ok(element, "Selector matched an element."); + isnot(element.value, "", "Label was not empty."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_security-error.js b/devtools/client/netmonitor/test/browser_net_security-error.js new file mode 100644 index 000000000..f6b8b34f3 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_security-error.js @@ -0,0 +1,70 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Test that Security details tab shows an error message with broken connections. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + let { $, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + RequestsMenu.lazyUpdate = false; + + info("Requesting a resource that has a certificate problem."); + + let wait = waitForSecurityBrokenNetworkEvent(); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(1, "https://nocert.example.com"); + }); + yield wait; + + info("Selecting the request."); + RequestsMenu.selectedIndex = 0; + + info("Waiting for details pane to be updated."); + yield monitor.panelWin.once(EVENTS.TAB_UPDATED); + + info("Selecting security tab."); + NetworkDetails.widget.selectedIndex = 5; + + info("Waiting for security tab to be updated."); + yield monitor.panelWin.once(EVENTS.TAB_UPDATED); + + let errorbox = $("#security-error"); + let errormsg = $("#security-error-message"); + let infobox = $("#security-information"); + + is(errorbox.hidden, false, "Error box is visble."); + is(infobox.hidden, true, "Information box is hidden."); + + isnot(errormsg.value, "", "Error message is not empty."); + + return teardown(monitor); + + /** + * Returns a promise that's resolved once a request with security issues is + * completed. + */ + function waitForSecurityBrokenNetworkEvent() { + let awaitedEvents = [ + "UPDATING_REQUEST_HEADERS", + "RECEIVED_REQUEST_HEADERS", + "UPDATING_REQUEST_COOKIES", + "RECEIVED_REQUEST_COOKIES", + "STARTED_RECEIVING_RESPONSE", + "UPDATING_RESPONSE_CONTENT", + "RECEIVED_RESPONSE_CONTENT", + "UPDATING_EVENT_TIMINGS", + "RECEIVED_EVENT_TIMINGS", + ]; + + let promises = awaitedEvents.map((event) => { + return monitor.panelWin.once(EVENTS[event]); + }); + + return Promise.all(promises); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_security-icon-click.js b/devtools/client/netmonitor/test/browser_net_security-icon-click.js new file mode 100644 index 000000000..2385b11aa --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_security-icon-click.js @@ -0,0 +1,57 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Test that clicking on the security indicator opens the security details tab. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + let { $, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + RequestsMenu.lazyUpdate = false; + + info("Requesting a resource over HTTPS."); + yield performRequestAndWait("https://example.com" + CORS_SJS_PATH + "?request_2"); + yield performRequestAndWait("https://example.com" + CORS_SJS_PATH + "?request_1"); + + is(RequestsMenu.itemCount, 2, "Two events event logged."); + + yield clickAndTestSecurityIcon(); + + info("Selecting headers panel again."); + NetworkDetails.widget.selectedIndex = 0; + yield monitor.panelWin.once(EVENTS.TAB_UPDATED); + + info("Sorting the items by filename."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-file-button")); + + info("Testing that security icon can be clicked after the items were sorted."); + yield clickAndTestSecurityIcon(); + + return teardown(monitor); + + function* performRequestAndWait(url) { + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, { url }, function* (args) { + content.wrappedJSObject.performRequests(1, args.url); + }); + return wait; + } + + function* clickAndTestSecurityIcon() { + let item = RequestsMenu.items[0]; + let icon = $(".requests-security-state-icon", item.target); + + info("Clicking security icon of the first request and waiting for the " + + "panel to update."); + + icon.click(); + yield monitor.panelWin.once(EVENTS.TAB_UPDATED); + + is(NetworkDetails.widget.selectedPanel, $("#security-tabpanel"), + "Security tab is selected."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_security-redirect.js b/devtools/client/netmonitor/test/browser_net_security-redirect.js new file mode 100644 index 000000000..5f2956dbb --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_security-redirect.js @@ -0,0 +1,38 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Test a http -> https redirect shows secure icon only for redirected https + * request. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + let { $, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 2); + yield ContentTask.spawn(tab.linkedBrowser, HTTPS_REDIRECT_SJS, function* (url) { + content.wrappedJSObject.performRequests(1, url); + }); + yield wait; + + is(RequestsMenu.itemCount, 2, "There were two requests due to redirect."); + + let initial = RequestsMenu.items[0]; + let redirect = RequestsMenu.items[1]; + + let initialSecurityIcon = $(".requests-security-state-icon", initial.target); + let redirectSecurityIcon = $(".requests-security-state-icon", redirect.target); + + ok(initialSecurityIcon.classList.contains("security-state-insecure"), + "Initial request was marked insecure."); + + ok(redirectSecurityIcon.classList.contains("security-state-secure"), + "Redirected request was marked secure."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_security-state.js b/devtools/client/netmonitor/test/browser_net_security-state.js new file mode 100644 index 000000000..054e7c969 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_security-state.js @@ -0,0 +1,119 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Test that correct security state indicator appears depending on the security + * state. + */ + +add_task(function* () { + const EXPECTED_SECURITY_STATES = { + "test1.example.com": "security-state-insecure", + "example.com": "security-state-secure", + "nocert.example.com": "security-state-broken", + "localhost": "security-state-local", + }; + + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + let { $, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + RequestsMenu.lazyUpdate = false; + + yield performRequests(); + + for (let item of RequestsMenu.items) { + let domain = $(".requests-menu-domain", item.target).value; + + info("Found a request to " + domain); + ok(domain in EXPECTED_SECURITY_STATES, "Domain " + domain + " was expected."); + + let classes = $(".requests-security-state-icon", item.target).classList; + let expectedClass = EXPECTED_SECURITY_STATES[domain]; + + info("Classes of security state icon are: " + classes); + info("Security state icon is expected to contain class: " + expectedClass); + ok(classes.contains(expectedClass), "Icon contained the correct class name."); + } + + return teardown(monitor); + + /** + * A helper that performs requests to + * - https://nocert.example.com (broken) + * - https://example.com (secure) + * - http://test1.example.com (insecure) + * - http://localhost (local) + * and waits until NetworkMonitor has handled all packets sent by the server. + */ + function* performRequests() { + function executeRequests(count, url) { + return ContentTask.spawn(tab.linkedBrowser, {count, url}, function* (args) { + content.wrappedJSObject.performRequests(args.count, args.url); + }); + } + + // waitForNetworkEvents does not work for requests with security errors as + // those only emit 9/13 events of a successful request. + let done = waitForSecurityBrokenNetworkEvent(); + + info("Requesting a resource that has a certificate problem."); + yield executeRequests(1, "https://nocert.example.com"); + + // Wait for the request to complete before firing another request. Otherwise + // the request with security issues interfere with waitForNetworkEvents. + info("Waiting for request to complete."); + yield done; + + // Next perform a request over HTTP. If done the other way around the latter + // occasionally hangs waiting for event timings that don't seem to appear... + done = waitForNetworkEvents(monitor, 1); + info("Requesting a resource over HTTP."); + yield executeRequests(1, "http://test1.example.com" + CORS_SJS_PATH); + yield done; + + done = waitForNetworkEvents(monitor, 1); + info("Requesting a resource over HTTPS."); + yield executeRequests(1, "https://example.com" + CORS_SJS_PATH); + yield done; + + done = waitForSecurityBrokenNetworkEvent(true); + info("Requesting a resource over HTTP to localhost."); + yield executeRequests(1, "http://localhost" + CORS_SJS_PATH); + yield done; + + const expectedCount = Object.keys(EXPECTED_SECURITY_STATES).length; + is(RequestsMenu.itemCount, expectedCount, expectedCount + " events logged."); + } + + /** + * Returns a promise that's resolved once a request with security issues is + * completed. + */ + function waitForSecurityBrokenNetworkEvent(networkError) { + let awaitedEvents = [ + "UPDATING_REQUEST_HEADERS", + "RECEIVED_REQUEST_HEADERS", + "UPDATING_REQUEST_COOKIES", + "RECEIVED_REQUEST_COOKIES", + "STARTED_RECEIVING_RESPONSE", + "UPDATING_RESPONSE_CONTENT", + "RECEIVED_RESPONSE_CONTENT", + "UPDATING_EVENT_TIMINGS", + "RECEIVED_EVENT_TIMINGS", + ]; + + // If the reason for breakage is a network error, then the + // STARTED_RECEIVING_RESPONSE event does not fire. + if (networkError) { + awaitedEvents = awaitedEvents.filter(e => e !== "STARTED_RECEIVING_RESPONSE"); + } + + let promises = awaitedEvents.map((event) => { + return monitor.panelWin.once(EVENTS[event]); + }); + + return Promise.all(promises); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_security-tab-deselect.js b/devtools/client/netmonitor/test/browser_net_security-tab-deselect.js new file mode 100644 index 000000000..4a2dd0885 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_security-tab-deselect.js @@ -0,0 +1,46 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Test that security details tab is no longer selected if an insecure request + * is selected. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + let { EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + RequestsMenu.lazyUpdate = false; + + info("Performing requests."); + let wait = waitForNetworkEvents(monitor, 2); + const REQUEST_URLS = [ + "https://example.com" + CORS_SJS_PATH, + "http://example.com" + CORS_SJS_PATH, + ]; + yield ContentTask.spawn(tab.linkedBrowser, REQUEST_URLS, function* (urls) { + for (let url of urls) { + content.wrappedJSObject.performRequests(1, url); + } + }); + yield wait; + + info("Selecting secure request."); + RequestsMenu.selectedIndex = 0; + + info("Selecting security tab."); + NetworkDetails.widget.selectedIndex = 5; + + info("Selecting insecure request."); + RequestsMenu.selectedIndex = 1; + + info("Waiting for security tab to be updated."); + yield monitor.panelWin.once(EVENTS.NETWORKDETAILSVIEW_POPULATED); + + is(NetworkDetails.widget.selectedIndex, 0, + "Selected tab was reset when selected security tab was hidden."); + + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_security-tab-visibility.js b/devtools/client/netmonitor/test/browser_net_security-tab-visibility.js new file mode 100644 index 000000000..b6685d7fe --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_security-tab-visibility.js @@ -0,0 +1,121 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Test that security details tab is visible only when it should. + */ + +add_task(function* () { + const TEST_DATA = [ + { + desc: "http request", + uri: "http://example.com" + CORS_SJS_PATH, + visibleOnNewEvent: false, + visibleOnSecurityInfo: false, + visibleOnceComplete: false, + }, { + desc: "working https request", + uri: "https://example.com" + CORS_SJS_PATH, + visibleOnNewEvent: false, + visibleOnSecurityInfo: true, + visibleOnceComplete: true, + }, { + desc: "broken https request", + uri: "https://nocert.example.com", + isBroken: true, + visibleOnNewEvent: false, + visibleOnSecurityInfo: true, + visibleOnceComplete: true, + } + ]; + + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + let { $, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + RequestsMenu.lazyUpdate = false; + + for (let testcase of TEST_DATA) { + info("Testing Security tab visibility for " + testcase.desc); + let onNewItem = monitor.panelWin.once(EVENTS.NETWORK_EVENT); + let onSecurityInfo = monitor.panelWin.once(EVENTS.RECEIVED_SECURITY_INFO); + let onComplete = testcase.isBroken ? + waitForSecurityBrokenNetworkEvent() : + waitForNetworkEvents(monitor, 1); + + let tabEl = $("#security-tab"); + let tabpanel = $("#security-tabpanel"); + + info("Performing a request to " + testcase.uri); + yield ContentTask.spawn(tab.linkedBrowser, testcase.uri, function* (url) { + content.wrappedJSObject.performRequests(1, url); + }); + + info("Waiting for new network event."); + yield onNewItem; + + info("Selecting the request."); + RequestsMenu.selectedIndex = 0; + + is(RequestsMenu.selectedItem.attachment.securityState, undefined, + "Security state has not yet arrived."); + is(tabEl.hidden, !testcase.visibleOnNewEvent, + "Security tab is " + + (testcase.visibleOnNewEvent ? "visible" : "hidden") + + " after new request was added to the menu."); + is(tabpanel.hidden, false, + "#security-tabpanel is visible after new request was added to the menu."); + + info("Waiting for security information to arrive."); + yield onSecurityInfo; + + ok(RequestsMenu.selectedItem.attachment.securityState, + "Security state arrived."); + is(tabEl.hidden, !testcase.visibleOnSecurityInfo, + "Security tab is " + + (testcase.visibleOnSecurityInfo ? "visible" : "hidden") + + " after security information arrived."); + is(tabpanel.hidden, false, + "#security-tabpanel is visible after security information arrived."); + + info("Waiting for request to complete."); + yield onComplete; + + is(tabEl.hidden, !testcase.visibleOnceComplete, + "Security tab is " + + (testcase.visibleOnceComplete ? "visible" : "hidden") + + " after request has been completed."); + is(tabpanel.hidden, false, + "#security-tabpanel is visible after request is complete."); + + info("Clearing requests."); + RequestsMenu.clear(); + } + + return teardown(monitor); + + /** + * Returns a promise that's resolved once a request with security issues is + * completed. + */ + function waitForSecurityBrokenNetworkEvent() { + let awaitedEvents = [ + "UPDATING_REQUEST_HEADERS", + "RECEIVED_REQUEST_HEADERS", + "UPDATING_REQUEST_COOKIES", + "RECEIVED_REQUEST_COOKIES", + "STARTED_RECEIVING_RESPONSE", + "UPDATING_RESPONSE_CONTENT", + "RECEIVED_RESPONSE_CONTENT", + "UPDATING_EVENT_TIMINGS", + "RECEIVED_EVENT_TIMINGS", + ]; + + let promises = awaitedEvents.map((event) => { + return monitor.panelWin.once(EVENTS[event]); + }); + + return Promise.all(promises); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_security-warnings.js b/devtools/client/netmonitor/test/browser_net_security-warnings.js new file mode 100644 index 000000000..cdfee70a1 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_security-warnings.js @@ -0,0 +1,56 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Test that warning indicators are shown when appropriate. + */ + +const TEST_CASES = [ + { + desc: "no warnings", + uri: "https://example.com" + CORS_SJS_PATH, + warnCipher: false, + }, +]; + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + let { $, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + RequestsMenu.lazyUpdate = false; + + let cipher = $("#security-warning-cipher"); + + for (let test of TEST_CASES) { + info("Testing site with " + test.desc); + + info("Performing request to " + test.uri); + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, test.uri, function* (url) { + content.wrappedJSObject.performRequests(1, url); + }); + yield wait; + + info("Selecting the request."); + RequestsMenu.selectedIndex = 0; + + info("Waiting for details pane to be updated."); + yield monitor.panelWin.once(EVENTS.TAB_UPDATED); + + if (NetworkDetails.widget.selectedIndex !== 5) { + info("Selecting security tab."); + NetworkDetails.widget.selectedIndex = 5; + + info("Waiting for details pane to be updated."); + yield monitor.panelWin.once(EVENTS.TAB_UPDATED); + } + + is(cipher.hidden, !test.warnCipher, "Cipher suite warning is hidden."); + + RequestsMenu.clear(); + } + + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_send-beacon-other-tab.js b/devtools/client/netmonitor/test/browser_net_send-beacon-other-tab.js new file mode 100644 index 000000000..b425ad5ca --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_send-beacon-other-tab.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if beacons from other tabs are properly ignored. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(SIMPLE_URL); + let { RequestsMenu } = monitor.panelWin.NetMonitorView; + RequestsMenu.lazyUpdate = false; + + let beaconTab = yield addTab(SEND_BEACON_URL); + info("Beacon tab added successfully."); + + is(RequestsMenu.itemCount, 0, "The requests menu should be empty."); + + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(beaconTab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequest(); + }); + tab.linkedBrowser.reload(); + yield wait; + + is(RequestsMenu.itemCount, 1, "Only the reload should be recorded."); + let request = RequestsMenu.getItemAtIndex(0); + is(request.attachment.method, "GET", "The method is correct."); + is(request.attachment.status, "200", "The status is correct."); + + yield removeTab(beaconTab); + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_send-beacon.js b/devtools/client/netmonitor/test/browser_net_send-beacon.js new file mode 100644 index 000000000..bdc30a960 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_send-beacon.js @@ -0,0 +1,31 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if beacons are handled correctly. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(SEND_BEACON_URL); + let { RequestsMenu } = monitor.panelWin.NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + is(RequestsMenu.itemCount, 0, "The requests menu should be empty."); + + let wait = waitForNetworkEvents(monitor, 1); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequest(); + }); + yield wait; + + is(RequestsMenu.itemCount, 1, "The beacon should be recorded."); + let request = RequestsMenu.getItemAtIndex(0); + is(request.attachment.method, "POST", "The method is correct."); + ok(request.attachment.url.endsWith("beacon_request"), "The URL is correct."); + is(request.attachment.status, "404", "The status is correct."); + + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_service-worker-status.js b/devtools/client/netmonitor/test/browser_net_service-worker-status.js new file mode 100644 index 000000000..d7ada1645 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_service-worker-status.js @@ -0,0 +1,87 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Tests if requests intercepted by service workers have the correct status code + */ + +// Service workers only work on https +const URL = EXAMPLE_URL.replace("http:", "https:"); + +const TEST_URL = URL + "service-workers/status-codes.html"; + +add_task(function* () { + yield new Promise(done => { + let options = { "set": [ + // Accept workers from mochitest's http. + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.openWindow.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}; + SpecialPowers.pushPrefEnv(options, done); + }); + + let { tab, monitor } = yield initNetMonitor(TEST_URL, null, true); + info("Starting test... "); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + const REQUEST_DATA = [ + { + method: "GET", + uri: URL + "service-workers/test/200", + details: { + status: 200, + statusText: "OK (service worker)", + displayedStatus: "service worker", + type: "plain", + fullMimeType: "text/plain; charset=UTF-8" + }, + stackFunctions: ["doXHR", "performRequests"] + }, + ]; + + info("Registering the service worker..."); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + yield content.wrappedJSObject.registerServiceWorker(); + }); + + info("Performing requests..."); + let wait = waitForNetworkEvents(monitor, REQUEST_DATA.length); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + let index = 0; + for (let request of REQUEST_DATA) { + let item = RequestsMenu.getItemAtIndex(index); + + info(`Verifying request #${index}`); + yield verifyRequestItemTarget(item, request.method, request.uri, request.details); + + let { stacktrace } = item.attachment.cause; + let stackLen = stacktrace ? stacktrace.length : 0; + + ok(stacktrace, `Request #${index} has a stacktrace`); + ok(stackLen >= request.stackFunctions.length, + `Request #${index} has a stacktrace with enough (${stackLen}) items`); + + request.stackFunctions.forEach((functionName, j) => { + is(stacktrace[j].functionName, functionName, + `Request #${index} has the correct function at position #${j} on the stack`); + }); + + index++; + } + + info("Unregistering the service worker..."); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + yield content.wrappedJSObject.unregisterServiceWorker(); + }); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_simple-init.js b/devtools/client/netmonitor/test/browser_net_simple-init.js new file mode 100644 index 000000000..19d05811c --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_simple-init.js @@ -0,0 +1,93 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Simple check if the network monitor starts up and shuts down properly. + */ + +function test() { + // These test suite functions are removed from the global scope inside a + // cleanup function. However, we still need them. + let gInfo = info; + let gOk = ok; + + initNetMonitor(SIMPLE_URL).then(({ tab, monitor }) => { + info("Starting test... "); + + is(tab.linkedBrowser.currentURI.spec, SIMPLE_URL, + "The current tab's location is the correct one."); + + function checkIfInitialized(tag) { + info(`Checking if initialization is ok (${tag}).`); + + ok(monitor._view, + `The network monitor view object exists (${tag}).`); + ok(monitor._controller, + `The network monitor controller object exists (${tag}).`); + ok(monitor._controller._startup, + `The network monitor controller object exists and is initialized (${tag}).`); + + ok(monitor.isReady, + `The network monitor panel appears to be ready (${tag}).`); + + ok(monitor._controller.tabClient, + `There should be a tabClient available at this point (${tag}).`); + ok(monitor._controller.webConsoleClient, + `There should be a webConsoleClient available at this point (${tag}).`); + ok(monitor._controller.timelineFront, + `There should be a timelineFront available at this point (${tag}).`); + } + + function checkIfDestroyed(tag) { + gInfo("Checking if destruction is ok."); + + gOk(monitor._view, + `The network monitor view object still exists (${tag}).`); + gOk(monitor._controller, + `The network monitor controller object still exists (${tag}).`); + gOk(monitor._controller._shutdown, + `The network monitor controller object still exists and is destroyed (${tag}).`); + + gOk(!monitor._controller.tabClient, + `There shouldn't be a tabClient available after destruction (${tag}).`); + gOk(!monitor._controller.webConsoleClient, + `There shouldn't be a webConsoleClient available after destruction (${tag}).`); + gOk(!monitor._controller.timelineFront, + `There shouldn't be a timelineFront available after destruction (${tag}).`); + } + + executeSoon(() => { + checkIfInitialized(1); + + monitor._controller.startupNetMonitor() + .then(() => { + info("Starting up again shouldn't do anything special."); + checkIfInitialized(2); + return monitor._controller.connect(); + }) + .then(() => { + info("Connecting again shouldn't do anything special."); + checkIfInitialized(3); + return teardown(monitor); + }) + .then(finish); + }); + + registerCleanupFunction(() => { + checkIfDestroyed(1); + + monitor._controller.shutdownNetMonitor() + .then(() => { + gInfo("Shutting down again shouldn't do anything special."); + checkIfDestroyed(2); + return monitor._controller.disconnect(); + }) + .then(() => { + gInfo("Disconnecting again shouldn't do anything special."); + checkIfDestroyed(3); + }); + }); + }); +} diff --git a/devtools/client/netmonitor/test/browser_net_simple-request-data.js b/devtools/client/netmonitor/test/browser_net_simple-request-data.js new file mode 100644 index 000000000..1b952bd71 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_simple-request-data.js @@ -0,0 +1,247 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if requests render correct information in the menu UI. + */ + +function test() { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + initNetMonitor(SIMPLE_SJS).then(({ tab, monitor }) => { + info("Starting test... "); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + waitForNetworkEvents(monitor, 1) + .then(() => teardown(monitor)) + .then(finish); + + monitor.panelWin.once(monitor.panelWin.EVENTS.NETWORK_EVENT, () => { + is(RequestsMenu.selectedItem, null, + "There shouldn't be any selected item in the requests menu."); + is(RequestsMenu.itemCount, 1, + "The requests menu should not be empty after the first request."); + is(NetMonitorView.detailsPaneHidden, true, + "The details pane should still be hidden after the first request."); + + let requestItem = RequestsMenu.getItemAtIndex(0); + + is(typeof requestItem.value, "string", + "The attached request id is incorrect."); + isnot(requestItem.value, "", + "The attached request id should not be empty."); + + is(typeof requestItem.attachment.startedDeltaMillis, "number", + "The attached startedDeltaMillis is incorrect."); + is(requestItem.attachment.startedDeltaMillis, 0, + "The attached startedDeltaMillis should be zero."); + + is(typeof requestItem.attachment.startedMillis, "number", + "The attached startedMillis is incorrect."); + isnot(requestItem.attachment.startedMillis, 0, + "The attached startedMillis should not be zero."); + + is(requestItem.attachment.requestHeaders, undefined, + "The requestHeaders should not yet be set."); + is(requestItem.attachment.requestCookies, undefined, + "The requestCookies should not yet be set."); + is(requestItem.attachment.requestPostData, undefined, + "The requestPostData should not yet be set."); + + is(requestItem.attachment.responseHeaders, undefined, + "The responseHeaders should not yet be set."); + is(requestItem.attachment.responseCookies, undefined, + "The responseCookies should not yet be set."); + + is(requestItem.attachment.httpVersion, undefined, + "The httpVersion should not yet be set."); + is(requestItem.attachment.status, undefined, + "The status should not yet be set."); + is(requestItem.attachment.statusText, undefined, + "The statusText should not yet be set."); + + is(requestItem.attachment.headersSize, undefined, + "The headersSize should not yet be set."); + is(requestItem.attachment.transferredSize, undefined, + "The transferredSize should not yet be set."); + is(requestItem.attachment.contentSize, undefined, + "The contentSize should not yet be set."); + + is(requestItem.attachment.mimeType, undefined, + "The mimeType should not yet be set."); + is(requestItem.attachment.responseContent, undefined, + "The responseContent should not yet be set."); + + is(requestItem.attachment.totalTime, undefined, + "The totalTime should not yet be set."); + is(requestItem.attachment.eventTimings, undefined, + "The eventTimings should not yet be set."); + + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS); + }); + + monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_HEADERS, () => { + let requestItem = RequestsMenu.getItemAtIndex(0); + + ok(requestItem.attachment.requestHeaders, + "There should be a requestHeaders attachment available."); + is(requestItem.attachment.requestHeaders.headers.length, 9, + "The requestHeaders attachment has an incorrect |headers| property."); + isnot(requestItem.attachment.requestHeaders.headersSize, 0, + "The requestHeaders attachment has an incorrect |headersSize| property."); + // Can't test for the exact request headers size because the value may + // vary across platforms ("User-Agent" header differs). + + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS); + }); + + monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_COOKIES, () => { + let requestItem = RequestsMenu.getItemAtIndex(0); + + ok(requestItem.attachment.requestCookies, + "There should be a requestCookies attachment available."); + is(requestItem.attachment.requestCookies.cookies.length, 2, + "The requestCookies attachment has an incorrect |cookies| property."); + + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS); + }); + + monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_POST_DATA, () => { + ok(false, "Trap listener: this request doesn't have any post data."); + }); + + monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_HEADERS, () => { + let requestItem = RequestsMenu.getItemAtIndex(0); + + ok(requestItem.attachment.responseHeaders, + "There should be a responseHeaders attachment available."); + is(requestItem.attachment.responseHeaders.headers.length, 10, + "The responseHeaders attachment has an incorrect |headers| property."); + is(requestItem.attachment.responseHeaders.headersSize, 330, + "The responseHeaders attachment has an incorrect |headersSize| property."); + + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS); + }); + + monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_COOKIES, () => { + let requestItem = RequestsMenu.getItemAtIndex(0); + + ok(requestItem.attachment.responseCookies, + "There should be a responseCookies attachment available."); + is(requestItem.attachment.responseCookies.cookies.length, 2, + "The responseCookies attachment has an incorrect |cookies| property."); + + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS); + }); + + monitor.panelWin.once(monitor.panelWin.EVENTS.STARTED_RECEIVING_RESPONSE, () => { + let requestItem = RequestsMenu.getItemAtIndex(0); + + is(requestItem.attachment.httpVersion, "HTTP/1.1", + "The httpVersion attachment has an incorrect value."); + is(requestItem.attachment.status, "200", + "The status attachment has an incorrect value."); + is(requestItem.attachment.statusText, "Och Aye", + "The statusText attachment has an incorrect value."); + is(requestItem.attachment.headersSize, 330, + "The headersSize attachment has an incorrect value."); + + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { + status: "200", + statusText: "Och Aye" + }); + }); + + monitor.panelWin.once(monitor.panelWin.EVENTS.UPDATING_RESPONSE_CONTENT, () => { + let requestItem = RequestsMenu.getItemAtIndex(0); + + is(requestItem.attachment.transferredSize, "12", + "The transferredSize attachment has an incorrect value."); + is(requestItem.attachment.contentSize, "12", + "The contentSize attachment has an incorrect value."); + is(requestItem.attachment.mimeType, "text/plain; charset=utf-8", + "The mimeType attachment has an incorrect value."); + + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01), + }); + }); + + monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_CONTENT, () => { + let requestItem = RequestsMenu.getItemAtIndex(0); + + ok(requestItem.attachment.responseContent, + "There should be a responseContent attachment available."); + is(requestItem.attachment.responseContent.content.mimeType, + "text/plain; charset=utf-8", + "The responseContent attachment has an incorrect |content.mimeType| property."); + is(requestItem.attachment.responseContent.content.text, + "Hello world!", + "The responseContent attachment has an incorrect |content.text| property."); + is(requestItem.attachment.responseContent.content.size, + 12, + "The responseContent attachment has an incorrect |content.size| property."); + + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01), + }); + }); + + monitor.panelWin.once(monitor.panelWin.EVENTS.UPDATING_EVENT_TIMINGS, () => { + let requestItem = RequestsMenu.getItemAtIndex(0); + + is(typeof requestItem.attachment.totalTime, "number", + "The attached totalTime is incorrect."); + ok(requestItem.attachment.totalTime >= 0, + "The attached totalTime should be positive."); + + is(typeof requestItem.attachment.endedMillis, "number", + "The attached endedMillis is incorrect."); + ok(requestItem.attachment.endedMillis >= 0, + "The attached endedMillis should be positive."); + + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { + time: true + }); + }); + + monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_EVENT_TIMINGS, () => { + let requestItem = RequestsMenu.getItemAtIndex(0); + + ok(requestItem.attachment.eventTimings, + "There should be a eventTimings attachment available."); + is(typeof requestItem.attachment.eventTimings.timings.blocked, "number", + "The eventTimings attachment has an incorrect |timings.blocked| property."); + is(typeof requestItem.attachment.eventTimings.timings.dns, "number", + "The eventTimings attachment has an incorrect |timings.dns| property."); + is(typeof requestItem.attachment.eventTimings.timings.connect, "number", + "The eventTimings attachment has an incorrect |timings.connect| property."); + is(typeof requestItem.attachment.eventTimings.timings.send, "number", + "The eventTimings attachment has an incorrect |timings.send| property."); + is(typeof requestItem.attachment.eventTimings.timings.wait, "number", + "The eventTimings attachment has an incorrect |timings.wait| property."); + is(typeof requestItem.attachment.eventTimings.timings.receive, "number", + "The eventTimings attachment has an incorrect |timings.receive| property."); + is(typeof requestItem.attachment.eventTimings.totalTime, "number", + "The eventTimings attachment has an incorrect |totalTime| property."); + + verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { + time: true + }); + }); + + tab.linkedBrowser.reload(); + }); +} diff --git a/devtools/client/netmonitor/test/browser_net_simple-request-details.js b/devtools/client/netmonitor/test/browser_net_simple-request-details.js new file mode 100644 index 000000000..6be634e68 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_simple-request-details.js @@ -0,0 +1,261 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if requests render correct information in the details UI. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(SIMPLE_SJS); + info("Starting test... "); + + let { document, EVENTS, Editor, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 1); + tab.linkedBrowser.reload(); + yield wait; + + is(RequestsMenu.selectedItem, null, + "There shouldn't be any selected item in the requests menu."); + is(RequestsMenu.itemCount, 1, + "The requests menu should not be empty after the first request."); + is(NetMonitorView.detailsPaneHidden, true, + "The details pane should still be hidden after the first request."); + + let onTabUpdated = monitor.panelWin.once(EVENTS.TAB_UPDATED); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + yield onTabUpdated; + + isnot(RequestsMenu.selectedItem, null, + "There should be a selected item in the requests menu."); + is(RequestsMenu.selectedIndex, 0, + "The first item should be selected in the requests menu."); + is(NetMonitorView.detailsPaneHidden, false, + "The details pane should not be hidden after toggle button was pressed."); + + testHeadersTab(); + yield testCookiesTab(); + testParamsTab(); + yield testResponseTab(); + testTimingsTab(); + return teardown(monitor); + + function testHeadersTab() { + let tabEl = document.querySelectorAll("#details-pane tab")[0]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[0]; + + is(tabEl.getAttribute("selected"), "true", + "The headers tab in the network details pane should be selected."); + + is(tabpanel.querySelector("#headers-summary-url-value").getAttribute("value"), + SIMPLE_SJS, "The url summary value is incorrect."); + is(tabpanel.querySelector("#headers-summary-url-value").getAttribute("tooltiptext"), + SIMPLE_SJS, "The url summary tooltiptext is incorrect."); + is(tabpanel.querySelector("#headers-summary-method-value").getAttribute("value"), + "GET", "The method summary value is incorrect."); + is(tabpanel.querySelector("#headers-summary-address-value").getAttribute("value"), + "127.0.0.1:8888", "The remote address summary value is incorrect."); + is(tabpanel.querySelector("#headers-summary-status-circle").getAttribute("code"), + "200", "The status summary code is incorrect."); + is(tabpanel.querySelector("#headers-summary-status-value").getAttribute("value"), + "200 Och Aye", "The status summary value is incorrect."); + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 2, + "There should be 2 header scopes displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variable-or-property").length, 19, + "There should be 19 header values displayed in this tabpanel."); + + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0, + "The empty notice should not be displayed in this tabpanel."); + + let responseScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + let requestScope = tabpanel.querySelectorAll(".variables-view-scope")[1]; + + is(responseScope.querySelector(".name").getAttribute("value"), + L10N.getStr("responseHeaders") + " (" + + L10N.getFormatStr("networkMenu.sizeKB", + L10N.numberWithDecimals(330 / 1024, 3)) + ")", + "The response headers scope doesn't have the correct title."); + + ok(requestScope.querySelector(".name").getAttribute("value").includes( + L10N.getStr("requestHeaders") + " (0"), + "The request headers scope doesn't have the correct title."); + // Can't test for full request headers title because the size may + // vary across platforms ("User-Agent" header differs). We're pretty + // sure it's smaller than 1 MB though, so it starts with a 0. + + is(responseScope.querySelectorAll(".variables-view-variable .name")[0] + .getAttribute("value"), + "Cache-Control", "The first response header name was incorrect."); + is(responseScope.querySelectorAll(".variables-view-variable .value")[0] + .getAttribute("value"), + "\"no-cache, no-store, must-revalidate\"", + "The first response header value was incorrect."); + is(responseScope.querySelectorAll(".variables-view-variable .name")[1] + .getAttribute("value"), + "Connection", "The second response header name was incorrect."); + is(responseScope.querySelectorAll(".variables-view-variable .value")[1] + .getAttribute("value"), + "\"close\"", "The second response header value was incorrect."); + is(responseScope.querySelectorAll(".variables-view-variable .name")[2] + .getAttribute("value"), + "Content-Length", "The third response header name was incorrect."); + is(responseScope.querySelectorAll(".variables-view-variable .value")[2] + .getAttribute("value"), + "\"12\"", "The third response header value was incorrect."); + is(responseScope.querySelectorAll(".variables-view-variable .name")[3] + .getAttribute("value"), + "Content-Type", "The fourth response header name was incorrect."); + is(responseScope.querySelectorAll(".variables-view-variable .value")[3] + .getAttribute("value"), + "\"text/plain; charset=utf-8\"", "The fourth response header value was incorrect."); + is(responseScope.querySelectorAll(".variables-view-variable .name")[9] + .getAttribute("value"), + "foo-bar", "The last response header name was incorrect."); + is(responseScope.querySelectorAll(".variables-view-variable .value")[9] + .getAttribute("value"), + "\"baz\"", "The last response header value was incorrect."); + + is(requestScope.querySelectorAll(".variables-view-variable .name")[0] + .getAttribute("value"), + "Host", "The first request header name was incorrect."); + is(requestScope.querySelectorAll(".variables-view-variable .value")[0] + .getAttribute("value"), + "\"example.com\"", "The first request header value was incorrect."); + is(requestScope.querySelectorAll(".variables-view-variable .name")[6] + .getAttribute("value"), + "Connection", "The ante-penultimate request header name was incorrect."); + is(requestScope.querySelectorAll(".variables-view-variable .value")[6] + .getAttribute("value"), + "\"keep-alive\"", "The ante-penultimate request header value was incorrect."); + is(requestScope.querySelectorAll(".variables-view-variable .name")[7] + .getAttribute("value"), + "Pragma", "The penultimate request header name was incorrect."); + is(requestScope.querySelectorAll(".variables-view-variable .value")[7] + .getAttribute("value"), + "\"no-cache\"", "The penultimate request header value was incorrect."); + is(requestScope.querySelectorAll(".variables-view-variable .name")[8] + .getAttribute("value"), + "Cache-Control", "The last request header name was incorrect."); + is(requestScope.querySelectorAll(".variables-view-variable .value")[8] + .getAttribute("value"), + "\"no-cache\"", "The last request header value was incorrect."); + } + + function* testCookiesTab() { + let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[1]); + yield onEvent; + + let tabEl = document.querySelectorAll("#details-pane tab")[1]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[1]; + + is(tabEl.getAttribute("selected"), "true", + "The cookies tab in the network details pane should be selected."); + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 2, + "There should be 2 cookie scopes displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variable-or-property").length, 6, + "There should be 6 cookie values displayed in this tabpanel."); + } + + function testParamsTab() { + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[2]); + + let tabEl = document.querySelectorAll("#details-pane tab")[2]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2]; + + is(tabEl.getAttribute("selected"), "true", + "The params tab in the network details pane should be selected."); + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 0, + "There should be no param scopes displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variable-or-property").length, 0, + "There should be no param values displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 1, + "The empty notice should be displayed in this tabpanel."); + + is(tabpanel.querySelector("#request-params-box") + .hasAttribute("hidden"), false, + "The request params box should not be hidden."); + is(tabpanel.querySelector("#request-post-data-textarea-box") + .hasAttribute("hidden"), true, + "The request post data textarea box should be hidden."); + } + + function* testResponseTab() { + let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[3]); + yield onEvent; + + let tabEl = document.querySelectorAll("#details-pane tab")[3]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3]; + + is(tabEl.getAttribute("selected"), "true", + "The response tab in the network details pane should be selected."); + + is(tabpanel.querySelector("#response-content-info-header") + .hasAttribute("hidden"), true, + "The response info header should be hidden."); + is(tabpanel.querySelector("#response-content-json-box") + .hasAttribute("hidden"), true, + "The response content json box should be hidden."); + is(tabpanel.querySelector("#response-content-textarea-box") + .hasAttribute("hidden"), false, + "The response content textarea box should not be hidden."); + is(tabpanel.querySelector("#response-content-image-box") + .hasAttribute("hidden"), true, + "The response content image box should be hidden."); + + let editor = yield NetMonitorView.editor("#response-content-textarea"); + is(editor.getText(), "Hello world!", + "The text shown in the source editor is incorrect."); + is(editor.getMode(), Editor.modes.text, + "The mode active in the source editor is incorrect."); + } + + function testTimingsTab() { + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[4]); + + let tabEl = document.querySelectorAll("#details-pane tab")[4]; + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[4]; + + is(tabEl.getAttribute("selected"), "true", + "The timings tab in the network details pane should be selected."); + + ok(tabpanel.querySelector("#timings-summary-blocked .requests-menu-timings-total") + .getAttribute("value").match(/[0-9]+/), + "The blocked timing info does not appear to be correct."); + + ok(tabpanel.querySelector("#timings-summary-dns .requests-menu-timings-total") + .getAttribute("value").match(/[0-9]+/), + "The dns timing info does not appear to be correct."); + + ok(tabpanel.querySelector("#timings-summary-connect .requests-menu-timings-total") + .getAttribute("value").match(/[0-9]+/), + "The connect timing info does not appear to be correct."); + + ok(tabpanel.querySelector("#timings-summary-send .requests-menu-timings-total") + .getAttribute("value").match(/[0-9]+/), + "The send timing info does not appear to be correct."); + + ok(tabpanel.querySelector("#timings-summary-wait .requests-menu-timings-total") + .getAttribute("value").match(/[0-9]+/), + "The wait timing info does not appear to be correct."); + + ok(tabpanel.querySelector("#timings-summary-receive .requests-menu-timings-total") + .getAttribute("value").match(/[0-9]+/), + "The receive timing info does not appear to be correct."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_simple-request.js b/devtools/client/netmonitor/test/browser_net_simple-request.js new file mode 100644 index 000000000..898cb3710 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_simple-request.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Test whether the UI state properly reflects existence of requests + * displayed in the Net panel. The following parts of the UI are + * tested: + * 1) Side panel visibility + * 2) Side panel toggle button + * 3) Empty user message visibility + * 4) Number of requests displayed + */ +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { document, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), true, + "The pane toggle button should be disabled when the frontend is opened."); + is(document.querySelector("#requests-menu-empty-notice").hasAttribute("hidden"), false, + "An empty notice should be displayed when the frontend is opened."); + is(RequestsMenu.itemCount, 0, + "The requests menu should be empty when the frontend is opened."); + is(NetMonitorView.detailsPaneHidden, true, + "The details pane should be hidden when the frontend is opened."); + + yield reloadAndWait(); + + is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), false, + "The pane toggle button should be enabled after the first request."); + is(document.querySelector("#requests-menu-empty-notice").hasAttribute("hidden"), true, + "The empty notice should be hidden after the first request."); + is(RequestsMenu.itemCount, 1, + "The requests menu should not be empty after the first request."); + is(NetMonitorView.detailsPaneHidden, true, + "The details pane should still be hidden after the first request."); + + yield reloadAndWait(); + + is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), false, + "The pane toggle button should be still be enabled after a reload."); + is(document.querySelector("#requests-menu-empty-notice").hasAttribute("hidden"), true, + "The empty notice should be still hidden after a reload."); + is(RequestsMenu.itemCount, 1, + "The requests menu should not be empty after a reload."); + is(NetMonitorView.detailsPaneHidden, true, + "The details pane should still be hidden after a reload."); + + RequestsMenu.clear(); + + is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), true, + "The pane toggle button should be disabled when after clear."); + is(document.querySelector("#requests-menu-empty-notice").hasAttribute("hidden"), false, + "An empty notice should be displayed again after clear."); + is(RequestsMenu.itemCount, 0, + "The requests menu should be empty after clear."); + is(NetMonitorView.detailsPaneHidden, true, + "The details pane should be hidden after clear."); + + return teardown(monitor); + + function* reloadAndWait() { + let wait = waitForNetworkEvents(monitor, 1); + tab.linkedBrowser.reload(); + return wait; + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_sort-01.js b/devtools/client/netmonitor/test/browser_net_sort-01.js new file mode 100644 index 000000000..2c4e718dc --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_sort-01.js @@ -0,0 +1,230 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test if the sorting mechanism works correctly. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(STATUS_CODES_URL); + info("Starting test... "); + + let { $all, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 5); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + testContents([0, 1, 2, 3, 4]); + + info("Testing swap(0, 0)"); + RequestsMenu.swapItemsAtIndices(0, 0); + RequestsMenu.refreshZebra(); + testContents([0, 1, 2, 3, 4]); + + info("Testing swap(0, 1)"); + RequestsMenu.swapItemsAtIndices(0, 1); + RequestsMenu.refreshZebra(); + testContents([1, 0, 2, 3, 4]); + + info("Testing swap(0, 2)"); + RequestsMenu.swapItemsAtIndices(0, 2); + RequestsMenu.refreshZebra(); + testContents([1, 2, 0, 3, 4]); + + info("Testing swap(0, 3)"); + RequestsMenu.swapItemsAtIndices(0, 3); + RequestsMenu.refreshZebra(); + testContents([1, 2, 3, 0, 4]); + + info("Testing swap(0, 4)"); + RequestsMenu.swapItemsAtIndices(0, 4); + RequestsMenu.refreshZebra(); + testContents([1, 2, 3, 4, 0]); + + info("Testing swap(1, 0)"); + RequestsMenu.swapItemsAtIndices(1, 0); + RequestsMenu.refreshZebra(); + testContents([0, 2, 3, 4, 1]); + + info("Testing swap(1, 1)"); + RequestsMenu.swapItemsAtIndices(1, 1); + RequestsMenu.refreshZebra(); + testContents([0, 2, 3, 4, 1]); + + info("Testing swap(1, 2)"); + RequestsMenu.swapItemsAtIndices(1, 2); + RequestsMenu.refreshZebra(); + testContents([0, 1, 3, 4, 2]); + + info("Testing swap(1, 3)"); + RequestsMenu.swapItemsAtIndices(1, 3); + RequestsMenu.refreshZebra(); + testContents([0, 3, 1, 4, 2]); + + info("Testing swap(1, 4)"); + RequestsMenu.swapItemsAtIndices(1, 4); + RequestsMenu.refreshZebra(); + testContents([0, 3, 4, 1, 2]); + + info("Testing swap(2, 0)"); + RequestsMenu.swapItemsAtIndices(2, 0); + RequestsMenu.refreshZebra(); + testContents([2, 3, 4, 1, 0]); + + info("Testing swap(2, 1)"); + RequestsMenu.swapItemsAtIndices(2, 1); + RequestsMenu.refreshZebra(); + testContents([1, 3, 4, 2, 0]); + + info("Testing swap(2, 2)"); + RequestsMenu.swapItemsAtIndices(2, 2); + RequestsMenu.refreshZebra(); + testContents([1, 3, 4, 2, 0]); + + info("Testing swap(2, 3)"); + RequestsMenu.swapItemsAtIndices(2, 3); + RequestsMenu.refreshZebra(); + testContents([1, 2, 4, 3, 0]); + + info("Testing swap(2, 4)"); + RequestsMenu.swapItemsAtIndices(2, 4); + RequestsMenu.refreshZebra(); + testContents([1, 4, 2, 3, 0]); + + info("Testing swap(3, 0)"); + RequestsMenu.swapItemsAtIndices(3, 0); + RequestsMenu.refreshZebra(); + testContents([1, 4, 2, 0, 3]); + + info("Testing swap(3, 1)"); + RequestsMenu.swapItemsAtIndices(3, 1); + RequestsMenu.refreshZebra(); + testContents([3, 4, 2, 0, 1]); + + info("Testing swap(3, 2)"); + RequestsMenu.swapItemsAtIndices(3, 2); + RequestsMenu.refreshZebra(); + testContents([2, 4, 3, 0, 1]); + + info("Testing swap(3, 3)"); + RequestsMenu.swapItemsAtIndices(3, 3); + RequestsMenu.refreshZebra(); + testContents([2, 4, 3, 0, 1]); + + info("Testing swap(3, 4)"); + RequestsMenu.swapItemsAtIndices(3, 4); + RequestsMenu.refreshZebra(); + testContents([2, 3, 4, 0, 1]); + + info("Testing swap(4, 0)"); + RequestsMenu.swapItemsAtIndices(4, 0); + RequestsMenu.refreshZebra(); + testContents([2, 3, 0, 4, 1]); + + info("Testing swap(4, 1)"); + RequestsMenu.swapItemsAtIndices(4, 1); + RequestsMenu.refreshZebra(); + testContents([2, 3, 0, 1, 4]); + + info("Testing swap(4, 2)"); + RequestsMenu.swapItemsAtIndices(4, 2); + RequestsMenu.refreshZebra(); + testContents([4, 3, 0, 1, 2]); + + info("Testing swap(4, 3)"); + RequestsMenu.swapItemsAtIndices(4, 3); + RequestsMenu.refreshZebra(); + testContents([3, 4, 0, 1, 2]); + + info("Testing swap(4, 4)"); + RequestsMenu.swapItemsAtIndices(4, 4); + RequestsMenu.refreshZebra(); + testContents([3, 4, 0, 1, 2]); + + info("Clearing sort."); + RequestsMenu.sortBy(); + testContents([0, 1, 2, 3, 4]); + + return teardown(monitor); + + function testContents([a, b, c, d, e]) { + is(RequestsMenu.items.length, 5, + "There should be a total of 5 items in the requests menu."); + is(RequestsMenu.visibleItems.length, 5, + "There should be a total of 5 visbile items in the requests menu."); + is($all(".side-menu-widget-item").length, 5, + "The visible items in the requests menu are, in fact, visible!"); + + is(RequestsMenu.getItemAtIndex(0), RequestsMenu.items[0], + "The requests menu items aren't ordered correctly. First item is misplaced."); + is(RequestsMenu.getItemAtIndex(1), RequestsMenu.items[1], + "The requests menu items aren't ordered correctly. Second item is misplaced."); + is(RequestsMenu.getItemAtIndex(2), RequestsMenu.items[2], + "The requests menu items aren't ordered correctly. Third item is misplaced."); + is(RequestsMenu.getItemAtIndex(3), RequestsMenu.items[3], + "The requests menu items aren't ordered correctly. Fourth item is misplaced."); + is(RequestsMenu.getItemAtIndex(4), RequestsMenu.items[4], + "The requests menu items aren't ordered correctly. Fifth item is misplaced."); + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(a), + "GET", STATUS_CODES_SJS + "?sts=100", { + status: 101, + statusText: "Switching Protocols", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + transferred: L10N.getStr("networkMenu.sizeUnavailable"), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(b), + "GET", STATUS_CODES_SJS + "?sts=200", { + status: 202, + statusText: "Created", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(c), + "GET", STATUS_CODES_SJS + "?sts=300", { + status: 303, + statusText: "See Other", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(d), + "GET", STATUS_CODES_SJS + "?sts=400", { + status: 404, + statusText: "Not Found", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(e), + "GET", STATUS_CODES_SJS + "?sts=500", { + status: 501, + statusText: "Not Implemented", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), + time: true + }); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_sort-02.js b/devtools/client/netmonitor/test/browser_net_sort-02.js new file mode 100644 index 000000000..ce8c69e45 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_sort-02.js @@ -0,0 +1,272 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test if sorting columns in the network table works correctly. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { monitor } = yield initNetMonitor(SORTING_URL); + info("Starting test... "); + + // It seems that this test may be slow on debug builds. This could be because + // of the heavy dom manipulation associated with sorting. + requestLongerTimeout(2); + + let { $, $all, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + // Loading the frame script and preparing the xhr request URLs so we can + // generate some requests later. + loadCommonFrameScript(); + let requests = [{ + url: "sjs_sorting-test-server.sjs?index=1&" + Math.random(), + method: "GET1" + }, { + url: "sjs_sorting-test-server.sjs?index=5&" + Math.random(), + method: "GET5" + }, { + url: "sjs_sorting-test-server.sjs?index=2&" + Math.random(), + method: "GET2" + }, { + url: "sjs_sorting-test-server.sjs?index=4&" + Math.random(), + method: "GET4" + }, { + url: "sjs_sorting-test-server.sjs?index=3&" + Math.random(), + method: "GET3" + }]; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 5); + yield performRequestsInContent(requests); + yield wait; + + EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle")); + + isnot(RequestsMenu.selectedItem, null, + "There should be a selected item in the requests menu."); + is(RequestsMenu.selectedIndex, 0, + "The first item should be selected in the requests menu."); + is(NetMonitorView.detailsPaneHidden, false, + "The details pane should not be hidden after toggle button was pressed."); + + testHeaders(); + testContents([0, 2, 4, 3, 1]); + + info("Testing status sort, ascending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-status-button")); + testHeaders("status", "ascending"); + testContents([0, 1, 2, 3, 4]); + + info("Testing status sort, descending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-status-button")); + testHeaders("status", "descending"); + testContents([4, 3, 2, 1, 0]); + + info("Testing status sort, ascending. Checking sort loops correctly."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-status-button")); + testHeaders("status", "ascending"); + testContents([0, 1, 2, 3, 4]); + + info("Testing method sort, ascending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-method-button")); + testHeaders("method", "ascending"); + testContents([0, 1, 2, 3, 4]); + + info("Testing method sort, descending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-method-button")); + testHeaders("method", "descending"); + testContents([4, 3, 2, 1, 0]); + + info("Testing method sort, ascending. Checking sort loops correctly."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-method-button")); + testHeaders("method", "ascending"); + testContents([0, 1, 2, 3, 4]); + + info("Testing file sort, ascending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-file-button")); + testHeaders("file", "ascending"); + testContents([0, 1, 2, 3, 4]); + + info("Testing file sort, descending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-file-button")); + testHeaders("file", "descending"); + testContents([4, 3, 2, 1, 0]); + + info("Testing file sort, ascending. Checking sort loops correctly."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-file-button")); + testHeaders("file", "ascending"); + testContents([0, 1, 2, 3, 4]); + + info("Testing type sort, ascending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-type-button")); + testHeaders("type", "ascending"); + testContents([0, 1, 2, 3, 4]); + + info("Testing type sort, descending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-type-button")); + testHeaders("type", "descending"); + testContents([4, 3, 2, 1, 0]); + + info("Testing type sort, ascending. Checking sort loops correctly."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-type-button")); + testHeaders("type", "ascending"); + testContents([0, 1, 2, 3, 4]); + + info("Testing transferred sort, ascending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-transferred-button")); + testHeaders("transferred", "ascending"); + testContents([0, 1, 2, 3, 4]); + + info("Testing transferred sort, descending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-transferred-button")); + testHeaders("transferred", "descending"); + testContents([4, 3, 2, 1, 0]); + + info("Testing transferred sort, ascending. Checking sort loops correctly."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-transferred-button")); + testHeaders("transferred", "ascending"); + testContents([0, 1, 2, 3, 4]); + + info("Testing size sort, ascending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button")); + testHeaders("size", "ascending"); + testContents([0, 1, 2, 3, 4]); + + info("Testing size sort, descending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button")); + testHeaders("size", "descending"); + testContents([4, 3, 2, 1, 0]); + + info("Testing size sort, ascending. Checking sort loops correctly."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button")); + testHeaders("size", "ascending"); + testContents([0, 1, 2, 3, 4]); + + info("Testing waterfall sort, ascending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-waterfall-button")); + testHeaders("waterfall", "ascending"); + testContents([0, 2, 4, 3, 1]); + + info("Testing waterfall sort, descending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-waterfall-button")); + testHeaders("waterfall", "descending"); + testContents([4, 2, 0, 1, 3]); + + info("Testing waterfall sort, ascending. Checking sort loops correctly."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-waterfall-button")); + testHeaders("waterfall", "ascending"); + testContents([0, 2, 4, 3, 1]); + + return teardown(monitor); + + function testHeaders(sortType, direction) { + let doc = monitor.panelWin.document; + let target = doc.querySelector("#requests-menu-" + sortType + "-button"); + let headers = doc.querySelectorAll(".requests-menu-header-button"); + + for (let header of headers) { + if (header != target) { + is(header.hasAttribute("sorted"), false, + "The " + header.id + " header should not have a 'sorted' attribute."); + is(header.hasAttribute("tooltiptext"), false, + "The " + header.id + " header should not have a 'tooltiptext' attribute."); + } else { + is(header.getAttribute("sorted"), direction, + "The " + header.id + " header has an incorrect 'sorted' attribute."); + is(header.getAttribute("tooltiptext"), direction == "ascending" + ? L10N.getStr("networkMenu.sortedAsc") + : L10N.getStr("networkMenu.sortedDesc"), + "The " + header.id + " has an incorrect 'tooltiptext' attribute."); + } + } + } + + function testContents([a, b, c, d, e]) { + isnot(RequestsMenu.selectedItem, null, + "There should still be a selected item after sorting."); + is(RequestsMenu.selectedIndex, a, + "The first item should be still selected after sorting."); + is(NetMonitorView.detailsPaneHidden, false, + "The details pane should still be visible after sorting."); + + is(RequestsMenu.items.length, 5, + "There should be a total of 5 items in the requests menu."); + is(RequestsMenu.visibleItems.length, 5, + "There should be a total of 5 visbile items in the requests menu."); + is($all(".side-menu-widget-item").length, 5, + "The visible items in the requests menu are, in fact, visible!"); + + is(RequestsMenu.getItemAtIndex(0), RequestsMenu.items[0], + "The requests menu items aren't ordered correctly. First item is misplaced."); + is(RequestsMenu.getItemAtIndex(1), RequestsMenu.items[1], + "The requests menu items aren't ordered correctly. Second item is misplaced."); + is(RequestsMenu.getItemAtIndex(2), RequestsMenu.items[2], + "The requests menu items aren't ordered correctly. Third item is misplaced."); + is(RequestsMenu.getItemAtIndex(3), RequestsMenu.items[3], + "The requests menu items aren't ordered correctly. Fourth item is misplaced."); + is(RequestsMenu.getItemAtIndex(4), RequestsMenu.items[4], + "The requests menu items aren't ordered correctly. Fifth item is misplaced."); + + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(a), + "GET1", SORTING_SJS + "?index=1", { + fuzzyUrl: true, + status: 101, + statusText: "Meh", + type: "1", + fullMimeType: "text/1", + transferred: L10N.getStr("networkMenu.sizeUnavailable"), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(b), + "GET2", SORTING_SJS + "?index=2", { + fuzzyUrl: true, + status: 200, + statusText: "Meh", + type: "2", + fullMimeType: "text/2", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(c), + "GET3", SORTING_SJS + "?index=3", { + fuzzyUrl: true, + status: 300, + statusText: "Meh", + type: "3", + fullMimeType: "text/3", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(d), + "GET4", SORTING_SJS + "?index=4", { + fuzzyUrl: true, + status: 400, + statusText: "Meh", + type: "4", + fullMimeType: "text/4", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39), + time: true + }); + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(e), + "GET5", SORTING_SJS + "?index=5", { + fuzzyUrl: true, + status: 500, + statusText: "Meh", + type: "5", + fullMimeType: "text/5", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49), + time: true + }); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_sort-03.js b/devtools/client/netmonitor/test/browser_net_sort-03.js new file mode 100644 index 000000000..ada0872a8 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_sort-03.js @@ -0,0 +1,209 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test if sorting columns in the network table works correctly with new requests. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { monitor } = yield initNetMonitor(SORTING_URL); + info("Starting test... "); + + // It seems that this test may be slow on debug builds. This could be because + // of the heavy dom manipulation associated with sorting. + requestLongerTimeout(2); + + let { $, $all, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + // Loading the frame script and preparing the xhr request URLs so we can + // generate some requests later. + loadCommonFrameScript(); + let requests = [{ + url: "sjs_sorting-test-server.sjs?index=1&" + Math.random(), + method: "GET1" + }, { + url: "sjs_sorting-test-server.sjs?index=5&" + Math.random(), + method: "GET5" + }, { + url: "sjs_sorting-test-server.sjs?index=2&" + Math.random(), + method: "GET2" + }, { + url: "sjs_sorting-test-server.sjs?index=4&" + Math.random(), + method: "GET4" + }, { + url: "sjs_sorting-test-server.sjs?index=3&" + Math.random(), + method: "GET3" + }]; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 5); + yield performRequestsInContent(requests); + yield wait; + + EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle")); + + isnot(RequestsMenu.selectedItem, null, + "There should be a selected item in the requests menu."); + is(RequestsMenu.selectedIndex, 0, + "The first item should be selected in the requests menu."); + is(NetMonitorView.detailsPaneHidden, false, + "The details pane should not be hidden after toggle button was pressed."); + + testHeaders(); + testContents([0, 2, 4, 3, 1], 0); + + info("Testing status sort, ascending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-status-button")); + testHeaders("status", "ascending"); + testContents([0, 1, 2, 3, 4], 0); + + info("Performing more requests."); + wait = waitForNetworkEvents(monitor, 5); + yield performRequestsInContent(requests); + yield wait; + + info("Testing status sort again, ascending."); + testHeaders("status", "ascending"); + testContents([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0); + + info("Testing status sort, descending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-status-button")); + testHeaders("status", "descending"); + testContents([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 9); + + info("Performing more requests."); + wait = waitForNetworkEvents(monitor, 5); + yield performRequestsInContent(requests); + yield wait; + + info("Testing status sort again, descending."); + testHeaders("status", "descending"); + testContents([14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 14); + + info("Testing status sort yet again, ascending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-status-button")); + testHeaders("status", "ascending"); + testContents([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 0); + + info("Testing status sort yet again, descending."); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-status-button")); + testHeaders("status", "descending"); + testContents([14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 14); + + return teardown(monitor); + + function testHeaders(sortType, direction) { + let doc = monitor.panelWin.document; + let target = doc.querySelector("#requests-menu-" + sortType + "-button"); + let headers = doc.querySelectorAll(".requests-menu-header-button"); + + for (let header of headers) { + if (header != target) { + is(header.hasAttribute("sorted"), false, + "The " + header.id + " header should not have a 'sorted' attribute."); + is(header.hasAttribute("tooltiptext"), false, + "The " + header.id + " header should not have a 'tooltiptext' attribute."); + } else { + is(header.getAttribute("sorted"), direction, + "The " + header.id + " header has an incorrect 'sorted' attribute."); + is(header.getAttribute("tooltiptext"), direction == "ascending" + ? L10N.getStr("networkMenu.sortedAsc") + : L10N.getStr("networkMenu.sortedDesc"), + "The " + header.id + " has an incorrect 'tooltiptext' attribute."); + } + } + } + + function testContents(order, selection) { + isnot(RequestsMenu.selectedItem, null, + "There should still be a selected item after sorting."); + is(RequestsMenu.selectedIndex, selection, + "The first item should be still selected after sorting."); + is(NetMonitorView.detailsPaneHidden, false, + "The details pane should still be visible after sorting."); + + is(RequestsMenu.items.length, order.length, + "There should be a specific number of items in the requests menu."); + is(RequestsMenu.visibleItems.length, order.length, + "There should be a specific number of visbile items in the requests menu."); + is($all(".side-menu-widget-item").length, order.length, + "The visible items in the requests menu are, in fact, visible!"); + + for (let i = 0; i < order.length; i++) { + is(RequestsMenu.getItemAtIndex(i), RequestsMenu.items[i], + "The requests menu items aren't ordered correctly. Misplaced item " + i + "."); + } + + for (let i = 0, len = order.length / 5; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i]), + "GET1", SORTING_SJS + "?index=1", { + fuzzyUrl: true, + status: 101, + statusText: "Meh", + type: "1", + fullMimeType: "text/1", + transferred: L10N.getStr("networkMenu.sizeUnavailable"), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0), + time: true + }); + } + for (let i = 0, len = order.length / 5; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len]), + "GET2", SORTING_SJS + "?index=2", { + fuzzyUrl: true, + status: 200, + statusText: "Meh", + type: "2", + fullMimeType: "text/2", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19), + time: true + }); + } + for (let i = 0, len = order.length / 5; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 2]), + "GET3", SORTING_SJS + "?index=3", { + fuzzyUrl: true, + status: 300, + statusText: "Meh", + type: "3", + fullMimeType: "text/3", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29), + time: true + }); + } + for (let i = 0, len = order.length / 5; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 3]), + "GET4", SORTING_SJS + "?index=4", { + fuzzyUrl: true, + status: 400, + statusText: "Meh", + type: "4", + fullMimeType: "text/4", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39), + time: true + }); + } + for (let i = 0, len = order.length / 5; i < len; i++) { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 4]), + "GET5", SORTING_SJS + "?index=5", { + fuzzyUrl: true, + status: 500, + statusText: "Meh", + type: "5", + fullMimeType: "text/5", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49), + time: true + }); + } + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_statistics-01.js b/devtools/client/netmonitor/test/browser_net_statistics-01.js new file mode 100644 index 000000000..d7e75b997 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_statistics-01.js @@ -0,0 +1,63 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the statistics view is populated correctly. + */ + +add_task(function* () { + let { monitor } = yield initNetMonitor(STATISTICS_URL); + info("Starting test... "); + + let panel = monitor.panelWin; + let { $, $all, EVENTS, NetMonitorView } = panel; + is(NetMonitorView.currentFrontendMode, "network-inspector-view", + "The initial frontend mode is correct."); + + is($("#primed-cache-chart").childNodes.length, 0, + "There should be no primed cache chart created yet."); + is($("#empty-cache-chart").childNodes.length, 0, + "There should be no empty cache chart created yet."); + + let onChartDisplayed = Promise.all([ + panel.once(EVENTS.PRIMED_CACHE_CHART_DISPLAYED), + panel.once(EVENTS.EMPTY_CACHE_CHART_DISPLAYED) + ]); + let onPlaceholderDisplayed = panel.once(EVENTS.PLACEHOLDER_CHARTS_DISPLAYED); + + info("Displaying statistics view"); + NetMonitorView.toggleFrontendMode(); + is(NetMonitorView.currentFrontendMode, "network-statistics-view", + "The current frontend mode is correct."); + + info("Waiting for placeholder to display"); + yield onPlaceholderDisplayed; + is($("#primed-cache-chart").childNodes.length, 1, + "There should be a placeholder primed cache chart created now."); + is($("#empty-cache-chart").childNodes.length, 1, + "There should be a placeholder empty cache chart created now."); + + is($all(".pie-chart-container[placeholder=true]").length, 2, + "Two placeholder pie chart appear to be rendered correctly."); + is($all(".table-chart-container[placeholder=true]").length, 2, + "Two placeholder table chart appear to be rendered correctly."); + + info("Waiting for chart to display"); + yield onChartDisplayed; + is($("#primed-cache-chart").childNodes.length, 1, + "There should be a real primed cache chart created now."); + is($("#empty-cache-chart").childNodes.length, 1, + "There should be a real empty cache chart created now."); + + yield waitUntil( + () => $all(".pie-chart-container:not([placeholder=true])").length == 2); + ok(true, "Two real pie charts appear to be rendered correctly."); + + yield waitUntil( + () => $all(".table-chart-container:not([placeholder=true])").length == 2); + ok(true, "Two real table charts appear to be rendered correctly."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_statistics-02.js b/devtools/client/netmonitor/test/browser_net_statistics-02.js new file mode 100644 index 000000000..361247e16 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_statistics-02.js @@ -0,0 +1,42 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if the network inspector view is shown when the target navigates + * away while in the statistics view. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(STATISTICS_URL); + info("Starting test... "); + + let panel = monitor.panelWin; + let { EVENTS, NetMonitorView } = panel; + is(NetMonitorView.currentFrontendMode, "network-inspector-view", + "The initial frontend mode is correct."); + + let onChartDisplayed = Promise.all([ + panel.once(EVENTS.PRIMED_CACHE_CHART_DISPLAYED), + panel.once(EVENTS.EMPTY_CACHE_CHART_DISPLAYED) + ]); + + info("Displaying statistics view"); + NetMonitorView.toggleFrontendMode(); + yield onChartDisplayed; + is(NetMonitorView.currentFrontendMode, "network-statistics-view", + "The frontend mode is currently in the statistics view."); + + info("Reloading page"); + let onWillNavigate = panel.once(EVENTS.TARGET_WILL_NAVIGATE); + let onDidNavigate = panel.once(EVENTS.TARGET_DID_NAVIGATE); + tab.linkedBrowser.reload(); + yield onWillNavigate; + is(NetMonitorView.currentFrontendMode, "network-inspector-view", + "The frontend mode switched back to the inspector view."); + yield onDidNavigate; + is(NetMonitorView.currentFrontendMode, "network-inspector-view", + "The frontend mode is still in the inspector view."); + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_statistics-03.js b/devtools/client/netmonitor/test/browser_net_statistics-03.js new file mode 100644 index 000000000..f3c6bf691 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_statistics-03.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test if the correct filtering predicates are used when filtering from + * the performance analysis view. + */ + +add_task(function* () { + let { monitor } = yield initNetMonitor(FILTERING_URL); + info("Starting test... "); + + let panel = monitor.panelWin; + let { $, EVENTS, NetMonitorView } = panel; + + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-css-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-js-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-ws-button")); + EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-other-button")); + testFilterButtonsCustom(monitor, [0, 1, 1, 1, 0, 0, 0, 0, 0, 1]); + info("The correct filtering predicates are used before entering perf. analysis mode."); + + let onEvents = promise.all([ + panel.once(EVENTS.PRIMED_CACHE_CHART_DISPLAYED), + panel.once(EVENTS.EMPTY_CACHE_CHART_DISPLAYED) + ]); + NetMonitorView.toggleFrontendMode(); + yield onEvents; + + is(NetMonitorView.currentFrontendMode, "network-statistics-view", + "The frontend mode is switched to the statistics view."); + + EventUtils.sendMouseEvent({ type: "click" }, $(".pie-chart-slice")); + + is(NetMonitorView.currentFrontendMode, "network-inspector-view", + "The frontend mode is switched back to the inspector view."); + + testFilterButtons(monitor, "html"); + info("The correct filtering predicate is used when exiting perf. analysis mode."); + + yield teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_status-codes.js b/devtools/client/netmonitor/test/browser_net_status-codes.js new file mode 100644 index 000000000..f38ee71e4 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_status-codes.js @@ -0,0 +1,213 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Tests if requests display the correct status code and text in the UI. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(STATUS_CODES_URL); + + info("Starting test... "); + + let { document, EVENTS, NetMonitorView } = monitor.panelWin; + let { RequestsMenu, NetworkDetails } = NetMonitorView; + let requestItems = []; + + RequestsMenu.lazyUpdate = false; + NetworkDetails._params.lazyEmpty = false; + + const REQUEST_DATA = [ + { + // request #0 + method: "GET", + uri: STATUS_CODES_SJS + "?sts=100", + details: { + status: 101, + statusText: "Switching Protocols", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0), + time: true + } + }, + { + // request #1 + method: "GET", + uri: STATUS_CODES_SJS + "?sts=200", + details: { + status: 202, + statusText: "Created", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), + time: true + } + }, + { + // request #2 + method: "GET", + uri: STATUS_CODES_SJS + "?sts=300", + details: { + status: 303, + statusText: "See Other", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), + time: true + } + }, + { + // request #3 + method: "GET", + uri: STATUS_CODES_SJS + "?sts=400", + details: { + status: 404, + statusText: "Not Found", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), + time: true + } + }, + { + // request #4 + method: "GET", + uri: STATUS_CODES_SJS + "?sts=500", + details: { + status: 501, + statusText: "Not Implemented", + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22), + time: true + } + } + ]; + + let wait = waitForNetworkEvents(monitor, 5); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + info("Performing tests"); + yield verifyRequests(); + yield testTab(0, testSummary); + yield testTab(2, testParams); + + return teardown(monitor); + + /** + * A helper that verifies all requests show the correct information and caches + * RequestsMenu items to requestItems array. + */ + function* verifyRequests() { + info("Verifying requests contain correct information."); + let index = 0; + for (let request of REQUEST_DATA) { + let item = RequestsMenu.getItemAtIndex(index); + requestItems[index] = item; + + info("Verifying request #" + index); + yield verifyRequestItemTarget(item, request.method, request.uri, request.details); + + index++; + } + } + + /** + * A helper that opens a given tab of request details pane, selects and passes + * all requests to the given test function. + * + * @param Number tabIdx + * The index of NetworkDetails tab to activate. + * @param Function testFn(requestItem) + * A function that should perform all necessary tests. It's called once + * for every item of REQUEST_DATA with that item being selected in the + * NetworkMonitor. + */ + function* testTab(tabIdx, testFn) { + info("Testing tab #" + tabIdx); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[tabIdx]); + + let counter = 0; + for (let item of REQUEST_DATA) { + info("Waiting tab #" + tabIdx + " to update with request #" + counter); + yield chooseRequest(counter); + + info("Tab updated. Performing checks"); + yield testFn(item); + + counter++; + } + } + + /** + * A function that tests "Summary" contains correct information. + */ + function* testSummary(data) { + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[0]; + + let { method, uri, details: { status, statusText } } = data; + is(tabpanel.querySelector("#headers-summary-url-value").getAttribute("value"), + uri, "The url summary value is incorrect."); + is(tabpanel.querySelector("#headers-summary-method-value").getAttribute("value"), + method, "The method summary value is incorrect."); + is(tabpanel.querySelector("#headers-summary-status-circle").getAttribute("code"), + status, "The status summary code is incorrect."); + is(tabpanel.querySelector("#headers-summary-status-value").getAttribute("value"), + status + " " + statusText, "The status summary value is incorrect."); + } + + /** + * A function that tests "Params" tab contains correct information. + */ + function* testParams(data) { + let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2]; + let statusParamValue = data.uri.split("=").pop(); + let statusParamShownValue = "\"" + statusParamValue + "\""; + + is(tabpanel.querySelectorAll(".variables-view-scope").length, 1, + "There should be 1 param scope displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variable-or-property").length, 1, + "There should be 1 param value displayed in this tabpanel."); + is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0, + "The empty notice should not be displayed in this tabpanel."); + + let paramsScope = tabpanel.querySelectorAll(".variables-view-scope")[0]; + + is(paramsScope.querySelector(".name").getAttribute("value"), + L10N.getStr("paramsQueryString"), + "The params scope doesn't have the correct title."); + + is(paramsScope.querySelectorAll(".variables-view-variable .name")[0] + .getAttribute("value"), + "sts", "The param name was incorrect."); + is(paramsScope.querySelectorAll(".variables-view-variable .value")[0] + .getAttribute("value"), + statusParamShownValue, "The param value was incorrect."); + + is(tabpanel.querySelector("#request-params-box") + .hasAttribute("hidden"), false, + "The request params box should not be hidden."); + is(tabpanel.querySelector("#request-post-data-textarea-box") + .hasAttribute("hidden"), true, + "The request post data textarea box should be hidden."); + } + + /** + * A helper that clicks on a specified request and returns a promise resolved + * when NetworkDetails has been populated with the data of the given request. + */ + function chooseRequest(index) { + let onTabUpdated = monitor.panelWin.once(EVENTS.TAB_UPDATED); + EventUtils.sendMouseEvent({ type: "mousedown" }, requestItems[index].target); + return onTabUpdated; + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_streaming-response.js b/devtools/client/netmonitor/test/browser_net_streaming-response.js new file mode 100644 index 000000000..49a75ec32 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_streaming-response.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if reponses from streaming content types (MPEG-DASH, HLS) are + * displayed as XML or plain text + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + + info("Starting test... "); + let { panelWin } = monitor; + let { document, Editor, NetMonitorView } = panelWin; + let { RequestsMenu } = NetMonitorView; + + const REQUESTS = [ + [ "hls-m3u8", /^#EXTM3U/, Editor.modes.text ], + [ "mpeg-dash", /^<\?xml/, Editor.modes.html ] + ]; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, REQUESTS.length); + for (let [fmt] of REQUESTS) { + let url = CONTENT_TYPE_SJS + "?fmt=" + fmt; + yield ContentTask.spawn(tab.linkedBrowser, { url }, function* (args) { + content.wrappedJSObject.performRequests(1, args.url); + }); + } + yield wait; + + REQUESTS.forEach(([ fmt ], i) => { + verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), + "GET", CONTENT_TYPE_SJS + "?fmt=" + fmt, { + status: 200, + statusText: "OK" + }); + }); + + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.getElementById("details-pane-toggle")); + EventUtils.sendMouseEvent({ type: "mousedown" }, + document.querySelectorAll("#details-pane tab")[3]); + + yield panelWin.once(panelWin.EVENTS.RESPONSE_BODY_DISPLAYED); + let editor = yield NetMonitorView.editor("#response-content-textarea"); + + // the hls-m3u8 part + testEditorContent(editor, REQUESTS[0]); + + RequestsMenu.selectedIndex = 1; + yield panelWin.once(panelWin.EVENTS.TAB_UPDATED); + yield panelWin.once(panelWin.EVENTS.RESPONSE_BODY_DISPLAYED); + + // the mpeg-dash part + testEditorContent(editor, REQUESTS[1]); + + return teardown(monitor); + + function testEditorContent(e, [ fmt, textRe, mode ]) { + ok(e.getText().match(textRe), + "The text shown in the source editor for " + fmt + " is correct."); + is(e.getMode(), mode, + "The mode active in the source editor for " + fmt + " is correct."); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_throttle.js b/devtools/client/netmonitor/test/browser_net_throttle.js new file mode 100644 index 000000000..c1e7723b8 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_throttle.js @@ -0,0 +1,57 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Network throttling integration test. + +"use strict"; + +add_task(function* () { + yield throttleTest(true); + yield throttleTest(false); +}); + +function* throttleTest(actuallyThrottle) { + requestLongerTimeout(2); + + let { monitor } = yield initNetMonitor(SIMPLE_URL); + const {ACTIVITY_TYPE, EVENTS, NetMonitorController, NetMonitorView} = monitor.panelWin; + + info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")"); + + // When throttling, must be smaller than the length of the content + // of SIMPLE_URL in bytes. + const size = actuallyThrottle ? 200 : 0; + + const request = { + "NetworkMonitor.throttleData": { + roundTripTimeMean: 0, + roundTripTimeMax: 0, + downloadBPSMean: size, + downloadBPSMax: size, + uploadBPSMean: 10000, + uploadBPSMax: 10000, + }, + }; + let client = monitor._controller.webConsoleClient; + + info("sending throttle request"); + let deferred = promise.defer(); + client.setPreferences(request, response => { + deferred.resolve(response); + }); + yield deferred.promise; + + let eventPromise = monitor.panelWin.once(EVENTS.RECEIVED_EVENT_TIMINGS); + yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED); + yield eventPromise; + + let requestItem = NetMonitorView.RequestsMenu.getItemAtIndex(0); + const reportedOneSecond = requestItem.attachment.eventTimings.timings.receive > 1000; + if (actuallyThrottle) { + ok(reportedOneSecond, "download reported as taking more than one second"); + } else { + ok(!reportedOneSecond, "download reported as taking less than one second"); + } + + yield teardown(monitor); +} diff --git a/devtools/client/netmonitor/test/browser_net_timeline_ticks.js b/devtools/client/netmonitor/test/browser_net_timeline_ticks.js new file mode 100644 index 000000000..2aafcb98d --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_timeline_ticks.js @@ -0,0 +1,142 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if timeline correctly displays interval divisions. + */ + +add_task(function* () { + let { L10N } = require("devtools/client/netmonitor/l10n"); + + let { tab, monitor } = yield initNetMonitor(SIMPLE_URL); + info("Starting test... "); + + let { $, $all, NetMonitorView, NetMonitorController } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + // Disable transferred size column support for this test. + // Without this, the waterfall only has enough room for one division, which + // would remove most of the value of this test. + $("#requests-menu-transferred-header-box").hidden = true; + $("#requests-menu-item-template .requests-menu-transferred").hidden = true; + + RequestsMenu.lazyUpdate = false; + + ok($("#requests-menu-waterfall-label"), + "An timeline label should be displayed when the frontend is opened."); + ok($all(".requests-menu-timings-division").length == 0, + "No tick labels should be displayed when the frontend is opened."); + + ok(!RequestsMenu._canvas, "No canvas should be created when the frontend is opened."); + ok(!RequestsMenu._ctx, "No 2d context should be created when the frontend is opened."); + + let wait = waitForNetworkEvents(monitor, 1); + tab.linkedBrowser.reload(); + yield wait; + + // Make sure the DOMContentLoaded and load markers don't interfere with + // this test by removing them and redrawing the waterfall (bug 1224088). + NetMonitorController.NetworkEventsHandler.clearMarkers(); + RequestsMenu._flushWaterfallViews(true); + + ok(!$("#requests-menu-waterfall-label"), + "The timeline label should be hidden after the first request."); + ok($all(".requests-menu-timings-division").length >= 3, + "There should be at least 3 tick labels in the network requests header."); + + is($all(".requests-menu-timings-division")[0].getAttribute("value"), + L10N.getFormatStr("networkMenu.millisecond", 0), + "The first tick label has an incorrect value"); + is($all(".requests-menu-timings-division")[1].getAttribute("value"), + L10N.getFormatStr("networkMenu.millisecond", 80), + "The second tick label has an incorrect value"); + is($all(".requests-menu-timings-division")[2].getAttribute("value"), + L10N.getFormatStr("networkMenu.millisecond", 160), + "The third tick label has an incorrect value"); + + is($all(".requests-menu-timings-division")[0].style.transform, "translateX(0px)", + "The first tick label has an incorrect translation"); + is($all(".requests-menu-timings-division")[1].style.transform, "translateX(80px)", + "The second tick label has an incorrect translation"); + is($all(".requests-menu-timings-division")[2].style.transform, "translateX(160px)", + "The third tick label has an incorrect translation"); + + ok(RequestsMenu._canvas, "A canvas should be created after the first request."); + ok(RequestsMenu._ctx, "A 2d context should be created after the first request."); + + let imageData = RequestsMenu._ctx.getImageData(0, 0, 161, 1); + ok(imageData, "The image data should have been created."); + + let data = imageData.data; + ok(data, "The image data should contain a pixel array."); + + ok(hasPixelAt(0), "The tick at 0 is should not be empty."); + ok(!hasPixelAt(1), "The tick at 1 is should be empty."); + ok(!hasPixelAt(19), "The tick at 19 is should be empty."); + ok(hasPixelAt(20), "The tick at 20 is should not be empty."); + ok(!hasPixelAt(21), "The tick at 21 is should be empty."); + ok(!hasPixelAt(39), "The tick at 39 is should be empty."); + ok(hasPixelAt(40), "The tick at 40 is should not be empty."); + ok(!hasPixelAt(41), "The tick at 41 is should be empty."); + ok(!hasPixelAt(59), "The tick at 59 is should be empty."); + ok(hasPixelAt(60), "The tick at 60 is should not be empty."); + ok(!hasPixelAt(61), "The tick at 61 is should be empty."); + ok(!hasPixelAt(79), "The tick at 79 is should be empty."); + ok(hasPixelAt(80), "The tick at 80 is should not be empty."); + ok(!hasPixelAt(81), "The tick at 81 is should be empty."); + ok(!hasPixelAt(159), "The tick at 159 is should be empty."); + ok(hasPixelAt(160), "The tick at 160 is should not be empty."); + ok(!hasPixelAt(161), "The tick at 161 is should be empty."); + + ok(isPixelBrighterAtThan(0, 20), + "The tick at 0 should be brighter than the one at 20"); + ok(isPixelBrighterAtThan(40, 20), + "The tick at 40 should be brighter than the one at 20"); + ok(isPixelBrighterAtThan(40, 60), + "The tick at 40 should be brighter than the one at 60"); + ok(isPixelBrighterAtThan(80, 60), + "The tick at 80 should be brighter than the one at 60"); + + ok(isPixelBrighterAtThan(80, 100), + "The tick at 80 should be brighter than the one at 100"); + ok(isPixelBrighterAtThan(120, 100), + "The tick at 120 should be brighter than the one at 100"); + ok(isPixelBrighterAtThan(120, 140), + "The tick at 120 should be brighter than the one at 140"); + ok(isPixelBrighterAtThan(160, 140), + "The tick at 160 should be brighter than the one at 140"); + + ok(isPixelEquallyBright(20, 60), + "The tick at 20 should be equally bright to the one at 60"); + ok(isPixelEquallyBright(100, 140), + "The tick at 100 should be equally bright to the one at 140"); + + ok(isPixelEquallyBright(40, 120), + "The tick at 40 should be equally bright to the one at 120"); + + ok(isPixelEquallyBright(0, 80), + "The tick at 80 should be equally bright to the one at 160"); + ok(isPixelEquallyBright(80, 160), + "The tick at 80 should be equally bright to the one at 160"); + + function hasPixelAt(x) { + let i = (x | 0) * 4; + return data[i] && data[i + 1] && data[i + 2] && data[i + 3]; + } + + function isPixelBrighterAtThan(x1, x2) { + let i = (x1 | 0) * 4; + let j = (x2 | 0) * 4; + return data[i + 3] > data [j + 3]; + } + + function isPixelEquallyBright(x1, x2) { + let i = (x1 | 0) * 4; + let j = (x2 | 0) * 4; + return data[i + 3] == data [j + 3]; + } + + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_timing-division.js b/devtools/client/netmonitor/test/browser_net_timing-division.js new file mode 100644 index 000000000..0114ba235 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_timing-division.js @@ -0,0 +1,61 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if timing intervals are divided againts seconds when appropriate. + */ + +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL); + info("Starting test... "); + + let { $all, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 2); + // Timeout needed for having enough divisions on the time scale. + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(2, null, 3000); + }); + yield wait; + + let milDivs = $all(".requests-menu-timings-division[division-scale=millisecond]"); + let secDivs = $all(".requests-menu-timings-division[division-scale=second]"); + let minDivs = $all(".requests-menu-timings-division[division-scale=minute]"); + + info("Number of millisecond divisions: " + milDivs.length); + info("Number of second divisions: " + secDivs.length); + info("Number of minute divisions: " + minDivs.length); + + for (let div of milDivs) { + info("Millisecond division: " + div.getAttribute("value")); + } + for (let div of secDivs) { + info("Second division: " + div.getAttribute("value")); + } + for (let div of minDivs) { + info("Minute division: " + div.getAttribute("value")); + } + + is(RequestsMenu.itemCount, 2, + "There should be only two requests made."); + + let firstRequest = RequestsMenu.getItemAtIndex(0); + let lastRequest = RequestsMenu.getItemAtIndex(1); + + info("First request happened at: " + + firstRequest.attachment.responseHeaders.headers.find(e => e.name == "Date").value); + info("Last request happened at: " + + lastRequest.attachment.responseHeaders.headers.find(e => e.name == "Date").value); + + ok(secDivs.length, + "There should be at least one division on the seconds time scale."); + ok(secDivs[0].getAttribute("value").match(/\d+\.\d{2}\s\w+/), + "The division on the seconds time scale looks legit."); + + return teardown(monitor); +}); diff --git a/devtools/client/netmonitor/test/browser_net_truncate.js b/devtools/client/netmonitor/test/browser_net_truncate.js new file mode 100644 index 000000000..bfb5c896d --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_truncate.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Verifies that truncated response bodies still have the correct reported size. + */ + +function test() { + let { L10N } = require("devtools/client/netmonitor/l10n"); + const { RESPONSE_BODY_LIMIT } = require("devtools/shared/webconsole/network-monitor"); + + const URL = EXAMPLE_URL + "sjs_truncate-test-server.sjs?limit=" + RESPONSE_BODY_LIMIT; + + // Another slow test on Linux debug. + requestLongerTimeout(2); + + initNetMonitor(URL).then(({ tab, monitor }) => { + info("Starting test... "); + + let { NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + waitForNetworkEvents(monitor, 1) + .then(() => teardown(monitor)) + .then(finish); + + monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_CONTENT, () => { + let requestItem = RequestsMenu.getItemAtIndex(0); + + verifyRequestItemTarget(RequestsMenu, requestItem, "GET", URL, { + type: "plain", + fullMimeType: "text/plain; charset=utf-8", + transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeMB", 2), + size: L10N.getFormatStrWithNumbers("networkMenu.sizeMB", 2), + }); + }); + + tab.linkedBrowser.reload(); + }); +} diff --git a/devtools/client/netmonitor/test/dropmarker.svg b/devtools/client/netmonitor/test/dropmarker.svg new file mode 100644 index 000000000..3e2987682 --- /dev/null +++ b/devtools/client/netmonitor/test/dropmarker.svg @@ -0,0 +1,6 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" width="8" height="4" viewBox="0 0 8 4"> + <polygon points="0,0 4,4 8,0" fill="#b6babf"/> +</svg> diff --git a/devtools/client/netmonitor/test/head.js b/devtools/client/netmonitor/test/head.js new file mode 100644 index 000000000..d733cc1d4 --- /dev/null +++ b/devtools/client/netmonitor/test/head.js @@ -0,0 +1,518 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/* import-globals-from ../../framework/test/shared-head.js */ + +// shared-head.js handles imports, constants, and utility functions +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", + this); + +var NetworkHelper = require("devtools/shared/webconsole/network-helper"); +var { Toolbox } = require("devtools/client/framework/toolbox"); + +const EXAMPLE_URL = "http://example.com/browser/devtools/client/netmonitor/test/"; +const HTTPS_EXAMPLE_URL = "https://example.com/browser/devtools/client/netmonitor/test/"; + +const API_CALLS_URL = EXAMPLE_URL + "html_api-calls-test-page.html"; +const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html"; +const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html"; +const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html"; +const CONTENT_TYPE_WITHOUT_CACHE_URL = EXAMPLE_URL + "html_content-type-without-cache-test-page.html"; +const CONTENT_TYPE_WITHOUT_CACHE_REQUESTS = 8; +const CYRILLIC_URL = EXAMPLE_URL + "html_cyrillic-test-page.html"; +const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html"; +const POST_DATA_URL = EXAMPLE_URL + "html_post-data-test-page.html"; +const POST_JSON_URL = EXAMPLE_URL + "html_post-json-test-page.html"; +const POST_RAW_URL = EXAMPLE_URL + "html_post-raw-test-page.html"; +const POST_RAW_WITH_HEADERS_URL = EXAMPLE_URL + "html_post-raw-with-headers-test-page.html"; +const PARAMS_URL = EXAMPLE_URL + "html_params-test-page.html"; +const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html"; +const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html"; +const JSON_MALFORMED_URL = EXAMPLE_URL + "html_json-malformed-test-page.html"; +const JSON_CUSTOM_MIME_URL = EXAMPLE_URL + "html_json-custom-mime-test-page.html"; +const JSON_TEXT_MIME_URL = EXAMPLE_URL + "html_json-text-mime-test-page.html"; +const SORTING_URL = EXAMPLE_URL + "html_sorting-test-page.html"; +const FILTERING_URL = EXAMPLE_URL + "html_filter-test-page.html"; +const INFINITE_GET_URL = EXAMPLE_URL + "html_infinite-get-page.html"; +const CUSTOM_GET_URL = EXAMPLE_URL + "html_custom-get-page.html"; +const SINGLE_GET_URL = EXAMPLE_URL + "html_single-get-page.html"; +const STATISTICS_URL = EXAMPLE_URL + "html_statistics-test-page.html"; +const CURL_URL = EXAMPLE_URL + "html_copy-as-curl.html"; +const CURL_UTILS_URL = EXAMPLE_URL + "html_curl-utils.html"; +const SEND_BEACON_URL = EXAMPLE_URL + "html_send-beacon.html"; +const CORS_URL = EXAMPLE_URL + "html_cors-test-page.html"; + +const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs"; +const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs"; +const HTTPS_CONTENT_TYPE_SJS = HTTPS_EXAMPLE_URL + "sjs_content-type-test-server.sjs"; +const STATUS_CODES_SJS = EXAMPLE_URL + "sjs_status-codes-test-server.sjs"; +const SORTING_SJS = EXAMPLE_URL + "sjs_sorting-test-server.sjs"; +const HTTPS_REDIRECT_SJS = EXAMPLE_URL + "sjs_https-redirect-test-server.sjs"; +const CORS_SJS_PATH = "/browser/devtools/client/netmonitor/test/sjs_cors-test-server.sjs"; +const HSTS_SJS = EXAMPLE_URL + "sjs_hsts-test-server.sjs"; + +const HSTS_BASE_URL = EXAMPLE_URL; +const HSTS_PAGE_URL = CUSTOM_GET_URL; + +const TEST_IMAGE = EXAMPLE_URL + "test-image.png"; +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=="; + +const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js"; + +// All tests are asynchronous. +waitForExplicitFinish(); + +const gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log"); +// To enable logging for try runs, just set the pref to true. +Services.prefs.setBoolPref("devtools.debugger.log", false); + +// Uncomment this pref to dump all devtools emitted events to the console. +// Services.prefs.setBoolPref("devtools.dump.emit", true); + +// Always reset some prefs to their original values after the test finishes. +const gDefaultFilters = Services.prefs.getCharPref("devtools.netmonitor.filters"); + +registerCleanupFunction(() => { + info("finish() was called, cleaning up..."); + + Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging); + Services.prefs.setCharPref("devtools.netmonitor.filters", gDefaultFilters); + Services.prefs.clearUserPref("devtools.cache.disabled"); +}); + +function waitForNavigation(aTarget) { + let deferred = promise.defer(); + aTarget.once("will-navigate", () => { + aTarget.once("navigate", () => { + deferred.resolve(); + }); + }); + return deferred.promise; +} + +function reconfigureTab(aTarget, aOptions) { + let deferred = promise.defer(); + aTarget.activeTab.reconfigure(aOptions, deferred.resolve); + return deferred.promise; +} + +function toggleCache(aTarget, aDisabled) { + let options = { cacheDisabled: aDisabled, performReload: true }; + let navigationFinished = waitForNavigation(aTarget); + + // Disable the cache for any toolbox that it is opened from this point on. + Services.prefs.setBoolPref("devtools.cache.disabled", aDisabled); + + return reconfigureTab(aTarget, options).then(() => navigationFinished); +} + +function initNetMonitor(aUrl, aWindow, aEnableCache) { + info("Initializing a network monitor pane."); + + return Task.spawn(function* () { + let tab = yield addTab(aUrl); + info("Net tab added successfully: " + aUrl); + + let target = TargetFactory.forTab(tab); + + yield target.makeRemote(); + info("Target remoted."); + + if (!aEnableCache) { + info("Disabling cache and reloading page."); + yield toggleCache(target, true); + info("Cache disabled when the current and all future toolboxes are open."); + // Remove any requests generated by the reload while toggling the cache to + // avoid interfering with the test. + isnot([...target.activeConsole.getNetworkEvents()].length, 0, + "Request to reconfigure the tab was recorded."); + target.activeConsole.clearNetworkRequests(); + } + + let toolbox = yield gDevTools.showToolbox(target, "netmonitor"); + info("Network monitor pane shown successfully."); + + let monitor = toolbox.getCurrentPanel(); + return {tab, monitor}; + }); +} + +function restartNetMonitor(monitor, newUrl) { + info("Restarting the specified network monitor."); + + return Task.spawn(function* () { + let tab = monitor.target.tab; + let url = newUrl || tab.linkedBrowser.currentURI.spec; + + let onDestroyed = monitor.once("destroyed"); + yield removeTab(tab); + yield onDestroyed; + + return initNetMonitor(url); + }); +} + +function teardown(monitor) { + info("Destroying the specified network monitor."); + + return Task.spawn(function* () { + let tab = monitor.target.tab; + + let onDestroyed = monitor.once("destroyed"); + yield removeTab(tab); + yield onDestroyed; + }); +} + +function waitForNetworkEvents(aMonitor, aGetRequests, aPostRequests = 0) { + let deferred = promise.defer(); + + let panel = aMonitor.panelWin; + let events = panel.EVENTS; + + let progress = {}; + let genericEvents = 0; + let postEvents = 0; + + let awaitedEventsToListeners = [ + ["UPDATING_REQUEST_HEADERS", onGenericEvent], + ["RECEIVED_REQUEST_HEADERS", onGenericEvent], + ["UPDATING_REQUEST_COOKIES", onGenericEvent], + ["RECEIVED_REQUEST_COOKIES", onGenericEvent], + ["UPDATING_REQUEST_POST_DATA", onPostEvent], + ["RECEIVED_REQUEST_POST_DATA", onPostEvent], + ["UPDATING_RESPONSE_HEADERS", onGenericEvent], + ["RECEIVED_RESPONSE_HEADERS", onGenericEvent], + ["UPDATING_RESPONSE_COOKIES", onGenericEvent], + ["RECEIVED_RESPONSE_COOKIES", onGenericEvent], + ["STARTED_RECEIVING_RESPONSE", onGenericEvent], + ["UPDATING_RESPONSE_CONTENT", onGenericEvent], + ["RECEIVED_RESPONSE_CONTENT", onGenericEvent], + ["UPDATING_EVENT_TIMINGS", onGenericEvent], + ["RECEIVED_EVENT_TIMINGS", onGenericEvent] + ]; + + function initProgressForURL(url) { + if (progress[url]) return; + progress[url] = {}; + awaitedEventsToListeners.forEach(([e]) => progress[url][e] = 0); + } + + function updateProgressForURL(url, event) { + initProgressForURL(url); + progress[url][Object.keys(events).find(e => events[e] == event)] = 1; + } + + function onGenericEvent(event, actor) { + genericEvents++; + maybeResolve(event, actor); + } + + function onPostEvent(event, actor) { + postEvents++; + maybeResolve(event, actor); + } + + function maybeResolve(event, actor) { + info("> Network events progress: " + + genericEvents + "/" + ((aGetRequests + aPostRequests) * 13) + ", " + + postEvents + "/" + (aPostRequests * 2) + ", " + + "got " + event + " for " + actor); + + let networkInfo = + panel.NetMonitorController.webConsoleClient.getNetworkRequest(actor); + let url = networkInfo.request.url; + updateProgressForURL(url, event); + + // Uncomment this to get a detailed progress logging (when debugging a test) + // info("> Current state: " + JSON.stringify(progress, null, 2)); + + // There are 15 updates which need to be fired for a request to be + // considered finished. The "requestPostData" packet isn't fired for + // non-POST requests. + if (genericEvents >= (aGetRequests + aPostRequests) * 13 && + postEvents >= aPostRequests * 2) { + + awaitedEventsToListeners.forEach(([e, l]) => panel.off(events[e], l)); + executeSoon(deferred.resolve); + } + } + + awaitedEventsToListeners.forEach(([e, l]) => panel.on(events[e], l)); + return deferred.promise; +} + +function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) { + info("> Verifying: " + aMethod + " " + aUrl + " " + aData.toSource()); + // This bloats log sizes significantly in automation (bug 992485) + // info("> Request: " + aRequestItem.attachment.toSource()); + + let requestsMenu = aRequestItem.ownerView; + let widgetIndex = requestsMenu.indexOfItem(aRequestItem); + let visibleIndex = requestsMenu.visibleItems.indexOf(aRequestItem); + + info("Widget index of item: " + widgetIndex); + info("Visible index of item: " + visibleIndex); + + let { fuzzyUrl, status, statusText, cause, type, fullMimeType, + transferred, size, time, displayedStatus } = aData; + let { attachment, target } = aRequestItem; + + let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL); + let unicodeUrl = NetworkHelper.convertToUnicode(unescape(aUrl)); + let name = NetworkHelper.convertToUnicode(unescape(uri.fileName || uri.filePath || "/")); + let query = NetworkHelper.convertToUnicode(unescape(uri.query)); + let hostPort = uri.hostPort; + let remoteAddress = attachment.remoteAddress; + + if (fuzzyUrl) { + ok(attachment.method.startsWith(aMethod), "The attached method is correct."); + ok(attachment.url.startsWith(aUrl), "The attached url is correct."); + } else { + is(attachment.method, aMethod, "The attached method is correct."); + is(attachment.url, aUrl, "The attached url is correct."); + } + + is(target.querySelector(".requests-menu-method").getAttribute("value"), + aMethod, "The displayed method is correct."); + + if (fuzzyUrl) { + ok(target.querySelector(".requests-menu-file").getAttribute("value").startsWith( + name + (query ? "?" + query : "")), "The displayed file is correct."); + ok(target.querySelector(".requests-menu-file").getAttribute("tooltiptext").startsWith(unicodeUrl), + "The tooltip file is correct."); + } else { + is(target.querySelector(".requests-menu-file").getAttribute("value"), + name + (query ? "?" + query : ""), "The displayed file is correct."); + is(target.querySelector(".requests-menu-file").getAttribute("tooltiptext"), + unicodeUrl, "The tooltip file is correct."); + } + + is(target.querySelector(".requests-menu-domain").getAttribute("value"), + hostPort, "The displayed domain is correct."); + + let domainTooltip = hostPort + (remoteAddress ? " (" + remoteAddress + ")" : ""); + is(target.querySelector(".requests-menu-domain").getAttribute("tooltiptext"), + domainTooltip, "The tooltip domain is correct."); + + if (status !== undefined) { + let value = target.querySelector(".requests-menu-status-icon").getAttribute("code"); + let codeValue = target.querySelector(".requests-menu-status-code").getAttribute("value"); + let tooltip = target.querySelector(".requests-menu-status").getAttribute("tooltiptext"); + info("Displayed status: " + value); + info("Displayed code: " + codeValue); + info("Tooltip status: " + tooltip); + is(value, displayedStatus ? displayedStatus : status, "The displayed status is correct."); + is(codeValue, status, "The displayed status code is correct."); + is(tooltip, status + " " + statusText, "The tooltip status is correct."); + } + if (cause !== undefined) { + let causeLabel = target.querySelector(".requests-menu-cause-label"); + let value = causeLabel.getAttribute("value"); + let tooltip = causeLabel.getAttribute("tooltiptext"); + info("Displayed cause: " + value); + info("Tooltip cause: " + tooltip); + is(value, cause.type, "The displayed cause is correct."); + is(tooltip, cause.loadingDocumentUri, "The tooltip cause is correct.") + } + if (type !== undefined) { + let value = target.querySelector(".requests-menu-type").getAttribute("value"); + let tooltip = target.querySelector(".requests-menu-type").getAttribute("tooltiptext"); + info("Displayed type: " + value); + info("Tooltip type: " + tooltip); + is(value, type, "The displayed type is correct."); + is(tooltip, fullMimeType, "The tooltip type is correct."); + } + if (transferred !== undefined) { + let value = target.querySelector(".requests-menu-transferred").getAttribute("value"); + let tooltip = target.querySelector(".requests-menu-transferred").getAttribute("tooltiptext"); + info("Displayed transferred size: " + value); + info("Tooltip transferred size: " + tooltip); + is(value, transferred, "The displayed transferred size is correct."); + is(tooltip, transferred, "The tooltip transferred size is correct."); + } + if (size !== undefined) { + let value = target.querySelector(".requests-menu-size").getAttribute("value"); + let tooltip = target.querySelector(".requests-menu-size").getAttribute("tooltiptext"); + info("Displayed size: " + value); + info("Tooltip size: " + tooltip); + is(value, size, "The displayed size is correct."); + is(tooltip, size, "The tooltip size is correct."); + } + if (time !== undefined) { + let value = target.querySelector(".requests-menu-timings-total").getAttribute("value"); + let tooltip = target.querySelector(".requests-menu-timings-total").getAttribute("tooltiptext"); + info("Displayed time: " + value); + info("Tooltip time: " + tooltip); + ok(~~(value.match(/[0-9]+/)) >= 0, "The displayed time is correct."); + ok(~~(tooltip.match(/[0-9]+/)) >= 0, "The tooltip time is correct."); + } + + if (visibleIndex != -1) { + if (visibleIndex % 2 == 0) { + ok(aRequestItem.target.hasAttribute("even"), + aRequestItem.value + " should have 'even' attribute."); + ok(!aRequestItem.target.hasAttribute("odd"), + aRequestItem.value + " shouldn't have 'odd' attribute."); + } else { + ok(!aRequestItem.target.hasAttribute("even"), + aRequestItem.value + " shouldn't have 'even' attribute."); + ok(aRequestItem.target.hasAttribute("odd"), + aRequestItem.value + " should have 'odd' attribute."); + } + } +} + +/** + * Helper function for waiting for an event to fire before resolving a promise. + * Example: waitFor(aMonitor.panelWin, aMonitor.panelWin.EVENTS.TAB_UPDATED); + * + * @param object subject + * The event emitter object that is being listened to. + * @param string eventName + * The name of the event to listen to. + * @return object + * Returns a promise that resolves upon firing of the event. + */ +function waitFor(subject, eventName) { + let deferred = promise.defer(); + subject.once(eventName, deferred.resolve); + return deferred.promise; +} + +/** + * Tests if a button for a filter of given type is the only one checked. + * + * @param string filterType + * The type of the filter that should be the only one checked. + */ +function testFilterButtons(monitor, filterType) { + let doc = monitor.panelWin.document; + let target = doc.querySelector("#requests-menu-filter-" + filterType + "-button"); + ok(target, `Filter button '${filterType}' was found`); + let buttons = [...doc.querySelectorAll(".menu-filter-button")]; + ok(buttons.length > 0, "More than zero filter buttons were found"); + + // Only target should be checked. + let checkStatus = buttons.map(button => button == target ? 1 : 0); + testFilterButtonsCustom(monitor, checkStatus); +} + +/** + * Tests if filter buttons have 'checked' attributes set correctly. + * + * @param array aIsChecked + * An array specifying if a button at given index should have a + * 'checked' attribute. For example, if the third item of the array + * evaluates to true, the third button should be checked. + */ +function testFilterButtonsCustom(aMonitor, aIsChecked) { + let doc = aMonitor.panelWin.document; + let buttons = doc.querySelectorAll(".menu-filter-button"); + for (let i = 0; i < aIsChecked.length; i++) { + let button = buttons[i]; + if (aIsChecked[i]) { + is(button.classList.contains("checked"), true, + "The " + button.id + " button should have a 'checked' class."); + } else { + is(button.classList.contains("checked"), false, + "The " + button.id + " button should not have a 'checked' class."); + } + } +} + +/** + * Loads shared/frame-script-utils.js in the specified tab. + * + * @param tab + * Optional tab to load the frame script in. Defaults to the current tab. + */ +function loadCommonFrameScript(tab) { + let browser = tab ? tab.linkedBrowser : gBrowser.selectedBrowser; + + browser.messageManager.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false); +} + +/** + * Perform the specified requests in the context of the page content. + * + * @param Array requests + * An array of objects specifying the requests to perform. See + * shared/frame-script-utils.js for more information. + * + * @return A promise that resolves once the requests complete. + */ +function performRequestsInContent(requests) { + info("Performing requests in the context of the content."); + return executeInContent("devtools:test:xhr", requests); +} + +/** + * Send an async message to the frame script (chrome -> content) and wait for a + * response message with the same name (content -> chrome). + * + * @param String name + * The message name. Should be one of the messages defined + * shared/frame-script-utils.js + * @param Object data + * Optional data to send along + * @param Object objects + * Optional CPOW objects to send along + * @param Boolean expectResponse + * If set to false, don't wait for a response with the same name from the + * content script. Defaults to true. + * + * @return Promise + * Resolves to the response data if a response is expected, immediately + * resolves otherwise + */ +function executeInContent(name, data = {}, objects = {}, expectResponse = true) { + let mm = gBrowser.selectedBrowser.messageManager; + + mm.sendAsyncMessage(name, data, objects); + if (expectResponse) { + return waitForContentMessage(name); + } else { + return promise.resolve(); + } +} + +/** + * Wait for a content -> chrome message on the message manager (the window + * messagemanager is used). + * @param {String} name The message name + * @return {Promise} A promise that resolves to the response data when the + * message has been received + */ +function waitForContentMessage(name) { + let mm = gBrowser.selectedBrowser.messageManager; + + let def = promise.defer(); + mm.addMessageListener(name, function onMessage(msg) { + mm.removeMessageListener(name, onMessage); + def.resolve(msg); + }); + return def.promise; +} + +/** + * Open the requestMenu menu and return all of it's items in a flat array + * @param {netmonitorPanel} netmonitor + * @param {Event} event mouse event with screenX and screenX coordinates + * @return An array of MenuItems + */ +function openContextMenuAndGetAllItems(netmonitor, event) { + let menu = netmonitor.RequestsMenu.contextMenu.open(event); + + // Flatten all menu items into a single array to make searching through it easier + let allItems = [].concat.apply([], menu.items.map(function addItem(item) { + if (item.submenu) { + return addItem(item.submenu.items); + } + return item; + })); + + return allItems; +} diff --git a/devtools/client/netmonitor/test/html_api-calls-test-page.html b/devtools/client/netmonitor/test/html_api-calls-test-page.html new file mode 100644 index 000000000..e31872319 --- /dev/null +++ b/devtools/client/netmonitor/test/html_api-calls-test-page.html @@ -0,0 +1,46 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>API calls request test</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(); + } + + function performRequests() { + get("/api/fileName.xml", function() { + get("/api/file%E2%98%A2.xml", function() { + get("/api/ascii/get/", function() { + get("/api/unicode/%E2%98%A2/", function() { + get("/api/search/?q=search%E2%98%A2", function() { + // Done. + }); + }); + }); + }); + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_brotli-test-page.html b/devtools/client/netmonitor/test/html_brotli-test-page.html new file mode 100644 index 000000000..d5afae4b3 --- /dev/null +++ b/devtools/client/netmonitor/test/html_brotli-test-page.html @@ -0,0 +1,38 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Brotli test</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + function performRequests() { + get("sjs_content-type-test-server.sjs?fmt=br", function() { + // Done. + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_cause-test-page.html b/devtools/client/netmonitor/test/html_cause-test-page.html new file mode 100644 index 000000000..d2b86682b --- /dev/null +++ b/devtools/client/netmonitor/test/html_cause-test-page.html @@ -0,0 +1,48 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + <link rel="stylesheet" type="text/css" href="stylesheet_request" /> + </head> + + <body> + <p>Request cause test</p> + <img src="img_request" /> + <script type="text/javascript"> + function performXhrRequest() { + var xhr = new XMLHttpRequest(); + xhr.open("GET", "xhr_request", true); + xhr.send(); + } + + function performFetchRequest() { + fetch("fetch_request"); + } + + function performBeaconRequest() { + navigator.sendBeacon("beacon_request"); + } + + performXhrRequest(); + performFetchRequest(); + + // Perform some requests with async stacks + Promise.resolve().then(function performPromiseFetchRequest() { + fetch("promise_fetch_request"); + setTimeout(function performTimeoutFetchRequest() { + fetch("timeout_fetch_request"); + + // Finally, send a beacon request + performBeaconRequest(); + }, 0); + }); + </script> + </body> +</html> diff --git a/devtools/client/netmonitor/test/html_content-type-test-page.html b/devtools/client/netmonitor/test/html_content-type-test-page.html new file mode 100644 index 000000000..23ecf1f44 --- /dev/null +++ b/devtools/client/netmonitor/test/html_content-type-test-page.html @@ -0,0 +1,48 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Content type test</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + function performRequests() { + get("sjs_content-type-test-server.sjs?fmt=xml", function() { + get("sjs_content-type-test-server.sjs?fmt=css", function() { + get("sjs_content-type-test-server.sjs?fmt=js", function() { + get("sjs_content-type-test-server.sjs?fmt=json", function() { + get("sjs_content-type-test-server.sjs?fmt=bogus", function() { + get("test-image.png", function() { + // Done. + }); + }); + }); + }); + }); + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_content-type-without-cache-test-page.html b/devtools/client/netmonitor/test/html_content-type-without-cache-test-page.html new file mode 100644 index 000000000..f27e6e105 --- /dev/null +++ b/devtools/client/netmonitor/test/html_content-type-without-cache-test-page.html @@ -0,0 +1,52 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Content type test</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + function performRequests() { + get("sjs_content-type-test-server.sjs?fmt=xml", function() { + get("sjs_content-type-test-server.sjs?fmt=css", function() { + get("sjs_content-type-test-server.sjs?fmt=js", function() { + get("sjs_content-type-test-server.sjs?fmt=json", function() { + get("sjs_content-type-test-server.sjs?fmt=bogus", function() { + get("test-image.png?v=" + Math.random(), function() { + get("sjs_content-type-test-server.sjs?fmt=gzip", function() { + get("sjs_content-type-test-server.sjs?fmt=br", function() { + // Done. + }); + }); + }); + }); + }); + }); + }); + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_copy-as-curl.html b/devtools/client/netmonitor/test/html_copy-as-curl.html new file mode 100644 index 000000000..3ddcfbced --- /dev/null +++ b/devtools/client/netmonitor/test/html_copy-as-curl.html @@ -0,0 +1,30 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Performing a GET request</p> + + <script type="text/javascript"> + function performRequest(aUrl) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aUrl, true); + xhr.setRequestHeader("Accept-Language", window.navigator.language); + xhr.setRequestHeader("X-Custom-Header-1", "Custom value"); + xhr.setRequestHeader("X-Custom-Header-2", "8.8.8.8"); + xhr.setRequestHeader("X-Custom-Header-3", "Mon, 3 Mar 2014 11:11:11 GMT"); + xhr.send(null); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_cors-test-page.html b/devtools/client/netmonitor/test/html_cors-test-page.html new file mode 100644 index 000000000..179b2ed00 --- /dev/null +++ b/devtools/client/netmonitor/test/html_cors-test-page.html @@ -0,0 +1,31 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>POST with CORS test page</p> + + <script type="text/javascript"> + function post(url, contentType, postData) { + var xhr = new XMLHttpRequest(); + xhr.open("POST", url, true); + xhr.setRequestHeader("Content-Type", contentType); + xhr.send(postData); + } + + function performRequests(url, contentType, postData) { + post(url, contentType, postData); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_curl-utils.html b/devtools/client/netmonitor/test/html_curl-utils.html new file mode 100644 index 000000000..8ff7ecdf0 --- /dev/null +++ b/devtools/client/netmonitor/test/html_curl-utils.html @@ -0,0 +1,102 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Performing requests</p> + + <p> + <canvas width="100" height="100"></canvas> + </p> + + <hr/> + + <form method="post" action="#" enctype="multipart/form-data" target="target" id="post-form"> + <input type="text" name="param1" value="value1"/> + <input type="text" name="param2" value="value2"/> + <input type="text" name="param3" value="value3"/> + <input type="submit"/> + </form> + <iframe name="target"></iframe> + + <script type="text/javascript"> + + function ajaxGet(aUrl, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aUrl + "?param1=value1¶m2=value2¶m3=value3", true); + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + xhr.onload = function() { + aCallback(); + }; + xhr.send(); + } + + function ajaxPost(aUrl, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("POST", aUrl, true); + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + xhr.onload = function() { + aCallback(); + }; + var params = "param1=value1¶m2=value2¶m3=value3"; + xhr.send(params); + } + + function ajaxMultipart(aUrl, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("POST", aUrl, true); + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + xhr.onload = function() { + aCallback(); + }; + + getCanvasElem().toBlob((blob) => { + var formData = new FormData(); + formData.append("param1", "value1"); + formData.append("file", blob, "filename.png"); + xhr.send(formData); + }); + } + + function submitForm() { + var form = document.querySelector("#post-form"); + form.submit(); + } + + function getCanvasElem() { + return document.querySelector("canvas"); + } + + function initCanvas() { + var canvas = getCanvasElem(); + var ctx = canvas.getContext("2d"); + ctx.fillRect(0,0,100,100); + ctx.clearRect(20,20,60,60); + ctx.strokeRect(25,25,50,50); + } + + function performRequests(aUrl) { + ajaxGet(aUrl, () => { + ajaxPost(aUrl, () => { + ajaxMultipart(aUrl, () => { + submitForm(); + }); + }); + }); + } + + initCanvas(); + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_custom-get-page.html b/devtools/client/netmonitor/test/html_custom-get-page.html new file mode 100644 index 000000000..19e40f93a --- /dev/null +++ b/devtools/client/netmonitor/test/html_custom-get-page.html @@ -0,0 +1,44 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Performing a custom number of GETs</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + // Use a count parameter to defeat caching. + var count = 0; + + function performRequests(aTotal, aUrl, aTimeout = 0) { + if (!aTotal) { + return; + } + get(aUrl || "request_" + (count++), function() { + setTimeout(performRequests.bind(this, --aTotal, aUrl, aTimeout), aTimeout); + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_cyrillic-test-page.html b/devtools/client/netmonitor/test/html_cyrillic-test-page.html new file mode 100644 index 000000000..8735ac674 --- /dev/null +++ b/devtools/client/netmonitor/test/html_cyrillic-test-page.html @@ -0,0 +1,39 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Cyrillic type test</p> + <p>Братан, ты вообще качаешься?</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + function performRequests() { + get("sjs_content-type-test-server.sjs?fmt=txt", function() { + // Done. + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_filter-test-page.html b/devtools/client/netmonitor/test/html_filter-test-page.html new file mode 100644 index 000000000..eb5d02ed9 --- /dev/null +++ b/devtools/client/netmonitor/test/html_filter-test-page.html @@ -0,0 +1,60 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Filtering test</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + // Use a random parameter to defeat caching. + xhr.open("GET", aAddress + "&" + Math.random(), true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + function performRequests(aOptions) { + var options = JSON.parse(aOptions); + get("sjs_content-type-test-server.sjs?fmt=html&res=" + options.htmlContent, function() { + get("sjs_content-type-test-server.sjs?fmt=css", function() { + get("sjs_content-type-test-server.sjs?fmt=js", function() { + if (!options.getMedia) { + return; + } + get("sjs_content-type-test-server.sjs?fmt=font", function() { + get("sjs_content-type-test-server.sjs?fmt=image", function() { + get("sjs_content-type-test-server.sjs?fmt=audio", function() { + get("sjs_content-type-test-server.sjs?fmt=video", function() { + if (!options.getFlash) { + return; + } + get("sjs_content-type-test-server.sjs?fmt=flash", function() { + // Done. + }); + }); + }); + }); + }); + }); + }); + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_frame-subdocument.html b/devtools/client/netmonitor/test/html_frame-subdocument.html new file mode 100644 index 000000000..9e800582c --- /dev/null +++ b/devtools/client/netmonitor/test/html_frame-subdocument.html @@ -0,0 +1,48 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + <link rel="stylesheet" type="text/css" href="stylesheet_request" /> + </head> + + <body> + <p>Request frame test</p> + <img src="img_request" /> + <script type="text/javascript"> + function performXhrRequest() { + var xhr = new XMLHttpRequest(); + xhr.open("GET", "xhr_request", true); + xhr.send(); + } + + function performFetchRequest() { + fetch("fetch_request"); + } + + function performBeaconRequest() { + navigator.sendBeacon("beacon_request"); + } + + performXhrRequest(); + performFetchRequest(); + + // Perform some requests with async stacks + Promise.resolve().then(function performPromiseFetchRequest() { + fetch("promise_fetch_request"); + setTimeout(function performTimeoutFetchRequest() { + fetch("timeout_fetch_request"); + + // Finally, send a beacon request + performBeaconRequest(); + }, 0); + }); + </script> + </body> +</html> diff --git a/devtools/client/netmonitor/test/html_frame-test-page.html b/devtools/client/netmonitor/test/html_frame-test-page.html new file mode 100644 index 000000000..66f6620af --- /dev/null +++ b/devtools/client/netmonitor/test/html_frame-test-page.html @@ -0,0 +1,49 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + <link rel="stylesheet" type="text/css" href="stylesheet_request" /> + </head> + + <body> + <p>Request frame test</p> + <img src="img_request" /> + <iframe src="html_frame-subdocument.html"></iframe> + <script type="text/javascript"> + function performXhrRequest() { + var xhr = new XMLHttpRequest(); + xhr.open("GET", "xhr_request", true); + xhr.send(); + } + + function performFetchRequest() { + fetch("fetch_request"); + } + + function performBeaconRequest() { + navigator.sendBeacon("beacon_request"); + } + + performXhrRequest(); + performFetchRequest(); + + // Perform some requests with async stacks + Promise.resolve().then(function performPromiseFetchRequest() { + fetch("promise_fetch_request"); + setTimeout(function performTimeoutFetchRequest() { + fetch("timeout_fetch_request"); + + // Finally, send a beacon request + performBeaconRequest(); + }, 0); + }); + </script> + </body> +</html> diff --git a/devtools/client/netmonitor/test/html_image-tooltip-test-page.html b/devtools/client/netmonitor/test/html_image-tooltip-test-page.html new file mode 100644 index 000000000..c39db909e --- /dev/null +++ b/devtools/client/netmonitor/test/html_image-tooltip-test-page.html @@ -0,0 +1,26 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>tooltip test</p> + + <script type="text/javascript"> + function performRequests() { + var xhr = new XMLHttpRequest(); + xhr.open("GET", "test-image.png?v=" + Math.random(), true); + xhr.send(null); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_infinite-get-page.html b/devtools/client/netmonitor/test/html_infinite-get-page.html new file mode 100644 index 000000000..f51b718ad --- /dev/null +++ b/devtools/client/netmonitor/test/html_infinite-get-page.html @@ -0,0 +1,41 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Infinite GETs</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + // Use a count parameter to defeat caching. + var count = 0; + + (function performRequests() { + get("request_" + (count++), function() { + setTimeout(performRequests, 50); + }); + })(); + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_json-custom-mime-test-page.html b/devtools/client/netmonitor/test/html_json-custom-mime-test-page.html new file mode 100644 index 000000000..646fc60ea --- /dev/null +++ b/devtools/client/netmonitor/test/html_json-custom-mime-test-page.html @@ -0,0 +1,38 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>JSONP test</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + function performRequests() { + get("sjs_content-type-test-server.sjs?fmt=json-custom-mime", function() { + // Done. + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_json-long-test-page.html b/devtools/client/netmonitor/test/html_json-long-test-page.html new file mode 100644 index 000000000..b538b4c27 --- /dev/null +++ b/devtools/client/netmonitor/test/html_json-long-test-page.html @@ -0,0 +1,38 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>JSON long string test</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + function performRequests() { + get("sjs_content-type-test-server.sjs?fmt=json-long", function() { + // Done. + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_json-malformed-test-page.html b/devtools/client/netmonitor/test/html_json-malformed-test-page.html new file mode 100644 index 000000000..0c8627ab5 --- /dev/null +++ b/devtools/client/netmonitor/test/html_json-malformed-test-page.html @@ -0,0 +1,38 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>JSON malformed test</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + function performRequests() { + get("sjs_content-type-test-server.sjs?fmt=json-malformed", function() { + // Done. + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_json-text-mime-test-page.html b/devtools/client/netmonitor/test/html_json-text-mime-test-page.html new file mode 100644 index 000000000..2c64e2531 --- /dev/null +++ b/devtools/client/netmonitor/test/html_json-text-mime-test-page.html @@ -0,0 +1,38 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>JSON text test</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + function performRequests() { + get("sjs_content-type-test-server.sjs?fmt=json-text-mime", function() { + // Done. + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_jsonp-test-page.html b/devtools/client/netmonitor/test/html_jsonp-test-page.html new file mode 100644 index 000000000..78c0da08b --- /dev/null +++ b/devtools/client/netmonitor/test/html_jsonp-test-page.html @@ -0,0 +1,40 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>JSONP test</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + function performRequests() { + get("sjs_content-type-test-server.sjs?fmt=jsonp&jsonp=$_0123Fun", function() { + get("sjs_content-type-test-server.sjs?fmt=jsonp2&jsonp=$_4567Sad", function() { + // Done. + }); + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_navigate-test-page.html b/devtools/client/netmonitor/test/html_navigate-test-page.html new file mode 100644 index 000000000..23f00f3df --- /dev/null +++ b/devtools/client/netmonitor/test/html_navigate-test-page.html @@ -0,0 +1,18 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Navigation test</p> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_params-test-page.html b/devtools/client/netmonitor/test/html_params-test-page.html new file mode 100644 index 000000000..3f30e3d76 --- /dev/null +++ b/devtools/client/netmonitor/test/html_params-test-page.html @@ -0,0 +1,67 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Request params type test</p> + + <script type="text/javascript"> + function get(aAddress, aQuery) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress + aQuery, true); + xhr.send(); + } + + function post(aAddress, aQuery, aContentType, aPostBody) { + var xhr = new XMLHttpRequest(); + xhr.open("POST", aAddress + aQuery, true); + xhr.setRequestHeader("content-type", aContentType); + xhr.send(aPostBody); + } + + function performRequests() { + var urlencoded = "application/x-www-form-urlencoded"; + + setTimeout(function() { + post("baz", "?a", urlencoded, '{ "foo": "bar" }'); + + setTimeout(function() { + post("baz", "?a=b", urlencoded, '{ "foo": "bar" }'); + + setTimeout(function() { + post("baz", "?a=b", urlencoded, '?foo=bar'); + + setTimeout(function() { + post("baz", "?a", undefined, '{ "foo": "bar" }'); + + setTimeout(function() { + post("baz", "?a=b", undefined, '{ "foo": "bar" }'); + + setTimeout(function() { + post("baz", "?a=b", undefined, '?foo=bar'); + + setTimeout(function() { + get("baz", ""); + + // Done. + }, 10); + }, 10); + }, 10); + }, 10); + }, 10); + }, 10); + }, 10); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_post-data-test-page.html b/devtools/client/netmonitor/test/html_post-data-test-page.html new file mode 100644 index 000000000..8dedc7b60 --- /dev/null +++ b/devtools/client/netmonitor/test/html_post-data-test-page.html @@ -0,0 +1,77 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + <style> + input { + display: block; + margin: 12px; + } + </style> + </head> + + <body> + <p>POST data test</p> + <form enctype="multipart/form-data" method="post" name="form-name"> + <input type="text" name="text" placeholder="text" value="Some text..."/> + <input type="email" name="email" placeholder="email"/> + <input type="range" name="range" value="42"/> + <input type="button" value="Post me!" onclick="window.form()"> + </form> + + <script type="text/javascript"> + function post(aAddress, aMessage, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("POST", aAddress, true); + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + + var data = ""; + for (var i in aMessage) { + data += "&" + i + "=" + aMessage[i]; + } + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(data); + } + + function form(aAddress, aForm, aCallback) { + var formData = new FormData(document.forms.namedItem(aForm)); + formData.append("Custom field", "Extra data"); + + var xhr = new XMLHttpRequest(); + xhr.open("POST", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(formData); + } + + function performRequests() { + var url = "sjs_simple-test-server.sjs"; + var url1 = url + "?foo=bar&baz=42&type=urlencoded"; + var url2 = url + "?foo=bar&baz=42&type=multipart"; + + post(url1, { foo: "bar", baz: 123 }, function() { + form(url2, "form-name", function() { + // Done. + }); + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_post-json-test-page.html b/devtools/client/netmonitor/test/html_post-json-test-page.html new file mode 100644 index 000000000..129feaf08 --- /dev/null +++ b/devtools/client/netmonitor/test/html_post-json-test-page.html @@ -0,0 +1,39 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>POST raw test</p> + + <script type="text/javascript"> + function post(address, message, callback) { + let xhr = new XMLHttpRequest(); + xhr.open("POST", address, true); + xhr.setRequestHeader("Content-Type", "application/json"); + + xhr.onreadystatechange = function () { + if (this.readyState == this.DONE) { + callback(); + } + }; + xhr.send(message); + } + + function performRequests() { + post("sjs_simple-test-server.sjs", JSON.stringify({a: 1}), function () { + // Done. + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_post-raw-test-page.html b/devtools/client/netmonitor/test/html_post-raw-test-page.html new file mode 100644 index 000000000..b4456348c --- /dev/null +++ b/devtools/client/netmonitor/test/html_post-raw-test-page.html @@ -0,0 +1,40 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>POST raw test</p> + + <script type="text/javascript"> + function post(aAddress, aMessage, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("POST", aAddress, true); + xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded"); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(aMessage); + } + + function performRequests() { + var rawData = "foo=bar&baz=123"; + post("sjs_simple-test-server.sjs", rawData, function() { + // Done. + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_post-raw-with-headers-test-page.html b/devtools/client/netmonitor/test/html_post-raw-with-headers-test-page.html new file mode 100644 index 000000000..3bb8f9071 --- /dev/null +++ b/devtools/client/netmonitor/test/html_post-raw-with-headers-test-page.html @@ -0,0 +1,45 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>POST raw with headers test</p> + + <script type="text/javascript"> + function post(aAddress, aMessage, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("POST", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(aMessage); + } + + function performRequests() { + var rawData = [ + "content-type: application/x-www-form-urlencoded\r", + "custom-header: hello world!\r", + "\r", + "\r", + "foo=bar&baz=123" + ]; + post("sjs_simple-test-server.sjs", rawData.join("\n"), function() { + // Done. + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_send-beacon.html b/devtools/client/netmonitor/test/html_send-beacon.html new file mode 100644 index 000000000..95cc005bd --- /dev/null +++ b/devtools/client/netmonitor/test/html_send-beacon.html @@ -0,0 +1,23 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Send beacon test</p> + + <script type="text/javascript"> + function performRequest() { + navigator.sendBeacon("beacon_request"); + } + </script> + </body> +</html> diff --git a/devtools/client/netmonitor/test/html_simple-test-page.html b/devtools/client/netmonitor/test/html_simple-test-page.html new file mode 100644 index 000000000..846681dbd --- /dev/null +++ b/devtools/client/netmonitor/test/html_simple-test-page.html @@ -0,0 +1,18 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Simple test</p> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_single-get-page.html b/devtools/client/netmonitor/test/html_single-get-page.html new file mode 100644 index 000000000..0055d4ee0 --- /dev/null +++ b/devtools/client/netmonitor/test/html_single-get-page.html @@ -0,0 +1,36 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Performing a custom number of GETs</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + (function performRequests() { + get("request_0", function() {}); + })(); + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_sorting-test-page.html b/devtools/client/netmonitor/test/html_sorting-test-page.html new file mode 100644 index 000000000..640c58b8e --- /dev/null +++ b/devtools/client/netmonitor/test/html_sorting-test-page.html @@ -0,0 +1,18 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Sorting test</p> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_statistics-test-page.html b/devtools/client/netmonitor/test/html_statistics-test-page.html new file mode 100644 index 000000000..b4b15b82b --- /dev/null +++ b/devtools/client/netmonitor/test/html_statistics-test-page.html @@ -0,0 +1,40 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Statistics test</p> + + <script type="text/javascript"> + function get(aAddress) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + xhr.send(null); + } + + get("sjs_content-type-test-server.sjs?sts=304&fmt=txt"); + get("sjs_content-type-test-server.sjs?sts=304&fmt=xml"); + get("sjs_content-type-test-server.sjs?sts=304&fmt=html"); + get("sjs_content-type-test-server.sjs?sts=304&fmt=css"); + get("sjs_content-type-test-server.sjs?sts=304&fmt=js"); + get("sjs_content-type-test-server.sjs?sts=304&fmt=json"); + get("sjs_content-type-test-server.sjs?sts=304&fmt=jsonp"); + get("sjs_content-type-test-server.sjs?sts=304&fmt=font"); + get("sjs_content-type-test-server.sjs?sts=304&fmt=image"); + get("sjs_content-type-test-server.sjs?sts=304&fmt=audio"); + get("sjs_content-type-test-server.sjs?sts=304&fmt=video"); + get("sjs_content-type-test-server.sjs?sts=304&fmt=flash"); + get("test-image.png?v=" + Math.random()); + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/html_status-codes-test-page.html b/devtools/client/netmonitor/test/html_status-codes-test-page.html new file mode 100644 index 000000000..4be779bd4 --- /dev/null +++ b/devtools/client/netmonitor/test/html_status-codes-test-page.html @@ -0,0 +1,55 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Status codes test</p> + + <script type="text/javascript"> + function get(aAddress, aCallback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", aAddress, true); + + xhr.onreadystatechange = function() { + if (this.readyState == this.DONE) { + aCallback(); + } + }; + xhr.send(null); + } + + function performRequests() { + get("sjs_status-codes-test-server.sjs?sts=100", function() { + get("sjs_status-codes-test-server.sjs?sts=200", function() { + get("sjs_status-codes-test-server.sjs?sts=300", function() { + get("sjs_status-codes-test-server.sjs?sts=400", function() { + get("sjs_status-codes-test-server.sjs?sts=500", function() { + // Done. + }); + }); + }); + }); + }); + } + + function performCachedRequests() { + get("sjs_status-codes-test-server.sjs?sts=ok&cached", function() { + get("sjs_status-codes-test-server.sjs?sts=redirect&cached", function() { + // Done. + }); + }); + } + + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/service-workers/status-codes-service-worker.js b/devtools/client/netmonitor/test/service-workers/status-codes-service-worker.js new file mode 100644 index 000000000..3c70c7dcb --- /dev/null +++ b/devtools/client/netmonitor/test/service-workers/status-codes-service-worker.js @@ -0,0 +1,15 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +self.addEventListener("activate", event => { + // start controlling the already loaded page + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener("fetch", event => { + let response = new Response("Service worker response"); + event.respondWith(response); +}); diff --git a/devtools/client/netmonitor/test/service-workers/status-codes.html b/devtools/client/netmonitor/test/service-workers/status-codes.html new file mode 100644 index 000000000..65c79ee00 --- /dev/null +++ b/devtools/client/netmonitor/test/service-workers/status-codes.html @@ -0,0 +1,59 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> + <meta http-equiv="Pragma" content="no-cache" /> + <meta http-equiv="Expires" content="0" /> + <title>Network Monitor test page</title> + </head> + + <body> + <p>Status codes test</p> + + <script type="text/javascript"> + let swRegistration; + + function registerServiceWorker() { + let sw = navigator.serviceWorker; + return sw.register("status-codes-service-worker.js") + .then(registration => { + swRegistration = registration; + console.log("Registered, scope is:", registration.scope); + return sw.ready; + }).then(() => { + // wait until the page is controlled + return new Promise(resolve => { + if (sw.controller) { + resolve(); + } else { + sw.addEventListener('controllerchange', function onControllerChange() { + sw.removeEventListener('controllerchange', onControllerChange); + resolve(); + }); + } + }); + }).catch(err => { + console.error("Registration failed"); + }); + } + + function unregisterServiceWorker() { + return swRegistration.unregister(); + } + + function performRequests() { + return new Promise(function doXHR(done) { + let xhr = new XMLHttpRequest(); + xhr.open("GET", "test/200", true); + xhr.onreadystatechange = done; + xhr.send(null); + }); + } + </script> + </body> + +</html> diff --git a/devtools/client/netmonitor/test/sjs_content-type-test-server.sjs b/devtools/client/netmonitor/test/sjs_content-type-test-server.sjs new file mode 100644 index 000000000..ee9a82e27 --- /dev/null +++ b/devtools/client/netmonitor/test/sjs_content-type-test-server.sjs @@ -0,0 +1,273 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { classes: Cc, interfaces: Ci } = Components; + +function gzipCompressString(string, obs) { + + let scs = Cc["@mozilla.org/streamConverters;1"] + .getService(Ci.nsIStreamConverterService); + let listener = Cc["@mozilla.org/network/stream-loader;1"] + .createInstance(Ci.nsIStreamLoader); + listener.init(obs); + let converter = scs.asyncConvertData("uncompressed", "gzip", + listener, null); + let stringStream = Cc["@mozilla.org/io/string-input-stream;1"] + .createInstance(Ci.nsIStringInputStream); + stringStream.data = string; + converter.onStartRequest(null, null); + converter.onDataAvailable(null, null, stringStream, 0, string.length); + converter.onStopRequest(null, null, null); +} + +function doubleGzipCompressString(string, observer) { + let observer2 = { + onStreamComplete: function(loader, context, status, length, result) { + let buffer = String.fromCharCode.apply(this, result); + gzipCompressString(buffer, observer); + } + }; + gzipCompressString(string, observer2); +} + +function handleRequest(request, response) { + response.processAsync(); + + let params = request.queryString.split("&"); + let format = (params.filter((s) => s.includes("fmt="))[0] || "").split("=")[1]; + let status = (params.filter((s) => s.includes("sts="))[0] || "").split("=")[1] || 200; + + let cachedCount = 0; + let cacheExpire = 60; // seconds + + function setCacheHeaders() { + if (status != 304) { + response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); + return; + } + // Spice things up a little! + if (cachedCount % 2) { + response.setHeader("Cache-Control", "max-age=" + cacheExpire, false); + } else { + response.setHeader("Expires", Date(Date.now() + cacheExpire * 1000), false); + } + cachedCount++; + } + + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback(() => { + // to avoid garbage collection + timer = null; + switch (format) { + case "txt": { + response.setStatusLine(request.httpVersion, status, "DA DA DA"); + response.setHeader("Content-Type", "text/plain", false); + setCacheHeaders(); + response.write("Братан, ты вообще качаешься?"); + response.finish(); + break; + } + case "xml": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/xml; charset=utf-8", false); + setCacheHeaders(); + response.write("<label value='greeting'>Hello XML!</label>"); + response.finish(); + break; + } + case "html": { + let content = (params.filter((s) => s.includes("res="))[0] || "").split("=")[1]; + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + setCacheHeaders(); + response.write(content || "<p>Hello HTML!</p>"); + response.finish(); + break; + } + case "html-long": { + let str = new Array(102400 /* 100 KB in bytes */).join("."); + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + setCacheHeaders(); + response.write("<p>" + str + "</p>"); + response.finish(); + break; + } + case "css": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/css; charset=utf-8", false); + setCacheHeaders(); + response.write("body:pre { content: 'Hello CSS!' }"); + response.finish(); + break; + } + case "js": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "application/javascript; charset=utf-8", false); + setCacheHeaders(); + response.write("function() { return 'Hello JS!'; }"); + response.finish(); + break; + } + case "json": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "application/json; charset=utf-8", false); + setCacheHeaders(); + response.write("{ \"greeting\": \"Hello JSON!\" }"); + response.finish(); + break; + } + case "jsonp": { + let fun = (params.filter((s) => s.includes("jsonp="))[0] || "").split("=")[1]; + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/json; charset=utf-8", false); + setCacheHeaders(); + response.write(fun + "({ \"greeting\": \"Hello JSONP!\" })"); + response.finish(); + break; + } + case "jsonp2": { + let fun = (params.filter((s) => s.includes("jsonp="))[0] || "").split("=")[1]; + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/json; charset=utf-8", false); + setCacheHeaders(); + response.write(" " + fun + " ( { \"greeting\": \"Hello weird JSONP!\" } ) ; "); + response.finish(); + break; + } + case "json-long": { + let str = "{ \"greeting\": \"Hello long string JSON!\" },"; + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/json; charset=utf-8", false); + setCacheHeaders(); + response.write("[" + new Array(2048).join(str).slice(0, -1) + "]"); + response.finish(); + break; + } + case "json-malformed": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/json; charset=utf-8", false); + setCacheHeaders(); + response.write("{ \"greeting\": \"Hello malformed JSON!\" },"); + response.finish(); + break; + } + case "json-text-mime": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/plain; charset=utf-8", false); + setCacheHeaders(); + response.write("{ \"greeting\": \"Hello third-party JSON!\" }"); + response.finish(); + break; + } + case "json-custom-mime": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/x-bigcorp-json; charset=utf-8", false); + setCacheHeaders(); + response.write("{ \"greeting\": \"Hello oddly-named JSON!\" }"); + response.finish(); + break; + } + case "font": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "font/woff", false); + setCacheHeaders(); + response.finish(); + break; + } + case "image": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "image/png", false); + setCacheHeaders(); + response.finish(); + break; + } + case "audio": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "audio/ogg", false); + setCacheHeaders(); + response.finish(); + break; + } + case "video": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "video/webm", false); + setCacheHeaders(); + response.finish(); + break; + } + case "flash": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "application/x-shockwave-flash", false); + setCacheHeaders(); + response.finish(); + break; + } + case "ws": { + response.setStatusLine(request.httpVersion, 101, "Switching Protocols"); + response.setHeader("Connection", "upgrade", false); + response.setHeader("Upgrade", "websocket", false); + setCacheHeaders(); + response.finish(); + break; + } + case "gzip": { + // Note: we're doing a double gzip encoding to test multiple + // converters in network monitor. + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/plain", false); + response.setHeader("Content-Encoding", "gzip\t ,gzip", false); + setCacheHeaders(); + let observer = { + onStreamComplete: function(loader, context, status, length, result) { + let buffer = String.fromCharCode.apply(this, result); + response.setHeader("Content-Length", "" + buffer.length, false); + response.write(buffer); + response.finish(); + } + }; + let data = new Array(1000).join("Hello gzip!"); + doubleGzipCompressString(data, observer); + break; + } + case "br": { + response.setStatusLine(request.httpVersion, status, "Connected"); + response.setHeader("Content-Type", "text/plain", false); + response.setHeader("Content-Encoding", "br", false); + setCacheHeaders(); + response.setHeader("Content-Length", "10", false); + // Use static data since we cannot encode brotli. + response.write("\x1b\x3f\x00\x00\x24\xb0\xe2\x99\x80\x12"); + response.finish(); + break; + } + case "hls-m3u8": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "application/x-mpegurl", false); + setCacheHeaders(); + response.write("#EXTM3U\n"); + response.finish(); + break; + } + case "mpeg-dash": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "video/vnd.mpeg.dash.mpd", false); + setCacheHeaders(); + response.write('<?xml version="1.0" encoding="UTF-8"?>\n'); + response.write('<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></MPD>\n'); + response.finish(); + break; + } + default: { + response.setStatusLine(request.httpVersion, 404, "Not Found"); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + setCacheHeaders(); + response.write("<blink>Not Found</blink>"); + response.finish(); + break; + } + } + }, 10, Ci.nsITimer.TYPE_ONE_SHOT); // Make sure this request takes a few ms. +} diff --git a/devtools/client/netmonitor/test/sjs_cors-test-server.sjs b/devtools/client/netmonitor/test/sjs_cors-test-server.sjs new file mode 100644 index 000000000..0bab80901 --- /dev/null +++ b/devtools/client/netmonitor/test/sjs_cors-test-server.sjs @@ -0,0 +1,17 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 200, "Och Aye"); + + response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); + + response.setHeader("Access-Control-Allow-Origin", "*", false); + response.setHeader("Access-Control-Allow-Headers", "content-type", false); + + response.setHeader("Content-Type", "text/plain; charset=utf-8", false); + + response.write("Access-Control-Allow-Origin: *"); +} diff --git a/devtools/client/netmonitor/test/sjs_hsts-test-server.sjs b/devtools/client/netmonitor/test/sjs_hsts-test-server.sjs new file mode 100644 index 000000000..c5715886e --- /dev/null +++ b/devtools/client/netmonitor/test/sjs_hsts-test-server.sjs @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); + + if (request.queryString === "reset") { + // Reset the HSTS policy, prevent influencing other tests + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Strict-Transport-Security", "max-age=0"); + response.write("Resetting HSTS"); + } else if (request.scheme === "http") { + response.setStatusLine(request.httpVersion, 302, "Found"); + response.setHeader("Location", "https://" + request.host + request.path); + } else { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Strict-Transport-Security", "max-age=100"); + response.write("Page was accessed over HTTPS!"); + } +} diff --git a/devtools/client/netmonitor/test/sjs_https-redirect-test-server.sjs b/devtools/client/netmonitor/test/sjs_https-redirect-test-server.sjs new file mode 100644 index 000000000..14ea34559 --- /dev/null +++ b/devtools/client/netmonitor/test/sjs_https-redirect-test-server.sjs @@ -0,0 +1,19 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); + + response.setHeader("Access-Control-Allow-Origin", "*", false); + + if (request.scheme === "http") { + response.setStatusLine(request.httpVersion, 302, "Found"); + response.setHeader("Location", "https://" + request.host + request.path); + } else { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write("Page was accessed over HTTPS!"); + } + +} diff --git a/devtools/client/netmonitor/test/sjs_simple-test-server.sjs b/devtools/client/netmonitor/test/sjs_simple-test-server.sjs new file mode 100644 index 000000000..9a3d44b6d --- /dev/null +++ b/devtools/client/netmonitor/test/sjs_simple-test-server.sjs @@ -0,0 +1,17 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 200, "Och Aye"); + + response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); + + response.setHeader("Set-Cookie", "bob=true; Max-Age=10; HttpOnly", true); + response.setHeader("Set-Cookie", "tom=cool; Max-Age=10; HttpOnly", true); + + response.setHeader("Content-Type", "text/plain; charset=utf-8", false); + response.setHeader("Foo-Bar", "baz", false); + response.write("Hello world!"); +} diff --git a/devtools/client/netmonitor/test/sjs_sorting-test-server.sjs b/devtools/client/netmonitor/test/sjs_sorting-test-server.sjs new file mode 100644 index 000000000..54c62866b --- /dev/null +++ b/devtools/client/netmonitor/test/sjs_sorting-test-server.sjs @@ -0,0 +1,26 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { classes: Cc, interfaces: Ci } = Components; + +function handleRequest(request, response) { + response.processAsync(); + + let params = request.queryString.split("&"); + let index = params.filter((s) => s.includes("index="))[0].split("=")[1]; + + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback(() => { + // to avoid garbage collection + timer = null; + response.setStatusLine(request.httpVersion, index == 1 ? 101 : index * 100, "Meh"); + + response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); + + response.setHeader("Content-Type", "text/" + index, false); + response.write(new Array(index * 10).join(index)); // + 0.01 KB + response.finish(); + }, 10, Ci.nsITimer.TYPE_ONE_SHOT); // Make sure this request takes a few ms. +} diff --git a/devtools/client/netmonitor/test/sjs_status-codes-test-server.sjs b/devtools/client/netmonitor/test/sjs_status-codes-test-server.sjs new file mode 100644 index 000000000..4f17d1235 --- /dev/null +++ b/devtools/client/netmonitor/test/sjs_status-codes-test-server.sjs @@ -0,0 +1,56 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { classes: Cc, interfaces: Ci } = Components; + +function handleRequest(request, response) { + response.processAsync(); + + let params = request.queryString.split("&"); + let status = params.filter(s => s.includes("sts="))[0].split("=")[1]; + let cached = params.filter(s => s === 'cached').length !== 0; + + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback(() => { + // to avoid garbage collection + timer = null; + switch (status) { + case "100": + response.setStatusLine(request.httpVersion, 101, "Switching Protocols"); + break; + case "200": + response.setStatusLine(request.httpVersion, 202, "Created"); + break; + case "300": + response.setStatusLine(request.httpVersion, 303, "See Other"); + break; + case "400": + response.setStatusLine(request.httpVersion, 404, "Not Found"); + break; + case "500": + response.setStatusLine(request.httpVersion, 501, "Not Implemented"); + break; + case "ok": + response.setStatusLine(request.httpVersion, 200, "OK"); + break; + case "redirect": + response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); + response.setHeader("Location", "http://example.com/redirected"); + break; + } + + if(!cached) { + response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); + } + else { + response.setHeader("Cache-Control", "no-transform,public,max-age=300,s-maxage=900"); + response.setHeader("Expires", "Thu, 01 Dec 2100 20:00:00 GMT"); + } + + response.setHeader("Content-Type", "text/plain; charset=utf-8", false); + response.write("Hello status code " + status + "!"); + response.finish(); + }, 10, Ci.nsITimer.TYPE_ONE_SHOT); // Make sure this request takes a few ms. +} diff --git a/devtools/client/netmonitor/test/sjs_truncate-test-server.sjs b/devtools/client/netmonitor/test/sjs_truncate-test-server.sjs new file mode 100644 index 000000000..54db23d9a --- /dev/null +++ b/devtools/client/netmonitor/test/sjs_truncate-test-server.sjs @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function handleRequest(request, response) { + let params = request.queryString.split("&"); + let limit = (params.filter((s) => s.includes("limit="))[0] || "").split("=")[1]; + + response.setStatusLine(request.httpVersion, 200, "Och Aye"); + + response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); + response.setHeader("Content-Type", "text/plain; charset=utf-8", false); + + response.write("x".repeat(2 * parseInt(limit, 10))); + + response.write("Hello world!"); +} diff --git a/devtools/client/netmonitor/test/test-image.png b/devtools/client/netmonitor/test/test-image.png Binary files differnew file mode 100644 index 000000000..769c63634 --- /dev/null +++ b/devtools/client/netmonitor/test/test-image.png |