import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,5 @@
"use strict";
module.exports = {
"extends": ["../../../../.eslintrc.xpcshell.js"]
};

View file

@ -0,0 +1,7 @@
[DEFAULT]
support-files =
head.js
[test_render_perf.html]
skip-if = true # Bug 1306783

View file

@ -0,0 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var { utils: Cu } = Components;
var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
var { Assert } = require("resource://testing-common/Assert.jsm");
var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
var { Task } = require("devtools/shared/task");
var { require: browserRequire } = BrowserLoader({
baseURI: "resource://devtools/client/webconsole/",
window
});

View file

@ -0,0 +1,90 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf8">
<title>Test for getRepeatId()</title>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript;version=1.8" src="head.js"></script>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
</head>
<body>
<p>Test for render perf</p>
<div id="output"></div>
<script type="text/javascript;version=1.8">
const testPackets = [];
const numMessages = 1000;
for (let id = 0; id < numMessages; id++) {
let message = "Odd text";
if (id % 2 === 0) {
message = "Even text";
}
testPackets.push({
"from": "server1.conn4.child1/consoleActor2",
"type": "consoleAPICall",
"message": {
"arguments": [
"foobar",
message,
id
],
"columnNumber": 1,
"counter": null,
"filename": "file:///test.html",
"functionName": "",
"groupName": "",
"level": "log",
"lineNumber": 1,
"private": false,
"styles": [],
"timeStamp": 1455064271115 + id,
"timer": null,
"workerType": "none",
"category": "webdev"
}
});
}
function timeit(cb) {
// Return a Promise that resolves the number of seconds cb takes.
return new Promise(resolve => {
let start = performance.now();
cb();
let elapsed = performance.now() - start;
resolve(elapsed / 1000);
});
}
window.onload = Task.async(function* () {
const { configureStore } = browserRequire("devtools/client/webconsole/new-console-output/store");
const { filterTextSet, filtersClear } = browserRequire("devtools/client/webconsole/new-console-output/actions/index");
const NewConsoleOutputWrapper = browserRequire("devtools/client/webconsole/new-console-output/new-console-output-wrapper");
const wrapper = new NewConsoleOutputWrapper(document.querySelector("#output"), {});
const store = configureStore();
let time = yield timeit(() => {
testPackets.forEach((message) => {
wrapper.dispatchMessageAdd(message);
});
});
info("took " + time + " seconds to render messages");
time = yield timeit(() => {
store.dispatch(filterTextSet("Odd text"));
});
info("took " + time + " seconds to search filter half the messages");
time = yield timeit(() => {
store.dispatch(filtersClear());
});
info("took " + time + " seconds to clear the filter");
ok(true, "Yay, it didn't time out!");
SimpleTest.finish();
});
</script>
</body>
</html>

View file

@ -0,0 +1,230 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test utils.
const expect = require("expect");
const { render, mount } = require("enzyme");
const sinon = require("sinon");
// React
const { createFactory } = require("devtools/client/shared/vendor/react");
const Provider = createFactory(require("react-redux").Provider);
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
// Components under test.
const ConsoleApiCall = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call"));
const {
MESSAGE_OPEN,
MESSAGE_CLOSE,
} = require("devtools/client/webconsole/new-console-output/constants");
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
// Test fakes.
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
const tempfilePath = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js";
describe("ConsoleAPICall component:", () => {
describe("console.log", () => {
it("renders string grips", () => {
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
expect(wrapper.find(".message-body").text()).toBe("foobar test");
expect(wrapper.find(".objectBox-string").length).toBe(2);
expect(wrapper.find("div.message.cm-s-mozilla span span.message-flex-body span.message-body.devtools-monospace").length).toBe(1);
// There should be the location
const locationLink = wrapper.find(`.message-location`);
expect(locationLink.length).toBe(1);
expect(locationLink.text()).toBe("test-tempfile.js:1:27");
});
it("renders string grips with custom style", () => {
const message = stubPreparedMessages.get("console.log(%cfoobar)");
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
const elements = wrapper.find(".objectBox-string");
expect(elements.text()).toBe("foobar");
expect(elements.length).toBe(2);
const firstElementStyle = elements.eq(0).prop("style");
// Allowed styles are applied accordingly on the first element.
expect(firstElementStyle.color).toBe(`blue`);
expect(firstElementStyle["font-size"]).toBe(`1.3em`);
// Forbidden styles are not applied.
expect(firstElementStyle["background-image"]).toBe(undefined);
expect(firstElementStyle.position).toBe(undefined);
expect(firstElementStyle.top).toBe(undefined);
const secondElementStyle = elements.eq(1).prop("style");
// Allowed styles are applied accordingly on the second element.
expect(secondElementStyle.color).toBe(`red`);
// Forbidden styles are not applied.
expect(secondElementStyle.background).toBe(undefined);
});
it("renders repeat node", () => {
const message =
stubPreparedMessages.get("console.log('foobar', 'test')")
.set("repeat", 107);
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
expect(wrapper.find(".message-repeats").text()).toBe("107");
expect(wrapper.find(".message-repeats").prop("title")).toBe("107 repeats");
expect(wrapper.find("span > span.message-flex-body > span.message-body.devtools-monospace + span.message-repeats").length).toBe(1);
});
it("has the expected indent", () => {
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
const indent = 10;
let wrapper = render(ConsoleApiCall({ message, serviceContainer, indent }));
expect(wrapper.find(".indent").prop("style").width)
.toBe(`${indent * INDENT_WIDTH}px`);
wrapper = render(ConsoleApiCall({ message, serviceContainer}));
expect(wrapper.find(".indent").prop("style").width).toBe(`0`);
});
});
describe("console.count", () => {
it("renders", () => {
const message = stubPreparedMessages.get("console.count('bar')");
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
expect(wrapper.find(".message-body").text()).toBe("bar: 1");
});
});
describe("console.assert", () => {
it("renders", () => {
const message = stubPreparedMessages.get("console.assert(false, {message: 'foobar'})");
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
expect(wrapper.find(".message-body").text()).toBe("Assertion failed: Object { message: \"foobar\" }");
});
});
describe("console.time", () => {
it("does not show anything", () => {
const message = stubPreparedMessages.get("console.time('bar')");
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
expect(wrapper.find(".message-body").text()).toBe("");
});
});
describe("console.timeEnd", () => {
it("renders as expected", () => {
const message = stubPreparedMessages.get("console.timeEnd('bar')");
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
expect(wrapper.find(".message-body").text()).toBe(message.messageText);
expect(wrapper.find(".message-body").text()).toMatch(/^bar: \d+(\.\d+)?ms$/);
});
});
describe("console.trace", () => {
it("renders", () => {
const message = stubPreparedMessages.get("console.trace()");
const wrapper = render(ConsoleApiCall({ message, serviceContainer, open: true }));
const filepath = `${tempfilePath}`;
expect(wrapper.find(".message-body").text()).toBe("console.trace()");
const frameLinks = wrapper.find(`.stack-trace span.frame-link[data-url='${filepath}']`);
expect(frameLinks.length).toBe(3);
expect(frameLinks.eq(0).find(".frame-link-function-display-name").text()).toBe("testStacktraceFiltering");
expect(frameLinks.eq(0).find(".frame-link-filename").text()).toBe(filepath);
expect(frameLinks.eq(1).find(".frame-link-function-display-name").text()).toBe("foo");
expect(frameLinks.eq(1).find(".frame-link-filename").text()).toBe(filepath);
expect(frameLinks.eq(2).find(".frame-link-function-display-name").text()).toBe("triggerPacket");
expect(frameLinks.eq(2).find(".frame-link-filename").text()).toBe(filepath);
//it should not be collapsible.
expect(wrapper.find(`.theme-twisty`).length).toBe(0);
});
});
describe("console.group", () => {
it("renders", () => {
const message = stubPreparedMessages.get("console.group('bar')");
const wrapper = render(ConsoleApiCall({ message, serviceContainer, open: true }));
expect(wrapper.find(".message-body").text()).toBe(message.messageText);
expect(wrapper.find(".theme-twisty.open").length).toBe(1);
});
it("toggle the group when the collapse button is clicked", () => {
const store = setupStore([]);
store.dispatch = sinon.spy();
const message = stubPreparedMessages.get("console.group('bar')");
let wrapper = mount(Provider({store},
ConsoleApiCall({
message,
open: true,
dispatch: store.dispatch,
serviceContainer,
})
));
wrapper.find(".theme-twisty.open").simulate("click");
let call = store.dispatch.getCall(0);
expect(call.args[0]).toEqual({
id: message.id,
type: MESSAGE_CLOSE
});
wrapper = mount(Provider({store},
ConsoleApiCall({
message,
open: false,
dispatch: store.dispatch,
serviceContainer,
})
));
wrapper.find(".theme-twisty").simulate("click");
call = store.dispatch.getCall(1);
expect(call.args[0]).toEqual({
id: message.id,
type: MESSAGE_OPEN
});
});
});
describe("console.groupEnd", () => {
it("does not show anything", () => {
const message = stubPreparedMessages.get("console.groupEnd('bar')");
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
expect(wrapper.find(".message-body").text()).toBe("");
});
});
describe("console.groupCollapsed", () => {
it("renders", () => {
const message = stubPreparedMessages.get("console.groupCollapsed('foo')");
const wrapper = render(ConsoleApiCall({ message, serviceContainer, open: false}));
expect(wrapper.find(".message-body").text()).toBe(message.messageText);
expect(wrapper.find(".theme-twisty:not(.open)").length).toBe(1);
});
});
describe("console.dirxml", () => {
it("renders", () => {
const message = stubPreparedMessages.get("console.dirxml(window)");
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
expect(wrapper.find(".message-body").text())
.toBe("Window http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html");
});
});
});

View file

@ -0,0 +1,84 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test utils.
const expect = require("expect");
const { render, mount } = require("enzyme");
const sinon = require("sinon");
// React
const { createFactory } = require("devtools/client/shared/vendor/react");
const Provider = createFactory(require("react-redux").Provider);
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
// Components under test.
const EvaluationResult = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result"));
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
// Test fakes.
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
describe("EvaluationResult component:", () => {
it("renders a grip result", () => {
const message = stubPreparedMessages.get("new Date(0)");
const wrapper = render(EvaluationResult({ message }));
expect(wrapper.find(".message-body").text()).toBe("Date 1970-01-01T00:00:00.000Z");
expect(wrapper.find(".message.log").length).toBe(1);
});
it("renders an error", () => {
const message = stubPreparedMessages.get("asdf()");
const wrapper = render(EvaluationResult({ message }));
expect(wrapper.find(".message-body").text())
.toBe("ReferenceError: asdf is not defined[Learn More]");
expect(wrapper.find(".message.error").length).toBe(1);
});
it("displays a [Learn more] link", () => {
const store = setupStore([]);
const message = stubPreparedMessages.get("asdf()");
serviceContainer.openLink = sinon.spy();
const wrapper = mount(Provider({store},
EvaluationResult({message, serviceContainer})
));
const url =
"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined";
const learnMore = wrapper.find(".learn-more-link");
expect(learnMore.length).toBe(1);
expect(learnMore.prop("title")).toBe(url);
learnMore.simulate("click");
let call = serviceContainer.openLink.getCall(0);
expect(call.args[0]).toEqual(message.exceptionDocURL);
});
it("has the expected indent", () => {
const message = stubPreparedMessages.get("new Date(0)");
const indent = 10;
let wrapper = render(EvaluationResult({ message, indent}));
expect(wrapper.find(".indent").prop("style").width)
.toBe(`${indent * INDENT_WIDTH}px`);
wrapper = render(EvaluationResult({ message}));
expect(wrapper.find(".indent").prop("style").width).toBe(`0`);
});
it("has location information", () => {
const message = stubPreparedMessages.get("1 + @");
const wrapper = render(EvaluationResult({ message }));
const locationLink = wrapper.find(`.message-location`);
expect(locationLink.length).toBe(1);
expect(locationLink.text()).toBe("debugger eval code:1:4");
});
});

