Backed out changeset 8c7e3a9b7b6b (bug 1613885) for dt failures in browser_net_content-type.js on a CLOSED TREE

--HG--
rename : devtools/client/netmonitor/src/components/previews/HtmlPreview.js => devtools/client/netmonitor/src/components/HtmlPreview.js
rename : devtools/client/netmonitor/src/components/request-details/ResponsePanel.js => devtools/client/netmonitor/src/components/ResponsePanel.js
This commit is contained in:
Oana Pop Rus 2020-03-12 15:25:39 +02:00
Родитель ad219d5402
Коммит 51881bc9e7
30 изменённых файлов: 413 добавлений и 876 удалений

Просмотреть файл

@ -12,6 +12,9 @@
.network-monitor .panel-container {
height: 100%;
}
.network-monitor .panel-container {
display: flex;
flex-direction: column;
overflow-x: hidden;
@ -80,7 +83,6 @@
right: 0;
}
/* Text inputs in tab panels */
.network-monitor .textbox-input {
@ -180,7 +182,7 @@
/* If there is a source editor shows up in the last row of TreeView,
* it should occupy the available vertical space.
*/
.network-monitor .accordion .editor-row-container,
.network-monitor .tree-container .treeTable .editor-row-container,
.network-monitor .tree-container .treeTable tr:last-child td[colspan="2"] {
display: block;
height: 100%;
@ -188,7 +190,7 @@
overflow-x: auto;
}
.network-monitor .accordion .responseTextContainer {
.network-monitor .tree-container .treeTable .editor-row-container .responseTextContainer {
overflow-x: auto;
width: 100%;
height: 100%;
@ -377,33 +379,6 @@
}
/* Response tabpanel */
/* Supports the single expand mode used for the accordion in the response panel */
.network-monitor #response-panel .panel-container {
overflow-y: hidden;
}
.network-monitor #response-panel .accordion {
position: relative;
overflow: hidden;
}
.network-monitor #response-panel .accordion-item .accordion-content {
position: absolute;
top: calc(var(--theme-toolbar-height) + 1px);
bottom: 0;
left: 0;
right: 0;
overflow: hidden;
}
.network-monitor #response-panel .accordion-item {
position: relative;
height: auto;
}
.network-monitor #response-panel .accordion-item.accordion-open {
height: 100%;
}
.network-monitor .response-image-box {
display: flex;

Просмотреть файл

@ -45,7 +45,7 @@ loader.lazyGetter(this, "SourceEditor", function() {
});
loader.lazyGetter(this, "HTMLPreview", function() {
return createFactory(
require("devtools/client/netmonitor/src/components/previews/HtmlPreview")
require("devtools/client/netmonitor/src/components/HtmlPreview")
);
});

Просмотреть файл

@ -3,53 +3,43 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
Component,
createFactory,
} = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const {
connect,
} = require("devtools/client/shared/redux/visibility-handler-connect");
const Services = require("Services");
const { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
const {
decodeUnicodeBase64,
fetchNetworkUpdatePacket,
formDataURI,
getUrlBaseName,
isJSON,
} = require("devtools/client/netmonitor/src/utils/request-utils");
const {
Filters,
} = require("devtools/client/netmonitor/src/utils/filter-predicates");
const {
FILTER_SEARCH_DELAY,
} = require("devtools/client/netmonitor/src/constants");
setTargetSearchResult,
} = require("devtools/client/netmonitor/src/actions/search");
// Components
const PropertiesView = createFactory(
require("devtools/client/netmonitor/src/components/request-details/PropertiesView")
);
const ImagePreview = createFactory(
require("devtools/client/netmonitor/src/components/previews/ImagePreview")
);
const SourcePreview = createFactory(
require("devtools/client/netmonitor/src/components/previews/SourcePreview")
);
const HtmlPreview = createFactory(
require("devtools/client/netmonitor/src/components/previews/HtmlPreview")
);
const Accordion = createFactory(
require("devtools/client/shared/components/Accordion")
);
const SearchBox = createFactory(
require("devtools/client/shared/components/SearchBox")
require("devtools/client/netmonitor/src/components/PropertiesView")
);
loader.lazyGetter(this, "MODE", function() {
return require("devtools/client/shared/components/reps/reps").MODE;
});
const { div } = dom;
const { div, img } = dom;
const JSON_SCOPE_NAME = L10N.getStr("jsonScopeName");
const JSON_FILTER_TEXT = L10N.getStr("jsonFilterText");
const RESPONSE_IMG_NAME = L10N.getStr("netmonitor.response.name");
const RESPONSE_IMG_DIMENSIONS = L10N.getStr("netmonitor.response.dimensions");
const RESPONSE_IMG_MIMETYPE = L10N.getStr("netmonitor.response.mime");
const RESPONSE_PAYLOAD = L10N.getStr("responsePayload");
const RESPONSE_PREVIEW = L10N.getStr("responsePreview");
const RESPONSE_EMPTY_TEXT = L10N.getStr("responseEmptyText");
@ -67,6 +57,7 @@ class ResponsePanel extends Component {
request: PropTypes.object.isRequired,
openLink: PropTypes.func,
targetSearchResult: PropTypes.object,
resetTargetSearchResult: PropTypes.func,
connector: PropTypes.object.isRequired,
};
}
@ -75,9 +66,13 @@ class ResponsePanel extends Component {
super(props);
this.state = {
filterText: "",
currentOpen: undefined,
imageDimensions: {
width: 0,
height: 0,
},
};
this.updateImageDimensions = this.updateImageDimensions.bind(this);
}
componentDidMount() {
@ -109,6 +104,15 @@ class ResponsePanel extends Component {
);
}
updateImageDimensions({ target }) {
this.setState({
imageDimensions: {
width: target.naturalWidth,
height: target.naturalHeight,
},
});
}
/**
* Handle json, which we tentatively identify by checking the
* MIME type for "json" after any word boundary. This works
@ -173,9 +177,8 @@ class ResponsePanel extends Component {
}
render() {
const { request, targetSearchResult } = this.props;
const { openLink, request, targetSearchResult } = this.props;
const { responseContent, url } = request;
const { filterText } = this.state;
if (
!responseContent ||
@ -188,7 +191,31 @@ class ResponsePanel extends Component {
let { encoding, mimeType, text } = responseContent.content;
if (mimeType.includes("image/")) {
return ImagePreview({ encoding, mimeType, text, url });
const { width, height } = this.state.imageDimensions;
return div(
{ className: "panel-container response-image-box devtools-monospace" },
img({
className: "response-image",
src: formDataURI(mimeType, encoding, text),
onLoad: this.updateImageDimensions,
}),
div(
{ className: "response-summary" },
div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_NAME),
div({ className: "tabpanel-summary-value" }, getUrlBaseName(url))
),
div(
{ className: "response-summary" },
div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_DIMENSIONS),
div({ className: "tabpanel-summary-value" }, `${width} × ${height}`)
),
div(
{ className: "response-summary" },
div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_MIMETYPE),
div({ className: "tabpanel-summary-value" }, mimeType)
)
);
}
// Decode response if it's coming from JSONView.
@ -199,120 +226,41 @@ class ResponsePanel extends Component {
// Display Properties View
const { json, jsonpCallback, error } =
this.handleJSONResponse(mimeType, text) || {};
const items = [];
const object = {};
let sectionName;
const onToggle = (open, item) => {
this.setState({ currentOpen: open ? item : null });
};
if (json) {
if (jsonpCallback) {
sectionName = L10N.getFormatStr("jsonpScopeName", jsonpCallback);
} else {
sectionName = JSON_SCOPE_NAME;
}
items.push({
component: PropertiesView,
componentProps: {
object: json,
useQuotes: true,
filterText,
targetSearchResult,
mode: MODE.LONG,
},
header: sectionName,
id: "jsonpScopeName",
opened: !!targetSearchResult,
shouldOpen: item => {
const { currentOpen } = this.state;
if (typeof currentOpen == "undefined" && item.id === items[0].id) {
// if this the first and panel just displayed, open this item
// by default;
return true;
} else if (!currentOpen) {
if (!targetSearchResult) {
return false;
}
return true;
}
// Open the item is toggled open or there is a serch result to show
if (item.id == currentOpen.id || targetSearchResult) {
return true;
}
return false;
},
onToggle,
});
object[sectionName] = json;
}
// Display HTML
// Display HTML under Properties View
if (Filters.html(this.props.request)) {
items.push({
component: HtmlPreview,
componentProps: { responseContent },
header: RESPONSE_PREVIEW,
id: "responsePreview",
opened: false,
shouldOpen: item => {
const { currentOpen } = this.state;
if (typeof currentOpen == "undefined" && item.id === items[0].id) {
// if this the first and panel just displayed, open this item
// by default;
if (targetSearchResult) {
// collapse when we do a search
return false;
}
return true;
} else if (!currentOpen) {
return false;
}
// close this if there is a search result since
// it does not apply search
if (targetSearchResult) {
return false;
}
if (item.id == currentOpen.id) {
return true;
}
return false;
},
onToggle,
});
object[RESPONSE_PREVIEW] = {
HTML_PREVIEW: { responseContent },
};
}
items.push({
component: SourcePreview,
componentProps: {
let scrollToLine;
let expandedNodes;
if (targetSearchResult && targetSearchResult.line) {
scrollToLine = targetSearchResult.line;
expandedNodes = new Set(["/" + RESPONSE_PAYLOAD]);
}
// Others like text/html, text/plain, application/javascript
object[RESPONSE_PAYLOAD] = {
EDITOR_CONFIG: {
text,
mode: json ? "application/json" : mimeType.replace(/;.+/, ""),
targetSearchResult,
limit: Services.prefs.getIntPref(
"devtools.netmonitor.response.ui.limit"
),
scrollToLine,
},
header: RESPONSE_PAYLOAD,
id: "paramsPostPayload",
opened: !!targetSearchResult,
shouldOpen: item => {
const { currentOpen } = this.state;
if (typeof currentOpen == "undefined" && item.id === items[0].id) {
return true;
} else if (!currentOpen) {
if (targetSearchResult) {
return true;
}
return false;
}
if (item.id == currentOpen.id || targetSearchResult) {
return true;
}
return false;
},
onToggle,
});
};
const classList = ["panel-container"];
if (Filters.html(this.props.request)) {
@ -322,19 +270,22 @@ class ResponsePanel extends Component {
return div(
{ className: classList.join(" ") },
error && div({ className: "response-error-header", title: error }, error),
json &&
div(
{ className: "devtools-toolbar devtools-input-toolbar" },
SearchBox({
delay: FILTER_SEARCH_DELAY,
type: "filter",
onChange: filter => this.setState({ filterText: filter }),
placeholder: JSON_FILTER_TEXT,
})
),
Accordion({ items })
PropertiesView({
object,
expandedNodes,
useQuotes: true,
filterPlaceHolder: JSON_FILTER_TEXT,
sectionNames: Object.keys(object),
openLink,
targetSearchResult,
})
);
}
}
module.exports = ResponsePanel;
module.exports = connect(
null,
dispatch => ({
resetTargetSearchResult: () => dispatch(setTargetSearchResult(null)),
})
)(ResponsePanel);

Просмотреть файл

@ -36,7 +36,7 @@ const CachePanel = createFactory(
require("devtools/client/netmonitor/src/components/request-details/CachePanel")
);
const ResponsePanel = createFactory(
require("devtools/client/netmonitor/src/components/request-details/ResponsePanel")
require("devtools/client/netmonitor/src/components/ResponsePanel")
);
const SecurityPanel = createFactory(
require("devtools/client/netmonitor/src/components/request-details/SecurityPanel")

Просмотреть файл

@ -16,10 +16,12 @@ DevToolsModules(
'CustomRequestPanel.js',
'DropHarHandler.js',
'HeadersPanel.js',
'HtmlPreview.js',
'JSONPreview.js',
'MonitorPanel.js',
'NetworkActionBar.js',
'PropertiesView.js',
'ResponsePanel.js',
'SecurityState.js',
'SourceEditor.js',
'StatisticsPanel.js',

Просмотреть файл

@ -1,87 +0,0 @@
/* 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/. */
"use strict";
const { Component } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
const {
div,
img,
} = require("devtools/client/shared/vendor/react-dom-factories");
const {
formDataURI,
getUrlBaseName,
} = require("devtools/client/netmonitor/src/utils/request-utils");
const RESPONSE_IMG_NAME = L10N.getStr("netmonitor.response.name");
const RESPONSE_IMG_DIMENSIONS = L10N.getStr("netmonitor.response.dimensions");
const RESPONSE_IMG_MIMETYPE = L10N.getStr("netmonitor.response.mime");
class ImagePreview extends Component {
static get propTypes() {
return {
mimeType: PropTypes.string,
encoding: PropTypes.string,
text: PropTypes.string,
url: PropTypes.string,
};
}
constructor(props) {
super(props);
this.state = {
dimensions: {
width: 0,
height: 0,
},
};
this.updateDimensions = this.updateDimensions.bind(this);
}
updateDimensions({ target }) {
this.setState({
dimensions: {
width: target.naturalWidth,
height: target.naturalHeight,
},
});
}
render() {
const { mimeType, encoding, text, url } = this.props;
const { width, height } = this.state.dimensions;
return div(
{ className: "panel-container response-image-box devtools-monospace" },
img({
className: "response-image",
src: formDataURI(mimeType, encoding, text),
onLoad: this.updateDimensions,
}),
div(
{ className: "response-summary" },
div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_NAME),
div({ className: "tabpanel-summary-value" }, getUrlBaseName(url))
),
div(
{ className: "response-summary" },
div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_DIMENSIONS),
div({ className: "tabpanel-summary-value" }, `${width} × ${height}`)
),
div(
{ className: "response-summary" },
div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_MIMETYPE),
div({ className: "tabpanel-summary-value" }, mimeType)
)
);
}
}
module.exports = ImagePreview;

