Bug 1295347 - New console frontend: Add support for console.time(). r=linclark draft
authorNicolas Chevobbe <chevobbe.nicolas@gmail.com>
Sat, 20 Aug 2016 00:46:26 +0200
changeset 403513 1bfa70b38624f3dbb75a8c1ea0c4d30bb1d2f098
parent 403386 8ef9629d8f90d6507b1bad01146b14101de79174
child 528927 5379a36d5d0701fa411cd4ce469e3e00a4d85bb7
push id26935
push userbmo:lclark@mozilla.com
push dateFri, 19 Aug 2016 22:49:43 +0000
reviewerslinclark
bugs1295347
milestone51.0a1
Bug 1295347 - New console frontend: Add support for console.time(). r=linclark
devtools/client/webconsole/new-console-output/constants.js
devtools/client/webconsole/new-console-output/reducers/messages.js
devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js
devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_console_api.js
devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js
devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
devtools/client/webconsole/new-console-output/test/store/messages.test.js
devtools/client/webconsole/new-console-output/utils/messages.js
--- 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
       };