View file

@ -0,0 +1,96 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const expect = require("expect");
const sinon = require("sinon");
const { render, mount } = require("enzyme");
const { createFactory } = require("devtools/client/shared/vendor/react");
const Provider = createFactory(require("react-redux").Provider);
const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button"));
const FilterBar = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar"));
const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
const {
MESSAGES_CLEAR,
MESSAGE_LEVEL
} = require("devtools/client/webconsole/new-console-output/constants");
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
describe("FilterBar component:", () => {
it("initial render", () => {
const store = setupStore([]);
const wrapper = render(Provider({store}, FilterBar({ serviceContainer })));
const toolbar = wrapper.find(
".devtools-toolbar.webconsole-filterbar-primary"
);
// Clear button
expect(toolbar.children().eq(0).attr("class"))
.toBe("devtools-button devtools-clear-icon");
expect(toolbar.children().eq(0).attr("title")).toBe("Clear output");
// Filter bar toggle
expect(toolbar.children().eq(1).attr("class"))
.toBe("devtools-button devtools-filter-icon");
expect(toolbar.children().eq(1).attr("title")).toBe("Toggle filter bar");
// Text filter
expect(toolbar.children().eq(2).attr("class")).toBe("devtools-plaininput text-filter");
expect(toolbar.children().eq(2).attr("placeholder")).toBe("Filter output");
expect(toolbar.children().eq(2).attr("type")).toBe("search");
expect(toolbar.children().eq(2).attr("value")).toBe("");
});
it("displays filter bar when button is clicked", () => {
const store = setupStore([]);
expect(getAllUi(store.getState()).filterBarVisible).toBe(false);
const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
wrapper.find(".devtools-filter-icon").simulate("click");
expect(getAllUi(store.getState()).filterBarVisible).toBe(true);
// Buttons are displayed
const buttonProps = {
active: true,
dispatch: store.dispatch
};
const logButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Logs", filterKey: MESSAGE_LEVEL.LOG }));
const debugButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Debug", filterKey: MESSAGE_LEVEL.DEBUG }));
const infoButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Info", filterKey: MESSAGE_LEVEL.INFO }));
const warnButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Warnings", filterKey: MESSAGE_LEVEL.WARN }));
const errorButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Errors", filterKey: MESSAGE_LEVEL.ERROR }));
expect(wrapper.contains([errorButton, warnButton, logButton, infoButton, debugButton])).toBe(true);
});
it("fires MESSAGES_CLEAR action when clear button is clicked", () => {
const store = setupStore([]);
store.dispatch = sinon.spy();
const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
wrapper.find(".devtools-clear-icon").simulate("click");
const call = store.dispatch.getCall(0);
expect(call.args[0]).toEqual({
type: MESSAGES_CLEAR
});
});
it("sets filter text when text is typed", () => {
const store = setupStore([]);
const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
wrapper.find(".devtools-plaininput").simulate("input", { target: { value: "a" } });
expect(store.getState().filters.text).toBe("a");
});
});

View file

@ -0,0 +1,34 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const expect = require("expect");
const { render } = require("enzyme");
const { createFactory } = require("devtools/client/shared/vendor/react");
const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button"));
const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
describe("FilterButton component:", () => {
const props = {
active: true,
label: "Error",
filterKey: MESSAGE_LEVEL.ERROR,
};
it("displays as active when turned on", () => {
const wrapper = render(FilterButton(props));
expect(wrapper.html()).toBe(
"<button class=\"menu-filter-button error checked\">Error</button>"
);
});
it("displays as inactive when turned off", () => {
const inactiveProps = Object.assign({}, props, { active: false });
const wrapper = render(FilterButton(inactiveProps));
expect(wrapper.html()).toBe(
"<button class=\"menu-filter-button error\">Error</button>"
);
});
});

View file

@ -0,0 +1,54 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test utils.
const expect = require("expect");
const {
renderComponent,
shallowRenderComponent
} = require("devtools/client/webconsole/new-console-output/test/helpers");
// Components under test.
const { MessageContainer } = require("devtools/client/webconsole/new-console-output/components/message-container");
const ConsoleApiCall = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call");
const EvaluationResult = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result");
const PageError = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
// Test fakes.
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
describe("MessageContainer component:", () => {
it("pipes data to children as expected", () => {
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
const rendered = renderComponent(MessageContainer, {message, serviceContainer});
expect(rendered.textContent.includes("foobar")).toBe(true);
});
it("picks correct child component", () => {
const messageTypes = [
{
component: ConsoleApiCall,
message: stubPreparedMessages.get("console.log('foobar', 'test')")
},
{
component: EvaluationResult,
message: stubPreparedMessages.get("new Date(0)")
},
{
component: PageError,
message: stubPreparedMessages.get("ReferenceError: asdf is not defined")
}
];
messageTypes.forEach(info => {
const { component, message } = info;
const rendered = shallowRenderComponent(MessageContainer, {
message,
serviceContainer,
});
expect(rendered.type).toBe(component);
});
});
});

View file

@ -0,0 +1,23 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const {
MESSAGE_LEVEL,
} = require("devtools/client/webconsole/new-console-output/constants");
const MessageIcon = require("devtools/client/webconsole/new-console-output/components/message-icon");
const expect = require("expect");
const {
renderComponent
} = require("devtools/client/webconsole/new-console-output/test/helpers");
describe("MessageIcon component:", () => {
it("renders icon based on level", () => {
const rendered = renderComponent(MessageIcon, { level: MESSAGE_LEVEL.ERROR });
expect(rendered.classList.contains("icon")).toBe(true);
expect(rendered.getAttribute("title")).toBe("Error");
});
});

View file

@ -0,0 +1,25 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const MessageRepeat = require("devtools/client/webconsole/new-console-output/components/message-repeat");
const expect = require("expect");
const {
renderComponent
} = require("devtools/client/webconsole/new-console-output/test/helpers");
describe("MessageRepeat component:", () => {
it("renders repeated value correctly", () => {
const rendered = renderComponent(MessageRepeat, { repeat: 99 });
expect(rendered.classList.contains("message-repeats")).toBe(true);
expect(rendered.style.visibility).toBe("visible");
expect(rendered.textContent).toBe("99");
});
it("renders an un-repeated value correctly", () => {
const rendered = renderComponent(MessageRepeat, { repeat: 1 });
expect(rendered.style.visibility).toBe("hidden");
});
});

View file

@ -0,0 +1,74 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test utils.
const expect = require("expect");
const { render } = require("enzyme");
// React
const { createFactory } = require("devtools/client/shared/vendor/react");
// Components under test.
const NetworkEventMessage = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/network-event-message"));
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
// Test fakes.
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
const EXPECTED_URL = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html";
describe("NetworkEventMessage component:", () => {
describe("GET request", () => {
it("renders as expected", () => {
const message = stubPreparedMessages.get("GET request");
const wrapper = render(NetworkEventMessage({ message, serviceContainer }));
expect(wrapper.find(".message-body .method").text()).toBe("GET");
expect(wrapper.find(".message-body .xhr").length).toBe(0);
expect(wrapper.find(".message-body .url").length).toBe(1);
expect(wrapper.find(".message-body .url").text()).toBe(EXPECTED_URL);
expect(wrapper.find("div.message.cm-s-mozilla span.message-body.devtools-monospace").length).toBe(1);
});
it("has the expected indent", () => {
const message = stubPreparedMessages.get("GET request");
const indent = 10;
let wrapper = render(NetworkEventMessage({ message, serviceContainer, indent}));
expect(wrapper.find(".indent").prop("style").width)
.toBe(`${indent * INDENT_WIDTH}px`);
wrapper = render(NetworkEventMessage({ message, serviceContainer }));
expect(wrapper.find(".indent").prop("style").width).toBe(`0`);
});
});
describe("XHR GET request", () => {
it("renders as expected", () => {
const message = stubPreparedMessages.get("XHR GET request");
const wrapper = render(NetworkEventMessage({ message, serviceContainer }));
expect(wrapper.find(".message-body .method").text()).toBe("GET");
expect(wrapper.find(".message-body .xhr").length).toBe(1);
expect(wrapper.find(".message-body .xhr").text()).toBe("XHR");
expect(wrapper.find(".message-body .url").text()).toBe(EXPECTED_URL);
expect(wrapper.find("div.message.cm-s-mozilla span.message-body.devtools-monospace").length).toBe(1);
});
});
describe("XHR POST request", () => {
it("renders as expected", () => {
const message = stubPreparedMessages.get("XHR POST request");
const wrapper = render(NetworkEventMessage({ message, serviceContainer }));
expect(wrapper.find(".message-body .method").text()).toBe("POST");
expect(wrapper.find(".message-body .xhr").length).toBe(1);
expect(wrapper.find(".message-body .xhr").text()).toBe("XHR");
expect(wrapper.find(".message-body .url").length).toBe(1);
expect(wrapper.find(".message-body .url").text()).toBe(EXPECTED_URL);
expect(wrapper.find("div.message.cm-s-mozilla span.message-body.devtools-monospace").length).toBe(1);
});
});
});

View file

@ -0,0 +1,126 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test utils.
const expect = require("expect");
const { render, mount } = require("enzyme");
const sinon = require("sinon");
// React
const { createFactory } = require("devtools/client/shared/vendor/react");
const Provider = createFactory(require("react-redux").Provider);
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
// Components under test.
const PageError = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
const {
MESSAGE_OPEN,
MESSAGE_CLOSE,
} = require("devtools/client/webconsole/new-console-output/constants");
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
// Test fakes.
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
describe("PageError component:", () => {
it("renders", () => {
const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
const wrapper = render(PageError({ message, serviceContainer }));
expect(wrapper.find(".message-body").text())
.toBe("ReferenceError: asdf is not defined[Learn More]");
// The stacktrace should be closed by default.
const frameLinks = wrapper.find(`.stack-trace`);
expect(frameLinks.length).toBe(0);
// There should be the location.
const locationLink = wrapper.find(`.message-location`);
expect(locationLink.length).toBe(1);
// @TODO Will likely change. See https://github.com/devtools-html/gecko-dev/issues/285
expect(locationLink.text()).toBe("test-tempfile.js:3:5");
});
it("displays a [Learn more] link", () => {
const store = setupStore([]);
const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
serviceContainer.openLink = sinon.spy();
const wrapper = mount(Provider({store},
PageError({message, serviceContainer})
));
// There should be a [Learn more] link.
const url =
"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined";
const learnMore = wrapper.find(".learn-more-link");
expect(learnMore.length).toBe(1);
expect(learnMore.prop("title")).toBe(url);
learnMore.simulate("click");
let call = serviceContainer.openLink.getCall(0);
expect(call.args[0]).toEqual(message.exceptionDocURL);
});
it("has a stacktrace which can be openned", () => {
const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
const wrapper = render(PageError({ message, serviceContainer, open: true }));
// There should be a collapse button.
expect(wrapper.find(".theme-twisty.open").length).toBe(1);
// There should be three stacktrace items.
const frameLinks = wrapper.find(`.stack-trace span.frame-link`);
expect(frameLinks.length).toBe(3);
});
it("toggle the stacktrace when the collapse button is clicked", () => {
const store = setupStore([]);
store.dispatch = sinon.spy();
const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
let wrapper = mount(Provider({store},
PageError({
message,
open: true,
dispatch: store.dispatch,
serviceContainer,
})
));
wrapper.find(".theme-twisty.open").simulate("click");
let call = store.dispatch.getCall(0);
expect(call.args[0]).toEqual({
id: message.id,
type: MESSAGE_CLOSE
});
wrapper = mount(Provider({store},
PageError({
message,
open: false,
dispatch: store.dispatch,
serviceContainer,
})
));
wrapper.find(".theme-twisty").simulate("click");
call = store.dispatch.getCall(1);
expect(call.args[0]).toEqual({
id: message.id,
type: MESSAGE_OPEN
});
});
it("has the expected indent", () => {
const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
const indent = 10;
let wrapper = render(PageError({ message, serviceContainer, indent}));
expect(wrapper.find(".indent").prop("style").width)
.toBe(`${indent * INDENT_WIDTH}px`);
wrapper = render(PageError({ message, serviceContainer}));
expect(wrapper.find(".indent").prop("style").width).toBe(`0`);
});
});

