summaryrefslogtreecommitdiffstats
path: root/devtools/client/webconsole/test/node
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/webconsole/test/node')
-rw-r--r--devtools/client/webconsole/test/node/.eslintrc.mocha.js14
-rw-r--r--devtools/client/webconsole/test/node/babel.config.js14
-rw-r--r--devtools/client/webconsole/test/node/components/.eslintrc.js5
-rw-r--r--devtools/client/webconsole/test/node/components/console-api-call.log-messages.test.js54
-rw-r--r--devtools/client/webconsole/test/node/components/console-api-call.test.js723
-rw-r--r--devtools/client/webconsole/test/node/components/console-output.test.js58
-rw-r--r--devtools/client/webconsole/test/node/components/css-warning.test.js118
-rw-r--r--devtools/client/webconsole/test/node/components/evaluation-result.test.js355
-rw-r--r--devtools/client/webconsole/test/node/components/filter-bar.test.js228
-rw-r--r--devtools/client/webconsole/test/node/components/filter-button.test.js39
-rw-r--r--devtools/client/webconsole/test/node/components/filter-checkbox.test.js37
-rw-r--r--devtools/client/webconsole/test/node/components/message-container.test.js68
-rw-r--r--devtools/client/webconsole/test/node/components/message-icon.test.js31
-rw-r--r--devtools/client/webconsole/test/node/components/message-location.test.js70
-rw-r--r--devtools/client/webconsole/test/node/components/message-repeat.test.js18
-rw-r--r--devtools/client/webconsole/test/node/components/message-types-aria.test.js55
-rw-r--r--devtools/client/webconsole/test/node/components/network-event-message.test.js152
-rw-r--r--devtools/client/webconsole/test/node/components/page-error.test.js531
-rw-r--r--devtools/client/webconsole/test/node/components/warning-group.test.js105
-rw-r--r--devtools/client/webconsole/test/node/components/webconsole-wrapper.test.js141
-rw-r--r--devtools/client/webconsole/test/node/fixtures/.eslintrc.js6
-rw-r--r--devtools/client/webconsole/test/node/fixtures/DevToolsUtils.js8
-rw-r--r--devtools/client/webconsole/test/node/fixtures/WebConsoleUtils.js17
-rw-r--r--devtools/client/webconsole/test/node/fixtures/async-storage.js13
-rw-r--r--devtools/client/webconsole/test/node/fixtures/serviceContainer.js31
-rw-r--r--devtools/client/webconsole/test/node/fixtures/stubs/browser_dummy.js11
-rw-r--r--devtools/client/webconsole/test/node/fixtures/stubs/consoleApi.js2042
-rw-r--r--devtools/client/webconsole/test/node/fixtures/stubs/cssMessage.js85
-rw-r--r--devtools/client/webconsole/test/node/fixtures/stubs/evaluationResult.js809
-rw-r--r--devtools/client/webconsole/test/node/fixtures/stubs/index.js37
-rw-r--r--devtools/client/webconsole/test/node/fixtures/stubs/networkEvent.js269
-rw-r--r--devtools/client/webconsole/test/node/fixtures/stubs/pageError.js1657
-rw-r--r--devtools/client/webconsole/test/node/fixtures/stubs/platformMessage.js54
-rw-r--r--devtools/client/webconsole/test/node/fixtures/stubs/stubs.ini14
-rw-r--r--devtools/client/webconsole/test/node/helpers.js177
-rw-r--r--devtools/client/webconsole/test/node/middleware/.eslintrc.js5
-rw-r--r--devtools/client/webconsole/test/node/middleware/debounce.test.js62
-rw-r--r--devtools/client/webconsole/test/node/mocha-test-setup.js182
-rw-r--r--devtools/client/webconsole/test/node/package.json36
-rw-r--r--devtools/client/webconsole/test/node/store/.eslintrc.js5
-rw-r--r--devtools/client/webconsole/test/node/store/filters.test.js338
-rw-r--r--devtools/client/webconsole/test/node/store/hidden-messages.test.js195
-rw-r--r--devtools/client/webconsole/test/node/store/messages.test.js1109
-rw-r--r--devtools/client/webconsole/test/node/store/network-messages.test.js133
-rw-r--r--devtools/client/webconsole/test/node/store/private-messages.test.js225
-rw-r--r--devtools/client/webconsole/test/node/store/release-actors.test.js172
-rw-r--r--devtools/client/webconsole/test/node/store/search.test.js113
-rw-r--r--devtools/client/webconsole/test/node/store/ui.test.js119
-rw-r--r--devtools/client/webconsole/test/node/utils/.eslintrc.js5
-rw-r--r--devtools/client/webconsole/test/node/utils/getRepeatId.test.js51
-rw-r--r--devtools/client/webconsole/test/node/yarn.lock3605
51 files changed, 14401 insertions, 0 deletions
diff --git a/devtools/client/webconsole/test/node/.eslintrc.mocha.js b/devtools/client/webconsole/test/node/.eslintrc.mocha.js
new file mode 100644
index 0000000000..c2dbe118c5
--- /dev/null
+++ b/devtools/client/webconsole/test/node/.eslintrc.mocha.js
@@ -0,0 +1,14 @@
+"use strict";
+
+module.exports = {
+ env: {
+ browser: false,
+ mocha: true,
+ },
+
+ globals: {
+ // document and window are injected via jsdom-global.
+ document: false,
+ window: false,
+ },
+};
diff --git a/devtools/client/webconsole/test/node/babel.config.js b/devtools/client/webconsole/test/node/babel.config.js
new file mode 100644
index 0000000000..e4fbe7fa68
--- /dev/null
+++ b/devtools/client/webconsole/test/node/babel.config.js
@@ -0,0 +1,14 @@
+/* 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/. */
+
+"use strict";
+
+module.exports = {
+ plugins: [
+ "@babel/plugin-proposal-class-properties",
+ "@babel/plugin-proposal-optional-chaining",
+ "@babel/plugin-proposal-nullish-coalescing-operator",
+ "transform-amd-to-commonjs",
+ ],
+};
diff --git a/devtools/client/webconsole/test/node/components/.eslintrc.js b/devtools/client/webconsole/test/node/components/.eslintrc.js
new file mode 100644
index 0000000000..9b9dfe0ad1
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/.eslintrc.js
@@ -0,0 +1,5 @@
+"use strict";
+
+module.exports = {
+ extends: "../.eslintrc.mocha.js",
+};
diff --git a/devtools/client/webconsole/test/node/components/console-api-call.log-messages.test.js b/devtools/client/webconsole/test/node/components/console-api-call.log-messages.test.js
new file mode 100644
index 0000000000..693702d980
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/console-api-call.log-messages.test.js
@@ -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 { render } = require("enzyme");
+
+// React
+const { createFactory } = require("devtools/client/shared/vendor/react");
+const { setupStore } = require("devtools/client/webconsole/test/node/helpers");
+const Provider = createFactory(require("react-redux").Provider);
+
+// Components under test.
+const ConsoleApiCall = createFactory(
+ require("devtools/client/webconsole/components/Output/message-types/ConsoleApiCall")
+);
+
+const {
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+
+const serviceContainer = require("devtools/client/webconsole/test/node/fixtures/serviceContainer");
+
+describe("ConsoleAPICall component for platform message", () => {
+ describe("Services.console.logStringMessage", () => {
+ it("renders logMessage grips", () => {
+ const message = stubPreparedMessages.get("platform-simple-message");
+ const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
+
+ expect(wrapper.find(".message-body").text()).toBe("foobar test");
+
+ // There should not be the location
+ expect(wrapper.find(".message-location").text()).toBe("");
+ });
+
+ it("renders longString logMessage grips", () => {
+ const message = stubPreparedMessages.get("platform-longString-message");
+
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ ConsoleApiCall({ message, serviceContainer })
+ )
+ );
+
+ expect(wrapper.find(".message-body").text()).toInclude(
+ `a\n${"a".repeat(100)}`
+ );
+ });
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/console-api-call.test.js b/devtools/client/webconsole/test/node/components/console-api-call.test.js
new file mode 100644
index 0000000000..ff9d381d94
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/console-api-call.test.js
@@ -0,0 +1,723 @@
+/* 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/test/node/helpers");
+
+// Components under test.
+const ConsoleApiCall = createFactory(
+ require("devtools/client/webconsole/components/Output/message-types/ConsoleApiCall")
+);
+const {
+ MESSAGE_OPEN,
+ MESSAGE_CLOSE,
+} = require("devtools/client/webconsole/constants");
+const {
+ INDENT_WIDTH,
+} = require("devtools/client/webconsole/components/Output/MessageIndent");
+const { prepareMessage } = require("devtools/client/webconsole/utils/messages");
+
+// Test fakes.
+const {
+ stubPreparedMessages,
+ stubPackets,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const serviceContainer = require("devtools/client/webconsole/test/node/fixtures/serviceContainer");
+
+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);
+ const selector =
+ "div.message.cm-s-mozilla span span.message-flex-body " +
+ "span.message-body.devtools-monospace";
+ expect(wrapper.find(selector).length).toBe(1);
+
+ // There should be the location
+ const locationLink = wrapper.find(`.message-location`);
+ expect(locationLink.length).toBe(1);
+ expect(locationLink.text()).toBe("test-console-api.html:1:35");
+ });
+
+ 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`);
+ expect(secondElementStyle["line-height"]).toBe("1.5");
+ // Forbidden styles are not applied.
+ expect(secondElementStyle.background).toBe(undefined);
+ });
+
+ it("renders string grips with data-url background", () => {
+ const message = stubPreparedMessages.get("console.log(%cfoobar)");
+
+ const dataURL =
+ "url(data:image/png,base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAAAXNSR0IArs4c6QAAAaZJREFUKBV9UjFLQlEUPueaaUUEDZESNTYI9RfK5oimIGxJcy4IodWxpeZKiHBwc6ghIiIaWqNB/ANiBSU6vHfvU+89nSPpINKB996993zfPef7zkP4CyLCzl02Y7VNg4aE8y2QoYrTVJg+ublCROpjURb0ko11mt2i05BkEDjt+CGgvzUYehrvqtTUefFD8KpXoemK1ich1MDALppIPITROARqlwzWJKdbtihYIWH7dv/AenRBAdYmOriKmUJDEv1oHaVnOy39bn27wJjsfLl0qawHaWkFNOWGCUKcOSs0uM0c6wPyWC+H4k2Ce+b+w89yMDKC0LPzWadgORTJxh8YM5ITIdUmw4b5jt9MssaJrdD9DtZGMvjQ+zEbvUoBVgWj2K2CWGsDeyqih4n1zeyk1S4vlcDCteRRbGwe7z2yO0lOiOU5YA3SklTgYee5/WVw1IVoZGnxrVTv+e4dpmIyB74xSayPBQ8GS5qvZgIRjPFfUQlHQ+s9kpSUil9bOxl2U0aQIO0Mf6tA6hoi4Xsw7QfGsHv4OiAJ8b/4XNmeC9pYRgTvF+HgISP3T9PvAAAAAElFTkSuQmCC)";
+
+ message.userProvidedStyles[0] = `background-image: ${dataURL}`;
+
+ const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
+ const elements = wrapper.find(".objectBox-string");
+ const firstElementStyle = elements.eq(0).prop("style");
+
+ // data-url background applied
+ expect(firstElementStyle["background-image"]).toBe(dataURL);
+ });
+
+ it("renders custom styled logs with empty style as expected", () => {
+ const message = stubPreparedMessages.get(
+ 'console.log("%cHello%c|%cWorld")'
+ );
+ const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
+
+ const elements = wrapper.find(".objectBox-string");
+ expect(elements.text()).toBe("Hello|World");
+ expect(elements.length).toBe(3);
+
+ const firstElementStyle = elements.eq(0).prop("style");
+ // Allowed styles are applied accordingly on the first element.
+ expect(firstElementStyle.color).toBe("red");
+
+ const secondElementStyle = elements.eq(1).prop("style");
+ expect(secondElementStyle.color).toBe(undefined);
+
+ const thirdElementStyle = elements.eq(2).prop("style");
+ // Allowed styles are applied accordingly on the third element.
+ expect(thirdElementStyle.color).toBe("blue");
+ });
+
+ it("renders prefixed messages", () => {
+ const packet = stubPackets.get("console.log('foobar', 'test')");
+ const stub = {
+ ...packet,
+ message: {
+ ...packet.message,
+ prefix: "MyNicePrefix",
+ },
+ };
+
+ const wrapper = render(
+ ConsoleApiCall({
+ message: prepareMessage(stub, { getNextId: () => "p" }),
+ serviceContainer,
+ })
+ );
+ const prefix = wrapper.find(".console-message-prefix");
+ expect(prefix.text()).toBe("MyNicePrefix: ");
+
+ expect(wrapper.find(".message-body").text()).toBe(
+ "MyNicePrefix: foobar test"
+ );
+
+ // There should be the location
+ const locationLink = wrapper.find(`.message-location`);
+ expect(locationLink.length).toBe(1);
+ expect(locationLink.text()).toBe("test-console-api.html:1:35");
+ });
+
+ it("renders repeat node", () => {
+ const message = stubPreparedMessages.get("console.log('foobar', 'test')");
+ const wrapper = render(
+ ConsoleApiCall({
+ message,
+ serviceContainer,
+ repeat: 107,
+ })
+ );
+
+ expect(wrapper.find(".message-repeats").text()).toBe("107");
+ expect(wrapper.find(".message-repeats").prop("title")).toBe(
+ "107 repeats"
+ );
+
+ const selector =
+ "span > span.message-flex-body > " +
+ "span.message-body.devtools-monospace + span.message-repeats";
+ expect(wrapper.find(selector).length).toBe(1);
+ });
+
+ it("has the expected indent", () => {
+ const message = stubPreparedMessages.get("console.log('foobar', 'test')");
+
+ const indent = 10;
+ let wrapper = render(
+ ConsoleApiCall({
+ message: Object.assign({}, message, { indent }),
+ serviceContainer,
+ })
+ );
+ let indentEl = wrapper.find(".indent");
+ expect(indentEl.prop("style").width).toBe(`${indent * INDENT_WIDTH}px`);
+ expect(indentEl.prop("data-indent")).toBe(`${indent}`);
+
+ wrapper = render(ConsoleApiCall({ message, serviceContainer }));
+ indentEl = wrapper.find(".indent");
+ expect(indentEl.prop("style").width).toBe(`0`);
+ expect(indentEl.prop("data-indent")).toBe(`0`);
+ });
+
+ it("renders a timestamp when passed a truthy timestampsVisible prop", () => {
+ const message = stubPreparedMessages.get("console.log('foobar', 'test')");
+ const wrapper = render(
+ ConsoleApiCall({
+ message,
+ serviceContainer,
+ timestampsVisible: true,
+ })
+ );
+ const {
+ timestampString,
+ } = require("devtools/client/webconsole/utils/l10n");
+
+ expect(wrapper.find(".timestamp").text()).toBe(
+ timestampString(message.timeStamp)
+ );
+ });
+
+ it("does not render a timestamp when not asked to", () => {
+ const message = stubPreparedMessages.get("console.log('foobar', 'test')");
+ const wrapper = render(
+ ConsoleApiCall({
+ message,
+ serviceContainer,
+ })
+ );
+
+ expect(wrapper.find(".timestamp").length).toBe(0);
+ });
+ });
+
+ describe("console.count", () => {
+ it("renders", () => {
+ const messages = [
+ {
+ key: "console.count('bar')",
+ expectedBodyText: "bar: 1",
+ },
+ {
+ key: "console.count | default: 1",
+ expectedBodyText: "default: 1",
+ },
+ {
+ key: "console.count | default: 2",
+ expectedBodyText: "default: 2",
+ },
+ {
+ key: "console.count | test counter: 1",
+ expectedBodyText: "test counter: 1",
+ },
+ {
+ key: "console.count | test counter: 2",
+ expectedBodyText: "test counter: 2",
+ },
+ {
+ key: "console.count | default: 3",
+ expectedBodyText: "default: 3",
+ },
+ {
+ key: "console.count | default: 4",
+ expectedBodyText: "default: 4",
+ },
+ {
+ key: "console.count | test counter: 3",
+ expectedBodyText: "test counter: 3",
+ },
+ {
+ key: "console.countReset | test counter: 0",
+ expectedBodyText: "test counter: 0",
+ },
+ {
+ key: "console.countReset | counterDoesntExist",
+ expectedBodyText: "Counter “test counter” doesn’t exist.",
+ },
+ ];
+
+ for (const { key, expectedBodyText } of messages) {
+ const message = stubPreparedMessages.get(key);
+ const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
+
+ expect(wrapper.find(".message-body").text()).toBe(expectedBodyText);
+ }
+ });
+ });
+
+ describe("console.assert", () => {
+ it("renders", () => {
+ const message = stubPreparedMessages.get(
+ "console.assert(false, {message: 'foobar'})"
+ );
+
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ 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("");
+ });
+ it("shows an error if called again", () => {
+ const message = stubPreparedMessages.get("timerAlreadyExists");
+ const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
+
+ expect(wrapper.find(".message-body").text()).toBe(
+ "Timer “bar” already exists."
+ );
+ });
+ });
+
+ describe("console.timeLog", () => {
+ it("renders as expected", () => {
+ let message = stubPreparedMessages.get("console.timeLog('bar') - 1");
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ let wrapper = render(
+ Provider(
+ { store: setupStore() },
+ ConsoleApiCall({ message, serviceContainer })
+ )
+ );
+
+ expect(wrapper.find(".message-body").text()).toBe(message.parameters[0]);
+ expect(wrapper.find(".message-body").text()).toMatch(
+ /^bar: \d+(\.\d+)?ms$/
+ );
+
+ message = stubPreparedMessages.get("console.timeLog('bar') - 2");
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ wrapper = render(
+ Provider(
+ { store: setupStore() },
+ ConsoleApiCall({ message, serviceContainer })
+ )
+ );
+ expect(wrapper.find(".message-body").text()).toMatch(
+ /^bar: \d+(\.\d+)?ms second call Object \{ state\: 1 \}$/
+ );
+ });
+ it("shows an error if the timer doesn't exist", () => {
+ const message = stubPreparedMessages.get("timeLog.timerDoesntExist");
+ const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
+
+ expect(wrapper.find(".message-body").text()).toBe(
+ "Timer “bar” doesn’t exist."
+ );
+ });
+ });
+
+ 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 - timer ended$/
+ );
+ });
+ it("shows an error if the timer doesn't exist", () => {
+ const message = stubPreparedMessages.get("timeEnd.timerDoesntExist");
+ const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
+
+ expect(wrapper.find(".message-body").text()).toBe(
+ "Timer “bar” doesn’t exist."
+ );
+ });
+ });
+
+ // Unskip will happen in Bug 1529548.
+ describe.skip("console.trace", () => {
+ it("renders", () => {
+ const message = stubPreparedMessages.get("console.trace()");
+ const wrapper = render(
+ ConsoleApiCall({ message, serviceContainer, open: true })
+ );
+ const filepath =
+ "http://example.com/browser/devtools/client/webconsole/" +
+ "test/fixtures/stub-generators/" +
+ "test-console-api.html";
+
+ expect(wrapper.find(".message-body").text()).toBe("console.trace()");
+
+ const frameLinks = wrapper.find(`.stack-trace span.frame-link[data-url]`);
+ 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);
+ });
+ it("render with arguments", () => {
+ const message = stubPreparedMessages.get(
+ "console.trace('bar', {'foo': 'bar'}, [1,2,3])"
+ );
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ ConsoleApiCall({ message, serviceContainer, open: true })
+ )
+ );
+
+ const filepath =
+ "http://example.com/browser/devtools/client/webconsole/" +
+ "test/fixtures/stub-generators/test-console-api.html";
+
+ expect(wrapper.find(".message-body").text()).toBe(
+ 'console.trace() bar Object { foo: "bar" } Array(3) [ 1, 2, 3 ]'
+ );
+
+ const frameLinks = wrapper.find(`.stack-trace span.frame-link[data-url]`);
+ expect(frameLinks.length).toBe(3);
+
+ expect(
+ frameLinks
+ .eq(0)
+ .find(".frame-link-function-display-name")
+ .text()
+ ).toBe("testStacktraceWithLog");
+ 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("bar");
+ expect(wrapper.find(".collapse-button[aria-expanded=true]").length).toBe(
+ 1
+ );
+ });
+
+ it("renders group with custom style", () => {
+ const message = stubPreparedMessages.get("console.group(%cfoo%cbar)");
+ const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
+ expect(wrapper.find(".message-body").text()).toBe("foobar");
+
+ const elements = wrapper.find(".objectBox-string");
+ 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("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(".collapse-button[aria-expanded='true']").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(".collapse-button").simulate("click");
+ call = store.dispatch.getCall(1);
+ expect(call.args[0]).toEqual({
+ id: message.id,
+ type: MESSAGE_OPEN,
+ });
+ });
+
+ it("toggle the group when the group name 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(".message-flex-body").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(".message-flex-body").simulate("click");
+ call = store.dispatch.getCall(1);
+ expect(call.args[0]).toEqual({
+ id: message.id,
+ type: MESSAGE_OPEN,
+ });
+ });
+
+ it("doesn't toggle the group when the location link is clicked", () => {
+ const store = setupStore();
+ store.dispatch = sinon.spy();
+ const message = stubPreparedMessages.get("console.group('bar')");
+
+ const wrapper = mount(
+ Provider(
+ { store },
+ ConsoleApiCall({
+ message,
+ open: true,
+ dispatch: store.dispatch,
+ serviceContainer,
+ })
+ )
+ );
+ wrapper.find(".frame-link-source").simulate("click");
+ const call = store.dispatch.getCall(0);
+ expect(call).toNotExist();
+ });
+ });
+
+ 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("foo");
+ expect(wrapper.find(".collapse-button:not(.expanded)").length).toBe(1);
+ });
+
+ it("renders group with custom style", () => {
+ const message = stubPreparedMessages.get(
+ "console.groupCollapsed(%cfoo%cbaz)"
+ );
+ const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
+
+ const elements = wrapper.find(".objectBox-string");
+ expect(elements.text()).toBe("foobaz");
+ 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);
+ });
+ });
+
+ describe("console.dirxml", () => {
+ it("renders", () => {
+ const message = stubPreparedMessages.get("console.dirxml(window)");
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ ConsoleApiCall({ message, serviceContainer })
+ )
+ );
+
+ expect(wrapper.find(".message-body").text()).toBe(
+ "Window http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html"
+ );
+ });
+ });
+
+ describe("console.dir", () => {
+ it("renders", () => {
+ const message = stubPreparedMessages.get("console.dir({C, M, Y, K})");
+
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ ConsoleApiCall({ message, serviceContainer })
+ )
+ );
+
+ expect(wrapper.find(".message-body").text()).toBe(
+ `Object { cyan: "C", magenta: "M", yellow: "Y", black: "K" }`
+ );
+ });
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/console-output.test.js b/devtools/client/webconsole/test/node/components/console-output.test.js
new file mode 100644
index 0000000000..c28a91f537
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/console-output.test.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { createFactory } = require("devtools/client/shared/vendor/react");
+// Test utils.
+const expect = require("expect");
+const { render } = require("enzyme");
+const Provider = createFactory(require("react-redux").Provider);
+
+const ConsoleOutput = createFactory(
+ require("devtools/client/webconsole/components/Output/ConsoleOutput")
+);
+const serviceContainer = require("devtools/client/webconsole/test/node/fixtures/serviceContainer");
+const { setupStore } = require("devtools/client/webconsole/test/node/helpers");
+const { initialize } = require("devtools/client/webconsole/actions/ui");
+const {
+ getInitialMessageCountForViewport,
+} = require("devtools/client/webconsole/utils/messages.js");
+
+const MESSAGES_NUMBER = 100;
+function getDefaultProps(initialized) {
+ const store = setupStore(
+ Array.from({ length: MESSAGES_NUMBER })
+ // Alternate message so we don't trigger the repeat mechanism.
+ .map((_, i) => (i % 2 ? "console.log(null)" : "console.log(NaN)"))
+ );
+
+ if (initialized) {
+ store.dispatch(initialize());
+ }
+
+ return {
+ store,
+ serviceContainer,
+ };
+}
+
+describe("ConsoleOutput component:", () => {
+ it("Render only the last messages that fits the viewport when non-initialized", () => {
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ const rendered = render(
+ Provider({ store: setupStore() }, ConsoleOutput(getDefaultProps(false)))
+ );
+ const messagesNumber = rendered.find(".message").length;
+ expect(messagesNumber).toBe(getInitialMessageCountForViewport(window));
+ });
+
+ it("Render every message when initialized", () => {
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ const rendered = render(
+ Provider({ store: setupStore() }, ConsoleOutput(getDefaultProps(true)))
+ );
+ expect(rendered.find(".message").length).toBe(MESSAGES_NUMBER);
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/css-warning.test.js b/devtools/client/webconsole/test/node/components/css-warning.test.js
new file mode 100644
index 0000000000..dd2a0f789f
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/css-warning.test.js
@@ -0,0 +1,118 @@
+/* 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/test/node/helpers");
+
+// Components under test.
+const CSSWarning = require("devtools/client/webconsole/components/Output/message-types/CSSWarning");
+const {
+ MESSAGE_OPEN,
+ MESSAGE_CLOSE,
+} = require("devtools/client/webconsole/constants");
+
+// Test fakes.
+const {
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const serviceContainer = require("devtools/client/webconsole/test/node/fixtures/serviceContainer");
+
+describe("CSSWarning component:", () => {
+ it("renders", () => {
+ const message = stubPreparedMessages.get(
+ "Unknown property ‘such-unknown-property’. Declaration dropped."
+ );
+ const wrapper = render(
+ CSSWarning({
+ message,
+ serviceContainer,
+ timestampsVisible: true,
+ })
+ );
+ const {
+ timestampString,
+ } = require("devtools/client/webconsole/utils/l10n");
+
+ expect(wrapper.find(".timestamp").text()).toBe(
+ timestampString(message.timeStamp)
+ );
+
+ expect(wrapper.find(".message-body").text()).toBe(
+ "Unknown property ‘such-unknown-property’. Declaration dropped."
+ );
+
+ // There shouldn't be a matched elements label rendered by default.
+ const elementLabel = wrapper.find(`.elements-label`);
+ expect(elementLabel.length).toBe(0);
+
+ // There should be a location.
+ const locationLink = wrapper.find(`.message-location`);
+ expect(locationLink.length).toBe(1);
+ expect(locationLink.text()).toBe("test-css-message.html:3:27");
+ });
+
+ it("closes an open message when the collapse button is clicked", () => {
+ const store = setupStore();
+ store.dispatch = sinon.spy();
+ const message = stubPreparedMessages.get(
+ "Unknown property ‘such-unknown-property’. Declaration dropped."
+ );
+
+ const wrapper = mount(
+ Provider(
+ { store },
+ CSSWarning({
+ message,
+ open: true,
+ dispatch: store.dispatch,
+ serviceContainer,
+ })
+ )
+ );
+
+ wrapper.find(".collapse-button[aria-expanded='true']").simulate("click");
+
+ const call = store.dispatch.getCall(0);
+ expect(call.args[0]).toEqual({
+ id: message.id,
+ type: MESSAGE_CLOSE,
+ });
+ });
+
+ it("opens a closed message when the collapse button is clicked", () => {
+ const store = setupStore();
+ store.dispatch = sinon.spy();
+ const message = stubPreparedMessages.get(
+ "Unknown property ‘such-unknown-property’. Declaration dropped."
+ );
+
+ const wrapper = mount(
+ Provider(
+ { store },
+ CSSWarning({
+ message,
+ open: false,
+ payload: {}, // fake the existence of a payload to test just MESSAGE_OPEN action
+ dispatch: store.dispatch,
+ serviceContainer,
+ })
+ )
+ );
+
+ wrapper.find(".collapse-button[aria-expanded='false']").simulate("click");
+
+ const call = store.dispatch.getCall(0);
+ expect(call.args[0]).toEqual({
+ id: message.id,
+ type: MESSAGE_OPEN,
+ });
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/evaluation-result.test.js b/devtools/client/webconsole/test/node/components/evaluation-result.test.js
new file mode 100644
index 0000000000..51117bbc67
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/evaluation-result.test.js
@@ -0,0 +1,355 @@
+/* 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/test/node/helpers");
+
+// Components under test.
+const EvaluationResult = createFactory(
+ require("devtools/client/webconsole/components/Output/message-types/EvaluationResult")
+);
+const {
+ INDENT_WIDTH,
+} = require("devtools/client/webconsole/components/Output/MessageIndent");
+
+// Test fakes.
+const {
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const serviceContainer = require("devtools/client/webconsole/test/node/fixtures/serviceContainer");
+
+describe("EvaluationResult component:", () => {
+ it.skip("renders a grip result", () => {
+ const message = stubPreparedMessages.get("new Date(0)");
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ EvaluationResult({ message, serviceContainer })
+ )
+ );
+
+ expect(wrapper.find(".message-body").text()).toBe(
+ "Date 1970-01-01T00:00:00.000Z"
+ );
+
+ expect(wrapper.hasClass("message")).toBe(true);
+ expect(wrapper.hasClass("log")).toBe(true);
+ });
+
+ it("renders an error", () => {
+ const message = stubPreparedMessages.get("asdf()");
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+
+ expect(wrapper.find(".message-body").text()).toBe(
+ "Uncaught ReferenceError: asdf is not defined[Learn More]"
+ );
+
+ expect(wrapper.hasClass("message")).toBe(true);
+ expect(wrapper.hasClass("error")).toBe(true);
+ });
+
+ it("renders an error with a longString exception message", () => {
+ const message = stubPreparedMessages.get("longString message Error");
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+
+ const text = wrapper.find(".message-body").text();
+ expect(text.startsWith("Uncaught Error: Long error Long error")).toBe(true);
+ expect(wrapper.hasClass("message")).toBe(true);
+ expect(wrapper.hasClass("error")).toBe(true);
+ });
+
+ it("renders thrown empty string", () => {
+ const message = stubPreparedMessages.get(`eval throw ""`);
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe("Uncaught <empty string>");
+ expect(wrapper.hasClass("error")).toBe(true);
+ });
+
+ it("renders thrown string", () => {
+ const message = stubPreparedMessages.get(`eval throw "tomato"`);
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe("Uncaught tomato");
+ expect(wrapper.hasClass("error")).toBe(true);
+ });
+
+ it("render thrown Boolean", () => {
+ const message = stubPreparedMessages.get(`eval throw false`);
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe("Uncaught false");
+ expect(wrapper.hasClass("error")).toBe(true);
+ });
+
+ it("render thrown Number", () => {
+ const message = stubPreparedMessages.get(`eval throw 0`);
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe("Uncaught 0");
+ expect(wrapper.hasClass("error")).toBe(true);
+ });
+
+ it("render thrown null", () => {
+ const message = stubPreparedMessages.get(`eval throw null`);
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe("Uncaught null");
+ expect(wrapper.hasClass("error")).toBe(true);
+ });
+
+ it("render thrown undefined", () => {
+ const message = stubPreparedMessages.get(`eval throw undefined`);
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe("Uncaught undefined");
+ expect(wrapper.hasClass("error")).toBe(true);
+ });
+
+ it("render thrown Symbol", () => {
+ const message = stubPreparedMessages.get(`eval throw Symbol`);
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe("Uncaught Symbol(potato)");
+ expect(wrapper.hasClass("error")).toBe(true);
+ });
+
+ it("render thrown Object", () => {
+ const message = stubPreparedMessages.get(`eval throw Object`);
+ // We need to wrap the EvaluationResult in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ EvaluationResult({ message, serviceContainer })
+ )
+ );
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught Object { vegetable: "cucumber" }`);
+ expect(wrapper.hasClass("error")).toBe(true);
+ });
+
+ it("render thrown Error Object", () => {
+ const message = stubPreparedMessages.get(`eval throw Error Object`);
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe("Uncaught Error: pumpkin");
+ expect(wrapper.hasClass("error")).toBe(true);
+ });
+
+ it("render thrown ErrorObject with custom name", () => {
+ const message = stubPreparedMessages.get(
+ `eval throw Error Object with custom name`
+ );
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe("Uncaught JuicyError: pineapple");
+ expect(wrapper.hasClass("error")).toBe(true);
+ });
+
+ it("render pending Promise", () => {
+ const message = stubPreparedMessages.get(`eval pending promise`);
+ // We need to wrap the EvaluationResult in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ EvaluationResult({ message, serviceContainer })
+ )
+ );
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Promise { <state>: "pending" }`);
+ });
+
+ it("render Promise.resolve result", () => {
+ const message = stubPreparedMessages.get(`eval Promise.resolve`);
+ // We need to wrap the EvaluationResult in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ EvaluationResult({ message, serviceContainer })
+ )
+ );
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Promise { <state>: "fulfilled", <value>: 123 }`);
+ });
+
+ it("render Promise.reject result", () => {
+ const message = stubPreparedMessages.get(`eval Promise.reject`);
+ // We need to wrap the EvaluationResult in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ EvaluationResult({ message, serviceContainer })
+ )
+ );
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Promise { <state>: "rejected", <reason>: "ouch" }`);
+ });
+
+ it("render promise fulfilled in microtask", () => {
+ // See Bug 1439963
+ const message = stubPreparedMessages.get(`eval resolved promise`);
+ // We need to wrap the EvaluationResult in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ EvaluationResult({ message, serviceContainer })
+ )
+ );
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Promise { <state>: "fulfilled", <value>: 246 }`);
+ });
+
+ it("render promise rejected in microtask", () => {
+ // See Bug 1439963
+ const message = stubPreparedMessages.get(`eval rejected promise`);
+ // We need to wrap the EvaluationResult in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ EvaluationResult({ message, serviceContainer })
+ )
+ );
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(
+ `Promise { <state>: "rejected", <reason>: ReferenceError }`
+ );
+ });
+
+ it("renders an inspect command result", () => {
+ const message = stubPreparedMessages.get("inspect({a: 1})");
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ EvaluationResult({ message, serviceContainer })
+ )
+ );
+
+ expect(wrapper.find(".message-body").text()).toBe("Object { a: 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,
+ dispatch: () => {},
+ })
+ )
+ );
+
+ 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");
+ const 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;
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ let wrapper = render(
+ Provider(
+ { store: setupStore() },
+ EvaluationResult({
+ message: Object.assign({}, message, { indent }),
+ serviceContainer,
+ })
+ )
+ );
+ let indentEl = wrapper.find(".indent");
+ expect(indentEl.prop("style").width).toBe(`${indent * INDENT_WIDTH}px`);
+ expect(indentEl.prop("data-indent")).toBe(`${indent}`);
+
+ wrapper = render(
+ Provider(
+ { store: setupStore() },
+ EvaluationResult({ message, serviceContainer })
+ )
+ );
+ indentEl = wrapper.find(".indent");
+ expect(indentEl.prop("style").width).toBe(`0`);
+ expect(indentEl.prop("data-indent")).toBe(`0`);
+ });
+
+ it("has location information", () => {
+ const message = stubPreparedMessages.get("1 + @");
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+
+ const locationLink = wrapper.find(`.message-location`);
+ expect(locationLink.length).toBe(1);
+ expect(locationLink.text()).toBe("debugger eval code:1:4");
+ });
+
+ it("has a timestamp when passed a truthy timestampsVisible prop", () => {
+ const message = stubPreparedMessages.get("new Date(0)");
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ EvaluationResult({
+ message,
+ serviceContainer,
+ timestampsVisible: true,
+ })
+ )
+ );
+ const {
+ timestampString,
+ } = require("devtools/client/webconsole/utils/l10n");
+
+ expect(wrapper.find(".timestamp").text()).toBe(
+ timestampString(message.timeStamp)
+ );
+ });
+
+ it("does not have a timestamp when timestampsVisible prop is falsy", () => {
+ const message = stubPreparedMessages.get("new Date(0)");
+ // We need to wrap the ConsoleApiElement in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ EvaluationResult({
+ message,
+ serviceContainer,
+ timestampsVisible: false,
+ })
+ )
+ );
+
+ expect(wrapper.find(".timestamp").length).toBe(0);
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/filter-bar.test.js b/devtools/client/webconsole/test/node/components/filter-bar.test.js
new file mode 100644
index 0000000000..1452066c35
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/filter-bar.test.js
@@ -0,0 +1,228 @@
+/* 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, shallow } = require("enzyme");
+
+const { createFactory } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const Provider = createFactory(require("react-redux").Provider);
+
+const actions = require("devtools/client/webconsole/actions/index");
+const FilterButton = require("devtools/client/webconsole/components/FilterBar/FilterButton");
+const FilterBar = createFactory(
+ require("devtools/client/webconsole/components/FilterBar/FilterBar")
+);
+const {
+ FILTERBAR_DISPLAY_MODES,
+} = require("devtools/client/webconsole/constants");
+const {
+ MESSAGES_CLEAR,
+ FILTERS,
+} = require("devtools/client/webconsole/constants");
+
+const {
+ setupStore,
+ clearPrefs,
+} = require("devtools/client/webconsole/test/node/helpers");
+const serviceContainer = require("devtools/client/webconsole/test/node/fixtures/serviceContainer");
+
+function getFilterBar(overrides = {}) {
+ return FilterBar({
+ serviceContainer,
+ hidePersistLogsCheckbox: false,
+ attachRefToWebConsoleUI: () => {},
+ webConsoleUI: {
+ document,
+ wrapper: {},
+ },
+ ...overrides,
+ });
+}
+
+describe("FilterBar component:", () => {
+ afterEach(() => {
+ clearPrefs();
+ });
+
+ it("initial render", () => {
+ const store = setupStore();
+
+ const wrapper = render(Provider({ store }, getFilterBar()));
+ const toolbar = wrapper.find(
+ ".devtools-toolbar.webconsole-filterbar-primary"
+ );
+
+ // Clear button
+ const clearButton = toolbar.children().eq(0);
+ expect(clearButton.attr("class")).toBe(
+ "devtools-button devtools-clear-icon"
+ );
+ expect(clearButton.attr("title")).toBe("Clear the Web Console output");
+
+ // Separator
+ expect(
+ toolbar
+ .children()
+ .eq(1)
+ .attr("class")
+ ).toBe("devtools-separator");
+
+ // Text filter
+ const textInput = toolbar.children().eq(2);
+ expect(textInput.attr("class")).toBe("devtools-searchbox");
+
+ // Text filter input
+ const textFilter = textInput.children().eq(0);
+ expect(textFilter.attr("class")).toBe("devtools-filterinput");
+ expect(textFilter.attr("placeholder")).toBe("Filter Output");
+ expect(textFilter.attr("type")).toBe("search");
+ expect(textFilter.attr("value")).toBe("");
+
+ // Text filter input clear button
+ const textFilterClearButton = textInput.children().eq(1);
+ expect(textFilterClearButton.attr("class")).toBe(
+ "devtools-searchinput-clear"
+ );
+
+ // Settings menu icon
+ expect(
+ wrapper.find(".webconsole-console-settings-menu-button").length
+ ).toBe(1);
+ });
+
+ it("displays the number of hidden messages when a search hide messages", () => {
+ const store = setupStore([
+ "console.log('foobar', 'test')",
+ "console.info('info message');",
+ "console.warn('danger, will robinson!')",
+ "console.debug('debug message');",
+ "console.error('error message');",
+ ]);
+ store.dispatch(actions.filterTextSet("qwerty"));
+
+ const wrapper = mount(Provider({ store }, getFilterBar()));
+
+ const message = wrapper.find(".devtools-searchinput-summary");
+ expect(message.text()).toBe("5 hidden");
+ expect(message.prop("title")).toBe("5 items hidden by text filter");
+ });
+
+ it("displays the number of hidden messages when a search hide 1 message", () => {
+ const store = setupStore([
+ "console.log('foobar', 'test')",
+ "console.info('info message');",
+ ]);
+ store.dispatch(actions.filterTextSet("foobar"));
+
+ const wrapper = mount(Provider({ store }, getFilterBar()));
+
+ const message = wrapper.find(".devtools-searchinput-summary");
+ expect(message.text()).toBe("1 hidden");
+ expect(message.prop("title")).toBe("1 item hidden by text filter");
+ });
+
+ it("displays the expected number of hidden messages when multiple filters", () => {
+ const store = setupStore([
+ "console.log('foobar', 'test')",
+ "console.info('info message');",
+ "console.warn('danger, will robinson!')",
+ "console.debug('debug message');",
+ "console.error('error message');",
+ ]);
+ store.dispatch(actions.filterTextSet("qwerty"));
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+ store.dispatch(actions.filterToggle(FILTERS.INFO));
+
+ const wrapper = mount(Provider({ store }, getFilterBar()));
+
+ const message = wrapper.find(".devtools-searchinput-summary");
+ expect(message.text()).toBe("3 hidden");
+ expect(message.prop("title")).toBe("3 items hidden by text filter");
+ });
+
+ it("does not display the number of hidden messages when there are no messages", () => {
+ const store = setupStore();
+ store.dispatch(actions.filterTextSet("qwerty"));
+ const wrapper = mount(Provider({ store }, getFilterBar()));
+
+ const toolbar = wrapper.find(".devtools-searchinput-summary");
+ expect(toolbar.exists()).toBeFalsy();
+ });
+
+ it("Displays a filter buttons bar on its own element in narrow displayMode", () => {
+ const store = setupStore();
+
+ const wrapper = mount(
+ Provider(
+ { store },
+ getFilterBar({
+ displayMode: FILTERBAR_DISPLAY_MODES.NARROW,
+ })
+ )
+ );
+
+ const secondaryBar = wrapper.find(".webconsole-filterbar-secondary");
+ expect(secondaryBar.length).toBe(1);
+
+ // Buttons are displayed
+ const filterBtn = props =>
+ FilterButton(
+ Object.assign(
+ {},
+ {
+ active: true,
+ dispatch: store.dispatch,
+ },
+ props
+ )
+ );
+
+ const buttons = [
+ filterBtn({ label: "Errors", filterKey: FILTERS.ERROR }),
+ filterBtn({ label: "Warnings", filterKey: FILTERS.WARN }),
+ filterBtn({ label: "Logs", filterKey: FILTERS.LOG }),
+ filterBtn({ label: "Info", filterKey: FILTERS.INFO }),
+ filterBtn({ label: "Debug", filterKey: FILTERS.DEBUG }),
+ dom.div({
+ className: "devtools-separator",
+ }),
+ filterBtn({
+ label: "CSS",
+ filterKey: "css",
+ active: false,
+ title:
+ "Stylesheets will be reparsed to check for errors. Refresh the page to also see errors from stylesheets modified from Javascript.",
+ }),
+ filterBtn({ label: "XHR", filterKey: "netxhr", active: false }),
+ filterBtn({ label: "Requests", filterKey: "net", active: false }),
+ ];
+
+ secondaryBar.children().forEach((child, index) => {
+ expect(child.html()).toEqual(shallow(buttons[index]).html());
+ });
+ });
+
+ it("fires MESSAGES_CLEAR action when clear button is clicked", () => {
+ const store = setupStore();
+ store.dispatch = sinon.spy();
+
+ const wrapper = mount(Provider({ store }, getFilterBar()));
+ 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 }, getFilterBar()));
+ const input = wrapper.find(".devtools-filterinput");
+ input.simulate("change", { target: { value: "a" } });
+ expect(store.getState().filters.text).toBe("a");
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/filter-button.test.js b/devtools/client/webconsole/test/node/components/filter-button.test.js
new file mode 100644
index 0000000000..14263fb265
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/filter-button.test.js
@@ -0,0 +1,39 @@
+/* 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/components/FilterBar/FilterButton")
+);
+const { MESSAGE_LEVEL } = require("devtools/client/webconsole/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.is("button")).toBe(true);
+ expect(wrapper.hasClass("devtools-togglebutton")).toBe(true);
+ expect(wrapper.attr("data-category")).toBe("error");
+ expect(wrapper.attr("aria-pressed")).toBe("true");
+ expect(wrapper.text()).toBe("Error");
+ });
+
+ it("displays as inactive when turned off", () => {
+ const wrapper = render(FilterButton({ ...props, active: false }));
+ expect(wrapper.is("button")).toBe(true);
+ expect(wrapper.hasClass("devtools-togglebutton")).toBe(true);
+ expect(wrapper.attr("data-category")).toBe("error");
+ expect(wrapper.attr("aria-pressed")).toBe("false");
+ expect(wrapper.text()).toBe("Error");
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/filter-checkbox.test.js b/devtools/client/webconsole/test/node/components/filter-checkbox.test.js
new file mode 100644
index 0000000000..57b9656ac2
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/filter-checkbox.test.js
@@ -0,0 +1,37 @@
+/* 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 FilterCheckbox = createFactory(
+ require("devtools/client/webconsole/components/FilterBar/FilterCheckbox")
+);
+
+describe("FilterCheckbox component:", () => {
+ const props = {
+ label: "test label",
+ title: "test title",
+ checked: true,
+ onChange: () => {},
+ };
+
+ it("displays as checked", () => {
+ const wrapper = render(FilterCheckbox(props));
+ expect(wrapper.is("label")).toBe(true);
+ expect(wrapper.attr("title")).toBe("test title");
+ expect(wrapper.hasClass("filter-checkbox")).toBe(true);
+ expect(wrapper.html()).toBe('<input type="checkbox" checked>test label');
+ });
+
+ it("displays as unchecked", () => {
+ const wrapper = render(FilterCheckbox({ ...props, checked: false }));
+ expect(wrapper.is("label")).toBe(true);
+ expect(wrapper.attr("title")).toBe("test title");
+ expect(wrapper.hasClass("filter-checkbox")).toBe(true);
+ expect(wrapper.html()).toBe('<input type="checkbox">test label');
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/message-container.test.js b/devtools/client/webconsole/test/node/components/message-container.test.js
new file mode 100644
index 0000000000..e9fd8074b2
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/message-container.test.js
@@ -0,0 +1,68 @@
+/* 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");
+const { createFactory } = require("devtools/client/shared/vendor/react");
+
+// Components under test.
+let {
+ MessageContainer,
+ getMessageComponent,
+} = require("devtools/client/webconsole/components/Output/MessageContainer");
+MessageContainer = createFactory(MessageContainer);
+const ConsoleApiCall = require("devtools/client/webconsole/components/Output/message-types/ConsoleApiCall");
+const CSSWarning = require("devtools/client/webconsole/components/Output/message-types/CSSWarning");
+const EvaluationResult = require("devtools/client/webconsole/components/Output/message-types/EvaluationResult");
+const PageError = require("devtools/client/webconsole/components/Output/message-types/PageError");
+
+// Test fakes.
+const {
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const serviceContainer = require("devtools/client/webconsole/test/node/fixtures/serviceContainer");
+
+describe("MessageContainer component:", () => {
+ it("pipes data to children as expected", () => {
+ const message = stubPreparedMessages.get("console.log('foobar', 'test')");
+ const rendered = render(
+ MessageContainer({
+ getMessage: () => message,
+ serviceContainer,
+ })
+ );
+
+ expect(rendered.text().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"
+ ),
+ },
+ {
+ component: CSSWarning,
+ message: stubPreparedMessages.get(
+ "Unknown property ‘such-unknown-property’. Declaration dropped."
+ ),
+ },
+ ];
+
+ messageTypes.forEach(info => {
+ const { component, message } = info;
+ expect(getMessageComponent(message)).toBe(component);
+ });
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/message-icon.test.js b/devtools/client/webconsole/test/node/components/message-icon.test.js
new file mode 100644
index 0000000000..173bf1d383
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/message-icon.test.js
@@ -0,0 +1,31 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { MESSAGE_LEVEL } = require("devtools/client/webconsole/constants");
+
+const expect = require("expect");
+const { render } = require("enzyme");
+const { createFactory } = require("devtools/client/shared/vendor/react");
+const MessageIcon = createFactory(
+ require("devtools/client/webconsole/components/Output/MessageIcon")
+);
+
+describe("MessageIcon component:", () => {
+ it("renders icon based on level", () => {
+ const rendered = render(MessageIcon({ level: MESSAGE_LEVEL.ERROR }));
+ expect(rendered.hasClass("icon")).toBe(true);
+ expect(rendered.attr("title")).toBe("Error");
+ expect(rendered.attr("aria-live")).toBe("off");
+ });
+
+ it("renders logpoint items", () => {
+ const rendered = render(
+ MessageIcon({
+ level: MESSAGE_LEVEL.LOG,
+ type: "logPoint",
+ })
+ );
+ expect(rendered.hasClass("logpoint")).toBe(true);
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/message-location.test.js b/devtools/client/webconsole/test/node/components/message-location.test.js
new file mode 100644
index 0000000000..08c164820c
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/message-location.test.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test utils.
+const expect = require("expect");
+const { mount } = require("enzyme");
+const sinon = require("sinon");
+const { createFactory } = require("devtools/client/shared/vendor/react");
+const serviceContainer = require("devtools/client/webconsole/test/node/fixtures/serviceContainer");
+
+let {
+ MessageContainer,
+} = require("devtools/client/webconsole/components/Output/MessageContainer");
+MessageContainer = createFactory(MessageContainer);
+
+// Test fakes.
+const {
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+
+describe("Message - location element", () => {
+ it("Calls onViewSourceInDebugger when clicked", () => {
+ const onViewSourceInDebugger = sinon.spy();
+ const onViewSource = sinon.spy();
+
+ const message = stubPreparedMessages.get("console.log('foobar', 'test')");
+ const wrapper = mount(
+ MessageContainer({
+ getMessage: () => message,
+ serviceContainer: Object.assign({}, serviceContainer, {
+ onViewSourceInDebugger,
+ onViewSource,
+ }),
+ })
+ );
+
+ // There should be the location
+ const locationLink = wrapper.find(`.message-location a`);
+ expect(locationLink.length).toBe(1);
+ expect(locationLink.text()).toBe("test-console-api.html:1:35");
+
+ locationLink.simulate("click");
+
+ expect(onViewSourceInDebugger.calledOnce).toBe(true);
+ expect(onViewSource.notCalled).toBe(true);
+ });
+
+ it("Calls onViewSource when clicked and onViewSourceInDebugger undefined", () => {
+ const onViewSource = sinon.spy();
+
+ const message = stubPreparedMessages.get("console.log('foobar', 'test')");
+
+ const wrapper = mount(
+ MessageContainer({
+ getMessage: () => message,
+ serviceContainer: Object.assign({}, serviceContainer, {
+ onViewSource,
+ onViewSourceInDebugger: undefined,
+ }),
+ })
+ );
+
+ // There should be the location
+ const locationLink = wrapper.find(`.message-location a`);
+
+ locationLink.simulate("click");
+ expect(onViewSource.calledOnce).toBe(true);
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/message-repeat.test.js b/devtools/client/webconsole/test/node/components/message-repeat.test.js
new file mode 100644
index 0000000000..989e95f091
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/message-repeat.test.js
@@ -0,0 +1,18 @@
+/* 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 MessageRepeat = createFactory(
+ require("devtools/client/webconsole/components/Output/MessageRepeat")
+);
+
+describe("MessageRepeat component:", () => {
+ it("renders repeated value correctly", () => {
+ const rendered = render(MessageRepeat({ repeat: 99 }));
+ expect(rendered.hasClass("message-repeats")).toBe(true);
+ expect(rendered.text()).toBe("99");
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/message-types-aria.test.js b/devtools/client/webconsole/test/node/components/message-types-aria.test.js
new file mode 100644
index 0000000000..08ceb86078
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/message-types-aria.test.js
@@ -0,0 +1,55 @@
+/* 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");
+const { createFactory } = require("devtools/client/shared/vendor/react");
+
+// Components under test.
+const ConsoleApiCall = createFactory(
+ require("devtools/client/webconsole/components/Output/message-types/ConsoleApiCall")
+);
+const ConsoleCmd = createFactory(
+ require("devtools/client/webconsole/components/Output/message-types/ConsoleCommand")
+);
+const EvaluationResult = createFactory(
+ require("devtools/client/webconsole/components/Output/message-types/EvaluationResult")
+);
+
+const { ConsoleCommand } = require("devtools/client/webconsole/types");
+
+// Test fakes.
+const {
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const serviceContainer = require("devtools/client/webconsole/test/node/fixtures/serviceContainer");
+
+describe("message types component ARIA:", () => {
+ describe("ConsoleAPICall", () => {
+ it("sets aria-live to polite", () => {
+ const message = stubPreparedMessages.get("console.log('foobar', 'test')");
+ const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
+ expect(wrapper.attr("aria-live")).toBe("polite");
+ });
+ });
+
+ describe("EvaluationResult", () => {
+ it("sets aria-live to polite", () => {
+ const message = stubPreparedMessages.get("asdf()");
+ const wrapper = render(EvaluationResult({ message, serviceContainer }));
+ expect(wrapper.attr("aria-live")).toBe("polite");
+ });
+ });
+
+ describe("ConsoleCommand", () => {
+ it("sets aria-live to off", () => {
+ const message = new ConsoleCommand({
+ messageText: `"simple"`,
+ });
+ const wrapper = render(ConsoleCmd({ message, serviceContainer }));
+ expect(wrapper.attr("aria-live")).toBe("off");
+ });
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/network-event-message.test.js b/devtools/client/webconsole/test/node/components/network-event-message.test.js
new file mode 100644
index 0000000000..afcbf2cfac
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/network-event-message.test.js
@@ -0,0 +1,152 @@
+/* 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/components/Output/message-types/NetworkEventMessage")
+);
+const {
+ INDENT_WIDTH,
+} = require("devtools/client/webconsole/components/Output/MessageIndent");
+
+// Test fakes.
+const {
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const serviceContainer = require("devtools/client/webconsole/test/node/fixtures/serviceContainer");
+
+const EXPECTED_URL = "http://example.com/inexistent.html";
+const EXPECTED_STATUS = /\[HTTP\/\d\.\d \d+ [A-Za-z ]+ \d+ms\]/;
+
+describe("NetworkEventMessage component:", () => {
+ describe("GET request", () => {
+ it("renders as expected", () => {
+ const message = stubPreparedMessages.get("GET request");
+ const update = stubPreparedMessages.get("GET request update");
+ const wrapper = render(
+ NetworkEventMessage({
+ message,
+ serviceContainer,
+ timestampsVisible: true,
+ networkMessageUpdate: update,
+ })
+ );
+ const {
+ timestampString,
+ } = require("devtools/client/webconsole/utils/l10n");
+
+ expect(wrapper.find(".timestamp").text()).toBe(
+ timestampString(message.timeStamp)
+ );
+ 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(".message-body .status").length).toBe(1);
+ expect(wrapper.find(".message-body .status").text()).toMatch(
+ EXPECTED_STATUS
+ );
+ });
+
+ it("does not have a timestamp when timestampsVisible prop is falsy", () => {
+ const message = stubPreparedMessages.get("GET request update");
+ const wrapper = render(
+ NetworkEventMessage({
+ message,
+ serviceContainer,
+ timestampsVisible: false,
+ })
+ );
+
+ expect(wrapper.find(".timestamp").length).toBe(0);
+ });
+
+ it("has the expected indent", () => {
+ const message = stubPreparedMessages.get("GET request");
+
+ const indent = 10;
+ let wrapper = render(
+ NetworkEventMessage({
+ message: Object.assign({}, message, { indent }),
+ serviceContainer,
+ })
+ );
+ let indentEl = wrapper.find(".indent");
+ expect(indentEl.prop("style").width).toBe(`${indent * INDENT_WIDTH}px`);
+ expect(indentEl.prop("data-indent")).toBe(`${indent}`);
+
+ wrapper = render(NetworkEventMessage({ message, serviceContainer }));
+ indentEl = wrapper.find(".indent");
+ expect(indentEl.prop("style").width).toBe(`0`);
+ expect(indentEl.prop("data-indent")).toBe(`0`);
+ });
+ });
+
+ describe("XHR GET request", () => {
+ it("renders as expected", () => {
+ const message = stubPreparedMessages.get("XHR GET request");
+ const update = stubPreparedMessages.get("XHR GET request update");
+ const wrapper = render(
+ NetworkEventMessage({
+ message,
+ serviceContainer,
+ networkMessageUpdate: update,
+ })
+ );
+
+ 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(".message-body .status").text()).toMatch(
+ EXPECTED_STATUS
+ );
+ });
+ });
+
+ describe("XHR POST request", () => {
+ it("renders as expected", () => {
+ const message = stubPreparedMessages.get("XHR POST request");
+ const update = stubPreparedMessages.get("XHR POST request update");
+ const wrapper = render(
+ NetworkEventMessage({
+ message,
+ serviceContainer,
+ networkMessageUpdate: update,
+ })
+ );
+
+ 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(".message-body .status").length).toBe(1);
+ expect(wrapper.find(".message-body .status").text()).toMatch(
+ EXPECTED_STATUS
+ );
+ });
+ });
+
+ describe("is expandable", () => {
+ it("renders as expected", () => {
+ const message = stubPreparedMessages.get("XHR POST request");
+ const wrapper = render(
+ NetworkEventMessage({
+ message,
+ serviceContainer,
+ })
+ );
+
+ expect(wrapper.find(".message .theme-twisty")).toExist();
+ });
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/page-error.test.js b/devtools/client/webconsole/test/node/components/page-error.test.js
new file mode 100644
index 0000000000..5a510fb9bc
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/page-error.test.js
@@ -0,0 +1,531 @@
+/* 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/test/node/helpers");
+const { prepareMessage } = require("devtools/client/webconsole/utils/messages");
+
+// Components under test.
+const PageError = require("devtools/client/webconsole/components/Output/message-types/PageError");
+const {
+ MESSAGE_OPEN,
+ MESSAGE_CLOSE,
+} = require("devtools/client/webconsole/constants");
+const {
+ INDENT_WIDTH,
+} = require("devtools/client/webconsole/components/Output/MessageIndent");
+
+// Test fakes.
+const {
+ stubPackets,
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const serviceContainer = require("devtools/client/webconsole/test/node/fixtures/serviceContainer");
+
+describe("PageError component:", () => {
+ it("renders", () => {
+ const message = stubPreparedMessages.get(
+ "ReferenceError: asdf is not defined"
+ );
+ const wrapper = render(
+ PageError({
+ message,
+ serviceContainer,
+ timestampsVisible: true,
+ })
+ );
+ const {
+ timestampString,
+ } = require("devtools/client/webconsole/utils/l10n");
+
+ expect(wrapper.find(".timestamp").text()).toBe(
+ timestampString(message.timeStamp)
+ );
+
+ expect(wrapper.find(".message-body").text()).toBe(
+ "Uncaught 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 bug 1307952
+ expect(locationLink.text()).toBe("test-console-api.html:3:5");
+ });
+
+ it("does not have a timestamp when timestampsVisible prop is falsy", () => {
+ const message = stubPreparedMessages.get(
+ "ReferenceError: asdf is not defined"
+ );
+ const wrapper = render(
+ PageError({
+ message,
+ serviceContainer,
+ timestampsVisible: false,
+ })
+ );
+
+ expect(wrapper.find(".timestamp").length).toBe(0);
+ });
+
+ it("renders an error with a longString exception message", () => {
+ const message = stubPreparedMessages.get("TypeError longString message");
+ const wrapper = render(PageError({ message, serviceContainer }));
+
+ const text = wrapper.find(".message-body").text();
+ expect(text.startsWith("Uncaught Error: Long error Long error")).toBe(true);
+ });
+
+ it("renders thrown empty string", () => {
+ const message = stubPreparedMessages.get(`throw ""`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe("Uncaught <empty string>");
+ });
+
+ it("renders thrown string", () => {
+ const message = stubPreparedMessages.get(`throw "tomato"`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught tomato`);
+ });
+
+ it("renders thrown boolean", () => {
+ const message = stubPreparedMessages.get(`throw false`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught false`);
+ });
+
+ it("renders thrown number ", () => {
+ const message = stubPreparedMessages.get(`throw 0`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught 0`);
+ });
+
+ it("renders thrown null", () => {
+ const message = stubPreparedMessages.get(`throw null`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught null`);
+ });
+
+ it("renders thrown undefined", () => {
+ const message = stubPreparedMessages.get(`throw undefined`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught undefined`);
+ });
+
+ it("renders thrown Symbol", () => {
+ const message = stubPreparedMessages.get(`throw Symbol`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught Symbol(potato)`);
+ });
+
+ it("renders thrown object", () => {
+ const message = stubPreparedMessages.get(`throw Object`);
+
+ // We need to wrap the PageError in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ PageError({ message, serviceContainer })
+ )
+ );
+
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught Object { vegetable: "cucumber" }`);
+ });
+
+ it("renders thrown error", () => {
+ const message = stubPreparedMessages.get(`throw Error Object`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught Error: pumpkin`);
+ });
+
+ it("renders thrown Error with custom name", () => {
+ const message = stubPreparedMessages.get(
+ `throw Error Object with custom name`
+ );
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught JuicyError: pineapple`);
+ });
+
+ it("renders uncaught rejected Promise with empty string", () => {
+ const message = stubPreparedMessages.get(`Promise reject ""`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe("Uncaught (in promise) <empty string>");
+ });
+
+ it("renders uncaught rejected Promise with string", () => {
+ const message = stubPreparedMessages.get(`Promise reject "tomato"`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught (in promise) tomato`);
+ });
+
+ it("renders uncaught rejected Promise with boolean", () => {
+ const message = stubPreparedMessages.get(`Promise reject false`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught (in promise) false`);
+ });
+
+ it("renders uncaught rejected Promise with number ", () => {
+ const message = stubPreparedMessages.get(`Promise reject 0`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught (in promise) 0`);
+ });
+
+ it("renders uncaught rejected Promise with null", () => {
+ const message = stubPreparedMessages.get(`Promise reject null`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught (in promise) null`);
+ });
+
+ it("renders uncaught rejected Promise with undefined", () => {
+ const message = stubPreparedMessages.get(`Promise reject undefined`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught (in promise) undefined`);
+ });
+
+ it("renders uncaught rejected Promise with Symbol", () => {
+ const message = stubPreparedMessages.get(`Promise reject Symbol`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught (in promise) Symbol(potato)`);
+ });
+
+ it("renders uncaught rejected Promise with object", () => {
+ const message = stubPreparedMessages.get(`Promise reject Object`);
+ // We need to wrap the PageError in a Provider in order for the
+ // ObjectInspector to work.
+ const wrapper = render(
+ Provider(
+ { store: setupStore() },
+ PageError({ message, serviceContainer })
+ )
+ );
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught (in promise) Object { vegetable: "cucumber" }`);
+ });
+
+ it("renders uncaught rejected Promise with error", () => {
+ const message = stubPreparedMessages.get(`Promise reject Error Object`);
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught (in promise) Error: pumpkin`);
+ });
+
+ it("renders uncaught rejected Promise with Error with custom name", () => {
+ const message = stubPreparedMessages.get(
+ `Promise reject Error Object with custom name`
+ );
+ const wrapper = render(PageError({ message, serviceContainer }));
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(`Uncaught (in promise) JuicyError: pineapple`);
+ });
+
+ it("renders URLs in message as actual, cropped, links", () => {
+ // Let's replace the packet data in order to mimick a pageError.
+ const packet = stubPackets.get("throw string with URL");
+
+ const evilDomain = `https://evil.com/?`;
+ const badDomain = `https://not-so-evil.com/?`;
+ const paramLength = 200;
+ const longParam = "a".repeat(paramLength);
+
+ const evilURL = `${evilDomain}${longParam}`;
+ const badURL = `${badDomain}${longParam}`;
+
+ // We remove the exceptionDocURL to not have the "learn more" link.
+ packet.pageError.exceptionDocURL = null;
+
+ const message = prepareMessage(packet, { getNextId: () => "1" });
+ const wrapper = render(PageError({ message, serviceContainer }));
+
+ // Keep in sync with `urlCropLimit` in PageError.js.
+ const cropLimit = 120;
+ const partLength = cropLimit / 2;
+ const getCroppedUrl = url =>
+ `${url}${"a".repeat(partLength - url.length)}…${"a".repeat(partLength)}`;
+
+ const croppedEvil = getCroppedUrl(evilDomain);
+ const croppedbad = getCroppedUrl(badDomain);
+
+ const text = wrapper.find(".message-body").text();
+ expect(text).toBe(
+ `Uncaught “${croppedEvil}“ is evil and “${croppedbad}“ is not good either`
+ );
+
+ // There should be 2 links.
+ const links = wrapper.find(".message-body a");
+ expect(links.length).toBe(2);
+
+ expect(links.eq(0).attr("href")).toBe(evilURL);
+ expect(links.eq(0).attr("title")).toBe(evilURL);
+
+ expect(links.eq(1).attr("href")).toBe(badURL);
+ expect(links.eq(1).attr("title")).toBe(badURL);
+ });
+
+ 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,
+ dispatch: () => {},
+ })
+ )
+ );
+
+ // 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");
+ const call = serviceContainer.openLink.getCall(0);
+ expect(call.args[0]).toEqual(message.exceptionDocURL);
+ });
+
+ // Unskip will happen in Bug 1529548.
+ it.skip("has a stacktrace which can be opened", () => {
+ 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(".collapse-button[aria-expanded=true]").length).toBe(1);
+
+ // There should be five stacktrace items.
+ const frameLinks = wrapper.find(`.stack-trace span.frame-link`);
+ expect(frameLinks.length).toBe(5);
+ });
+
+ // Unskip will happen in Bug 1529548.
+ it.skip("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(".collapse-button[aria-expanded='true']").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(".collapse-button[aria-expanded='false']").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: Object.assign({}, message, { indent }),
+ serviceContainer,
+ })
+ );
+ let indentEl = wrapper.find(".indent");
+ expect(indentEl.prop("style").width).toBe(`${indent * INDENT_WIDTH}px`);
+ expect(indentEl.prop("data-indent")).toBe(`${indent}`);
+
+ wrapper = render(PageError({ message, serviceContainer }));
+ indentEl = wrapper.find(".indent");
+ expect(indentEl.prop("style").width).toBe(`0`);
+ expect(indentEl.prop("data-indent")).toBe(`0`);
+ });
+
+ it("has empty error notes", () => {
+ const message = stubPreparedMessages.get(
+ "ReferenceError: asdf is not defined"
+ );
+ const wrapper = render(PageError({ message, serviceContainer }));
+
+ const notes = wrapper.find(".error-note");
+
+ expect(notes.length).toBe(0);
+ });
+
+ it("can show an error note", () => {
+ const origMessage = stubPreparedMessages.get(
+ "ReferenceError: asdf is not defined"
+ );
+ const message = Object.assign({}, origMessage, {
+ notes: [
+ {
+ messageBody: "test note",
+ frame: {
+ source: "http://example.com/test.js",
+ line: 2,
+ column: 6,
+ },
+ },
+ ],
+ });
+
+ const wrapper = render(PageError({ message, serviceContainer }));
+
+ const notes = wrapper.find(".error-note");
+ expect(notes.length).toBe(1);
+
+ const note = notes.eq(0);
+ expect(note.find(".message-body").text()).toBe("note: test note");
+
+ // There should be the location.
+ const locationLink = note.find(`.message-location`);
+ expect(locationLink.length).toBe(1);
+ expect(locationLink.text()).toBe("test.js:2:6");
+ });
+
+ it("can show multiple error notes", () => {
+ const origMessage = stubPreparedMessages.get(
+ "ReferenceError: asdf is not defined"
+ );
+ const message = Object.assign({}, origMessage, {
+ notes: [
+ {
+ messageBody: "test note 1",
+ frame: {
+ source: "http://example.com/test1.js",
+ line: 2,
+ column: 6,
+ },
+ },
+ {
+ messageBody: "test note 2",
+ frame: {
+ source: "http://example.com/test2.js",
+ line: 10,
+ column: 18,
+ },
+ },
+ {
+ messageBody: "test note 3",
+ frame: {
+ source: "http://example.com/test3.js",
+ line: 9,
+ column: 4,
+ },
+ },
+ ],
+ });
+
+ const wrapper = render(PageError({ message, serviceContainer }));
+
+ const notes = wrapper.find(".error-note");
+ expect(notes.length).toBe(3);
+
+ const note1 = notes.eq(0);
+ expect(note1.find(".message-body").text()).toBe("note: test note 1");
+
+ const locationLink1 = note1.find(`.message-location`);
+ expect(locationLink1.length).toBe(1);
+ expect(locationLink1.text()).toBe("test1.js:2:6");
+
+ const note2 = notes.eq(1);
+ expect(note2.find(".message-body").text()).toBe("note: test note 2");
+
+ const locationLink2 = note2.find(`.message-location`);
+ expect(locationLink2.length).toBe(1);
+ expect(locationLink2.text()).toBe("test2.js:10:18");
+
+ const note3 = notes.eq(2);
+ expect(note3.find(".message-body").text()).toBe("note: test note 3");
+
+ const locationLink3 = note3.find(`.message-location`);
+ expect(locationLink3.length).toBe(1);
+ expect(locationLink3.text()).toBe("test3.js:9:4");
+ });
+
+ it("displays error notes", () => {
+ const message = stubPreparedMessages.get(
+ "SyntaxError: redeclaration of let a"
+ );
+
+ const wrapper = render(PageError({ message, serviceContainer }));
+
+ const notes = wrapper.find(".error-note");
+ expect(notes.length).toBe(1);
+
+ const note = notes.eq(0);
+ expect(note.find(".message-body").text()).toBe(
+ "note: Previously declared at line 2, column 6"
+ );
+
+ // There should be the location.
+ const locationLink = note.find(`.message-location`);
+ expect(locationLink.length).toBe(1);
+ expect(locationLink.text()).toBe("test-console-api.html:2:6");
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/warning-group.test.js b/devtools/client/webconsole/test/node/components/warning-group.test.js
new file mode 100644
index 0000000000..71b8ae13a4
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/warning-group.test.js
@@ -0,0 +1,105 @@
+/* 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");
+
+// Components under test.
+const WarningGroup = require("devtools/client/webconsole/components/Output/message-types/WarningGroup");
+const {
+ MESSAGE_SOURCE,
+ MESSAGE_TYPE,
+} = require("devtools/client/webconsole/constants");
+const { ConsoleMessage } = require("devtools/client/webconsole/types");
+const {
+ createWarningGroupMessage,
+} = require("devtools/client/webconsole/utils/messages");
+
+// Test fakes.
+const {
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const serviceContainer = require("devtools/client/webconsole/test/node/fixtures/serviceContainer");
+const mockMessage = ConsoleMessage({
+ messageText: "this is a warning group",
+ source: MESSAGE_SOURCE.CONSOLE_FRONTEND,
+ timeStamp: Date.now(),
+});
+
+describe("WarningGroup component:", () => {
+ it("renders", () => {
+ const wrapper = render(
+ WarningGroup({
+ message: mockMessage,
+ serviceContainer,
+ timestampsVisible: true,
+ badge: 42,
+ })
+ );
+
+ const {
+ timestampString,
+ } = require("devtools/client/webconsole/utils/l10n");
+ expect(wrapper.find(".timestamp").text()).toBe(
+ timestampString(mockMessage.timeStamp)
+ );
+ expect(wrapper.find(".message-body").text()).toBe(
+ "this is a warning group 42"
+ );
+ expect(wrapper.find(".arrow[aria-expanded=false]")).toExist();
+ });
+
+ it("does have an expanded arrow when `open` prop is true", () => {
+ const wrapper = render(
+ WarningGroup({
+ message: mockMessage,
+ serviceContainer,
+ open: true,
+ })
+ );
+
+ expect(wrapper.find(".arrow[aria-expanded=true]")).toExist();
+ });
+
+ it("does not have a timestamp when timestampsVisible prop is falsy", () => {
+ const wrapper = render(
+ WarningGroup({
+ message: mockMessage,
+ serviceContainer,
+ timestampsVisible: false,
+ })
+ );
+
+ expect(wrapper.find(".timestamp").length).toBe(0);
+ });
+
+ it("renders Content Blocking Group message", () => {
+ const firstMessage = stubPreparedMessages.get(
+ "ReferenceError: asdf is not defined"
+ );
+ firstMessage.messageText =
+ "The resource at “https://evil.com” was blocked.";
+ firstMessage.category = "cookieBlockedPermission";
+ const type = MESSAGE_TYPE.CONTENT_BLOCKING_GROUP;
+ const message = createWarningGroupMessage(
+ `${firstMessage.type}-${firstMessage.innerWindowID}`,
+ type,
+ firstMessage
+ );
+
+ const wrapper = render(
+ WarningGroup({
+ message,
+ serviceContainer,
+ badge: 24,
+ })
+ );
+
+ expect(wrapper.find(".message-body").text()).toBe(
+ "The resource at “<URL>” was blocked. 24"
+ );
+ expect(wrapper.find(".arrow[aria-expanded=false]")).toExist();
+ });
+});
diff --git a/devtools/client/webconsole/test/node/components/webconsole-wrapper.test.js b/devtools/client/webconsole/test/node/components/webconsole-wrapper.test.js
new file mode 100644
index 0000000000..b750d43ab0
--- /dev/null
+++ b/devtools/client/webconsole/test/node/components/webconsole-wrapper.test.js
@@ -0,0 +1,141 @@
+/* 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/. */
+
+"use strict";
+
+// Test utils.
+const expect = require("expect");
+const {
+ stubPackets,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const {
+ clonePacket,
+ getMessageAt,
+ getPrivatePacket,
+ getWebConsoleUiMock,
+} = require("devtools/client/webconsole/test/node/helpers");
+
+const WebConsoleWrapper = require("devtools/client/webconsole/webconsole-wrapper");
+const { messagesAdd } = require("devtools/client/webconsole/actions/messages");
+
+async function getWebConsoleWrapper() {
+ const hud = {
+ currentTarget: { client: {}, getFront: () => {} },
+ getMappedExpression: () => {},
+ };
+ const webConsoleUi = getWebConsoleUiMock(hud);
+
+ const wcow = new WebConsoleWrapper(null, webConsoleUi, null, null);
+ await wcow.init();
+ return wcow;
+}
+
+describe("WebConsoleWrapper", () => {
+ it("clears queues when dispatchMessagesClear is called", async () => {
+ const ncow = await getWebConsoleWrapper();
+ ncow.queuedMessageAdds.push({ fakePacket: "message" });
+ ncow.queuedMessageUpdates.push({ fakePacket: "message-update" });
+ ncow.queuedRequestUpdates.push({ fakePacket: "request-update" });
+
+ ncow.dispatchMessagesClear();
+
+ expect(ncow.queuedMessageAdds.length).toBe(0);
+ expect(ncow.queuedMessageUpdates.length).toBe(0);
+ expect(ncow.queuedRequestUpdates.length).toBe(0);
+ });
+
+ it("removes private packets from message queue on dispatchPrivateMessagesClear", async () => {
+ const ncow = await getWebConsoleWrapper();
+
+ const publicLog = stubPackets.get("console.log('mymap')");
+ ncow.queuedMessageAdds.push(
+ getPrivatePacket("console.trace()"),
+ publicLog,
+ getPrivatePacket("XHR POST request")
+ );
+
+ ncow.dispatchPrivateMessagesClear();
+ expect(ncow.queuedMessageAdds).toEqual([publicLog]);
+ });
+
+ it("removes private packets from network update queue on dispatchPrivateMessagesClear", async () => {
+ const ncow = await getWebConsoleWrapper();
+ const publicLog = stubPackets.get("console.log('mymap')");
+ ncow.queuedMessageAdds.push(
+ getPrivatePacket("console.trace()"),
+ publicLog,
+ getPrivatePacket("XHR POST request")
+ );
+
+ const postId = "pid1";
+ const getId = "gid1";
+
+ // Add messages in the store to make sure that update to private requests are
+ // removed from the queue.
+ ncow
+ .getStore()
+ .dispatch(
+ messagesAdd([
+ stubPackets.get("GET request"),
+ { ...getPrivatePacket("XHR GET request"), actor: getId },
+ ])
+ );
+
+ // Add packet to the message queue to make sure that update to private requests are
+ // removed from the queue.
+ ncow.queuedMessageAdds.push({
+ ...getPrivatePacket("XHR POST request"),
+ actor: postId,
+ });
+
+ const publicNetworkUpdate = stubPackets.get("GET request update");
+ ncow.queuedMessageUpdates.push(
+ publicNetworkUpdate,
+ {
+ ...getPrivatePacket("XHR GET request update"),
+ actor: getId,
+ },
+ {
+ ...getPrivatePacket("XHR POST request update"),
+ actor: postId,
+ }
+ );
+
+ ncow.dispatchPrivateMessagesClear();
+
+ expect(ncow.queuedMessageUpdates.length).toBe(1);
+ expect(ncow.queuedMessageUpdates).toEqual([publicNetworkUpdate]);
+ });
+
+ it("removes private packets from network request queue on dispatchPrivateMessagesClear", async () => {
+ const ncow = await getWebConsoleWrapper();
+
+ const packet1 = clonePacket(stubPackets.get("GET request"));
+ const packet2 = clonePacket(getPrivatePacket("XHR GET request"));
+ const packet3 = clonePacket(getPrivatePacket("XHR POST request"));
+
+ // We need to reassign the timeStamp of the packet to guarantee the order.
+ packet1.timeStamp = packet1.timeStamp + 1;
+ packet2.timeStamp = packet2.timeStamp + 2;
+ packet3.timeStamp = packet3.timeStamp + 3;
+
+ ncow.getStore().dispatch(messagesAdd([packet1, packet2, packet3]));
+
+ const state = ncow.getStore().getState();
+ const publicId = getMessageAt(state, 0).id;
+ const privateXhrGetId = getMessageAt(state, 1).id;
+ const privateXhrPostId = getMessageAt(state, 2).id;
+ ncow.queuedRequestUpdates.push(
+ { id: publicId },
+ { id: privateXhrGetId },
+ { id: privateXhrPostId }
+ );
+ // ncow.queuedRequestUpdates.push({fakePacket: "request-update"});
+
+ ncow.dispatchPrivateMessagesClear();
+
+ expect(ncow.queuedRequestUpdates.length).toBe(1);
+ expect(ncow.queuedRequestUpdates).toEqual([{ id: publicId }]);
+ });
+});
diff --git a/devtools/client/webconsole/test/node/fixtures/.eslintrc.js b/devtools/client/webconsole/test/node/fixtures/.eslintrc.js
new file mode 100644
index 0000000000..56bfc52fb1
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/.eslintrc.js
@@ -0,0 +1,6 @@
+"use strict";
+
+module.exports = {
+ // Extend from the shared list of defined globals for mochitests.
+ extends: "../../../../../.eslintrc.mochitests.js",
+};
diff --git a/devtools/client/webconsole/test/node/fixtures/DevToolsUtils.js b/devtools/client/webconsole/test/node/fixtures/DevToolsUtils.js
new file mode 100644
index 0000000000..5be367a60e
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/DevToolsUtils.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+module.exports = {
+ getTopWindow: () => ({}),
+};
diff --git a/devtools/client/webconsole/test/node/fixtures/WebConsoleUtils.js b/devtools/client/webconsole/test/node/fixtures/WebConsoleUtils.js
new file mode 100644
index 0000000000..17714bee64
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/WebConsoleUtils.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const L10n = require("devtools/client/webconsole/test/node/fixtures/L10n");
+
+const Utils = {
+ L10n,
+ supportsString: function(s) {
+ return s;
+ },
+};
+
+module.exports = {
+ Utils,
+};
diff --git a/devtools/client/webconsole/test/node/fixtures/async-storage.js b/devtools/client/webconsole/test/node/fixtures/async-storage.js
new file mode 100644
index 0000000000..35ed2f594f
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/async-storage.js
@@ -0,0 +1,13 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+module.exports = {
+ getItem: () => Promise.resolve(),
+ setItem: () => Promise.resolve(),
+ removeItem: () => Promise.resolve(),
+ clear: () => Promise.resolve(),
+ length: () => Promise.resolve(),
+ key: () => Promise.resolve(),
+};
diff --git a/devtools/client/webconsole/test/node/fixtures/serviceContainer.js b/devtools/client/webconsole/test/node/fixtures/serviceContainer.js
new file mode 100644
index 0000000000..f78712c3aa
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/serviceContainer.js
@@ -0,0 +1,31 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+module.exports = {
+ attachRefToWebConsoleUI: () => {},
+ emitForTests: () => {},
+ proxy: {
+ client: {},
+ releaseActor: actor => console.log("Release actor", actor),
+ },
+ onViewSourceInDebugger: () => {},
+ onViewSourceInStyleEditor: () => {},
+ openNetworkPanel: () => {},
+ resendNetworkRequest: () => {},
+ sourceMapURLService: {
+ subscribeByURL: () => {
+ return () => {};
+ },
+ subscribeByID: () => {
+ return () => {};
+ },
+ subscribeByLocation: () => {
+ return () => {};
+ },
+ },
+ openLink: () => {},
+ // eslint-disable-next-line react/display-name
+ createElement: tagName => document.createElement(tagName),
+};
diff --git a/devtools/client/webconsole/test/node/fixtures/stubs/browser_dummy.js b/devtools/client/webconsole/test/node/fixtures/stubs/browser_dummy.js
new file mode 100644
index 0000000000..8a9353cd7e
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/stubs/browser_dummy.js
@@ -0,0 +1,11 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This file is a fake test so we can have support files in the stubs.ini, which are then
+// referenced as support files in the webconsole mochitest ini file.
+
+"use strict";
+
+add_task(function() {
+ ok(true, "this is not a test");
+});
diff --git a/devtools/client/webconsole/test/node/fixtures/stubs/consoleApi.js b/devtools/client/webconsole/test/node/fixtures/stubs/consoleApi.js
new file mode 100644
index 0000000000..d40621cc34
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/stubs/consoleApi.js
@@ -0,0 +1,2042 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint-disable max-len */
+
+"use strict";
+
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
+ */
+
+const {
+ parsePacketsWithFronts,
+} = require("chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/stub-generator-helpers");
+const { prepareMessage } = require("devtools/client/webconsole/utils/messages");
+const {
+ ConsoleMessage,
+ NetworkEventMessage,
+} = require("devtools/client/webconsole/types");
+
+const rawPackets = new Map();
+rawPackets.set(`console.log('foobar', 'test')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "foobar",
+ "test"
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log(undefined)`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ {
+ "type": "undefined"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.warn('danger, will robinson!')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "danger, will robinson!"
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "warn",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log(NaN)`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ {
+ "type": "NaN"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log(null)`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ {
+ "type": "null"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log('鼬')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "鼬"
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.clear()`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "clear",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.count('bar')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "bar"
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": {
+ "count": 1,
+ "label": "bar"
+ },
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "count",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.assert(false, {message: 'foobar'})`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj31",
+ "class": "Object",
+ "ownPropertyLength": 1,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "message": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "foobar"
+ }
+ },
+ "ownSymbols": [],
+ "ownPropertiesLength": 1,
+ "ownSymbolsLength": 0,
+ "safeGetterValues": {}
+ }
+ },
+ "actorID": "server0.conn0.child1/obj31"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "assert",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source30",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "stacktrace": [
+ {
+ "columnNumber": 35,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "lineNumber": 1,
+ "sourceId": "server0.conn0.child1/source30"
+ }
+ ],
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log('úṇĩçödê țĕșť')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "úṇĩçödê țĕșť"
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.dirxml(window)`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj34",
+ "class": "Window",
+ "ownPropertyLength": 818,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "ObjectWithURL",
+ "url": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html"
+ }
+ },
+ "actorID": "server0.conn0.child1/obj34"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "dirxml",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log('myarray', ['red', 'green', 'blue'])`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "myarray",
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj36",
+ "class": "Array",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "ArrayLike",
+ "length": 3,
+ "items": [
+ "red",
+ "green",
+ "blue"
+ ]
+ }
+ },
+ "actorID": "server0.conn0.child1/obj36"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log('myregex', /a.b.c/)`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "myregex",
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj38",
+ "class": "RegExp",
+ "ownPropertyLength": 1,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "displayString": "/a.b.c/"
+ },
+ "actorID": "server0.conn0.child1/obj38"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.table(['red', 'green', 'blue']);`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj40",
+ "class": "Array",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": null,
+ "ownProperties": {
+ "0": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "red"
+ },
+ "1": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "green"
+ },
+ "2": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "blue"
+ }
+ }
+ },
+ "actorID": "server0.conn0.child1/obj40"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "table",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log('myobject', {red: 'redValue', green: 'greenValue', blue: 'blueValue'});`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "myobject",
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj42",
+ "class": "Object",
+ "ownPropertyLength": 3,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "red": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "redValue"
+ },
+ "green": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "greenValue"
+ },
+ "blue": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "blueValue"
+ }
+ },
+ "ownSymbols": [],
+ "ownPropertiesLength": 3,
+ "ownSymbolsLength": 0,
+ "safeGetterValues": {}
+ }
+ },
+ "actorID": "server0.conn0.child1/obj42"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.debug('debug message');`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "debug message"
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "debug",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.info('info message');`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "info message"
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "info",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.error('error message');`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "error message"
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "error",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source30",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "stacktrace": [
+ {
+ "columnNumber": 35,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "lineNumber": 1,
+ "sourceId": "server0.conn0.child1/source30"
+ }
+ ],
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log('mymap')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "mymap",
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj47",
+ "class": "Map",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "MapLike",
+ "size": 2,
+ "entries": [
+ [
+ "key1",
+ "value1"
+ ],
+ [
+ "key2",
+ "value2"
+ ]
+ ]
+ }
+ },
+ "actorID": "server0.conn0.child1/obj47"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 5,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source46",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log('myset')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "myset",
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj49",
+ "class": "Set",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "ArrayLike",
+ "length": 2,
+ "items": [
+ "a",
+ "b"
+ ]
+ }
+ },
+ "actorID": "server0.conn0.child1/obj49"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source48",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.trace()`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [],
+ "chromeContext": false,
+ "columnNumber": 13,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "testStacktraceFiltering",
+ "groupName": "",
+ "level": "trace",
+ "lineNumber": 3,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source50",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "stacktrace": [
+ {
+ "columnNumber": 13,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "testStacktraceFiltering",
+ "lineNumber": 3,
+ "sourceId": "server0.conn0.child1/source50"
+ },
+ {
+ "columnNumber": 5,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "foo",
+ "lineNumber": 6,
+ "sourceId": "server0.conn0.child1/source50"
+ },
+ {
+ "columnNumber": 3,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "lineNumber": 9,
+ "sourceId": "server0.conn0.child1/source50"
+ }
+ ],
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.trace('bar', {'foo': 'bar'}, [1,2,3])`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "bar",
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj52",
+ "class": "Object",
+ "ownPropertyLength": 1,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "foo": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "bar"
+ }
+ },
+ "ownSymbols": [],
+ "ownPropertiesLength": 1,
+ "ownSymbolsLength": 0,
+ "safeGetterValues": {}
+ }
+ },
+ "actorID": "server0.conn0.child1/obj52"
+ },
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj53",
+ "class": "Array",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "ArrayLike",
+ "length": 3,
+ "items": [
+ 1,
+ 2,
+ 3
+ ]
+ }
+ },
+ "actorID": "server0.conn0.child1/obj53"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 13,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "testStacktraceWithLog",
+ "groupName": "",
+ "level": "trace",
+ "lineNumber": 3,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source51",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "stacktrace": [
+ {
+ "columnNumber": 13,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "testStacktraceWithLog",
+ "lineNumber": 3,
+ "sourceId": "server0.conn0.child1/source51"
+ },
+ {
+ "columnNumber": 5,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "foo",
+ "lineNumber": 6,
+ "sourceId": "server0.conn0.child1/source50"
+ },
+ {
+ "columnNumber": 3,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "lineNumber": 9,
+ "sourceId": "server0.conn0.child1/source50"
+ }
+ ],
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.trace("%cHello%c|%cWorld")`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "Hello",
+ "|",
+ "World"
+ ],
+ "chromeContext": false,
+ "columnNumber": 13,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "trace",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child3/source57",
+ "styles": [
+ "color:red",
+ "",
+ "color: blue"
+ ],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "stacktrace": [
+ {
+ "columnNumber": 13,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "lineNumber": 2,
+ "sourceId": "server0.conn0.child3/source57"
+ }
+ ],
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 10737418241
+ }
+});
+
+rawPackets.set(`console.time('bar')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "bar"
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "time",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source48",
+ "timeStamp": 1572867483805,
+ "timer": {
+ "name": "bar"
+ },
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`timerAlreadyExists`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "bar"
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "time",
+ "lineNumber": 3,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source54",
+ "timeStamp": 1572867483805,
+ "timer": {
+ "error": "timerAlreadyExists",
+ "name": "bar"
+ },
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.timeLog('bar') - 1`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "bar"
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "timeLog",
+ "lineNumber": 4,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source54",
+ "timeStamp": 1572867483805,
+ "timer": {
+ "duration": 4,
+ "name": "bar"
+ },
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.timeLog('bar') - 2`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "bar",
+ "second call",
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj55",
+ "class": "Object",
+ "ownPropertyLength": 1,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "state": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": 1
+ }
+ },
+ "ownSymbols": [],
+ "ownPropertiesLength": 1,
+ "ownSymbolsLength": 0,
+ "safeGetterValues": {}
+ }
+ },
+ "actorID": "server0.conn0.child1/obj55"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "timeLog",
+ "lineNumber": 5,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source46",
+ "timeStamp": 1572867483805,
+ "timer": {
+ "duration": 5,
+ "name": "bar"
+ },
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.timeEnd('bar')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "bar"
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "timeEnd",
+ "lineNumber": 6,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source54",
+ "timeStamp": 1572867483805,
+ "timer": {
+ "duration": 9,
+ "name": "bar"
+ },
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`timeEnd.timerDoesntExist`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "bar"
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "timeEnd",
+ "lineNumber": 7,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source54",
+ "timeStamp": 1572867483805,
+ "timer": {
+ "error": "timerDoesntExist",
+ "name": "bar"
+ },
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`timeLog.timerDoesntExist`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "bar"
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "timeLog",
+ "lineNumber": 8,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source54",
+ "timeStamp": 1572867483805,
+ "timer": {
+ "error": "timerDoesntExist",
+ "name": "bar"
+ },
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.table('bar')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "bar"
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "table",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source48",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.table(['a', 'b', 'c'])`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj58",
+ "class": "Array",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": null,
+ "ownProperties": {
+ "0": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "a"
+ },
+ "1": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "b"
+ },
+ "2": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "c"
+ }
+ }
+ },
+ "actorID": "server0.conn0.child1/obj58"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "table",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source48",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.group('bar')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "bar"
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "bar",
+ "level": "group",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source48",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.groupEnd('bar')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "bar",
+ "level": "groupEnd",
+ "lineNumber": 3,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source54",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.groupCollapsed('foo')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "foo"
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "foo",
+ "level": "groupCollapsed",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source48",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.groupEnd('foo')`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "foo",
+ "level": "groupEnd",
+ "lineNumber": 3,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source54",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.group()`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "group",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source48",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.groupEnd()`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "groupEnd",
+ "lineNumber": 3,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source54",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log(%cfoobar)`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "foo",
+ "bar"
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source48",
+ "styles": [
+ "color:blue; font-size:1.3em; background:url('http://example.com/test'); position:absolute; top:10px; ",
+ "color:red; line-height: 1.5; background:url('http://example.com/test')"
+ ],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log("%cHello%c|%cWorld")`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "Hello",
+ "|",
+ "World"
+ ],
+ "chromeContext": false,
+ "columnNumber": 13,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source63",
+ "styles": [
+ "color:red",
+ "",
+ "color: blue"
+ ],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.group(%cfoo%cbar)`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "foo",
+ "bar"
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "foo bar",
+ "level": "group",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source48",
+ "styles": [
+ "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
+ "color:red;background:url('http://example.com/test')"
+ ],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.groupEnd(%cfoo%cbar)`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "foo bar",
+ "level": "groupEnd",
+ "lineNumber": 6,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source54",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.groupCollapsed(%cfoo%cbaz)`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "foo",
+ "baz"
+ ],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "foo baz",
+ "level": "groupCollapsed",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source48",
+ "styles": [
+ "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
+ "color:red;background:url('http://example.com/test')"
+ ],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.groupEnd(%cfoo%cbaz)`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [],
+ "chromeContext": false,
+ "columnNumber": 11,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "foo baz",
+ "level": "groupEnd",
+ "lineNumber": 6,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source54",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.dir({C, M, Y, K})`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj67",
+ "class": "Object",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "cyan": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "C"
+ },
+ "magenta": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "M"
+ },
+ "yellow": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "Y"
+ },
+ "black": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "K"
+ }
+ },
+ "ownSymbols": [],
+ "ownPropertiesLength": 4,
+ "ownSymbolsLength": 0,
+ "safeGetterValues": {}
+ }
+ },
+ "actorID": "server0.conn0.child1/obj67"
+ }
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "dir",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.count | default: 1`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "default"
+ ],
+ "chromeContext": false,
+ "columnNumber": 15,
+ "counter": {
+ "count": 1,
+ "label": "default"
+ },
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "count",
+ "lineNumber": 2,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source68",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.count | default: 2`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "default"
+ ],
+ "chromeContext": false,
+ "columnNumber": 15,
+ "counter": {
+ "count": 2,
+ "label": "default"
+ },
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "count",
+ "lineNumber": 3,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source68",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.count | test counter: 1`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "test counter"
+ ],
+ "chromeContext": false,
+ "columnNumber": 15,
+ "counter": {
+ "count": 1,
+ "label": "test counter"
+ },
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "count",
+ "lineNumber": 4,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source68",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.count | test counter: 2`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "test counter"
+ ],
+ "chromeContext": false,
+ "columnNumber": 15,
+ "counter": {
+ "count": 2,
+ "label": "test counter"
+ },
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "count",
+ "lineNumber": 5,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source68",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.count | default: 3`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "default"
+ ],
+ "chromeContext": false,
+ "columnNumber": 15,
+ "counter": {
+ "count": 3,
+ "label": "default"
+ },
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "count",
+ "lineNumber": 6,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source68",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.count | clear`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [],
+ "chromeContext": false,
+ "columnNumber": 15,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "clear",
+ "lineNumber": 7,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source68",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.count | default: 4`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "default"
+ ],
+ "chromeContext": false,
+ "columnNumber": 15,
+ "counter": {
+ "count": 4,
+ "label": "default"
+ },
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "count",
+ "lineNumber": 8,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source68",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.count | test counter: 3`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "test counter"
+ ],
+ "chromeContext": false,
+ "columnNumber": 15,
+ "counter": {
+ "count": 3,
+ "label": "test counter"
+ },
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "count",
+ "lineNumber": 9,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source68",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.countReset | test counter: 0`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "test counter"
+ ],
+ "chromeContext": false,
+ "columnNumber": 15,
+ "counter": {
+ "count": 0,
+ "label": "test counter"
+ },
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "countReset",
+ "lineNumber": 10,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source68",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.countReset | counterDoesntExist`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "test counter"
+ ],
+ "chromeContext": false,
+ "columnNumber": 15,
+ "counter": {
+ "error": "counterDoesntExist",
+ "label": "test counter"
+ },
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "countReset",
+ "lineNumber": 11,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source68",
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "styles": [],
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+rawPackets.set(`console.log escaped characters`, {
+ "resourceType": "console-message",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "hello \nfrom \rthe \"string world!"
+ ],
+ "chromeContext": false,
+ "columnNumber": 35,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 1,
+ "prefix": "",
+ "private": false,
+ "sourceId": "server0.conn0.child1/source22",
+ "styles": [],
+ "timeStamp": 1572867483805,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev",
+ "innerWindowID": 8589934593
+ }
+});
+
+
+const stubPackets = parsePacketsWithFronts(rawPackets);
+
+const stubPreparedMessages = new Map();
+for (const [key, packet] of Array.from(stubPackets.entries())) {
+ const transformedPacket = prepareMessage(packet, {
+ getNextId: () => "1",
+ });
+ const message = ConsoleMessage(transformedPacket);
+ stubPreparedMessages.set(key, message);
+}
+
+module.exports = {
+ rawPackets,
+ stubPreparedMessages,
+ stubPackets,
+};
diff --git a/devtools/client/webconsole/test/node/fixtures/stubs/cssMessage.js b/devtools/client/webconsole/test/node/fixtures/stubs/cssMessage.js
new file mode 100644
index 0000000000..cda3465e39
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/stubs/cssMessage.js
@@ -0,0 +1,85 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint-disable max-len */
+
+"use strict";
+
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
+ */
+
+const {
+ parsePacketsWithFronts,
+} = require("chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/stub-generator-helpers");
+const { prepareMessage } = require("devtools/client/webconsole/utils/messages");
+const {
+ ConsoleMessage,
+ NetworkEventMessage,
+} = require("devtools/client/webconsole/types");
+
+const rawPackets = new Map();
+rawPackets.set(`Unknown property ‘such-unknown-property’. Declaration dropped.`, {
+ "pageError": {
+ "errorMessage": "Unknown property ‘such-unknown-property’. Declaration dropped.",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-css-message.html",
+ "sourceId": null,
+ "lineText": "",
+ "lineNumber": 3,
+ "columnNumber": 27,
+ "category": "CSS Parser",
+ "innerWindowID": 8589934593,
+ "timeStamp": 1572867483805,
+ "warning": true,
+ "error": false,
+ "info": false,
+ "private": false,
+ "stacktrace": null,
+ "notes": null,
+ "chromeContext": false,
+ "isForwardedFromContentProcess": false
+ },
+ "resourceType": "css-message",
+ "cssSelectors": "p"
+});
+
+rawPackets.set(`Error in parsing value for ‘padding-top’. Declaration dropped.`, {
+ "pageError": {
+ "errorMessage": "Error in parsing value for ‘padding-top’. Declaration dropped.",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-css-message.html",
+ "sourceId": null,
+ "lineText": "",
+ "lineNumber": 3,
+ "columnNumber": 18,
+ "category": "CSS Parser",
+ "innerWindowID": 8589934593,
+ "timeStamp": 1572867483805,
+ "warning": true,
+ "error": false,
+ "info": false,
+ "private": false,
+ "stacktrace": null,
+ "notes": null,
+ "chromeContext": false,
+ "isForwardedFromContentProcess": false
+ },
+ "resourceType": "css-message",
+ "cssSelectors": "p"
+});
+
+
+const stubPackets = parsePacketsWithFronts(rawPackets);
+
+const stubPreparedMessages = new Map();
+for (const [key, packet] of Array.from(stubPackets.entries())) {
+ const transformedPacket = prepareMessage(packet, {
+ getNextId: () => "1",
+ });
+ const message = ConsoleMessage(transformedPacket);
+ stubPreparedMessages.set(key, message);
+}
+
+module.exports = {
+ rawPackets,
+ stubPreparedMessages,
+ stubPackets,
+};
diff --git a/devtools/client/webconsole/test/node/fixtures/stubs/evaluationResult.js b/devtools/client/webconsole/test/node/fixtures/stubs/evaluationResult.js
new file mode 100644
index 0000000000..f8837393a0
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/stubs/evaluationResult.js
@@ -0,0 +1,809 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint-disable max-len */
+
+"use strict";
+
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
+ */
+
+const {
+ parsePacketsWithFronts,
+} = require("chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/stub-generator-helpers");
+const { prepareMessage } = require("devtools/client/webconsole/utils/messages");
+const {
+ ConsoleMessage,
+ NetworkEventMessage,
+} = require("devtools/client/webconsole/types");
+
+const rawPackets = new Map();
+rawPackets.set(`new Date(0)`, {
+ "resultID": "1573832025018-0",
+ "hasException": false,
+ "input": "new Date(0)",
+ "result": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj23",
+ "class": "Date",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "timestamp": 0
+ }
+ },
+ "actorID": "server0.conn0.child1/obj23"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`asdf()`, {
+ "resultID": "1573832025112-1",
+ "errorMessageName": "JSMSG_NOT_DEFINED",
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj25",
+ "class": "ReferenceError",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "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
+ }
+ },
+ "actorID": "server0.conn0.child1/obj25"
+ },
+ "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",
+ "exceptionStack": [
+ {
+ "filename": "debugger eval code",
+ "sourceId": "server0.conn0.child1/source24",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ }
+ ],
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "line": 1,
+ "column": 1
+ },
+ "input": "asdf()",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`1 + @`, {
+ "resultID": "1573832025117-2",
+ "errorMessageName": "JSMSG_ILLEGAL_CHARACTER",
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj26",
+ "class": "SyntaxError",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "preview": {
+ "kind": "Error",
+ "name": "SyntaxError",
+ "message": "illegal character U+0040",
+ "stack": "",
+ "fileName": "debugger eval code",
+ "lineNumber": 1,
+ "columnNumber": 4
+ }
+ },
+ "actorID": "server0.conn0.child1/obj26"
+ },
+ "exceptionMessage": "SyntaxError: illegal character U+0040",
+ "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Illegal_character?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "line": 1,
+ "column": 4
+ },
+ "input": "1 + @",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`inspect({a: 1})`, {
+ "resultID": "1573832025122-3",
+ "hasException": false,
+ "helperResult": {
+ "type": "inspectObject",
+ "input": "inspect({a: 1})",
+ "object": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj28",
+ "class": "Object",
+ "ownPropertyLength": 1,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "a": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": 1
+ }
+ },
+ "ownSymbols": [],
+ "ownPropertiesLength": 1,
+ "ownSymbolsLength": 0,
+ "safeGetterValues": {}
+ }
+ },
+ "actorID": "server0.conn0.child1/obj28"
+ },
+ "forceExpandInConsole": false
+ },
+ "input": "inspect({a: 1})",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`undefined`, {
+ "resultID": "1573832025127-5",
+ "hasException": false,
+ "input": "undefined",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`longString message Error`, {
+ "resultID": "1573832025130-6",
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child1/obj32",
+ "class": "Error",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "preview": {
+ "kind": "Error",
+ "name": "Error",
+ "message": {
+ "_grip": {
+ "type": "longString",
+ "actor": "server0.conn0.child1/longstractor33",
+ "length": 110000,
+ "initial": "Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error"
+ },
+ "actorID": "server0.conn0.child1/longstractor33"
+ },
+ "stack": "@debugger eval code:1:7\n",
+ "fileName": "debugger eval code",
+ "lineNumber": 1,
+ "columnNumber": 7
+ }
+ },
+ "actorID": "server0.conn0.child1/obj32"
+ },
+ "exceptionMessage": {
+ "_grip": {
+ "type": "longString",
+ "actor": "server0.conn0.child1/longstractor34",
+ "length": 110007,
+ "initial": "Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Lon"
+ },
+ "actorID": "server0.conn0.child1/longstractor34"
+ },
+ "exceptionStack": [
+ {
+ "filename": "debugger eval code",
+ "sourceId": "server0.conn0.child1/source31",
+ "lineNumber": 1,
+ "columnNumber": 7,
+ "functionName": null
+ }
+ ],
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "sourceId": "server0.conn0.child1/source31",
+ "line": 1,
+ "column": 7
+ },
+ "input": "throw new Error(\"Long error \".repeat(10000))",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval throw ""`, {
+ "resultID": "1573832025134-7",
+ "exception": "",
+ "exceptionMessage": "",
+ "exceptionStack": [
+ {
+ "filename": "debugger eval code",
+ "sourceId": "server0.conn0.child1/source24",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ }
+ ],
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "sourceId": "server0.conn0.child1/source24",
+ "line": 1,
+ "column": 1
+ },
+ "input": "throw \"\"",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval throw "tomato"`, {
+ "resultID": "1573832025137-8",
+ "exception": "tomato",
+ "exceptionMessage": "tomato",
+ "exceptionStack": [
+ {
+ "filename": "debugger eval code",
+ "sourceId": "server0.conn0.child1/source24",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ }
+ ],
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "sourceId": "server0.conn0.child1/source24",
+ "line": 1,
+ "column": 1
+ },
+ "input": "throw \"tomato\"",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval throw false`, {
+ "resultID": "1588154002962-9",
+ "exception": false,
+ "exceptionMessage": "false",
+ "exceptionStack": [
+ {
+ "filename": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source26",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ }
+ ],
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source26",
+ "line": 1,
+ "column": 1
+ },
+ "input": "throw false",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval throw 0`, {
+ "resultID": "1588154002979-10",
+ "exception": 0,
+ "exceptionMessage": "0",
+ "exceptionStack": [
+ {
+ "filename": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source26",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ }
+ ],
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source26",
+ "line": 1,
+ "column": 1
+ },
+ "input": "throw 0",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval throw null`, {
+ "resultID": "1588154003064-11",
+ "exception": {
+ "type": "null"
+ },
+ "exceptionMessage": "null",
+ "exceptionStack": [
+ {
+ "filename": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source26",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ }
+ ],
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source26",
+ "line": 1,
+ "column": 1
+ },
+ "input": "throw null",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval throw undefined`, {
+ "resultID": "1588154003073-12",
+ "exception": {
+ "type": "undefined"
+ },
+ "exceptionMessage": "undefined",
+ "exceptionStack": [
+ {
+ "filename": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source26",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ }
+ ],
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source26",
+ "line": 1,
+ "column": 1
+ },
+ "input": "throw undefined",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval throw Symbol`, {
+ "resultID": "1588154003077-13",
+ "exception": {
+ "type": "symbol",
+ "actor": "server0.conn2.child2/symbol44",
+ "name": "potato"
+ },
+ "exceptionMessage": "Symbol(potato)",
+ "exceptionStack": [
+ {
+ "filename": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source33",
+ "lineNumber": 1,
+ "columnNumber": 7,
+ "functionName": null
+ }
+ ],
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source33",
+ "line": 1,
+ "column": 7
+ },
+ "input": "throw Symbol(\"potato\")",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval throw Object`, {
+ "resultID": "1588154003082-14",
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn2.child2/obj46",
+ "class": "Object",
+ "ownPropertyLength": 1,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "vegetable": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "cucumber"
+ }
+ },
+ "ownSymbols": [],
+ "ownPropertiesLength": 1,
+ "ownSymbolsLength": 0,
+ "safeGetterValues": {}
+ }
+ },
+ "actorID": "server0.conn2.child2/obj46"
+ },
+ "exceptionMessage": "[object Object]",
+ "exceptionStack": [
+ {
+ "filename": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source26",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ }
+ ],
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source26",
+ "line": 1,
+ "column": 1
+ },
+ "input": "throw {vegetable: \"cucumber\"}",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval throw Error Object`, {
+ "resultID": "1588154003093-15",
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn2.child2/obj48",
+ "class": "Error",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "preview": {
+ "kind": "Error",
+ "name": "Error",
+ "message": "pumpkin",
+ "stack": "@debugger eval code:1:7\n",
+ "fileName": "debugger eval code",
+ "lineNumber": 1,
+ "columnNumber": 7
+ }
+ },
+ "actorID": "server0.conn2.child2/obj48"
+ },
+ "exceptionMessage": "Error: pumpkin",
+ "exceptionStack": [
+ {
+ "filename": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source33",
+ "lineNumber": 1,
+ "columnNumber": 7,
+ "functionName": null
+ }
+ ],
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source33",
+ "line": 1,
+ "column": 7
+ },
+ "input": "throw new Error(\"pumpkin\")",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval throw Error Object with custom name`, {
+ "resultID": "1588154003097-16",
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn2.child2/obj50",
+ "class": "Error",
+ "ownPropertyLength": 6,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "preview": {
+ "kind": "Error",
+ "name": "JuicyError",
+ "message": "pineapple",
+ "stack": "@debugger eval code:2:15\n",
+ "fileName": "debugger eval code",
+ "lineNumber": 2,
+ "columnNumber": 15
+ }
+ },
+ "actorID": "server0.conn2.child2/obj50"
+ },
+ "exceptionMessage": "JuicyError: pineapple",
+ "exceptionStack": [
+ {
+ "filename": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source49",
+ "lineNumber": 5,
+ "columnNumber": 5,
+ "functionName": null
+ }
+ ],
+ "hasException": true,
+ "frame": {
+ "source": "debugger eval code",
+ "sourceId": "server0.conn2.child2/source49",
+ "line": 5,
+ "column": 5
+ },
+ "input": "\n var err = new Error(\"pineapple\");\n err.name = \"JuicyError\";\n err.flavor = \"delicious\";\n throw err;\n ",
+ "result": {
+ "type": "undefined"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval pending promise`, {
+ "resultID": "1609858965386-17",
+ "hasException": false,
+ "input": "new Promise(() => {})",
+ "result": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child3/obj53",
+ "class": "Promise",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "<state>": {
+ "value": "pending"
+ }
+ },
+ "ownPropertiesLength": 1
+ }
+ },
+ "actorID": "server0.conn0.child3/obj53"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval Promise.resolve`, {
+ "resultID": "1609858965388-18",
+ "hasException": false,
+ "input": "Promise.resolve(123)",
+ "result": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child3/obj55",
+ "class": "Promise",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "<state>": {
+ "value": "fulfilled"
+ },
+ "<value>": {
+ "value": 123
+ }
+ },
+ "ownPropertiesLength": 2
+ }
+ },
+ "actorID": "server0.conn0.child3/obj55"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval Promise.reject`, {
+ "resultID": "1609858965389-19",
+ "hasException": false,
+ "input": "Promise.reject(\"ouch\")",
+ "result": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child3/obj57",
+ "class": "Promise",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "<state>": {
+ "value": "rejected"
+ },
+ "<reason>": {
+ "value": "ouch"
+ }
+ },
+ "ownPropertiesLength": 2
+ }
+ },
+ "actorID": "server0.conn0.child3/obj57"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval resolved promise`, {
+ "resultID": "1609858965393-20",
+ "hasException": false,
+ "input": "Promise.resolve().then(() => 246)",
+ "result": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child3/obj59",
+ "class": "Promise",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "<state>": {
+ "value": "fulfilled"
+ },
+ "<value>": {
+ "value": 246
+ }
+ },
+ "ownPropertiesLength": 2
+ }
+ },
+ "actorID": "server0.conn0.child3/obj59"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+rawPackets.set(`eval rejected promise`, {
+ "resultID": "1609858965397-21",
+ "hasException": false,
+ "input": "Promise.resolve().then(() => a.b.c)",
+ "result": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.child3/obj61",
+ "class": "Promise",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "<state>": {
+ "value": "rejected"
+ },
+ "<reason>": {
+ "value": {
+ "type": "object",
+ "actor": "server0.conn0.child3/obj62",
+ "class": "ReferenceError",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "preview": {
+ "kind": "Error",
+ "name": "ReferenceError",
+ "message": "a is not defined",
+ "stack": "@debugger eval code:1:30\npromise callback*@debugger eval code:1:19\n",
+ "fileName": "debugger eval code",
+ "lineNumber": 1,
+ "columnNumber": 30
+ }
+ }
+ }
+ },
+ "ownPropertiesLength": 2
+ }
+ },
+ "actorID": "server0.conn0.child3/obj61"
+ },
+ "startTime": 1572867483805,
+ "timestamp": 1572867483805
+});
+
+
+const stubPackets = parsePacketsWithFronts(rawPackets);
+
+const stubPreparedMessages = new Map();
+for (const [key, packet] of Array.from(stubPackets.entries())) {
+ const transformedPacket = prepareMessage(packet, {
+ getNextId: () => "1",
+ });
+ const message = ConsoleMessage(transformedPacket);
+ stubPreparedMessages.set(key, message);
+}
+
+module.exports = {
+ rawPackets,
+ stubPreparedMessages,
+ stubPackets,
+};
diff --git a/devtools/client/webconsole/test/node/fixtures/stubs/index.js b/devtools/client/webconsole/test/node/fixtures/stubs/index.js
new file mode 100644
index 0000000000..9e5de16308
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/stubs/index.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const maps = [];
+
+[
+ "consoleApi",
+ "cssMessage",
+ "evaluationResult",
+ "networkEvent",
+ "pageError",
+ "platformMessage",
+].forEach(filename => {
+ maps[filename] = require(`./${filename}`);
+});
+
+// Combine all the maps into a single map.
+module.exports = {
+ stubPreparedMessages: new Map([
+ ...maps.consoleApi.stubPreparedMessages,
+ ...maps.cssMessage.stubPreparedMessages,
+ ...maps.evaluationResult.stubPreparedMessages,
+ ...maps.networkEvent.stubPreparedMessages,
+ ...maps.pageError.stubPreparedMessages,
+ ...maps.platformMessage.stubPreparedMessages,
+ ]),
+ stubPackets: new Map([
+ ...maps.consoleApi.stubPackets,
+ ...maps.cssMessage.stubPackets,
+ ...maps.evaluationResult.stubPackets,
+ ...maps.networkEvent.stubPackets,
+ ...maps.pageError.stubPackets,
+ ...maps.platformMessage.stubPackets,
+ ]),
+};
diff --git a/devtools/client/webconsole/test/node/fixtures/stubs/networkEvent.js b/devtools/client/webconsole/test/node/fixtures/stubs/networkEvent.js
new file mode 100644
index 0000000000..d06191698c
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/stubs/networkEvent.js
@@ -0,0 +1,269 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint-disable max-len */
+
+"use strict";
+
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
+ */
+
+const {
+ parsePacketsWithFronts,
+} = require("chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/stub-generator-helpers");
+const { prepareMessage } = require("devtools/client/webconsole/utils/messages");
+const {
+ ConsoleMessage,
+ NetworkEventMessage,
+} = require("devtools/client/webconsole/types");
+
+const rawPackets = new Map();
+rawPackets.set(`GET request`, {
+ "resourceType": "network-event",
+ "timeStamp": 1572867483805,
+ "actor": "server0.conn0.netEvent4",
+ "startedDateTime": "2019-11-04T11:06:34.542Z",
+ "method": "GET",
+ "url": "http://example.com/inexistent.html",
+ "isXHR": false,
+ "cause": {
+ "type": "img",
+ "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
+ "stacktraceAvailable": true,
+ "lastFrame": {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
+ "lineNumber": 3,
+ "columnNumber": 1,
+ "functionName": "triggerPacket",
+ "asyncCause": null
+ }
+ },
+ "httpVersion": "HTTP/1.1",
+ "status": "404",
+ "statusText": "Not Found",
+ "remoteAddress": "127.0.0.1",
+ "remotePort": 8888,
+ "mimeType": "text/html; charset=utf-8",
+ "waitingTime": 1,
+ "contentSize": 418,
+ "transferredSize": 578,
+ "timings": {},
+ "private": false,
+ "isThirdPartyTrackingResource": false,
+ "referrerPolicy": "no-referrer-when-downgrade",
+ "blockedReason": 0,
+ "totalTime": 2,
+ "securityState": "insecure",
+ "isRacing": false
+});
+
+rawPackets.set(`GET request update`, {
+ "resourceType": "network-event",
+ "timeStamp": 1572867483805,
+ "actor": "server0.conn0.netEvent5",
+ "startedDateTime": "2020-07-07T14:41:14.572Z",
+ "method": "GET",
+ "url": "http://example.com/inexistent.html",
+ "isXHR": false,
+ "cause": {
+ "type": "img",
+ "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
+ "stacktraceAvailable": true,
+ "lastFrame": {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
+ "lineNumber": 3,
+ "columnNumber": 1,
+ "functionName": "triggerPacket",
+ "asyncCause": null
+ }
+ },
+ "httpVersion": "HTTP/1.1",
+ "status": "404",
+ "statusText": "Not Found",
+ "remoteAddress": "127.0.0.1",
+ "remotePort": 8888,
+ "mimeType": "text/html; charset=utf-8",
+ "waitingTime": 1,
+ "contentSize": 418,
+ "transferredSize": 578,
+ "timings": {},
+ "private": false,
+ "isThirdPartyTrackingResource": false,
+ "referrerPolicy": "no-referrer-when-downgrade",
+ "blockedReason": 0,
+ "totalTime": 3,
+ "securityState": "insecure",
+ "isRacing": false
+});
+
+rawPackets.set(`XHR GET request`, {
+ "resourceType": "network-event",
+ "timeStamp": 1572867483805,
+ "actor": "server0.conn0.netEvent21",
+ "startedDateTime": "2020-07-07T14:41:14.612Z",
+ "method": "GET",
+ "url": "http://example.com/inexistent.html",
+ "isXHR": true,
+ "cause": {
+ "type": "xhr",
+ "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
+ "stacktraceAvailable": true,
+ "lastFrame": {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
+ "lineNumber": 4,
+ "columnNumber": 5,
+ "functionName": "triggerPacket",
+ "asyncCause": null
+ }
+ },
+ "httpVersion": "HTTP/1.1",
+ "status": "404",
+ "statusText": "Not Found",
+ "remoteAddress": "127.0.0.1",
+ "remotePort": 8888,
+ "mimeType": "text/html; charset=utf-8",
+ "waitingTime": 1,
+ "contentSize": 418,
+ "transferredSize": 578,
+ "timings": {},
+ "private": false,
+ "isThirdPartyTrackingResource": false,
+ "referrerPolicy": "no-referrer-when-downgrade",
+ "blockedReason": 0,
+ "totalTime": 1,
+ "securityState": "insecure",
+ "isRacing": false
+});
+
+rawPackets.set(`XHR GET request update`, {
+ "resourceType": "network-event",
+ "timeStamp": 1572867483805,
+ "actor": "server0.conn0.netEvent20",
+ "method": "GET",
+ "url": "http://example.com/inexistent.html",
+ "isXHR": true,
+ "cause": {
+ "type": "xhr",
+ "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
+ "stacktraceAvailable": true,
+ "lastFrame": {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
+ "lineNumber": 4,
+ "columnNumber": 5,
+ "functionName": "triggerPacket",
+ "asyncCause": null
+ }
+ },
+ "httpVersion": "HTTP/1.1",
+ "status": "404",
+ "statusText": "Not Found",
+ "remoteAddress": "127.0.0.1",
+ "remotePort": 8888,
+ "mimeType": "text/html; charset=utf-8",
+ "waitingTime": 1,
+ "contentSize": 418,
+ "transferredSize": 578,
+ "timings": {},
+ "private": false,
+ "isThirdPartyTrackingResource": false,
+ "referrerPolicy": "no-referrer-when-downgrade",
+ "blockedReason": 0,
+ "totalTime": 1,
+ "securityState": "insecure",
+ "isRacing": false
+});
+
+rawPackets.set(`XHR POST request`, {
+ "resourceType": "network-event",
+ "timeStamp": 1572867483805,
+ "actor": "server0.conn0.netEvent36",
+ "startedDateTime": "2019-11-04T11:06:35.007Z",
+ "method": "POST",
+ "url": "http://example.com/inexistent.html",
+ "isXHR": true,
+ "cause": {
+ "type": "xhr",
+ "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
+ "stacktraceAvailable": true,
+ "lastFrame": {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
+ "lineNumber": 4,
+ "columnNumber": 5,
+ "functionName": "triggerPacket",
+ "asyncCause": null
+ }
+ },
+ "httpVersion": "HTTP/1.1",
+ "status": "404",
+ "statusText": "Not Found",
+ "remoteAddress": "127.0.0.1",
+ "remotePort": 8888,
+ "mimeType": "text/html; charset=utf-8",
+ "waitingTime": 1,
+ "contentSize": 418,
+ "transferredSize": 578,
+ "timings": {},
+ "private": false,
+ "isThirdPartyTrackingResource": false,
+ "referrerPolicy": "no-referrer-when-downgrade",
+ "blockedReason": 0,
+ "totalTime": 1,
+ "securityState": "insecure",
+ "isRacing": false
+});
+
+rawPackets.set(`XHR POST request update`, {
+ "resourceType": "network-event",
+ "timeStamp": 1572867483805,
+ "actor": "server0.conn0.netEvent36",
+ "method": "POST",
+ "url": "http://example.com/inexistent.html",
+ "isXHR": true,
+ "cause": {
+ "type": "xhr",
+ "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
+ "stacktraceAvailable": true,
+ "lastFrame": {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
+ "lineNumber": 4,
+ "columnNumber": 5,
+ "functionName": "triggerPacket",
+ "asyncCause": null
+ }
+ },
+ "httpVersion": "HTTP/1.1",
+ "status": "404",
+ "statusText": "Not Found",
+ "remoteAddress": "127.0.0.1",
+ "remotePort": 8888,
+ "mimeType": "text/html; charset=utf-8",
+ "waitingTime": 1,
+ "contentSize": 418,
+ "transferredSize": 578,
+ "timings": {},
+ "private": false,
+ "isThirdPartyTrackingResource": false,
+ "referrerPolicy": "no-referrer-when-downgrade",
+ "blockedReason": 0,
+ "totalTime": 2,
+ "securityState": "insecure",
+ "isRacing": false
+});
+
+
+const stubPackets = parsePacketsWithFronts(rawPackets);
+
+const stubPreparedMessages = new Map();
+for (const [key, packet] of Array.from(stubPackets.entries())) {
+ const transformedPacket = prepareMessage(packet, {
+ getNextId: () => "1",
+ });
+ const message = NetworkEventMessage(transformedPacket);
+ stubPreparedMessages.set(key, message);
+}
+
+module.exports = {
+ rawPackets,
+ stubPreparedMessages,
+ stubPackets,
+};
diff --git a/devtools/client/webconsole/test/node/fixtures/stubs/pageError.js b/devtools/client/webconsole/test/node/fixtures/stubs/pageError.js
new file mode 100644
index 0000000000..c6a41811ae
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/stubs/pageError.js
@@ -0,0 +1,1657 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint-disable max-len */
+
+"use strict";
+
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
+ */
+
+const {
+ parsePacketsWithFronts,
+} = require("chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/stub-generator-helpers");
+const { prepareMessage } = require("devtools/client/webconsole/utils/messages");
+const {
+ ConsoleMessage,
+ NetworkEventMessage,
+} = require("devtools/client/webconsole/types");
+
+const rawPackets = new Map();
+rawPackets.set(`ReferenceError: asdf is not defined`, {
+ "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/test/browser/test-console-api.html",
+ "sourceId": "server0.conn0.child1/source22",
+ "lineText": "",
+ "lineNumber": 3,
+ "columnNumber": 5,
+ "category": "content javascript",
+ "innerWindowID": 8589934593,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn0.child1/source22",
+ "lineNumber": 3,
+ "columnNumber": 5,
+ "functionName": "bar"
+ },
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn0.child1/source22",
+ "lineNumber": 6,
+ "columnNumber": 5,
+ "functionName": "foo"
+ },
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn0.child1/source22",
+ "lineNumber": 9,
+ "columnNumber": 3,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn4.child2/obj25",
+ "class": "ReferenceError",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "preview": {
+ "kind": "Error",
+ "name": "ReferenceError",
+ "message": "asdf is not defined",
+ "stack": "bar@http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html:3:5\nfoo@http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html:6:5\n@http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html:9:3\n",
+ "fileName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "lineNumber": 3,
+ "columnNumber": 5
+ }
+ },
+ "actorID": "server0.conn4.child2/obj25"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`SyntaxError: redeclaration of let a`, {
+ "pageError": {
+ "errorMessage": "SyntaxError: redeclaration of let a",
+ "errorMessageName": "JSMSG_REDECLARED_VAR",
+ "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Redeclared_parameter?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineText": " let a, a;",
+ "lineNumber": 2,
+ "columnNumber": 9,
+ "category": "content javascript",
+ "innerWindowID": 8589934593,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": [
+ {
+ "messageBody": "Previously declared at line 2, column 6",
+ "frame": {
+ "source": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "line": 2,
+ "column": 6
+ }
+ }
+ ],
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn4.child2/obj26",
+ "class": "SyntaxError",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "preview": {
+ "kind": "Error",
+ "name": "SyntaxError",
+ "message": "redeclaration of let a",
+ "stack": "",
+ "fileName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "lineNumber": 2,
+ "columnNumber": 9
+ }
+ },
+ "actorID": "server0.conn4.child2/obj26"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`TypeError longString message`, {
+ "pageError": {
+ "errorMessage": {
+ "_grip": {
+ "type": "longString",
+ "actor": "server0.conn0.child1/longstractor24",
+ "length": 110007,
+ "initial": "Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Lon"
+ },
+ "actorID": "server0.conn0.child1/longstractor24"
+ },
+ "errorMessageName": "",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn0.child1/source23",
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 7,
+ "category": "content javascript",
+ "innerWindowID": 8589934593,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn0.child1/source23",
+ "lineNumber": 1,
+ "columnNumber": 7,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn4.child2/obj29",
+ "class": "Error",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "preview": {
+ "kind": "Error",
+ "name": "Error",
+ "message": {
+ "_grip": {
+ "type": "longString",
+ "actor": "server0.conn4.child2/longstractor30",
+ "length": 110000,
+ "initial": "Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error"
+ },
+ "actorID": "server0.conn4.child2/longstractor30"
+ },
+ "stack": "@http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html:1:7\n",
+ "fileName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "lineNumber": 1,
+ "columnNumber": 7
+ }
+ },
+ "actorID": "server0.conn4.child2/obj29"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`throw string with URL`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: “https://evil.com/?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa“ is evil and “https://not-so-evil.com/?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa“ is not good either",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn0.child2/source31",
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "category": "content javascript",
+ "innerWindowID": 10737418241,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn0.child2/source31",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": "“https://evil.com/?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa“ is evil and “https://not-so-evil.com/?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa“ is not good either",
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`throw ""`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: ",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "category": "content javascript",
+ "innerWindowID": 8589934593,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn0.child1/source25",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": "",
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`throw "tomato"`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: tomato",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "category": "content javascript",
+ "innerWindowID": 8589934593,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn0.child1/source25",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": "tomato",
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`throw false`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: false",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source31",
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source31",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": false,
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`throw 0`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: 0",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source31",
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source31",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": 0,
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`throw null`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: null",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source31",
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source31",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "type": "null"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`throw undefined`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: undefined",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source31",
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source31",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "type": "undefined"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`throw Symbol`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: Symbol(potato)",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source27",
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 7,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source27",
+ "lineNumber": 1,
+ "columnNumber": 7,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "type": "symbol",
+ "actor": "server0.conn4.child2/symbol38",
+ "name": "potato"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`throw Object`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: [object Object]",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source31",
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source31",
+ "lineNumber": 1,
+ "columnNumber": 1,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn4.child2/obj40",
+ "class": "Object",
+ "ownPropertyLength": 1,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "vegetable": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "cucumber"
+ }
+ },
+ "ownSymbols": [],
+ "ownPropertiesLength": 1,
+ "ownSymbolsLength": 0,
+ "safeGetterValues": {}
+ }
+ },
+ "actorID": "server0.conn4.child2/obj40"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`throw Error Object`, {
+ "pageError": {
+ "errorMessage": "Error: pumpkin",
+ "errorMessageName": "",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source41",
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 7,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source27",
+ "lineNumber": 1,
+ "columnNumber": 7,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn4.child2/obj42",
+ "class": "Error",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "preview": {
+ "kind": "Error",
+ "name": "Error",
+ "message": "pumpkin",
+ "stack": "@http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html:1:7\n",
+ "fileName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "lineNumber": 1,
+ "columnNumber": 7
+ }
+ },
+ "actorID": "server0.conn4.child2/obj42"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`throw Error Object with custom name`, {
+ "pageError": {
+ "errorMessage": "JuicyError: pineapple",
+ "errorMessageName": "",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source43",
+ "lineText": "",
+ "lineNumber": 2,
+ "columnNumber": 15,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source43",
+ "lineNumber": 2,
+ "columnNumber": 15,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": false,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn4.child2/obj44",
+ "class": "Error",
+ "ownPropertyLength": 6,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "preview": {
+ "kind": "Error",
+ "name": "JuicyError",
+ "message": "pineapple",
+ "stack": "@http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html:2:15\n",
+ "fileName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "lineNumber": 2,
+ "columnNumber": 15
+ }
+ },
+ "actorID": "server0.conn4.child2/obj44"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`Promise reject ""`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: ",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": true,
+ "isForwardedFromContentProcess": false,
+ "exception": "",
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`Promise reject "tomato"`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: tomato",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": true,
+ "isForwardedFromContentProcess": false,
+ "exception": "tomato",
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`Promise reject false`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: false",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": true,
+ "isForwardedFromContentProcess": false,
+ "exception": false,
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`Promise reject 0`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: 0",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": true,
+ "isForwardedFromContentProcess": false,
+ "exception": 0,
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`Promise reject null`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: null",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": true,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "type": "null"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`Promise reject undefined`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: undefined",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": true,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "type": "undefined"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`Promise reject Symbol`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: Symbol(potato)",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": true,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "type": "symbol",
+ "actor": "server0.conn4.child2/symbol52",
+ "name": "potato"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`Promise reject Object`, {
+ "pageError": {
+ "errorMessage": "uncaught exception: Object",
+ "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": true,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn4.child2/obj54",
+ "class": "Object",
+ "ownPropertyLength": 1,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {
+ "vegetable": {
+ "configurable": true,
+ "enumerable": true,
+ "writable": true,
+ "value": "cucumber"
+ }
+ },
+ "ownSymbols": [],
+ "ownPropertiesLength": 1,
+ "ownSymbolsLength": 0,
+ "safeGetterValues": {}
+ }
+ },
+ "actorID": "server0.conn4.child2/obj54"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`Promise reject Error Object`, {
+ "pageError": {
+ "errorMessage": "Error: pumpkin",
+ "errorMessageName": "",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source55",
+ "lineText": "",
+ "lineNumber": 1,
+ "columnNumber": 16,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineNumber": 1,
+ "columnNumber": 9,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": true,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn4.child2/obj56",
+ "class": "Error",
+ "ownPropertyLength": 4,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "preview": {
+ "kind": "Error",
+ "name": "Error",
+ "message": "pumpkin",
+ "stack": "@http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html:1:16\n",
+ "fileName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "lineNumber": 1,
+ "columnNumber": 16
+ }
+ },
+ "actorID": "server0.conn4.child2/obj56"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+rawPackets.set(`Promise reject Error Object with custom name`, {
+ "pageError": {
+ "errorMessage": "JuicyError: pineapple",
+ "errorMessageName": "",
+ "sourceName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": "server0.conn4.child2/source57",
+ "lineText": "",
+ "lineNumber": 2,
+ "columnNumber": 15,
+ "category": "content javascript",
+ "innerWindowID": 6442450946,
+ "timeStamp": 1572867483805,
+ "warning": false,
+ "error": true,
+ "info": false,
+ "private": false,
+ "stacktrace": [
+ {
+ "filename": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "sourceId": null,
+ "lineNumber": 5,
+ "columnNumber": 13,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js line 110 > eval",
+ "sourceId": null,
+ "lineNumber": 6,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 111,
+ "columnNumber": 29,
+ "functionName": null
+ },
+ {
+ "filename": "resource://testing-common/content-task.js",
+ "sourceId": null,
+ "lineNumber": 64,
+ "columnNumber": 19,
+ "functionName": null,
+ "asyncCause": "MessageListener.receiveMessage"
+ }
+ ],
+ "notes": null,
+ "chromeContext": false,
+ "isPromiseRejection": true,
+ "isForwardedFromContentProcess": false,
+ "exception": {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn4.child2/obj58",
+ "class": "Error",
+ "ownPropertyLength": 6,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": true,
+ "preview": {
+ "kind": "Error",
+ "name": "JuicyError",
+ "message": "pineapple",
+ "stack": "@http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html:2:15\n",
+ "fileName": "http://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html",
+ "lineNumber": 2,
+ "columnNumber": 15
+ }
+ },
+ "actorID": "server0.conn4.child2/obj58"
+ },
+ "hasException": true
+ },
+ "resourceType": "error-message"
+});
+
+
+const stubPackets = parsePacketsWithFronts(rawPackets);
+
+const stubPreparedMessages = new Map();
+for (const [key, packet] of Array.from(stubPackets.entries())) {
+ const transformedPacket = prepareMessage(packet, {
+ getNextId: () => "1",
+ });
+ const message = ConsoleMessage(transformedPacket);
+ stubPreparedMessages.set(key, message);
+}
+
+module.exports = {
+ rawPackets,
+ stubPreparedMessages,
+ stubPackets,
+};
diff --git a/devtools/client/webconsole/test/node/fixtures/stubs/platformMessage.js b/devtools/client/webconsole/test/node/fixtures/stubs/platformMessage.js
new file mode 100644
index 0000000000..83ec867a98
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/stubs/platformMessage.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint-disable max-len */
+
+"use strict";
+
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
+ */
+
+const {
+ parsePacketsWithFronts,
+} = require("chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/stub-generator-helpers");
+const { prepareMessage } = require("devtools/client/webconsole/utils/messages");
+const {
+ ConsoleMessage,
+ NetworkEventMessage,
+} = require("devtools/client/webconsole/types");
+
+const rawPackets = new Map();
+rawPackets.set(`platform-simple-message`, {
+ "message": "foobar test",
+ "timeStamp": 1572867483805,
+ "resourceType": "platform-message"
+});
+
+rawPackets.set(`platform-longString-message`, {
+ "message": {
+ "type": "longString",
+ "actor": "server0.conn0.longstractor43",
+ "length": 20002,
+ "initial": "a\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ },
+ "timeStamp": 1572867483805,
+ "resourceType": "platform-message"
+});
+
+
+const stubPackets = parsePacketsWithFronts(rawPackets);
+
+const stubPreparedMessages = new Map();
+for (const [key, packet] of Array.from(stubPackets.entries())) {
+ const transformedPacket = prepareMessage(packet, {
+ getNextId: () => "1",
+ });
+ const message = ConsoleMessage(transformedPacket);
+ stubPreparedMessages.set(key, message);
+}
+
+module.exports = {
+ rawPackets,
+ stubPreparedMessages,
+ stubPackets,
+};
diff --git a/devtools/client/webconsole/test/node/fixtures/stubs/stubs.ini b/devtools/client/webconsole/test/node/fixtures/stubs/stubs.ini
new file mode 100644
index 0000000000..4c8b42030e
--- /dev/null
+++ b/devtools/client/webconsole/test/node/fixtures/stubs/stubs.ini
@@ -0,0 +1,14 @@
+[DEFAULT]
+tags = devtools
+subsuite = devtools
+support-files =
+ consoleApi.js
+ cssMessage.js
+ evaluationResult.js
+ index.js
+ networkEvent.js
+ pageError.js
+ platformMessage.js
+
+[browser_dummy.js]
+skip-if=true #This is only here so we can expose the support files in other ini files.
diff --git a/devtools/client/webconsole/test/node/helpers.js b/devtools/client/webconsole/test/node/helpers.js
new file mode 100644
index 0000000000..32514651bd
--- /dev/null
+++ b/devtools/client/webconsole/test/node/helpers.js
@@ -0,0 +1,177 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const reduxActions = require("devtools/client/webconsole/actions/index");
+const { configureStore } = require("devtools/client/webconsole/store");
+const {
+ IdGenerator,
+} = require("devtools/client/webconsole/utils/id-generator");
+const {
+ stubPackets,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const {
+ getAllMessagesById,
+} = require("devtools/client/webconsole/selectors/messages");
+const { getPrefsService } = require("devtools/client/webconsole/utils/prefs");
+const prefsService = getPrefsService({});
+const { PREFS } = require("devtools/client/webconsole/constants");
+const Telemetry = require("devtools/client/shared/telemetry");
+const {
+ getSerializedPacket,
+ parsePacketAndCreateFronts,
+} = require("devtools/client/webconsole/test/browser/stub-generator-helpers");
+
+/**
+ * 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({}, reduxActions);
+
+ const idGenerator = new IdGenerator();
+ wrappedActions.messagesAdd = packets => {
+ return reduxActions.messagesAdd(packets, idGenerator);
+ };
+
+ return {
+ ...reduxActions,
+ messagesAdd: packets => reduxActions.messagesAdd(packets, idGenerator),
+ };
+}
+
+/**
+ * Prepare the store for use in testing.
+ */
+function setupStore(
+ input = [],
+ { storeOptions = {}, actions, webConsoleUI } = {}
+) {
+ if (!webConsoleUI) {
+ webConsoleUI = getWebConsoleUiMock();
+ }
+ const store = configureStore(webConsoleUI, {
+ ...storeOptions,
+ thunkArgs: { toolbox: { sessionId: -1 } },
+ telemetry: new Telemetry(),
+ });
+
+ // Add the messages from the input commands to the store.
+ const messagesAdd = actions ? actions.messagesAdd : reduxActions.messagesAdd;
+ store.dispatch(messagesAdd(input.map(cmd => stubPackets.get(cmd))));
+
+ return store;
+}
+
+/**
+ * Create deep copy of given packet object.
+ */
+function clonePacket(packet) {
+ const strPacket = getSerializedPacket(packet);
+ return parsePacketAndCreateFronts(JSON.parse(strPacket));
+}
+
+/**
+ * Return the message in the store at the given index.
+ *
+ * @param {object} state - The redux state of the console.
+ * @param {int} index - The index of the message in the map.
+ * @return {Message} - The message, or undefined if the index does not exists in the map.
+ */
+function getMessageAt(state, index) {
+ const messages = getAllMessagesById(state);
+ return messages.get([...messages.keys()][index]);
+}
+
+/**
+ * Return the first message in the store.
+ *
+ * @param {object} state - The redux state of the console.
+ * @return {Message} - The last message, or undefined if there are no message in store.
+ */
+function getFirstMessage(state) {
+ return getMessageAt(state, 0);
+}
+
+/**
+ * Return the last message in the store.
+ *
+ * @param {object} state - The redux state of the console.
+ * @return {Message} - The last message, or undefined if there are no message in store.
+ */
+function getLastMessage(state) {
+ const lastIndex = getAllMessagesById(state).size - 1;
+ return getMessageAt(state, lastIndex);
+}
+
+function getFiltersPrefs() {
+ return Object.values(PREFS.FILTER).reduce((res, pref) => {
+ res[pref] = prefsService.getBoolPref(pref);
+ return res;
+ }, {});
+}
+
+function clearPrefs() {
+ [
+ "devtools.hud.loglimit",
+ ...Object.values(PREFS.FILTER),
+ ...Object.values(PREFS.UI),
+ ].forEach(prefsService.clearUserPref);
+}
+
+function getPrivatePacket(key) {
+ const packet = clonePacket(stubPackets.get(key));
+ if (packet.message) {
+ packet.message.private = true;
+ }
+ if (Object.getOwnPropertyNames(packet).includes("private")) {
+ packet.private = true;
+ }
+ return packet;
+}
+
+function getWebConsoleUiMock(hud, proxyOverrides) {
+ const proxy = getProxyMock(proxyOverrides);
+ return {
+ emit: () => {},
+ emitForTests: () => {},
+ hud,
+ getAllProxies: () => [proxy],
+ proxy,
+ clearNetworkRequests: () => {},
+ clearMessagesCache: () => {},
+ releaseActor: proxy.releaseActor,
+ getProxy: () => proxy,
+ inspectObjectActor: () => {},
+ toolbox: {
+ sessionId: 1,
+ },
+ watchCssMessages: () => {},
+ };
+}
+
+function getProxyMock(overrides = {}) {
+ return {
+ releaseActor: actor => {},
+ target: {
+ ensureCSSErrorReportingEnabled: () => {},
+ },
+ ...overrides,
+ };
+}
+
+module.exports = {
+ clearPrefs,
+ clonePacket,
+ getFiltersPrefs,
+ getFirstMessage,
+ getLastMessage,
+ getMessageAt,
+ getPrivatePacket,
+ getWebConsoleUiMock,
+ prefsService,
+ setupActions,
+ setupStore,
+};
diff --git a/devtools/client/webconsole/test/node/middleware/.eslintrc.js b/devtools/client/webconsole/test/node/middleware/.eslintrc.js
new file mode 100644
index 0000000000..9b9dfe0ad1
--- /dev/null
+++ b/devtools/client/webconsole/test/node/middleware/.eslintrc.js
@@ -0,0 +1,5 @@
+"use strict";
+
+module.exports = {
+ extends: "../.eslintrc.mocha.js",
+};
diff --git a/devtools/client/webconsole/test/node/middleware/debounce.test.js b/devtools/client/webconsole/test/node/middleware/debounce.test.js
new file mode 100644
index 0000000000..a1eec00915
--- /dev/null
+++ b/devtools/client/webconsole/test/node/middleware/debounce.test.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const expect = require("expect");
+
+const {
+ debounceActions,
+} = require("devtools/client/shared/redux/middleware/debounce");
+
+describe("Debounce Middleware", () => {
+ let nextArgs = [];
+ const fakeStore = {};
+ const fakeNext = (...args) => {
+ nextArgs.push(args);
+ };
+
+ beforeEach(() => {
+ nextArgs = [];
+ });
+
+ it("should pass the intercepted action to next", () => {
+ const fakeAction = {
+ type: "FAKE_ACTION",
+ };
+
+ debounceActions()(fakeStore)(fakeNext)(fakeAction);
+
+ expect(nextArgs.length).toEqual(1);
+ expect(nextArgs[0]).toEqual([fakeAction]);
+ });
+
+ it("should debounce if specified", () => {
+ const fakeAction = {
+ type: "FAKE_ACTION",
+ meta: {
+ debounce: true,
+ },
+ };
+
+ const executed = debounceActions(1, 1)(fakeStore)(fakeNext)(fakeAction);
+ expect(nextArgs.length).toEqual(0);
+
+ return executed.then(() => {
+ expect(nextArgs.length).toEqual(1);
+ });
+ });
+
+ it("should have no effect if no timeout", () => {
+ const fakeAction = {
+ type: "FAKE_ACTION",
+ meta: {
+ debounce: true,
+ },
+ };
+
+ debounceActions()(fakeStore)(fakeNext)(fakeAction);
+ expect(nextArgs.length).toEqual(1);
+ expect(nextArgs[0]).toEqual([fakeAction]);
+ });
+});
diff --git a/devtools/client/webconsole/test/node/mocha-test-setup.js b/devtools/client/webconsole/test/node/mocha-test-setup.js
new file mode 100644
index 0000000000..b620e047b9
--- /dev/null
+++ b/devtools/client/webconsole/test/node/mocha-test-setup.js
@@ -0,0 +1,182 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env node */
+
+"use strict";
+
+require("@babel/register")({
+ // by default everything is ignored
+ ignore: [/node_modules/],
+});
+
+const mcRoot = `${__dirname}/../../../../../`;
+const getModule = mcPath =>
+ `module.exports = require("${(mcRoot + mcPath).replace(/\\/gi, "/")}");`;
+
+const { pref } = require("devtools-services");
+pref("devtools.debugger.remote-timeout", 10000);
+pref("devtools.hud.loglimit", 10000);
+pref("devtools.webconsole.filter.error", true);
+pref("devtools.webconsole.filter.warn", true);
+pref("devtools.webconsole.filter.info", true);
+pref("devtools.webconsole.filter.log", true);
+pref("devtools.webconsole.filter.debug", true);
+pref("devtools.webconsole.filter.css", false);
+pref("devtools.webconsole.filter.net", false);
+pref("devtools.webconsole.filter.netxhr", false);
+pref("devtools.webconsole.inputHistoryCount", 300);
+pref("devtools.webconsole.persistlog", false);
+pref("devtools.webconsole.timestampMessages", false);
+pref("devtools.webconsole.sidebarToggle", true);
+pref("devtools.webconsole.groupWarningMessages", false);
+pref("devtools.webconsole.input.editor", false);
+pref("devtools.webconsole.input.autocomplete", true);
+pref("devtools.webconsole.input.eagerEvaluation", true);
+pref("devtools.browserconsole.contentMessages", true);
+pref("devtools.webconsole.input.editorWidth", 800);
+pref("devtools.webconsole.input.editorOnboarding", true);
+pref("devtools.webconsole.input.context", false);
+pref("devtools.contenttoolbox.webconsole.input.context", false);
+
+global.loader = {
+ lazyServiceGetter: () => {},
+ lazyGetter: (context, name, fn) => {
+ try {
+ global[name] = fn();
+ } catch (_) {}
+ },
+ lazyRequireGetter: (context, names, path, destruct) => {
+ const excluded = [
+ "Debugger",
+ "devtools/shared/event-emitter",
+ "devtools/client/shared/autocomplete-popup",
+ "devtools/client/framework/devtools",
+ "devtools/client/shared/keycodes",
+ "devtools/client/shared/sourceeditor/editor",
+ "devtools/client/shared/telemetry",
+ "devtools/client/shared/save-screenshot",
+ "devtools/client/shared/focus",
+ ];
+ if (!excluded.includes(path)) {
+ if (!Array.isArray(names)) {
+ names = [names];
+ }
+
+ for (const name of names) {
+ const module = require(path);
+ global[name] = destruct ? module[name] : module;
+ }
+ }
+ },
+};
+
+// Setting up globals used in some modules.
+global.isWorker = false;
+global.indexedDB = { open: () => ({}) };
+
+// URLSearchParams was added to the global object in Node 10.0.0. To not cause any issue
+// with prior versions, we add it to the global object if it is not defined there.
+if (!global.URLSearchParams) {
+ global.URLSearchParams = require("url").URLSearchParams;
+}
+
+if (!global.ResizeObserver) {
+ global.ResizeObserver = class ResizeObserver {
+ observe() {}
+ unobserve() {}
+ disconnect() {}
+ };
+}
+
+// Mock ChromeUtils.
+global.ChromeUtils = {
+ import: () => {},
+ defineModuleGetter: () => {},
+};
+
+global.define = function() {};
+
+// Used for the HTMLTooltip component.
+// And set "isSystemPrincipal: false" because can't support XUL element in node.
+global.document.nodePrincipal = {
+ isSystemPrincipal: false,
+};
+
+// Point to vendored-in files and mocks when needed.
+const requireHacker = require("require-hacker");
+requireHacker.global_hook("default", (path, module) => {
+ const paths = {
+ "acorn/acorn": () => getModule("devtools/shared/acorn/acorn"),
+
+ // For Enzyme
+ "react-dom": () => getModule("devtools/client/shared/vendor/react-dom"),
+ "react-dom/server": () =>
+ getModule("devtools/client/shared/vendor/react-dom-server"),
+ "react-dom/test-utils": () =>
+ getModule("devtools/client/shared/vendor/react-dom-test-utils-dev"),
+ "react-redux": () => getModule("devtools/client/shared/vendor/react-redux"),
+ // Use react-dev. This would be handled by browserLoader in Firefox.
+ react: () => getModule("devtools/client/shared/vendor/react-dev"),
+ "devtools/client/shared/vendor/react": () =>
+ getModule("devtools/client/shared/vendor/react-dev"),
+ "chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/stub-generator-helpers": () =>
+ getModule(
+ "devtools/client/webconsole/test/browser/stub-generator-helpers"
+ ),
+
+ chrome: () =>
+ `module.exports = { Cc: {}, Ci: {}, Cu: { now: () => {}}, components: {stack: {caller: ""}} }`,
+ ChromeUtils: () =>
+ `module.exports = { addProfilerMarker: () => {}, import: () => ({}) }`,
+ // Some modules depend on Chrome APIs which don't work in mocha. When such a module
+ // is required, replace it with a mock version.
+ Services: () => `module.exports = require("devtools-services")`,
+ "devtools/server/devtools-server": () =>
+ `module.exports = {DevToolsServer: {}}`,
+ "devtools/client/shared/components/SmartTrace": () =>
+ "module.exports = () => null;",
+ "devtools/client/netmonitor/src/components/TabboxPanel": () => "{}",
+ "devtools/client/webconsole/utils/context-menu": () => "{}",
+ "devtools/client/shared/telemetry": () => `module.exports = function() {
+ this.recordEvent = () => {};
+ this.getKeyedHistogramById = () => ({add: () => {}});
+ }`,
+ "devtools/shared/event-emitter": () =>
+ `module.exports = require("devtools-modules/src/utils/event-emitter")`,
+ "devtools/client/shared/unicode-url": () =>
+ `module.exports = require("devtools-modules/src/unicode-url")`,
+ "devtools/shared/DevToolsUtils": () =>
+ getModule("devtools/client/webconsole/test/node/fixtures/DevToolsUtils"),
+ "devtools/server/actors/reflow": () => "{}",
+ "devtools/shared/layout/utils": () => "{getCurrentZoom = () => {}}",
+ "resource://gre/modules/AppConstants.jsm": () => "module.exports = {};",
+ "devtools/client/framework/devtools": () => `module.exports = {
+ gDevTools: {}
+ };`,
+ "devtools/shared/async-storage": () =>
+ getModule("devtools/client/webconsole/test/node/fixtures/async-storage"),
+ "devtools/shared/generate-uuid": () =>
+ getModule(
+ "devtools/client/shared/test-helpers/jest-fixtures/generate-uuid"
+ ),
+ };
+
+ if (paths.hasOwnProperty(path)) {
+ return paths[path]();
+ }
+
+ // We need to rewrite all the modules assuming the root is mozilla-central and give them
+ // an absolute path.
+ if (path.startsWith("devtools/")) {
+ return getModule(path);
+ }
+
+ return undefined;
+});
+
+// Configure enzyme with React 16 adapter. This needs to be done after we set the
+// requireHack hook so `require()` calls in Enzyme are handled as well.
+const Enzyme = require("enzyme");
+const Adapter = require("enzyme-adapter-react-16");
+Enzyme.configure({ adapter: new Adapter() });
diff --git a/devtools/client/webconsole/test/node/package.json b/devtools/client/webconsole/test/node/package.json
new file mode 100644
index 0000000000..705aa9fc18
--- /dev/null
+++ b/devtools/client/webconsole/test/node/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "webconsole-tests",
+ "version": "0.0.1",
+ "scripts": {
+ "//": [
+ "Here's the script to run tests with `npm test`. Here's what it does: ",
+ " * Run mocha on components, middleware, store and utils folders, on .test.js files.",
+ " We need to specify them so we don't run unwanted tests (e.g. in node_modules).",
+ " * We require jsdom-global to inject `document` and `window` objects which are",
+ " not in nodejs natively.",
+ " * Finally we require mocha-test-setup where we configure Enzyme and",
+ " intercept require() calls with require-hacker and modify them if needed."
+ ],
+ "test": "mocha \"./{,!(node_modules)/**}/*.test.js\" -r mock-local-storage -r jsdom-global/register -r ./mocha-test-setup.js",
+ "test-ci": "mocha \"./{,!(node_modules)/**}/*.test.js\" -r mock-local-storage -r jsdom-global/register -r ./mocha-test-setup.js --reporter json"
+ },
+ "dependencies": {
+ "@babel/core": "^7.8.7",
+ "@babel/plugin-proposal-class-properties": "7.10.4",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-proposal-optional-chaining": "^7.8.3",
+ "@babel/register": "^7.8.6",
+ "babel-plugin-transform-amd-to-commonjs": "1.4.0",
+ "devtools-modules": "0.0.37",
+ "devtools-services": "^0.0.3",
+ "enzyme": "^3.3.0",
+ "enzyme-adapter-react-16": "^1.1.1",
+ "expect": "^1.16.0",
+ "jsdom": "^9.4.1",
+ "jsdom-global": "^2.0.0",
+ "mocha": "^5.0.1",
+ "mock-local-storage": "^1.0.5",
+ "require-hacker": "^2.1.4",
+ "sinon": "^1.17.5"
+ }
+} \ No newline at end of file
diff --git a/devtools/client/webconsole/test/node/store/.eslintrc.js b/devtools/client/webconsole/test/node/store/.eslintrc.js
new file mode 100644
index 0000000000..9b9dfe0ad1
--- /dev/null
+++ b/devtools/client/webconsole/test/node/store/.eslintrc.js
@@ -0,0 +1,5 @@
+"use strict";
+
+module.exports = {
+ extends: "../.eslintrc.mocha.js",
+};
diff --git a/devtools/client/webconsole/test/node/store/filters.test.js b/devtools/client/webconsole/test/node/store/filters.test.js
new file mode 100644
index 0000000000..6b1dea5f62
--- /dev/null
+++ b/devtools/client/webconsole/test/node/store/filters.test.js
@@ -0,0 +1,338 @@
+/* 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/actions/index");
+const { messagesAdd } = require("devtools/client/webconsole/actions/index");
+const { ConsoleCommand } = require("devtools/client/webconsole/types");
+const {
+ getVisibleMessages,
+} = require("devtools/client/webconsole/selectors/messages");
+const {
+ getAllFilters,
+} = require("devtools/client/webconsole/selectors/filters");
+const {
+ setupStore,
+ getFiltersPrefs,
+} = require("devtools/client/webconsole/test/node/helpers");
+const { FILTERS, PREFS } = require("devtools/client/webconsole/constants");
+const {
+ stubPackets,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const {
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+
+describe("Filtering", () => {
+ let store;
+ let numMessages;
+ // Number of messages in prepareBaseStore which are not filtered out, i.e. Evaluation
+ // Results and console commands.
+ const numUnfilterableMessages = 2;
+
+ beforeEach(() => {
+ store = prepareBaseStore();
+ store.dispatch(actions.filtersClear());
+ numMessages = getVisibleMessages(store.getState()).length;
+ });
+
+ /**
+ * Tests for filter buttons in Console toolbar. The test switches off
+ * all filters and consequently tests one by one on the list of messages
+ * created in `prepareBaseStore` method.
+ */
+ describe("Level filter", () => {
+ beforeEach(() => {
+ // Switch off all filters (include those which are on by default).
+ store.dispatch(actions.filtersClear());
+ store.dispatch(actions.filterToggle(FILTERS.DEBUG));
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+ store.dispatch(actions.filterToggle(FILTERS.INFO));
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+ store.dispatch(actions.filterToggle(FILTERS.WARN));
+
+ const messages = getVisibleMessages(store.getState());
+ expect(messages.length).toEqual(numUnfilterableMessages);
+ });
+
+ it("filters log messages", () => {
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+
+ const messages = getVisibleMessages(store.getState());
+ expect(messages.length).toEqual(numUnfilterableMessages + 6);
+ });
+
+ it("filters debug messages", () => {
+ store.dispatch(actions.filterToggle(FILTERS.DEBUG));
+
+ const messages = getVisibleMessages(store.getState());
+ expect(messages.length).toEqual(numUnfilterableMessages + 1);
+ });
+
+ it("filters info messages", () => {
+ store.dispatch(actions.filterToggle(FILTERS.INFO));
+
+ const messages = getVisibleMessages(store.getState());
+ expect(messages.length).toEqual(numUnfilterableMessages + 1);
+ });
+
+ it("filters warning messages", () => {
+ store.dispatch(actions.filterToggle(FILTERS.WARN));
+
+ const messages = getVisibleMessages(store.getState());
+ expect(messages.length).toEqual(numUnfilterableMessages + 1);
+ });
+
+ it("filters error messages", () => {
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+
+ const messages = getVisibleMessages(store.getState());
+ expect(messages.length).toEqual(numUnfilterableMessages + 5);
+ });
+
+ it("filters css messages", () => {
+ const message = stubPreparedMessages.get(
+ "Unknown property ‘such-unknown-property’. Declaration dropped."
+ );
+ store.dispatch(messagesAdd([message]));
+
+ let messages = getVisibleMessages(store.getState());
+ expect(messages.length).toEqual(numUnfilterableMessages);
+
+ store.dispatch(actions.filterToggle("css"));
+ messages = getVisibleMessages(store.getState());
+ expect(messages.length).toEqual(numUnfilterableMessages + 1);
+ });
+
+ it("filters xhr messages", () => {
+ const message = stubPreparedMessages.get("XHR GET request");
+ store.dispatch(messagesAdd([message]));
+
+ let messages = getVisibleMessages(store.getState());
+ expect(messages.length).toEqual(numUnfilterableMessages);
+
+ store.dispatch(actions.filterToggle("netxhr"));
+ messages = getVisibleMessages(store.getState());
+ expect(messages.length).toEqual(numUnfilterableMessages + 1);
+ });
+
+ it("filters network messages", () => {
+ const message = stubPreparedMessages.get("GET request update");
+ store.dispatch(messagesAdd([message]));
+
+ let messages = getVisibleMessages(store.getState());
+ expect(messages.length).toEqual(numUnfilterableMessages);
+
+ store.dispatch(actions.filterToggle("net"));
+ messages = getVisibleMessages(store.getState());
+ expect(messages.length).toEqual(numUnfilterableMessages + 1);
+ });
+ });
+
+ describe("Text filter", () => {
+ it("set the expected property on the store", () => {
+ store.dispatch(actions.filterTextSet("danger"));
+ expect(getAllFilters(store.getState()).text).toEqual("danger");
+ });
+
+ it("matches on value grips", () => {
+ store.dispatch(actions.filterTextSet("danger"));
+ let messages = getVisibleMessages(store.getState());
+ expect(messages.length - numUnfilterableMessages).toEqual(1);
+
+ // Checks that trimming works.
+ store.dispatch(actions.filterTextSet(" danger "));
+ messages = getVisibleMessages(store.getState());
+ expect(messages.length - numUnfilterableMessages).toEqual(1);
+ });
+
+ it("matches unicode values", () => {
+ store.dispatch(actions.filterTextSet("鼬"));
+
+ const messages = getVisibleMessages(store.getState());
+ expect(messages.length - numUnfilterableMessages).toEqual(1);
+ });
+
+ it("matches locations", () => {
+ // Add a message with a different filename.
+ const locationMsg = Object.assign(
+ {},
+ stubPackets.get("console.log('foobar', 'test')")
+ );
+ locationMsg.message = Object.assign({}, locationMsg.message, {
+ filename: "search-location-test.js",
+ });
+ store.dispatch(messagesAdd([locationMsg]));
+
+ store.dispatch(actions.filterTextSet("search-location-test.js"));
+
+ const messages = getVisibleMessages(store.getState());
+ expect(messages.length - numUnfilterableMessages).toEqual(1);
+ });
+
+ it("matches stacktrace functionName", () => {
+ const traceMessage = stubPackets.get("console.trace()");
+ store.dispatch(messagesAdd([traceMessage]));
+
+ store.dispatch(actions.filterTextSet("testStacktraceFiltering"));
+
+ const messages = getVisibleMessages(store.getState());
+ expect(messages.length - numUnfilterableMessages).toEqual(1);
+ });
+
+ it("matches stacktrace location", () => {
+ const traceMessage = stubPackets.get("console.trace()");
+ traceMessage.message = Object.assign({}, traceMessage.message, {
+ filename: "search-location-test.js",
+ lineNumber: 85,
+ columnNumber: 13,
+ });
+
+ store.dispatch(messagesAdd([traceMessage]));
+
+ store.dispatch(actions.filterTextSet("search-location-test.js:85:13"));
+
+ const messages = getVisibleMessages(store.getState());
+ expect(messages.length - numUnfilterableMessages).toEqual(1);
+ });
+
+ it("matches prefixed log message", () => {
+ const stub = {
+ level: "debug",
+ filename: "resource:///modules/CustomizableUI.jsm",
+ lineNumber: 181,
+ functionName: "initialize",
+ timeStamp: 1519311532912,
+ arguments: ["Initializing"],
+ prefix: "MyNicePrefix",
+ workerType: "none",
+ styles: [],
+ category: "webdev",
+ _type: "ConsoleAPI",
+ };
+ store.dispatch(messagesAdd([stub]));
+
+ store.dispatch(actions.filterTextSet("MyNice"));
+ let messages = getVisibleMessages(store.getState());
+ expect(messages.length - numUnfilterableMessages).toEqual(1);
+
+ store.dispatch(actions.filterTextSet("MyNicePrefix"));
+ messages = getVisibleMessages(store.getState());
+ expect(messages.length - numUnfilterableMessages).toEqual(1);
+
+ store.dispatch(actions.filterTextSet("MyNicePrefix:"));
+ messages = getVisibleMessages(store.getState());
+ expect(messages.length - numUnfilterableMessages).toEqual(1);
+ });
+
+ it("restores all messages once text is cleared", () => {
+ store.dispatch(actions.filterTextSet("danger"));
+ store.dispatch(actions.filterTextSet(""));
+
+ const messages = getVisibleMessages(store.getState());
+ expect(messages.length).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(FILTERS.ERROR));
+ store.dispatch(actions.filterToggle(FILTERS.CSS));
+ store.dispatch(actions.filterToggle(FILTERS.NET));
+ store.dispatch(actions.filterToggle(FILTERS.NETXHR));
+ store.dispatch(actions.filterTextSet("foobar"));
+
+ expect(getAllFilters(store.getState())).toEqual({
+ // default
+ [FILTERS.WARN]: true,
+ [FILTERS.LOG]: true,
+ [FILTERS.INFO]: true,
+ [FILTERS.DEBUG]: true,
+ // changed
+ [FILTERS.ERROR]: false,
+ [FILTERS.CSS]: true,
+ [FILTERS.NET]: true,
+ [FILTERS.NETXHR]: true,
+ [FILTERS.TEXT]: "foobar",
+ });
+ expect(getFiltersPrefs()).toEqual({
+ [PREFS.FILTER.WARN]: true,
+ [PREFS.FILTER.LOG]: true,
+ [PREFS.FILTER.INFO]: true,
+ [PREFS.FILTER.DEBUG]: true,
+ [PREFS.FILTER.ERROR]: false,
+ [PREFS.FILTER.CSS]: true,
+ [PREFS.FILTER.NET]: true,
+ [PREFS.FILTER.NETXHR]: true,
+ });
+
+ store.dispatch(actions.filtersClear());
+
+ expect(getAllFilters(store.getState())).toEqual({
+ [FILTERS.CSS]: false,
+ [FILTERS.DEBUG]: true,
+ [FILTERS.ERROR]: true,
+ [FILTERS.INFO]: true,
+ [FILTERS.LOG]: true,
+ [FILTERS.NET]: false,
+ [FILTERS.NETXHR]: false,
+ [FILTERS.WARN]: true,
+ [FILTERS.TEXT]: "",
+ });
+
+ expect(getFiltersPrefs()).toEqual({
+ [PREFS.FILTER.CSS]: false,
+ [PREFS.FILTER.DEBUG]: true,
+ [PREFS.FILTER.ERROR]: true,
+ [PREFS.FILTER.INFO]: true,
+ [PREFS.FILTER.LOG]: true,
+ [PREFS.FILTER.NET]: false,
+ [PREFS.FILTER.NETXHR]: false,
+ [PREFS.FILTER.WARN]: true,
+ });
+ });
+});
+
+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",
+ "TypeError longString message",
+ "console.debug('debug message');",
+ "console.info('info message');",
+ "console.error('error message');",
+ "console.table(['red', 'green', 'blue']);",
+ "console.assert(false, {message: 'foobar'})",
+ // This is a 404 request, it's displayed as an error
+ "GET request update",
+ "console.group('bar')",
+ "console.groupEnd()",
+ ]);
+
+ // Console Command - never filtered
+ store.dispatch(
+ messagesAdd([new ConsoleCommand({ messageText: `console.warn("x")` })])
+ );
+
+ return store;
+}
diff --git a/devtools/client/webconsole/test/node/store/hidden-messages.test.js b/devtools/client/webconsole/test/node/store/hidden-messages.test.js
new file mode 100644
index 0000000000..7beab96361
--- /dev/null
+++ b/devtools/client/webconsole/test/node/store/hidden-messages.test.js
@@ -0,0 +1,195 @@
+/* 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/actions/index");
+const {
+ getFilteredMessagesCount,
+} = require("devtools/client/webconsole/selectors/messages");
+const { setupStore } = require("devtools/client/webconsole/test/node/helpers");
+const { FILTERS } = require("devtools/client/webconsole/constants");
+const {
+ stubPackets,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+
+describe("Filtering - Hidden messages", () => {
+ let store;
+
+ beforeEach(() => {
+ store = prepareBaseStore();
+ // Switch off all filters (include those which are on by default).
+ store.dispatch(actions.filtersClear());
+ store.dispatch(actions.filterToggle(FILTERS.DEBUG));
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+ store.dispatch(actions.filterToggle(FILTERS.INFO));
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+ store.dispatch(actions.filterToggle(FILTERS.WARN));
+ });
+
+ it("has the expected numbers", () => {
+ const counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual(BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT);
+ });
+
+ it("has the expected numbers when there is a text search", () => {
+ // "info" is disabled and the filter input only matches a warning message.
+ store.dispatch(actions.filtersClear());
+ store.dispatch(actions.filterToggle(FILTERS.INFO));
+ store.dispatch(actions.filterTextSet("danger, will robinson!"));
+
+ let counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual({
+ [FILTERS.ERROR]: 0,
+ [FILTERS.WARN]: 0,
+ [FILTERS.LOG]: 0,
+ [FILTERS.INFO]: 1,
+ [FILTERS.DEBUG]: 0,
+ [FILTERS.TEXT]: 9,
+ global: 10,
+ });
+
+ // Numbers update if the text search is cleared.
+ store.dispatch(actions.filterTextSet(""));
+ counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual({
+ [FILTERS.ERROR]: 0,
+ [FILTERS.WARN]: 0,
+ [FILTERS.LOG]: 0,
+ [FILTERS.INFO]: 1,
+ [FILTERS.DEBUG]: 0,
+ [FILTERS.TEXT]: 0,
+ global: 1,
+ });
+ });
+
+ it("has the expected numbers when there's a text search on disabled categories", () => {
+ store.dispatch(actions.filterTextSet("danger, will robinson!"));
+ let counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual({
+ [FILTERS.ERROR]: 3,
+ [FILTERS.WARN]: 1,
+ [FILTERS.LOG]: 5,
+ [FILTERS.INFO]: 1,
+ [FILTERS.DEBUG]: 1,
+ [FILTERS.TEXT]: 0,
+ global: 11,
+ });
+
+ // Numbers update if the text search is cleared.
+ store.dispatch(actions.filterTextSet(""));
+ counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual(BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT);
+ });
+
+ it("updates when messages are added", () => {
+ const packets = MESSAGES.map(key => stubPackets.get(key));
+ store.dispatch(actions.messagesAdd(packets));
+
+ const counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual({
+ [FILTERS.ERROR]: 6,
+ [FILTERS.WARN]: 2,
+ [FILTERS.LOG]: 10,
+ [FILTERS.INFO]: 2,
+ [FILTERS.DEBUG]: 2,
+ [FILTERS.TEXT]: 0,
+ global: 22,
+ });
+ });
+
+ it("updates when filters are toggled", () => {
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+
+ let counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual(
+ Object.assign({}, BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT, {
+ [FILTERS.LOG]: 0,
+ global: 6,
+ })
+ );
+
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+
+ counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual(
+ Object.assign({}, BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT, {
+ [FILTERS.ERROR]: 0,
+ [FILTERS.LOG]: 0,
+ global: 3,
+ })
+ );
+
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+ counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual(BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT);
+ });
+
+ it("has the expected numbers after message clear", () => {
+ // Add a text search to make sure it is handled as well.
+ store.dispatch(actions.filterTextSet("danger, will robinson!"));
+ store.dispatch(actions.messagesClear());
+ const counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual({
+ [FILTERS.ERROR]: 0,
+ [FILTERS.WARN]: 0,
+ [FILTERS.LOG]: 0,
+ [FILTERS.INFO]: 0,
+ [FILTERS.DEBUG]: 0,
+ [FILTERS.TEXT]: 0,
+ global: 0,
+ });
+ });
+
+ it("has the expected numbers after filter clear", () => {
+ // Add a text search to make sure it is handled as well.
+ store.dispatch(actions.filterTextSet("danger, will robinson!"));
+ store.dispatch(actions.filtersClear());
+ const counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual({
+ [FILTERS.ERROR]: 0,
+ [FILTERS.WARN]: 0,
+ [FILTERS.LOG]: 0,
+ [FILTERS.INFO]: 0,
+ [FILTERS.DEBUG]: 0,
+ [FILTERS.TEXT]: 0,
+ global: 0,
+ });
+ });
+});
+
+const MESSAGES = [
+ // Error
+ "ReferenceError: asdf is not defined",
+ "console.error('error message');",
+ "console.assert(false, {message: 'foobar'})",
+ // Warning
+ "console.warn('danger, will robinson!')",
+ // Log
+ "console.log('foobar', 'test')",
+ "console.log(undefined)",
+ "console.count('bar')",
+ "console.log('鼬')",
+ "console.table(['red', 'green', 'blue']);",
+ // Info
+ "console.info('info message');",
+ // Debug
+ "console.debug('debug message');",
+];
+
+const BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT = {
+ [FILTERS.ERROR]: 3,
+ [FILTERS.WARN]: 1,
+ [FILTERS.LOG]: 5,
+ [FILTERS.INFO]: 1,
+ [FILTERS.DEBUG]: 1,
+ [FILTERS.TEXT]: 0,
+ global: 11,
+};
+
+function prepareBaseStore() {
+ return setupStore(MESSAGES);
+}
diff --git a/devtools/client/webconsole/test/node/store/messages.test.js b/devtools/client/webconsole/test/node/store/messages.test.js
new file mode 100644
index 0000000000..abe64cd108
--- /dev/null
+++ b/devtools/client/webconsole/test/node/store/messages.test.js
@@ -0,0 +1,1109 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const {
+ getAllMessagesUiById,
+ getAllMessagesPayloadById,
+ getAllNetworkMessagesUpdateById,
+ getAllRepeatById,
+ getCurrentGroup,
+ getGroupsById,
+ getAllMessagesById,
+ getVisibleMessages,
+} = require("devtools/client/webconsole/selectors/messages");
+
+const {
+ clonePacket,
+ getFirstMessage,
+ getLastMessage,
+ getMessageAt,
+ setupActions,
+ setupStore,
+} = require("devtools/client/webconsole/test/node/helpers");
+const {
+ stubPackets,
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const { MESSAGE_TYPE } = require("devtools/client/webconsole/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')");
+ dispatch(actions.messagesAdd([packet]));
+
+ const message = stubPreparedMessages.get("console.log('foobar', 'test')");
+
+ expect(getFirstMessage(getState())).toEqual(message);
+ });
+
+ it("increments repeat on a repeating log message", () => {
+ const key1 = "console.log('foobar', 'test')";
+ const { dispatch, getState } = setupStore([key1, key1], { actions });
+
+ const packet = clonePacket(stubPackets.get(key1));
+ const packet2 = clonePacket(packet);
+
+ // Repeat ID must be the same even if the timestamp is different.
+ packet.message.timeStamp = 1;
+ packet2.message.timeStamp = 2;
+ dispatch(actions.messagesAdd([packet, packet2]));
+
+ const messages = getAllMessagesById(getState());
+
+ expect(messages.size).toBe(1);
+ const repeat = getAllRepeatById(getState());
+ expect(repeat[getFirstMessage(getState()).id]).toBe(4);
+ });
+
+ it("doesn't increment repeat on same log message with different locations", () => {
+ const key1 = "console.log('foobar', 'test')";
+ const { dispatch, getState } = setupStore();
+
+ const packet = clonePacket(stubPackets.get(key1));
+
+ // Dispatch original packet.
+ dispatch(actions.messagesAdd([packet]));
+
+ // Dispatch same packet with modified column number.
+ packet.message.columnNumber = packet.message.columnNumber + 1;
+ dispatch(actions.messagesAdd([packet]));
+
+ // Dispatch same packet with modified line number.
+ packet.message.lineNumber = packet.message.lineNumber + 1;
+ dispatch(actions.messagesAdd([packet]));
+
+ const messages = getAllMessagesById(getState());
+
+ expect(messages.size).toBe(3);
+
+ const repeat = getAllRepeatById(getState());
+ expect(Object.keys(repeat).length).toBe(0);
+ });
+
+ it("increments repeat on a repeating css message", () => {
+ const key1 =
+ "Unknown property ‘such-unknown-property’. Declaration dropped.";
+ const { dispatch, getState } = setupStore([key1, key1]);
+
+ const packet = clonePacket(stubPackets.get(key1));
+
+ // Repeat ID must be the same even if the timestamp is different.
+ packet.pageError.timeStamp = 1;
+ dispatch(actions.messagesAdd([packet]));
+ packet.pageError.timeStamp = 2;
+ dispatch(actions.messagesAdd([packet]));
+
+ const messages = getAllMessagesById(getState());
+
+ expect(messages.size).toBe(1);
+
+ const repeat = getAllRepeatById(getState());
+ expect(repeat[getFirstMessage(getState()).id]).toBe(4);
+ });
+
+ it("doesn't increment repeat on same css message with different locations", () => {
+ const key1 =
+ "Unknown property ‘such-unknown-property’. Declaration dropped.";
+ const { dispatch, getState } = setupStore();
+
+ const packet = clonePacket(stubPackets.get(key1));
+
+ // Dispatch original packet.
+ dispatch(actions.messagesAdd([packet]));
+
+ // Dispatch same packet with modified column number.
+ packet.pageError.columnNumber = packet.pageError.columnNumber + 1;
+ dispatch(actions.messagesAdd([packet]));
+
+ // Dispatch same packet with modified line number.
+ packet.pageError.lineNumber = packet.pageError.lineNumber + 1;
+ dispatch(actions.messagesAdd([packet]));
+
+ const messages = getAllMessagesById(getState());
+
+ expect(messages.size).toBe(3);
+
+ const repeat = getAllRepeatById(getState());
+ expect(Object.keys(repeat).length).toBe(0);
+ });
+
+ it("increments repeat on a repeating error message", () => {
+ const key1 = "ReferenceError: asdf is not defined";
+ const { dispatch, getState } = setupStore([key1, key1]);
+
+ const packet = clonePacket(stubPackets.get(key1));
+
+ // Repeat ID must be the same even if the timestamp is different.
+ packet.pageError.timeStamp = 1;
+ dispatch(actions.messagesAdd([packet]));
+ packet.pageError.timeStamp = 2;
+ dispatch(actions.messagesAdd([packet]));
+
+ const messages = getAllMessagesById(getState());
+
+ expect(messages.size).toBe(1);
+
+ const repeat = getAllRepeatById(getState());
+ expect(repeat[getFirstMessage(getState()).id]).toBe(4);
+ });
+
+ it("does not increment repeat after closing a group", () => {
+ const logKey = "console.log('foobar', 'test')";
+ const { getState } = setupStore([
+ logKey,
+ logKey,
+ "console.group('bar')",
+ logKey,
+ logKey,
+ logKey,
+ "console.groupEnd()",
+ logKey,
+ ]);
+
+ const messages = getAllMessagesById(getState());
+
+ expect(messages.size).toBe(4);
+ const repeat = getAllRepeatById(getState());
+ expect(repeat[getFirstMessage(getState()).id]).toBe(2);
+ expect(repeat[getMessageAt(getState(), 2).id]).toBe(3);
+ expect(repeat[getLastMessage(getState()).id]).toBe(undefined);
+ });
+
+ it("doesn't increment undefined messages coming from different places", () => {
+ const { getState } = setupStore(["console.log(undefined)", "undefined"]);
+
+ const messages = getAllMessagesById(getState());
+ expect(messages.size).toBe(2);
+
+ const repeat = getAllRepeatById(getState());
+ expect(Object.keys(repeat).length).toBe(0);
+ });
+
+ it("doesn't increment successive falsy but different messages", () => {
+ const { getState } = setupStore(
+ ["console.log(NaN)", "console.log(undefined)", "console.log(null)"],
+ { actions }
+ );
+
+ const messages = getAllMessagesById(getState());
+ expect(messages.size).toBe(3);
+ const repeat = getAllRepeatById(getState());
+ expect(Object.keys(repeat).length).toBe(0);
+ });
+
+ it("increment falsy messages when expected", () => {
+ const { dispatch, getState } = setupStore();
+
+ const nanPacket = stubPackets.get("console.log(NaN)");
+ dispatch(actions.messagesAdd([nanPacket, nanPacket]));
+ let messages = getAllMessagesById(getState());
+ expect(messages.size).toBe(1);
+ let repeat = getAllRepeatById(getState());
+ expect(repeat[getLastMessage(getState()).id]).toBe(2);
+
+ const undefinedPacket = stubPackets.get("console.log(undefined)");
+ dispatch(actions.messagesAdd([undefinedPacket, undefinedPacket]));
+ messages = getAllMessagesById(getState());
+ expect(messages.size).toBe(2);
+ repeat = getAllRepeatById(getState());
+ expect(repeat[getLastMessage(getState()).id]).toBe(2);
+
+ const nullPacket = stubPackets.get("console.log(null)");
+ dispatch(actions.messagesAdd([nullPacket, nullPacket]));
+ messages = getAllMessagesById(getState());
+ expect(messages.size).toBe(3);
+ repeat = getAllRepeatById(getState());
+ expect(repeat[getLastMessage(getState()).id]).toBe(2);
+ });
+
+ it("does not clobber a unique message", () => {
+ const key1 = "console.log('foobar', 'test')";
+ const { dispatch, getState } = setupStore([key1, key1]);
+
+ const packet = stubPackets.get(key1);
+ dispatch(actions.messagesAdd([packet]));
+
+ const packet2 = stubPackets.get("console.log(undefined)");
+ dispatch(actions.messagesAdd([packet2]));
+
+ const messages = getAllMessagesById(getState());
+ expect(messages.size).toBe(2);
+
+ const repeat = getAllRepeatById(getState());
+ expect(repeat[getFirstMessage(getState()).id]).toBe(3);
+ expect(repeat[getLastMessage(getState()).id]).toBe(undefined);
+ });
+
+ it("adds a message in response to console.clear()", () => {
+ const { dispatch, getState } = setupStore([]);
+
+ dispatch(actions.messagesAdd([stubPackets.get("console.clear()")]));
+
+ const messages = getAllMessagesById(getState());
+
+ expect(messages.size).toBe(1);
+ expect(getFirstMessage(getState()).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('foobar', 'test')",
+ "console.log(undefined)",
+ "console.table(['red', 'green', 'blue']);",
+ "console.group('bar')",
+ ]);
+
+ dispatch(actions.messagesClear());
+
+ const state = getState();
+ expect(getAllMessagesById(state).size).toBe(0);
+ expect(getVisibleMessages(state).length).toBe(0);
+ expect(getAllMessagesUiById(state).length).toBe(0);
+ expect(getGroupsById(state).size).toBe(0);
+ expect(getAllMessagesPayloadById(state).size).toBe(0);
+ expect(getCurrentGroup(state)).toBe(null);
+ expect(getAllRepeatById(state)).toEqual({});
+ });
+
+ it("cleans the repeatsById object when messages are pruned", () => {
+ const { dispatch, getState } = setupStore(
+ [
+ "console.log('foobar', 'test')",
+ "console.log('foobar', 'test')",
+ "console.log(undefined)",
+ "console.log(undefined)",
+ ],
+ {
+ actions,
+ storeOptions: {
+ logLimit: 2,
+ },
+ }
+ );
+
+ // Check that we have the expected data.
+ let repeats = getAllRepeatById(getState());
+ expect(Object.keys(repeats).length).toBe(2);
+ const lastMessageId = getLastMessage(getState()).id;
+
+ // This addition will prune the first message out of the store.
+ let packet = stubPackets.get("console.log('foobar', 'test')");
+ dispatch(actions.messagesAdd([packet]));
+
+ repeats = getAllRepeatById(getState());
+
+ // There should be only the data for the "undefined" message.
+ expect(Object.keys(repeats)).toEqual([lastMessageId]);
+ expect(Object.keys(repeats).length).toBe(1);
+ expect(repeats[lastMessageId]).toBe(2);
+
+ // This addition will prune the first message out of the store.
+ packet = stubPackets.get("console.log(undefined)");
+ dispatch(actions.messagesAdd([packet]));
+
+ // repeatById should now be empty.
+ expect(getAllRepeatById(getState())).toEqual({});
+ });
+
+ it("properly limits number of messages", () => {
+ const logLimit = 1000;
+ const { dispatch, getState } = setupStore([], {
+ storeOptions: {
+ logLimit,
+ },
+ });
+
+ const packet = clonePacket(stubPackets.get("console.log(undefined)"));
+
+ for (let i = 1; i <= logLimit + 2; i++) {
+ packet.message.arguments = [`message num ${i}`];
+ dispatch(actions.messagesAdd([packet]));
+ }
+
+ const messages = getAllMessagesById(getState());
+ expect(messages.size).toBe(logLimit);
+ expect(getFirstMessage(getState()).parameters[0]).toBe(`message num 3`);
+ expect(getLastMessage(getState()).parameters[0]).toBe(
+ `message num ${logLimit + 2}`
+ );
+ });
+
+ it("properly limits number of messages when there are nested groups", () => {
+ const logLimit = 1000;
+ const { dispatch, getState } = setupStore([], {
+ storeOptions: {
+ logLimit,
+ },
+ });
+
+ const packet = clonePacket(stubPackets.get("console.log(undefined)"));
+ const packetGroup = clonePacket(stubPackets.get("console.group('bar')"));
+ const packetGroupEnd = clonePacket(stubPackets.get("console.groupEnd()"));
+
+ packetGroup.message.arguments = [`group-1`];
+ dispatch(actions.messagesAdd([packetGroup]));
+ packetGroup.message.arguments = [`group-1-1`];
+ dispatch(actions.messagesAdd([packetGroup]));
+ packetGroup.message.arguments = [`group-1-1-1`];
+ dispatch(actions.messagesAdd([packetGroup]));
+ packet.message.arguments = [`message-in-group-1`];
+ dispatch(actions.messagesAdd([packet]));
+ packet.message.arguments = [`message-in-group-2`];
+ dispatch(actions.messagesAdd([packet]));
+ // Closing group-1-1-1
+ dispatch(actions.messagesAdd([packetGroupEnd]));
+ // Closing group-1-1
+ dispatch(actions.messagesAdd([packetGroupEnd]));
+ // Closing group-1
+ dispatch(actions.messagesAdd([packetGroupEnd]));
+
+ for (let i = 0; i < logLimit; i++) {
+ packet.message.arguments = [`message-${i}`];
+ dispatch(actions.messagesAdd([packet]));
+ }
+
+ const visibleMessages = getVisibleMessages(getState());
+ const messages = getAllMessagesById(getState());
+
+ expect(messages.size).toBe(logLimit);
+ expect(visibleMessages.length).toBe(logLimit);
+ expect(messages.get(visibleMessages[0]).parameters[0]).toBe(`message-0`);
+ expect(messages.get(visibleMessages[logLimit - 1]).parameters[0]).toBe(
+ `message-${logLimit - 1}`
+ );
+
+ // The groups were cleaned up.
+ const groups = getGroupsById(getState());
+ expect(groups.size).toBe(0);
+ });
+
+ it("properly limits number of groups", () => {
+ const logLimit = 100;
+ const { dispatch, getState } = setupStore([], {
+ storeOptions: { logLimit },
+ });
+
+ const packet = clonePacket(stubPackets.get("console.log(undefined)"));
+ const packetGroup = clonePacket(stubPackets.get("console.group('bar')"));
+ const packetGroupEnd = clonePacket(stubPackets.get("console.groupEnd()"));
+
+ for (let i = 0; i < logLimit + 2; i++) {
+ dispatch(actions.messagesAdd([packetGroup]));
+ packet.message.arguments = [`message-${i}-a`];
+ dispatch(actions.messagesAdd([packet]));
+ packet.message.arguments = [`message-${i}-b`];
+ dispatch(actions.messagesAdd([packet]));
+ dispatch(actions.messagesAdd([packetGroupEnd]));
+ }
+
+ const visibleMessages = getVisibleMessages(getState());
+ const messages = getAllMessagesById(getState());
+ // We should have three times the logLimit since each group has one message inside.
+ expect(messages.size).toBe(logLimit * 3);
+
+ // We should have logLimit number of groups
+ const groups = getGroupsById(getState());
+ expect(groups.size).toBe(logLimit);
+
+ expect(messages.get(visibleMessages[1]).parameters[0]).toBe(
+ `message-2-a`
+ );
+ expect(getLastMessage(getState()).parameters[0]).toBe(
+ `message-${logLimit + 1}-b`
+ );
+ });
+
+ it("properly limits number of collapsed groups", () => {
+ const logLimit = 100;
+ const { dispatch, getState } = setupStore([], {
+ storeOptions: { logLimit },
+ });
+
+ const packet = clonePacket(stubPackets.get("console.log(undefined)"));
+ const packetGroupCollapsed = clonePacket(
+ stubPackets.get("console.groupCollapsed('foo')")
+ );
+ const packetGroupEnd = clonePacket(stubPackets.get("console.groupEnd()"));
+
+ for (let i = 0; i < logLimit + 2; i++) {
+ packetGroupCollapsed.message.arguments = [`group-${i}`];
+ dispatch(actions.messagesAdd([packetGroupCollapsed]));
+ packet.message.arguments = [`message-${i}-a`];
+ dispatch(actions.messagesAdd([packet]));
+ packet.message.arguments = [`message-${i}-b`];
+ dispatch(actions.messagesAdd([packet]));
+ dispatch(actions.messagesAdd([packetGroupEnd]));
+ }
+
+ const messages = getAllMessagesById(getState());
+ // We should have three times the logLimit since each group has two message inside.
+ expect(messages.size).toBe(logLimit * 3);
+
+ // We should have logLimit number of groups
+ const groups = getGroupsById(getState());
+ expect(groups.size).toBe(logLimit);
+
+ expect(getFirstMessage(getState()).parameters[0]).toBe(`group-2`);
+ expect(getLastMessage(getState()).parameters[0]).toBe(
+ `message-${logLimit + 1}-b`
+ );
+
+ const visibleMessages = getVisibleMessages(getState());
+ expect(visibleMessages.length).toBe(logLimit);
+ const lastVisibleMessageId = visibleMessages[visibleMessages.length - 1];
+ expect(messages.get(lastVisibleMessageId).parameters[0]).toBe(
+ `group-${logLimit + 1}`
+ );
+ });
+
+ it("does not add null messages to the store", () => {
+ const { dispatch, getState } = setupStore();
+
+ const message = stubPackets.get("console.time('bar')");
+ dispatch(actions.messagesAdd([message]));
+
+ const messages = getAllMessagesById(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.messagesAdd([packet]));
+
+ const tableMessage = getLastMessage(getState());
+ 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.messagesAdd([message]));
+
+ const messages = getAllMessagesById(getState());
+ expect(messages.size).toBe(1);
+ });
+
+ it("adds messages in console.group to the store", () => {
+ const { dispatch, getState } = setupStore();
+
+ const groupPacket = stubPackets.get("console.group('bar')");
+ const groupEndPacket = stubPackets.get("console.groupEnd('bar')");
+ const logPacket = stubPackets.get("console.log('foobar', 'test')");
+
+ const packets = [
+ groupPacket,
+ logPacket,
+ groupPacket,
+ groupPacket,
+ logPacket,
+ groupEndPacket,
+ logPacket,
+ groupEndPacket,
+ logPacket,
+ groupEndPacket,
+ logPacket,
+ ];
+ dispatch(actions.messagesAdd(packets));
+
+ // Here is what we should have (8 messages)
+ // ▼ bar
+ // | foobar test
+ // | ▼ bar
+ // | | ▼ bar
+ // | | | foobar test
+ // | | foobar test
+ // | foobar test
+ // foobar test
+
+ const isNotGroupEnd = p => p !== groupEndPacket;
+ const messageCount = packets.filter(isNotGroupEnd).length;
+
+ const messages = getAllMessagesById(getState());
+ const visibleMessages = getVisibleMessages(getState());
+ expect(messages.size).toBe(messageCount);
+ expect(visibleMessages.length).toBe(messageCount);
+ });
+
+ it("sets groupId property as expected", () => {
+ const { dispatch, getState } = setupStore();
+
+ dispatch(
+ actions.messagesAdd([
+ stubPackets.get("console.group('bar')"),
+ stubPackets.get("console.log('foobar', 'test')"),
+ ])
+ );
+
+ const messages = getAllMessagesById(getState());
+ expect(messages.size).toBe(2);
+ expect(getLastMessage(getState()).groupId).toBe(
+ getFirstMessage(getState()).id
+ );
+ });
+
+ it("does not display console.groupEnd messages to the store", () => {
+ const { dispatch, getState } = setupStore();
+
+ const message = stubPackets.get("console.groupEnd('bar')");
+ dispatch(actions.messagesAdd([message]));
+
+ const messages = getAllMessagesById(getState());
+ expect(messages.size).toBe(0);
+ });
+
+ it("filters out message added after a console.groupCollapsed message", () => {
+ const { dispatch, getState } = setupStore();
+
+ dispatch(
+ actions.messagesAdd([
+ stubPackets.get("console.groupCollapsed('foo')"),
+ stubPackets.get("console.log('foobar', 'test')"),
+ ])
+ );
+
+ const messages = getVisibleMessages(getState());
+ expect(messages.length).toBe(1);
+ });
+
+ it("adds console.dirxml call as console.log", () => {
+ const { dispatch, getState } = setupStore();
+
+ const packet = stubPackets.get("console.dirxml(window)");
+ dispatch(actions.messagesAdd([packet]));
+
+ const dirxmlMessage = getLastMessage(getState());
+ expect(dirxmlMessage.level).toEqual(MESSAGE_TYPE.LOG);
+ });
+
+ it("does not throw when adding incomplete console.count packet", () => {
+ const { dispatch, getState } = setupStore();
+ const packet = clonePacket(stubPackets.get(`console.count('bar')`));
+
+ // Remove counter information to mimick packet we receive in the browser console.
+ delete packet.message.counter;
+
+ dispatch(actions.messagesAdd([packet]));
+ // The message should not be added to the state.
+ expect(getAllMessagesById(getState()).size).toBe(0);
+ });
+ });
+
+ describe("expandedMessageIds", () => {
+ it("opens console.trace messages when they are added", () => {
+ const { dispatch, getState } = setupStore();
+
+ const message = stubPackets.get("console.trace()");
+ dispatch(actions.messagesAdd([message]));
+
+ const expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).toBe(1);
+ expect(expanded[0]).toBe(getFirstMessage(getState()).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.messagesAdd([traceMessage]));
+
+ dispatch(actions.messagesClear());
+
+ const expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).toBe(0);
+ });
+
+ it("cleans the messages UI list when messages are pruned", () => {
+ const { dispatch, getState } = setupStore(
+ ["console.trace()", "console.log(undefined)", "console.trace()"],
+ {
+ storeOptions: {
+ logLimit: 3,
+ },
+ }
+ );
+
+ // Check that we have the expected data.
+ let expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).toBe(2);
+ expect(expanded[0]).toBe(getFirstMessage(getState()).id);
+ const lastMessageId = getLastMessage(getState()).id;
+ expect(expanded[expanded.length - 1]).toBe(lastMessageId);
+
+ // This addition will prune the first message out of the store.
+ let packet = stubPackets.get("console.log(undefined)");
+ dispatch(actions.messagesAdd([packet]));
+
+ expanded = getAllMessagesUiById(getState());
+
+ // There should be only the id of the last console.trace message.
+ expect(expanded.length).toBe(1);
+ expect(expanded[0]).toBe(lastMessageId);
+
+ // These additions will prune the last console.trace message out of the store.
+ packet = stubPackets.get("console.log('foobar', 'test')");
+ dispatch(actions.messagesAdd([packet]));
+ packet = stubPackets.get("console.log(undefined)");
+ dispatch(actions.messagesAdd([packet]));
+
+ // expandedMessageIds should now be empty.
+ expect(getAllMessagesUiById(getState()).length).toBe(0);
+ });
+
+ it("opens console.group messages when they are added", () => {
+ const { dispatch, getState } = setupStore();
+
+ const message = stubPackets.get("console.group('bar')");
+ dispatch(actions.messagesAdd([message]));
+
+ const expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).toBe(1);
+ expect(expanded[0]).toBe(getFirstMessage(getState()).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.messagesAdd([message]));
+
+ const expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).toBe(0);
+ });
+
+ it("reacts to messageClose/messageOpen actions on console.group", () => {
+ const { dispatch, getState } = setupStore(["console.group('bar')"]);
+ const firstMessageId = getFirstMessage(getState()).id;
+
+ let expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).toBe(1);
+ expect(expanded[0]).toBe(firstMessageId);
+
+ dispatch(actions.messageClose(firstMessageId));
+
+ expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).toBe(0);
+
+ dispatch(actions.messageOpen(firstMessageId));
+
+ expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).toBe(1);
+ expect(expanded[0]).toBe(firstMessageId);
+ });
+
+ it("reacts to messageClose/messageOpen actions on exception", () => {
+ const { dispatch, getState } = setupStore([
+ "ReferenceError: asdf is not defined",
+ ]);
+ const firstMessageId = getFirstMessage(getState()).id;
+
+ let expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).toBe(0);
+
+ dispatch(actions.messageOpen(firstMessageId));
+
+ expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).toBe(1);
+ expect(expanded[0]).toBe(firstMessageId);
+
+ dispatch(actions.messageClose(firstMessageId));
+
+ expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).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.messagesAdd([packet]));
+
+ const currentGroup = getCurrentGroup(getState());
+ expect(currentGroup).toBe(getFirstMessage(getState()).id);
+ });
+
+ it("sets currentGroup to expected value when console.groupEnd is added", () => {
+ const { dispatch, getState } = setupStore([
+ "console.group('bar')",
+ "console.groupCollapsed('foo')",
+ "console.group('bar')",
+ "console.groupEnd('bar')",
+ ]);
+
+ let currentGroup = getCurrentGroup(getState());
+ expect(currentGroup).toBe(getMessageAt(getState(), 1).id);
+
+ const endFooPacket = stubPackets.get("console.groupEnd('foo')");
+ dispatch(actions.messagesAdd([endFooPacket]));
+ currentGroup = getCurrentGroup(getState());
+ expect(currentGroup).toBe(getFirstMessage(getState()).id);
+
+ const endBarPacket = stubPackets.get("console.groupEnd('bar')");
+ dispatch(actions.messagesAdd([endBarPacket]));
+ 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.messagesAdd([barPacket]));
+
+ let groupsById = getGroupsById(getState());
+ expect(groupsById.size).toBe(1);
+ expect(groupsById.has(getFirstMessage(getState()).id)).toBe(true);
+ expect(groupsById.get(getFirstMessage(getState()).id)).toEqual([]);
+
+ const fooPacket = stubPackets.get("console.groupCollapsed('foo')");
+ dispatch(actions.messagesAdd([fooPacket]));
+
+ groupsById = getGroupsById(getState());
+ expect(groupsById.size).toBe(2);
+ expect(groupsById.has(getLastMessage(getState()).id)).toBe(true);
+ expect(groupsById.get(getLastMessage(getState()).id)).toEqual([
+ getFirstMessage(getState()).id,
+ ]);
+ });
+
+ it("resets groupsById in response to MESSAGES_CLEAR action", () => {
+ const { dispatch, getState } = setupStore([
+ "console.group('bar')",
+ "console.groupCollapsed('foo')",
+ ]);
+
+ let groupsById = getGroupsById(getState());
+ expect(groupsById.size).toBe(2);
+
+ dispatch(actions.messagesClear());
+
+ groupsById = getGroupsById(getState());
+ expect(groupsById.size).toBe(0);
+ });
+
+ it("cleans the groupsById property when messages are pruned", () => {
+ const { dispatch, getState } = setupStore(
+ [
+ "console.group('bar')",
+ "console.group()",
+ "console.groupEnd()",
+ "console.groupEnd('bar')",
+ "console.group('bar')",
+ "console.groupEnd('bar')",
+ "console.log('foobar', 'test')",
+ ],
+ {
+ actions,
+ storeOptions: {
+ logLimit: 3,
+ },
+ }
+ );
+
+ /*
+ * Here is the initial state of the console:
+ * ▶︎ bar
+ * ▶︎ noLabel
+ * ▶︎ bar
+ * foobar test
+ */
+
+ // Check that we have the expected data.
+ let groupsById = getGroupsById(getState());
+ expect(groupsById.size).toBe(3);
+
+ // This addition will prune the first group (and its child group) out of the store.
+ /*
+ * ▶︎ bar
+ * foobar test
+ * undefined
+ */
+ let packet = stubPackets.get("console.log(undefined)");
+ dispatch(actions.messagesAdd([packet]));
+
+ groupsById = getGroupsById(getState());
+
+ // There should be only the id of the last console.group message.
+ expect(groupsById.size).toBe(1);
+
+ // This additions will prune the last group message out of the store.
+ /*
+ * foobar test
+ * undefined
+ * foobar test
+ */
+ packet = stubPackets.get("console.log('foobar', 'test')");
+ dispatch(actions.messagesAdd([packet]));
+
+ // groupsById should now be empty.
+ expect(getGroupsById(getState()).size).toBe(0);
+ });
+ });
+
+ describe("networkMessagesUpdateById", () => {
+ it("adds the network update message when network update action is called", () => {
+ const { dispatch, getState } = setupStore();
+
+ let packet = clonePacket(stubPackets.get("GET request"));
+ let updatePacket = clonePacket(stubPackets.get("GET request update"));
+
+ packet.actor = "message1";
+ updatePacket.actor = "message1";
+ dispatch(actions.messagesAdd([packet]));
+ dispatch(actions.networkMessageUpdates([updatePacket], null));
+
+ let networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(Object.keys(networkUpdates)).toEqual(["message1"]);
+
+ packet = clonePacket(stubPackets.get("GET request"));
+ updatePacket = stubPackets.get("XHR GET request update");
+ packet.actor = "message2";
+ updatePacket.actor = "message2";
+ dispatch(actions.messagesAdd([packet]));
+ dispatch(actions.networkMessageUpdates([updatePacket], null));
+
+ networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(Object.keys(networkUpdates)).toEqual(["message1", "message2"]);
+ });
+
+ it("resets networkMessagesUpdateById in response to MESSAGES_CLEAR action", () => {
+ const { dispatch, getState } = setupStore(["XHR GET request"]);
+
+ const updatePacket = stubPackets.get("XHR GET request update");
+ dispatch(actions.networkMessageUpdates([updatePacket], null));
+
+ let networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(Object.keys(networkUpdates).length > 0).toBe(true);
+
+ dispatch(actions.messagesClear());
+
+ networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(Object.keys(networkUpdates).length).toBe(0);
+ });
+
+ it("cleans the networkMessagesUpdateById property when messages are pruned", () => {
+ const { dispatch, getState } = setupStore([], {
+ storeOptions: {
+ logLimit: 3,
+ },
+ });
+
+ // Add 3 network messages and their updates
+ let packet = clonePacket(stubPackets.get("XHR GET request"));
+ const updatePacket = clonePacket(
+ stubPackets.get("XHR GET request update")
+ );
+ packet.actor = "message1";
+ updatePacket.actor = "message1";
+ dispatch(actions.messagesAdd([packet]));
+ dispatch(actions.networkMessageUpdates([updatePacket], null));
+
+ packet.actor = "message2";
+ updatePacket.actor = "message2";
+ dispatch(actions.messagesAdd([packet]));
+ dispatch(actions.networkMessageUpdates([updatePacket], null));
+
+ packet.actor = "message3";
+ updatePacket.actor = "message3";
+ dispatch(actions.messagesAdd([packet]));
+ dispatch(actions.networkMessageUpdates([updatePacket], null));
+
+ // Check that we have the expected data.
+ const messages = getAllMessagesById(getState());
+ const [
+ firstNetworkMessageId,
+ secondNetworkMessageId,
+ thirdNetworkMessageId,
+ ] = [...messages.keys()];
+
+ let networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(Object.keys(networkUpdates)).toEqual([
+ firstNetworkMessageId,
+ secondNetworkMessageId,
+ thirdNetworkMessageId,
+ ]);
+
+ // This addition will remove the first network message.
+ packet = stubPackets.get("console.log(undefined)");
+ dispatch(actions.messagesAdd([packet]));
+
+ networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(Object.keys(networkUpdates)).toEqual([
+ secondNetworkMessageId,
+ thirdNetworkMessageId,
+ ]);
+
+ // This addition will remove the second network message.
+ packet = stubPackets.get("console.log('foobar', 'test')");
+ dispatch(actions.messagesAdd([packet]));
+
+ networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(Object.keys(networkUpdates)).toEqual([thirdNetworkMessageId]);
+
+ // This addition will remove the last network message.
+ packet = stubPackets.get("console.log(undefined)");
+ dispatch(actions.messagesAdd([packet]));
+
+ // networkMessageUpdateById should now be empty.
+ networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(Object.keys(networkUpdates)).toEqual([]);
+ });
+ });
+
+ describe("messagesPayloadById", () => {
+ it("resets messagesPayloadById in response to MESSAGES_CLEAR action", () => {
+ const { dispatch, getState } = setupStore([
+ "console.table(['a', 'b', 'c'])",
+ ]);
+
+ const data = Symbol("tableData");
+ dispatch(
+ actions.messageUpdatePayload(getFirstMessage(getState()).id, data)
+ );
+ const table = getAllMessagesPayloadById(getState());
+ expect(table.size).toBe(1);
+ expect(table.get(getFirstMessage(getState()).id)).toBe(data);
+
+ dispatch(actions.messagesClear());
+
+ expect(getAllMessagesPayloadById(getState()).size).toBe(0);
+ });
+
+ it("cleans the messagesPayloadById property when messages are pruned", () => {
+ const { dispatch, getState } = setupStore([], {
+ storeOptions: {
+ logLimit: 2,
+ },
+ });
+
+ // Add 2 table message and their data.
+ dispatch(
+ actions.messagesAdd([stubPackets.get("console.table(['a', 'b', 'c'])")])
+ );
+ dispatch(
+ actions.messagesAdd([
+ stubPackets.get("console.table(['red', 'green', 'blue']);"),
+ ])
+ );
+
+ const messages = getAllMessagesById(getState());
+
+ const tableData1 = Symbol();
+ const tableData2 = Symbol();
+ const [id1, id2] = [...messages.keys()];
+ dispatch(actions.messageUpdatePayload(id1, tableData1));
+ dispatch(actions.messageUpdatePayload(id2, tableData2));
+
+ let table = getAllMessagesPayloadById(getState());
+ expect(table.size).toBe(2);
+
+ // This addition will remove the first table message.
+ dispatch(
+ actions.messagesAdd([stubPackets.get("console.log(undefined)")])
+ );
+
+ table = getAllMessagesPayloadById(getState());
+ expect(table.size).toBe(1);
+ expect(table.get(id2)).toBe(tableData2);
+
+ // This addition will remove the second table message.
+ dispatch(
+ actions.messagesAdd([stubPackets.get("console.log('foobar', 'test')")])
+ );
+
+ expect(getAllMessagesPayloadById(getState()).size).toBe(0);
+ });
+ });
+
+ describe("messagesAdd", () => {
+ it("still log repeated message over logLimit, but only repeated ones", () => {
+ // Log two distinct messages
+ const key1 = "console.log('foobar', 'test')";
+ const key2 = "console.log(undefined)";
+ const { dispatch, getState } = setupStore([key1, key2], {
+ storeOptions: {
+ logLimit: 2,
+ },
+ });
+
+ // Then repeat the last one two times and log the first one again
+ const packet1 = clonePacket(stubPackets.get(key2));
+ const packet2 = clonePacket(stubPackets.get(key2));
+ const packet3 = clonePacket(stubPackets.get(key1));
+
+ // Repeat ID must be the same even if the timestamp is different.
+ packet1.message.timeStamp = packet1.message.timeStamp + 1;
+ packet2.message.timeStamp = packet2.message.timeStamp + 2;
+ packet3.message.timeStamp = packet3.message.timeStamp + 3;
+ dispatch(actions.messagesAdd([packet1, packet2, packet3]));
+
+ // There is still only two messages being logged,
+ const messages = getAllMessagesById(getState());
+ expect(messages.size).toBe(2);
+
+ // the second one being repeated 3 times
+ const repeat = getAllRepeatById(getState());
+ expect(repeat[getFirstMessage(getState()).id]).toBe(3);
+ expect(repeat[getLastMessage(getState()).id]).toBe(undefined);
+ });
+ });
+
+ describe("messageRemove", () => {
+ it("removes the message from the store", () => {
+ const { dispatch, getState } = setupStore([
+ "console.trace()",
+ "console.log(undefined)",
+ "console.trace()",
+ "console.log(undefined)",
+ ]);
+
+ let expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).toBe(2);
+
+ const secondTraceMessage = getMessageAt(getState(), 2);
+ dispatch(actions.messageRemove(secondTraceMessage.id));
+
+ const messages = getAllMessagesById(getState());
+ // The messages was removed
+ expect(messages.size).toBe(3);
+
+ // Its id was removed from the messagesUI property as well
+ expanded = getAllMessagesUiById(getState());
+ expect(expanded.length).toBe(1);
+ expect(expanded.includes(secondTraceMessage.id)).toBeFalsy();
+ });
+ });
+});
diff --git a/devtools/client/webconsole/test/node/store/network-messages.test.js b/devtools/client/webconsole/test/node/store/network-messages.test.js
new file mode 100644
index 0000000000..af6fe40f9b
--- /dev/null
+++ b/devtools/client/webconsole/test/node/store/network-messages.test.js
@@ -0,0 +1,133 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const {
+ getAllNetworkMessagesUpdateById,
+} = require("devtools/client/webconsole/selectors/messages");
+const {
+ setupActions,
+ setupStore,
+ clonePacket,
+} = require("devtools/client/webconsole/test/node/helpers");
+const {
+ stubPackets,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+
+const expect = require("expect");
+
+describe("Network message reducer:", () => {
+ let actions;
+ let getState;
+ let dispatch;
+
+ before(() => {
+ actions = setupActions();
+ });
+
+ beforeEach(() => {
+ const store = setupStore();
+
+ getState = store.getState;
+ dispatch = store.dispatch;
+
+ const packet = clonePacket(stubPackets.get("GET request"));
+ const updatePacket = clonePacket(stubPackets.get("GET request update"));
+
+ packet.actor = "message1";
+ updatePacket.actor = "message1";
+ dispatch(actions.messagesAdd([packet]));
+ dispatch(actions.networkMessageUpdates([updatePacket], null));
+ });
+
+ describe("networkMessagesUpdateById", () => {
+ it("adds fetched HTTP request headers", () => {
+ const headers = {
+ headers: [],
+ };
+
+ dispatch(
+ actions.networkUpdateRequests([
+ {
+ id: "message1",
+ data: {
+ requestHeaders: headers,
+ },
+ },
+ ])
+ );
+
+ const networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(networkUpdates.message1.requestHeaders).toBe(headers);
+ });
+
+ it("makes sure multiple HTTP updates of same request does not override", () => {
+ dispatch(
+ actions.networkUpdateRequests([
+ {
+ id: "message1",
+ data: {
+ stacktrace: [{}],
+ },
+ },
+ {
+ id: "message1",
+ data: {
+ requestHeaders: { headers: [] },
+ },
+ },
+ ])
+ );
+
+ const networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(networkUpdates.message1.requestHeaders).toNotBe(undefined);
+ expect(networkUpdates.message1.stacktrace).toNotBe(undefined);
+ });
+
+ it("adds fetched HTTP security info", () => {
+ const securityInfo = {
+ state: "insecure",
+ };
+
+ dispatch(
+ actions.networkUpdateRequests([
+ {
+ id: "message1",
+ data: {
+ securityInfo: securityInfo,
+ },
+ },
+ ])
+ );
+
+ const networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(networkUpdates.message1.securityInfo).toBe(securityInfo);
+ expect(networkUpdates.message1.securityState).toBe("insecure");
+ });
+
+ it("adds fetched HTTP post data", () => {
+ const uploadHeaders = Symbol();
+ const requestPostData = {
+ postData: {
+ text: "",
+ },
+ uploadHeaders,
+ };
+
+ dispatch(
+ actions.networkUpdateRequests([
+ {
+ id: "message1",
+ data: {
+ requestPostData,
+ },
+ },
+ ])
+ );
+
+ const { message1 } = getAllNetworkMessagesUpdateById(getState());
+ expect(message1.requestPostData).toBe(requestPostData);
+ expect(message1.requestHeadersFromUploadStream).toBe(uploadHeaders);
+ });
+ });
+});
diff --git a/devtools/client/webconsole/test/node/store/private-messages.test.js b/devtools/client/webconsole/test/node/store/private-messages.test.js
new file mode 100644
index 0000000000..4392cd11b1
--- /dev/null
+++ b/devtools/client/webconsole/test/node/store/private-messages.test.js
@@ -0,0 +1,225 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ getAllMessagesUiById,
+ getAllMessagesPayloadById,
+ getAllNetworkMessagesUpdateById,
+ getAllRepeatById,
+ getCurrentGroup,
+ getGroupsById,
+ getAllMessagesById,
+ getVisibleMessages,
+} = require("devtools/client/webconsole/selectors/messages");
+const {
+ getFirstMessage,
+ getLastMessage,
+ getPrivatePacket,
+ setupActions,
+ setupStore,
+} = require("devtools/client/webconsole/test/node/helpers");
+const {
+ stubPackets,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+
+const expect = require("expect");
+
+describe("private messages", () => {
+ let actions;
+ before(() => {
+ actions = setupActions();
+ });
+
+ it("removes private messages on PRIVATE_MESSAGES_CLEAR action", () => {
+ const { dispatch, getState } = setupStore();
+
+ dispatch(
+ actions.messagesAdd([
+ getPrivatePacket("console.trace()"),
+ stubPackets.get("console.log('mymap')"),
+ getPrivatePacket("console.log(undefined)"),
+ getPrivatePacket("GET request"),
+ ])
+ );
+
+ let state = getState();
+ const messages = getAllMessagesById(state);
+ expect(messages.size).toBe(4);
+
+ dispatch(actions.privateMessagesClear());
+
+ state = getState();
+ expect(getAllMessagesById(state).size).toBe(1);
+ expect(getVisibleMessages(state).length).toBe(1);
+ });
+
+ it("cleans messagesUiById on PRIVATE_MESSAGES_CLEAR action", () => {
+ const { dispatch, getState } = setupStore();
+
+ dispatch(
+ actions.messagesAdd([
+ getPrivatePacket("console.trace()"),
+ stubPackets.get("console.trace()"),
+ ])
+ );
+
+ let state = getState();
+ expect(getAllMessagesUiById(state).length).toBe(2);
+
+ dispatch(actions.privateMessagesClear());
+
+ state = getState();
+ expect(getAllMessagesUiById(state).length).toBe(1);
+ });
+
+ it("cleans repeatsById on PRIVATE_MESSAGES_CLEAR action", () => {
+ const { dispatch, getState } = setupStore();
+
+ dispatch(
+ actions.messagesAdd([
+ getPrivatePacket("console.log(undefined)"),
+ getPrivatePacket("console.log(undefined)"),
+ stubPackets.get("console.log(undefined)"),
+ stubPackets.get("console.log(undefined)"),
+ ])
+ );
+
+ let state = getState();
+ expect(getAllRepeatById(state)).toEqual({
+ [getFirstMessage(state).id]: 2,
+ [getLastMessage(state).id]: 2,
+ });
+
+ dispatch(actions.privateMessagesClear());
+
+ state = getState();
+ expect(Object.keys(getAllRepeatById(state)).length).toBe(1);
+ expect(getAllRepeatById(state)).toEqual({
+ [getFirstMessage(state).id]: 2,
+ });
+ });
+
+ it("cleans messagesPayloadById on PRIVATE_MESSAGES_CLEAR action", () => {
+ const { dispatch, getState } = setupStore();
+
+ dispatch(
+ actions.messagesAdd([
+ getPrivatePacket("console.table(['a', 'b', 'c'])"),
+ stubPackets.get("console.table(['a', 'b', 'c'])"),
+ ])
+ );
+
+ const privateTableData = Symbol("privateTableData");
+ const publicTableData = Symbol("publicTableData");
+ dispatch(
+ actions.messageUpdatePayload(
+ getFirstMessage(getState()).id,
+ privateTableData
+ )
+ );
+ dispatch(
+ actions.messageUpdatePayload(
+ getLastMessage(getState()).id,
+ publicTableData
+ )
+ );
+
+ let state = getState();
+ expect(getAllMessagesPayloadById(state).size).toBe(2);
+
+ dispatch(actions.privateMessagesClear());
+
+ state = getState();
+ expect(getAllMessagesPayloadById(state).size).toBe(1);
+ expect(
+ getAllMessagesPayloadById(state).get(getFirstMessage(getState()).id)
+ ).toBe(publicTableData);
+ });
+
+ it("cleans group properties on PRIVATE_MESSAGES_CLEAR action", () => {
+ const { dispatch, getState } = setupStore();
+ dispatch(
+ actions.messagesAdd([
+ stubPackets.get("console.group()"),
+ getPrivatePacket("console.group()"),
+ ])
+ );
+
+ let state = getState();
+ const publicMessageId = getFirstMessage(state).id;
+ const privateMessageId = getLastMessage(state).id;
+ expect(getCurrentGroup(state)).toBe(privateMessageId);
+ expect(getGroupsById(state).size).toBe(2);
+
+ dispatch(actions.privateMessagesClear());
+
+ state = getState();
+ expect(getGroupsById(state).size).toBe(1);
+ expect(getGroupsById(state).has(publicMessageId)).toBe(true);
+ expect(getCurrentGroup(state)).toBe(publicMessageId);
+ });
+
+ it("cleans networkMessagesUpdateById on PRIVATE_MESSAGES_CLEAR action", () => {
+ const { dispatch, getState } = setupStore();
+
+ const publicActor = "network/public";
+ const privateActor = "network/private";
+ const publicPacket = {
+ ...stubPackets.get("GET request"),
+ actor: publicActor,
+ };
+ const privatePacket = {
+ ...getPrivatePacket("XHR GET request"),
+ actor: privateActor,
+ };
+
+ // We need to reassign the timeStamp of the packet to guarantee the order.
+ publicPacket.timeStamp = publicPacket.timeStamp + 1;
+ privatePacket.timeStamp = privatePacket.timeStamp + 2;
+
+ dispatch(actions.messagesAdd([publicPacket, privatePacket]));
+
+ let networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(Object.keys(networkUpdates)).toEqual([publicActor, privateActor]);
+
+ dispatch(actions.privateMessagesClear());
+
+ networkUpdates = getAllNetworkMessagesUpdateById(getState());
+ expect(Object.keys(networkUpdates)).toEqual([publicActor]);
+ });
+
+ it("releases private backend actors on PRIVATE_MESSAGES_CLEAR action", () => {
+ const releasedActors = [];
+ const { dispatch, getState } = setupStore([]);
+ const mockFrontRelease = function() {
+ releasedActors.push(this.actorID);
+ };
+
+ const publicPacket = stubPackets.get(
+ "console.log('myarray', ['red', 'green', 'blue'])"
+ );
+ const privatePacket = getPrivatePacket("console.log('mymap')");
+
+ publicPacket.message.arguments[1].release = mockFrontRelease;
+ privatePacket.message.arguments[1].release = mockFrontRelease;
+
+ // Add a log message.
+ dispatch(actions.messagesAdd([publicPacket, privatePacket]));
+
+ const firstMessage = getFirstMessage(getState());
+ const firstMessageActor = firstMessage.parameters[1].actorID;
+
+ const lastMessage = getLastMessage(getState());
+ const lastMessageActor = lastMessage.parameters[1].actorID;
+
+ // Kick-off the actor release.
+ dispatch(actions.privateMessagesClear());
+
+ expect(releasedActors.length).toBe(1);
+ expect(releasedActors).toInclude(lastMessageActor);
+ expect(releasedActors).toNotInclude(firstMessageActor);
+ });
+});
diff --git a/devtools/client/webconsole/test/node/store/release-actors.test.js b/devtools/client/webconsole/test/node/store/release-actors.test.js
new file mode 100644
index 0000000000..f1bc6d073e
--- /dev/null
+++ b/devtools/client/webconsole/test/node/store/release-actors.test.js
@@ -0,0 +1,172 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const {
+ getFirstMessage,
+ setupActions,
+ setupStore,
+} = require("devtools/client/webconsole/test/node/helpers");
+
+const {
+ stubPackets,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+const expect = require("expect");
+
+describe("Release actor enhancer:", () => {
+ let actions;
+
+ before(() => {
+ actions = setupActions();
+ });
+
+ describe("Client proxy", () => {
+ it("releases backend actors when limit reached adding a single message", () => {
+ const logLimit = 100;
+ const releasedActors = [];
+ const mockFrontRelease = function() {
+ releasedActors.push(this.actorID);
+ };
+
+ const { dispatch, getState } = setupStore([], {
+ storeOptions: { logLimit },
+ });
+
+ // Add a log message.
+ const packet = stubPackets.get(
+ "console.log('myarray', ['red', 'green', 'blue'])"
+ );
+ packet.message.arguments[1].release = mockFrontRelease;
+ dispatch(actions.messagesAdd([packet]));
+
+ const firstMessage = getFirstMessage(getState());
+ const firstMessageActor = firstMessage.parameters[1].actorID;
+
+ // Add an evaluation result message (see Bug 1408321).
+ const evaluationResultPacket = stubPackets.get("new Date(0)");
+ evaluationResultPacket.result.release = mockFrontRelease;
+ dispatch(actions.messagesAdd([evaluationResultPacket]));
+ const secondMessageActor = evaluationResultPacket.result.actorID;
+
+ const logCount = logLimit + 1;
+ const assertPacket = stubPackets.get(
+ "console.assert(false, {message: 'foobar'})"
+ );
+ assertPacket.message.arguments[0].release = mockFrontRelease;
+ const thirdMessageActor = assertPacket.message.arguments[0].actorID;
+
+ for (let i = 1; i <= logCount; i++) {
+ assertPacket.message.arguments.push(`message num ${i}`);
+ dispatch(actions.messagesAdd([assertPacket]));
+ }
+
+ expect(releasedActors.length).toBe(3);
+ expect(releasedActors).toInclude(firstMessageActor);
+ expect(releasedActors).toInclude(secondMessageActor);
+ expect(releasedActors).toInclude(thirdMessageActor);
+ });
+
+ it("releases backend actors when limit reached adding multiple messages", () => {
+ const logLimit = 100;
+ const releasedActors = [];
+ const { dispatch, getState } = setupStore([], {
+ storeOptions: { logLimit },
+ });
+
+ const mockFrontRelease = function() {
+ releasedActors.push(this.actorID);
+ };
+
+ // Add a log message.
+ const logPacket = stubPackets.get(
+ "console.log('myarray', ['red', 'green', 'blue'])"
+ );
+ logPacket.message.arguments[1].release = mockFrontRelease;
+ dispatch(actions.messagesAdd([logPacket]));
+
+ const firstMessage = getFirstMessage(getState());
+ const firstMessageActor = firstMessage.parameters[1].actorID;
+
+ // Add an evaluation result message (see Bug 1408321).
+ const evaluationResultPacket = stubPackets.get("new Date(0)");
+ evaluationResultPacket.result.release = mockFrontRelease;
+ dispatch(actions.messagesAdd([evaluationResultPacket]));
+ const secondMessageActor = evaluationResultPacket.result.actorID;
+
+ // Add an assertion message.
+ const assertPacket = stubPackets.get(
+ "console.assert(false, {message: 'foobar'})"
+ );
+ assertPacket.message.arguments[0].release = mockFrontRelease;
+ dispatch(actions.messagesAdd([assertPacket]));
+ const thirdMessageActor = assertPacket.message.arguments[0].actorID;
+
+ // Add ${logLimit} messages so we prune the ones we added before.
+ const packets = [];
+ // Alternate between 2 packets so we don't trigger the repeat message mechanism.
+ const oddPacket = stubPackets.get("console.log(undefined)");
+ const evenPacket = stubPackets.get("console.log('foobar', 'test')");
+ for (let i = 0; i < logLimit; i++) {
+ const packet = i % 2 === 0 ? evenPacket : oddPacket;
+ packets.push(packet);
+ }
+
+ // Add all the packets at once. This will prune the first 3 messages.
+ dispatch(actions.messagesAdd(packets));
+
+ expect(releasedActors.length).toBe(3);
+ expect(releasedActors).toInclude(firstMessageActor);
+ expect(releasedActors).toInclude(secondMessageActor);
+ expect(releasedActors).toInclude(thirdMessageActor);
+ });
+
+ it("properly releases backend actors after clear", () => {
+ const releasedActors = [];
+ const { dispatch, getState } = setupStore([]);
+
+ const mockFrontRelease = function() {
+ releasedActors.push(this.actorID);
+ };
+
+ // Add a log message.
+ const logPacket = stubPackets.get(
+ "console.log('myarray', ['red', 'green', 'blue'])"
+ );
+ logPacket.message.arguments[1].release = mockFrontRelease;
+ dispatch(actions.messagesAdd([logPacket]));
+
+ const firstMessage = getFirstMessage(getState());
+ const firstMessageActor = firstMessage.parameters[1].actorID;
+
+ // Add an assertion message.
+ const assertPacket = stubPackets.get(
+ "console.assert(false, {message: 'foobar'})"
+ );
+ assertPacket.message.arguments[0].release = mockFrontRelease;
+ dispatch(actions.messagesAdd([assertPacket]));
+ const secondMessageActor = assertPacket.message.arguments[0].actorID;
+
+ // Add an evaluation result message (see Bug 1408321).
+ const evaluationResultPacket = stubPackets.get("new Date(0)");
+ evaluationResultPacket.result.release = mockFrontRelease;
+ dispatch(actions.messagesAdd([evaluationResultPacket]));
+ const thirdMessageActor = evaluationResultPacket.result.actorID;
+
+ // Add a message with a long string messageText property.
+ const longStringPacket = stubPackets.get("TypeError longString message");
+ longStringPacket.pageError.errorMessage.release = mockFrontRelease;
+ dispatch(actions.messagesAdd([longStringPacket]));
+ const fourthMessageActor =
+ longStringPacket.pageError.errorMessage.actorID;
+
+ // Kick-off the actor release.
+ dispatch(actions.messagesClear());
+
+ expect(releasedActors.length).toBe(4);
+ expect(releasedActors).toInclude(firstMessageActor);
+ expect(releasedActors).toInclude(secondMessageActor);
+ expect(releasedActors).toInclude(thirdMessageActor);
+ expect(releasedActors).toInclude(fourthMessageActor);
+ });
+ });
+});
diff --git a/devtools/client/webconsole/test/node/store/search.test.js b/devtools/client/webconsole/test/node/store/search.test.js
new file mode 100644
index 0000000000..407c7b6b59
--- /dev/null
+++ b/devtools/client/webconsole/test/node/store/search.test.js
@@ -0,0 +1,113 @@
+/* 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/actions/index");
+const {
+ getVisibleMessages,
+} = require("devtools/client/webconsole/selectors/messages");
+const { setupStore } = require("devtools/client/webconsole/test/node/helpers");
+
+describe("Searching in grips", () => {
+ let store;
+
+ beforeEach(() => {
+ store = prepareBaseStore();
+ store.dispatch(actions.filtersClear());
+ });
+
+ describe("Search in table & array & object props", () => {
+ it("matches on value grips", () => {
+ store.dispatch(actions.filterTextSet("red"));
+ expect(getVisibleMessages(store.getState()).length).toEqual(3);
+ });
+ });
+
+ describe("Search in object value", () => {
+ it("matches on value grips", () => {
+ store.dispatch(actions.filterTextSet("redValue"));
+ expect(getVisibleMessages(store.getState()).length).toEqual(1);
+ });
+ });
+
+ describe("Search in regex", () => {
+ it("matches on value grips", () => {
+ store.dispatch(actions.filterTextSet("a.b.c"));
+ expect(getVisibleMessages(store.getState()).length).toEqual(1);
+ });
+ });
+
+ describe("Search in map values", () => {
+ it("matches on value grips", () => {
+ store.dispatch(actions.filterTextSet("value1"));
+ expect(getVisibleMessages(store.getState()).length).toEqual(1);
+ });
+ });
+
+ describe("Search in map keys", () => {
+ it("matches on value grips", () => {
+ store.dispatch(actions.filterTextSet("key1"));
+ expect(getVisibleMessages(store.getState()).length).toEqual(1);
+ });
+ });
+
+ describe("Search in text", () => {
+ it("matches on value grips", () => {
+ store.dispatch(actions.filterTextSet("myobj"));
+ expect(getVisibleMessages(store.getState()).length).toEqual(1);
+ });
+ });
+
+ describe("Search in logs with net messages", () => {
+ it("matches on network messages", () => {
+ store.dispatch(actions.filterToggle("net"));
+ store.dispatch(actions.filterTextSet("get"));
+ expect(getVisibleMessages(store.getState()).length).toEqual(1);
+ });
+ });
+
+ describe("Search in frame", () => {
+ it("matches on file name", () => {
+ store.dispatch(actions.filterTextSet("test-console-api.html:1:35"));
+ expect(getVisibleMessages(store.getState()).length).toEqual(7);
+ });
+
+ it("do not match on full url", () => {
+ store.dispatch(
+ actions.filterTextSet("http://example.com/browser/devtools")
+ );
+ expect(getVisibleMessages(store.getState()).length).toEqual(0);
+ });
+ });
+
+ describe("Reverse search", () => {
+ it("reverse matches on value grips", () => {
+ store.dispatch(actions.filterTextSet("-red"));
+ expect(getVisibleMessages(store.getState()).length).toEqual(6);
+ });
+
+ it("reverse matches on file name", () => {
+ store.dispatch(actions.filterTextSet("-test-console-api.html:1:35"));
+ expect(getVisibleMessages(store.getState()).length).toEqual(2);
+ });
+ });
+});
+
+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.log('mymap')",
+ "console.log('myobject', {red: 'redValue', green: 'greenValue', blue: 'blueValue'});",
+ "GET request update",
+ ]);
+
+ return store;
+}
diff --git a/devtools/client/webconsole/test/node/store/ui.test.js b/devtools/client/webconsole/test/node/store/ui.test.js
new file mode 100644
index 0000000000..8b4315a465
--- /dev/null
+++ b/devtools/client/webconsole/test/node/store/ui.test.js
@@ -0,0 +1,119 @@
+/* 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/actions/index");
+const {
+ setupStore,
+ getFirstMessage,
+ getLastMessage,
+} = require("devtools/client/webconsole/test/node/helpers");
+const {
+ stubPackets,
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+
+describe("Testing UI", () => {
+ let store;
+
+ beforeEach(() => {
+ store = setupStore();
+ });
+
+ describe("Toggle sidebar", () => {
+ it("sidebar is toggled on and off", () => {
+ const packet = stubPackets.get("inspect({a: 1})");
+ const message = stubPreparedMessages.get("inspect({a: 1})");
+ store.dispatch(actions.messagesAdd([packet]));
+
+ const { actorID } = message.parameters[0];
+ const messageId = getFirstMessage(store.getState()).id;
+ store.dispatch(actions.showMessageObjectInSidebar(actorID, messageId));
+
+ expect(store.getState().ui.sidebarVisible).toEqual(true);
+ store.dispatch(actions.sidebarClose());
+ expect(store.getState().ui.sidebarVisible).toEqual(false);
+ });
+ });
+
+ describe("Hide sidebar on clear", () => {
+ it("sidebar is hidden on clear", () => {
+ const packet = stubPackets.get("inspect({a: 1})");
+ const message = stubPreparedMessages.get("inspect({a: 1})");
+ store.dispatch(actions.messagesAdd([packet]));
+
+ const { actorID } = message.parameters[0];
+ const messageId = getFirstMessage(store.getState()).id;
+ store.dispatch(actions.showMessageObjectInSidebar(actorID, messageId));
+
+ expect(store.getState().ui.sidebarVisible).toEqual(true);
+ store.dispatch(actions.messagesClear());
+ expect(store.getState().ui.sidebarVisible).toEqual(false);
+ store.dispatch(actions.messagesClear());
+ expect(store.getState().ui.sidebarVisible).toEqual(false);
+ });
+ });
+
+ describe("Show object in sidebar", () => {
+ it("sidebar is shown with correct object", () => {
+ const packet = stubPackets.get("inspect({a: 1})");
+ const message = stubPreparedMessages.get("inspect({a: 1})");
+ store.dispatch(actions.messagesAdd([packet]));
+
+ const { actorID } = message.parameters[0];
+ const messageId = getFirstMessage(store.getState()).id;
+ store.dispatch(actions.showMessageObjectInSidebar(actorID, messageId));
+
+ expect(store.getState().ui.sidebarVisible).toEqual(true);
+ expect(store.getState().ui.frontInSidebar).toEqual(message.parameters[0]);
+ });
+
+ it("sidebar is not updated for the same object", () => {
+ const packet = stubPackets.get("inspect({a: 1})");
+ const message = stubPreparedMessages.get("inspect({a: 1})");
+ store.dispatch(actions.messagesAdd([packet]));
+
+ const { actorID } = message.parameters[0];
+ const messageId = getFirstMessage(store.getState()).id;
+ store.dispatch(actions.showMessageObjectInSidebar(actorID, messageId));
+
+ expect(store.getState().ui.sidebarVisible).toEqual(true);
+ expect(store.getState().ui.frontInSidebar).toEqual(message.parameters[0]);
+ const state = store.getState().ui;
+
+ store.dispatch(actions.showMessageObjectInSidebar(actorID, messageId));
+ expect(store.getState().ui).toEqual(state);
+ });
+
+ it("sidebar shown and updated for new object", () => {
+ const packet = stubPackets.get("inspect({a: 1})");
+ const message = stubPreparedMessages.get("inspect({a: 1})");
+ store.dispatch(actions.messagesAdd([packet]));
+
+ const { actorID } = message.parameters[0];
+ const messageId = getFirstMessage(store.getState()).id;
+ store.dispatch(actions.showMessageObjectInSidebar(actorID, messageId));
+
+ expect(store.getState().ui.sidebarVisible).toEqual(true);
+ expect(store.getState().ui.frontInSidebar).toEqual(message.parameters[0]);
+
+ const newPacket = stubPackets.get("new Date(0)");
+ const newMessage = stubPreparedMessages.get("new Date(0)");
+ store.dispatch(actions.messagesAdd([newPacket]));
+
+ const newActorID = newMessage.parameters[0].actorID;
+ const newMessageId = getLastMessage(store.getState()).id;
+ store.dispatch(
+ actions.showMessageObjectInSidebar(newActorID, newMessageId)
+ );
+
+ expect(store.getState().ui.sidebarVisible).toEqual(true);
+ expect(store.getState().ui.frontInSidebar).toEqual(
+ newMessage.parameters[0]
+ );
+ });
+ });
+});
diff --git a/devtools/client/webconsole/test/node/utils/.eslintrc.js b/devtools/client/webconsole/test/node/utils/.eslintrc.js
new file mode 100644
index 0000000000..9b9dfe0ad1
--- /dev/null
+++ b/devtools/client/webconsole/test/node/utils/.eslintrc.js
@@ -0,0 +1,5 @@
+"use strict";
+
+module.exports = {
+ extends: "../.eslintrc.mocha.js",
+};
diff --git a/devtools/client/webconsole/test/node/utils/getRepeatId.test.js b/devtools/client/webconsole/test/node/utils/getRepeatId.test.js
new file mode 100644
index 0000000000..6d728107f8
--- /dev/null
+++ b/devtools/client/webconsole/test/node/utils/getRepeatId.test.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { getRepeatId } = require("devtools/client/webconsole/utils/messages");
+const {
+ stubPreparedMessages,
+} = require("devtools/client/webconsole/test/node/fixtures/stubs/index");
+
+const expect = require("expect");
+
+describe("getRepeatId:", () => {
+ it("returns same repeatId for duplicate values", () => {
+ const baseMessage = stubPreparedMessages.get(
+ "console.log('foobar', 'test')"
+ );
+
+ // Repeat ID must be the same even if the timestamp is different.
+ const message1 = Object.assign({}, baseMessage, { timeStamp: 1 });
+ const message2 = Object.assign({}, baseMessage, { timeStamp: 2 });
+
+ expect(getRepeatId(message1)).toEqual(getRepeatId(message2));
+ });
+
+ it("returns different repeatIds for different values", () => {
+ const message1 = stubPreparedMessages.get("console.log('foobar', 'test')");
+ const message2 = Object.assign({}, message1, {
+ 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 = Object.assign({}, message1, { 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);
+ });
+});
diff --git a/devtools/client/webconsole/test/node/yarn.lock b/devtools/client/webconsole/test/node/yarn.lock
new file mode 100644
index 0000000000..be36a2d585
--- /dev/null
+++ b/devtools/client/webconsole/test/node/yarn.lock
@@ -0,0 +1,3605 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
+ integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
+ dependencies:
+ "@babel/highlight" "^7.10.4"
+
+"@babel/code-frame@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
+ integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==
+ dependencies:
+ "@babel/highlight" "^7.8.3"
+
+"@babel/core@^7.8.7":
+ version "7.8.7"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.7.tgz#b69017d221ccdeb203145ae9da269d72cf102f3b"
+ integrity sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==
+ dependencies:
+ "@babel/code-frame" "^7.8.3"
+ "@babel/generator" "^7.8.7"
+ "@babel/helpers" "^7.8.4"
+ "@babel/parser" "^7.8.7"
+ "@babel/template" "^7.8.6"
+ "@babel/traverse" "^7.8.6"
+ "@babel/types" "^7.8.7"
+ convert-source-map "^1.7.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.1"
+ json5 "^2.1.0"
+ lodash "^4.17.13"
+ resolve "^1.3.2"
+ semver "^5.4.1"
+ source-map "^0.5.0"
+
+"@babel/generator@^7.11.5":
+ version "7.11.6"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620"
+ integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==
+ dependencies:
+ "@babel/types" "^7.11.5"
+ jsesc "^2.5.1"
+ source-map "^0.5.0"
+
+"@babel/generator@^7.8.6", "@babel/generator@^7.8.7":
+ version "7.8.8"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.8.tgz#cdcd58caab730834cee9eeadb729e833b625da3e"
+ integrity sha512-HKyUVu69cZoclptr8t8U5b6sx6zoWjh8jiUhnuj3MpZuKT2dJ8zPTuiy31luq32swhI0SpwItCIlU8XW7BZeJg==
+ dependencies:
+ "@babel/types" "^7.8.7"
+ jsesc "^2.5.1"
+ lodash "^4.17.13"
+ source-map "^0.5.0"
+
+"@babel/helper-create-class-features-plugin@^7.10.4":
+ version "7.10.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d"
+ integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==
+ dependencies:
+ "@babel/helper-function-name" "^7.10.4"
+ "@babel/helper-member-expression-to-functions" "^7.10.5"
+ "@babel/helper-optimise-call-expression" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-replace-supers" "^7.10.4"
+ "@babel/helper-split-export-declaration" "^7.10.4"
+
+"@babel/helper-function-name@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a"
+ integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==
+ dependencies:
+ "@babel/helper-get-function-arity" "^7.10.4"
+ "@babel/template" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
+"@babel/helper-function-name@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca"
+ integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==
+ dependencies:
+ "@babel/helper-get-function-arity" "^7.8.3"
+ "@babel/template" "^7.8.3"
+ "@babel/types" "^7.8.3"
+
+"@babel/helper-get-function-arity@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2"
+ integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==
+ dependencies:
+ "@babel/types" "^7.10.4"
+
+"@babel/helper-get-function-arity@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5"
+ integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==
+ dependencies:
+ "@babel/types" "^7.8.3"
+
+"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df"
+ integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==
+ dependencies:
+ "@babel/types" "^7.11.0"
+
+"@babel/helper-optimise-call-expression@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673"
+ integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==
+ dependencies:
+ "@babel/types" "^7.10.4"
+
+"@babel/helper-plugin-utils@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375"
+ integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==
+
+"@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670"
+ integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==
+
+"@babel/helper-replace-supers@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf"
+ integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==
+ dependencies:
+ "@babel/helper-member-expression-to-functions" "^7.10.4"
+ "@babel/helper-optimise-call-expression" "^7.10.4"
+ "@babel/traverse" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
+"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f"
+ integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==
+ dependencies:
+ "@babel/types" "^7.11.0"
+
+"@babel/helper-split-export-declaration@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9"
+ integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==
+ dependencies:
+ "@babel/types" "^7.8.3"
+
+"@babel/helper-validator-identifier@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
+ integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
+
+"@babel/helpers@^7.8.4":
+ version "7.8.4"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73"
+ integrity sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==
+ dependencies:
+ "@babel/template" "^7.8.3"
+ "@babel/traverse" "^7.8.4"
+ "@babel/types" "^7.8.3"
+
+"@babel/highlight@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143"
+ integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.10.4"
+ chalk "^2.0.0"
+ js-tokens "^4.0.0"
+
+"@babel/highlight@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797"
+ integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==
+ dependencies:
+ chalk "^2.0.0"
+ esutils "^2.0.2"
+ js-tokens "^4.0.0"
+
+"@babel/parser@^7.10.4", "@babel/parser@^7.11.5":
+ version "7.11.5"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037"
+ integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==
+
+"@babel/parser@^7.8.6", "@babel/parser@^7.8.7":
+ version "7.8.8"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.8.tgz#4c3b7ce36db37e0629be1f0d50a571d2f86f6cd4"
+ integrity sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA==
+
+"@babel/plugin-proposal-class-properties@7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807"
+ integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2"
+ integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.3"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+
+"@babel/plugin-proposal-optional-chaining@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz#ae10b3214cb25f7adb1f3bc87ba42ca10b7e2543"
+ integrity sha512-QIoIR9abkVn+seDE3OjA08jWcs3eZ9+wJCKSRgo3WdEU2csFYgdScb+8qHB3+WXsGJD55u+5hWCISI7ejXS+kg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.3"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
+ integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-chaining@^7.8.0":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
+ integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/register@^7.8.6":
+ version "7.8.6"
+ resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.8.6.tgz#a1066aa6168a73a70c35ef28cc5865ccc087ea69"
+ integrity sha512-7IDO93fuRsbyml7bAafBQb3RcBGlCpU4hh5wADA2LJEEcYk92WkwFZ0pHyIi2fb5Auoz1714abETdZKCOxN0CQ==
+ dependencies:
+ find-cache-dir "^2.0.0"
+ lodash "^4.17.13"
+ make-dir "^2.1.0"
+ pirates "^4.0.0"
+ source-map-support "^0.5.16"
+
+"@babel/template@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
+ integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ "@babel/parser" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
+"@babel/template@^7.8.3", "@babel/template@^7.8.6":
+ version "7.8.6"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
+ integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==
+ dependencies:
+ "@babel/code-frame" "^7.8.3"
+ "@babel/parser" "^7.8.6"
+ "@babel/types" "^7.8.6"
+
+"@babel/traverse@^7.10.4":
+ version "7.11.5"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3"
+ integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ "@babel/generator" "^7.11.5"
+ "@babel/helper-function-name" "^7.10.4"
+ "@babel/helper-split-export-declaration" "^7.11.0"
+ "@babel/parser" "^7.11.5"
+ "@babel/types" "^7.11.5"
+ debug "^4.1.0"
+ globals "^11.1.0"
+ lodash "^4.17.19"
+
+"@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6":
+ version "7.8.6"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.6.tgz#acfe0c64e1cd991b3e32eae813a6eb564954b5ff"
+ integrity sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==
+ dependencies:
+ "@babel/code-frame" "^7.8.3"
+ "@babel/generator" "^7.8.6"
+ "@babel/helper-function-name" "^7.8.3"
+ "@babel/helper-split-export-declaration" "^7.8.3"
+ "@babel/parser" "^7.8.6"
+ "@babel/types" "^7.8.6"
+ debug "^4.1.0"
+ globals "^11.1.0"
+ lodash "^4.17.13"
+
+"@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.11.5":
+ version "7.11.5"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d"
+ integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.10.4"
+ lodash "^4.17.19"
+ to-fast-properties "^2.0.0"
+
+"@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.8.7":
+ version "7.8.7"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.7.tgz#1fc9729e1acbb2337d5b6977a63979b4819f5d1d"
+ integrity sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==
+ dependencies:
+ esutils "^2.0.2"
+ lodash "^4.17.13"
+ to-fast-properties "^2.0.0"
+
+"@types/node@*":
+ version "13.9.2"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.2.tgz#ace1880c03594cc3e80206d96847157d8e7fa349"
+ integrity sha512-bnoqK579sAYrQbp73wwglccjJ4sfRdKU7WNEZ5FW4K2U6Kc0/eZ5kvXG0JKsEKFB50zrFmfFt52/cvBbZa7eXg==
+
+abab@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e"
+ integrity sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=
+
+acorn-globals@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
+ integrity sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=
+ dependencies:
+ acorn "^4.0.4"
+
+acorn@^4.0.4:
+ version "4.0.13"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
+ integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=
+
+airbnb-prop-types@^2.15.0:
+ version "2.15.0"
+ resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.15.0.tgz#5287820043af1eb469f5b0af0d6f70da6c52aaef"
+ integrity sha512-jUh2/hfKsRjNFC4XONQrxo/n/3GG4Tn6Hl0WlFQN5PY9OMC9loSCoAYKnZsWaP8wEfd5xcrPloK0Zg6iS1xwVA==
+ dependencies:
+ array.prototype.find "^2.1.0"
+ function.prototype.name "^1.1.1"
+ has "^1.0.3"
+ is-regex "^1.0.4"
+ object-is "^1.0.1"
+ object.assign "^4.1.0"
+ object.entries "^1.1.0"
+ prop-types "^15.7.2"
+ prop-types-exact "^1.2.0"
+ react-is "^16.9.0"
+
+ajv@^6.5.5:
+ version "6.12.0"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
+ integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ansi-escapes@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
+ integrity sha1-06ioOzGapneTZisT52HHkRQiMG4=
+
+ansi-regex@^2.0.0, ansi-regex@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+ integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
+
+ansi-styles@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+ integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
+
+ansi-styles@^3.0.0, ansi-styles@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+ dependencies:
+ color-convert "^1.9.0"
+
+anymatch@^1.3.0:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a"
+ integrity sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==
+ dependencies:
+ micromatch "^2.1.5"
+ normalize-path "^2.0.0"
+
+append-transform@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991"
+ integrity sha1-126/jKlNJ24keja61EpLdKthGZE=
+ dependencies:
+ default-require-extensions "^1.0.0"
+
+argparse@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+ dependencies:
+ sprintf-js "~1.0.2"
+
+arr-diff@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
+ integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=
+ dependencies:
+ arr-flatten "^1.0.1"
+
+arr-flatten@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+ integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
+
+array-equal@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
+ integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
+
+array-filter@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83"
+ integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=
+
+array-unique@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
+ integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=
+
+array.prototype.find@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.1.tgz#3baca26108ca7affb08db06bf0be6cb3115a969c"
+ integrity sha512-mi+MYNJYLTx2eNYy+Yh6raoQacCsNeeMUaspFPh9Y141lFSsWxxB8V9mM2ye+eqiRs917J6/pJ4M9ZPzenWckA==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.4"
+
+array.prototype.flat@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b"
+ integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.0-next.1"
+
+arrify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
+ integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
+
+asn1@~0.2.3:
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
+ integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
+ dependencies:
+ safer-buffer "~2.1.0"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+ integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
+
+async@^2.1.4:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
+ integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
+ dependencies:
+ lodash "^4.17.14"
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
+
+aws-sign2@~0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+ integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
+
+aws4@^1.8.0:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
+ integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
+
+babel-code-frame@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
+ integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=
+ dependencies:
+ chalk "^1.1.3"
+ esutils "^2.0.2"
+ js-tokens "^3.0.2"
+
+babel-core@^6.0.0, babel-core@^6.26.0:
+ version "6.26.3"
+ resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207"
+ integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==
+ dependencies:
+ babel-code-frame "^6.26.0"
+ babel-generator "^6.26.0"
+ babel-helpers "^6.24.1"
+ babel-messages "^6.23.0"
+ babel-register "^6.26.0"
+ babel-runtime "^6.26.0"
+ babel-template "^6.26.0"
+ babel-traverse "^6.26.0"
+ babel-types "^6.26.0"
+ babylon "^6.18.0"
+ convert-source-map "^1.5.1"
+ debug "^2.6.9"
+ json5 "^0.5.1"
+ lodash "^4.17.4"
+ minimatch "^3.0.4"
+ path-is-absolute "^1.0.1"
+ private "^0.1.8"
+ slash "^1.0.0"
+ source-map "^0.5.7"
+
+babel-generator@^6.18.0, babel-generator@^6.26.0:
+ version "6.26.1"
+ resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90"
+ integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==
+ dependencies:
+ babel-messages "^6.23.0"
+ babel-runtime "^6.26.0"
+ babel-types "^6.26.0"
+ detect-indent "^4.0.0"
+ jsesc "^1.3.0"
+ lodash "^4.17.4"
+ source-map "^0.5.7"
+ trim-right "^1.0.1"
+
+babel-helpers@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
+ integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+
+babel-jest@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-20.0.3.tgz#e4a03b13dc10389e140fc645d09ffc4ced301671"
+ integrity sha1-5KA7E9wQOJ4UD8ZF0J/8TO0wFnE=
+ dependencies:
+ babel-core "^6.0.0"
+ babel-plugin-istanbul "^4.0.0"
+ babel-preset-jest "^20.0.3"
+
+babel-messages@^6.23.0:
+ version "6.23.0"
+ resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
+ integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-istanbul@^4.0.0:
+ version "4.1.6"
+ resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz#36c59b2192efce81c5b378321b74175add1c9a45"
+ integrity sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==
+ dependencies:
+ babel-plugin-syntax-object-rest-spread "^6.13.0"
+ find-up "^2.1.0"
+ istanbul-lib-instrument "^1.10.1"
+ test-exclude "^4.2.1"
+
+babel-plugin-jest-hoist@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz#afedc853bd3f8dc3548ea671fbe69d03cc2c1767"
+ integrity sha1-r+3IU70/jcNUjqZx++adA8wsF2c=
+
+babel-plugin-syntax-object-rest-spread@^6.13.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
+ integrity sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=
+
+babel-plugin-transform-amd-to-commonjs@1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-amd-to-commonjs/-/babel-plugin-transform-amd-to-commonjs-1.4.0.tgz#d9bc5003eaa26dbdd4e854e453f84903852af2ca"
+ integrity sha512-Xx0kYPn0LPyms+8n2KLn9yd2R5XMb2P1sNe4qn64/UQY5F2KFYlhhhyYUNm/BThfODAzl7rbaOsEfpU2M8iDKQ==
+
+babel-preset-jest@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz#cbacaadecb5d689ca1e1de1360ebfc66862c178a"
+ integrity sha1-y6yq3stdaJyh4d4TYOv8ZoYsF4o=
+ dependencies:
+ babel-plugin-jest-hoist "^20.0.3"
+
+babel-register@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
+ integrity sha1-btAhFz4vy0htestFxgCahW9kcHE=
+ dependencies:
+ babel-core "^6.26.0"
+ babel-runtime "^6.26.0"
+ core-js "^2.5.0"
+ home-or-tmp "^2.0.0"
+ lodash "^4.17.4"
+ mkdirp "^0.5.1"
+ source-map-support "^0.4.15"
+
+babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runtime@^6.6.1:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+ integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
+ dependencies:
+ core-js "^2.4.0"
+ regenerator-runtime "^0.11.0"
+
+babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
+ integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=
+ dependencies:
+ babel-runtime "^6.26.0"
+ babel-traverse "^6.26.0"
+ babel-types "^6.26.0"
+ babylon "^6.18.0"
+ lodash "^4.17.4"
+
+babel-traverse@^6.18.0, babel-traverse@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
+ integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=
+ dependencies:
+ babel-code-frame "^6.26.0"
+ babel-messages "^6.23.0"
+ babel-runtime "^6.26.0"
+ babel-types "^6.26.0"
+ babylon "^6.18.0"
+ debug "^2.6.8"
+ globals "^9.18.0"
+ invariant "^2.2.2"
+ lodash "^4.17.4"
+
+babel-types@^6.18.0, babel-types@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
+ integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=
+ dependencies:
+ babel-runtime "^6.26.0"
+ esutils "^2.0.2"
+ lodash "^4.17.4"
+ to-fast-properties "^1.0.3"
+
+babylon@^6.18.0:
+ version "6.18.0"
+ resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
+ integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
+
+balanced-match@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+ integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+bcrypt-pbkdf@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
+ integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
+ dependencies:
+ tweetnacl "^0.14.3"
+
+boolbase@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+ integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+braces@^1.8.2:
+ version "1.8.5"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
+ integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=
+ dependencies:
+ expand-range "^1.8.1"
+ preserve "^0.2.0"
+ repeat-element "^1.1.2"
+
+browser-resolve@^1.11.2:
+ version "1.11.3"
+ resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6"
+ integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==
+ dependencies:
+ resolve "1.1.7"
+
+browser-stdout@1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
+ integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
+
+bser@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/bser/-/bser-1.0.2.tgz#381116970b2a6deea5646dd15dd7278444b56169"
+ integrity sha1-OBEWlwsqbe6lZG3RXdcnhES1YWk=
+ dependencies:
+ node-int64 "^0.4.0"
+
+bser@2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
+ integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==
+ dependencies:
+ node-int64 "^0.4.0"
+
+buffer-from@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+ integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
+
+builtin-modules@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+ integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
+
+callsites@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
+ integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
+
+camelcase@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
+ integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
+
+caseless@~0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+ integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
+
+chalk@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+ integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
+ dependencies:
+ ansi-styles "^2.2.1"
+ escape-string-regexp "^1.0.2"
+ has-ansi "^2.0.0"
+ strip-ansi "^3.0.0"
+ supports-color "^2.0.0"
+
+chalk@^2.0.0:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+ integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+ dependencies:
+ ansi-styles "^3.2.1"
+ escape-string-regexp "^1.0.5"
+ supports-color "^5.3.0"
+
+cheerio@^1.0.0-rc.3:
+ version "1.0.0-rc.3"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6"
+ integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==
+ dependencies:
+ css-select "~1.2.0"
+ dom-serializer "~0.1.1"
+ entities "~1.1.1"
+ htmlparser2 "^3.9.1"
+ lodash "^4.15.0"
+ parse5 "^3.0.1"
+
+ci-info@^1.5.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
+ integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==
+
+cliui@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
+ integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+ wrap-ansi "^2.0.0"
+
+code-point-at@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+ integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
+
+color-convert@^1.9.0:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+ dependencies:
+ color-name "1.1.3"
+
+color-name@1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+ integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+colors@^1.1.2:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
+ integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
+
+combined-stream@^1.0.6, combined-stream@~1.0.6:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ dependencies:
+ delayed-stream "~1.0.0"
+
+commander@2.15.1:
+ version "2.15.1"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
+ integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
+
+commander@^2.19.0, commander@~2.20.3:
+ version "2.20.3"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+commondir@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+ integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+content-type-parser@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7"
+ integrity sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ==
+
+convert-source-map@^1.4.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
+ integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
+ dependencies:
+ safe-buffer "~5.1.1"
+
+core-js@^0.8.3:
+ version "0.8.4"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-0.8.4.tgz#c22665f1e0d1b9c3c5e1b08dabd1f108695e4fcf"
+ integrity sha1-wiZl8eDRucPF4bCNq9HxCGleT88=
+
+core-js@^2.4.0, core-js@^2.5.0:
+ version "2.6.11"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
+ integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
+
+core-util-is@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+ integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+
+css-select@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
+ integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=
+ dependencies:
+ boolbase "~1.0.0"
+ css-what "2.1"
+ domutils "1.5.1"
+ nth-check "~1.0.1"
+
+css-what@2.1:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
+ integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
+
+cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
+ version "0.3.8"
+ resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
+ integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
+
+"cssstyle@>= 0.2.37 < 0.3.0":
+ version "0.2.37"
+ resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54"
+ integrity sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=
+ dependencies:
+ cssom "0.3.x"
+
+dashdash@^1.12.0:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+ integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
+ dependencies:
+ assert-plus "^1.0.0"
+
+debug@3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+ integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
+ dependencies:
+ ms "2.0.0"
+
+debug@^2.6.8, debug@^2.6.9:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+debug@^3.1.0:
+ version "3.2.6"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
+ integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+ dependencies:
+ ms "^2.1.1"
+
+debug@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
+ integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
+ dependencies:
+ ms "^2.1.1"
+
+decamelize@^1.1.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+ integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
+
+deep-is@~0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+ integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+
+default-require-extensions@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8"
+ integrity sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=
+ dependencies:
+ strip-bom "^2.0.0"
+
+define-properties@^1.1.2, define-properties@^1.1.3, define-properties@~1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
+ integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
+ dependencies:
+ object-keys "^1.0.12"
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
+
+detect-indent@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
+ integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg=
+ dependencies:
+ repeating "^2.0.0"
+
+devtools-modules@0.0.37:
+ version "0.0.37"
+ resolved "https://registry.yarnpkg.com/devtools-modules/-/devtools-modules-0.0.37.tgz#29b0041e444fe8b08aae3833b5433ab004d012b3"
+ integrity sha512-60yjoRsmBSwaHc2vbwxgo+NKgzAZYRppLI1Q5x3sQ8VArGD2nUDVNRBCtR2SihjodNSQuMB4S9owIiE6fSJ2ow==
+ dependencies:
+ jest "^20.0.4"
+ punycode "^2.1.0"
+
+devtools-services@^0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/devtools-services/-/devtools-services-0.0.3.tgz#69f1c5653f26b45b4cb0eed41b182a7bb74b98ad"
+ integrity sha512-O3GGocQwd1q7yUzEFMpHc/aPS2IVOltjlWFlmJXedi1VoX0mbDpjkJHU0svQfiJzEqL7ySMS4qMn+MlIUXySNA==
+
+diff@3.5.0, diff@^3.2.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
+ integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
+
+discontinuous-range@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a"
+ integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=
+
+dom-serializer@0:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
+ integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
+ dependencies:
+ domelementtype "^2.0.1"
+ entities "^2.0.0"
+
+dom-serializer@~0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
+ integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==
+ dependencies:
+ domelementtype "^1.3.0"
+ entities "^1.1.1"
+
+dom-walk@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
+ integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=
+
+domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
+ integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
+
+domelementtype@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d"
+ integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==
+
+domhandler@^2.3.0:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
+ integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==
+ dependencies:
+ domelementtype "1"
+
+domutils@1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
+ integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
+ dependencies:
+ dom-serializer "0"
+ domelementtype "1"
+
+domutils@^1.5.1:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
+ integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
+ dependencies:
+ dom-serializer "0"
+ domelementtype "1"
+
+ecc-jsbn@~0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
+ integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
+ dependencies:
+ jsbn "~0.1.0"
+ safer-buffer "^2.1.0"
+
+entities@^1.1.1, entities@~1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
+ integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
+
+entities@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
+ integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
+
+enzyme-adapter-react-16@^1.1.1:
+ version "1.15.2"
+ resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.2.tgz#b16db2f0ea424d58a808f9df86ab6212895a4501"
+ integrity sha512-SkvDrb8xU3lSxID8Qic9rB8pvevDbLybxPK6D/vW7PrT0s2Cl/zJYuXvsd1EBTz0q4o3iqG3FJhpYz3nUNpM2Q==
+ dependencies:
+ enzyme-adapter-utils "^1.13.0"
+ enzyme-shallow-equal "^1.0.1"
+ has "^1.0.3"
+ object.assign "^4.1.0"
+ object.values "^1.1.1"
+ prop-types "^15.7.2"
+ react-is "^16.12.0"
+ react-test-renderer "^16.0.0-0"
+ semver "^5.7.0"
+
+enzyme-adapter-utils@^1.13.0:
+ version "1.13.0"
+ resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.13.0.tgz#01c885dde2114b4690bf741f8dc94cee3060eb78"
+ integrity sha512-YuEtfQp76Lj5TG1NvtP2eGJnFKogk/zT70fyYHXK2j3v6CtuHqc8YmgH/vaiBfL8K1SgVVbQXtTcgQZFwzTVyQ==
+ dependencies:
+ airbnb-prop-types "^2.15.0"
+ function.prototype.name "^1.1.2"
+ object.assign "^4.1.0"
+ object.fromentries "^2.0.2"
+ prop-types "^15.7.2"
+ semver "^5.7.1"
+
+enzyme-shallow-equal@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.1.tgz#7afe03db3801c9b76de8440694096412a8d9d49e"
+ integrity sha512-hGA3i1so8OrYOZSM9whlkNmVHOicJpsjgTzC+wn2JMJXhq1oO4kA4bJ5MsfzSIcC71aLDKzJ6gZpIxrqt3QTAQ==
+ dependencies:
+ has "^1.0.3"
+ object-is "^1.0.2"
+
+enzyme@^3.3.0:
+ version "3.11.0"
+ resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.11.0.tgz#71d680c580fe9349f6f5ac6c775bc3e6b7a79c28"
+ integrity sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==
+ dependencies:
+ array.prototype.flat "^1.2.3"
+ cheerio "^1.0.0-rc.3"
+ enzyme-shallow-equal "^1.0.1"
+ function.prototype.name "^1.1.2"
+ has "^1.0.3"
+ html-element-map "^1.2.0"
+ is-boolean-object "^1.0.1"
+ is-callable "^1.1.5"
+ is-number-object "^1.0.4"
+ is-regex "^1.0.5"
+ is-string "^1.0.5"
+ is-subset "^0.1.1"
+ lodash.escape "^4.0.1"
+ lodash.isequal "^4.5.0"
+ object-inspect "^1.7.0"
+ object-is "^1.0.2"
+ object.assign "^4.1.0"
+ object.entries "^1.1.1"
+ object.values "^1.1.1"
+ raf "^3.4.1"
+ rst-selector-parser "^2.2.3"
+ string.prototype.trim "^1.2.1"
+
+errno@~0.1.7:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
+ integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==
+ dependencies:
+ prr "~1.0.1"
+
+error-ex@^1.2.0:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+ integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+ dependencies:
+ is-arrayish "^0.2.1"
+
+es-abstract@^1.17.0-next.1, es-abstract@^1.17.4:
+ version "1.17.4"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184"
+ integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==
+ dependencies:
+ es-to-primitive "^1.2.1"
+ function-bind "^1.1.1"
+ has "^1.0.3"
+ has-symbols "^1.0.1"
+ is-callable "^1.1.5"
+ is-regex "^1.0.5"
+ object-inspect "^1.7.0"
+ object-keys "^1.1.1"
+ object.assign "^4.1.0"
+ string.prototype.trimleft "^2.1.1"
+ string.prototype.trimright "^2.1.1"
+
+es-get-iterator@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8"
+ integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==
+ dependencies:
+ es-abstract "^1.17.4"
+ has-symbols "^1.0.1"
+ is-arguments "^1.0.4"
+ is-map "^2.0.1"
+ is-set "^2.0.1"
+ is-string "^1.0.5"
+ isarray "^2.0.5"
+
+es-to-primitive@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
+ integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
+ dependencies:
+ is-callable "^1.1.4"
+ is-date-object "^1.0.1"
+ is-symbol "^1.0.2"
+
+escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+escodegen@^1.6.1:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457"
+ integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==
+ dependencies:
+ esprima "^4.0.1"
+ estraverse "^4.2.0"
+ esutils "^2.0.2"
+ optionator "^0.8.1"
+ optionalDependencies:
+ source-map "~0.6.1"
+
+esprima@^4.0.0, esprima@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+estraverse@^4.2.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+exec-sh@^0.2.0:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.2.tgz#2a5e7ffcbd7d0ba2755bdecb16e5a427dfbdec36"
+ integrity sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==
+ dependencies:
+ merge "^1.2.0"
+
+expand-brackets@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
+ integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=
+ dependencies:
+ is-posix-bracket "^0.1.0"
+
+expand-range@^1.8.1:
+ version "1.8.2"
+ resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
+ integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=
+ dependencies:
+ fill-range "^2.1.0"
+
+expect@^1.16.0:
+ version "1.20.2"
+ resolved "https://registry.yarnpkg.com/expect/-/expect-1.20.2.tgz#d458fe4c56004036bae3232416a3f6361f04f965"
+ integrity sha1-1Fj+TFYAQDa64yMkFqP2Nh8E+WU=
+ dependencies:
+ define-properties "~1.1.2"
+ has "^1.0.1"
+ is-equal "^1.5.1"
+ is-regex "^1.0.3"
+ object-inspect "^1.1.0"
+ object-keys "^1.0.9"
+ tmatch "^2.0.1"
+
+extend@~3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+ integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+
+extglob@^0.3.1:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
+ integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=
+ dependencies:
+ is-extglob "^1.0.0"
+
+extsprintf@1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+ integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
+
+extsprintf@^1.2.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
+ integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
+
+fast-deep-equal@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
+ integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@~2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
+fb-watchman@^1.8.0:
+ version "1.9.2"
+ resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-1.9.2.tgz#a24cf47827f82d38fb59a69ad70b76e3b6ae7383"
+ integrity sha1-okz0eCf4LTj7Waaa1wt247auc4M=
+ dependencies:
+ bser "1.0.2"
+
+fb-watchman@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85"
+ integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==
+ dependencies:
+ bser "2.1.1"
+
+filename-regex@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
+ integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=
+
+fileset@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0"
+ integrity sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=
+ dependencies:
+ glob "^7.0.3"
+ minimatch "^3.0.3"
+
+fill-range@^2.1.0:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565"
+ integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==
+ dependencies:
+ is-number "^2.1.0"
+ isobject "^2.0.0"
+ randomatic "^3.0.0"
+ repeat-element "^1.1.2"
+ repeat-string "^1.5.2"
+
+find-cache-dir@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
+ integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==
+ dependencies:
+ commondir "^1.0.1"
+ make-dir "^2.0.0"
+ pkg-dir "^3.0.0"
+
+find-up@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+ integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=
+ dependencies:
+ path-exists "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+find-up@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
+ integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
+ dependencies:
+ locate-path "^2.0.0"
+
+find-up@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+ integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+ dependencies:
+ locate-path "^3.0.0"
+
+for-in@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+ integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
+
+for-own@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
+ integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=
+ dependencies:
+ for-in "^1.0.1"
+
+forever-agent@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+ integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
+
+form-data@~2.3.2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
+ integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.6"
+ mime-types "^2.1.12"
+
+formatio@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9"
+ integrity sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=
+ dependencies:
+ samsam "~1.1"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+function-bind@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+function.prototype.name@^1.1.1, function.prototype.name@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.2.tgz#5cdf79d7c05db401591dfde83e3b70c5123e9a45"
+ integrity sha512-C8A+LlHBJjB2AdcRPorc5JvJ5VUoWlXdEHLOJdCI7kjHEtGTpHQUiqMvCIKUwIsGwZX2jZJy761AXsn356bJQg==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.0-next.1"
+ functions-have-names "^1.2.0"
+
+functions-have-names@^1.2.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.1.tgz#a981ac397fa0c9964551402cdc5533d7a4d52f91"
+ integrity sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA==
+
+gensync@^1.0.0-beta.1:
+ version "1.0.0-beta.1"
+ resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
+ integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==
+
+get-caller-file@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
+ integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
+
+getpass@^0.1.1:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+ integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
+ dependencies:
+ assert-plus "^1.0.0"
+
+glob-base@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
+ integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=
+ dependencies:
+ glob-parent "^2.0.0"
+ is-glob "^2.0.0"
+
+glob-parent@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
+ integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=
+ dependencies:
+ is-glob "^2.0.0"
+
+glob@7.1.2:
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
+ integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+glob@^7.0.3, glob@^7.1.1, glob@^7.1.3:
+ version "7.1.6"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
+ integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+global@^4.3.2:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406"
+ integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==
+ dependencies:
+ min-document "^2.19.0"
+ process "^0.11.10"
+
+globals@^11.1.0:
+ version "11.12.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+ integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+globals@^9.18.0:
+ version "9.18.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
+ integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.2:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
+ integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
+
+growl@1.10.5:
+ version "1.10.5"
+ resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
+ integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
+
+growly@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
+ integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
+
+handlebars@^4.0.3:
+ version "4.7.3"
+ resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.3.tgz#8ece2797826886cf8082d1726ff21d2a022550ee"
+ integrity sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg==
+ dependencies:
+ neo-async "^2.6.0"
+ optimist "^0.6.1"
+ source-map "^0.6.1"
+ optionalDependencies:
+ uglify-js "^3.1.4"
+
+har-schema@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+ integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
+
+har-validator@~5.1.3:
+ version "5.1.3"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
+ integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
+ dependencies:
+ ajv "^6.5.5"
+ har-schema "^2.0.0"
+
+has-ansi@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+ integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
+ dependencies:
+ ansi-regex "^2.0.0"
+
+has-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+ integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
+
+has-flag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+ integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
+has-symbols@^1.0.0, has-symbols@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
+ integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
+
+has@^1.0.1, has@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+ integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+ dependencies:
+ function-bind "^1.1.1"
+
+he@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
+ integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
+
+home-or-tmp@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
+ integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg=
+ dependencies:
+ os-homedir "^1.0.0"
+ os-tmpdir "^1.0.1"
+
+hosted-git-info@^2.1.4:
+ version "2.8.8"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
+ integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
+
+html-element-map@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.2.0.tgz#dfbb09efe882806af63d990cf6db37993f099f22"
+ integrity sha512-0uXq8HsuG1v2TmQ8QkIhzbrqeskE4kn52Q18QJ9iAA/SnHoEKXWiUxHQtclRsCFWEUD2So34X+0+pZZu862nnw==
+ dependencies:
+ array-filter "^1.0.0"
+
+html-encoding-sniffer@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8"
+ integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==
+ dependencies:
+ whatwg-encoding "^1.0.1"
+
+htmlparser2@^3.9.1:
+ version "3.10.1"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
+ integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
+ dependencies:
+ domelementtype "^1.3.1"
+ domhandler "^2.3.0"
+ domutils "^1.5.1"
+ entities "^1.1.1"
+ inherits "^2.0.1"
+ readable-stream "^3.1.1"
+
+http-signature@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+ integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
+ dependencies:
+ assert-plus "^1.0.0"
+ jsprim "^1.2.2"
+ sshpk "^1.7.0"
+
+iconv-lite@0.4.24:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+ integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@^2.0.1, inherits@^2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+invariant@^2.2.2:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+ integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
+ dependencies:
+ loose-envify "^1.0.0"
+
+invert-kv@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
+ integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY=
+
+is-arguments@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3"
+ integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+ integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
+
+is-arrow-function@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/is-arrow-function/-/is-arrow-function-2.0.3.tgz#29be2c2d8d9450852b8bbafb635ba7b8d8e87ec2"
+ integrity sha1-Kb4sLY2UUIUri7r7Y1unuNjofsI=
+ dependencies:
+ is-callable "^1.0.4"
+
+is-bigint@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4"
+ integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==
+
+is-boolean-object@^1.0.0, is-boolean-object@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e"
+ integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==
+
+is-buffer@^1.1.5:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+ integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
+is-builtin-module@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
+ integrity sha1-VAVy0096wxGfj3bDDLwbHgN6/74=
+ dependencies:
+ builtin-modules "^1.0.0"
+
+is-callable@^1.0.4, is-callable@^1.1.4, is-callable@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
+ integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==
+
+is-ci@^1.0.10:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c"
+ integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==
+ dependencies:
+ ci-info "^1.5.0"
+
+is-date-object@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
+ integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
+
+is-dotfile@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
+ integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=
+
+is-equal-shallow@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534"
+ integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=
+ dependencies:
+ is-primitive "^2.0.0"
+
+is-equal@^1.5.1:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/is-equal/-/is-equal-1.6.1.tgz#74fafde5060fcaf187041c05f11f0b9f020bb9b3"
+ integrity sha512-3/79QTolnfNFrxQAvqH8M+O01uGWsVq54BUPG2mXQH7zi4BE/0TY+fmA444t8xSBvIwyNMvsTmCZ5ViVDlqPJg==
+ dependencies:
+ es-get-iterator "^1.0.1"
+ functions-have-names "^1.2.0"
+ has "^1.0.3"
+ is-arrow-function "^2.0.3"
+ is-bigint "^1.0.0"
+ is-boolean-object "^1.0.0"
+ is-callable "^1.1.4"
+ is-date-object "^1.0.1"
+ is-generator-function "^1.0.7"
+ is-number-object "^1.0.3"
+ is-regex "^1.0.4"
+ is-string "^1.0.4"
+ is-symbol "^1.0.3"
+ isarray "^2.0.5"
+ object-inspect "^1.7.0"
+ object.entries "^1.1.0"
+ which-boxed-primitive "^1.0.1"
+ which-collection "^1.0.0"
+
+is-extendable@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+ integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
+
+is-extglob@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
+ integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=
+
+is-finite@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3"
+ integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==
+
+is-fullwidth-code-point@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+ integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-generator-function@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522"
+ integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==
+
+is-glob@^2.0.0, is-glob@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
+ integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=
+ dependencies:
+ is-extglob "^1.0.0"
+
+is-map@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1"
+ integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==
+
+is-number-object@^1.0.3, is-number-object@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197"
+ integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==
+
+is-number@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
+ integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=
+ dependencies:
+ kind-of "^3.0.2"
+
+is-number@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
+ integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
+
+is-posix-bracket@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
+ integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=
+
+is-primitive@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
+ integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU=
+
+is-regex@^1.0.3, is-regex@^1.0.4, is-regex@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae"
+ integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==
+ dependencies:
+ has "^1.0.3"
+
+is-set@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43"
+ integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==
+
+is-string@^1.0.4, is-string@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6"
+ integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==
+
+is-subset@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6"
+ integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=
+
+is-symbol@^1.0.2, is-symbol@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
+ integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==
+ dependencies:
+ has-symbols "^1.0.1"
+
+is-typedarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+ integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
+
+is-utf8@^0.2.0:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
+ integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
+
+is-weakmap@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2"
+ integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==
+
+is-weakset@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83"
+ integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==
+
+is-wsl@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
+ integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
+
+isarray@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+ integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isarray@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
+ integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+isobject@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+ integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
+ dependencies:
+ isarray "1.0.0"
+
+isstream@~0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+ integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
+
+istanbul-api@^1.1.1:
+ version "1.3.7"
+ resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.3.7.tgz#a86c770d2b03e11e3f778cd7aedd82d2722092aa"
+ integrity sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA==
+ dependencies:
+ async "^2.1.4"
+ fileset "^2.0.2"
+ istanbul-lib-coverage "^1.2.1"
+ istanbul-lib-hook "^1.2.2"
+ istanbul-lib-instrument "^1.10.2"
+ istanbul-lib-report "^1.1.5"
+ istanbul-lib-source-maps "^1.2.6"
+ istanbul-reports "^1.5.1"
+ js-yaml "^3.7.0"
+ mkdirp "^0.5.1"
+ once "^1.4.0"
+
+istanbul-lib-coverage@^1.0.1, istanbul-lib-coverage@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0"
+ integrity sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==
+
+istanbul-lib-hook@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz#bc6bf07f12a641fbf1c85391d0daa8f0aea6bf86"
+ integrity sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw==
+ dependencies:
+ append-transform "^0.4.0"
+
+istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.10.2, istanbul-lib-instrument@^1.4.2:
+ version "1.10.2"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz#1f55ed10ac3c47f2bdddd5307935126754d0a9ca"
+ integrity sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==
+ dependencies:
+ babel-generator "^6.18.0"
+ babel-template "^6.16.0"
+ babel-traverse "^6.18.0"
+ babel-types "^6.18.0"
+ babylon "^6.18.0"
+ istanbul-lib-coverage "^1.2.1"
+ semver "^5.3.0"
+
+istanbul-lib-report@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz#f2a657fc6282f96170aaf281eb30a458f7f4170c"
+ integrity sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw==
+ dependencies:
+ istanbul-lib-coverage "^1.2.1"
+ mkdirp "^0.5.1"
+ path-parse "^1.0.5"
+ supports-color "^3.1.2"
+
+istanbul-lib-source-maps@^1.1.0, istanbul-lib-source-maps@^1.2.6:
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz#37b9ff661580f8fca11232752ee42e08c6675d8f"
+ integrity sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg==
+ dependencies:
+ debug "^3.1.0"
+ istanbul-lib-coverage "^1.2.1"
+ mkdirp "^0.5.1"
+ rimraf "^2.6.1"
+ source-map "^0.5.3"
+
+istanbul-reports@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.5.1.tgz#97e4dbf3b515e8c484caea15d6524eebd3ff4e1a"
+ integrity sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw==
+ dependencies:
+ handlebars "^4.0.3"
+
+jest-changed-files@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-20.0.3.tgz#9394d5cc65c438406149bef1bf4d52b68e03e3f8"
+ integrity sha1-k5TVzGXEOEBhSb7xv01Sto4D4/g=
+
+jest-cli@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-20.0.4.tgz#e532b19d88ae5bc6c417e8b0593a6fe954b1dc93"
+ integrity sha1-5TKxnYiuW8bEF+iwWTpv6VSx3JM=
+ dependencies:
+ ansi-escapes "^1.4.0"
+ callsites "^2.0.0"
+ chalk "^1.1.3"
+ graceful-fs "^4.1.11"
+ is-ci "^1.0.10"
+ istanbul-api "^1.1.1"
+ istanbul-lib-coverage "^1.0.1"
+ istanbul-lib-instrument "^1.4.2"
+ istanbul-lib-source-maps "^1.1.0"
+ jest-changed-files "^20.0.3"
+ jest-config "^20.0.4"
+ jest-docblock "^20.0.3"
+ jest-environment-jsdom "^20.0.3"
+ jest-haste-map "^20.0.4"
+ jest-jasmine2 "^20.0.4"
+ jest-message-util "^20.0.3"
+ jest-regex-util "^20.0.3"
+ jest-resolve-dependencies "^20.0.3"
+ jest-runtime "^20.0.4"
+ jest-snapshot "^20.0.3"
+ jest-util "^20.0.3"
+ micromatch "^2.3.11"
+ node-notifier "^5.0.2"
+ pify "^2.3.0"
+ slash "^1.0.0"
+ string-length "^1.0.1"
+ throat "^3.0.0"
+ which "^1.2.12"
+ worker-farm "^1.3.1"
+ yargs "^7.0.2"
+
+jest-config@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-20.0.4.tgz#e37930ab2217c913605eff13e7bd763ec48faeea"
+ integrity sha1-43kwqyIXyRNgXv8T5712PsSPruo=
+ dependencies:
+ chalk "^1.1.3"
+ glob "^7.1.1"
+ jest-environment-jsdom "^20.0.3"
+ jest-environment-node "^20.0.3"
+ jest-jasmine2 "^20.0.4"
+ jest-matcher-utils "^20.0.3"
+ jest-regex-util "^20.0.3"
+ jest-resolve "^20.0.4"
+ jest-validate "^20.0.3"
+ pretty-format "^20.0.3"
+
+jest-diff@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-20.0.3.tgz#81f288fd9e675f0fb23c75f1c2b19445fe586617"
+ integrity sha1-gfKI/Z5nXw+yPHXxwrGURf5YZhc=
+ dependencies:
+ chalk "^1.1.3"
+ diff "^3.2.0"
+ jest-matcher-utils "^20.0.3"
+ pretty-format "^20.0.3"
+
+jest-docblock@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-20.0.3.tgz#17bea984342cc33d83c50fbe1545ea0efaa44712"
+ integrity sha1-F76phDQswz2DxQ++FUXqDvqkRxI=
+
+jest-environment-jsdom@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-20.0.3.tgz#048a8ac12ee225f7190417713834bb999787de99"
+ integrity sha1-BIqKwS7iJfcZBBdxODS7mZeH3pk=
+ dependencies:
+ jest-mock "^20.0.3"
+ jest-util "^20.0.3"
+ jsdom "^9.12.0"
+
+jest-environment-node@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-20.0.3.tgz#d488bc4612af2c246e986e8ae7671a099163d403"
+ integrity sha1-1Ii8RhKvLCRumG6K52caCZFj1AM=
+ dependencies:
+ jest-mock "^20.0.3"
+ jest-util "^20.0.3"
+
+jest-haste-map@^20.0.4:
+ version "20.0.5"
+ resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-20.0.5.tgz#abad74efb1a005974a7b6517e11010709cab9112"
+ integrity sha512-0IKAQjUvuZjMCNi/0VNQQF74/H9KB67hsHJqGiwTWQC6XO5Azs7kLWm+6Q/dwuhvDUvABDOBMFK2/FwZ3sZ07Q==
+ dependencies:
+ fb-watchman "^2.0.0"
+ graceful-fs "^4.1.11"
+ jest-docblock "^20.0.3"
+ micromatch "^2.3.11"
+ sane "~1.6.0"
+ worker-farm "^1.3.1"
+
+jest-jasmine2@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-20.0.4.tgz#fcc5b1411780d911d042902ef1859e852e60d5e1"
+ integrity sha1-/MWxQReA2RHQQpAu8YWehS5g1eE=
+ dependencies:
+ chalk "^1.1.3"
+ graceful-fs "^4.1.11"
+ jest-diff "^20.0.3"
+ jest-matcher-utils "^20.0.3"
+ jest-matchers "^20.0.3"
+ jest-message-util "^20.0.3"
+ jest-snapshot "^20.0.3"
+ once "^1.4.0"
+ p-map "^1.1.1"
+
+jest-matcher-utils@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-20.0.3.tgz#b3a6b8e37ca577803b0832a98b164f44b7815612"
+ integrity sha1-s6a443yld4A7CDKpixZPRLeBVhI=
+ dependencies:
+ chalk "^1.1.3"
+ pretty-format "^20.0.3"
+
+jest-matchers@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-matchers/-/jest-matchers-20.0.3.tgz#ca69db1c32db5a6f707fa5e0401abb55700dfd60"
+ integrity sha1-ymnbHDLbWm9wf6XgQBq7VXAN/WA=
+ dependencies:
+ jest-diff "^20.0.3"
+ jest-matcher-utils "^20.0.3"
+ jest-message-util "^20.0.3"
+ jest-regex-util "^20.0.3"
+
+jest-message-util@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-20.0.3.tgz#6aec2844306fcb0e6e74d5796c1006d96fdd831c"
+ integrity sha1-auwoRDBvyw5udNV5bBAG2W/dgxw=
+ dependencies:
+ chalk "^1.1.3"
+ micromatch "^2.3.11"
+ slash "^1.0.0"
+
+jest-mock@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-20.0.3.tgz#8bc070e90414aa155c11a8d64c869a0d5c71da59"
+ integrity sha1-i8Bw6QQUqhVcEajWTIaaDVxx2lk=
+
+jest-regex-util@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-20.0.3.tgz#85bbab5d133e44625b19faf8c6aa5122d085d762"
+ integrity sha1-hburXRM+RGJbGfr4xqpRItCF12I=
+
+jest-resolve-dependencies@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-20.0.3.tgz#6e14a7b717af0f2cb3667c549de40af017b1723a"
+ integrity sha1-bhSntxevDyyzZnxUneQK8Bexcjo=
+ dependencies:
+ jest-regex-util "^20.0.3"
+
+jest-resolve@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-20.0.4.tgz#9448b3e8b6bafc15479444c6499045b7ffe597a5"
+ integrity sha1-lEiz6La6/BVHlETGSZBFt//ll6U=
+ dependencies:
+ browser-resolve "^1.11.2"
+ is-builtin-module "^1.0.0"
+ resolve "^1.3.2"
+
+jest-runtime@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-20.0.4.tgz#a2c802219c4203f754df1404e490186169d124d8"
+ integrity sha1-osgCIZxCA/dU3xQE5JAYYWnRJNg=
+ dependencies:
+ babel-core "^6.0.0"
+ babel-jest "^20.0.3"
+ babel-plugin-istanbul "^4.0.0"
+ chalk "^1.1.3"
+ convert-source-map "^1.4.0"
+ graceful-fs "^4.1.11"
+ jest-config "^20.0.4"
+ jest-haste-map "^20.0.4"
+ jest-regex-util "^20.0.3"
+ jest-resolve "^20.0.4"
+ jest-util "^20.0.3"
+ json-stable-stringify "^1.0.1"
+ micromatch "^2.3.11"
+ strip-bom "3.0.0"
+ yargs "^7.0.2"
+
+jest-snapshot@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-20.0.3.tgz#5b847e1adb1a4d90852a7f9f125086e187c76566"
+ integrity sha1-W4R+GtsaTZCFKn+fElCG4YfHZWY=
+ dependencies:
+ chalk "^1.1.3"
+ jest-diff "^20.0.3"
+ jest-matcher-utils "^20.0.3"
+ jest-util "^20.0.3"
+ natural-compare "^1.4.0"
+ pretty-format "^20.0.3"
+
+jest-util@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-20.0.3.tgz#0c07f7d80d82f4e5a67c6f8b9c3fe7f65cfd32ad"
+ integrity sha1-DAf32A2C9OWmfG+LnD/n9lz9Mq0=
+ dependencies:
+ chalk "^1.1.3"
+ graceful-fs "^4.1.11"
+ jest-message-util "^20.0.3"
+ jest-mock "^20.0.3"
+ jest-validate "^20.0.3"
+ leven "^2.1.0"
+ mkdirp "^0.5.1"
+
+jest-validate@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-20.0.3.tgz#d0cfd1de4f579f298484925c280f8f1d94ec3cab"
+ integrity sha1-0M/R3k9XnymEhJJcKA+PHZTsPKs=
+ dependencies:
+ chalk "^1.1.3"
+ jest-matcher-utils "^20.0.3"
+ leven "^2.1.0"
+ pretty-format "^20.0.3"
+
+jest@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest/-/jest-20.0.4.tgz#3dd260c2989d6dad678b1e9cc4d91944f6d602ac"
+ integrity sha1-PdJgwpidba1nix6cxNkZRPbWAqw=
+ dependencies:
+ jest-cli "^20.0.4"
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-tokens@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+ integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
+
+js-yaml@^3.7.0:
+ version "3.13.1"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
+ integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
+jsbn@~0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+ integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
+
+jsdom-global@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/jsdom-global/-/jsdom-global-2.1.1.tgz#47d46fe77f6167baf5d34431d3bb59fc41b0915a"
+ integrity sha1-R9Rv539hZ7r100Qx07tZ/EGwkVo=
+
+jsdom@^9.12.0, jsdom@^9.4.1:
+ version "9.12.0"
+ resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4"
+ integrity sha1-6MVG//ywbADUgzyoRBD+1/igl9Q=
+ dependencies:
+ abab "^1.0.3"
+ acorn "^4.0.4"
+ acorn-globals "^3.1.0"
+ array-equal "^1.0.0"
+ content-type-parser "^1.0.1"
+ cssom ">= 0.3.2 < 0.4.0"
+ cssstyle ">= 0.2.37 < 0.3.0"
+ escodegen "^1.6.1"
+ html-encoding-sniffer "^1.0.1"
+ nwmatcher ">= 1.3.9 < 2.0.0"
+ parse5 "^1.5.1"
+ request "^2.79.0"
+ sax "^1.2.1"
+ symbol-tree "^3.2.1"
+ tough-cookie "^2.3.2"
+ webidl-conversions "^4.0.0"
+ whatwg-encoding "^1.0.1"
+ whatwg-url "^4.3.0"
+ xml-name-validator "^2.0.1"
+
+jsesc@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
+ integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s=
+
+jsesc@^2.5.1:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+ integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema@0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+ integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
+
+json-stable-stringify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
+ integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=
+ dependencies:
+ jsonify "~0.0.0"
+
+json-stringify-safe@~5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+ integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
+
+json5@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
+ integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
+
+json5@^2.1.0:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.2.tgz#43ef1f0af9835dd624751a6b7fa48874fb2d608e"
+ integrity sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==
+ dependencies:
+ minimist "^1.2.5"
+
+jsonify@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+ integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
+
+jsprim@^1.2.2:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
+ integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
+ dependencies:
+ assert-plus "1.0.0"
+ extsprintf "1.3.0"
+ json-schema "0.2.3"
+ verror "1.10.0"
+
+kind-of@^3.0.2:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+ integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
+ dependencies:
+ is-buffer "^1.1.5"
+
+kind-of@^6.0.0:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
+lcid@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
+ integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=
+ dependencies:
+ invert-kv "^1.0.0"
+
+leven@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
+ integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA=
+
+levn@~0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+ integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
+ dependencies:
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+
+load-json-file@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
+ integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=
+ dependencies:
+ graceful-fs "^4.1.2"
+ parse-json "^2.2.0"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+ strip-bom "^2.0.0"
+
+locate-path@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
+ integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=
+ dependencies:
+ p-locate "^2.0.0"
+ path-exists "^3.0.0"
+
+locate-path@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+ integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+ dependencies:
+ p-locate "^3.0.0"
+ path-exists "^3.0.0"
+
+lodash.escape@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98"
+ integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=
+
+lodash.flattendeep@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
+ integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=
+
+lodash.isequal@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
+ integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
+
+lodash@^4.15.0, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.4:
+ version "4.17.15"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
+ integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+
+lodash@^4.17.19:
+ version "4.17.20"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
+ integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
+
+lolex@1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31"
+ integrity sha1-fD2mL/yzDw9agKJWbKJORdigHzE=
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+ integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+ dependencies:
+ js-tokens "^3.0.0 || ^4.0.0"
+
+make-dir@^2.0.0, make-dir@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
+ integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
+ dependencies:
+ pify "^4.0.1"
+ semver "^5.6.0"
+
+makeerror@1.0.x:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c"
+ integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=
+ dependencies:
+ tmpl "1.0.x"
+
+math-random@^1.0.1:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
+ integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==
+
+merge@^1.2.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145"
+ integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==
+
+micromatch@^2.1.5, micromatch@^2.3.11:
+ version "2.3.11"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
+ integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=
+ dependencies:
+ arr-diff "^2.0.0"
+ array-unique "^0.2.1"
+ braces "^1.8.2"
+ expand-brackets "^0.1.4"
+ extglob "^0.3.1"
+ filename-regex "^2.0.0"
+ is-extglob "^1.0.0"
+ is-glob "^2.0.1"
+ kind-of "^3.0.2"
+ normalize-path "^2.0.1"
+ object.omit "^2.0.0"
+ parse-glob "^3.0.4"
+ regex-cache "^0.4.2"
+
+mime-db@1.43.0:
+ version "1.43.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
+ integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==
+
+mime-types@^2.1.12, mime-types@~2.1.19:
+ version "2.1.26"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06"
+ integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==
+ dependencies:
+ mime-db "1.43.0"
+
+min-document@^2.19.0:
+ version "2.19.0"
+ resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
+ integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=
+ dependencies:
+ dom-walk "^0.1.0"
+
+minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+ integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
+
+minimist@^1.1.1, minimist@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+ integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+
+minimist@~0.0.1:
+ version "0.0.10"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
+ integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
+
+mkdirp@0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+ integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
+ dependencies:
+ minimist "0.0.8"
+
+mkdirp@^0.5.1:
+ version "0.5.3"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.3.tgz#5a514b7179259287952881e94410ec5465659f8c"
+ integrity sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==
+ dependencies:
+ minimist "^1.2.5"
+
+mocha@^5.0.1:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
+ integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==
+ dependencies:
+ browser-stdout "1.3.1"
+ commander "2.15.1"
+ debug "3.1.0"
+ diff "3.5.0"
+ escape-string-regexp "1.0.5"
+ glob "7.1.2"
+ growl "1.10.5"
+ he "1.1.1"
+ minimatch "3.0.4"
+ mkdirp "0.5.1"
+ supports-color "5.4.0"
+
+mock-local-storage@^1.0.5:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/mock-local-storage/-/mock-local-storage-1.1.11.tgz#2a36faeb30f76ef3c5005460b6bbf12f19555811"
+ integrity sha512-71sytP93tB0CkPbacafcP1iTVj9ssXU+ztRmx1MrS488JpO0az5d2sx+SNuvhGVSW56XO5M0f82uFhxteZEp9w==
+ dependencies:
+ core-js "^0.8.3"
+ global "^4.3.2"
+
+moo@^0.5.0:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
+ integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@^2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+
+nearley@^2.7.10:
+ version "2.19.1"
+ resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.1.tgz#4af4006e16645ff800e9f993c3af039857d9dbdc"
+ integrity sha512-xq47GIUGXxU9vQg7g/y1o1xuKnkO7ev4nRWqftmQrLkfnE/FjRqDaGOUakM8XHPn/6pW3bGjU2wgoJyId90rqg==
+ dependencies:
+ commander "^2.19.0"
+ moo "^0.5.0"
+ railroad-diagrams "^1.0.0"
+ randexp "0.4.6"
+ semver "^5.4.1"
+
+neo-async@^2.6.0:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
+ integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==
+
+node-int64@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
+ integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=
+
+node-modules-regexp@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40"
+ integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=
+
+node-notifier@^5.0.2:
+ version "5.4.3"
+ resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50"
+ integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==
+ dependencies:
+ growly "^1.3.0"
+ is-wsl "^1.1.0"
+ semver "^5.5.0"
+ shellwords "^0.1.1"
+ which "^1.3.0"
+
+normalize-package-data@^2.3.2:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
+ integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
+ dependencies:
+ hosted-git-info "^2.1.4"
+ resolve "^1.10.0"
+ semver "2 || 3 || 4 || 5"
+ validate-npm-package-license "^3.0.1"
+
+normalize-path@^2.0.0, normalize-path@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
+ integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
+ dependencies:
+ remove-trailing-separator "^1.0.1"
+
+nth-check@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
+ integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
+ dependencies:
+ boolbase "~1.0.0"
+
+number-is-nan@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+ integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
+
+"nwmatcher@>= 1.3.9 < 2.0.0":
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.4.tgz#2285631f34a95f0d0395cd900c96ed39b58f346e"
+ integrity sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ==
+
+oauth-sign@~0.9.0:
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
+ integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
+
+object-assign@^4.1.0, object-assign@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+object-inspect@^1.1.0, object-inspect@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
+ integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==
+
+object-is@^1.0.1, object-is@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4"
+ integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==
+
+object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.9, object-keys@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+ integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+object.assign@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
+ integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
+ dependencies:
+ define-properties "^1.1.2"
+ function-bind "^1.1.1"
+ has-symbols "^1.0.0"
+ object-keys "^1.0.11"
+
+object.entries@^1.1.0, object.entries@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b"
+ integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.0-next.1"
+ function-bind "^1.1.1"
+ has "^1.0.3"
+
+object.fromentries@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.2.tgz#4a09c9b9bb3843dd0f89acdb517a794d4f355ac9"
+ integrity sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.0-next.1"
+ function-bind "^1.1.1"
+ has "^1.0.3"
+
+object.omit@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
+ integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=
+ dependencies:
+ for-own "^0.1.4"
+ is-extendable "^0.1.1"
+
+object.values@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e"
+ integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.0-next.1"
+ function-bind "^1.1.1"
+ has "^1.0.3"
+
+once@^1.3.0, once@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ dependencies:
+ wrappy "1"
+
+optimist@^0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
+ integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY=
+ dependencies:
+ minimist "~0.0.1"
+ wordwrap "~0.0.2"
+
+optionator@^0.8.1:
+ version "0.8.3"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
+ integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
+ dependencies:
+ deep-is "~0.1.3"
+ fast-levenshtein "~2.0.6"
+ levn "~0.3.0"
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+ word-wrap "~1.2.3"
+
+os-homedir@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+ integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
+
+os-locale@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
+ integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=
+ dependencies:
+ lcid "^1.0.0"
+
+os-tmpdir@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+ integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
+
+p-limit@^1.1.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
+ integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
+ dependencies:
+ p-try "^1.0.0"
+
+p-limit@^2.0.0:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e"
+ integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==
+ dependencies:
+ p-try "^2.0.0"
+
+p-locate@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
+ integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=
+ dependencies:
+ p-limit "^1.1.0"
+
+p-locate@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+ integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+ dependencies:
+ p-limit "^2.0.0"
+
+p-map@^1.1.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
+ integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==
+
+p-try@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
+ integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
+
+p-try@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+parse-glob@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
+ integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw=
+ dependencies:
+ glob-base "^0.3.0"
+ is-dotfile "^1.0.0"
+ is-extglob "^1.0.0"
+ is-glob "^2.0.0"
+
+parse-json@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+ integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
+ dependencies:
+ error-ex "^1.2.0"
+
+parse5@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94"
+ integrity sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=
+
+parse5@^3.0.1:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c"
+ integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==
+ dependencies:
+ "@types/node" "*"
+
+path-exists@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+ integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=
+ dependencies:
+ pinkie-promise "^2.0.0"
+
+path-exists@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+ integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+
+path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-parse@^1.0.5, path-parse@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+ integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+
+path-type@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
+ integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=
+ dependencies:
+ graceful-fs "^4.1.2"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+performance-now@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+ integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
+
+pify@^2.0.0, pify@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+ integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
+
+pify@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
+ integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+
+pinkie-promise@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o=
+ dependencies:
+ pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+ integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
+
+pirates@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87"
+ integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==
+ dependencies:
+ node-modules-regexp "^1.0.0"
+
+pkg-dir@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
+ integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==
+ dependencies:
+ find-up "^3.0.0"
+
+prelude-ls@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+ integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
+
+preserve@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
+ integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=
+
+pretty-format@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-20.0.3.tgz#020e350a560a1fe1a98dc3beb6ccffb386de8b14"
+ integrity sha1-Ag41ClYKH+GpjcO+tsz/s4beixQ=
+ dependencies:
+ ansi-regex "^2.1.1"
+ ansi-styles "^3.0.0"
+
+private@^0.1.8:
+ version "0.1.8"
+ resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
+ integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
+
+process@^0.11.10:
+ version "0.11.10"
+ resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+ integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
+
+prop-types-exact@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869"
+ integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==
+ dependencies:
+ has "^1.0.3"
+ object.assign "^4.1.0"
+ reflect.ownkeys "^0.2.0"
+
+prop-types@^15.6.2, prop-types@^15.7.2:
+ version "15.7.2"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
+ integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.8.1"
+
+prr@~1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
+ integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
+
+psl@^1.1.28:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c"
+ integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==
+
+punycode@^2.1.0, punycode@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+qs@~6.5.2:
+ version "6.5.2"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
+ integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+
+raf@^3.4.1:
+ version "3.4.1"
+ resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
+ integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
+ dependencies:
+ performance-now "^2.1.0"
+
+railroad-diagrams@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e"
+ integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=
+
+randexp@0.4.6:
+ version "0.4.6"
+ resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3"
+ integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==
+ dependencies:
+ discontinuous-range "1.0.0"
+ ret "~0.1.10"
+
+randomatic@^3.0.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed"
+ integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==
+ dependencies:
+ is-number "^4.0.0"
+ kind-of "^6.0.0"
+ math-random "^1.0.1"
+
+react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.6, react-is@^16.9.0:
+ version "16.13.0"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527"
+ integrity sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==
+
+react-test-renderer@^16.0.0-0:
+ version "16.13.0"
+ resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.13.0.tgz#39ba3bf72cedc8210c3f81983f0bb061b14a3014"
+ integrity sha512-NQ2S9gdMUa7rgPGpKGyMcwl1d6D9MCF0lftdI3kts6kkiX+qvpC955jNjAZXlIDTjnN9jwFI8A8XhRh/9v0spA==
+ dependencies:
+ object-assign "^4.1.1"
+ prop-types "^15.6.2"
+ react-is "^16.8.6"
+ scheduler "^0.19.0"
+
+read-pkg-up@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
+ integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=
+ dependencies:
+ find-up "^1.0.0"
+ read-pkg "^1.0.0"
+
+read-pkg@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
+ integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=
+ dependencies:
+ load-json-file "^1.0.0"
+ normalize-package-data "^2.3.2"
+ path-type "^1.0.0"
+
+readable-stream@^3.1.1:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+ integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
+ dependencies:
+ inherits "^2.0.3"
+ string_decoder "^1.1.1"
+ util-deprecate "^1.0.1"
+
+reflect.ownkeys@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460"
+ integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=
+
+regenerator-runtime@^0.11.0:
+ version "0.11.1"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+ integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+
+regex-cache@^0.4.2:
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
+ integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==
+ dependencies:
+ is-equal-shallow "^0.1.3"
+
+remove-trailing-separator@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
+ integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
+
+repeat-element@^1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
+ integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
+
+repeat-string@^1.5.2:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+ integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
+
+repeating@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
+ integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=
+ dependencies:
+ is-finite "^1.0.0"
+
+request@^2.79.0:
+ version "2.88.2"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
+ integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
+ dependencies:
+ aws-sign2 "~0.7.0"
+ aws4 "^1.8.0"
+ caseless "~0.12.0"
+ combined-stream "~1.0.6"
+ extend "~3.0.2"
+ forever-agent "~0.6.1"
+ form-data "~2.3.2"
+ har-validator "~5.1.3"
+ http-signature "~1.2.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.19"
+ oauth-sign "~0.9.0"
+ performance-now "^2.1.0"
+ qs "~6.5.2"
+ safe-buffer "^5.1.2"
+ tough-cookie "~2.5.0"
+ tunnel-agent "^0.6.0"
+ uuid "^3.3.2"
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
+
+require-hacker@^2.1.4:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/require-hacker/-/require-hacker-2.1.4.tgz#1683da866119495e0ffcda8ebed9bbcf556849f2"
+ integrity sha1-FoPahmEZSV4P/NqOvtm7z1VoSfI=
+ dependencies:
+ babel-runtime "^6.6.1"
+ colors "^1.1.2"
+
+require-main-filename@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
+ integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
+
+resolve@1.1.7:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
+ integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
+
+resolve@^1.10.0, resolve@^1.3.2:
+ version "1.15.1"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
+ integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==
+ dependencies:
+ path-parse "^1.0.6"
+
+ret@~0.1.10:
+ version "0.1.15"
+ resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
+ integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+
+rimraf@^2.6.1:
+ version "2.7.1"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+ integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+ dependencies:
+ glob "^7.1.3"
+
+rst-selector-parser@^2.2.3:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91"
+ integrity sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=
+ dependencies:
+ lodash.flattendeep "^4.4.0"
+ nearley "^2.7.10"
+
+safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
+ integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
+
+safe-buffer@~5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+samsam@1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567"
+ integrity sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=
+
+samsam@~1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621"
+ integrity sha1-n1CHQZtNCR8jJXHn+lLpCw9VJiE=
+
+sane@~1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/sane/-/sane-1.6.0.tgz#9610c452307a135d29c1fdfe2547034180c46775"
+ integrity sha1-lhDEUjB6E10pwf3+JUcDQYDEZ3U=
+ dependencies:
+ anymatch "^1.3.0"
+ exec-sh "^0.2.0"
+ fb-watchman "^1.8.0"
+ minimatch "^3.0.2"
+ minimist "^1.1.1"
+ walker "~1.0.5"
+ watch "~0.10.0"
+
+sax@^1.2.1:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+ integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
+scheduler@^0.19.0:
+ version "0.19.0"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.0.tgz#a715d56302de403df742f4a9be11975b32f5698d"
+ integrity sha512-xowbVaTPe9r7y7RUejcK73/j8tt2jfiyTednOvHbA8JoClvMYCp+r8QegLwK/n8zWQAtZb1fFnER4XLBZXrCxA==
+ dependencies:
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+
+"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+ integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+set-blocking@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+ integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
+shellwords@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
+ integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
+
+sinon@^1.17.5:
+ version "1.17.7"
+ resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf"
+ integrity sha1-RUKk9JugxFwF6y6d2dID4rjv4L8=
+ dependencies:
+ formatio "1.1.1"
+ lolex "1.3.2"
+ samsam "1.1.2"
+ util ">=0.10.3 <1"
+
+slash@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
+ integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=
+
+source-map-support@^0.4.15:
+ version "0.4.18"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
+ integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==
+ dependencies:
+ source-map "^0.5.6"
+
+source-map-support@^0.5.16:
+ version "0.5.16"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
+ integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+ integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+spdx-correct@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
+ integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
+ dependencies:
+ spdx-expression-parse "^3.0.0"
+ spdx-license-ids "^3.0.0"
+
+spdx-exceptions@^2.1.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
+ integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
+
+spdx-expression-parse@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
+ integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
+ dependencies:
+ spdx-exceptions "^2.1.0"
+ spdx-license-ids "^3.0.0"
+
+spdx-license-ids@^3.0.0:
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
+ integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
+
+sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+ integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
+
+sshpk@^1.7.0:
+ version "1.16.1"
+ resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
+ integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
+ dependencies:
+ asn1 "~0.2.3"
+ assert-plus "^1.0.0"
+ bcrypt-pbkdf "^1.0.0"
+ dashdash "^1.12.0"
+ ecc-jsbn "~0.1.1"
+ getpass "^0.1.1"
+ jsbn "~0.1.0"
+ safer-buffer "^2.0.2"
+ tweetnacl "~0.14.0"
+
+string-length@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac"
+ integrity sha1-VpcPscOFWOnnC3KL894mmsRa36w=
+ dependencies:
+ strip-ansi "^3.0.0"
+
+string-width@^1.0.1, string-width@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+ integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ strip-ansi "^3.0.0"
+
+string.prototype.trim@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz#141233dff32c82bfad80684d7e5f0869ee0fb782"
+ integrity sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.0-next.1"
+ function-bind "^1.1.1"
+
+string.prototype.trimleft@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74"
+ integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==
+ dependencies:
+ define-properties "^1.1.3"
+ function-bind "^1.1.1"
+
+string.prototype.trimright@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9"
+ integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==
+ dependencies:
+ define-properties "^1.1.3"
+ function-bind "^1.1.1"
+
+string_decoder@^1.1.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+ integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+ dependencies:
+ safe-buffer "~5.2.0"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+ integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
+ dependencies:
+ ansi-regex "^2.0.0"
+
+strip-bom@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+ integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
+
+strip-bom@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
+ integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=
+ dependencies:
+ is-utf8 "^0.2.0"
+
+supports-color@5.4.0:
+ version "5.4.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
+ integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==
+ dependencies:
+ has-flag "^3.0.0"
+
+supports-color@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+ integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
+
+supports-color@^3.1.2:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+ integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=
+ dependencies:
+ has-flag "^1.0.0"
+
+supports-color@^5.3.0:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+ dependencies:
+ has-flag "^3.0.0"
+
+symbol-tree@^3.2.1:
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
+ integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
+
+test-exclude@^4.2.1:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.3.tgz#a9a5e64474e4398339245a0a769ad7c2f4a97c20"
+ integrity sha512-SYbXgY64PT+4GAL2ocI3HwPa4Q4TBKm0cwAVeKOt/Aoc0gSpNRjJX8w0pA1LMKZ3LBmd8pYBqApFNQLII9kavA==
+ dependencies:
+ arrify "^1.0.1"
+ micromatch "^2.3.11"
+ object-assign "^4.1.0"
+ read-pkg-up "^1.0.1"
+ require-main-filename "^1.0.1"
+
+throat@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/throat/-/throat-3.2.0.tgz#50cb0670edbc40237b9e347d7e1f88e4620af836"
+ integrity sha512-/EY8VpvlqJ+sFtLPeOgc8Pl7kQVOWv0woD87KTXVHPIAE842FGT+rokxIhe8xIUP1cfgrkt0as0vDLjDiMtr8w==
+
+tmatch@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/tmatch/-/tmatch-2.0.1.tgz#0c56246f33f30da1b8d3d72895abaf16660f38cf"
+ integrity sha1-DFYkbzPzDaG409colauvFmYPOM8=
+
+tmpl@1.0.x:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
+ integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=
+
+to-fast-properties@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
+ integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=
+
+to-fast-properties@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+ integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
+
+tough-cookie@^2.3.2, tough-cookie@~2.5.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
+ integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
+ dependencies:
+ psl "^1.1.28"
+ punycode "^2.1.1"
+
+tr46@~0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+ integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
+
+trim-right@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
+ integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
+
+tunnel-agent@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+ integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
+ dependencies:
+ safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+ version "0.14.5"
+ resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+ integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
+
+type-check@~0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+ integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
+ dependencies:
+ prelude-ls "~1.1.2"
+
+uglify-js@^3.1.4:
+ version "3.8.0"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.8.0.tgz#f3541ae97b2f048d7e7e3aa4f39fd8a1f5d7a805"
+ integrity sha512-ugNSTT8ierCsDHso2jkBHXYrU8Y5/fY2ZUprfrJUiD7YpuFvV4jODLFmb3h4btQjqr5Nh4TX4XtgDfCU1WdioQ==
+ dependencies:
+ commander "~2.20.3"
+ source-map "~0.6.1"
+
+uri-js@^4.2.2:
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
+ integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+ dependencies:
+ punycode "^2.1.0"
+
+util-deprecate@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
+"util@>=0.10.3 <1":
+ version "0.12.2"
+ resolved "https://registry.yarnpkg.com/util/-/util-0.12.2.tgz#54adb634c9e7c748707af2bf5a8c7ab640cbba2b"
+ integrity sha512-XE+MkWQvglYa+IOfBt5UFG93EmncEMP23UqpgDvVZVFBPxwmkK10QRp6pgU4xICPnWRf/t0zPv4noYSUq9gqUQ==
+ dependencies:
+ inherits "^2.0.3"
+ is-arguments "^1.0.4"
+ is-generator-function "^1.0.7"
+ safe-buffer "^5.1.2"
+
+uuid@^3.3.2:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
+ integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
+
+validate-npm-package-license@^3.0.1:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
+ integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
+ dependencies:
+ spdx-correct "^3.0.0"
+ spdx-expression-parse "^3.0.0"
+
+verror@1.10.0:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
+ integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
+ dependencies:
+ assert-plus "^1.0.0"
+ core-util-is "1.0.2"
+ extsprintf "^1.2.0"
+
+walker@~1.0.5:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
+ integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=
+ dependencies:
+ makeerror "1.0.x"
+
+watch@~0.10.0:
+ version "0.10.0"
+ resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc"
+ integrity sha1-d3mLLaD5kQ1ZXxrOWwwiWFIfIdw=
+
+webidl-conversions@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+ integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
+
+webidl-conversions@^4.0.0:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
+ integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
+
+whatwg-encoding@^1.0.1:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
+ integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==
+ dependencies:
+ iconv-lite "0.4.24"
+
+whatwg-url@^4.3.0:
+ version "4.8.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.8.0.tgz#d2981aa9148c1e00a41c5a6131166ab4683bbcc0"
+ integrity sha1-0pgaqRSMHgCkHFphMRZqtGg7vMA=
+ dependencies:
+ tr46 "~0.0.3"
+ webidl-conversions "^3.0.0"
+
+which-boxed-primitive@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1"
+ integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==
+ dependencies:
+ is-bigint "^1.0.0"
+ is-boolean-object "^1.0.0"
+ is-number-object "^1.0.3"
+ is-string "^1.0.4"
+ is-symbol "^1.0.2"
+
+which-collection@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906"
+ integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==
+ dependencies:
+ is-map "^2.0.1"
+ is-set "^2.0.1"
+ is-weakmap "^2.0.1"
+ is-weakset "^2.0.1"
+
+which-module@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
+ integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=
+
+which@^1.2.12, which@^1.3.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+ integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+ dependencies:
+ isexe "^2.0.0"
+
+word-wrap@~1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+ integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
+wordwrap@~0.0.2:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
+ integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc=
+
+worker-farm@^1.3.1:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8"
+ integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==
+ dependencies:
+ errno "~0.1.7"
+
+wrap-ansi@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
+ integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+xml-name-validator@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
+ integrity sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=
+
+y18n@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
+ integrity sha1-bRX7qITAhnnA136I53WegR4H+kE=
+
+yargs-parser@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"
+ integrity sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=
+ dependencies:
+ camelcase "^3.0.0"
+
+yargs@^7.0.2:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"
+ integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=
+ dependencies:
+ camelcase "^3.0.0"
+ cliui "^3.2.0"
+ decamelize "^1.1.1"
+ get-caller-file "^1.0.1"
+ os-locale "^1.4.0"
+ read-pkg-up "^1.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^1.0.1"
+ set-blocking "^2.0.0"
+ string-width "^1.0.2"
+ which-module "^1.0.0"
+ y18n "^3.2.1"
+ yargs-parser "^5.0.0"