Bug 1295347 - New console frontend: Add support for console.time(). r=linclark
--- a/devtools/client/webconsole/new-console-output/constants.js
+++ b/devtools/client/webconsole/new-console-output/constants.js
@@ -39,17 +39,20 @@ const chromeRDPEnums = {
START_GROUP_COLLAPSED: "startGroupCollapsed",
END_GROUP: "endGroup",
ASSERT: "assert",
PROFILE: "profile",
PROFILE_END: "profileEnd",
// Undocumented in Chrome RDP, but is used for evaluation results.
RESULT: "result",
// Undocumented in Chrome RDP, but is used for input.
- COMMAND: "command"
+ COMMAND: "command",
+ // Undocumented in Chrome RDP, but is used for messages that should not
+ // output anything (e.g. `console.time()` calls).
+ NULL_MESSAGE: "nullMessage",
},
MESSAGE_LEVEL: {
LOG: "log",
ERROR: "error",
WARN: "warn",
DEBUG: "debug",
INFO: "info"
}
--- a/devtools/client/webconsole/new-console-output/reducers/messages.js
+++ b/devtools/client/webconsole/new-console-output/reducers/messages.js
@@ -16,17 +16,21 @@ const MessageState = Immutable.Record({
function messages(state = new MessageState(), action) {
const messagesById = state.messagesById;
const messagesUiById = state.messagesUiById;
switch (action.type) {
case constants.MESSAGE_ADD:
let newMessage = action.message;
- if (newMessage.type === "clear") {
+ if (newMessage.type === constants.MESSAGE_TYPE.NULL_MESSAGE) {
+ return state;
+ }
+
+ if (newMessage.type === constants.MESSAGE_TYPE.CLEAR) {
return state.set("messagesById", Immutable.List([newMessage]));
}
if (newMessage.allowRepeating && messagesById.size > 0) {
let lastMessage = messagesById.last();
if (lastMessage.repeatId === newMessage.repeatId) {
return state.withMutations(function (record) {
record.set("messagesById", messagesById.pop().push(
--- a/devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js
@@ -41,19 +41,40 @@ describe("ConsoleAPICall component:", ()
it("renders", () => {
const message = stubConsoleMessages.get("console.count('bar')");
const rendered = renderComponent(ConsoleApiCall, {message, onViewSourceInDebugger});
const messageBody = getMessageBody(rendered);
expect(messageBody.textContent).toBe(message.messageText);
});
});
+
+ describe("console.time", () => {
+ it("does not show anything", () => {
+ const message = stubConsoleMessages.get("console.time('bar')");
+ const rendered = renderComponent(ConsoleApiCall, {message, onViewSourceInDebugger});
+
+ const messageBody = getMessageBody(rendered);
+ expect(messageBody.textContent).toBe("");
+ });
+ });
+
+ describe("console.timeEnd", () => {
+ it("renders as expected", () => {
+ const message = stubConsoleMessages.get("console.timeEnd('bar')");
+ const rendered = renderComponent(ConsoleApiCall, {message, onViewSourceInDebugger});
+
+ const messageBody = getMessageBody(rendered);
+ expect(messageBody.textContent).toBe(message.messageText);
+ expect(messageBody.textContent).toMatch(/^bar: \d+(\.\d+)?ms$/);
+ });
+ });
});
function getMessageBody(rendered) {
const queryPath = "div.message.cm-s-mozilla span span.message-flex-body span.message-body.devtools-monospace";
return rendered.querySelector(queryPath);
}
function getRepeatNode(rendered) {
const repeatPath = "span > span.message-flex-body > span.message-body.devtools-monospace + span.message-repeats";
return rendered.querySelectorAll(repeatPath);
-}
\ No newline at end of file
+}
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_console_api.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_console_api.js
@@ -7,34 +7,39 @@
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 = [];
-snippets.forEach((code, key) => {
- add_task(function* () {
- let tempFilePath = OS.Path.join(`${BASE_PATH}/stub-generators`, "test-tempfile.js");
+add_task(function* () {
+ let tempFilePath = OS.Path.join(`${BASE_PATH}/stub-generators`, "test-tempfile.js");
+ for (var [key, {keys, code}] of snippets) {
OS.File.writeAtomic(tempFilePath, `function triggerPacket() {${code}}`);
-
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");
- toolbox.target.client.addListener("consoleAPICall", (type, res) => {
- stubs.push(formatStub(key, res));
- if (stubs.length == snippets.size) {
- let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "consoleApi.js");
- OS.File.writeAtomic(filePath, formatFile(stubs));
- OS.File.writeAtomic(tempFilePath, "");
- }
+ let received = new Promise(resolve => {
+ let i = 0;
+ toolbox.target.client.addListener("consoleAPICall", (type, res) => {
+ stubs.push(formatStub(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`, "consoleApi.js");
+ OS.File.writeAtomic(filePath, formatFile(stubs));
+ OS.File.writeAtomic(tempFilePath, "");
});
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js
@@ -10,35 +10,38 @@ const consoleApiCommands = [
"console.log(undefined)",
"console.warn('danger, will robinson!')",
"console.log(NaN)",
"console.log(null)",
"console.clear()",
"console.count('bar')",
];
-let consoleApi = new Map(consoleApiCommands.map(cmd => [cmd, cmd]));
+let consoleApi = new Map(consoleApiCommands.map(
+ cmd => [cmd, {keys: [cmd], code: cmd}]));
-consoleApi.set("console.trace()",
-`
+consoleApi.set("console.trace()", {
+ keys: ["console.trace()"],
+ code: `
function bar() {
console.trace()
}
function foo() {
bar()
}
foo()
-`);
+`});
-consoleApi.set("console.time()",
-`
-console.time()
-console.timeEnd()
-`);
+consoleApi.set("console.time('bar')", {
+ keys: ["console.time('bar')", "console.timeEnd('bar')"],
+ code: `
+console.time("bar");
+console.timeEnd("bar");
+`});
// Evaluation Result
const evaluationResultCommands = [
"new Date(0)"
];
let evaluationResult = new Map(evaluationResultCommands.map(cmd => [cmd, cmd]));
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
@@ -191,10 +191,46 @@ stubConsoleMessages.set("console.trace()
],
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"line": 3,
"column": 3
}
}));
+stubConsoleMessages.set("console.time('bar')", new ConsoleMessage({
+ "id": "1",
+ "allowRepeating": true,
+ "source": "console-api",
+ "type": "nullMessage",
+ "level": "log",
+ "messageText": null,
+ "parameters": null,
+ "repeat": 1,
+ "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"nullMessage\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"line\":2,\"column\":1}}",
+ "stacktrace": null,
+ "frame": {
+ "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
+ "line": 2,
+ "column": 1
+ }
+}));
+
+stubConsoleMessages.set("console.timeEnd('bar')", new ConsoleMessage({
+ "id": "1",
+ "allowRepeating": true,
+ "source": "console-api",
+ "type": "timeEnd",
+ "level": "log",
+ "messageText": "bar: 3.87ms",
+ "parameters": null,
+ "repeat": 1,
+ "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"timeEnd\",\"level\":\"log\",\"messageText\":\"bar: 3.87ms\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"line\":3,\"column\":1}}",
+ "stacktrace": null,
+ "frame": {
+ "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
+ "line": 3,
+ "column": 1
+ }
+}));
+
module.exports = stubConsoleMessages
\ No newline at end of file
--- a/devtools/client/webconsole/new-console-output/test/store/messages.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/messages.test.js
@@ -91,9 +91,19 @@ describe("Message reducer:", () => {
dispatch(actions.messageAdd(msg));
}
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 = stubConsoleMessages.get("console.time('bar')");
+ dispatch(actions.messageAdd(message));
+
+ const messages = getAllMessages(getState());
+ expect(messages.size).toBe(0);
+ });
});
--- a/devtools/client/webconsole/new-console-output/utils/messages.js
+++ b/devtools/client/webconsole/new-console-output/utils/messages.js
@@ -40,31 +40,49 @@ function transformPacket(packet) {
switch (packet.type) {
case "consoleAPICall": {
let { message } = packet;
let parameters = message.arguments;
let type = message.level;
let level = getLevelFromType(type);
let messageText = null;
+ const timer = message.timer;
// Special per-type conversion.
switch (type) {
case "clear":
// We show a message to users when calls console.clear() is called.
parameters = [l10n.getStr("consoleCleared")];
break;
case "count":
// Chrome RDP doesn't have a special type for count.
type = MESSAGE_TYPE.LOG;
let {counter} = message;
let label = counter.label ? counter.label : l10n.getStr("noCounterLabel");
messageText = `${label}: ${counter.count}`;
parameters = null;
break;
+ case "time":
+ // We don't show anything for console.time calls to match Chrome's behaviour.
+ parameters = null;
+ type = MESSAGE_TYPE.NULL_MESSAGE;
+ break;
+ case "timeEnd":
+ parameters = null;
+ if (timer) {
+ // We show the duration to users when calls console.timeEnd() is called,
+ // if corresponding console.time() was called before.
+ let duration = Math.round(timer.duration * 100) / 100;
+ messageText = l10n.getFormatStr("timeEnd", [timer.name, duration]);
+ } else {
+ // If the `timer` property does not exists, we don't output anything.
+ type = MESSAGE_TYPE.NULL_MESSAGE;
+ }
+ break;
}
const frame = {
source: message.filename || null,
line: message.lineNumber || null,
column: message.columnNumber || null
};