View file

@ -0,0 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// @TODO Load the actual strings from webconsole.properties instead.
class L10n {
getStr(str) {
switch (str) {
case "level.error":
return "Error";
case "consoleCleared":
return "Console was cleared.";
case "webConsoleXhrIndicator":
return "XHR";
case "webConsoleMoreInfoLabel":
return "Learn More";
}
return str;
}
getFormatStr(str) {
return this.getStr(str);
}
}
module.exports = L10n;

View file

@ -0,0 +1,10 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const LocalizationHelper = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
module.exports = {
LocalizationHelper
};

View file

@ -0,0 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
class ObjectClient {
}
module.exports = ObjectClient;

View file

@ -0,0 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
module.exports = {
PluralForm: {
get: function (occurence, str) {
// @TODO Remove when loading the actual strings from webconsole.properties
// is done in the L10n fixture.
if (str === "messageRepeats.tooltip2") {
return `${occurence} repeats`;
}
return str;
}
}
};

View file

@ -0,0 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { PREFS } = require("devtools/client/webconsole/new-console-output/constants");
module.exports = {
prefs: {
getIntPref: pref => {
switch (pref) {
case "devtools.hud.loglimit":
return 1000;
}
},
getBoolPref: pref => {
const falsey = [
PREFS.FILTER.NET,
PREFS.FILTER.NETXHR,
PREFS.UI.FILTER_BAR,
];
return !falsey.includes(pref);
},
setBoolPref: () => {},
clearUserPref: () => {},
}
};

View file

@ -0,0 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const L10n = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
const Utils = {
L10n
};
module.exports = {
Utils
};

View file

@ -0,0 +1,9 @@
# vim: set filetype=python:
# 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/.
DIRS += [
'stub-generators',
'stubs'
]

View file

@ -0,0 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
module.exports = {
attachRefToHud: () => {},
emitNewMessage: () => {},
hudProxyClient: {},
onViewSourceInDebugger: () => {},
openNetworkPanel: () => {},
sourceMapService: {
subscribe: () => {},
},
openLink: () => {},
createElement: tagName => document.createElement(tagName)
};

View file

@ -0,0 +1,18 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js
!/devtools/client/framework/test/shared-head.js
test-console-api.html
test-network-event.html
test-tempfile.js
[browser_webconsole_update_stubs_console_api.js]
skip-if=true # This is only used to update stubs. It is not an actual test.
[browser_webconsole_update_stubs_evaluation_result.js]
skip-if=true # This is only used to update stubs. It is not an actual test.
[browser_webconsole_update_stubs_network_event.js]
skip-if=true # This is only used to update stubs. It is not an actual test.
[browser_webconsole_update_stubs_page_error.js]
skip-if=true # This is only used to update stubs. It is not an actual test.

View file

@ -0,0 +1,56 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
requestLongerTimeout(2)
Cu.import("resource://gre/modules/osfile.jsm");
const { consoleApi: snippets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html";
let stubs = {
preparedMessages: [],
packets: [],
};
add_task(function* () {
for (var [key, {keys, code}] of snippets) {
yield OS.File.writeAtomic(TEMP_FILE_PATH, `function triggerPacket() {${code}}`);
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
let {ui} = toolbox.getCurrentPanel().hud;
ok(ui.jsterm, "jsterm exists");
ok(ui.newConsoleOutput, "newConsoleOutput exists");
let received = new Promise(resolve => {
let i = 0;
let listener = (type, res) => {
stubs.packets.push(formatPacket(keys[i], res));
stubs.preparedMessages.push(formatStub(keys[i], res));
if(++i === keys.length ){
toolbox.target.client.removeListener("consoleAPICall", listener);
resolve();
}
};
toolbox.target.client.addListener("consoleAPICall", listener);
});
yield ContentTask.spawn(gBrowser.selectedBrowser, key, function(key) {
var script = content.document.createElement("script");
script.src = "test-tempfile.js?key=" + encodeURIComponent(key);
script.onload = function() { content.wrappedJSObject.triggerPacket(); }
content.document.body.appendChild(script);
});
yield received;
yield closeTabAndToolbox();
}
let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "consoleApi.js");
OS.File.writeAtomic(filePath, formatFile(stubs));
OS.File.writeAtomic(TEMP_FILE_PATH, "");
});

View file

@ -0,0 +1,32 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
Cu.import("resource://gre/modules/osfile.jsm");
const TEST_URI = "data:text/html;charset=utf-8,stub generation";
const { evaluationResult: snippets} = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
let stubs = {
preparedMessages: [],
packets: [],
};
add_task(function* () {
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
ok(true, "make the test not fail");
for (var [code,key] of snippets) {
const packet = yield new Promise(resolve => {
toolbox.target.activeConsole.evaluateJS(code, resolve);
});
stubs.packets.push(formatPacket(key, packet));
stubs.preparedMessages.push(formatStub(key, packet));
}
let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "evaluationResult.js");
OS.File.writeAtomic(filePath, formatFile(stubs));
});

View file

@ -0,0 +1,47 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
Cu.import("resource://gre/modules/osfile.jsm");
const TARGET = "networkEvent";
const { [TARGET]: snippets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html";
let stubs = {
preparedMessages: [],
packets: [],
};
add_task(function* () {
for (var [key, {keys, code}] of snippets) {
OS.File.writeAtomic(TEMP_FILE_PATH, `function triggerPacket() {${code}}`);
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
let {ui} = toolbox.getCurrentPanel().hud;
ok(ui.jsterm, "jsterm exists");
ok(ui.newConsoleOutput, "newConsoleOutput exists");
let received = new Promise(resolve => {
let i = 0;
toolbox.target.client.addListener(TARGET, (type, res) => {
stubs.packets.push(formatPacket(keys[i], res));
stubs.preparedMessages.push(formatNetworkStub(keys[i], res));
if(++i === keys.length ){
resolve();
}
});
});
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
content.wrappedJSObject.triggerPacket();
});
yield received;
}
let filePath = OS.Path.join(`${BASE_PATH}/stubs/${TARGET}.js`);
OS.File.writeAtomic(filePath, formatFile(stubs));
OS.File.writeAtomic(TEMP_FILE_PATH, "");
});

View file

@ -0,0 +1,48 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
Cu.import("resource://gre/modules/osfile.jsm");
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html";
const { pageError: snippets} = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
let stubs = {
preparedMessages: [],
packets: [],
};
add_task(function* () {
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
ok(true, "make the test not fail");
for (var [key,code] of snippets) {
OS.File.writeAtomic(TEMP_FILE_PATH, `${code}`);
let received = new Promise(resolve => {
toolbox.target.client.addListener("pageError", function onPacket(e, packet) {
toolbox.target.client.removeListener("pageError", onPacket);
info("Received page error:" + e + " " + JSON.stringify(packet, null, "\t"));
let message = prepareMessage(packet, {getNextId: () => 1});
stubs.packets.push(formatPacket(message.messageText, packet));
stubs.preparedMessages.push(formatStub(message.messageText, packet));
resolve();
});
});
yield ContentTask.spawn(gBrowser.selectedBrowser, key, function(key) {
var script = content.document.createElement("script");
script.src = "test-tempfile.js?key=" + encodeURIComponent(key);
content.document.body.appendChild(script);
});
yield received;
}
let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "pageError.js");
OS.File.writeAtomic(filePath, formatFile(stubs));
OS.File.writeAtomic(TEMP_FILE_PATH, "");
});

View file

