505 lines
17 KiB
JavaScript
505 lines
17 KiB
JavaScript
/* 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("resource://devtools/client/shared/vendor/react.js");
|
|
const Provider = createFactory(
|
|
require("resource://devtools/client/shared/vendor/react-redux.js").Provider
|
|
);
|
|
const {
|
|
formatErrorTextWithCausedBy,
|
|
setupStore,
|
|
} = require("resource://devtools/client/webconsole/test/node/helpers.js");
|
|
|
|
// Components under test.
|
|
const EvaluationResult = createFactory(
|
|
require("resource://devtools/client/webconsole/components/Output/message-types/EvaluationResult.js")
|
|
);
|
|
const {
|
|
INDENT_WIDTH,
|
|
} = require("resource://devtools/client/webconsole/components/Output/MessageIndent.js");
|
|
|
|
// Test fakes.
|
|
const {
|
|
stubPreparedMessages,
|
|
} = require("resource://devtools/client/webconsole/test/node/fixtures/stubs/index.js");
|
|
const serviceContainer = require("resource://devtools/client/webconsole/test/node/fixtures/serviceContainer.js");
|
|
|
|
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 Error object 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 thrown Error object with an error cause", () => {
|
|
const message = stubPreparedMessages.get(
|
|
`eval throw Error Object with error cause`
|
|
);
|
|
const wrapper = render(EvaluationResult({ message, serviceContainer }));
|
|
const text = formatErrorTextWithCausedBy(
|
|
wrapper.find(".message-body").text()
|
|
);
|
|
expect(text).toBe(
|
|
"Uncaught Error: something went wrong\nCaused by: SyntaxError: original error"
|
|
);
|
|
expect(wrapper.hasClass("error")).toBe(true);
|
|
});
|
|
|
|
it("render thrown Error object with an error cause chain", () => {
|
|
const message = stubPreparedMessages.get(
|
|
`eval throw Error Object with cause chain`
|
|
);
|
|
const wrapper = render(EvaluationResult({ message, serviceContainer }));
|
|
const text = formatErrorTextWithCausedBy(
|
|
wrapper.find(".message-body").text()
|
|
);
|
|
expect(text).toBe(
|
|
[
|
|
"Uncaught Error: err-d",
|
|
"Caused by: Error: err-c",
|
|
"Caused by: Error: err-b",
|
|
"Caused by: Error: err-a",
|
|
].join("\n")
|
|
);
|
|
expect(wrapper.hasClass("error")).toBe(true);
|
|
});
|
|
|
|
it("render thrown Error object with a cyclical error cause chain", () => {
|
|
const message = stubPreparedMessages.get(
|
|
`eval throw Error Object with cyclical cause chain`
|
|
);
|
|
const wrapper = render(EvaluationResult({ message, serviceContainer }));
|
|
const text = formatErrorTextWithCausedBy(
|
|
wrapper.find(".message-body").text()
|
|
);
|
|
expect(text).toBe(
|
|
[
|
|
"Uncaught Error: err-y",
|
|
"Caused by: Error: err-x",
|
|
// TODO: it shouldn't be displayed like this. This will
|
|
// be fixed in Bug 1719605
|
|
"Caused by: undefined",
|
|
].join("\n")
|
|
);
|
|
expect(wrapper.hasClass("error")).toBe(true);
|
|
});
|
|
|
|
it("render thrown Error object with a falsy cause", () => {
|
|
const message = stubPreparedMessages.get(
|
|
`eval throw Error Object with falsy cause`
|
|
);
|
|
const wrapper = render(EvaluationResult({ message, serviceContainer }));
|
|
const text = formatErrorTextWithCausedBy(
|
|
wrapper.find(".message-body").text()
|
|
);
|
|
expect(text).toBe("Uncaught Error: false cause\nCaused by: false");
|
|
expect(wrapper.hasClass("error")).toBe(true);
|
|
});
|
|
|
|
it("render thrown Error object with a null cause", () => {
|
|
const message = stubPreparedMessages.get(
|
|
`eval throw Error Object with null cause`
|
|
);
|
|
const wrapper = render(EvaluationResult({ message, serviceContainer }));
|
|
const text = formatErrorTextWithCausedBy(
|
|
wrapper.find(".message-body").text()
|
|
);
|
|
expect(text).toBe("Uncaught Error: null cause\nCaused by: null");
|
|
expect(wrapper.hasClass("error")).toBe(true);
|
|
});
|
|
|
|
it("render thrown Error object with an undefined cause", () => {
|
|
const message = stubPreparedMessages.get(
|
|
`eval throw Error Object with undefined cause`
|
|
);
|
|
const wrapper = render(EvaluationResult({ message, serviceContainer }));
|
|
const text = formatErrorTextWithCausedBy(
|
|
wrapper.find(".message-body").text()
|
|
);
|
|
expect(text).toBe("Uncaught Error: undefined cause\nCaused by: undefined");
|
|
expect(wrapper.hasClass("error")).toBe(true);
|
|
});
|
|
|
|
it("render thrown Error object with a number cause", () => {
|
|
const message = stubPreparedMessages.get(
|
|
`eval throw Error Object with number cause`
|
|
);
|
|
const wrapper = render(EvaluationResult({ message, serviceContainer }));
|
|
const text = formatErrorTextWithCausedBy(
|
|
wrapper.find(".message-body").text()
|
|
);
|
|
expect(text).toBe("Uncaught Error: number cause\nCaused by: 0");
|
|
expect(wrapper.hasClass("error")).toBe(true);
|
|
});
|
|
|
|
it("render thrown Error object with a string cause", () => {
|
|
const message = stubPreparedMessages.get(
|
|
`eval throw Error Object with string cause`
|
|
);
|
|
const wrapper = render(EvaluationResult({ message, serviceContainer }));
|
|
const text = formatErrorTextWithCausedBy(
|
|
wrapper.find(".message-body").text()
|
|
);
|
|
expect(text).toBe(
|
|
`Uncaught Error: string cause\nCaused by: "cause message"`
|
|
);
|
|
expect(wrapper.hasClass("error")).toBe(true);
|
|
});
|
|
|
|
it("render thrown Error object with object cause", () => {
|
|
const message = stubPreparedMessages.get(
|
|
`eval throw Error Object with object cause`
|
|
);
|
|
const wrapper = render(EvaluationResult({ message, serviceContainer }));
|
|
const text = formatErrorTextWithCausedBy(
|
|
wrapper.find(".message-body").text()
|
|
);
|
|
expect(text).toBe(`Uncaught Error: object cause\nCaused by: Object { … }`);
|
|
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("render rejected promise with Error with cause", () => {
|
|
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,
|
|
})
|
|
)
|
|
);
|
|
expect(wrapper.prop("data-indent")).toBe(`${indent}`);
|
|
const indentEl = wrapper.find(".indent");
|
|
expect(indentEl.prop("style").width).toBe(`${indent * INDENT_WIDTH}px`);
|
|
|
|
wrapper = render(
|
|
Provider(
|
|
{ store: setupStore() },
|
|
EvaluationResult({ message, serviceContainer })
|
|
)
|
|
);
|
|
expect(wrapper.prop("data-indent")).toBe(`0`);
|
|
// there's no indent element where the indent is 0
|
|
expect(wrapper.find(".indent").length).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:5");
|
|
});
|
|
|
|
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("resource://devtools/client/webconsole/utils/l10n.js");
|
|
|
|
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);
|
|
});
|
|
});
|