Просмотреть файл

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Component } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const {
@ -13,7 +14,8 @@ const Editor = require("devtools/client/shared/sourceeditor/editor");
const {
setTargetSearchResult,
} = require("devtools/client/netmonitor/src/actions/search");
const { div, pre } = dom;
const { div } = dom;
/**
* CodeMirror editor as a React component
*/
@ -25,58 +27,16 @@ class SourcePreview extends Component {
// Source editor content
text: PropTypes.string,
// Search result text to select
targetSearchResult: PropTypes.object,
targetSearchResult: PropTypes.string,
// Reset target search result that has been used for navigation in this panel.
// This is done to avoid second navigation the next time.
resetTargetSearchResult: PropTypes.func,
// Limit for determine how to render large content
limit: PropTypes.number,
};
}
componentDidMount() {
const { mode, text } = this.props;
if (!this.isOverSizeLimit(text)) {
this.loadEditor(mode, text);
}
}
shouldComponentUpdate(nextProps) {
return (
nextProps.mode !== this.props.mode ||
nextProps.text !== this.props.text ||
nextProps.targetSearchResult !== this.props.targetSearchResult
);
}
componentDidUpdate(prevProps) {
const { mode, targetSearchResult, text } = this.props;
if (prevProps.text !== text) {
// if the text size is over the limit we have switched to
// rendering with a PRE, lets clean up any previous editor object.
if (this.isOverSizeLimit(text)) {
this.unloadEditor();
return;
}
if (!this.editor || this.editor.isDestroyed()) {
// When updating from a PRE to an editor
this.loadEditor(mode, text);
} else {
// When updating from editor to editor
this.updateEditor(mode, text);
}
} else if (prevProps.targetSearchResult !== targetSearchResult) {
this.findSearchResult();
}
}
componentWillUnmount() {
this.unloadEditor();
}
loadEditor(mode, text) {
this.editor = new Editor({
lineNumbers: true,
lineWrapping: false,
@ -102,33 +62,49 @@ class SourcePreview extends Component {
});
}
updateEditor(mode, text) {
// Reset the existed 'mode' attribute in order to make setText() process faster
// to prevent drawing unnecessary syntax highlight.
this.editor.setMode(null);
this.editor.setText(text);
if (this.editorSetModeTimeout) {
clearTimeout(this.editorSetModeTimeout);
}
// CodeMirror's setMode() (syntax highlight) is the performance bottleneck when
// processing large content, so we enable it asynchronously within the setTimeout
// to avoid UI blocking. (rendering source code -> drawing syntax highlight)
this.editorSetModeTimeout = setTimeout(() => {
this.editorSetModeTimeout = null;
this.editor.setMode(mode);
this.findSearchResult();
});
shouldComponentUpdate(nextProps) {
return (
nextProps.mode !== this.props.mode ||
nextProps.text !== this.props.text ||
nextProps.targetSearchResult !== this.props.targetSearchResult
);
}
unloadEditor() {
componentDidUpdate(prevProps) {
const { mode, targetSearchResult, text } = this.props;
// Bail out if the editor has been destroyed in the meantime.
if (this.editor.isDestroyed()) {
return;
}
if (prevProps.text !== text) {
// Reset the existed 'mode' attribute in order to make setText() process faster
// to prevent drawing unnecessary syntax highlight.
this.editor.setMode(null);
this.editor.setText(text);
if (this.editorSetModeTimeout) {
clearTimeout(this.editorSetModeTimeout);
}
// CodeMirror's setMode() (syntax highlight) is the performance bottleneck when
// processing large content, so we enable it asynchronously within the setTimeout
// to avoid UI blocking. (rendering source code -> drawing syntax highlight)
this.editorSetModeTimeout = setTimeout(() => {
this.editorSetModeTimeout = null;
this.editor.setMode(mode);
this.findSearchResult();
});
} else if (prevProps.targetSearchResult !== targetSearchResult) {
this.findSearchResult();
}
}
componentWillUnmount() {
clearTimeout(this.editorTimeout);
clearTimeout(this.editorSetModeTimeout);
if (this.editor) {
this.editor.destroy();
this.editor = null;
}
this.editor.destroy();
}
findSearchResult() {
@ -138,71 +114,17 @@ class SourcePreview extends Component {
const { line } = targetSearchResult;
// scroll the editor to center the line
// with the target search result
if (this.editor) {
this.editor.setCursor({ line: line - 1 }, "center");
}
this.editor.setCursor({ line: line - 1 }, "center");
}
resetTargetSearchResult();
}
// Scroll to specified line if the user clicks on search results.
scrollToLine(element) {
const { targetSearchResult, resetTargetSearchResult } = this.props;
// The following code is responsible for scrolling given line
// to visible view-port.
// It gets the <div> child element representing the target
// line (by index) and uses `scrollIntoView` API to make sure
// it's visible to the user.
if (element && targetSearchResult && targetSearchResult.line) {
const child = element.children[targetSearchResult.line - 1];
if (child) {
const range = document.createRange();
range.selectNode(child);
document.getSelection().addRange(range);
child.scrollIntoView({ block: "center" });
}
resetTargetSearchResult();
}
}
isOverSizeLimit(text) {
const { limit } = this.props;
return text && text.length > limit;
}
renderPre(text) {
return div(
{ className: "responseTextContainer" },
pre(
{ ref: element => this.scrollToLine(element) },
text.split(/\r\n|\r|\n/).map((line, index) => {
return div({ key: index }, line);
})
)
);
}
renderEditor() {
return div(
{ className: "editor-row-container" },
div({
ref: "editorElement",
className: "source-editor-mount devtools-monospace",
})
);
}
render() {
const { text } = this.props;
// To prevent performance issues, switch from editor to pre()
// if response size is greater than specified limit.
const isOverSize = this.isOverSizeLimit(text);
return div(
{ key: "EDITOR_CONFIG", className: "editor-row-container" },
isOverSize ? this.renderPre(text) : this.renderEditor()
);
return div({
ref: "editorElement",
className: "source-editor-mount devtools-monospace",
});
}
}

Просмотреть файл

@ -3,7 +3,5 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'HtmlPreview.js',
'ImagePreview.js',
'SourcePreview.js',
'SourcePreview.js',
)

Просмотреть файл

@ -198,12 +198,13 @@ class ParamsPanel extends Component {
}
// Request payload section
const limit = Services.prefs.getIntPref(
"devtools.netmonitor.requestBodyLimit"
);
// Check if the request post data has been truncated from the backend,
// in which case no parse should be attempted.
// Check if the request post data has been truncated, in which case no parse should
// be attempted.
if (postData && limit <= postData.length) {
error = REQUEST_TRUNCATED;
}
@ -235,10 +236,6 @@ class ParamsPanel extends Component {
text: postData,
mode: mimeType.replace(/;.+/, ""),
targetSearchResult,
// Using the response limit to improve perf when rendering
limit: Services.prefs.getIntPref(
"devtools.netmonitor.response.ui.limit"
),
},
header: PARAMS_POST_PAYLOAD,
id: "paramsPostPayload",

Просмотреть файл

@ -50,7 +50,7 @@ const { div } = dom;
class PropertiesView extends Component {
static get propTypes() {
return {
object: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
object: PropTypes.object,
provider: PropTypes.object,
enableInput: PropTypes.bool,
expandableStrings: PropTypes.bool,
@ -59,7 +59,6 @@ class PropertiesView extends Component {
cropLimit: PropTypes.number,
targetSearchResult: PropTypes.object,
resetTargetSearchResult: PropTypes.func,
mode: PropTypes.symbol,
};
}
@ -170,7 +169,7 @@ class PropertiesView extends Component {
// FIXME: A workaround for the issue in StringRep
// Force StringRep to crop the text every time
member: Object.assign({}, member, { open: false }),
mode: this.props.mode || MODE.TINY,
mode: MODE.TINY,
cropLimit: this.props.cropLimit,
noGrip: true,
})

Просмотреть файл

@ -8,9 +8,7 @@ DevToolsModules(
'NetworkDetailsBar.js',
'ParamsPanel.js',
'PropertiesView.js',
'ResponsePanel.js',
'SecurityPanel.js',
'StackTracePanel.js',
'TimingsPanel.js'
)

Просмотреть файл

@ -13,13 +13,10 @@ add_task(async function() {
info("Starting test... ");
const { document, store, windowRequire } = monitor.panelWin;
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
const { getDisplayedRequests, getSortedRequests } = windowRequire(
"devtools/client/netmonitor/src/selectors/index"
);
store.dispatch(Actions.batchEnable(false));
let wait = waitForNetworkEvents(monitor, 1);
tab.linkedBrowser.reload();
await wait;
@ -49,21 +46,13 @@ add_task(async function() {
document.querySelectorAll(".request-list-item")[0]
);
await wait;
wait = waitForDOM(document, "#response-panel .accordion-item", 2);
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector("#response-tab")
);
await wait;
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
const header = document.querySelector(
"#response-panel .accordion-item:last-child .accordion-header"
);
clickElement(header, monitor);
await wait;
// CodeMirror will only load lines currently in view to the DOM. getValue()
// retrieves all lines pending render after a user begins scrolling.
const text = document.querySelector(".CodeMirror").CodeMirror.getValue();

Просмотреть файл

@ -20,58 +20,22 @@ add_task(async function() {
// Execute requests.
await performRequests(monitor, tab, 1);
let wait = waitForDOM(document, "#response-panel .accordion-item", 2);
const waitForPropsView = waitForDOM(
document,
"#response-panel .properties-view",
1
);
const wait = waitForDOM(document, "#response-panel .CodeMirror-code");
store.dispatch(Actions.toggleNetworkDetails());
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector("#response-tab")
);
await Promise.all([wait, waitForPropsView]);
await wait;
const tabpanel = document.querySelector("#response-panel");
is(
tabpanel.querySelectorAll(".treeRow").length,
1,
"There should be 1 json properties displayed in this tabpanel."
);
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
is(
labels[0].textContent,
"greeting",
"The first json property name was incorrect."
);
is(
values[0].textContent,
`"This is a base 64 string."`,
"The first json property value was incorrect."
);
// Open the response payload section, it should hide the json section
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
const header = document.querySelector(
"#response-panel .accordion-item:last-child .accordion-header"
);
clickElement(header, monitor);
await wait;
is(
tabpanel.querySelector(".response-error-header") === null,
true,
"The response error header doesn't have the intended visibility."
);
const jsonView =
tabpanel.querySelector(".accordion-item .accordion-header-label") || {};
const jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
is(
jsonView.textContent === L10N.getStr("jsonScopeName"),
true,
@ -89,15 +53,38 @@ add_task(async function() {
);
is(
tabpanel.querySelectorAll(".accordion-item").length,
tabpanel.querySelectorAll(".tree-section").length,
2,
"There should be 2 tree sections displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length,
1,
"There should be 1 json properties displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".empty-notice").length,
0,
"The empty notice should not be displayed in this tabpanel."
);
const labels = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeLabelCell .treeLabel"
);
const values = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeValueCell .objectBox"
);
is(
labels[0].textContent,
"greeting",
"The first json property name was incorrect."
);
is(
values[0].textContent,
`"This is a base 64 string."`,
"The first json property value was incorrect."
);
await teardown(monitor);
});

Просмотреть файл

@ -22,35 +22,20 @@ add_task(async function() {
const onResponsePanelReady = waitForDOM(
document,
"#response-panel .accordion-item",
2
"#response-panel .CodeMirror-code"
);
store.dispatch(Actions.toggleNetworkDetails());
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector("#response-tab")
);
await onResponsePanelReady;
const codeMirrorReady = waitForDOM(
document,
"#response-panel .CodeMirror-code"
);
const header = document.querySelector(
"#response-panel .accordion-item:last-child .accordion-header"
);
clickElement(header, monitor);
await codeMirrorReady;
const tabpanel = document.querySelector("#response-panel");
is(
tabpanel.querySelectorAll(".accordion-item").length,
tabpanel.querySelectorAll(".tree-section").length,
2,
"There should be 2 accordion items displayed in this tabpanel."
"There should be 2 tree sections displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".empty-notice").length,
@ -74,8 +59,7 @@ add_task(async function() {
"The response image box doesn't have the intended visibility."
);
const jsonView =
tabpanel.querySelector(".accordion-item .accordion-header-label") || {};
const jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
is(
jsonView.textContent === L10N.getStr("jsonScopeName"),
true,

Просмотреть файл

@ -53,45 +53,76 @@ add_task(async function() {
}
);
let wait = waitForDOM(document, "#response-panel .accordion-item", 2);
const waitForPropsView = waitForDOM(
document,
"#response-panel .properties-view",
1
);
const wait = waitForDOM(document, "#response-panel .CodeMirror-code");
store.dispatch(Actions.toggleNetworkDetails());
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector("#response-tab")
);
await Promise.all([wait, waitForPropsView]);
testJsonAccordionInResposeTab();
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
const payloadHeader = document.querySelector(
"#response-panel .accordion-item:last-child .accordion-header"
);
clickElement(payloadHeader, monitor);
await wait;
testResponseTab();
await teardown(monitor);
function testJsonAccordionInResposeTab() {
function testResponseTab() {
const tabpanel = document.querySelector("#response-panel");
is(
tabpanel.querySelectorAll(".treeRow").length,
tabpanel.querySelector(".response-error-header") === null,
true,
"The response error header doesn't have the intended visibility."
);
const jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
is(
jsonView.textContent === L10N.getStr("jsonScopeName"),
true,
"The response json view has the intended visibility."
);
is(
tabpanel.querySelector(".editor-row-container").clientHeight !== 0,
true,
"The source editor container has visible height."
);
is(
tabpanel.querySelector(".CodeMirror-code") === null,
false,
"The response editor has the intended visibility."
);
is(
tabpanel.querySelector(".response-image-box") === null,
true,
"The response image box doesn't have the intended visibility."
);
is(
tabpanel.querySelectorAll(".tree-section").length,
2,
"There should be 2 tree sections displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length,
2047,
"There should be 2047 json properties displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".empty-notice").length,
0,
"The empty notice should not be displayed in this tabpanel."
);
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
is(
tabpanel.querySelector(".tree-section .treeLabel").textContent,
L10N.getStr("jsonScopeName"),
"The json view section doesn't have the correct title."
);
const labels = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeLabelCell .treeLabel"
);
const values = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeValueCell .objectBox"
);
is(
labels[0].textContent,
@ -115,54 +146,4 @@ add_task(async function() {
"The second json property value was incorrect."
);
}
function testResponseTab() {
const tabpanel = document.querySelector("#response-panel");
is(
tabpanel.querySelector(".response-error-header") === null,
true,
"The response error header doesn't have the intended visibility."
);
const jsonView =
tabpanel.querySelector(".accordion-item .accordion-header-label") || {};
is(
jsonView.textContent === L10N.getStr("jsonScopeName"),
true,
"The response json view has the intended visibility."
);
is(
tabpanel.querySelector(".source-editor-mount").clientHeight !== 0,
true,
"The source editor container has visible height."
);
is(
tabpanel.querySelector(".CodeMirror-code") === null,
false,
"The response editor has the intended visibility."
);
is(
tabpanel.querySelector(".response-image-box") === null,
true,
"The response image box doesn't have the intended visibility."
);
is(
tabpanel.querySelectorAll(".accordion-item").length,
2,
"There should be 2 accordion items displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".empty-notice").length,
0,
"The empty notice should not be displayed in this tabpanel."
);
is(
tabpanel.querySelector(".accordion-item .accordion-header-label")
.textContent,
L10N.getStr("jsonScopeName"),
"The json view section doesn't have the correct title."
);
}
});

Просмотреть файл

@ -22,26 +22,22 @@ add_task(async function() {
const onResponsePanelReady = waitForDOM(
document,
"#response-panel .accordion-item",
2
"#response-panel .CodeMirror-code"
);
const onPropsViewReady = waitForDOM(
document,
"#response-panel .properties-view",
1
);
store.dispatch(Actions.toggleNetworkDetails());
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector("#response-tab")
);
await Promise.all([onResponsePanelReady, onPropsViewReady]);
await onResponsePanelReady;
const tabpanel = document.querySelector("#response-panel");
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
const labels = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeLabelCell .treeLabel"
);
const values = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeValueCell .objectBox"
);
is(labels[0].textContent, "obj", "The first json property name is correct.");
is(

Просмотреть файл

@ -22,30 +22,25 @@ add_task(async function() {
const onResponsePanelReady = waitForDOM(
document,
"#response-panel .accordion-item",
2
);
const onPropsViewReady = waitForDOM(
document,
"#response-panel .properties-view",
1
"#response-panel .CodeMirror-code"
);
store.dispatch(Actions.toggleNetworkDetails());
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector("#response-tab")
);
await Promise.all([onResponsePanelReady, onPropsViewReady]);
await onResponsePanelReady;
checkResponsePanelDisplaysJSON();
const tabpanel = document.querySelector("#response-panel");
is(
tabpanel.querySelectorAll(".accordion-item").length,
tabpanel.querySelectorAll(".tree-section").length,
2,
"There should be 2 accordion items displayed in this tabpanel."
"There should be 2 tree sections displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".treeRow").length,
tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length,
1,
"There should be 1 json properties displayed in this tabpanel."
);
@ -55,8 +50,12 @@ add_task(async function() {
"The empty notice should not be displayed in this tabpanel."
);
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
const labels = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeLabelCell .treeLabel"
);
const values = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeValueCell .objectBox"
);
is(
labels[0].textContent,
@ -69,20 +68,6 @@ add_task(async function() {
"The first json property value was incorrect."
);
const onCodeMirrorReady = waitForDOM(
document,
"#response-panel .CodeMirror-code"
);
const payloadHeader = document.querySelector(
"#response-panel .accordion-item:last-child .accordion-header"
);
clickElement(payloadHeader, monitor);
await onCodeMirrorReady;
checkResponsePanelDisplaysJSON();
await teardown(monitor);
/**
@ -96,8 +81,7 @@ add_task(async function() {
true,
"The response error header doesn't have the intended visibility."
);
const jsonView =
panel.querySelector(".accordion-item .accordion-header-label") || {};
const jsonView = panel.querySelector(".tree-section .treeLabel") || {};
is(
jsonView.textContent === L10N.getStr("jsonScopeName"),
true,

Просмотреть файл

@ -45,56 +45,18 @@ add_task(async function() {
}
);
let wait = waitForDOM(document, "#response-panel .accordion-item", 2);
const waitForPropsView = waitForDOM(
document,
"#response-panel .properties-view",
1
);
const wait = waitForDOM(document, "#response-panel .CodeMirror-code");
store.dispatch(Actions.toggleNetworkDetails());
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector("#response-tab")
);
await Promise.all([wait, waitForPropsView]);
testJsonSectionInResponseTab();
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
const payloadHeader = document.querySelector(
"#response-panel .accordion-item:last-child .accordion-header"
);
clickElement(payloadHeader, monitor);
await wait;
testResponseTab();
await teardown(monitor);
function testJsonSectionInResponseTab() {
const tabpanel = document.querySelector("#response-panel");
is(
tabpanel.querySelectorAll(".treeRow").length,
1,
"There should be 1 json properties displayed in this tabpanel."
);
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
is(
labels[0].textContent,
"greeting",
"The first json property name was incorrect."
);
is(
values[0].textContent,
`"Hello oddly-named JSON!"`,
"The first json property value was incorrect."
);
}
function testResponseTab() {
const tabpanel = document.querySelector("#response-panel");
@ -103,8 +65,7 @@ add_task(async function() {
true,
"The response error header doesn't have the intended visibility."
);
const jsonView =
tabpanel.querySelector(".accordion-item .accordion-header-label") || {};
const jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
is(
jsonView.textContent === L10N.getStr("jsonScopeName"),
true,
@ -122,15 +83,37 @@ add_task(async function() {
);
is(
tabpanel.querySelectorAll(".accordion-item").length,
tabpanel.querySelectorAll(".tree-section").length,
2,
"There should be 2 accordion items displayed in this tabpanel."
"There should be 2 tree sections displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length,
1,
"There should be 1 json properties displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".empty-notice").length,
0,
"The empty notice should not be displayed in this tabpanel."
);
const labels = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeLabelCell .treeLabel"
);
const values = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeValueCell .objectBox"
);
is(
labels[0].textContent,
"greeting",
"The first json property name was incorrect."
);
is(
values[0].textContent,
`"Hello oddly-named JSON!"`,
"The first json property value was incorrect."
);
}
});

Просмотреть файл

@ -46,56 +46,18 @@ add_task(async function() {
}
);
let wait = waitForDOM(document, "#response-panel .accordion-item", 2);
const waitForPropsView = waitForDOM(
document,
"#response-panel .properties-view",
1
);
const wait = waitForDOM(document, "#response-panel .CodeMirror-code");
store.dispatch(Actions.toggleNetworkDetails());
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector("#response-tab")
);
await Promise.all([wait, waitForPropsView]);
testJsonSectionInResponseTab();
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
const payloadHeader = document.querySelector(
"#response-panel .accordion-item:last-child .accordion-header"
);
clickElement(payloadHeader, monitor);
await wait;
testResponseTab();
await teardown(monitor);
function testJsonSectionInResponseTab() {
const tabpanel = document.querySelector("#response-panel");
is(
tabpanel.querySelectorAll(".treeRow").length,
1,
"There should be 1 json properties displayed in this tabpanel."
);
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
is(
labels[0].textContent,
"greeting",
"The first json property name was incorrect."
);
is(
values[0].textContent,
`"Hello third-party JSON!"`,
"The first json property value was incorrect."
);
}
function testResponseTab() {
const tabpanel = document.querySelector("#response-panel");
@ -104,8 +66,7 @@ add_task(async function() {
true,
"The response error header doesn't have the intended visibility."
);
const jsonView =
tabpanel.querySelector(".accordion-item .accordion-header-label") || {};
const jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
is(
jsonView.textContent === L10N.getStr("jsonScopeName"),
true,
@ -123,14 +84,37 @@ add_task(async function() {
);
is(
tabpanel.querySelectorAll(".accordion-item").length,
tabpanel.querySelectorAll(".tree-section").length,
2,
"There should be 2 accordion items displayed in this tabpanel."
"There should be 2 tree sections displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length,
1,
"There should be 1 json properties displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".empty-notice").length,
0,
"The empty notice should not be displayed in this tabpanel."
);
const labels = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeLabelCell .treeLabel"
);
const values = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeValueCell .objectBox"
);
is(
labels[0].textContent,
"greeting",
"The first json property name was incorrect."
);
is(
values[0].textContent,
`"Hello third-party JSON!"`,
"The first json property value was incorrect."
);
}
});

Просмотреть файл

@ -65,90 +65,30 @@ add_task(async function() {
);
info("Testing first request");
let wait = waitForDOM(document, "#response-panel .accordion-item", 2);
let waitForPropsView = waitForDOM(
document,
"#response-panel .properties-view",
1
);
let wait = waitForDOM(document, "#response-panel .CodeMirror-code");
store.dispatch(Actions.toggleNetworkDetails());
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector("#response-tab")
);
await Promise.all([wait, waitForPropsView]);
testJsonSectionInResponseTab(`"Hello JSONP!"`);
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
let payloadHeader = document.querySelector(
"#response-panel .accordion-item:last-child .accordion-header"
);
clickElement(payloadHeader, monitor);
await wait;
testResponseTab("$_0123Fun");
testResponseTab("$_0123Fun", `"Hello JSONP!"`);
info("Testing second request");
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
wait = waitForDOM(document, "#response-panel .accordion-item", 2);
EventUtils.sendMouseEvent(
{ type: "mousedown" },
document.querySelectorAll(".request-list-item")[1]
);
await wait;
waitForPropsView = waitForDOM(
document,
"#response-panel .properties-view",
1
);
payloadHeader = document.querySelector(
"#response-panel .accordion-item:first-child .accordion-header"
);
clickElement(payloadHeader, monitor);
await waitForPropsView;
testJsonSectionInResponseTab(`"Hello weird JSONP!"`);
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
payloadHeader = document.querySelector(
"#response-panel .accordion-item:last-child .accordion-header"
);
clickElement(payloadHeader, monitor);
await wait;
testResponseTab("$_4567Sad");
testResponseTab("$_4567Sad", `"Hello weird JSONP!"`);
await teardown(monitor);
function testJsonSectionInResponseTab(greeting) {
const tabpanel = document.querySelector("#response-panel");
is(
tabpanel.querySelectorAll(".treeRow").length,
1,
"There should be 1 json properties displayed in this tabpanel."
);
const labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
const values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
is(
labels[0].textContent,
"greeting",
"The first json property name was incorrect."
);
is(
values[0].textContent,
greeting,
"The first json property value was incorrect."
);
}
function testResponseTab(func) {
function testResponseTab(func, greeting) {
const tabpanel = document.querySelector("#response-panel");
is(
@ -157,8 +97,7 @@ add_task(async function() {
"The response error header doesn't have the intended visibility."
);
is(
tabpanel.querySelector(".accordion-item .accordion-header-label")
.textContent,
tabpanel.querySelector(".tree-section .treeLabel").textContent,
L10N.getFormatStr("jsonpScopeName", func),
"The response json view has the intened visibility and correct title."
);
@ -174,14 +113,37 @@ add_task(async function() {
);
is(
tabpanel.querySelectorAll(".accordion-item").length,
tabpanel.querySelectorAll(".tree-section").length,
2,
"There should be 2 accordion items displayed in this tabpanel."
"There should be 2 tree sections displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length,
1,
"There should be 1 json properties displayed in this tabpanel."
);
is(
tabpanel.querySelectorAll(".empty-notice").length,
0,
"The empty notice should not be displayed in this tabpanel."
);
const labels = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeLabelCell .treeLabel"
);
const values = tabpanel.querySelectorAll(
"tr:not(.tree-section) .treeValueCell .objectBox"
);
is(
labels[0].textContent,
"greeting",
"The first json property name was incorrect."
);
is(
values[0].textContent,
greeting,
"The first json property value was incorrect."
);
}
});

Просмотреть файл

@ -52,7 +52,7 @@ add_task(async function() {
}
);
wait = waitForDOM(document, "#response-panel .accordion-item", 2);
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
store.dispatch(Actions.toggleNetworkDetails());
EventUtils.sendMouseEvent(
{ type: "click" },
@ -60,13 +60,6 @@ add_task(async function() {
);
await wait;
wait = waitForDOM(document, "#response-panel .CodeMirror-code");
const payloadHeader = document.querySelector(
"#response-panel .accordion-item:last-child .accordion-header"
);
clickElement(payloadHeader, monitor);
await wait;
ok(
getCodeMirrorValue(monitor).match(/^<p>/),
"The text shown in the source editor is incorrect."

Просмотреть файл

@ -33,7 +33,7 @@ add_task(async function() {
const responsePanel = document.querySelector("#response-panel");
const objectRow = responsePanel.querySelectorAll(".objectRow")[0];
const objectRow = responsePanel.querySelectorAll(".objectRow")[1];
const stringRow = responsePanel.querySelectorAll(".stringRow")[0];
/* Test for copy an object */
@ -46,7 +46,9 @@ add_task(async function() {
EventUtils.sendMouseEvent({ type: "contextmenu" }, objectRow);
await waitForClipboardPromise(function setup() {
getContextMenuItem(monitor, "properties-view-context-menu-copyall").click();
}, `{"obj":{"type":"string"}}`);
}, `{"JSON":{"obj":{"type":"string"}},` +
`"Response Payload":{"EDITOR_CONFIG":{"text":` +
`"{\\"obj\\": {\\"type\\": \\"string\\" }}","mode":"application/json"}}}`);
/* Test for copy a single row */
EventUtils.sendMouseEvent({ type: "contextmenu" }, stringRow);
@ -93,7 +95,9 @@ add_task(async function() {
const cookiesPanel = document.querySelector("#cookies-panel");
const objectRows = cookiesPanel.querySelectorAll(".objectRow");
const objectRows = cookiesPanel.querySelectorAll(
".objectRow:not(.tree-section)"
);
const stringRows = cookiesPanel.querySelectorAll(".stringRow");
const expectedResponseCookies = [

Просмотреть файл

@ -4,7 +4,7 @@
"use strict";
/**
* Tests if large response contents are displayed using <pre>
* Tests if large response contents are displayed usin <pre>
* and not using syntax color highlighting (e.g. using CodeMirror)
*/
const HTML_LONG_URL = CONTENT_TYPE_SJS + "?fmt=html-long";
@ -32,7 +32,7 @@ add_task(async function() {
});
await wait;
wait = waitForDOM(document, "#response-panel .accordion-item", 2);
wait = waitForDOM(document, "#response-panel .responseTextContainer");
store.dispatch(Actions.toggleNetworkDetails());
EventUtils.sendMouseEvent(
{ type: "click" },
@ -40,13 +40,6 @@ add_task(async function() {
);
await wait;
wait = waitForDOM(document, "#response-panel .responseTextContainer");
const payloadHeader = document.querySelector(
"#response-panel .accordion-item:last-child .accordion-header"
);
clickElement(payloadHeader, monitor);
await wait;
await teardown(monitor);
// This test uses a lot of memory, so force a GC to help fragmentation.

Просмотреть файл

@ -299,16 +299,16 @@ add_task(async function() {
async function testResponseTab() {
const tabpanel = await selectTab(PANELS.RESPONSE, 3);
await waitForDOM(document, ".accordion .source-editor-mount");
await waitForDOM(document, ".treeTable tbody");
const responseAccordion = tabpanel.querySelector(".accordion");
const responseTable = tabpanel.querySelector(".treeTable tbody");
is(
responseAccordion.querySelectorAll(".accordion-item").length,
responseTable.querySelectorAll(".tree-section").length,
1,
"There should be 1 response scope displayed in this tabpanel."
);
is(
responseAccordion.querySelectorAll(".source-editor-mount").length,
responseTable.querySelectorAll(".editor-row-container").length,
1,
"The response payload tab should be open initially."
);

Просмотреть файл

@ -35,7 +35,7 @@ add_task(async function() {
);
const waitSourceEditor = waitForDOM(
document,
"#params-panel .responseTextContainer"
"#params-panel .CodeMirror-code"
);
store.dispatch(Actions.toggleNetworkDetails());

Просмотреть файл

@ -1196,16 +1196,3 @@ async function toggleBlockedUrl(element, monitor, store, action = "block") {
await onRequestComplete;
info(`Selected request is now ${action}ed`);
}
/**
* Find and click an element
*
* @param {Element} element
* Target element to be clicked
* @param {Object} monitor
* The netmonitor instance used for retrieving the window.
*/
function clickElement(element, monitor) {
EventUtils.synthesizeMouseAtCenter(element, {}, monitor.panelWin);
}

Просмотреть файл

@ -25,22 +25,40 @@ class Accordion extends Component {
PropTypes.shape({
buttons: PropTypes.arrayOf(PropTypes.object),
className: PropTypes.string,
component: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
component: PropTypes.object,
componentProps: PropTypes.object,
contentClassName: PropTypes.string,
header: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
onToggle: PropTypes.func,
// Determines the initial open state of the accordion item
opened: PropTypes.bool.isRequired,
// Enables dynamically changing the open state of the accordion
// on update.
shouldOpen: PropTypes.func,
})
).isRequired,
};
}
/**
* Add initial data to the state.opened map, and inject new data
* when receiving updated props.
*/
static getDerivedStateFromProps(props, state) {
const newItems = props.items.filter(
({ id }) => typeof state.opened[id] !== "boolean"
);
if (newItems.length) {
const everOpened = { ...state.everOpened };
const opened = { ...state.opened };
for (const item of newItems) {
everOpened[item.id] = item.opened;
opened[item.id] = item.opened;
}
return { everOpened, opened };
}
return null;
}
constructor(props) {
super(props);
@ -50,115 +68,56 @@ class Accordion extends Component {
this.onHeaderClick = this.onHeaderClick.bind(this);
this.onHeaderKeyDown = this.onHeaderKeyDown.bind(this);
this.setInitialState = this.setInitialState.bind(this);
this.updateCurrentState = this.updateCurrentState.bind(this);
}
componentDidMount() {
this.setInitialState();
}
componentDidUpdate(prevProps) {
if (prevProps.items !== this.props.items) {
this.updateCurrentState();
}
}
setInitialState() {
/**
* Add initial data to the `state.opened` map.
* This happens only on initial mount of the accordion.
*/
const newItems = this.props.items.filter(
({ id }) => typeof this.state.opened[id] !== "boolean"
);
if (newItems.length) {
const everOpened = { ...this.state.everOpened };
const opened = { ...this.state.opened };
for (const item of newItems) {
everOpened[item.id] = item.opened;
opened[item.id] = item.opened;
}
this.setState({ everOpened, opened });
}
}
updateCurrentState() {
/**
* Updates the `state.opened` map based on the
* new items that have been added and those that
* `item.shouldOpen()` has changed. This happens
* on each update.
*/
const updatedItems = this.props.items.filter(item => {
const notExist = typeof this.state.opened[item.id] !== "boolean";
if (typeof item.shouldOpen == "function") {
return notExist || this.state.opened[item.id] !== item.shouldOpen(item);
}
return notExist;
});
if (updatedItems.length) {
const everOpened = { ...this.state.everOpened };
const opened = { ...this.state.opened };
for (const item of updatedItems) {
let itemOpen = item.opened;
if (typeof item.shouldOpen == "function") {
itemOpen = item.shouldOpen(item);
}
everOpened[item.id] = itemOpen;
opened[item.id] = itemOpen;
}
this.setState({ everOpened, opened });
}
}
/**
* @param {Event} event Click event.
* @param {Object} item The item to be collapsed/expanded.
*/
onHeaderClick(event, item) {
onHeaderClick(event) {
event.preventDefault();
// In the Browser Toolbox's Inspector/Layout view, handleHeaderClick is
// called twice unless we call stopPropagation, making the accordion item
// open-and-close or close-and-open
event.stopPropagation();
this.toggleItem(item);
this.toggleItem(event.currentTarget.parentElement.id);
}
/**
* @param {Event} event Keyboard event.
* @param {Object} item The item to be collapsed/expanded.
*/
onHeaderKeyDown(event, item) {
onHeaderKeyDown(event) {
if (event.key === " " || event.key === "Enter") {
event.preventDefault();
this.toggleItem(item);
this.toggleItem(event.currentTarget.parentElement.id);
}
}
/**
* Expand or collapse an accordion list item.
* @param {Object} item The item to be collapsed or expanded.
* @param {String} id Id of the item to be collapsed or expanded.
*/
toggleItem(item) {
const opened = !this.state.opened[item.id];
toggleItem(id) {
const item = this.props.items.find(x => x.id === id);
const opened = !this.state.opened[id];
// We could have no item if props just changed
if (!item) {
return;
}
this.setState({
everOpened: {
...this.state.everOpened,
[item.id]: true,
[id]: true,
},
opened: {
...this.state.opened,
[item.id]: opened,
[id]: opened,
},
});
if (typeof item.onToggle === "function") {
item.onToggle(opened, item);
item.onToggle(opened);
}
}
@ -172,15 +131,14 @@ class Accordion extends Component {
header,
id,
} = item;
const headerId = `${id}-header`;
const opened = this.state.opened[id];
let itemContent;
// Only render content if the accordion item is open or has been opened once before.
// This saves us rendering complex components when users are keeping
// Only render content if the accordion item is open or has been opened once
// before. This saves us rendering complex components when users are keeping
// them closed (e.g. in Inspector/Layout) or may not open them at all.
if (this.state.everOpened && this.state.everOpened[id]) {
if (this.state.everOpened[id]) {
if (typeof component === "function") {
itemContent = createElement(component, componentProps);
} else if (typeof component === "object") {
@ -192,9 +150,7 @@ class Accordion extends Component {
{
key: id,
id,
className: `accordion-item ${
opened ? "accordion-open" : ""
} ${className} `.trim(),
className: `accordion-item ${className}`.trim(),
"aria-labelledby": headerId,
},
h2(
@ -206,8 +162,8 @@ class Accordion extends Component {
// If the header contains buttons, make sure the heading name only
// contains the "header" text and not the button text
"aria-label": header,
onKeyDown: event => this.onHeaderKeyDown(event, item),
onClick: event => this.onHeaderClick(event, item),
onKeyDown: this.onHeaderKeyDown,
onClick: this.onHeaderClick,
},
span({
className: `theme-twisty${opened ? " open" : ""}`,

Просмотреть файл

@ -27,8 +27,8 @@ window._snapshots = {
tabIndex: 0,
"aria-expanded": false,
"aria-label": "Test Accordion Item 1",
onKeyDown: "event => this.onHeaderKeyDown(event, item)",
onClick: "event => this.onHeaderClick(event, item)",
onKeyDown: "function() {\n [native code]\n}",
onClick: "function() {\n [native code]\n}",
},
children: [
{
@ -73,8 +73,8 @@ window._snapshots = {
tabIndex: 0,
"aria-expanded": false,
"aria-label": "Test Accordion Item 2",
onKeyDown: "event => this.onHeaderKeyDown(event, item)",
onClick: "event => this.onHeaderClick(event, item)",
onKeyDown: "function() {\n [native code]\n}",
onClick: "function() {\n [native code]\n}",
},
children: [
{
@ -121,7 +121,7 @@ window._snapshots = {
type: "li",
props: {
id: "accordion-item-3",
className: "accordion-item accordion-open",
className: "accordion-item",
"aria-labelledby": "accordion-item-3-header",
},
children: [
@ -133,8 +133,8 @@ window._snapshots = {
tabIndex: 0,
"aria-expanded": true,
"aria-label": "Test Accordion Item 3",
onKeyDown: "event => this.onHeaderKeyDown(event, item)",
onClick: "event => this.onHeaderClick(event, item)",
onKeyDown: "function() {\n [native code]\n}",
onClick: "function() {\n [native code]\n}",
},
children: [
{

Просмотреть файл

@ -61,7 +61,6 @@ window.onload = async function() {
},
];
// Accordion basic render
const accordion = React.createElement(Accordion, { items: testItems });
matchSnapshot("Accordion basic render.", accordion);
@ -86,7 +85,7 @@ window.onload = async function() {
"accordion-item-1": true,
"accordion-item-2": false,
"accordion-item-3": true,
},
}
},
"State updated correctly"
);
@ -105,7 +104,7 @@ window.onload = async function() {
"accordion-item-1": false,
"accordion-item-2": false,
"accordion-item-3": true,
},
}
},
"State updated correctly"
);
@ -124,7 +123,7 @@ window.onload = async function() {
"accordion-item-1": false,
"accordion-item-2": true,
"accordion-item-3": true,
},
}
},
"State updated correctly"
);