@ -0,0 +1,192 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/ */
/* import-globals-from ../../../../framework/test/shared-head.js */
"use strict";
// shared-head.js handles imports, constants, and utility functions
// Load the shared-head file first.
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
});
const { prepareMessage } = require("devtools/client/webconsole/new-console-output/utils/messages");
const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index.js");
const BASE_PATH = "../../../../devtools/client/webconsole/new-console-output/test/fixtures";
const TEMP_FILE_PATH = OS.Path.join(`${BASE_PATH}/stub-generators`, "test-tempfile.js");
let cachedPackets = {};
function getCleanedPacket(key, packet) {
if(Object.keys(cachedPackets).includes(key)) {
return cachedPackets[key];
}
// Strip escaped characters.
let safeKey = key
.replace(/\\n/g, "\n")
.replace(/\\r/g, "\r")
.replace(/\\\"/g, `\"`)
.replace(/\\\'/g, `\'`);
// If the stub already exist, we want to ignore irrelevant properties
// (actor, timeStamp, timer, ...) that might changed and "pollute"
// the diff resulting from this stub generation.
let res;
if(stubPackets.has(safeKey)) {
let existingPacket = stubPackets.get(safeKey);
res = Object.assign({}, packet, {
from: existingPacket.from
});
// Clean root timestamp.
if(res.timestamp) {
res.timestamp = existingPacket.timestamp;
}
if (res.message) {
// Clean timeStamp on the message prop.
res.message.timeStamp = existingPacket.message.timeStamp;
if (res.message.timer) {
// Clean timer properties on the message.
// Those properties are found on console.time and console.timeEnd calls,
// and those time can vary, which is why we need to clean them.
if (res.message.timer.started) {
res.message.timer.started = existingPacket.message.timer.started;
}
if (res.message.timer.duration) {
res.message.timer.duration = existingPacket.message.timer.duration;
}
}
if(Array.isArray(res.message.arguments)) {
// Clean actor ids on each message.arguments item.
res.message.arguments.forEach((argument, i) => {
if (argument && argument.actor) {
argument.actor = existingPacket.message.arguments[i].actor;
}
});
}
}
if (res.result) {
// Clean actor ids on evaluation result messages.
res.result.actor = existingPacket.result.actor;
if (res.result.preview) {
if(res.result.preview.timestamp) {
// Clean timestamp there too.
res.result.preview.timestamp = existingPacket.result.preview.timestamp;
}
}
}
if (res.exception) {
// Clean actor ids on exception messages.
res.exception.actor = existingPacket.exception.actor;
if (res.exception.preview) {
if(res.exception.preview.timestamp) {
// Clean timestamp there too.
res.exception.preview.timestamp = existingPacket.exception.preview.timestamp;
}
}
}
if (res.eventActor) {
// Clean actor ids, timeStamp and startedDateTime on network messages.
res.eventActor.actor = existingPacket.eventActor.actor;
res.eventActor.startedDateTime = existingPacket.eventActor.startedDateTime;
res.eventActor.timeStamp = existingPacket.eventActor.timeStamp;
}
if (res.pageError) {
// Clean timeStamp on pageError messages.
res.pageError.timeStamp = existingPacket.pageError.timeStamp;
}
} else {
res = packet;
}
cachedPackets[key] = res;
return res;
}
function formatPacket(key, packet) {
return `
stubPackets.set("${key}", ${JSON.stringify(getCleanedPacket(key, packet), null, "\t")});
`;
}
function formatStub(key, packet) {
let prepared = prepareMessage(
getCleanedPacket(key, packet),
{getNextId: () => "1"}
);
return `
stubPreparedMessages.set("${key}", new ConsoleMessage(${JSON.stringify(prepared, null, "\t")}));
`;
}
function formatNetworkStub(key, packet) {
let actor = packet.eventActor;
let networkInfo = {
_type: "NetworkEvent",
timeStamp: actor.timeStamp,
node: null,
actor: actor.actor,
discardRequestBody: true,
discardResponseBody: true,
startedDateTime: actor.startedDateTime,
request: {
url: actor.url,
method: actor.method,
},
isXHR: actor.isXHR,
cause: actor.cause,
response: {},
timings: {},
// track the list of network event updates
updates: [],
private: actor.private,
fromCache: actor.fromCache,
fromServiceWorker: actor.fromServiceWorker
};
let prepared = prepareMessage(networkInfo, {getNextId: () => "1"});
return `
stubPreparedMessages.set("${key}", new NetworkEventMessage(${JSON.stringify(prepared, null, "\t")}));
`;
}
function formatFile(stubs) {
return `/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
*/
const { ConsoleMessage, NetworkEventMessage } = require("devtools/client/webconsole/new-console-output/types");
let stubPreparedMessages = new Map();
let stubPackets = new Map();
${stubs.preparedMessages.join("")}
${stubs.packets.join("")}
module.exports = {
stubPreparedMessages,
stubPackets,
}`;
}

View file

@ -0,0 +1,8 @@
# vim: set filetype=python:
# 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/.
DevToolsModules(
'stub-snippets.js',
)

View file

@ -0,0 +1,148 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var {DebuggerServer} = require("devtools/server/main");
var longString = (new Array(DebuggerServer.LONG_STRING_LENGTH + 4)).join("a");
var initialString = longString.substring(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
// Console API
const consoleApiCommands = [
"console.log('foobar', 'test')",
"console.log(undefined)",
"console.warn('danger, will robinson!')",
"console.log(NaN)",
"console.log(null)",
"console.log('\u9f2c')",
"console.clear()",
"console.count('bar')",
"console.assert(false, {message: 'foobar'})",
"console.log('hello \\nfrom \\rthe \\\"string world!')",
"console.log('\xFA\u1E47\u0129\xE7\xF6d\xEA \u021B\u0115\u0219\u0165')",
"console.dirxml(window)",
];
let consoleApi = new Map(consoleApiCommands.map(
cmd => [cmd, {keys: [cmd], code: cmd}]));
consoleApi.set("console.trace()", {
keys: ["console.trace()"],
code: `
function testStacktraceFiltering() {
console.trace()
}
function foo() {
testStacktraceFiltering()
}
foo()
`});
consoleApi.set("console.time('bar')", {
keys: ["console.time('bar')", "console.timeEnd('bar')"],
code: `
console.time("bar");
console.timeEnd("bar");
`});
consoleApi.set("console.table('bar')", {
keys: ["console.table('bar')"],
code: `
console.table('bar');
`});
consoleApi.set("console.table(['a', 'b', 'c'])", {
keys: ["console.table(['a', 'b', 'c'])"],
code: `
console.table(['a', 'b', 'c']);
`});
consoleApi.set("console.group('bar')", {
keys: ["console.group('bar')", "console.groupEnd('bar')"],
code: `
console.group("bar");
console.groupEnd("bar");
`});
consoleApi.set("console.groupCollapsed('foo')", {
keys: ["console.groupCollapsed('foo')", "console.groupEnd('foo')"],
code: `
console.groupCollapsed("foo");
console.groupEnd("foo");
`});
consoleApi.set("console.group()", {
keys: ["console.group()", "console.groupEnd()"],
code: `
console.group();
console.groupEnd();
`});
consoleApi.set("console.log(%cfoobar)", {
keys: ["console.log(%cfoobar)"],
code: `
console.log(
"%cfoo%cbar",
"color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
"color:red;background:\\165rl('http://example.com/test')");
`});
// Evaluation Result
const evaluationResultCommands = [
"new Date(0)",
"asdf()",
"1 + @"
];
let evaluationResult = new Map(evaluationResultCommands.map(cmd => [cmd, cmd]));
// Network Event
let networkEvent = new Map();
networkEvent.set("GET request", {
keys: ["GET request"],
code: `
let i = document.createElement("img");
i.src = "inexistent.html";
`});
networkEvent.set("XHR GET request", {
keys: ["XHR GET request"],
code: `
const xhr = new XMLHttpRequest();
xhr.open("GET", "inexistent.html");
xhr.send();
`});
networkEvent.set("XHR POST request", {
keys: ["XHR POST request"],
code: `
const xhr = new XMLHttpRequest();
xhr.open("POST", "inexistent.html");
xhr.send();
`});
// Page Error
let pageError = new Map();
pageError.set("Reference Error", `
function bar() {
asdf()
}
function foo() {
bar()
}
foo()
`);
module.exports = {
consoleApi,
evaluationResult,
networkEvent,
pageError,
};

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Stub generator</title>
</head>
<body>
<p>Stub generator</p>
<script src="test-tempfile.js"></script>
</body>
</html>

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Stub generator for network event</title>
</head>
<body>
<p>Stub generator for network event</p>
<script src="test-tempfile.js"></script>
</body>
</html>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,182 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
*/
const { ConsoleMessage, NetworkEventMessage } = require("devtools/client/webconsole/new-console-output/types");
let stubPreparedMessages = new Map();
let stubPackets = new Map();
stubPreparedMessages.set("new Date(0)", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "javascript",
"type": "result",
"level": "log",
"parameters": {
"type": "object",
"actor": "server1.conn0.child1/obj30",
"class": "Date",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"timestamp": 0
}
},
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"type\":\"result\",\"level\":\"log\",\"parameters\":{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj30\",\"class\":\"Date\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"timestamp\":0}},\"repeatId\":null,\"stacktrace\":null,\"frame\":null,\"groupId\":null,\"userProvidedStyles\":null}",
"stacktrace": null,
"frame": null,
"groupId": null,
"userProvidedStyles": null
}));
stubPreparedMessages.set("asdf()", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "javascript",
"type": "result",
"level": "error",
"messageText": "ReferenceError: asdf is not defined",
"parameters": {
"type": "undefined"
},
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"type\":\"result\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":{\"type\":\"undefined\"},\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"debugger eval code\",\"line\":1,\"column\":1},\"groupId\":null,\"exceptionDocURL\":\"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default\",\"userProvidedStyles\":null}",
"stacktrace": null,
"frame": {
"source": "debugger eval code",
"line": 1,
"column": 1
},
"groupId": null,
"exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
"userProvidedStyles": null
}));
stubPreparedMessages.set("1 + @", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "javascript",
"type": "result",
"level": "error",
"messageText": "SyntaxError: illegal character",
"parameters": {
"type": "undefined"
},
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"type\":\"result\",\"level\":\"error\",\"messageText\":\"SyntaxError: illegal character\",\"parameters\":{\"type\":\"undefined\"},\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"debugger eval code\",\"line\":1,\"column\":4},\"groupId\":null,\"userProvidedStyles\":null}",
"stacktrace": null,
"frame": {
"source": "debugger eval code",
"line": 1,
"column": 4
},
"groupId": null,
"userProvidedStyles": null
}));
stubPackets.set("new Date(0)", {
"from": "server1.conn0.child1/consoleActor2",
"input": "new Date(0)",
"result": {
"type": "object",
"actor": "server1.conn0.child1/obj30",
"class": "Date",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"timestamp": 0
}
},
"timestamp": 1476573073424,
"exception": null,
"frame": null,
"helperResult": null
});
stubPackets.set("asdf()", {
"from": "server1.conn0.child1/consoleActor2",
"input": "asdf()",
"result": {
"type": "undefined"
},
"timestamp": 1476573073442,
"exception": {
"type": "object",
"actor": "server1.conn0.child1/obj32",
"class": "Error",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 4,
"preview": {
"kind": "Error",
"name": "ReferenceError",
"message": "asdf is not defined",
"stack": "@debugger eval code:1:1\n",
"fileName": "debugger eval code",
"lineNumber": 1,
"columnNumber": 1
}
},
"exceptionMessage": "ReferenceError: asdf is not defined",
"exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
"frame": {
"source": "debugger eval code",
"line": 1,
"column": 1
},
"helperResult": null
});
stubPackets.set("1 + @", {
"from": "server1.conn0.child1/consoleActor2",
"input": "1 + @",
"result": {
"type": "undefined"
},
"timestamp": 1478755616654,
"exception": {
"type": "object",
"actor": "server1.conn0.child1/obj33",
"class": "Error",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 4,
"preview": {
"kind": "Error",
"name": "SyntaxError",
"message": "illegal character",
"stack": "",
"fileName": "debugger eval code",
"lineNumber": 1,
"columnNumber": 4
}
},
"exceptionMessage": "SyntaxError: illegal character",
"frame": {
"source": "debugger eval code",
"line": 1,
"column": 4
},
"helperResult": null
});
module.exports = {
stubPreparedMessages,
stubPackets,
}

View file

@ -0,0 +1,29 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let maps = [];
[
"consoleApi",
"evaluationResult",
"networkEvent",
"pageError",
].forEach((filename) => {
maps[filename] = require(`./${filename}`);
});
// Combine all the maps into a single map.
module.exports = {
stubPreparedMessages: new Map([
...maps.consoleApi.stubPreparedMessages,
...maps.evaluationResult.stubPreparedMessages,
...maps.networkEvent.stubPreparedMessages,
...maps.pageError.stubPreparedMessages, ]),
stubPackets: new Map([
...maps.consoleApi.stubPackets,
...maps.evaluationResult.stubPackets,
...maps.networkEvent.stubPackets,
...maps.pageError.stubPackets, ]),
};

View file

@ -0,0 +1,11 @@
# 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/.
DevToolsModules(
'consoleApi.js',
'evaluationResult.js',
'index.js',
'networkEvent.js',
'pageError.js',
)

View file

