--- 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
@@ -22,25 +22,31 @@ const consoleApiCommands = [
"console.log('myregex', /a.b.c/)",
"console.table(['red', 'green', 'blue']);",
"console.log('myobject', {red: 'redValue', green: 'greenValue', blue: 'blueValue'});",
];
let consoleApi = new Map(consoleApiCommands.map(
cmd => [cmd, {keys: [cmd], code: cmd}]));
-consoleApi.set("console.map('mymap')", {
- keys: ["console.map('mymap')"],
+consoleApi.set("console.log('mymap')", {
+ keys: ["console.log('mymap')"],
code: `
var map = new Map();
map.set("key1", "value1");
map.set("key2", "value2");
console.log('mymap', map);
`});
+consoleApi.set("console.log('myset')", {
+ keys: ["console.log('myset')"],
+ code: `
+console.log('myset', new Set(["a", "b"]));
+`});
+
consoleApi.set("console.trace()", {
keys: ["console.trace()"],
code: `
function testStacktraceFiltering() {
console.trace()
}
function foo() {
testStacktraceFiltering()
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
@@ -553,30 +553,30 @@ stubPreparedMessages.set("console.log('m
},
"groupId": null,
"exceptionDocURL": null,
"userProvidedStyles": [],
"notes": null,
"indent": 0
}));
-stubPreparedMessages.set("console.map('mymap')", new ConsoleMessage({
+stubPreparedMessages.set("console.log('mymap')", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
- "timeStamp": 1493125410207,
+ "timeStamp": 1501506737042,
"type": "log",
"helperType": null,
"level": "log",
"messageText": null,
"parameters": [
"mymap",
{
"type": "object",
- "actor": "server1.conn0.child1/obj36",
+ "actor": "server1.conn0.child1/obj37",
"class": "Map",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "MapLike",
"size": 2,
@@ -588,30 +588,73 @@ stubPreparedMessages.set("console.map('m
[
"key2",
"value2"
]
]
}
}
],
- "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":5,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"mymap\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj36\",\"class\":\"Map\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"kind\":\"MapLike\",\"size\":2,\"entries\":[[\"key1\",\"value1\"],[\"key2\",\"value2\"]]}}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
+ "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":5,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"mymap\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj37\",\"class\":\"Map\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"kind\":\"MapLike\",\"size\":2,\"entries\":[[\"key1\",\"value1\"],[\"key2\",\"value2\"]]}}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 5,
"column": 1
},
"groupId": null,
"exceptionDocURL": null,
"userProvidedStyles": [],
"notes": null,
"indent": 0
}));
+stubPreparedMessages.set("console.log('myset')", new ConsoleMessage({
+ "id": "1",
+ "allowRepeating": true,
+ "source": "console-api",
+ "timeStamp": 1501506737051,
+ "type": "log",
+ "helperType": null,
+ "level": "log",
+ "messageText": null,
+ "parameters": [
+ "myset",
+ {
+ "type": "object",
+ "actor": "server1.conn0.child1/obj38",
+ "class": "Set",
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "ownPropertyLength": 0,
+ "preview": {
+ "kind": "ArrayLike",
+ "length": 2,
+ "items": [
+ "a",
+ "b"
+ ]
+ }
+ }
+ ],
+ "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"myset\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj38\",\"class\":\"Set\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"kind\":\"ArrayLike\",\"length\":2,\"items\":[\"a\",\"b\"]}}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
+ "stacktrace": null,
+ "frame": {
+ "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+ "line": 2,
+ "column": 1
+ },
+ "groupId": null,
+ "exceptionDocURL": null,
+ "userProvidedStyles": [],
+ "notes": null,
+ "indent": 0
+}));
+
stubPreparedMessages.set("console.trace()", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"timeStamp": 1479159910198,
"type": "trace",
"helperType": null,
"level": "log",
@@ -1684,26 +1727,26 @@ stubPackets.set("console.log('myobject',
"styles": [],
"timeStamp": 1493125748177,
"timer": null,
"workerType": "none",
"category": "webdev"
}
});
-stubPackets.set("console.map('mymap')", {
+stubPackets.set("console.log('mymap')", {
"from": "server1.conn0.child1/consoleActor2",
"type": "consoleAPICall",
"message": {
"addonId": "",
"arguments": [
"mymap",
{
"type": "object",
- "actor": "server1.conn0.child1/obj36",
+ "actor": "server1.conn0.child1/obj37",
"class": "Map",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "MapLike",
"size": 2,
@@ -1724,17 +1767,58 @@ stubPackets.set("console.map('mymap')",
"counter": null,
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"functionName": "triggerPacket",
"groupName": "",
"level": "log",
"lineNumber": 5,
"private": false,
"styles": [],
- "timeStamp": 1493125410207,
+ "timeStamp": 1501506737042,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev"
+ }
+});
+
+stubPackets.set("console.log('myset')", {
+ "from": "server1.conn0.child1/consoleActor2",
+ "type": "consoleAPICall",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "myset",
+ {
+ "type": "object",
+ "actor": "server1.conn0.child1/obj38",
+ "class": "Set",
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "ownPropertyLength": 0,
+ "preview": {
+ "kind": "ArrayLike",
+ "length": 2,
+ "items": [
+ "a",
+ "b"
+ ]
+ }
+ }
+ ],
+ "columnNumber": 1,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 2,
+ "private": false,
+ "styles": [],
+ "timeStamp": 1501506737051,
"timer": null,
"workerType": "none",
"category": "webdev"
}
});
stubPackets.set("console.trace()", {
"from": "server1.conn12.child1/consoleActor2",
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -39,15 +39,16 @@ skip-if = (os == 'linux' && bits == 32 &
[browser_webconsole_keyboard_accessibility.js]
[browser_webconsole_location_debugger_link.js]
[browser_webconsole_location_scratchpad_link.js]
[browser_webconsole_location_styleeditor_link.js]
[browser_webconsole_logErrorInPage.js]
[browser_webconsole_network_messages_click.js]
[browser_webconsole_nodes_highlight.js]
[browser_webconsole_nodes_select.js]
+[browser_webconsole_object_inspector_entries.js]
[browser_webconsole_object_inspector.js]
[browser_webconsole_observer_notifications.js]
[browser_webconsole_shows_reqs_in_netmonitor.js]
[browser_webconsole_stacktrace_location_debugger_link.js]
[browser_webconsole_stacktrace_location_scratchpad_link.js]
[browser_webconsole_string.js]
[browser_webconsole_timestamps.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector_entries.js
@@ -0,0 +1,185 @@
+/* -*- 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 expanding/collapsing maps and sets in the console.
+const TEST_URI = "data:text/html;charset=utf8,<h1>Object Inspector on Maps & Sets</h1>";
+
+add_task(async function () {
+ const hud = await openNewTabAndConsole(TEST_URI);
+ const store = hud.ui.newConsoleOutput.getStore();
+ // Adding logging 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
+ .reduce(function (res, {id, type, parameters, messageText}) {
+ res.push({id, type, parameters, messageText});
+ return res;
+ }, []);
+ info("messages : " + JSON.stringify(messages));
+ });
+
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, function () {
+ content.wrappedJSObject.console.log(
+ "oi-entries-test",
+ new Map(
+ Array.from({length: 20}).map((el, i) => [Symbol(i), i])
+ ),
+ new Map(
+ Array.from({length: 331}).map((el, i) => [Symbol(i), i])
+ ),
+ new Set(Array.from({length: 20}).map((el, i) => i)),
+ new Set(Array.from({length: 222}).map((el, i) => i)),
+ );
+ });
+
+ let node = await waitFor(() => findMessage(hud, "oi-entries-test"));
+ const objectInspectors = [...node.querySelectorAll(".tree")];
+ is(objectInspectors.length, 4, "There is the expected number of object inspectors");
+
+ const [
+ mapOi,
+ largeMapOi,
+ setOi,
+ largeSetOi,
+ ] = objectInspectors;
+
+ await testMap(mapOi);
+ await testLargeMap(largeMapOi);
+ await testSet(setOi);
+ await testLargeSet(largeSetOi);
+});
+
+async function testMap(oi) {
+ info("Expanding the Map");
+ let onMapOiMutation = waitForNodeMutation(oi, {
+ childList: true
+ });
+
+ oi.querySelector(".arrow").click();
+ await onMapOiMutation;
+
+ ok(oi.querySelector(".arrow").classList.contains("expanded"),
+ "The arrow of the node has the expected class after clicking on it");
+
+ let oiNodes = oi.querySelectorAll(".node");
+ // There are 4 nodes: the root, size, entries and the proto.
+ is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
+
+ info("Expanding the <entries> leaf of the map");
+ let entriesNode = oiNodes[2];
+ is(entriesNode.textContent, "<entries>", "There is the expected <entries> node");
+ onMapOiMutation = waitForNodeMutation(oi, {
+ childList: true
+ });
+
+ entriesNode.querySelector(".arrow").click();
+ await onMapOiMutation;
+
+ oiNodes = oi.querySelectorAll(".node");
+ // There are now 24 nodes, the 4 original ones, and the 20 entries.
+ is(oiNodes.length, 24, "There is the expected number of nodes in the tree");
+}
+
+async function testLargeMap(oi) {
+ info("Expanding the large map");
+ let onMapOiMutation = waitForNodeMutation(oi, {
+ childList: true
+ });
+
+ oi.querySelector(".arrow").click();
+ await onMapOiMutation;
+
+ ok(oi.querySelector(".arrow").classList.contains("expanded"),
+ "The arrow of the node has the expected class after clicking on it");
+
+ let oiNodes = oi.querySelectorAll(".node");
+ // There are 4 nodes: the root, size, entries and the proto.
+ is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
+
+ info("Expanding the <entries> leaf of the map");
+ let entriesNode = oiNodes[2];
+ is(entriesNode.textContent, "<entries>", "There is the expected <entries> node");
+ onMapOiMutation = waitForNodeMutation(oi, {
+ childList: true
+ });
+
+ entriesNode.querySelector(".arrow").click();
+ await onMapOiMutation;
+
+ oiNodes = oi.querySelectorAll(".node");
+ // There are now 8 nodes, the 4 original ones, and the 4 buckets.
+ is(oiNodes.length, 8, "There is the expected number of nodes in the tree");
+ is(oiNodes[3].textContent, "[0..99]");
+ is(oiNodes[4].textContent, "[100..199]");
+ is(oiNodes[5].textContent, "[200..299]");
+ is(oiNodes[6].textContent, "[300..331]");
+}
+
+async function testSet(oi) {
+ info("Expanding the Set");
+ let onSetOiMutation = waitForNodeMutation(oi, {
+ childList: true
+ });
+
+ oi.querySelector(".arrow").click();
+ await onSetOiMutation;
+
+ ok(oi.querySelector(".arrow").classList.contains("expanded"),
+ "The arrow of the node has the expected class after clicking on it");
+
+ let oiNodes = oi.querySelectorAll(".node");
+ // There are 4 nodes: the root, size, entries and the proto.
+ is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
+
+ info("Expanding the <entries> leaf of the Set");
+ let entriesNode = oiNodes[2];
+ is(entriesNode.textContent, "<entries>", "There is the expected <entries> node");
+ onSetOiMutation = waitForNodeMutation(oi, {
+ childList: true
+ });
+
+ entriesNode.querySelector(".arrow").click();
+ await onSetOiMutation;
+
+ oiNodes = oi.querySelectorAll(".node");
+ // There are now 24 nodes, the 4 original ones, and the 20 entries.
+ is(oiNodes.length, 24, "There is the expected number of nodes in the tree");
+}
+
+async function testLargeSet(oi) {
+ info("Expanding the large Set");
+ let onSetOiMutation = waitForNodeMutation(oi, {
+ childList: true
+ });
+
+ oi.querySelector(".arrow").click();
+ await onSetOiMutation;
+
+ ok(oi.querySelector(".arrow").classList.contains("expanded"),
+ "The arrow of the node has the expected class after clicking on it");
+
+ let oiNodes = oi.querySelectorAll(".node");
+ // There are 4 nodes: the root, size, entries and the proto.
+ is(oiNodes.length, 4, "There is the expected number of nodes in the tree");
+
+ info("Expanding the <entries> leaf of the Set");
+ let entriesNode = oiNodes[2];
+ is(entriesNode.textContent, "<entries>", "There is the expected <entries> node");
+ onSetOiMutation = waitForNodeMutation(oi, {
+ childList: true
+ });
+
+ entriesNode.querySelector(".arrow").click();
+ await onSetOiMutation;
+
+ oiNodes = oi.querySelectorAll(".node");
+ // There are now 7 nodes, the 4 original ones, and the 3 buckets.
+ is(oiNodes.length, 7, "There is the expected number of nodes in the tree");
+ is(oiNodes[3].textContent, "[0..99]");
+ is(oiNodes[4].textContent, "[100..199]");
+ is(oiNodes[5].textContent, "[200..222]");
+}
--- a/devtools/client/webconsole/new-console-output/test/store/messages.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/messages.test.js
@@ -7,16 +7,17 @@ const {
getAllMessagesById,
getAllMessagesTableDataById,
getAllMessagesUiById,
getAllNetworkMessagesUpdateById,
getAllRepeatById,
getCurrentGroup,
getVisibleMessages,
getAllMessagesObjectPropertiesById,
+ getAllMessagesObjectEntriesById,
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
const {
clonePacket,
getMessageAt,
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");
@@ -864,9 +865,105 @@ describe("Message reducer:", () => {
});
// This addition will remove the second table message.
dispatch(actions.messageAdd(stubPackets.get("console.log('foobar', 'test')")));
expect(getAllMessagesObjectPropertiesById(getState()).size).toBe(0);
});
});
+
+ describe("messagesObjectEntriesById", () => {
+ it(`adds messagesObjectEntriesById data in response to
+ MESSAGE_OBJECT_ENTRIES_RECEIVE action`, () => {
+ const { dispatch, getState } = setupStore([]);
+
+ // Add 2 log messages with loaded entries.
+ dispatch(actions.messageAdd(stubPackets.get("console.log('myset')")));
+ dispatch(actions.messageAdd(stubPackets.get("console.log('mymap')")));
+
+ let messages = getAllMessagesById(getState());
+
+ const setEntries = Symbol();
+ const mapEntries = Symbol();
+ const mapEntries2 = Symbol();
+
+ const [id1, id2] = [...messages.keys()];
+ dispatch(actions.messageObjectEntriesReceive(id1, "fakeActor1", setEntries));
+ dispatch(actions.messageObjectEntriesReceive(id2, "fakeActor2", mapEntries));
+ dispatch(actions.messageObjectEntriesReceive(id2, "fakeActor3", mapEntries2));
+
+ let loadedEntries = getAllMessagesObjectEntriesById(getState());
+ expect(loadedEntries.size).toBe(2);
+
+ expect(loadedEntries.get(id1)).toEqual({
+ fakeActor1: setEntries,
+ });
+
+ expect(loadedEntries.get(id2)).toEqual({
+ fakeActor2: mapEntries,
+ fakeActor3: mapEntries2,
+ });
+ });
+
+ it("resets messagesObjectEntriesById in response to MESSAGES_CLEAR action", () => {
+ const { dispatch, getState } = setupStore([
+ "console.log('myset')"
+ ]);
+
+ let messages = getAllMessagesById(getState());
+ const entries = Symbol("entries");
+ const message = messages.first();
+ const {actor} = message.parameters[1];
+
+ dispatch(actions.messageObjectEntriesReceive(message.id, actor, entries));
+
+ let loadedEntries = getAllMessagesObjectEntriesById(getState());
+ expect(loadedEntries.size).toBe(1);
+ expect(loadedEntries.get(message.id)).toEqual({
+ [actor]: entries
+ });
+
+ dispatch(actions.messagesClear());
+
+ expect(getAllMessagesObjectEntriesById(getState()).size).toBe(0);
+ });
+
+ it("cleans messagesObjectPropertiesById when messages are pruned", () => {
+ const { dispatch, getState } = setupStore([], null, {
+ logLimit: 2
+ });
+
+ // Add 2 log messages with loaded entries.
+ dispatch(actions.messageAdd(stubPackets.get("console.log('myset')")));
+ dispatch(actions.messageAdd(stubPackets.get("console.log('mymap')")));
+
+ let messages = getAllMessagesById(getState());
+
+ const setEntries = Symbol();
+ const mapEntries = Symbol();
+ const mapEntries2 = Symbol();
+
+ const [id1, id2] = [...messages.keys()];
+ dispatch(actions.messageObjectEntriesReceive(id1, "fakeActor1", setEntries));
+ dispatch(actions.messageObjectEntriesReceive(id2, "fakeActor2", mapEntries));
+ dispatch(actions.messageObjectEntriesReceive(id2, "fakeActor3", mapEntries2));
+
+ let loadedEntries = getAllMessagesObjectEntriesById(getState());
+ expect(loadedEntries.size).toBe(2);
+
+ // This addition will remove the first message.
+ dispatch(actions.messageAdd(stubPackets.get("console.log(undefined)")));
+
+ loadedEntries = getAllMessagesObjectEntriesById(getState());
+ expect(loadedEntries.size).toBe(1);
+ expect(loadedEntries.get(id2)).toEqual({
+ fakeActor2: mapEntries,
+ fakeActor3: mapEntries2,
+ });
+
+ // This addition will remove the second table message.
+ dispatch(actions.messageAdd(stubPackets.get("console.log('foobar', 'test')")));
+
+ expect(getAllMessagesObjectEntriesById(getState()).size).toBe(0);
+ });
+ });
});
--- a/devtools/client/webconsole/new-console-output/test/store/release-actors.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/release-actors.test.js
@@ -80,29 +80,37 @@ describe("Release actor enhancer:", () =
dispatch(actions.messageAdd(
stubPackets.get("console.log('myarray', ['red', 'green', 'blue'])")));
let messages = getAllMessagesById(getState());
const firstMessage = messages.first();
const firstMessageActor = firstMessage.parameters[1].actor;
const arrayProperties = Symbol();
const arraySubProperties = Symbol();
+ const mapEntries1 = Symbol();
+ const mapEntries2 = Symbol();
const [id] = [...messages.keys()];
dispatch(actions.messageObjectPropertiesReceive(
id, "fakeActor1", arrayProperties));
dispatch(actions.messageObjectPropertiesReceive(
id, "fakeActor2", arraySubProperties));
+ dispatch(actions.messageObjectEntriesReceive(
+ id, "mapActor1", mapEntries1));
+ dispatch(actions.messageObjectEntriesReceive(
+ id, "mapActor2", mapEntries2));
const packet = clonePacket(stubPackets.get(
"console.assert(false, {message: 'foobar'})"));
const secondMessageActor = packet.message.arguments[0].actor;
dispatch(actions.messageAdd(packet));
dispatch(actions.messagesClear());
- expect(releasedActors.length).toBe(4);
+ expect(releasedActors.length).toBe(6);
expect(releasedActors).toInclude(firstMessageActor);
expect(releasedActors).toInclude("fakeActor1");
expect(releasedActors).toInclude("fakeActor2");
+ expect(releasedActors).toInclude("mapActor1");
+ expect(releasedActors).toInclude("mapActor2");
expect(releasedActors).toInclude(secondMessageActor);
});
});
});
--- a/devtools/client/webconsole/new-console-output/test/store/search.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/search.test.js
@@ -83,15 +83,15 @@ describe("Searching in grips", () => {
function prepareBaseStore() {
const store = setupStore([
"console.log('foobar', 'test')",
"console.warn('danger, will robinson!')",
"console.table(['red', 'green', 'blue']);",
"console.count('bar')",
"console.log('myarray', ['red', 'green', 'blue'])",
"console.log('myregex', /a.b.c/)",
- "console.map('mymap')",
+ "console.log('mymap')",
"console.log('myobject', {red: 'redValue', green: 'greenValue', blue: 'blueValue'});",
"GET request",
]);
return store;
}