зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1290437 - Fix and land the console netlogging tests r=jsnajdr
MozReview-Commit-ID: 9HRfcfn8g6b --HG-- extra : rebase_source : 3630e5fdba23076c1553d3ce3d966e80cc2979a4
This commit is contained in:
Родитель
adf056b0ee
Коммит
95dd013945
|
@ -26,31 +26,37 @@ var CookiesTab = React.createClass({
|
||||||
|
|
||||||
displayName: "CookiesTab",
|
displayName: "CookiesTab",
|
||||||
|
|
||||||
render() {
|
componentDidMount() {
|
||||||
let actions = this.props.actions;
|
let { actions, data } = this.props;
|
||||||
let file = this.props.data;
|
let requestCookies = data.request.cookies;
|
||||||
|
let responseCookies = data.response.cookies;
|
||||||
|
|
||||||
let cookies = file.request.cookies;
|
// TODO: use async action objects as soon as Redux is in place
|
||||||
if (!cookies || !cookies.length) {
|
if (!requestCookies || !requestCookies.length) {
|
||||||
// TODO: use async action objects as soon as Redux is in place
|
|
||||||
actions.requestData("requestCookies");
|
actions.requestData("requestCookies");
|
||||||
|
|
||||||
return (
|
|
||||||
Spinner()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!responseCookies || !responseCookies.length) {
|
||||||
|
actions.requestData("responseCookies");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { actions, data: file } = this.props;
|
||||||
|
let requestCookies = file.request.cookies;
|
||||||
|
let responseCookies = file.response.cookies;
|
||||||
|
|
||||||
// The cookie panel displays two groups of cookies:
|
// The cookie panel displays two groups of cookies:
|
||||||
// 1) Response Cookies
|
// 1) Response Cookies
|
||||||
// 2) Request Cookies
|
// 2) Request Cookies
|
||||||
let groups = [{
|
let groups = [{
|
||||||
key: "responseCookies",
|
key: "responseCookies",
|
||||||
name: Locale.$STR("responseCookies"),
|
name: Locale.$STR("responseCookies"),
|
||||||
params: file.response.cookies
|
params: responseCookies
|
||||||
}, {
|
}, {
|
||||||
key: "requestCookies",
|
key: "requestCookies",
|
||||||
name: Locale.$STR("requestCookies"),
|
name: Locale.$STR("requestCookies"),
|
||||||
params: file.request.cookies
|
params: requestCookies
|
||||||
}];
|
}];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -26,10 +26,10 @@ var HeadersTab = React.createClass({
|
||||||
|
|
||||||
displayName: "HeadersTab",
|
displayName: "HeadersTab",
|
||||||
|
|
||||||
render() {
|
componentDidMount() {
|
||||||
let {data, actions} = this.props;
|
let { actions, data } = this.props;
|
||||||
let responseHeaders = data.response.headers;
|
|
||||||
let requestHeaders = data.request.headers;
|
let requestHeaders = data.request.headers;
|
||||||
|
let responseHeaders = data.response.headers;
|
||||||
|
|
||||||
// Request headers if they are not available yet.
|
// Request headers if they are not available yet.
|
||||||
// TODO: use async action objects as soon as Redux is in place
|
// TODO: use async action objects as soon as Redux is in place
|
||||||
|
@ -40,6 +40,12 @@ var HeadersTab = React.createClass({
|
||||||
if (!responseHeaders) {
|
if (!responseHeaders) {
|
||||||
actions.requestData("responseHeaders");
|
actions.requestData("responseHeaders");
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { data } = this.props;
|
||||||
|
let requestHeaders = data.request.headers;
|
||||||
|
let responseHeaders = data.response.headers;
|
||||||
|
|
||||||
// TODO: Another groups to implement:
|
// TODO: Another groups to implement:
|
||||||
// 1) Cached Headers
|
// 1) Cached Headers
|
||||||
|
|
|
@ -45,7 +45,7 @@ var NetInfoBody = React.createClass({
|
||||||
|
|
||||||
getDefaultProps() {
|
getDefaultProps() {
|
||||||
return {
|
return {
|
||||||
tabActive: 1
|
tabActive: 0
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -65,8 +65,9 @@ var NetInfoBody = React.createClass({
|
||||||
|
|
||||||
hasCookies() {
|
hasCookies() {
|
||||||
let {request, response} = this.state.data;
|
let {request, response} = this.state.data;
|
||||||
return NetUtils.getHeaderValue(request.headers, "Cookie") ||
|
return this.state.hasCookies ||
|
||||||
NetUtils.getHeaderValue(response.headers, "Cookie");
|
NetUtils.getHeaderValue(request.headers, "Cookie") ||
|
||||||
|
NetUtils.getHeaderValue(response.headers, "Set-Cookie");
|
||||||
},
|
},
|
||||||
|
|
||||||
hasStackTrace() {
|
hasStackTrace() {
|
||||||
|
|
|
@ -38,9 +38,9 @@ var PostTab = React.createClass({
|
||||||
displayName: "PostTab",
|
displayName: "PostTab",
|
||||||
|
|
||||||
isJson(file) {
|
isJson(file) {
|
||||||
let postData = file.request.postData;
|
let text = file.request.postData.text;
|
||||||
let value = NetUtils.getHeaderValue(file.request.headers, "content-type");
|
let value = NetUtils.getHeaderValue(file.request.headers, "content-type");
|
||||||
return Json.isJSON(value, postData);
|
return Json.isJSON(value, text);
|
||||||
},
|
},
|
||||||
|
|
||||||
parseJson(file) {
|
parseJson(file) {
|
||||||
|
@ -202,9 +202,17 @@ var PostTab = React.createClass({
|
||||||
return group;
|
return group;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
let { actions, data: file } = this.props;
|
||||||
|
|
||||||
|
if (!file.request.postData) {
|
||||||
|
// TODO: use async action objects as soon as Redux is in place
|
||||||
|
actions.requestData("requestPostData");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let actions = this.props.actions;
|
let { actions, data: file } = this.props;
|
||||||
let file = this.props.data;
|
|
||||||
|
|
||||||
if (file.discardRequestBody) {
|
if (file.discardRequestBody) {
|
||||||
return DOM.span({className: "netInfoBodiesDiscarded"},
|
return DOM.span({className: "netInfoBodiesDiscarded"},
|
||||||
|
@ -212,10 +220,7 @@ var PostTab = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let postData = file.request.postData;
|
if (!file.request.postData) {
|
||||||
if (!postData) {
|
|
||||||
// TODO: use async action objects as soon as Redux is in place
|
|
||||||
actions.requestData("requestPostData");
|
|
||||||
return (
|
return (
|
||||||
Spinner()
|
Spinner()
|
||||||
);
|
);
|
||||||
|
|
|
@ -199,6 +199,16 @@ var ResponseTab = React.createClass({
|
||||||
return group;
|
return group;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
let { actions, data: file } = this.props;
|
||||||
|
let content = file.response.content;
|
||||||
|
|
||||||
|
if (!content || typeof (content.text) == "undefined") {
|
||||||
|
// TODO: use async action objects as soon as Redux is in place
|
||||||
|
actions.requestData("responseContent");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The response panel displays two groups:
|
* The response panel displays two groups:
|
||||||
*
|
*
|
||||||
|
@ -206,8 +216,7 @@ var ResponseTab = React.createClass({
|
||||||
* 2) Raw response data (always displayed if not discarded)
|
* 2) Raw response data (always displayed if not discarded)
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
let actions = this.props.actions;
|
let { actions, data: file } = this.props;
|
||||||
let file = this.props.data;
|
|
||||||
|
|
||||||
// If response bodies are discarded (not collected) let's just
|
// If response bodies are discarded (not collected) let's just
|
||||||
// display a info message indicating what to do to collect even
|
// display a info message indicating what to do to collect even
|
||||||
|
@ -224,9 +233,6 @@ var ResponseTab = React.createClass({
|
||||||
// empty or not available yet.
|
// empty or not available yet.
|
||||||
let content = file.response.content;
|
let content = file.response.content;
|
||||||
if (!content || typeof (content.text) == "undefined") {
|
if (!content || typeof (content.text) == "undefined") {
|
||||||
// TODO: use async action objects as soon as Redux is in place
|
|
||||||
actions.requestData("responseContent");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
Spinner()
|
Spinner()
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,3 +14,6 @@ DevToolsModules(
|
||||||
'net-request.css',
|
'net-request.css',
|
||||||
'net-request.js',
|
'net-request.js',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
|
||||||
|
BROWSER_CHROME_MANIFESTS += ['test/mochitest/browser.ini']
|
||||||
|
|
|
@ -47,6 +47,7 @@ NetRequest.prototype = {
|
||||||
this.file = log.response;
|
this.file = log.response;
|
||||||
this.parentNode = log.node;
|
this.parentNode = log.node;
|
||||||
this.file.request.queryString = parseURLParams(this.file.request.url);
|
this.file.request.queryString = parseURLParams(this.file.request.url);
|
||||||
|
this.hasCookies = false;
|
||||||
|
|
||||||
// Map of fetched responses (to avoid unnecessary RDP round trip).
|
// Map of fetched responses (to avoid unnecessary RDP round trip).
|
||||||
this.cachedResponses = new Map();
|
this.cachedResponses = new Map();
|
||||||
|
@ -112,6 +113,16 @@ NetRequest.prototype = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateCookies: function(method, response) {
|
||||||
|
// TODO: This code will be part of a reducer.
|
||||||
|
let result;
|
||||||
|
if (response.cookies > 0 &&
|
||||||
|
["requestCookies", "responseCookies"].includes(method)) {
|
||||||
|
this.hasCookies = true;
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executed when 'networkEventUpdate' is received from the backend.
|
* Executed when 'networkEventUpdate' is received from the backend.
|
||||||
*/
|
*/
|
||||||
|
@ -121,8 +132,8 @@ NetRequest.prototype = {
|
||||||
// cache and if this data has been already requested before they
|
// cache and if this data has been already requested before they
|
||||||
// need to be updated now (re-requested).
|
// need to be updated now (re-requested).
|
||||||
let method = response.updateType;
|
let method = response.updateType;
|
||||||
let cached = this.cachedResponses.get(method);
|
this.updateCookies(method, response);
|
||||||
if (cached) {
|
if (this.cachedResponses.get(method)) {
|
||||||
this.cachedResponses.delete(method);
|
this.cachedResponses.delete(method);
|
||||||
this.requestData(method);
|
this.requestData(method);
|
||||||
}
|
}
|
||||||
|
@ -172,7 +183,8 @@ NetRequest.prototype = {
|
||||||
// TODO: As soon as Redux is in place there will be reducer
|
// TODO: As soon as Redux is in place there will be reducer
|
||||||
// computing a new state.
|
// computing a new state.
|
||||||
let newState = Object.assign({}, this.body.state, {
|
let newState = Object.assign({}, this.body.state, {
|
||||||
data: this.file
|
data: this.file,
|
||||||
|
hasCookies: this.hasCookies
|
||||||
});
|
});
|
||||||
|
|
||||||
this.body.setState(newState);
|
this.body.setState(newState);
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
// Extend from the shared list of defined globals for mochitests.
|
||||||
|
"extends": "../../../../../.eslintrc.mochitests",
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
[DEFAULT]
|
||||||
|
tags = devtools
|
||||||
|
subsuite = devtools
|
||||||
|
support-files =
|
||||||
|
head.js
|
||||||
|
page_basic.html
|
||||||
|
test.json
|
||||||
|
test.json^headers^
|
||||||
|
test-cookies.json
|
||||||
|
test-cookies.json^headers^
|
||||||
|
test.txt
|
||||||
|
test.xml
|
||||||
|
test.xml^headers^
|
||||||
|
!/devtools/client/webconsole/test/head.js
|
||||||
|
!/devtools/client/framework/test/shared-head.js
|
||||||
|
|
||||||
|
[browser_net_basic.js]
|
||||||
|
[browser_net_cookies.js]
|
||||||
|
[browser_net_headers.js]
|
||||||
|
[browser_net_params.js]
|
||||||
|
[browser_net_post.js]
|
||||||
|
[browser_net_response.js]
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
|
||||||
|
const JSON_XHR_URL = URL_ROOT + "test.json";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic test that generates XHR in the content and
|
||||||
|
* checks the related log in the Console panel can
|
||||||
|
* be expanded.
|
||||||
|
*/
|
||||||
|
add_task(function* () {
|
||||||
|
info("Test XHR Spy basic started");
|
||||||
|
|
||||||
|
let {hud} = yield addTestTab(TEST_PAGE_URL);
|
||||||
|
|
||||||
|
let netInfoBody = yield executeAndInspectXhr(hud, {
|
||||||
|
method: "GET",
|
||||||
|
url: JSON_XHR_URL
|
||||||
|
});
|
||||||
|
|
||||||
|
ok(netInfoBody, "The network details must be available");
|
||||||
|
|
||||||
|
// There should be at least two tabs: Headers and Response
|
||||||
|
ok(netInfoBody.querySelector(".tabs .tabs-menu-item.headers"),
|
||||||
|
"Headers tab must be available");
|
||||||
|
ok(netInfoBody.querySelector(".tabs .tabs-menu-item.response"),
|
||||||
|
"Response tab must be available");
|
||||||
|
});
|
|
@ -0,0 +1,54 @@
|
||||||
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
|
||||||
|
const JSON_XHR_URL = URL_ROOT + "test-cookies.json";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test generates XHR requests in the page, expands
|
||||||
|
* networks details in the Console panel and checks that
|
||||||
|
* Cookies are properly displayed.
|
||||||
|
*/
|
||||||
|
add_task(function* () {
|
||||||
|
info("Test XHR Spy cookies started");
|
||||||
|
|
||||||
|
let {hud} = yield addTestTab(TEST_PAGE_URL);
|
||||||
|
|
||||||
|
let netInfoBody = yield executeAndInspectXhr(hud, {
|
||||||
|
method: "GET",
|
||||||
|
url: JSON_XHR_URL
|
||||||
|
});
|
||||||
|
|
||||||
|
// Select "Cookies" tab
|
||||||
|
let tabBody = yield selectNetInfoTab(hud, netInfoBody, "cookies");
|
||||||
|
|
||||||
|
let requestCookieName = tabBody.querySelector(
|
||||||
|
".netInfoGroup.requestCookies .netInfoParamName > span[title='bar']");
|
||||||
|
|
||||||
|
// Verify request cookies (name and value)
|
||||||
|
ok(requestCookieName, "Request Cookie name must exist");
|
||||||
|
is(requestCookieName.textContent, "bar",
|
||||||
|
"The cookie name must have proper value");
|
||||||
|
|
||||||
|
let requestCookieValue = requestCookieName.parentNode.nextSibling;
|
||||||
|
ok(requestCookieValue, "Request Cookie value must exist");
|
||||||
|
is(requestCookieValue.textContent, "foo",
|
||||||
|
"The cookie value must have proper value");
|
||||||
|
|
||||||
|
let responseCookieName = tabBody.querySelector(
|
||||||
|
".netInfoGroup.responseCookies .netInfoParamName > span[title='test']");
|
||||||
|
|
||||||
|
// Verify response cookies (name and value)
|
||||||
|
ok(responseCookieName, "Response Cookie name must exist");
|
||||||
|
is(responseCookieName.textContent, "test",
|
||||||
|
"The cookie name must have proper value");
|
||||||
|
|
||||||
|
let responseCookieValue = responseCookieName.parentNode.nextSibling;
|
||||||
|
ok(responseCookieValue, "Response Cookie value must exist");
|
||||||
|
is(responseCookieValue.textContent, "abc",
|
||||||
|
"The cookie value must have proper value");
|
||||||
|
});
|
|
@ -0,0 +1,40 @@
|
||||||
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
|
||||||
|
const JSON_XHR_URL = URL_ROOT + "test.json";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test generates XHR requests in the page, expands
|
||||||
|
* networks details in the Console panel and checks that
|
||||||
|
* HTTP headers are there.
|
||||||
|
*/
|
||||||
|
add_task(function* () {
|
||||||
|
info("Test XHR Spy headers started");
|
||||||
|
|
||||||
|
let {hud} = yield addTestTab(TEST_PAGE_URL);
|
||||||
|
|
||||||
|
let netInfoBody = yield executeAndInspectXhr(hud, {
|
||||||
|
method: "GET",
|
||||||
|
url: JSON_XHR_URL
|
||||||
|
});
|
||||||
|
|
||||||
|
// Select "Headers" tab
|
||||||
|
let tabBody = yield selectNetInfoTab(hud, netInfoBody, "headers");
|
||||||
|
let paramName = tabBody.querySelector(
|
||||||
|
".netInfoParamName > span[title='Content-Type']");
|
||||||
|
|
||||||
|
// Verify "Content-Type" header (name and value)
|
||||||
|
ok(paramName, "Header name must exist");
|
||||||
|
is(paramName.textContent, "Content-Type",
|
||||||
|
"The header name must have proper value");
|
||||||
|
|
||||||
|
let paramValue = paramName.parentNode.nextSibling;
|
||||||
|
ok(paramValue, "Header value must exist");
|
||||||
|
is(paramValue.textContent, "application/json; charset=utf-8",
|
||||||
|
"The header value must have proper value");
|
||||||
|
});
|
|
@ -0,0 +1,42 @@
|
||||||
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
|
||||||
|
const JSON_XHR_URL = URL_ROOT + "test.json";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test generates XHR requests in the page, expands
|
||||||
|
* networks details in the Console panel and checks that
|
||||||
|
* HTTP parameters (query string) are there.
|
||||||
|
*/
|
||||||
|
add_task(function* () {
|
||||||
|
info("Test XHR Spy params started");
|
||||||
|
|
||||||
|
let {hud} = yield addTestTab(TEST_PAGE_URL);
|
||||||
|
|
||||||
|
let netInfoBody = yield executeAndInspectXhr(hud, {
|
||||||
|
method: "GET",
|
||||||
|
url: JSON_XHR_URL,
|
||||||
|
queryString: "?foo=bar"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check headers
|
||||||
|
let tabBody = yield selectNetInfoTab(hud, netInfoBody, "params");
|
||||||
|
|
||||||
|
let paramName = tabBody.querySelector(
|
||||||
|
".netInfoParamName > span[title='foo']");
|
||||||
|
|
||||||
|
// Verify "Content-Type" header (name and value)
|
||||||
|
ok(paramName, "Header name must exist");
|
||||||
|
is(paramName.textContent, "foo",
|
||||||
|
"The param name must have proper value");
|
||||||
|
|
||||||
|
let paramValue = paramName.parentNode.nextSibling;
|
||||||
|
ok(paramValue, "param value must exist");
|
||||||
|
is(paramValue.textContent, "bar",
|
||||||
|
"The param value must have proper value");
|
||||||
|
});
|
|
@ -0,0 +1,88 @@
|
||||||
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
|
||||||
|
const JSON_XHR_URL = URL_ROOT + "test.json";
|
||||||
|
|
||||||
|
const plainPostBody = "test-data";
|
||||||
|
const jsonData = "{\"bar\": \"baz\"}";
|
||||||
|
const jsonRendered = "bar\"baz\"";
|
||||||
|
const xmlPostBody = "<xml><name>John</name></xml>";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test generates XHR requests in the page, expands
|
||||||
|
* networks details in the Console panel and checks that
|
||||||
|
* Post data are properly rendered.
|
||||||
|
*/
|
||||||
|
add_task(function* () {
|
||||||
|
info("Test XHR Spy post plain body started");
|
||||||
|
|
||||||
|
let {hud} = yield addTestTab(TEST_PAGE_URL);
|
||||||
|
|
||||||
|
let netInfoBody = yield executeAndInspectXhr(hud, {
|
||||||
|
method: "POST",
|
||||||
|
url: JSON_XHR_URL,
|
||||||
|
body: plainPostBody
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check post body data
|
||||||
|
let tabBody = yield selectNetInfoTab(hud, netInfoBody, "post");
|
||||||
|
let postContent = tabBody.querySelector(
|
||||||
|
".netInfoGroup.raw.opened .netInfoGroupContent");
|
||||||
|
is(postContent.textContent, plainPostBody,
|
||||||
|
"Post body must be properly rendered");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* () {
|
||||||
|
info("Test XHR Spy post JSON body started");
|
||||||
|
|
||||||
|
let {hud} = yield addTestTab(TEST_PAGE_URL);
|
||||||
|
|
||||||
|
let netInfoBody = yield executeAndInspectXhr(hud, {
|
||||||
|
method: "POST",
|
||||||
|
url: JSON_XHR_URL,
|
||||||
|
body: jsonData,
|
||||||
|
requestHeaders: [{
|
||||||
|
name: "Content-Type",
|
||||||
|
value: "application/json"
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check post body data
|
||||||
|
let tabBody = yield selectNetInfoTab(hud, netInfoBody, "post");
|
||||||
|
let postContent = tabBody.querySelector(
|
||||||
|
".netInfoGroup.json.opened .netInfoGroupContent");
|
||||||
|
is(postContent.textContent, jsonRendered,
|
||||||
|
"Post body must be properly rendered");
|
||||||
|
|
||||||
|
let rawPostContent = tabBody.querySelector(
|
||||||
|
".netInfoGroup.raw.opened .netInfoGroupContent");
|
||||||
|
ok(!rawPostContent, "Raw response group must be collapsed");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* () {
|
||||||
|
info("Test XHR Spy post XML body started");
|
||||||
|
|
||||||
|
let {hud} = yield addTestTab(TEST_PAGE_URL);
|
||||||
|
|
||||||
|
let netInfoBody = yield executeAndInspectXhr(hud, {
|
||||||
|
method: "POST",
|
||||||
|
url: JSON_XHR_URL,
|
||||||
|
body: xmlPostBody,
|
||||||
|
requestHeaders: [{
|
||||||
|
name: "Content-Type",
|
||||||
|
value: "application/xml"
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check post body data
|
||||||
|
let tabBody = yield selectNetInfoTab(hud, netInfoBody, "post");
|
||||||
|
let rawPostContent = tabBody.querySelector(
|
||||||
|
".netInfoGroup.raw.opened .netInfoGroupContent");
|
||||||
|
is(rawPostContent.textContent, xmlPostBody,
|
||||||
|
"Raw response group must not be collapsed");
|
||||||
|
});
|
|
@ -0,0 +1,86 @@
|
||||||
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
|
||||||
|
const TEXT_XHR_URL = URL_ROOT + "test.txt";
|
||||||
|
const JSON_XHR_URL = URL_ROOT + "test.json";
|
||||||
|
const XML_XHR_URL = URL_ROOT + "test.xml";
|
||||||
|
|
||||||
|
const textResponseBody = "this is a response";
|
||||||
|
const jsonResponseBody = "name\"John\"";
|
||||||
|
|
||||||
|
// Individual tests below generate XHR request in the page, expand
|
||||||
|
// network details in the Console panel and checks various types
|
||||||
|
// of response bodies.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate plain text response
|
||||||
|
*/
|
||||||
|
add_task(function* () {
|
||||||
|
info("Test XHR Spy respone plain body started");
|
||||||
|
|
||||||
|
let {hud} = yield addTestTab(TEST_PAGE_URL);
|
||||||
|
|
||||||
|
let netInfoBody = yield executeAndInspectXhr(hud, {
|
||||||
|
method: "GET",
|
||||||
|
url: TEXT_XHR_URL,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check response body data
|
||||||
|
let tabBody = yield selectNetInfoTab(hud, netInfoBody, "response");
|
||||||
|
let responseContent = tabBody.querySelector(
|
||||||
|
".netInfoGroup.raw.opened .netInfoGroupContent");
|
||||||
|
|
||||||
|
ok(responseContent.textContent.indexOf(textResponseBody) > -1,
|
||||||
|
"Response body must be properly rendered");
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate XML response
|
||||||
|
*/
|
||||||
|
add_task(function* () {
|
||||||
|
info("Test XHR Spy response XML body started");
|
||||||
|
|
||||||
|
let {hud} = yield addTestTab(TEST_PAGE_URL);
|
||||||
|
|
||||||
|
let netInfoBody = yield executeAndInspectXhr(hud, {
|
||||||
|
method: "GET",
|
||||||
|
url: XML_XHR_URL,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check response body data
|
||||||
|
let tabBody = yield selectNetInfoTab(hud, netInfoBody, "response");
|
||||||
|
let rawResponseContent = tabBody.querySelector(
|
||||||
|
".netInfoGroup.raw.opened .netInfoGroupContent");
|
||||||
|
ok(rawResponseContent, "Raw response group must not be collapsed");
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate JSON response
|
||||||
|
*/
|
||||||
|
add_task(function* () {
|
||||||
|
info("Test XHR Spy response JSON body started");
|
||||||
|
|
||||||
|
let {hud} = yield addTestTab(TEST_PAGE_URL);
|
||||||
|
|
||||||
|
let netInfoBody = yield executeAndInspectXhr(hud, {
|
||||||
|
method: "GET",
|
||||||
|
url: JSON_XHR_URL,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check response body data
|
||||||
|
let tabBody = yield selectNetInfoTab(hud, netInfoBody, "response");
|
||||||
|
let responseContent = tabBody.querySelector(
|
||||||
|
".netInfoGroup.json .netInfoGroupContent");
|
||||||
|
|
||||||
|
is(responseContent.textContent, jsonResponseBody,
|
||||||
|
"Response body must be properly rendered");
|
||||||
|
|
||||||
|
let rawResponseContent = tabBody.querySelector(
|
||||||
|
".netInfoGroup.raw.opened .netInfoGroupContent");
|
||||||
|
ok(!rawResponseContent, "Raw response group must be collapsed");
|
||||||
|
});
|
|
@ -0,0 +1,202 @@
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
|
||||||
|
/* import-globals-from ../../../test/head.js */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Load Web Console head.js, it implements helper console test API
|
||||||
|
Services.scriptloader.loadSubScript(
|
||||||
|
"chrome://mochitests/content/browser/devtools/client/webconsole/test/head.js", this);
|
||||||
|
|
||||||
|
const FRAME_SCRIPT_UTILS_URL =
|
||||||
|
"chrome://devtools/content/shared/frame-script-utils.js";
|
||||||
|
|
||||||
|
const NET_INFO_PREF = "devtools.webconsole.filter.networkinfo";
|
||||||
|
const NET_XHR_PREF = "devtools.webconsole.filter.netxhr";
|
||||||
|
|
||||||
|
// Enable XHR logging for the test
|
||||||
|
Services.prefs.setBoolPref(NET_INFO_PREF, true);
|
||||||
|
Services.prefs.setBoolPref(NET_XHR_PREF, true);
|
||||||
|
|
||||||
|
registerCleanupFunction(() => {
|
||||||
|
Services.prefs.clearUserPref(NET_INFO_PREF, true);
|
||||||
|
Services.prefs.clearUserPref(NET_XHR_PREF, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new test tab in the browser and load the given url.
|
||||||
|
* @param {String} url The url to be loaded in the new tab
|
||||||
|
* @return a promise that resolves to the tab object when the url is loaded
|
||||||
|
*/
|
||||||
|
function addTestTab(url) {
|
||||||
|
info("Adding a new JSON tab with URL: '" + url + "'");
|
||||||
|
|
||||||
|
return Task.spawn(function* () {
|
||||||
|
let tab = yield addTab(url);
|
||||||
|
|
||||||
|
// Load devtools/shared/frame-script-utils.js
|
||||||
|
loadCommonFrameScript(tab);
|
||||||
|
|
||||||
|
// Open the Console panel
|
||||||
|
let hud = yield openConsole();
|
||||||
|
|
||||||
|
return {
|
||||||
|
tab: tab,
|
||||||
|
browser: tab.linkedBrowser,
|
||||||
|
hud: hud
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param hud
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
function executeAndInspectXhr(hud, options) {
|
||||||
|
hud.jsterm.clearOutput();
|
||||||
|
|
||||||
|
options.queryString = options.queryString || "";
|
||||||
|
|
||||||
|
// Execute XHR in the content scope.
|
||||||
|
performRequestsInContent({
|
||||||
|
method: options.method,
|
||||||
|
url: options.url + options.queryString,
|
||||||
|
body: options.body,
|
||||||
|
nocache: options.nocache,
|
||||||
|
requestHeaders: options.requestHeaders
|
||||||
|
});
|
||||||
|
|
||||||
|
return Task.spawn(function* () {
|
||||||
|
// Wait till the appropriate Net log appears in the Console panel.
|
||||||
|
let rules = yield waitForMessages({
|
||||||
|
webconsole: hud,
|
||||||
|
messages: [{
|
||||||
|
text: options.url,
|
||||||
|
category: CATEGORY_NETWORK,
|
||||||
|
severity: SEVERITY_INFO,
|
||||||
|
isXhr: true,
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// The log is here, get its parent element (className: 'message').
|
||||||
|
let msg = [...rules[0].matched][0];
|
||||||
|
let body = msg.querySelector(".message-body");
|
||||||
|
|
||||||
|
// Open XHR HTTP details body and wait till the UI fetches
|
||||||
|
// all necessary data from the backend. All RPD requests
|
||||||
|
// needs to be finished before we can continue testing.
|
||||||
|
yield synthesizeMouseClickSoon(hud, body);
|
||||||
|
yield waitForBackend(msg);
|
||||||
|
let netInfoBody = body.querySelector(".netInfoBody");
|
||||||
|
ok(netInfoBody, "Net info body must exist");
|
||||||
|
return netInfoBody;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait till XHR data are fetched from the backend (i.e. there are
|
||||||
|
* no pending RDP requests.
|
||||||
|
*/
|
||||||
|
function waitForBackend(element) {
|
||||||
|
if (!element.hasAttribute("loading")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return once(element, "netlog-no-pending-requests", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select specific tab in XHR info body.
|
||||||
|
*
|
||||||
|
* @param netInfoBody The main XHR info body
|
||||||
|
* @param tabId Tab ID (possible values: 'headers', 'cookies', 'params',
|
||||||
|
* 'post', 'response');
|
||||||
|
*
|
||||||
|
* @returns Tab body element.
|
||||||
|
*/
|
||||||
|
function selectNetInfoTab(hud, netInfoBody, tabId) {
|
||||||
|
let tab = netInfoBody.querySelector(".tabs-menu-item." + tabId);
|
||||||
|
ok(tab, "Tab must exist " + tabId);
|
||||||
|
|
||||||
|
// Click to select specified tab and wait till its
|
||||||
|
// UI is populated with data from the backend.
|
||||||
|
// There must be no pending RDP requests before we can
|
||||||
|
// continue testing the UI.
|
||||||
|
return Task.spawn(function* () {
|
||||||
|
yield synthesizeMouseClickSoon(hud, tab);
|
||||||
|
let msg = getAncestorByClass(netInfoBody, "message");
|
||||||
|
yield waitForBackend(msg);
|
||||||
|
let tabBody = netInfoBody.querySelector("." + tabId + "TabBox");
|
||||||
|
ok(tabBody, "Tab body must exist");
|
||||||
|
return tabBody;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return parent node with specified class.
|
||||||
|
*
|
||||||
|
* @param node A child element
|
||||||
|
* @param className Specified class name.
|
||||||
|
*
|
||||||
|
* @returns A parent element.
|
||||||
|
*/
|
||||||
|
function getAncestorByClass(node, className) {
|
||||||
|
for (let parent = node; parent; parent = parent.parentNode) {
|
||||||
|
if (parent.classList && parent.classList.contains(className)) {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synthesize asynchronous click event (with clean stack trace).
|
||||||
|
*/
|
||||||
|
function synthesizeMouseClickSoon(hud, element) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
executeSoon(() => {
|
||||||
|
EventUtils.synthesizeMouse(element, 2, 2, {}, hud.iframeWindow);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute XHR in the content scope.
|
||||||
|
*/
|
||||||
|
function performRequestsInContent(requests) {
|
||||||
|
info("Performing requests in the context of the content.");
|
||||||
|
return executeInContent("devtools:test:xhr", requests);
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeInContent(name, data = {}, objects = {},
|
||||||
|
expectResponse = true) {
|
||||||
|
let mm = gBrowser.selectedBrowser.messageManager;
|
||||||
|
|
||||||
|
mm.sendAsyncMessage(name, data, objects);
|
||||||
|
if (expectResponse) {
|
||||||
|
return waitForContentMessage(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForContentMessage(name) {
|
||||||
|
info("Expecting message " + name + " from content");
|
||||||
|
|
||||||
|
let mm = gBrowser.selectedBrowser.messageManager;
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
mm.addMessageListener(name, function onMessage(msg) {
|
||||||
|
mm.removeMessageListener(name, onMessage);
|
||||||
|
resolve(msg.data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCommonFrameScript(tab) {
|
||||||
|
let browser = tab ? tab.linkedBrowser : gBrowser.selectedBrowser;
|
||||||
|
browser.messageManager.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!-- Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>XHR Spy test page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.cookie = "bar=foo";
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1 @@
|
||||||
|
{"name":"Cookies Test"}
|
|
@ -0,0 +1,2 @@
|
||||||
|
Content-Type: application/json; charset=utf-8
|
||||||
|
Set-Cookie: test=abc
|
|
@ -0,0 +1 @@
|
||||||
|
{"name":"John"}
|
|
@ -0,0 +1 @@
|
||||||
|
Content-Type: application/json; charset=utf-8
|
|
@ -0,0 +1 @@
|
||||||
|
this is a response
|
|
@ -0,0 +1 @@
|
||||||
|
<xml><name>John</name></xml>
|
|
@ -0,0 +1 @@
|
||||||
|
Content-Type: application/xml; charset=utf-8
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
// Extend from the common devtools xpcshell eslintrc config.
|
||||||
|
"extends": "../../../../../.eslintrc.xpcshell"
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Cu = Components.utils;
|
||||||
|
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||||
|
const { parseJSONString, isJSON } = require("devtools/client/webconsole/net/utils/json");
|
||||||
|
|
||||||
|
// Test data
|
||||||
|
const simpleJson = '{"name":"John"}';
|
||||||
|
const jsonInFunc = 'someFunc({"name":"John"})';
|
||||||
|
|
||||||
|
const json1 = "{'a': 1}";
|
||||||
|
const json2 = " {'a': 1}";
|
||||||
|
const json3 = "\t {'a': 1}";
|
||||||
|
const json4 = "\n\n\t {'a': 1}";
|
||||||
|
const json5 = "\n\n\t ";
|
||||||
|
|
||||||
|
const textMimeType = "text/plain";
|
||||||
|
const jsonMimeType = "text/javascript";
|
||||||
|
const unknownMimeType = "text/unknown";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testing API provided by webconsole/net/utils/json.js
|
||||||
|
*/
|
||||||
|
function run_test() {
|
||||||
|
// parseJSONString
|
||||||
|
equal(parseJSONString(simpleJson).name, "John");
|
||||||
|
equal(parseJSONString(jsonInFunc).name, "John");
|
||||||
|
|
||||||
|
// isJSON
|
||||||
|
equal(isJSON(textMimeType, json1), true);
|
||||||
|
equal(isJSON(textMimeType, json2), true);
|
||||||
|
equal(isJSON(jsonMimeType, json3), true);
|
||||||
|
equal(isJSON(jsonMimeType, json4), true);
|
||||||
|
|
||||||
|
equal(isJSON(unknownMimeType, json1), true);
|
||||||
|
equal(isJSON(textMimeType, json1), true);
|
||||||
|
|
||||||
|
equal(isJSON(unknownMimeType), false);
|
||||||
|
equal(isJSON(unknownMimeType, json5), false);
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Cu = Components.utils;
|
||||||
|
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||||
|
const {
|
||||||
|
isImage,
|
||||||
|
isHTML,
|
||||||
|
getHeaderValue,
|
||||||
|
isURLEncodedRequest,
|
||||||
|
isMultiPartRequest
|
||||||
|
} = require("devtools/client/webconsole/net/utils/net");
|
||||||
|
|
||||||
|
// Test data
|
||||||
|
const imageMimeTypes = ["image/jpeg", "image/jpg", "image/gif",
|
||||||
|
"image/png", "image/bmp"];
|
||||||
|
|
||||||
|
const htmlMimeTypes = ["text/html", "text/xml", "application/xml",
|
||||||
|
"application/rss+xml", "application/atom+xml", "application/xhtml+xml",
|
||||||
|
"application/mathml+xml", "application/rdf+xml"];
|
||||||
|
|
||||||
|
const headers = [{name: "headerName", value: "value1"}];
|
||||||
|
|
||||||
|
const har1 = {
|
||||||
|
request: {
|
||||||
|
postData: {
|
||||||
|
text: "content-type: application/x-www-form-urlencoded"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const har2 = {
|
||||||
|
request: {
|
||||||
|
headers: [{
|
||||||
|
name: "content-type",
|
||||||
|
value: "application/x-www-form-urlencoded"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const har3 = {
|
||||||
|
request: {
|
||||||
|
headers: [{
|
||||||
|
name: "content-type",
|
||||||
|
value: "multipart/form-data"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testing API provided by webconsole/net/utils/net.js
|
||||||
|
*/
|
||||||
|
function run_test() {
|
||||||
|
// isImage
|
||||||
|
imageMimeTypes.forEach(mimeType => {
|
||||||
|
ok(isImage(mimeType));
|
||||||
|
});
|
||||||
|
|
||||||
|
// isHTML
|
||||||
|
htmlMimeTypes.forEach(mimeType => {
|
||||||
|
ok(isHTML(mimeType));
|
||||||
|
});
|
||||||
|
|
||||||
|
// getHeaderValue
|
||||||
|
equal(getHeaderValue(headers, "headerName"), "value1");
|
||||||
|
|
||||||
|
// isURLEncodedRequest
|
||||||
|
ok(isURLEncodedRequest(har1));
|
||||||
|
ok(isURLEncodedRequest(har2));
|
||||||
|
|
||||||
|
// isMultiPartRequest
|
||||||
|
ok(isMultiPartRequest(har3));
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
[DEFAULT]
|
||||||
|
tags = devtools
|
||||||
|
head =
|
||||||
|
tail =
|
||||||
|
firefox-appdir = browser
|
||||||
|
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||||
|
|
||||||
|
[test_json-utils.js]
|
||||||
|
[test_net-utils.js]
|
Загрузка…
Ссылка в новой задаче