@ -0,0 +1,189 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
*/
const { ConsoleMessage, NetworkEventMessage } = require("devtools/client/webconsole/new-console-output/types");
let stubPreparedMessages = new Map();
let stubPackets = new Map();
stubPreparedMessages.set("GET request", new NetworkEventMessage({
"id": "1",
"actor": "server1.conn0.child1/netEvent29",
"level": "log",
"isXHR": false,
"request": {
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"method": "GET"
},
"response": {},
"source": "network",
"type": "log",
"groupId": null
}));
stubPreparedMessages.set("XHR GET request", new NetworkEventMessage({
"id": "1",
"actor": "server1.conn1.child1/netEvent29",
"level": "log",
"isXHR": true,
"request": {
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"method": "GET"
},
"response": {},
"source": "network",
"type": "log",
"groupId": null
}));
stubPreparedMessages.set("XHR POST request", new NetworkEventMessage({
"id": "1",
"actor": "server1.conn2.child1/netEvent29",
"level": "log",
"isXHR": true,
"request": {
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"method": "POST"
},
"response": {},
"source": "network",
"type": "log",
"groupId": null
}));
stubPackets.set("GET request", {
"from": "server1.conn0.child1/consoleActor2",
"type": "networkEvent",
"eventActor": {
"actor": "server1.conn0.child1/netEvent29",
"startedDateTime": "2016-10-15T23:12:04.196Z",
"timeStamp": 1476573124196,
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"method": "GET",
"isXHR": false,
"cause": {
"type": 3,
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
"stacktrace": [
{
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"lineNumber": 3,
"columnNumber": 1,
"functionName": "triggerPacket",
"asyncCause": null
},
{
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
"lineNumber": 4,
"columnNumber": 7,
"functionName": null,
"asyncCause": null
},
{
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
"lineNumber": 53,
"columnNumber": 20,
"functionName": null,
"asyncCause": null
}
]
},
"private": false
}
});
stubPackets.set("XHR GET request", {
"from": "server1.conn1.child1/consoleActor2",
"type": "networkEvent",
"eventActor": {
"actor": "server1.conn1.child1/netEvent29",
"startedDateTime": "2016-10-15T23:12:05.690Z",
"timeStamp": 1476573125690,
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"method": "GET",
"isXHR": true,
"cause": {
"type": 11,
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
"stacktrace": [
{
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"lineNumber": 4,
"columnNumber": 1,
"functionName": "triggerPacket",
"asyncCause": null
},
{
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
"lineNumber": 4,
"columnNumber": 7,
"functionName": null,
"asyncCause": null
},
{
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
"lineNumber": 53,
"columnNumber": 20,
"functionName": null,
"asyncCause": null
}
]
},
"private": false
}
});
stubPackets.set("XHR POST request", {
"from": "server1.conn2.child1/consoleActor2",
"type": "networkEvent",
"eventActor": {
"actor": "server1.conn2.child1/netEvent29",
"startedDateTime": "2016-10-15T23:12:07.158Z",
"timeStamp": 1476573127158,
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"method": "POST",
"isXHR": true,
"cause": {
"type": 11,
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
"stacktrace": [
{
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"lineNumber": 4,
"columnNumber": 1,
"functionName": "triggerPacket",
"asyncCause": null
},
{
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
"lineNumber": 4,
"columnNumber": 7,
"functionName": null,
"asyncCause": null
},
{
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
"lineNumber": 53,
"columnNumber": 20,
"functionName": null,
"asyncCause": null
}
]
},
"private": false
}
});
module.exports = {
stubPreparedMessages,
stubPackets,
}

View file

@ -0,0 +1,102 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
*/
const { ConsoleMessage, NetworkEventMessage } = require("devtools/client/webconsole/new-console-output/types");
let stubPreparedMessages = new Map();
let stubPackets = new Map();
stubPreparedMessages.set("ReferenceError: asdf is not defined", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "javascript",
"type": "log",
"level": "error",
"messageText": "ReferenceError: asdf is not defined",
"parameters": null,
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"type\":\"log\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"lineNumber\":3,\"columnNumber\":5,\"functionName\":\"bar\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"lineNumber\":6,\"columnNumber\":5,\"functionName\":\"foo\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"lineNumber\":9,\"columnNumber\":3,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"line\":3,\"column\":5},\"groupId\":null,\"exceptionDocURL\":\"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default\"}",
"stacktrace": [
{
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
"lineNumber": 3,
"columnNumber": 5,
"functionName": "bar"
},
{
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
"lineNumber": 6,
"columnNumber": 5,
"functionName": "foo"
},
{
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
"lineNumber": 9,
"columnNumber": 3,
"functionName": null
}
],
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
"line": 3,
"column": 5
},
"groupId": null,
"exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default"
}));
stubPackets.set("ReferenceError: asdf is not defined", {
"from": "server1.conn0.child1/consoleActor2",
"type": "pageError",
"pageError": {
"errorMessage": "ReferenceError: asdf is not defined",
"errorMessageName": "JSMSG_NOT_DEFINED",
"exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
"sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
"lineText": "",
"lineNumber": 3,
"columnNumber": 5,
"category": "content javascript",
"timeStamp": 1476573167137,
"warning": false,
"error": false,
"exception": true,
"strict": false,
"info": false,
"private": false,
"stacktrace": [
{
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
"lineNumber": 3,
"columnNumber": 5,
"functionName": "bar"
},
{
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
"lineNumber": 6,
"columnNumber": 5,
"functionName": "foo"
},
{
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
"lineNumber": 9,
"columnNumber": 3,
"functionName": null
}
]
}
});
module.exports = {
stubPreparedMessages,
stubPackets,
}

View file

@ -0,0 +1,67 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let ReactDOM = require("devtools/client/shared/vendor/react-dom");
let React = require("devtools/client/shared/vendor/react");
var TestUtils = React.addons.TestUtils;
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const { configureStore } = require("devtools/client/webconsole/new-console-output/store");
const { IdGenerator } = require("devtools/client/webconsole/new-console-output/utils/id-generator");
const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
/**
* Prepare actions for use in testing.
*/
function setupActions() {
// Some actions use dependency injection. This helps them avoid using state in
// a hard-to-test way. We need to inject stubbed versions of these dependencies.
const wrappedActions = Object.assign({}, actions);
const idGenerator = new IdGenerator();
wrappedActions.messageAdd = (packet) => {
return actions.messageAdd(packet, idGenerator);
};
return wrappedActions;
}
/**
* Prepare the store for use in testing.
*/
function setupStore(input) {
const store = configureStore();
// Add the messages from the input commands to the store.
input.forEach((cmd) => {
store.dispatch(actions.messageAdd(stubPackets.get(cmd)));
});
return store;
}
function renderComponent(component, props) {
const el = React.createElement(component, props, {});
// By default, renderIntoDocument() won't work for stateless components, but
// it will work if the stateless component is wrapped in a stateful one.
// See https://github.com/facebook/react/issues/4839
const wrappedEl = React.DOM.span({}, [el]);
const renderedComponent = TestUtils.renderIntoDocument(wrappedEl);
return ReactDOM.findDOMNode(renderedComponent).children[0];
}
function shallowRenderComponent(component, props) {
const el = React.createElement(component, props);
const renderer = TestUtils.createRenderer();
renderer.render(el, {});
return renderer.getRenderOutput();
}
module.exports = {
setupActions,
setupStore,
renderComponent,
shallowRenderComponent
};

View file

@ -0,0 +1,21 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js
test-batching.html
test-console.html
test-console-filters.html
test-console-group.html
test-console-table.html
!/devtools/client/framework/test/shared-head.js
[browser_webconsole_batching.js]
[browser_webconsole_console_group.js]
[browser_webconsole_console_table.js]
[browser_webconsole_filters.js]
[browser_webconsole_init.js]
[browser_webconsole_input_focus.js]
[browser_webconsole_keyboard_accessibility.js]
[browser_webconsole_observer_notifications.js]
[browser_webconsole_vview_close_on_esc_key.js]

View file

@ -0,0 +1,51 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
// Check adding console calls as batch keep the order of the message.
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-batching.html";
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
const messageNumber = 100;
yield testSimpleBatchLogging(hud, messageNumber);
yield testBatchLoggingAndClear(hud, messageNumber);
});
function* testSimpleBatchLogging(hud, messageNumber) {
yield ContentTask.spawn(gBrowser.selectedBrowser, messageNumber,
function (numMessages) {
content.wrappedJSObject.batchLog(numMessages);
}
);
for (let i = 0; i < messageNumber; i++) {
let node = yield waitFor(() => findMessageAtIndex(hud, i, i));
is(node.textContent, i.toString(), `message at index "${i}" is the expected one`);
}
}
function* testBatchLoggingAndClear(hud, messageNumber) {
yield ContentTask.spawn(gBrowser.selectedBrowser, messageNumber,
function (numMessages) {
content.wrappedJSObject.batchLogAndClear(numMessages);
}
);
yield waitFor(() => findMessage(hud, l10n.getStr("consoleCleared")));
ok(true, "console cleared message is displayed");
// Passing the text argument as an empty string will returns all the message,
// whatever their content is.
const messages = findMessages(hud, "");
is(messages.length, 1, "console was cleared as expected");
}
function findMessageAtIndex(hud, text, index) {
const selector = `.message:nth-of-type(${index + 1}) .message-body`;
return findMessage(hud, text, selector);
}

View file

@ -0,0 +1,91 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
// Check console.group, console.groupCollapsed and console.groupEnd calls
// behave as expected.
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console-group.html";
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
add_task(function* () {
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
let hud = toolbox.getCurrentPanel().hud;
const store = hud.ui.newConsoleOutput.getStore();
// Adding loggin each time the store is modified in order to check
// the store state in case of failure.
store.subscribe(() => {
const messages = store.getState().messages.messagesById.toJS()
.map(message => {
return {
id: message.id,
type: message.type,
parameters: message.parameters,
messageText: message.messageText
};
}
);
info("messages : " + JSON.stringify(messages));
});
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function () {
content.wrappedJSObject.doLog();
});
info("Test a group at root level");
let node = yield waitFor(() => findMessage(hud, "group-1"));
testClass(node, "startGroup");
testIndent(node, 0);
info("Test a message in a 1 level deep group");
node = yield waitFor(() => findMessage(hud, "log-1"));
testClass(node, "log");
testIndent(node, 1);
info("Test a group in a 1 level deep group");
node = yield waitFor(() => findMessage(hud, "group-2"));
testClass(node, "startGroup");
testIndent(node, 1);
info("Test a message in a 2 level deep group");
node = yield waitFor(() => findMessage(hud, "log-2"));
testClass(node, "log");
testIndent(node, 2);
info("Test a message in a 1 level deep group, after closing a 2 level deep group");
node = yield waitFor(() => findMessage(hud, "log-3"));
testClass(node, "log");
testIndent(node, 1);
info("Test a message at root level, after closing all the groups");
node = yield waitFor(() => findMessage(hud, "log-4"));
testClass(node, "log");
testIndent(node, 0);
info("Test a collapsed group at root level");
node = yield waitFor(() => findMessage(hud, "group-3"));
testClass(node, "startGroupCollapsed");
testIndent(node, 0);
info("Test a message at root level, after closing a collapsed group");
node = yield waitFor(() => findMessage(hud, "log-6"));
testClass(node, "log");
testIndent(node, 0);
let nodes = hud.ui.experimentalOutputNode.querySelectorAll(".message");
is(nodes.length, 8, "expected number of messages are displayed");
});
function testClass(node, className) {
ok(node.classList.contains(className), `message has the expected "${className}" class`);
}
function testIndent(node, indent) {
indent = `${indent * INDENT_WIDTH}px`;
is(node.querySelector(".indent").style.width, indent,
"message has the expected level of indentation");
}

View file

