Bug 1408454 - Move error.pprint to format.pprint. r?whimboo
Pretty-printing an object belongs more naturally to the new format module.
MozReview-Commit-ID: AfXLMPAT5ar
--- a/testing/marionette/action.js
+++ b/testing/marionette/action.js
@@ -6,22 +6,22 @@
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("chrome://marionette/content/assert.js");
Cu.import("chrome://marionette/content/element.js");
const {
- pprint,
InvalidArgumentError,
MoveTargetOutOfBoundsError,
UnsupportedOperationError,
} = Cu.import("chrome://marionette/content/error.js", {});
Cu.import("chrome://marionette/content/event.js");
+const {pprint} = Cu.import("chrome://marionette/content/format.js", {});
Cu.import("chrome://marionette/content/interaction.js");
this.EXPORTED_SYMBOLS = ["action"];
// TODO? With ES 2016 and Symbol you can make a safer approximation
// to an enum e.g. https://gist.github.com/xmlking/e86e4f15ec32b12c4689
/**
* Implements WebDriver Actions API: a low-level interface for providing
--- a/testing/marionette/assert.js
+++ b/testing/marionette/assert.js
@@ -9,20 +9,20 @@ const {utils: Cu} = Components;
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const {
InvalidArgumentError,
InvalidSessionIDError,
NoSuchWindowError,
- pprint,
UnexpectedAlertOpenError,
UnsupportedOperationError,
} = Cu.import("chrome://marionette/content/error.js", {});
+const {pprint} = Cu.import("chrome://marionette/content/format.js", {});
this.EXPORTED_SYMBOLS = ["assert"];
const isFennec = () => AppConstants.platform == "android";
const isFirefox = () =>
Services.appinfo.ID == "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
/**
--- a/testing/marionette/cookie.js
+++ b/testing/marionette/cookie.js
@@ -4,20 +4,18 @@
"use strict";
const {interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("chrome://marionette/content/assert.js");
-const {
- InvalidCookieDomainError,
- pprint,
-} = Cu.import("chrome://marionette/content/error.js", {});
+const {InvalidCookieDomainError} = Cu.import("chrome://marionette/content/error.js", {});
+const {pprint} = Cu.import("chrome://marionette/content/format.js", {});
this.EXPORTED_SYMBOLS = ["cookie"];
const IPV4_PORT_EXPR = /:\d+$/;
/** @namespace */
this.cookie = {
manager: Services.cookies,
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -7,19 +7,19 @@
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("chrome://marionette/content/assert.js");
Cu.import("chrome://marionette/content/atom.js");
const {
InvalidSelectorError,
NoSuchElementError,
- pprint,
StaleElementReferenceError,
} = Cu.import("chrome://marionette/content/error.js", {});
+const {pprint} = Cu.import("chrome://marionette/content/format.js", {});
const {PollPromise} = Cu.import("chrome://marionette/content/sync.js", {});
this.EXPORTED_SYMBOLS = ["element"];
const XMLNS = "http://www.w3.org/1999/xhtml";
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
/** XUL elements that support checked property. */
--- a/testing/marionette/error.js
+++ b/testing/marionette/error.js
@@ -1,16 +1,18 @@
/* 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 {interfaces: Ci, utils: Cu} = Components;
+const {pprint} = Cu.import("chrome://marionette/content/format.js", {});
+
const ERRORS = new Set([
"ElementClickInterceptedError",
"ElementNotAccessibleError",
"ElementNotInteractableError",
"InsecureCertificateError",
"InvalidArgumentError",
"InvalidCookieDomainError",
"InvalidElementStateError",
@@ -42,17 +44,16 @@ const BUILTIN_ERRORS = new Set([
"ReferenceError",
"SyntaxError",
"TypeError",
"URIError",
]);
this.EXPORTED_SYMBOLS = [
"error",
- "pprint",
"stack",
].concat(Array.from(ERRORS));
/** @namespace */
this.error = {};
/**
* Check if |val| is an instance of the |Error| prototype.
@@ -143,84 +144,16 @@ error.stringify = function(err) {
s += "\n" + err.stack;
}
return s;
} catch (e) {
return "<unprintable error>";
}
};
-/**
- * Pretty-print values passed to template strings.
- *
- * Usage:
- *
- * const {pprint} = Cu.import("chrome://marionette/content/error.js", {});
- * let bool = {value: true};
- * pprint`Expected boolean, got ${bool}`;
- * => 'Expected boolean, got [object Object] {"value": true}'
- *
- * let htmlElement = document.querySelector("input#foo");
- * pprint`Expected element ${htmlElement}`;
- * => 'Expected element <input id="foo" class="bar baz">'
- */
-this.pprint = function(ss, ...values) {
- function prettyObject(obj) {
- let proto = Object.prototype.toString.call(obj);
- let s = "";
- try {
- s = JSON.stringify(obj);
- } catch (e) {
- if (e instanceof TypeError) {
- s = `<${e.message}>`;
- } else {
- throw e;
- }
- }
- return proto + " " + s;
- }
-
- function prettyElement(el) {
- let ident = [];
- if (el.id) {
- ident.push(`id="${el.id}"`);
- }
- if (el.classList.length > 0) {
- ident.push(`class="${el.className}"`);
- }
-
- let idents = "";
- if (ident.length > 0) {
- idents = " " + ident.join(" ");
- }
-
- return `<${el.localName}${idents}>`;
- }
-
- let res = [];
- for (let i = 0; i < ss.length; i++) {
- res.push(ss[i]);
- if (i < values.length) {
- let val = values[i];
- let s;
- try {
- if (val && val.nodeType === 1) {
- s = prettyElement(val);
- } else {
- s = prettyObject(val);
- }
- } catch (e) {
- s = typeof val;
- }
- res.push(s);
- }
- }
- return res.join("");
-};
-
/** Create a stacktrace to the current line in the program. */
this.stack = function() {
let trace = new Error().stack;
let sa = trace.split("\n");
sa = sa.slice(1);
return "stacktrace:\n" + sa.join("\n");
};
--- a/testing/marionette/format.js
+++ b/testing/marionette/format.js
@@ -1,19 +1,90 @@
/* 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";
-this.EXPORTED_SYMBOLS = ["truncate"];
+this.EXPORTED_SYMBOLS = ["pprint", "truncate"];
const MAX_STRING_LENGTH = 250;
/**
+ * Pretty-print values passed to template strings.
+ *
+ * Usage:
+ *
+ * <pre><code>
+ * const {pprint} = Cu.import("chrome://marionette/content/error.js", {});
+ * let bool = {value: true};
+ * pprint`Expected boolean, got ${bool}`;
+ * => 'Expected boolean, got [object Object] {"value": true}'
+ *
+ * let htmlElement = document.querySelector("input#foo");
+ * pprint`Expected element ${htmlElement}`;
+ * => 'Expected element <input id="foo" class="bar baz">'
+ * </code></pre>
+ */
+function pprint(ss, ...values) {
+ function prettyObject(obj) {
+ let proto = Object.prototype.toString.call(obj);
+ let s = "";
+ try {
+ s = JSON.stringify(obj);
+ } catch (e) {
+ if (e instanceof TypeError) {
+ s = `<${e.message}>`;
+ } else {
+ throw e;
+ }
+ }
+ return proto + " " + s;
+ }
+
+ function prettyElement(el) {
+ let ident = [];
+ if (el.id) {
+ ident.push(`id="${el.id}"`);
+ }
+ if (el.classList.length > 0) {
+ ident.push(`class="${el.className}"`);
+ }
+
+ let idents = "";
+ if (ident.length > 0) {
+ idents = " " + ident.join(" ");
+ }
+
+ return `<${el.localName}${idents}>`;
+ }
+
+ let res = [];
+ for (let i = 0; i < ss.length; i++) {
+ res.push(ss[i]);
+ if (i < values.length) {
+ let val = values[i];
+ let s;
+ try {
+ if (val && val.nodeType === 1) {
+ s = prettyElement(val);
+ } else {
+ s = prettyObject(val);
+ }
+ } catch (e) {
+ s = typeof val;
+ }
+ res.push(s);
+ }
+ }
+ return res.join("");
+}
+this.pprint = pprint;
+
+/**
* Template literal that truncates string values in arbitrary objects.
*
* Given any object, the template will walk the object and truncate
* any strings it comes across to a reasonable limit. This is suitable
* when you have arbitrary data and data integrity is not important.
*
* The strings are truncated in the middle so that the beginning and
* the end is preserved. This will make a long, truncated string look
--- a/testing/marionette/interaction.js
+++ b/testing/marionette/interaction.js
@@ -8,19 +8,19 @@ const {utils: Cu} = Components;
Cu.import("chrome://marionette/content/accessibility.js");
Cu.import("chrome://marionette/content/atom.js");
const {
ElementClickInterceptedError,
ElementNotInteractableError,
InvalidArgumentError,
InvalidElementStateError,
- pprint,
} = Cu.import("chrome://marionette/content/error.js", {});
Cu.import("chrome://marionette/content/element.js");
+const {pprint} = Cu.import("chrome://marionette/content/format.js", {});
Cu.import("chrome://marionette/content/event.js");
Cu.importGlobalProperties(["File"]);
this.EXPORTED_SYMBOLS = ["interaction"];
/** XUL elements that support disabled attribute. */
const DISABLED_ATTRIBUTE_SUPPORTED_XUL = new Set([
--- a/testing/marionette/test_error.js
+++ b/testing/marionette/test_error.js
@@ -16,17 +16,16 @@ const {
InvalidSelectorError,
InvalidSessionIDError,
JavaScriptError,
MoveTargetOutOfBoundsError,
NoAlertOpenError,
NoSuchElementError,
NoSuchFrameError,
NoSuchWindowError,
- pprint,
ScriptTimeoutError,
SessionNotCreatedError,
stack,
StaleElementReferenceError,
TimeoutError,
UnableToSetCookieError,
UnexpectedAlertOpenError,
UnknownCommandError,
@@ -113,43 +112,16 @@ add_test(function test_stringify() {
equal("WebDriverError: foo",
error.stringify(new WebDriverError("foo")).split("\n")[0]);
equal("InvalidArgumentError: foo",
error.stringify(new InvalidArgumentError("foo")).split("\n")[0]);
run_next_test();
});
-add_test(function test_pprint() {
- equal('[object Object] {"foo":"bar"}', pprint`${{foo: "bar"}}`);
-
- equal("[object Number] 42", pprint`${42}`);
- equal("[object Boolean] true", pprint`${true}`);
- equal("[object Undefined] undefined", pprint`${undefined}`);
- equal("[object Null] null", pprint`${null}`);
-
- let complexObj = {toJSON: () => "foo"};
- equal('[object Object] "foo"', pprint`${complexObj}`);
-
- let cyclic = {};
- cyclic.me = cyclic;
- equal("[object Object] <cyclic object value>", pprint`${cyclic}`);
-
- let el = {
- nodeType: 1,
- localName: "input",
- id: "foo",
- classList: {length: 1},
- className: "bar baz",
- };
- equal('<input id="foo" class="bar baz">', pprint`${el}`);
-
- run_next_test();
-});
-
add_test(function test_stack() {
equal("string", typeof stack());
ok(stack().includes("test_stack"));
ok(!stack().includes("add_test"));
run_next_test();
});
--- a/testing/marionette/test_format.js
+++ b/testing/marionette/test_format.js
@@ -1,19 +1,46 @@
/* 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/. */
const {utils: Cu} = Components;
-const {truncate} = Cu.import("chrome://marionette/content/format.js", {});
+const {pprint, truncate} = Cu.import("chrome://marionette/content/format.js", {});
const MAX_STRING_LENGTH = 250;
const HALF = "x".repeat(MAX_STRING_LENGTH / 2);
+add_test(function test_pprint() {
+ equal('[object Object] {"foo":"bar"}', pprint`${{foo: "bar"}}`);
+
+ equal("[object Number] 42", pprint`${42}`);
+ equal("[object Boolean] true", pprint`${true}`);
+ equal("[object Undefined] undefined", pprint`${undefined}`);
+ equal("[object Null] null", pprint`${null}`);
+
+ let complexObj = {toJSON: () => "foo"};
+ equal('[object Object] "foo"', pprint`${complexObj}`);
+
+ let cyclic = {};
+ cyclic.me = cyclic;
+ equal("[object Object] <cyclic object value>", pprint`${cyclic}`);
+
+ let el = {
+ nodeType: 1,
+ localName: "input",
+ id: "foo",
+ classList: {length: 1},
+ className: "bar baz",
+ };
+ equal('<input id="foo" class="bar baz">', pprint`${el}`);
+
+ run_next_test();
+});
+
add_test(function test_truncate_empty() {
equal(truncate``, "");
run_next_test();
});
add_test(function test_truncate_noFields() {
equal(truncate`foo bar`, "foo bar");
run_next_test();