Bug 1132203 - Tests for JSON Viewer; r=jryans

This commit is contained in:
Jan Odvarko 2015-09-28 13:33:45 +02:00
parent 522a672e19
commit 44cfe0bcf1
25 changed files with 462 additions and 13 deletions

View File

@ -50,7 +50,7 @@ var HeadersToolbar = React.createFactory(React.createClass({
render: function() {
return (
Toolbar({},
ToolbarButton({onClick: this.onCopy},
ToolbarButton({className: "copy", onClick: this.onCopy},
Locale.$STR("jsonViewer.Copy")
)
)

View File

@ -34,7 +34,7 @@ var JsonPanel = React.createClass({
},
onKeyPress: function(e) {
// TODO shortcut for focusing the Filter field.
// XXX shortcut for focusing the Filter field (see Bug 1178771).
},
render: function() {
@ -79,10 +79,10 @@ var JsonToolbar = React.createFactory(React.createClass({
render: function() {
return (
Toolbar({},
ToolbarButton({onClick: this.onSave},
ToolbarButton({className: "save", onClick: this.onSave},
Locale.$STR("jsonViewer.Save")
),
ToolbarButton({onClick: this.onCopy},
ToolbarButton({className: "copy", onClick: this.onCopy},
Locale.$STR("jsonViewer.Copy")
),
SearchBox({

View File

@ -38,20 +38,20 @@ var MainTabbedArea = React.createClass({
render: function() {
return (
Tabs({tabActive: this.state.tabActive, onAfterChange: this.onTabChanged},
TabPanel({title: Locale.$STR("jsonViewer.tab.JSON")},
TabPanel({className: "json", title: Locale.$STR("jsonViewer.tab.JSON")},
JsonPanel({
data: this.props.json,
actions: this.props.actions,
searchFilter: this.state.searchFilter
})
),
TabPanel({title: Locale.$STR("jsonViewer.tab.RawData")},
TabPanel({className: "rawdata", title: Locale.$STR("jsonViewer.tab.RawData")},
TextPanel({
data: this.state.jsonText,
actions: this.props.actions
})
),
TabPanel({title: Locale.$STR("jsonViewer.tab.Headers")},
TabPanel({className: "headers", title: Locale.$STR("jsonViewer.tab.Headers")},
HeadersPanel({
data: this.props.headers,
actions: this.props.actions,

View File

@ -127,9 +127,11 @@ var Tabs = React.createClass({
}).map(function(panel, index) {
var ref = ("tab-menu-" + (index + 1));
var title = panel.props.title;
var tabClassName = panel.props.className;
var classes = [
"tabs-menu-item",
tabClassName,
this.state.tabActive === (index + 1) && "is-active"
].join(" ");

View File

@ -46,13 +46,13 @@ var TextToolbar = React.createFactory(React.createClass({
render: function() {
return (
Toolbar({},
ToolbarButton({onClick: this.onPrettify},
ToolbarButton({className: "prettyprint",onClick: this.onPrettify},
Locale.$STR("jsonViewer.PrettyPrint")
),
ToolbarButton({onClick: this.onSave},
ToolbarButton({className: "save", onClick: this.onSave},
Locale.$STR("jsonViewer.Save")
),
ToolbarButton({onClick: this.onCopy},
ToolbarButton({className: "copy", onClick: this.onCopy},
Locale.$STR("jsonViewer.Copy")
)
)

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -89,6 +89,12 @@ var onResize = event => {
window.addEventListener("resize", onResize);
onResize();
// Send notification event to the window. Can be useful for
// tests as well as extensions.
var event = new CustomEvent("JSONViewInitialized", {});
window.jsonViewInitialized = true;
window.dispatchEvent(event);
// End of json-viewer.js
});

View File

@ -0,0 +1 @@
[{"name": "jan"},{"name": "honza"},{"name": "odvarko"}]

View File

@ -0,0 +1 @@
Content-Type: application/json; charset=utf-8

View File

@ -2,4 +2,20 @@
tags = devtools
subsuite = devtools
support-files =
array_json.json
array_json.json^headers^
doc_frame_script.js
head.js
invalid_json.json
invalid_json.json^headers^
simple_json.json
simple_json.json^headers^
valid_json.json
valid_json.json^headers^
[browser_jsonview_copy_headers.js]
[browser_jsonview_copy_json.js]
[browser_jsonview_copy_rawdata.js]
[browser_jsonview_filter.js]
[browser_jsonview_invalid_json.js]
[browser_jsonview_valid_json.js]

View File

@ -0,0 +1,35 @@
/* -*- 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_JSON_URL = URL_ROOT + "valid_json.json";
add_task(function* () {
info("Test valid JSON started");
let tab = yield addJsonViewTab(TEST_JSON_URL);
// Select the RawData tab
yield selectJsonViewContentTab("headers");
// Check displayed headers
let count = yield getElementCount(".headersPanelBox .netHeadersGroup");
is(count, 2, "There must be two header groups");
let text = yield getElementText(".headersPanelBox .netInfoHeadersTable");
isnot(text, "", "Headers text must not be empty");
let browser = gBrowser.selectedBrowser
// Verify JSON copy into the clipboard.
yield waitForClipboardPromise(function setup() {
BrowserTestUtils.synthesizeMouseAtCenter(
".headersPanelBox .toolbar button.copy",
{}, browser);
}, function validator(value) {
return value.indexOf("application/json") > 0;
});
});

View File

@ -0,0 +1,31 @@
/* -*- 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_JSON_URL = URL_ROOT + "simple_json.json";
add_task(function* () {
info("Test copy JSON started");
let tab = yield addJsonViewTab(TEST_JSON_URL);
let countBefore = yield getElementCount(".jsonPanelBox .domTable .memberRow");
ok(countBefore == 1, "There must be one row");
let text = yield getElementText(".jsonPanelBox .domTable .memberRow");
is(text, "name\"value\"", "There must be proper JSON displayed");
// Verify JSON copy into the clipboard.
let value = "{\"name\": \"value\"}\n";
let browser = gBrowser.selectedBrowser
let selector = ".jsonPanelBox .toolbar button.copy";
yield waitForClipboardPromise(function setup() {
BrowserTestUtils.synthesizeMouseAtCenter(selector, {}, browser);
}, function validator(result) {
let str = normalizeNewLines(result);
return str == value;
});
});

View File

@ -0,0 +1,52 @@
/* -*- 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_JSON_URL = URL_ROOT + "simple_json.json";
let jsonText = "{\"name\": \"value\"}\n";
let prettyJson = "{\n \"name\": \"value\"\n}";
add_task(function* () {
info("Test copy raw data started");
let tab = yield addJsonViewTab(TEST_JSON_URL);
// Select the RawData tab
yield selectJsonViewContentTab("rawdata");
// Check displayed JSON
let text = yield getElementText(".textPanelBox .data");
is(text, jsonText, "Proper JSON must be displayed in DOM");
let browser = gBrowser.selectedBrowser
// Verify JSON copy into the clipboard.
yield waitForClipboardPromise(function setup() {
BrowserTestUtils.synthesizeMouseAtCenter(
".textPanelBox .toolbar button.copy",
{}, browser);
}, jsonText);
// Click 'Pretty Print' button
yield BrowserTestUtils.synthesizeMouseAtCenter(
".textPanelBox .toolbar button.prettyprint",
{}, browser);
let prettyText = yield getElementText(".textPanelBox .data");
prettyText = normalizeNewLines(prettyText);
ok(prettyText.startsWith(prettyJson), "Pretty printed JSON must be displayed");
// Verify JSON copy into the clipboard.
yield waitForClipboardPromise(function setup() {
BrowserTestUtils.synthesizeMouseAtCenter(
".textPanelBox .toolbar button.copy",
{}, browser);
}, function validator(value) {
let str = normalizeNewLines(value);
return str == prettyJson;
});
});

View File

@ -0,0 +1,27 @@
/* -*- 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_JSON_URL = URL_ROOT + "array_json.json";
add_task(function* () {
info("Test valid JSON started");
let tab = yield addJsonViewTab(TEST_JSON_URL);
let count = yield getElementCount(".jsonPanelBox .domTable .memberRow");
is(count, 3, "There must be three rows");
// XXX use proper shortcut to focus the filter box
// as soon as bug Bug 1178771 is fixed.
yield sendString("h", ".jsonPanelBox .searchBox");
// The filtering is done asynchronously so, we need to wait.
yield waitForFilter();
let hiddenCount = yield getElementCount(".jsonPanelBox .domTable .memberRow.hidden");
is(hiddenCount, 2, "There must be two hidden rows");
});

View File

@ -0,0 +1,20 @@
/* -*- 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_JSON_URL = URL_ROOT + "invalid_json.json";
add_task(function* () {
info("Test invalid JSON started");
let tab = yield addJsonViewTab(TEST_JSON_URL);
let count = yield getElementCount(".jsonPanelBox .domTable .memberRow");
ok(count == 0, "There must be no row");
let text = yield getElementText(".jsonPanelBox .jsonParseError");
ok(text, "There must be an error description");
});

View File

@ -0,0 +1,22 @@
/* -*- 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_JSON_URL = URL_ROOT + "valid_json.json";
add_task(function* () {
info("Test valid JSON started");
let tab = yield addJsonViewTab(TEST_JSON_URL);
let countBefore = yield getElementCount(".jsonPanelBox .domTable .memberRow");
ok(countBefore == 1, "There must be one row");
yield expandJsonNode(".jsonPanelBox .domTable .memberLabel");
let countAfter = yield getElementCount(".jsonPanelBox .domTable .memberRow");
ok(countAfter == 3, "There must be three rows");
});

View File

@ -0,0 +1,89 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// XXX Some helper API could go to testing/mochitest/tests/SimpleTest/AsyncContentUtils.js
// (or at least to share test API in devtools)
// Set up a dummy environment so that EventUtils works. We need to be careful to
// pass a window object into each EventUtils method we call rather than having
// it rely on the |window| global.
let EventUtils = {};
EventUtils.window = content;
EventUtils.parent = EventUtils.window;
EventUtils._EU_Ci = Components.interfaces;
EventUtils._EU_Cc = Components.classes;
EventUtils.navigator = content.navigator;
EventUtils.KeyboardEvent = content.KeyboardEvent;
Services.scriptloader.loadSubScript(
"chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
/**
* When the JSON View is done rendering it triggers custom event
* "JSONViewInitialized", then the Test:TestPageProcessingDone message
* will be sent to the parent process for tests to wait for this event
* if needed.
*/
content.addEventListener("JSONViewInitialized", () => {
sendAsyncMessage("Test:JsonView:JSONViewInitialized");
}, false);
addMessageListener("Test:JsonView:GetElementCount", function(msg) {
let {selector} = msg.data;
let nodeList = content.document.querySelectorAll(selector);
sendAsyncMessage(msg.name, {count: nodeList.length});
});
addMessageListener("Test:JsonView:GetElementText", function(msg) {
let {selector} = msg.data;
let element = content.document.querySelector(selector);
let text = element ? element.textContent : null;
sendAsyncMessage(msg.name, {text: text});
});
addMessageListener("Test:JsonView:FocusElement", function(msg) {
let {selector} = msg.data;
let element = content.document.querySelector(selector);
if (element) {
element.focus();
}
sendAsyncMessage(msg.name);
});
addMessageListener("Test:JsonView:SendString", function(msg) {
let {selector, str} = msg.data;
if (selector) {
let element = content.document.querySelector(selector);
if (element) {
element.focus();
}
}
EventUtils.sendString(str, content);
sendAsyncMessage(msg.name);
});
addMessageListener("Test:JsonView:WaitForFilter", function(msg) {
let firstRow = content.document.querySelector(
".jsonPanelBox .domTable .memberRow");
// Wait till the first row has 'hidden' class set.
var observer = new content.MutationObserver(function(mutations) {
for (let i = 0; i < mutations.length; i++) {
let mutation = mutations[i];
if (mutation.attributeName == "class") {
if (firstRow.classList.contains("hidden")) {
observer.disconnect();
sendAsyncMessage(msg.name);
break;
}
}
}
});
observer.observe(firstRow, { attributes: true });
});

View File

@ -1,3 +1,137 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// shared-head.js handles imports, constants, and utility functions
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/head.js", this);
// XXX move some API into devtools/framework/test/shared-head.js
/**
* 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 addJsonViewTab(url) {
info("Adding a new JSON tab with URL: '" + url + "'");
let deferred = promise.defer();
addTab(url).then(tab => {
let browser = tab.linkedBrowser;
// Load devtools/shared/frame-script-utils.js
getFrameScript();
// Load frame script with helpers for JSON View tests.
let rootDir = getRootDirectory(gTestPath);
let frameScriptUrl = rootDir + "doc_frame_script.js";
browser.messageManager.loadFrameScript(frameScriptUrl, false);
// Resolve if the JSONView is fully loaded or wait
// for an initialization event.
if (content.window.wrappedJSObject.jsonViewInitialized) {
deferred.resolve(tab);
} else {
waitForContentMessage("Test:JsonView:JSONViewInitialized").then(() => {
deferred.resolve(tab);
});
}
});
return deferred.promise;
}
/**
* Expanding a node in the JSON tree
*/
function expandJsonNode(selector) {
info("Expanding node: '" + selector + "'");
let browser = gBrowser.selectedBrowser;
return BrowserTestUtils.synthesizeMouseAtCenter(selector, {}, browser);
}
/**
* Select JSON View tab (in the content).
*/
function selectJsonViewContentTab(name) {
info("Selecting tab: '" + name + "'");
let browser = gBrowser.selectedBrowser;
let selector = ".tabs-menu .tabs-menu-item." + name + " a";
return BrowserTestUtils.synthesizeMouseAtCenter(selector, {}, browser);
}
function getElementCount(selector) {
info("Get element count: '" + selector + "'");
let data = {
selector: selector
};
return executeInContent("Test:JsonView:GetElementCount", data).then(result => {
return result.count;
});
}
function getElementText(selector) {
info("Get element text: '" + selector + "'");
let data = {
selector: selector
};
return executeInContent("Test:JsonView:GetElementText", data).then(result => {
return result.text;
});
}
function focusElement(selector) {
info("Focus element: '" + selector + "'");
let data = {
selector: selector
};
return executeInContent("Test:JsonView:FocusElement", data);
}
/**
* Send the string aStr to the focused element.
*
* For now this method only works for ASCII characters and emulates the shift
* key state on US keyboard layout.
*/
function sendString(str, selector) {
info("Send string: '" + str + "'");
let data = {
selector: selector,
str: str
};
return executeInContent("Test:JsonView:SendString", data);
}
function waitForTime(aDelay) {
let deferred = promise.defer();
setTimeout(deferred.resolve, aDelay);
return deferred.promise;
}
function waitForClipboardPromise(setup, expected) {
return new Promise((resolve, reject) => {
SimpleTest.waitForClipboard(expected, setup, resolve, reject);
});
}
function waitForFilter() {
return executeInContent("Test:JsonView:WaitForFilter");
}
function normalizeNewLines(value) {
return value.replace("(\r\n|\n)", "\n");
}

View File

@ -0,0 +1 @@
{,}

View File

@ -0,0 +1 @@
Content-Type: application/json; charset=utf-8

View File

@ -0,0 +1 @@
{"name": "value"}

View File

@ -0,0 +1 @@
Content-Type: application/json; charset=utf-8

View File

@ -0,0 +1,6 @@
{
"family": {
"father": "John Doe",
"mother": "Alice Doe"
}
}

View File

@ -0,0 +1 @@
Content-Type: application/json; charset=utf-8

View File

@ -28,6 +28,7 @@ user_pref("javascript.options.showInConsole", true);
user_pref("devtools.browsertoolbox.panel", "jsdebugger");
user_pref("devtools.errorconsole.enabled", true);
user_pref("devtools.debugger.remote-port", 6023);
user_pref("devtools.jsonview.enabled", true);
user_pref("browser.EULA.override", true);
user_pref("gfx.color_management.force_srgb", true);
user_pref("network.manage-offline-status", false);