@ -0,0 +1,173 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
// Check console.table calls with all the test cases shown
// in the MDN doc (https://developer.mozilla.org/en-US/docs/Web/API/Console/table)
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console-table.html";
add_task(function* () {
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
let hud = toolbox.getCurrentPanel().hud;
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const testCases = [{
info: "Testing when data argument is an array",
input: ["apples", "oranges", "bananas"],
expected: {
columns: ["(index)", "Values"],
rows: [
["0", "apples"],
["1", "oranges"],
["2", "bananas"],
]
}
}, {
info: "Testing when data argument is an object",
input: new Person("John", "Smith"),
expected: {
columns: ["(index)", "Values"],
rows: [
["firstName", "John"],
["lastName", "Smith"],
]
}
}, {
info: "Testing when data argument is an array of arrays",
input: [["Jane", "Doe"], ["Emily", "Jones"]],
expected: {
columns: ["(index)", "0", "1"],
rows: [
["0", "Jane", "Doe"],
["1", "Emily", "Jones"],
]
}
}, {
info: "Testing when data argument is an array of objects",
input: [
new Person("Jack", "Foo"),
new Person("Emma", "Bar"),
new Person("Michelle", "Rax"),
],
expected: {
columns: ["(index)", "firstName", "lastName"],
rows: [
["0", "Jack", "Foo"],
["1", "Emma", "Bar"],
["2", "Michelle", "Rax"],
]
}
}, {
info: "Testing when data argument is an object whose properties are objects",
input: {
father: new Person("Darth", "Vader"),
daughter: new Person("Leia", "Organa"),
son: new Person("Luke", "Skywalker"),
},
expected: {
columns: ["(index)", "firstName", "lastName"],
rows: [
["father", "Darth", "Vader"],
["daughter", "Leia", "Organa"],
["son", "Luke", "Skywalker"],
]
}
}, {
info: "Testing when data argument is a Set",
input: new Set(["a", "b", "c"]),
expected: {
columns: ["(iteration index)", "Values"],
rows: [
["0", "a"],
["1", "b"],
["2", "c"],
]
}
}, {
info: "Testing when data argument is a Map",
input: new Map([["key-a", "value-a"], ["key-b", "value-b"]]),
expected: {
columns: ["(iteration index)", "Key", "Values"],
rows: [
["0", "key-a", "value-a"],
["1", "key-b", "value-b"],
]
}
}, {
info: "Testing restricting the columns displayed",
input: [
new Person("Sam", "Wright"),
new Person("Elena", "Bartz"),
],
headers: ["firstName"],
expected: {
columns: ["(index)", "firstName"],
rows: [
["0", "Sam"],
["1", "Elena"],
]
}
}];
yield ContentTask.spawn(gBrowser.selectedBrowser, testCases, function (tests) {
tests.forEach((test) => {
content.wrappedJSObject.doConsoleTable(test.input, test.headers);
});
});
let nodes = [];
for (let testCase of testCases) {
let node = yield waitFor(
() => findConsoleTable(hud.ui.experimentalOutputNode, testCases.indexOf(testCase))
);
nodes.push(node);
}
let consoleTableNodes = hud.ui.experimentalOutputNode.querySelectorAll(
".message .new-consoletable");
is(consoleTableNodes.length, testCases.length,
"console has the expected number of consoleTable items");
testCases.forEach((testCase, index) => {
info(testCase.info);
let node = nodes[index];
let columns = Array.from(node.querySelectorAll("thead th"));
let rows = Array.from(node.querySelectorAll("tbody tr"));
is(
JSON.stringify(testCase.expected.columns),
JSON.stringify(columns.map(column => column.textContent)),
"table has the expected columns"
);
is(testCase.expected.rows.length, rows.length,
"table has the expected number of rows");
testCase.expected.rows.forEach((expectedRow, rowIndex) => {
let row = rows[rowIndex];
let cells = row.querySelectorAll("td");
is(expectedRow.length, cells.length, "row has the expected number of cells");
expectedRow.forEach((expectedCell, cellIndex) => {
let cell = cells[cellIndex];
is(expectedCell, cell.textContent, "cell has the expected content");
});
});
});
});
function findConsoleTable(node, index) {
let condition = node.querySelector(
`.message:nth-of-type(${index + 1}) .new-consoletable`);
return condition;
}

View file

@ -0,0 +1,72 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/ */
// Tests filters.
"use strict";
const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console-filters.html";
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
const outputNode = hud.ui.experimentalOutputNode;
const toolbar = yield waitFor(() => {
return outputNode.querySelector(".webconsole-filterbar-primary");
});
ok(toolbar, "Toolbar found");
// Show the filter bar
toolbar.querySelector(".devtools-filter-icon").click();
const filterBar = yield waitFor(() => {
return outputNode.querySelector(".webconsole-filterbar-secondary");
});
ok(filterBar, "Filter bar is shown when filter icon is clicked.");
// Check defaults.
Object.values(MESSAGE_LEVEL).forEach(level => {
ok(filterIsEnabled(filterBar.querySelector(`.${level}`)),
`Filter button for ${level} is on by default`);
});
["net", "netxhr"].forEach(category => {
ok(!filterIsEnabled(filterBar.querySelector(`.${category}`)),
`Filter button for ${category} is off by default`);
});
// Check that messages are shown as expected. This depends on cached messages being
// shown.
ok(findMessages(hud, "").length == 5,
"Messages of all levels shown when filters are on.");
// Check that messages are not shown when their filter is turned off.
filterBar.querySelector(".error").click();
yield waitFor(() => findMessages(hud, "").length == 4);
ok(true, "When a filter is turned off, its messages are not shown.");
// Check that the ui settings were persisted.
yield closeTabAndToolbox();
yield testFilterPersistence();
});
function filterIsEnabled(button) {
return button.classList.contains("checked");
}
function* testFilterPersistence() {
let hud = yield openNewTabAndConsole(TEST_URI);
const outputNode = hud.ui.experimentalOutputNode;
const filterBar = yield waitFor(() => {
return outputNode.querySelector(".webconsole-filterbar-secondary");
});
ok(filterBar, "Filter bar ui setting is persisted.");
// Check that the filter settings were persisted.
ok(!filterIsEnabled(filterBar.querySelector(".error")),
"Filter button setting is persisted");
ok(findMessages(hud, "").length == 4,
"Messages of all levels shown when filters are on.");
}

View file

@ -0,0 +1,35 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console.html";
add_task(function* () {
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
let hud = toolbox.getCurrentPanel().hud;
let {ui} = hud;
ok(ui.jsterm, "jsterm exists");
ok(ui.newConsoleOutput, "newConsoleOutput exists");
// @TODO: fix proptype errors
let receievedMessages = waitForMessages({
hud,
messages: [{
text: '0',
}, {
text: '1',
}, {
text: '2',
}],
});
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
content.wrappedJSObject.doLogs(3);
});
yield receievedMessages;
});

View file

@ -0,0 +1,57 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/ */
// Tests that the input field is focused when the console is opened.
"use strict";
const TEST_URI =
`data:text/html;charset=utf-8,Test input focused
<script>
console.log("console message 1");
</script>`;
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
hud.jsterm.clearOutput();
let inputNode = hud.jsterm.inputNode;
ok(inputNode.getAttribute("focused"), "input node is focused after output is cleared");
ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
content.wrappedJSObject.console.log("console message 2");
});
let msg = yield waitFor(() => findMessage(hud, "console message 2"));
let outputItem = msg.querySelector(".message-body");
inputNode = hud.jsterm.inputNode;
ok(inputNode.getAttribute("focused"), "input node is focused, first");
yield waitForBlurredInput(inputNode);
EventUtils.sendMouseEvent({type: "click"}, hud.outputNode);
ok(inputNode.getAttribute("focused"), "input node is focused, second time");
yield waitForBlurredInput(inputNode);
info("Setting a text selection and making sure a click does not re-focus");
let selection = hud.iframeWindow.getSelection();
selection.selectAllChildren(outputItem);
EventUtils.sendMouseEvent({type: "click"}, hud.outputNode);
ok(!inputNode.getAttribute("focused"),
"input node focused after text is selected");
});
function waitForBlurredInput(inputNode) {
return new Promise(resolve => {
let lostFocus = () => {
ok(!inputNode.getAttribute("focused"), "input node is not focused");
resolve();
};
inputNode.addEventListener("blur", lostFocus, { once: true });
document.getElementById("urlbar").click();
});
}

View file

@ -0,0 +1,71 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/ */
// Check that basic keyboard shortcuts work in the web console.
"use strict";
const TEST_URI =
`data:text/html;charset=utf-8,<p>Test keyboard accessibility</p>
<script>
for (let i = 1; i <= 100; i++) {
console.log("console message " + i);
}
</script>
`;
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
info("Web Console opened");
const outputScroller = hud.ui.outputScroller;
yield waitFor(() => findMessages(hud, "").length == 100);
let currentPosition = outputScroller.scrollTop;
const bottom = currentPosition;
EventUtils.sendMouseEvent({type: "click"}, hud.jsterm.inputNode);
// Page up.
EventUtils.synthesizeKey("VK_PAGE_UP", {});
isnot(outputScroller.scrollTop, currentPosition,
"scroll position changed after page up");
// Page down.
currentPosition = outputScroller.scrollTop;
EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
ok(outputScroller.scrollTop > currentPosition,
"scroll position now at bottom");
// Home
EventUtils.synthesizeKey("VK_HOME", {});
is(outputScroller.scrollTop, 0, "scroll position now at top");
// End
EventUtils.synthesizeKey("VK_END", {});
let scrollTop = outputScroller.scrollTop;
ok(scrollTop > 0 && Math.abs(scrollTop - bottom) <= 5,
"scroll position now at bottom");
// Clear output
info("try ctrl-l to clear output");
let clearShortcut;
if (Services.appinfo.OS === "Darwin") {
clearShortcut = WCUL10n.getStr("webconsole.clear.keyOSX");
} else {
clearShortcut = WCUL10n.getStr("webconsole.clear.key");
}
synthesizeKeyShortcut(clearShortcut);
yield waitFor(() => findMessages(hud, "").length == 0);
is(hud.jsterm.inputNode.getAttribute("focused"), "true", "jsterm input is focused");
// Focus filter
info("try ctrl-f to focus filter");
synthesizeKeyShortcut(WCUL10n.getStr("webconsole.find.key"));
ok(!hud.jsterm.inputNode.getAttribute("focused"), "jsterm input is not focused");
is(hud.ui.filterBox, outputScroller.ownerDocument.activeElement,
"filter input is focused");
});

View file

@ -0,0 +1,47 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
const TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for " +
"obeserver notifications";
let created = false;
let destroyed = false;
add_task(function* () {
setupObserver();
yield openNewTabAndConsole(TEST_URI);
yield waitFor(() => created);
yield closeTabAndToolbox(gBrowser.selectedTab);
yield waitFor(() => destroyed);
});
function setupObserver() {
const observer = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
observe: function observe(subject, topic) {
subject = subject.QueryInterface(Ci.nsISupportsString);
switch (topic) {
case "web-console-created":
ok(HUDService.getHudReferenceById(subject.data), "We have a hud reference");
Services.obs.removeObserver(observer, "web-console-created");
created = true;
break;
case "web-console-destroyed":
ok(!HUDService.getHudReferenceById(subject.data), "We do not have a hud reference");
Services.obs.removeObserver(observer, "web-console-destroyed");
destroyed = true;
break;
}
},
};
Services.obs.addObserver(observer, "web-console-created", false);
Services.obs.addObserver(observer, "web-console-destroyed", false);
}

View file

@ -0,0 +1,46 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/ */
// Check that the variables view sidebar can be closed by pressing Escape in the
// web console.
"use strict";
const TEST_URI =
"data:text/html;charset=utf8,<script>let fooObj = {testProp: 'testValue'}</script>";
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
let jsterm = hud.jsterm;
let vview;
yield openSidebar("fooObj", 'testProp: "testValue"');
vview.window.focus();
let sidebarClosed = jsterm.once("sidebar-closed");
EventUtils.synthesizeKey("VK_ESCAPE", {});
yield sidebarClosed;
function* openSidebar(objName, expectedText) {
yield jsterm.execute(objName);
info("JSTerm executed");
let msg = yield waitFor(() => findMessage(hud, "Object"));
ok(msg, "Message found");
let anchor = msg.querySelector("a");
let body = msg.querySelector(".message-body");
ok(anchor, "object anchor");
ok(body, "message body");
ok(body.textContent.includes(expectedText), "message text check");
msg.scrollIntoView();
yield EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
let vviewVar = yield jsterm.once("variablesview-fetched");
vview = vviewVar._variablesView;
ok(vview, "variables view object exists");
}
});

View file

@ -0,0 +1,137 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/ */
/* import-globals-from ../../../../framework/test/shared-head.js */
"use strict";
// shared-head.js handles imports, constants, and utility functions
// Load the shared-head file first.
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
var {Utils: WebConsoleUtils} = require("devtools/client/webconsole/utils");
const WEBCONSOLE_STRINGS_URI = "devtools/client/locales/webconsole.properties";
var WCUL10n = new WebConsoleUtils.L10n(WEBCONSOLE_STRINGS_URI);
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
registerCleanupFunction(function* () {
Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
let browserConsole = HUDService.getBrowserConsole();
if (browserConsole) {
if (browserConsole.jsterm) {
browserConsole.jsterm.clearOutput(true);
}
yield HUDService.toggleBrowserConsole();
}
});
/**
* Add a new tab and open the toolbox in it, and select the webconsole.
*
* @param string url
* The URL for the tab to be opened.
* @return Promise
* Resolves when the tab has been added, loaded and the toolbox has been opened.
* Resolves to the toolbox.
*/
var openNewTabAndConsole = Task.async(function* (url) {
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
let hud = toolbox.getCurrentPanel().hud;
hud.jsterm._lazyVariablesView = false;
return hud;
});
/**
* Wait for messages in the web console output, resolving once they are receieved.
*
* @param object options
* - hud: the webconsole
* - messages: Array[Object]. An array of messages to match. Current supported options:
* - text: Exact text match in .message-body
*/
function waitForMessages({ hud, messages }) {
return new Promise(resolve => {
let numMatched = 0;
let receivedLog = hud.ui.on("new-messages", function messagesReceieved(e, newMessages) {
for (let message of messages) {
if (message.matched) {
continue;
}
for (let newMessage of newMessages) {
if (newMessage.node.querySelector(".message-body").textContent == message.text) {
numMatched++;
message.matched = true;
info("Matched a message with text: " + message.text + ", still waiting for " + (messages.length - numMatched) + " messages");
break;
}
}
if (numMatched === messages.length) {
hud.ui.off("new-messages", messagesReceieved);
resolve(receivedLog);
return;
}
}
});
});
}
/**
* Wait for a predicate to return a result.
*
* @param function condition
* Invoked once in a while until it returns a truthy value. This should be an
* idempotent function, since we have to run it a second time after it returns
* true in order to return the value.
* @param string message [optional]
* A message to output if the condition failes.
* @param number interval [optional]
* How often the predicate is invoked, in milliseconds.
* @return object
* A promise that is resolved with the result of the condition.
*/
function* waitFor(condition, message = "waitFor", interval = 10, maxTries = 500) {
return new Promise(resolve => {
BrowserTestUtils.waitForCondition(condition, message, interval, maxTries)
.then(() => resolve(condition()));
});
}
/**
* Find a message in the output.
*
* @param object hud
* The web console.
* @param string text
* A substring that can be found in the message.
* @param selector [optional]
* The selector to use in finding the message.
*/
function findMessage(hud, text, selector = ".message") {
const elements = findMessages(hud, text, selector);
return elements.pop();
}
/**
* Find multiple messages in the output.
*
* @param object hud
* The web console.
* @param string text
* A substring that can be found in the message.
* @param selector [optional]
* The selector to use in finding the message.
*/
function findMessages(hud, text, selector = ".message") {
const messages = hud.ui.experimentalOutputNode.querySelectorAll(selector);
const elements = Array.prototype.filter.call(
messages,
(el) => el.textContent.includes(text)
);
return elements;
}

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webconsole batch console calls test page</title>
</head>
<body>
<p>batch console calls test page</p>
<script>
"use strict";
function batchLog(numMessages = 0) {
for (let i = 0; i < numMessages; i++) {
console.log(i);
}
}
function batchLogAndClear(numMessages = 0) {
for (let i = 0; i < numMessages; i++) {
console.log(i);
if (i === numMessages - 1) {
console.clear();
}
}
}
</script>
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webconsole filters test page</title>
</head>
<body>
<p>Webconsole filters test page</p>
<script>
console.log("console log");
console.warn("console warn");
console.error("console error");
console.info("console info");
console.count("console debug");
</script>
</body>
</html>

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webconsole console.group test page</title>
</head>
<body>
<p>console.group() & console.groupCollapsed() test page</p>
<script>
"use strict";
function doLog() {
console.group("group-1");
console.log("log-1");
console.group("group-2");
console.log("log-2");
console.groupEnd("group-2");
console.log("log-3");
console.groupEnd("group-1");
console.log("log-4");
console.groupCollapsed("group-3");
console.log("log-5");
console.groupEnd("group-3");
console.log("log-6");
}
</script>
</body>
</html>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Simple webconsole test page</title>
</head>
<body>
<p>console.table() test page</p>
<script>
function doConsoleTable(data, constrainedHeaders = null) {
if (constrainedHeaders) {
console.table(data, constrainedHeaders);
} else {
console.table(data);
}
}
</script>
</body>
</html>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Simple webconsole test page</title>
</head>
<body>
<p>Simple webconsole test page</p>
<script>
function doLogs(num) {
num = num || 1;
for (var i = 0; i < num; i++) {
console.log(i);
}
}
</script>
</body>
</html>

View file

@ -0,0 +1,17 @@
# vim: set filetype=python:
# 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/.
BROWSER_CHROME_MANIFESTS += [
'fixtures/stub-generators/browser.ini',
'mochitest/browser.ini',
]
DIRS += [
'fixtures'
]
MOCHITEST_CHROME_MANIFESTS += [
'chrome/chrome.ini',
]

View file

@ -0,0 +1,38 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const requireHacker = require("require-hacker");
requireHacker.global_hook("default", path => {
switch (path) {
// For Enzyme
case "react-dom/server":
return `const React = require('react-dev'); module.exports = React`;
case "react-addons-test-utils":
return `const React = require('react-dev'); module.exports = React.addons.TestUtils`;
// Use react-dev. This would be handled by browserLoader in Firefox.
case "react":
case "devtools/client/shared/vendor/react":
return `const React = require('react-dev'); module.exports = React`;
// For Rep's use of AMD
case "devtools/client/shared/vendor/react.default":
return `const React = require('react-dev'); module.exports = React`;
}
// Some modules depend on Chrome APIs which don't work in mocha. When such a module
// is required, replace it with a mock version.
switch (path) {
case "devtools/client/webconsole/utils":
return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/WebConsoleUtils")`;
case "devtools/shared/l10n":
return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/LocalizationHelper")`;
case "devtools/shared/plural-form":
return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/PluralForm")`;
case "Services":
case "Services.default":
return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/Services")`;
case "devtools/shared/client/main":
return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/ObjectClient")`;
}
});

View file

@ -0,0 +1,215 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const expect = require("expect");
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const { messageAdd } = require("devtools/client/webconsole/new-console-output/actions/index");
const { ConsoleCommand } = require("devtools/client/webconsole/new-console-output/types");
const { getAllMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
describe("Filtering", () => {
let store;
let numMessages;
// Number of messages in prepareBaseStore which are not filtered out, i.e. Evaluation
// Results, console commands and console.groups .
const numUnfilterableMessages = 3;
beforeEach(() => {
store = prepareBaseStore();
store.dispatch(actions.filtersClear());
numMessages = getAllMessages(store.getState()).size;
});
describe("Level filter", () => {
it("filters log messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.LOG));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages - 3);
});
it("filters debug messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.DEBUG));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages - 1);
});
// @TODO add info stub
it("filters info messages");
it("filters warning messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.WARN));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages - 1);
});
it("filters error messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages - 1);
});
it("filters xhr messages", () => {
let message = stubPreparedMessages.get("XHR GET request");
store.dispatch(messageAdd(message));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages);
store.dispatch(actions.filterToggle("netxhr"));
messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages + 1);
});
it("filters network messages", () => {
let message = stubPreparedMessages.get("GET request");
store.dispatch(messageAdd(message));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages);
store.dispatch(actions.filterToggle("net"));
messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages + 1);
});
});
describe("Text filter", () => {
it("matches on value grips", () => {
store.dispatch(actions.filterTextSet("danger"));
let messages = getAllMessages(store.getState());
expect(messages.size - numUnfilterableMessages).toEqual(1);
});
it("matches unicode values", () => {
store.dispatch(actions.filterTextSet("鼬"));
let messages = getAllMessages(store.getState());
expect(messages.size - numUnfilterableMessages).toEqual(1);
});
it("matches locations", () => {
// Add a message with a different filename.
let locationMsg =
Object.assign({}, stubPackets.get("console.log('foobar', 'test')"));
locationMsg.message =
Object.assign({}, locationMsg.message, { filename: "search-location-test.js" });
store.dispatch(messageAdd(locationMsg));
store.dispatch(actions.filterTextSet("search-location-test.js"));
let messages = getAllMessages(store.getState());
expect(messages.size - numUnfilterableMessages).toEqual(1);
});
it("matches stacktrace functionName", () => {
let traceMessage = stubPackets.get("console.trace()");
store.dispatch(messageAdd(traceMessage));
store.dispatch(actions.filterTextSet("testStacktraceFiltering"));
let messages = getAllMessages(store.getState());
expect(messages.size - numUnfilterableMessages).toEqual(1);
});
it("matches stacktrace location", () => {
let traceMessage = stubPackets.get("console.trace()");
traceMessage.message =
Object.assign({}, traceMessage.message, {
filename: "search-location-test.js",
lineNumber: 85,
columnNumber: 13
});
store.dispatch(messageAdd(traceMessage));
store.dispatch(actions.filterTextSet("search-location-test.js:85:13"));
let messages = getAllMessages(store.getState());
expect(messages.size - numUnfilterableMessages).toEqual(1);
});
it("restores all messages once text is cleared", () => {
store.dispatch(actions.filterTextSet("danger"));
store.dispatch(actions.filterTextSet(""));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages);
});
});
describe("Combined filters", () => {
// @TODO add test
it("filters");
});
});
describe("Clear filters", () => {
it("clears all filters", () => {
const store = setupStore([]);
// Setup test case
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
store.dispatch(actions.filterToggle("netxhr"));
store.dispatch(actions.filterTextSet("foobar"));
let filters = getAllFilters(store.getState());
expect(filters.toJS()).toEqual({
"debug": true,
"error": false,
"info": true,
"log": true,
"net": false,
"netxhr": true,
"warn": true,
"text": "foobar"
});
store.dispatch(actions.filtersClear());
filters = getAllFilters(store.getState());
expect(filters.toJS()).toEqual({
"debug": true,
"error": true,
"info": true,
"log": true,
"net": false,
"netxhr": false,
"warn": true,
"text": ""
});
});
});
function prepareBaseStore() {
const store = setupStore([
// Console API
"console.log('foobar', 'test')",
"console.warn('danger, will robinson!')",
"console.log(undefined)",
"console.count('bar')",
"console.log('鼬')",
// Evaluation Result - never filtered
"new Date(0)",
// PageError
"ReferenceError: asdf is not defined",
"console.group('bar')"
]);
// Console Command - never filtered
store.dispatch(messageAdd(new ConsoleCommand({ messageText: `console.warn("x")` })));
return store;
}

View file

@ -0,0 +1,353 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const {
getAllMessages,
getAllMessagesUiById,
getAllGroupsById,
getCurrentGroup,
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
const {
setupActions,
setupStore
} = require("devtools/client/webconsole/new-console-output/test/helpers");
const { stubPackets, stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const {
MESSAGE_TYPE,
} = require("devtools/client/webconsole/new-console-output/constants");
const expect = require("expect");
describe("Message reducer:", () => {
let actions;
before(() => {
actions = setupActions();
});
describe("messagesById", () => {
it("adds a message to an empty store", () => {
const { dispatch, getState } = setupStore([]);
const packet = stubPackets.get("console.log('foobar', 'test')");
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
dispatch(actions.messageAdd(packet));
const messages = getAllMessages(getState());
expect(messages.first()).toEqual(message);
});
it("increments repeat on a repeating message", () => {
const { dispatch, getState } = setupStore([
"console.log('foobar', 'test')",
"console.log('foobar', 'test')"
]);
const packet = stubPackets.get("console.log('foobar', 'test')");
dispatch(actions.messageAdd(packet));
dispatch(actions.messageAdd(packet));
const messages = getAllMessages(getState());
expect(messages.size).toBe(1);
expect(messages.first().repeat).toBe(4);
});
it("does not clobber a unique message", () => {
const { dispatch, getState } = setupStore([
"console.log('foobar', 'test')",
"console.log('foobar', 'test')"
]);
const packet = stubPackets.get("console.log('foobar', 'test')");
dispatch(actions.messageAdd(packet));
const packet2 = stubPackets.get("console.log(undefined)");
dispatch(actions.messageAdd(packet2));
const messages = getAllMessages(getState());
expect(messages.size).toBe(2);
expect(messages.first().repeat).toBe(3);
expect(messages.last().repeat).toBe(1);
});
it("adds a message in response to console.clear()", () => {
const { dispatch, getState } = setupStore([]);
dispatch(actions.messageAdd(stubPackets.get("console.clear()")));
const messages = getAllMessages(getState());
expect(messages.size).toBe(1);
expect(messages.first().parameters[0]).toBe("Console was cleared.");
});
it("clears the messages list in response to MESSAGES_CLEAR action", () => {
const { dispatch, getState } = setupStore([
"console.log('foobar', 'test')",
"console.log(undefined)"
]);
dispatch(actions.messagesClear());
const messages = getAllMessages(getState());
expect(messages.size).toBe(0);
});
it("limits the number of messages displayed", () => {
const { dispatch, getState } = setupStore([]);
const logLimit = 1000;
const packet = stubPackets.get("console.log(undefined)");
for (let i = 1; i <= logLimit + 1; i++) {
packet.message.arguments = [`message num ${i}`];
dispatch(actions.messageAdd(packet));
}
const messages = getAllMessages(getState());
expect(messages.count()).toBe(logLimit);
expect(messages.first().parameters[0]).toBe(`message num 2`);
expect(messages.last().parameters[0]).toBe(`message num ${logLimit + 1}`);
});
it("does not add null messages to the store", () => {
const { dispatch, getState } = setupStore([]);
const message = stubPackets.get("console.time('bar')");
dispatch(actions.messageAdd(message));
const messages = getAllMessages(getState());
expect(messages.size).toBe(0);
});
it("adds console.table call with unsupported type as console.log", () => {
const { dispatch, getState } = setupStore([]);
const packet = stubPackets.get("console.table('bar')");
dispatch(actions.messageAdd(packet));
const messages = getAllMessages(getState());
const tableMessage = messages.last();
expect(tableMessage.level).toEqual(MESSAGE_TYPE.LOG);
});
it("adds console.group messages to the store", () => {
const { dispatch, getState } = setupStore([]);
const message = stubPackets.get("console.group('bar')");
dispatch(actions.messageAdd(message));
const messages = getAllMessages(getState());
expect(messages.size).toBe(1);
});
it("sets groupId property as expected", () => {
const { dispatch, getState } = setupStore([]);
dispatch(actions.messageAdd(
stubPackets.get("console.group('bar')")));
const packet = stubPackets.get("console.log('foobar', 'test')");
dispatch(actions.messageAdd(packet));
const messages = getAllMessages(getState());
expect(messages.size).toBe(2);
expect(messages.last().groupId).toBe(messages.first().id);
});
it("does not display console.groupEnd messages to the store", () => {
const { dispatch, getState } = setupStore([]);
const message = stubPackets.get("console.groupEnd('bar')");
dispatch(actions.messageAdd(message));
const messages = getAllMessages(getState());
expect(messages.size).toBe(0);
});
it("filters out message added after a console.groupCollapsed message", () => {
const { dispatch, getState } = setupStore([]);
const message = stubPackets.get("console.groupCollapsed('foo')");
dispatch(actions.messageAdd(message));
dispatch(actions.messageAdd(
stubPackets.get("console.log('foobar', 'test')")));
const messages = getAllMessages(getState());
expect(messages.size).toBe(1);
});
it("shows the group of the first displayed message when messages are pruned", () => {
const { dispatch, getState } = setupStore([]);
const logLimit = 1000;
const groupMessage = stubPreparedMessages.get("console.group('bar')");
dispatch(actions.messageAdd(
stubPackets.get("console.group('bar')")));
const packet = stubPackets.get("console.log(undefined)");
for (let i = 1; i <= logLimit + 1; i++) {
packet.message.arguments = [`message num ${i}`];
dispatch(actions.messageAdd(packet));
}
const messages = getAllMessages(getState());
expect(messages.count()).toBe(logLimit);
expect(messages.first().messageText).toBe(groupMessage.messageText);
expect(messages.get(1).parameters[0]).toBe(`message num 3`);
expect(messages.last().parameters[0]).toBe(`message num ${logLimit + 1}`);
});
it("adds console.dirxml call as console.log", () => {
const { dispatch, getState } = setupStore([]);
const packet = stubPackets.get("console.dirxml(window)");
dispatch(actions.messageAdd(packet));
const messages = getAllMessages(getState());
const dirxmlMessage = messages.last();
expect(dirxmlMessage.level).toEqual(MESSAGE_TYPE.LOG);
});
});
describe("messagesUiById", () => {
it("opens console.trace messages when they are added", () => {
const { dispatch, getState } = setupStore([]);
const message = stubPackets.get("console.trace()");
dispatch(actions.messageAdd(message));
const messages = getAllMessages(getState());
const messagesUi = getAllMessagesUiById(getState());
expect(messagesUi.size).toBe(1);
expect(messagesUi.first()).toBe(messages.first().id);
});
it("clears the messages UI list in response to MESSAGES_CLEAR action", () => {
const { dispatch, getState } = setupStore([
"console.log('foobar', 'test')",
"console.log(undefined)"
]);
const traceMessage = stubPackets.get("console.trace()");
dispatch(actions.messageAdd(traceMessage));
dispatch(actions.messagesClear());
const messagesUi = getAllMessagesUiById(getState());
expect(messagesUi.size).toBe(0);
});
it("opens console.group messages when they are added", () => {
const { dispatch, getState } = setupStore([]);
const message = stubPackets.get("console.group('bar')");
dispatch(actions.messageAdd(message));
const messages = getAllMessages(getState());
const messagesUi = getAllMessagesUiById(getState());
expect(messagesUi.size).toBe(1);
expect(messagesUi.first()).toBe(messages.first().id);
});
it("does not open console.groupCollapsed messages when they are added", () => {
const { dispatch, getState } = setupStore([]);
const message = stubPackets.get("console.groupCollapsed('foo')");
dispatch(actions.messageAdd(message));
const messagesUi = getAllMessagesUiById(getState());
expect(messagesUi.size).toBe(0);
});
});
describe("currentGroup", () => {
it("sets the currentGroup when console.group message is added", () => {
const { dispatch, getState } = setupStore([]);
const packet = stubPackets.get("console.group('bar')");
dispatch(actions.messageAdd(packet));
const messages = getAllMessages(getState());
const currentGroup = getCurrentGroup(getState());
expect(currentGroup).toBe(messages.first().id);
});
it("sets currentGroup to expected value when console.groupEnd is added", () => {
const { dispatch, getState } = setupStore([
"console.group('bar')",
"console.groupCollapsed('foo')"
]);
let messages = getAllMessages(getState());
let currentGroup = getCurrentGroup(getState());
expect(currentGroup).toBe(messages.last().id);
const endFooPacket = stubPackets.get("console.groupEnd('foo')");
dispatch(actions.messageAdd(endFooPacket));
messages = getAllMessages(getState());
currentGroup = getCurrentGroup(getState());
expect(currentGroup).toBe(messages.first().id);
const endBarPacket = stubPackets.get("console.groupEnd('foo')");
dispatch(actions.messageAdd(endBarPacket));
messages = getAllMessages(getState());
currentGroup = getCurrentGroup(getState());
expect(currentGroup).toBe(null);
});
it("resets the currentGroup to null in response to MESSAGES_CLEAR action", () => {
const { dispatch, getState } = setupStore([
"console.group('bar')"
]);
dispatch(actions.messagesClear());
const currentGroup = getCurrentGroup(getState());
expect(currentGroup).toBe(null);
});
});
describe("groupsById", () => {
it("adds the group with expected array when console.group message is added", () => {
const { dispatch, getState } = setupStore([]);
const barPacket = stubPackets.get("console.group('bar')");
dispatch(actions.messageAdd(barPacket));
let messages = getAllMessages(getState());
let groupsById = getAllGroupsById(getState());
expect(groupsById.size).toBe(1);
expect(groupsById.has(messages.first().id)).toBe(true);
expect(groupsById.get(messages.first().id)).toEqual([]);
const fooPacket = stubPackets.get("console.groupCollapsed('foo')");
dispatch(actions.messageAdd(fooPacket));
messages = getAllMessages(getState());
groupsById = getAllGroupsById(getState());
expect(groupsById.size).toBe(2);
expect(groupsById.has(messages.last().id)).toBe(true);
expect(groupsById.get(messages.last().id)).toEqual([messages.first().id]);
});
it("resets groupsById in response to MESSAGES_CLEAR action", () => {
const { dispatch, getState } = setupStore([
"console.group('bar')",
"console.groupCollapsed('foo')",
]);
let groupsById = getAllGroupsById(getState());
expect(groupsById.size).toBe(2);
dispatch(actions.messagesClear());
groupsById = getAllGroupsById(getState());
expect(groupsById.size).toBe(0);
});
});
});

View file

@ -0,0 +1,41 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { getRepeatId } = require("devtools/client/webconsole/new-console-output/utils/messages");
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const expect = require("expect");
describe("getRepeatId:", () => {
it("returns same repeatId for duplicate values", () => {
const message1 = stubPreparedMessages.get("console.log('foobar', 'test')");
const message2 = message1.set("repeat", 3);
expect(getRepeatId(message1)).toEqual(getRepeatId(message2));
});
it("returns different repeatIds for different values", () => {
const message1 = stubPreparedMessages.get("console.log('foobar', 'test')");
const message2 = message1.set("parameters", ["funny", "monkey"]);
expect(getRepeatId(message1)).toNotEqual(getRepeatId(message2));
});
it("returns different repeatIds for different severities", () => {
const message1 = stubPreparedMessages.get("console.log('foobar', 'test')");
const message2 = message1.set("level", "error");
expect(getRepeatId(message1)).toNotEqual(getRepeatId(message2));
});
it("handles falsy values distinctly", () => {
const messageNaN = stubPreparedMessages.get("console.log(NaN)");
const messageUnd = stubPreparedMessages.get("console.log(undefined)");
const messageNul = stubPreparedMessages.get("console.log(null)");
const repeatIds = new Set([
getRepeatId(messageNaN),
getRepeatId(messageUnd),
getRepeatId(messageNul)]
);
expect(repeatIds.size).toEqual(3);
});
});